shiyunteng
2026-08-26 b967d23ae6c1f7761feb3c5328e3b591df6a735d
platformx-business-finance-biz/src/main/java/com/by4cloud/platformx/business/service/impl/ContractServiceImpl.java
@@ -11,25 +11,28 @@
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.by4cloud.platformx.admin.api.entity.SysDept;
import com.by4cloud.platformx.admin.api.entity.SysDictItem;
import com.by4cloud.platformx.admin.api.feign.RemoteDeptService;
import com.by4cloud.platformx.admin.api.feign.RemoteDictService;
import com.by4cloud.platformx.business.api.feign.RemoteFlowProcessService;
import com.by4cloud.platformx.business.constant.FlowNameEnum;
import com.by4cloud.platformx.business.dto.*;
import com.by4cloud.platformx.business.entity.*;
import com.by4cloud.platformx.business.mapper.*;
import com.by4cloud.platformx.business.service.ContractService;
import com.by4cloud.platformx.business.utils.BatchInsertListener;
import com.by4cloud.platformx.business.utils.ItemGeneratorUtil;
import com.by4cloud.platformx.business.utils.NetworkFileToBase64;
import com.by4cloud.platformx.business.vo.ContractDetailVo;
import com.by4cloud.platformx.business.vo.ContractInvoiceVo;
import com.by4cloud.platformx.business.vo.ContractSlipVo;
import com.by4cloud.platformx.business.vo.FourAssociationPageVo;
import com.by4cloud.platformx.business.vo.*;
import com.by4cloud.platformx.common.core.util.R;
import com.by4cloud.platformx.common.data.datascope.DataScope;
import com.by4cloud.platformx.common.security.util.SecurityUtils;
@@ -45,6 +48,7 @@
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.OutputStream;
@@ -84,6 +88,7 @@
   private final ContractInvoiceMapper contractInvoiceMapper;
   private final StringRedisTemplate redisTemplate;
   private final ContractOutBoundMapper contractOutBoundMapper;
   private final RemoteDictService remoteDictService;
   @Value("${erp.ymj}")
   private String ymjErp;
@@ -1704,7 +1709,10 @@
      if (ObjUtil.isNotNull(addDTO.getDeliveryCycle())){
         contract.setExpirationDate(DateUtil.offsetDay(addDTO.getSignDate(),addDTO.getDeliveryCycle()));
      }
      if (ObjUtil.isNotNull(contract.getSignDate()) && ObjUtil.isNotNull(contract.getExpirationDate())) {
         Long bd = DateUtil.betweenDay(contract.getExpirationDate(), contract.getSignDate(), true);
         contract.setDeliveryCycle(bd.intValue());
      }
      contract.setContractNo(generateGYBContractNo());
      if (!StrUtil.equals(addDTO.getContractCategory(), "water_house")){
@@ -1899,6 +1907,7 @@
   /**
    * 判断付款阶段列表是否符合标准
    *
    * @param scheduleList 付款阶段列表
    * @return true 如果包含且仅包含标准的五个阶段,false 否则
    */
@@ -2215,7 +2224,6 @@
      map.put("buyerPart", contract.getBuyerPart());
      //标的物
      List<Map<String, Object>> items = new ArrayList<>();
      subjectMatterList.stream().forEach(contractSubjectMatter -> {
@@ -2381,4 +2389,220 @@
      }
      return R.ok();
   }
   @Override
   public R importContractMultiSheet(MultipartFile file) {
      // 用于收集所有 Sheet 解析后的最终结果(如果需要统一返回或处理)
      // 注意:如果数据量极大,建议在 Listener 中直接入库,不要全部加载到内存 List 中
      List<ContractExceVO> baseList = new ArrayList<>();
      List<ContractPaymentScheduleExceVO> scheduleList = new ArrayList<>();
      List<ContractSubjectMatterExceVO> matterList = new ArrayList<>();
      try (ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build()) {
         // 1. 构建第一个 Sheet 的读取配置
         ReadSheet sheet0 = EasyExcel.readSheet(0)
               .headRowNumber(1) // 表头行数
               .head(ContractExceVO.class) // 指定实体类
               .registerReadListener(new BatchInsertListener<ContractExceVO>(data -> {
                  baseList.addAll(data);
                  // 建议:在这里直接调用 service 保存,而不是 addTo List,除非数据量很小
                  // contractService.saveBaseInfo(data);
               }))
               .build();
         // 2. 构建第二个 Sheet 的读取配置
         ReadSheet sheet1 = EasyExcel.readSheet(1)
               .headRowNumber(1)
               .head(ContractSubjectMatterExceVO.class)
               .registerReadListener(new BatchInsertListener<ContractSubjectMatterExceVO>(data -> {
                  matterList.addAll(data);
                  // contractService.saveSchedule(data);
               }))
               .build();
         // 3. 构建第三个 Sheet 的读取配置
         ReadSheet sheet2 = EasyExcel.readSheet(2)
               .headRowNumber(1)
               .head(ContractPaymentScheduleExceVO.class) // 注意:这里你原代码写的是 ContractPaymentScheduleExceVO,但变量是 matterList,需确认实体类是否一致
               .registerReadListener(new BatchInsertListener<ContractPaymentScheduleExceVO>(data -> {
                  scheduleList.addAll(data);
                  // contractService.saveMatter(data);
               }))
               .build();
         // 4. 依次执行读取
         // 注意:excelReader.read(sheet) 会复用同一个输入流,所以必须用 try-with-resources 确保流管理正确
         excelReader.read(sheet0);
         excelReader.read(sheet1);
         excelReader.read(sheet2);
         // 后续处理 baseList, scheduleList, matterList
         log.info("主表数据量: " + baseList.size());
         log.info("进度表数据量: " + scheduleList.size());
         log.info("标的物数据量: " + matterList.size());
         List<Map<String, String>> errList = new ArrayList<>();
         for (int i = 0; i < baseList.size(); i++) {
            ContractExceVO contractExceVO = baseList.get(i);
            if (StrUtil.isBlank(contractExceVO.getContractNo())) {
               Map<String, String> map = new HashMap<String, String>();
               map.put("lineNum", "第" + (i + 1) + "行");
               map.put("errors", "合同编号为空");
               errList.add(map);
               continue;
            }
            BusinessCustomer bc = businessCustomerMapper.selectOne(Wrappers.<BusinessCustomer>lambdaQuery()
                  .eq(BusinessCustomer::getErpCompanyCode, contractExceVO.getBcErpCode()));
            if (ObjUtil.isNull(bc)) {
               Map<String, String> map = new HashMap<String, String>();
               map.put("lineNum", "第" + (i + 1) + "行");
               map.put("errors", "客户erp编码不存在");
               errList.add(map);
               continue;
            }
            R<List<SysDictItem>> rem = remoteDictService.getDictByType("economic_matters");
            if (!rem.isOk()) {
               return R.failed("经济事项字典查询异常");
            }
            List<SysDictItem> itemList = rem.getData();
            for (SysDictItem dictItem : itemList
            ) {
               if (StrUtil.equals(contractExceVO.getEconomicMatters(), dictItem.getLabel())) {
                  contractExceVO.setEconomicMatters(dictItem.getItemValue());
                  break;
               }
            }
            R<List<SysDictItem>> rsa = remoteDictService.getDictByType("supply_attribute");
            if (!rsa.isOk()) {
               return R.failed("供应属性字典查询异常");
            }
            for (SysDictItem dictItem : rsa.getData()
            ) {
               if (StrUtil.equals(contractExceVO.getSupplyAttribute().replace(" ", ""), dictItem.getLabel())) {
                  contractExceVO.setSupplyAttribute(dictItem.getItemValue());
                  break;
               }
            }
            R<List<SysDictItem>> rssa = remoteDictService.getDictByType("server_attribute");
            if (!rssa.isOk()) {
               return R.failed("服务属性字典查询异常");
            }
            for (SysDictItem dictItem : rssa.getData()
            ) {
               if (StrUtil.equals(contractExceVO.getContractAttribute().replace(" ", ""), dictItem.getLabel())) {
                  contractExceVO.setContractAttribute(dictItem.getItemValue());
                  break;
               }
            }
            R<List<SysDictItem>> ris = remoteDictService.getDictByType("industry_sector");
            if (!ris.isOk()) {
               return R.failed("服务属性字典查询异常");
            }
            for (SysDictItem dictItem : ris.getData()
            ) {
               if (StrUtil.equals(contractExceVO.getIndustryCode(), dictItem.getLabel())) {
                  contractExceVO.setIndustryCode(dictItem.getItemValue());
                  break;
               }
            }
            List<Contract> contractList = baseMapper.selectList(Wrappers.<Contract>lambdaQuery()
                  .eq(Contract::getContractNo, contractExceVO.getContractNo()));
            if (CollUtil.isNotEmpty(contractList)) {
               Map<String, String> map = new HashMap<String, String>();
               map.put("lineNum", "第" + (i + 1) + "行");
               map.put("errors", "合同编号已存在");
               errList.add(map);
               continue;
            }
            Contract contract = BeanUtil.copyProperties(contractExceVO, Contract.class);
            if (ObjUtil.isNotNull(contract.getSignDate()) && ObjUtil.isNotNull(contract.getExpirationDate())) {
               Long bd = DateUtil.betweenDay(contract.getExpirationDate(), contract.getSignDate(), true);
               contract.setDeliveryCycle(bd.intValue());
            }
            contract.setPartyA(bc.getCompanyName());
            contract.setPartyAId(bc.getId());
            contract.setPartyB(SecurityUtils.getUser().getCompName());
            contract.setPartyBId(SecurityUtils.getUser().getCompId());
            contract.setContractStatus(2);
            contract.setPaidAmount(ObjUtil.isNotNull(contract.getPaidAmount())?
                  contract.getPaidAmount():new BigDecimal("0"));
            contract.setBillingAmout(ObjUtil.isNotNull(contract.getBillingAmout())?
                  contract.getBillingAmout():new BigDecimal("0"));
            contract.setBillingStatus("1");
            baseMapper.insert(contract);
            BigDecimal remainAmount = ObjUtil.isNotNull(contract.getPaidAmount())?contract.getPaidAmount():new BigDecimal("0");
            List<ContractSubjectMatterExceVO> resultMatter = matterList.stream().filter(item ->
                  StrUtil.isNotBlank(item.getMaterialCode()) &&
                        StrUtil.equals(item.getContractNo(), contract.getContractNo())).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(resultMatter)) {
               for (ContractSubjectMatterExceVO subMatEx : resultMatter
               ) {
                  ContractSubjectMatter subjectMatter = BeanUtil.copyProperties(subMatEx, ContractSubjectMatter.class);
                  subjectMatter.setContractId(contract.getId());
                  subjectMatter.setDeliveredQuantity(StrUtil.isBlank(subMatEx.getDeliveredQuantity())
                        ? new BigDecimal("0") : new BigDecimal(subMatEx.getDeliveredQuantity()));
                  subjectMatter.setRemainingQuantity(subjectMatter.getQuantity().subtract(subjectMatter.getDeliveredQuantity()));
                  subjectMatter.setDeliveryStatus(subjectMatter.getRemainingQuantity().compareTo(subjectMatter.getQuantity()) == 0 ? 0
                        : subjectMatter.getRemainingQuantity().compareTo(new BigDecimal("0")) > 0 ? 1 : 2);
                  contractSubjectMatterMapper.insert(subjectMatter);
               }
            }
            List<ContractPaymentScheduleExceVO> resultSchedule = scheduleList.stream().filter(item ->
                  StrUtil.equals(item.getContractNo(), contract.getContractNo())).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(resultSchedule)) {
               for (int j = 0; j < resultSchedule.size(); j++) {
                  ContractPaymentSchedule schedule = new ContractPaymentSchedule();
                  schedule.setStageName(resultSchedule.get(j).getStageName());
                  schedule.setContractId(contract.getId());
                  schedule.setPaymentRatio(StrUtil.isBlank(resultSchedule.get(j).getPaymentRatio())
                        ? new BigDecimal("0") : new BigDecimal(resultSchedule.get(j).getPaymentRatio()));
                  schedule.setAgreedDays(StrUtil.isBlank(resultSchedule.get(j).getAgreedDays())
                        ? 0 : Integer.valueOf(resultSchedule.get(j).getPaymentRatio()));
                  schedule.setPlannedAmount(contract.getAmount().multiply(schedule.getPaymentRatio())
                        .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
                  schedule.setPaymentStatus(0);
                  schedule.setStageOrder(j + 1);
                  if (remainAmount.compareTo(new BigDecimal("0"))>0&&schedule.getPlannedAmount().compareTo(new BigDecimal("0"))>0){
                     remainAmount = remainAmount.subtract(schedule.getPlannedAmount());
                     if (remainAmount.compareTo(new BigDecimal("0"))>=0){
                        schedule.setPaymentStatus(2);
                        schedule.setActualAmount(schedule.getPlannedAmount());
                     }
                     if (remainAmount.compareTo(new BigDecimal("0"))<0){
                        schedule.setPaymentStatus(1);
                        schedule.setActualAmount(schedule.getPlannedAmount().add(remainAmount));
                     }
                  }
                  contractPaymentScheduleMapper.insert(schedule);
                  if (schedule.getStageName().equals("质保金")&&contract.getAmount().subtract(ObjUtil.isNotNull(contract.getPaidAmount())?
                        contract.getPaidAmount():new BigDecimal("0")).compareTo(new BigDecimal("0"))>0){
                     PaymentConfirm confirm = new PaymentConfirm();
                     confirm.setBusGuestId(contract.getPartyAId());
                     confirm.setBusGuestName(contract.getPartyA());
                     confirm.setBusinessType("质保金应收");
                     confirm.setConfirmTime(new Date());
                     confirm.setContractId(contract.getId());
                     confirm.setContractName(contract.getContractName());
                     confirm.setContractNo(contract.getContractNo());
                     confirm.setScheduleId(schedule.getId());
                     confirm.setScheduleName(schedule.getStageName());
                     confirm.setTransationAmount(contract.getAmount().subtract(ObjUtil.isNotNull(contract.getPaidAmount())?
                           contract.getPaidAmount():new BigDecimal("0")));
                     confirm.setReceivableAmount(confirm.getTransationAmount());
                     confirm.setTotalAmount(confirm.getTransationAmount().multiply(new BigDecimal("-1")));
                     paymentConfirmMapper.insert(confirm);
                  }
               }
            }
         }
         if (CollUtil.isNotEmpty(errList)) {
            return R.failed(errList);
         }
      } catch (IOException e) {
         e.printStackTrace();
         return R.failed("文件解析失败: " + e.getMessage());
      }
      return R.ok();
   }
}