基础导入、大物导入、发票箱单导入已完善
							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