基础导入、大物导入、发票箱单导入已完善
parent
3c8a28ad7c
commit
43c444c1f5
|
|
@ -19,56 +19,35 @@ import cn.hutool.poi.excel.ExcelReader;
|
|||
import cn.hutool.poi.excel.ExcelUtil;
|
||||
import com.youchain.annotation.AnonymousAccess;
|
||||
import com.youchain.annotation.Log;
|
||||
import com.youchain.basicdata.domain.BigItem;
|
||||
import com.youchain.basicdata.domain.Item;
|
||||
import com.youchain.basicdata.domain.StockType;
|
||||
import com.youchain.basicdata.repository.BigItemRepository;
|
||||
import com.youchain.basicdata.repository.ItemRepository;
|
||||
import com.youchain.basicdata.service.ImportAsnService;
|
||||
import com.youchain.basicdata.service.ImportDataService;
|
||||
import com.youchain.basicdata.service.ItemService;
|
||||
import com.youchain.basicdata.service.StockTypeService;
|
||||
import com.youchain.basicdata.service.dto.BomAccountQueryCriteria;
|
||||
import com.youchain.basicdata.service.dto.ItemDto;
|
||||
import com.youchain.basicdata.service.dto.ItemQueryCriteria;
|
||||
import com.youchain.businessdata.domain.Asn;
|
||||
import com.youchain.businessdata.domain.PickDetail;
|
||||
import com.youchain.businessdata.domain.PickTicket;
|
||||
import com.youchain.businessdata.service.AsnService;
|
||||
import com.youchain.businessdata.service.PickDetailService;
|
||||
import com.youchain.businessdata.service.PickTicketService;
|
||||
import com.youchain.businessdata.service.dto.AsnDto;
|
||||
import com.youchain.config.FileProperties;
|
||||
import com.youchain.exception.BadRequestException;
|
||||
import com.youchain.exception.handler.ApiError;
|
||||
import com.youchain.exception.handler.ApiResult;
|
||||
import com.youchain.modules.system.domain.DictDetail;
|
||||
import com.youchain.modules.system.service.DictDetailService;
|
||||
import com.youchain.modules.system.service.dto.DictQueryCriteria;
|
||||
import com.youchain.utils.BizStatus;
|
||||
import com.youchain.utils.CodeUtils;
|
||||
import com.youchain.utils.FileUtil;
|
||||
import com.youchain.utils.UserUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.transaction.Transactional;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
|
||||
|
|
@ -84,13 +63,11 @@ import static org.springframework.http.HttpStatus.OK;
|
|||
@RequestMapping("/api/importData")
|
||||
public class ImportDataController {
|
||||
private final FileProperties properties;
|
||||
private final BigItemRepository bigItemRepository;
|
||||
private final ItemRepository itemRepository;
|
||||
private final ImportDataService importDataService;
|
||||
private final AsnService asnService;
|
||||
private final PickTicketService pickTicketService;
|
||||
private final PickDetailService pickDetailService;
|
||||
private final CodeUtils codeUtils;
|
||||
private final ImportDataService importDataService;
|
||||
private final ImportAsnService importAsnService;
|
||||
|
||||
@Log("导入完成品品番")
|
||||
@ApiOperation("导入完成品品番")
|
||||
|
|
@ -253,9 +230,8 @@ public class ImportDataController {
|
|||
public ResponseEntity<Object> importAsn(@RequestParam("file") MultipartFile file, @RequestParam("templateType") String templateType) {
|
||||
log.info("开始导入");
|
||||
long start = System.currentTimeMillis();
|
||||
importDataService.importAsn(file, templateType);
|
||||
importAsnService.importAsn(file, templateType);
|
||||
log.info("导入结束,耗时:{}ms", (System.currentTimeMillis() - start));
|
||||
return new ResponseEntity<>(ApiResult.success(OK.value(), "导入成功", null), HttpStatus.OK);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.youchain.basicdata.service;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public interface ImportAsnService {
|
||||
/**
|
||||
* 批量导入
|
||||
* @param file 文件
|
||||
* @param templateType 模板类型;1-标准模板;2-大物模板;3-发票箱单模板
|
||||
*/
|
||||
void importAsn(MultipartFile file, String templateType);
|
||||
}
|
||||
|
|
@ -56,11 +56,4 @@ public interface ImportDataService {
|
|||
|
||||
void importCountMoveDetail(Long countId,List<Map<String, Object>> readAll);
|
||||
|
||||
|
||||
/**
|
||||
* 入库导入
|
||||
*
|
||||
* @param file 上传文件
|
||||
*/
|
||||
void importAsn(MultipartFile file, String templateType);
|
||||
}
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
package com.youchain.basicdata.service.impl;
|
||||
|
||||
import com.youchain.basicdata.domain.Area;
|
||||
import com.youchain.basicdata.domain.BillType;
|
||||
import com.youchain.basicdata.domain.Item;
|
||||
import com.youchain.basicdata.repository.*;
|
||||
import com.youchain.basicdata.service.*;
|
||||
import com.youchain.businessdata.domain.Asn;
|
||||
import com.youchain.businessdata.domain.AsnDetail;
|
||||
import com.youchain.businessdata.inputJson.imports.BaseImport;
|
||||
import com.youchain.businessdata.inputJson.imports.invoicePackingImport;
|
||||
import com.youchain.businessdata.repository.*;
|
||||
import com.youchain.businessdata.service.*;
|
||||
import com.youchain.exception.BadRequestException;
|
||||
import com.youchain.utils.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ImportAsnServiceImpl implements ImportAsnService {
|
||||
|
||||
private final AsnRepository asnRepository;
|
||||
|
||||
private final AsnDetailRepository asnDetailRepository;
|
||||
|
||||
private final BillTypeRepository billTypeRepository;
|
||||
|
||||
private final AreaService areaService;
|
||||
|
||||
private final ItemService itemService;
|
||||
|
||||
private final AsnService asnService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void importAsn(MultipartFile file, String templateType) {
|
||||
|
||||
// 验证模板类型是否正确
|
||||
isValidTemplateType(file, templateType);
|
||||
|
||||
// 根据模板类型进行不同的处理
|
||||
switch (templateType) {
|
||||
case "template1":
|
||||
baseImportTemplate(file);
|
||||
break;
|
||||
case "template2":
|
||||
bigItemTemplate(file);
|
||||
break;
|
||||
case "template3":
|
||||
invoicePackingTemplate(file);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("不支持的模板类型: " + templateType);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理标准模板
|
||||
@Transactional
|
||||
public void baseImportTemplate(MultipartFile file) {
|
||||
// 读取sheet数据
|
||||
List<BaseImport> dataList = FastExcelUtil.readExcelData(file, BaseImport.class, 0, 1);
|
||||
|
||||
//批量导入
|
||||
importAsnData(dataList);
|
||||
}
|
||||
|
||||
//处理大物模板
|
||||
private void bigItemTemplate(MultipartFile file) {
|
||||
// 获取发票号
|
||||
FastExcelCellListener reader = new FastExcelCellListener()
|
||||
.addTargetCell(5, 55); // invoiceNo
|
||||
Map<String, String> cellValues = FastExcelUtil.readExcelCellValues(file, reader);
|
||||
String invoiceNo = cellValues.get("5-55");
|
||||
|
||||
// 读取两个sheet的数据
|
||||
List<BaseImport> sheet1Data = FastExcelUtil.readExcelData(file, BaseImport.class, 0, 21);
|
||||
List<BaseImport> sheet2Data = FastExcelUtil.readExcelData(file, BaseImport.class, 1, 21);
|
||||
|
||||
//大物数据处理
|
||||
List<BaseImport> dataList = bigItemDataProcess(invoiceNo, sheet1Data, sheet2Data);
|
||||
|
||||
//批量导入数据
|
||||
importAsnData(dataList);
|
||||
|
||||
}
|
||||
|
||||
// 处理发票箱单模板
|
||||
private void invoicePackingTemplate(MultipartFile file) {
|
||||
// 获取发票号
|
||||
FastExcelCellListener reader = new FastExcelCellListener().addTargetCell(2, 11); // invoiceNo
|
||||
Map<String, String> cellValues = FastExcelUtil.readExcelCellValues(file, reader);
|
||||
String invoiceNo = cellValues.get("2-11");
|
||||
|
||||
// 读取sheet数据
|
||||
List<invoicePackingImport> sheetData1 = FastExcelUtil.readExcelData(file, invoicePackingImport.class, 0, 5);
|
||||
|
||||
//发票箱单数据处理
|
||||
List<BaseImport> dataList = invoicePackingDataProcess(invoiceNo, sheetData1);
|
||||
|
||||
//批量导入
|
||||
importAsnData(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理批量导入
|
||||
*/
|
||||
private void importAsnData(List<BaseImport> dataList) {
|
||||
// TODO: 实现批量导入逻辑
|
||||
log.info("处理批量导入,数据条数: {}", dataList.size());
|
||||
|
||||
//获取文件中所有的托盘号
|
||||
List<String> codes = dataList.stream().map(BaseImport::getCNo).collect(Collectors.toList());
|
||||
|
||||
//验证托盘号
|
||||
validateAsn(codes);
|
||||
|
||||
//获取文件中所有的品番
|
||||
List<String> itemCodes = dataList.stream().map(BaseImport::getPartNo).collect(Collectors.toList());
|
||||
|
||||
//验证品番
|
||||
Map<String, Item> exitItemMap = validateItem(itemCodes);
|
||||
|
||||
//库区
|
||||
Area area = areaService.findByCode(BaseStatus.DEFAULT_AREA);
|
||||
|
||||
//单据类型
|
||||
BillType billType = billTypeRepository.findByName(BaseStatus.RK);
|
||||
|
||||
Map<String, Asn> asnMap = new HashMap<>();
|
||||
List<Asn> insertToAsn = new ArrayList<>();//批量新增ASN
|
||||
List<AsnDetail> insertToAsnDetail = new ArrayList<>();//批量新增asnDetail
|
||||
for (BaseImport data : dataList) {
|
||||
String relatedBill1 = data.getInvoiceNo();//发票号
|
||||
String propC2 = data.getBoi();//税别
|
||||
String po = data.getPoNo();//订单号
|
||||
String itemCode = data.getPartNo();//品番
|
||||
String remark = data.getDescription();//描述
|
||||
Double orderQty = data.getQty();//数量
|
||||
String code = data.getCNo();//托盘号
|
||||
|
||||
//品番
|
||||
Item item = exitItemMap.get(itemCode);
|
||||
|
||||
Asn asn;
|
||||
if (asnMap.containsKey(code)) {
|
||||
asn = asnMap.get(code);
|
||||
asn.setOrderQuantity(asn.getOrderQuantity() + orderQty);
|
||||
} else {
|
||||
asn = Asn.builder()
|
||||
.relatedBill1(relatedBill1)
|
||||
.code(code)
|
||||
.area(area)
|
||||
.status(BizStatus.OPEN)
|
||||
.orderDate(new Timestamp(new Date().getTime()))
|
||||
.orderQuantity(orderQty)
|
||||
.billType(billType)
|
||||
.receivedQuantity(0d)
|
||||
.putawayQuantity(0d)
|
||||
.build();
|
||||
asnMap.put(code, asn);
|
||||
insertToAsn.add(asn);
|
||||
}
|
||||
|
||||
AsnDetail asnDetail = AsnDetail.builder()
|
||||
.propC2(propC2)
|
||||
.po(po)
|
||||
.item(item)
|
||||
.remark(remark)
|
||||
.orderQty(orderQty)
|
||||
.asn(asn)
|
||||
.status(BizStatus.OPEN)
|
||||
.receivedQty(0d)
|
||||
.moveQty(0d)
|
||||
.putQty(0d)
|
||||
.weight(0d)
|
||||
.volume(0d)
|
||||
.dept(item.getDept())
|
||||
.build();
|
||||
insertToAsnDetail.add(asnDetail);
|
||||
}
|
||||
if (!insertToAsn.isEmpty()) {
|
||||
asnRepository.saveAll(insertToAsn);
|
||||
}
|
||||
if (!insertToAsnDetail.isEmpty()) {
|
||||
asnDetailRepository.saveAll(insertToAsnDetail);
|
||||
}
|
||||
}
|
||||
|
||||
//验证品番
|
||||
private Map<String, Item> validateItem(List<String> itemCodes) {
|
||||
Map<String, Item> exitItemMap = itemService.queryByItemCodesToMap(itemCodes);
|
||||
if (exitItemMap.isEmpty()) {
|
||||
throw new BadRequestException(itemCodes + "品番不存在或已失效");
|
||||
}
|
||||
|
||||
List<String> existingItemCodes = new ArrayList<>(exitItemMap.keySet());
|
||||
|
||||
// 获取两个集合的非交集说明品番不存在或失效,直接提示
|
||||
List<String> difference = SmartStringUtil.getDifference(itemCodes, existingItemCodes);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(difference)) {
|
||||
throw new BadRequestException(difference + "品番不存在或已失效");
|
||||
}
|
||||
return exitItemMap;
|
||||
}
|
||||
|
||||
//验证ASN单号
|
||||
private void validateAsn(List<String> codes) {
|
||||
Map<String, Asn> exitAsnMap = asnService.queryByasnCodesToMap(codes);
|
||||
List<String> existingAsnCodes = new ArrayList<>(exitAsnMap.keySet());
|
||||
// 获取两个集合的交集说明ASN重复导入了,直接提示
|
||||
List<String> difference = SmartStringUtil.getIntersection(codes, existingAsnCodes);
|
||||
if (CollectionUtils.isNotEmpty(difference)) {
|
||||
//去重difference
|
||||
difference = difference.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
throw new BadRequestException(difference + "托盘号已导入,请勿重复导入");
|
||||
}
|
||||
}
|
||||
|
||||
//验证模板是否使用正确
|
||||
private void isValidTemplateType(MultipartFile file, String templateType) {
|
||||
// 根据模板类型进行不同的处理
|
||||
if ("template1".equals(templateType)) {
|
||||
List<String> requiredColumns = Arrays.asList("INVOICE NO.", "BOI", "PO&LN NO.", "PART NO.", "DESCRIPTION", "Q'TY \n" + "(PCS)", "C/NO.");
|
||||
List<String> headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 0);
|
||||
if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) {
|
||||
throw new BadRequestException("标准导入模板不正确,请确认模板信息");
|
||||
}
|
||||
} else if ("template2".equals(templateType)) {
|
||||
List<String> requiredColumns = Arrays.asList("BOI", "PO&LN NO.", "PART NO.", "DESCRIPTION", "Q'TY \n" + "(PCS)");
|
||||
List<String> sheet1Headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 20);
|
||||
if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, sheet1Headers)) {
|
||||
throw new BadRequestException("大物第1个Sheet目录模板不正确,请确认模板信息");
|
||||
}
|
||||
|
||||
List<String> requiredColumns2 = Collections.singletonList("C/NO.");
|
||||
List<String> sheet2Headers = FastExcelUtil.readHeadContent(file, requiredColumns2, 1, 20);
|
||||
if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns2, sheet2Headers)) {
|
||||
throw new BadRequestException("大物第2个Sheet目录模板不正确,请确认模板信息");
|
||||
}
|
||||
|
||||
} else if ("template3".equals(templateType)) {
|
||||
List<String> requiredColumns = Arrays.asList("A-PROS\n" + "CASE NO", "KMT NO. PO&LN NO.", "PART NO.", "DESCRIPTION", "Q'TY", "JOB NO");
|
||||
List<String> headers = FastExcelUtil.readHeadContent(file, requiredColumns, 0, 4);
|
||||
if (!SmartStringUtil.containsAllIgnoreCase(requiredColumns, headers)) {
|
||||
throw new BadRequestException("发票箱单导入模板不正确,请确认模板信息");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//大物数据处理
|
||||
private static List<BaseImport> bigItemDataProcess(String invoiceNo, List<BaseImport> sheet1, List<BaseImport> sheet2) {
|
||||
|
||||
//处理sheet1数据
|
||||
List<BaseImport> sheet1Date = new ArrayList<>();
|
||||
for (BaseImport item : sheet1) {
|
||||
if (item.getBoi().toUpperCase().contains("TOTAL")) {
|
||||
break; // 不区分大小写 遇到TOTAL则停止添加
|
||||
}
|
||||
sheet1Date.add(item);
|
||||
}
|
||||
|
||||
//处理sheet2数据
|
||||
List<BaseImport> sheet2Date = new ArrayList<>();
|
||||
|
||||
// 用于记录上一个非 null 的 cNo 值
|
||||
String lastNonNullCNo = null;
|
||||
for (BaseImport item : sheet2) {
|
||||
String currentCNo = item.getCNo();
|
||||
|
||||
// 先检查是否遇到终止标志
|
||||
if (currentCNo != null && currentCNo.toUpperCase().contains("TOTAL")) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentCNo == null) {
|
||||
// 处理空值:使用最近的非空值填充
|
||||
if (lastNonNullCNo != null) {
|
||||
item.setCNo(lastNonNullCNo);
|
||||
}
|
||||
} else {
|
||||
// 更新最近的非空值记录
|
||||
lastNonNullCNo = currentCNo;
|
||||
}
|
||||
|
||||
// 统一添加元素到列表(排除TOTAL情况)
|
||||
sheet2Date.add(item);
|
||||
}
|
||||
|
||||
if (sheet1Date.size() != sheet2Date.size()) {
|
||||
throw new BadRequestException("大物模板中数据行数不一致");
|
||||
}
|
||||
|
||||
List<BaseImport> dataList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < sheet1Date.size(); i++) {
|
||||
BaseImport t1 = sheet1Date.get(i);//sheet1中的列数据
|
||||
|
||||
BaseImport t2 = sheet2Date.get(i);//sheet2中的列数据
|
||||
|
||||
BaseImport baseImport = BaseImport.builder()
|
||||
.invoiceNo(invoiceNo)
|
||||
.boi(t1.getBoi())
|
||||
.poNo(t1.getPoNo())
|
||||
.partNo(t1.getPartNo())
|
||||
.description(t1.getDescription())
|
||||
.qty(t1.getQty())
|
||||
.cNo(t2.getCNo())
|
||||
.build();
|
||||
dataList.add(baseImport);
|
||||
|
||||
}
|
||||
return dataList;
|
||||
}
|
||||
|
||||
//发票箱单数据处理
|
||||
private static List<BaseImport> invoicePackingDataProcess(String invoiceNo, List<invoicePackingImport> sheetData1) {
|
||||
List<BaseImport> dataList = new ArrayList<>();
|
||||
for (invoicePackingImport data : sheetData1) {
|
||||
if (data.getCNo() == null) {
|
||||
break;
|
||||
}
|
||||
BaseImport baseImport = BaseImport.builder()
|
||||
.invoiceNo(invoiceNo)
|
||||
.boi(data.getBoi())
|
||||
.poNo(data.getPoNo())
|
||||
.partNo(data.getPartNo())
|
||||
.description(data.getDescription())
|
||||
.qty(data.getQty())
|
||||
.cNo(data.getCNo())
|
||||
.build();
|
||||
dataList.add(baseImport);
|
||||
}
|
||||
return dataList;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,43 +15,28 @@
|
|||
*/
|
||||
package com.youchain.basicdata.service.impl;
|
||||
|
||||
import cn.hutool.poi.excel.ExcelReader;
|
||||
import cn.hutool.poi.excel.ExcelUtil;
|
||||
import cn.idev.excel.FastExcel;
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool;
|
||||
import com.youchain.basicdata.domain.*;
|
||||
import com.youchain.basicdata.repository.*;
|
||||
import com.youchain.basicdata.service.*;
|
||||
import com.youchain.basicdata.service.dto.AreaDto;
|
||||
import com.youchain.basicdata.service.dto.AreaQueryCriteria;
|
||||
import com.youchain.basicdata.service.mapstruct.AreaMapper;
|
||||
import com.youchain.basicdata.service.mapstruct.PointMapper;
|
||||
import com.youchain.businessdata.domain.*;
|
||||
import com.youchain.businessdata.inputJson.imports.AsnImportForm;
|
||||
import com.youchain.businessdata.inputJson.imports.BaseImport;
|
||||
import com.youchain.businessdata.repository.*;
|
||||
import com.youchain.businessdata.service.*;
|
||||
import com.youchain.config.FileProperties;
|
||||
import com.youchain.exception.BadRequestException;
|
||||
import com.youchain.modules.system.domain.DictDetail;
|
||||
import com.youchain.modules.system.repository.DictDetailRepository;
|
||||
import com.youchain.modules.system.repository.DictRepository;
|
||||
import com.youchain.utils.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -431,186 +416,4 @@ public class ImportDataServiceImpl implements ImportDataService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void importAsn(MultipartFile file, String templateType) {
|
||||
|
||||
// 验证模板类型是否有效
|
||||
if (!isValidTemplateType(templateType)) {
|
||||
throw new BadRequestException("无效的模板类型: " + templateType);
|
||||
}
|
||||
|
||||
List<AsnImportForm> dataList;
|
||||
try {
|
||||
dataList = FastExcel.read(file.getInputStream()).head(AsnImportForm.class)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(dataList)) {
|
||||
throw new BadRequestException("数据为空");
|
||||
}
|
||||
|
||||
// 根据模板类型进行不同的处理
|
||||
switch (templateType) {
|
||||
case "template1":
|
||||
template1Import(dataList);
|
||||
break;
|
||||
case "template2":
|
||||
template2Import(dataList);
|
||||
break;
|
||||
case "template3":
|
||||
template3Import(dataList);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("不支持的模板类型: " + templateType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证模板类型是否有效
|
||||
*/
|
||||
private boolean isValidTemplateType(String templateType) {
|
||||
return "template1".equals(templateType) ||
|
||||
"template2".equals(templateType) ||
|
||||
"template3".equals(templateType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理标准导入
|
||||
*/
|
||||
private void template1Import(List<AsnImportForm> dataList) {
|
||||
// TODO: 实现标准导入逻辑
|
||||
log.info("处理标准导入,数据条数: {}", dataList.size());
|
||||
|
||||
//获取文件中所有的托盘号
|
||||
List<String> codes = dataList.stream().map(AsnImportForm::getCNo).collect(Collectors.toList());
|
||||
|
||||
//验证托盘号
|
||||
validateAsn(codes);
|
||||
|
||||
//获取文件中所有的品番
|
||||
List<String> itemCodes = dataList.stream().map(AsnImportForm::getPartNo).collect(Collectors.toList());
|
||||
|
||||
//验证品番
|
||||
Map<String, Item> exitItemMap = validateItem(itemCodes);
|
||||
|
||||
//库区
|
||||
Area area=areaService.findByCode(BaseStatus.DEFAULT_AREA);
|
||||
|
||||
//单据类型
|
||||
BillType billType=billTypeRepository.findByName(BaseStatus.RK);
|
||||
|
||||
Map<String, Asn> asnMap = new HashMap<>();
|
||||
List<Asn> insertToAsn=new ArrayList<>();//批量新增ASN
|
||||
List<AsnDetail> insertToAsnDetail=new ArrayList<>();
|
||||
for (AsnImportForm data : dataList) {
|
||||
String relatedBill1 = data.getInvoiceNo();//发票号
|
||||
String propC2 = data.getBoi();//税别
|
||||
String po = data.getPoNo();//订单号
|
||||
String itemCode = data.getPartNo();//品番
|
||||
String remark = data.getDescription();//描述
|
||||
Double orderQty = data.getQty();//数量
|
||||
String code = data.getCNo();//托盘号
|
||||
|
||||
//品番
|
||||
Item item=exitItemMap.get(itemCode);
|
||||
|
||||
Asn asn = null;
|
||||
if (asnMap.containsKey(code)) {
|
||||
asn=asnMap.get(code);
|
||||
asn.setOrderQuantity(asn.getOrderQuantity() + orderQty);
|
||||
}else{
|
||||
|
||||
asn = Asn.builder()
|
||||
.relatedBill1(relatedBill1)
|
||||
.code(code)
|
||||
.area(area)
|
||||
.status(BizStatus.OPEN)
|
||||
.orderDate(new Timestamp(new Date().getTime()))
|
||||
.orderQuantity(orderQty)
|
||||
.billType(billType)
|
||||
.receivedQuantity(0d)
|
||||
.putawayQuantity(0d)
|
||||
.build();
|
||||
asnMap.put(code, asn);
|
||||
insertToAsn.add(asn);
|
||||
}
|
||||
|
||||
AsnDetail asnDetail = AsnDetail.builder()
|
||||
.propC2(propC2)
|
||||
.po(po)
|
||||
.item(item)
|
||||
.remark( remark)
|
||||
.orderQty(orderQty)
|
||||
.asn(asn)
|
||||
.status(BizStatus.OPEN)
|
||||
.receivedQty(0d)
|
||||
.moveQty(0d)
|
||||
.putQty(0d)
|
||||
.weight(0d)
|
||||
.volume(0d)
|
||||
.dept(item.getDept())
|
||||
.build();
|
||||
insertToAsnDetail.add(asnDetail);
|
||||
}
|
||||
if(!insertToAsn.isEmpty()){
|
||||
asnRepository.saveAll(insertToAsn);
|
||||
}
|
||||
if(!insertToAsnDetail.isEmpty()){
|
||||
asnDetailRepository.saveAll(insertToAsnDetail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理大物导入
|
||||
*/
|
||||
private void template2Import(List<AsnImportForm> dataList) {
|
||||
// TODO: 实现大物导入逻辑
|
||||
log.info("处理大物导入,数据条数: {}", dataList.size());
|
||||
// 这里添加大物导入的具体业务逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理发票箱单导入
|
||||
*/
|
||||
private void template3Import(List<AsnImportForm> dataList) {
|
||||
// TODO: 实现发票箱单导入逻辑
|
||||
log.info("处理发票箱单导入,数据条数: {}", dataList.size());
|
||||
// 这里添加发票箱单导入的具体业务逻辑
|
||||
}
|
||||
|
||||
//验证品番
|
||||
private Map<String, Item> validateItem(List<String> itemCodes) {
|
||||
Map<String, Item> exitItemMap = itemService.queryByItemCodesToMap(itemCodes);
|
||||
if (exitItemMap.isEmpty()) {
|
||||
throw new BadRequestException(itemCodes + "品番不存在或已失效");
|
||||
}
|
||||
|
||||
List<String> existingItemCodes = new ArrayList<>(exitItemMap.keySet());
|
||||
|
||||
// 获取两个集合的非交集说明品番不存在或失效,直接提示
|
||||
List<String> difference = SmartStringUtil.getDifference(itemCodes, existingItemCodes);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(difference)) {
|
||||
throw new BadRequestException(difference + "品番不存在或已失效");
|
||||
}
|
||||
return exitItemMap;
|
||||
}
|
||||
|
||||
//验证ASN单号
|
||||
private void validateAsn(List<String> codes) {
|
||||
Map<String, Asn> exitAsnMap = asnService.queryByasnCodesToMap(codes);
|
||||
List<String> existingAsnCodes = new ArrayList<>(exitAsnMap.keySet());
|
||||
// 获取两个集合的交集说明ASN重复导入了,直接提示
|
||||
List<String> difference = SmartStringUtil.getIntersection(codes, existingAsnCodes);
|
||||
if (CollectionUtils.isNotEmpty(difference)) {
|
||||
//去重difference
|
||||
difference = difference.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
throw new BadRequestException(difference + "托盘号已导入,请勿重复导入");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,21 @@
|
|||
package com.youchain.businessdata.inputJson.imports;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 基础导入
|
||||
* @author: youzhi.gao
|
||||
* @date: 2020-04-01 15:01
|
||||
*/
|
||||
@Data
|
||||
public class AsnImportForm {
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class BaseImport {
|
||||
@ExcelProperty("INVOICE NO.")
|
||||
private String invoiceNo;
|
||||
|
||||
|
|
@ -20,7 +31,7 @@ public class AsnImportForm {
|
|||
@ExcelProperty("DESCRIPTION")
|
||||
private String description;
|
||||
|
||||
@ExcelProperty("Q'TY")
|
||||
@ExcelProperty("Q'TY \n" +"(PCS)")
|
||||
private Double qty;
|
||||
|
||||
@ExcelProperty("C/NO.")
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.youchain.businessdata.inputJson.imports;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @description: 发票箱单导入
|
||||
* @author: youzhi.gao
|
||||
* @date: 2020-04-01 15:01
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class invoicePackingImport {
|
||||
|
||||
@ExcelIgnore
|
||||
private String invoiceNo;
|
||||
|
||||
@ExcelProperty("JOB NO")
|
||||
private String boi;
|
||||
|
||||
@ExcelProperty("KMT NO. PO&LN NO.")
|
||||
private String poNo;
|
||||
|
||||
@ExcelProperty("PART NO.")
|
||||
private String partNo;
|
||||
|
||||
@ExcelProperty("DESCRIPTION")
|
||||
private String description;
|
||||
|
||||
@ExcelProperty("Q'TY")
|
||||
private Double qty;
|
||||
|
||||
@ExcelProperty("A-PROS\n" + "CASE NO")
|
||||
private String cNo;
|
||||
}
|
||||
|
|
@ -15,15 +15,10 @@
|
|||
*/
|
||||
package com.youchain.businessdata.service.impl;
|
||||
|
||||
import cn.idev.excel.FastExcel;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.youchain.basicdata.domain.Item;
|
||||
import com.youchain.businessdata.domain.Asn;
|
||||
import com.youchain.businessdata.domain.AsnDetail;
|
||||
import com.youchain.businessdata.inputJson.imports.AsnImportForm;
|
||||
import com.youchain.businessdata.repository.AsnDetailRepository;
|
||||
import com.youchain.businessdata.service.AsnDetailService;
|
||||
import com.youchain.exception.BadRequestException;
|
||||
import com.youchain.utils.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import com.youchain.businessdata.repository.AsnRepository;
|
||||
|
|
@ -33,18 +28,12 @@ import com.youchain.businessdata.service.dto.AsnQueryCriteria;
|
|||
import com.youchain.businessdata.service.mapstruct.AsnMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
package com.youchain.utils;
|
||||
|
||||
import cn.idev.excel.context.AnalysisContext;
|
||||
import cn.idev.excel.read.listener.ReadListener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class FastExcelCellListener implements ReadListener<Map<Integer, String>> {
|
||||
private final Map<String, String> cellValues = new HashMap<>();
|
||||
private final Set<String> targetCells = new HashSet<>();
|
||||
private boolean isCellRead = false;
|
||||
|
||||
/**
|
||||
* 添加目标单元格
|
||||
* @param row 行索引(从0开始)
|
||||
* @param col 列索引(从0开始)
|
||||
* @return 当前监听器实例,支持链式调用
|
||||
*/
|
||||
public FastExcelCellListener addTargetCell(int row, int col) {
|
||||
targetCells.add(row + "-" + col);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(Map<Integer, String> data, AnalysisContext context) {
|
||||
if (isCellRead) {
|
||||
return;
|
||||
}
|
||||
int currentRowIndex = context.readRowHolder().getRowIndex()-1;
|
||||
|
||||
for (String targetCell : targetCells) {
|
||||
String[] parts = targetCell.split("-");
|
||||
int targetRow = Integer.parseInt(parts[0]);
|
||||
int targetCol = Integer.parseInt(parts[1]);
|
||||
|
||||
if (currentRowIndex == targetRow-1) {
|
||||
String cellValue = data.get(targetCol-1);
|
||||
if (cellValue != null) {
|
||||
cellValues.put(targetCell, cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否所有目标单元格都已读取
|
||||
if (cellValues.size() == targetCells.size()) {
|
||||
isCellRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||
// 可根据需要添加逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定单元格的值
|
||||
* @param row 行索引(从0开始)
|
||||
* @param col 列索引(从0开始)
|
||||
* @return 单元格值
|
||||
*/
|
||||
public String getCellValue(int row, int col) {
|
||||
return cellValues.get(row + "-" + col);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有单元格值
|
||||
* @return 所有单元格值的Map
|
||||
*/
|
||||
public Map<String, String> getAllCellValues() {
|
||||
return new HashMap<>(cellValues);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,18 +9,21 @@ import cn.idev.excel.write.style.HorizontalCellStyleStrategy;
|
|||
|
||||
import com.youchain.exception.BadRequestException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.MediaTypeFactory;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToLongFunction;
|
||||
|
|
@ -98,16 +101,17 @@ public class FastExcelUtil {
|
|||
|
||||
/**
|
||||
* 大数据导出
|
||||
* @param response 响应
|
||||
* @param fileName 文件名
|
||||
* @param sheetName sheet名称
|
||||
* @param head 头
|
||||
*
|
||||
* @param response 响应
|
||||
* @param fileName 文件名
|
||||
* @param sheetName sheet名称
|
||||
* @param head 头
|
||||
* @param pageQueryFunction 查询方法
|
||||
* @param convertFunction 转换方法
|
||||
* @param idExtractor id提取方法
|
||||
* @param pageSize 每页大小
|
||||
* @param <T> 泛型
|
||||
* @param <E> 泛型
|
||||
* @param convertFunction 转换方法
|
||||
* @param idExtractor id提取方法
|
||||
* @param pageSize 每页大小
|
||||
* @param <T> 泛型
|
||||
* @param <E> 泛型
|
||||
*/
|
||||
public static <T, E> void batchExportExcel(HttpServletResponse response, String fileName, String sheetName, Class head, BiFunction<Long, Integer, List<T>> pageQueryFunction, Function<T, E> convertFunction, ToLongFunction<T> idExtractor, int pageSize) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
|
@ -141,4 +145,96 @@ public class FastExcelUtil {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从 Excel 文件中读取指定类型的数据
|
||||
*
|
||||
* @param file 上传的 Excel 文件
|
||||
* @param clazz 数据映射的类(如 Template1.class)
|
||||
* @param <T> 数据类型
|
||||
* @return 解析后的数据列表
|
||||
*/
|
||||
public static <T> List<T> readExcelData(MultipartFile file, Class<T> clazz, int sheetNo, int headRowNumber) {
|
||||
List<T> dataList;
|
||||
try {
|
||||
dataList = FastExcel.read(file.getInputStream()).head(clazz)
|
||||
.sheet(sheetNo)
|
||||
.headRowNumber(headRowNumber)
|
||||
.doReadSync();
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(dataList)) {
|
||||
throw new BadRequestException("数据为空");
|
||||
}
|
||||
return dataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取Excel文件中指定位置的单元格值
|
||||
*
|
||||
* @param file Excel文件
|
||||
* @param reader 指定行和列
|
||||
*/
|
||||
public static Map<String, String> readExcelCellValues(MultipartFile file, FastExcelCellListener reader) {
|
||||
try {
|
||||
FastExcel.read(file.getInputStream(), reader)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||||
}
|
||||
|
||||
return reader.getAllCellValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 Excel 文件头部的所有内容(第一行)
|
||||
*
|
||||
* @param inputStream Excel 文件输入流
|
||||
* @return 头部内容列表
|
||||
*/
|
||||
public static List<String> readHeadContent(InputStream inputStream, int sheetNo, int headRowNumber) {
|
||||
List<Object> data = FastExcel.read(inputStream)
|
||||
.sheet(sheetNo)
|
||||
.headRowNumber(headRowNumber)
|
||||
.doReadSync();
|
||||
// 检查是否有数据
|
||||
if (data.isEmpty()) {
|
||||
throw new BadRequestException("导入模板不正确,请确认模板信息");
|
||||
}
|
||||
Object firstRow = data.get(0);
|
||||
// 将头部内容转换为字符串列表
|
||||
if (firstRow instanceof Map) {
|
||||
Map<?, ?> rowMap = (Map<?, ?>) firstRow;
|
||||
return rowMap.values().stream()
|
||||
.map(value -> value == null ? "" : value.toString())
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
throw new BadRequestException("读取Excel头部内容格式异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Excel 文件头部是否包含指定的列名
|
||||
*
|
||||
* @param requiredColumns 必需的列名列表
|
||||
* @return 验证结果,true 表示包含所有必需列
|
||||
*/
|
||||
public static List<String> readHeadContent(MultipartFile file, List<String> requiredColumns, int sheetNo, int headRowNumber) {
|
||||
if (requiredColumns.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
//判断头部内容是否包含所有必填列
|
||||
return readHeadContent(file.getInputStream(), sheetNo, headRowNumber);
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException("数据格式存在问题,无法读取");
|
||||
}
|
||||
}
|
||||
|
||||
private static String readHeadContent(File file, int headRowNumber) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,9 @@ public class SmartStringUtil {
|
|||
.filter(code -> !existingLocationCodesSet.contains(code))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static boolean containsAllIgnoreCase(List<String> requiredColumns, List<String> headers) {
|
||||
Set<String> headersSet = new HashSet<>(headers);
|
||||
return headersSet.containsAll(requiredColumns);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,12 @@ spring:
|
|||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
|
||||
|
||||
jdbc:
|
||||
batch_size: 500
|
||||
batch_versioned_data: true
|
||||
order_inserts: true
|
||||
order_updates: true
|
||||
generate_statistics: false
|
||||
|
||||
redis:
|
||||
#数据库索引
|
||||
|
|
|
|||
Loading…
Reference in New Issue