package com.ruoyi.sales.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; 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.ruoyi.account.service.AccountIncomeService; import com.ruoyi.basic.mapper.CustomerMapper; import com.ruoyi.basic.mapper.ProductMapper; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.pojo.Customer; import com.ruoyi.basic.pojo.CustomerRegions; import com.ruoyi.basic.service.ICustomerRegionsService; import com.ruoyi.common.enums.FileNameType; import com.ruoyi.common.enums.SaleEnum; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.EnumUtil; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.security.LoginUser; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.other.mapper.TempFileMapper; import com.ruoyi.other.pojo.TempFile; import com.ruoyi.production.mapper.*; import com.ruoyi.production.pojo.ProcessRoute; import com.ruoyi.production.pojo.ProcessRouteItem; import com.ruoyi.production.service.ProductionProductMainService; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto; import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper; import com.ruoyi.quality.mapper.QualityInspectMapper; import com.ruoyi.sales.dto.*; import com.ruoyi.sales.mapper.*; import com.ruoyi.sales.pojo.*; import com.ruoyi.sales.service.ISalesLedgerProcessRouteService; import com.ruoyi.sales.service.ISalesLedgerProductProcessBindService; import com.ruoyi.sales.service.ISalesLedgerProductProcessService; import com.ruoyi.sales.service.ISalesLedgerService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; import org.jetbrains.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; /** * 销售台账Service业务层处理 * * @author ruoyi * @date 2025-05-08 */ @Service @RequiredArgsConstructor @Slf4j public class SalesLedgerServiceImpl extends ServiceImpl implements ISalesLedgerService { private static final String LOCK_PREFIX = "contract_no_lock:"; private static final long LOCK_WAIT_TIMEOUT = 10; // 锁等待超时时间(秒) private static final long LOCK_EXPIRE_TIME = 30; // 锁自动过期时间(秒) private final AccountIncomeService accountIncomeService; private final SalesLedgerMapper salesLedgerMapper; private final CustomerMapper customerMapper; private final SalesLedgerProductMapper salesLedgerProductMapper; private final SalesLedgerProductServiceImpl salesLedgerProductServiceImpl; private final CommonFileMapper commonFileMapper; private final TempFileMapper tempFileMapper; private final ReceiptPaymentMapper receiptPaymentMapper; private final ShippingInfoServiceImpl shippingInfoServiceImpl; private final CommonFileServiceImpl commonFileService; private final ShippingInfoMapper shippingInfoMapper; private final InvoiceLedgerMapper invoiceLedgerMapper; private final SalesLedgerSchedulingMapper salesLedgerSchedulingMapper; private final SalesLedgerWorkMapper salesLedgerWorkMapper; private final SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper; private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper; private final InvoiceRegistrationMapper invoiceRegistrationMapper; private final ProductOrderMapper productOrderMapper; private final ProcessRouteMapper processRouteMapper; private final ProductProcessRouteMapper productProcessRouteMapper; private final ProcessRouteItemMapper processRouteItemMapper; private final ProductProcessRouteItemMapper productProcessRouteItemMapper; private final ProductWorkOrderMapper productWorkOrderMapper; private final ProductionProductMainMapper productionProductMainMapper; private final ProductionProductOutputMapper productionProductOutputMapper; private final ProductionProductInputMapper productionProductInputMapper; private final QualityInspectMapper qualityInspectMapper; private final RedisTemplate redisTemplate; private final ISalesLedgerProductProcessService salesLedgerProductProcessService; private final ISalesLedgerProductProcessBindService salesLedgerProductProcessBindService; private final ISalesLedgerProcessRouteService salesLedgerProcessRouteService; @Autowired private SysDeptMapper sysDeptMapper; @Value("${file.upload-dir}") private String uploadDir; @Autowired private ProductModelMapper productModelMapper; @Autowired private ProductMapper productMapper; @Autowired private ProductStructureMapper productStructureMapper; @Autowired private ProductionProductMainService productionProductMainService; @Autowired private PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper; ; @Autowired private SysUserMapper sysUserMapper; private final ICustomerRegionsService customerRegionsService; @Override public List selectSalesLedgerList(SalesLedgerDto salesLedgerDto) { return salesLedgerMapper.selectSalesLedgerList(salesLedgerDto); } public List getSalesLedgerProductListByRelateId(Long relateId, SaleEnum type) { LambdaQueryWrapper productWrapper = new LambdaQueryWrapper<>(); productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, relateId); productWrapper.eq(SalesLedgerProduct::getType, type.getCode()); return salesLedgerProductMapper.selectList(productWrapper); } @Override public List getSalesLedgerProductListByIds(@Nullable List relateIds, SaleEnum type) { if (CollectionUtils.isEmpty(relateIds)) { return Collections.emptyList(); } LambdaQueryWrapper productWrapper = new LambdaQueryWrapper<>(); productWrapper.in(SalesLedgerProduct::getId, relateIds); productWrapper.eq(SalesLedgerProduct::getType, type.getCode()); List salesLedgerProducts = salesLedgerProductMapper.selectList(productWrapper); if (type.equals(SaleEnum.PURCHASE)) { // 查询退货信息 List productIds = salesLedgerProducts.stream().map(SalesLedgerProduct::getId).collect(Collectors.toList()); List groupListByProductIds = new ArrayList<>(); if (CollectionUtils.isNotEmpty(productIds)) { groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds); } Map returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getSalesLedgerProductId, SimpleReturnOrderGroupDto::getSumReturnQuantity)); salesLedgerProducts.forEach(item -> { BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getId(), BigDecimal.ZERO); item.setReturnQuality(returnQuality); item.setAvailableQuality(item.getQuantity().subtract(returnQuality)); }); } return salesLedgerProducts; } @Override public SalesLedgerDto getSalesLedgerWithProducts(SalesLedgerDto salesLedgerDto) { // 1. 查询主表 SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerDto.getId()); if (salesLedger == null) { throw new BaseException("台账不存在"); } // 2. 查询子表 LambdaQueryWrapper productWrapper = new LambdaQueryWrapper<>(); productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId()); productWrapper.eq(SalesLedgerProduct::getType, 1); List products = salesLedgerProductMapper.selectList(productWrapper); for (SalesLedgerProduct product : products) { product.setOriginalNoInvoiceNum(product.getNoInvoiceNum()); // 提供临时未开票数,未开票金额供前段计算 product.setTempnoInvoiceAmount(product.getNoInvoiceAmount()); product.setTempNoInvoiceNum(product.getNoInvoiceNum()); product.setRegister(SecurityUtils.getLoginUser().getUser().getNickName()); product.setRegisterDate(LocalDateTime.now()); // 发货信息 ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper() .eq(ShippingInfo::getSalesLedgerProductId, product.getId()) .orderByDesc(ShippingInfo::getCreateTime) .last("limit 1")); if (shippingInfo != null) { product.setShippingStatus(shippingInfo.getStatus()); } // 加工明细,先查bind表获取该产品关联的工序及数量 List bindList = salesLedgerProductProcessBindService.list( new LambdaQueryWrapper() .eq(SalesLedgerProductProcessBind::getSalesLedgerProductId, product.getId())); if (!bindList.isEmpty()) { List processIds = bindList.stream() .map(SalesLedgerProductProcessBind::getSalesLedgerProductProcessId) .collect(Collectors.toList()); Map processQuantityMap = bindList.stream() .collect(Collectors.toMap( SalesLedgerProductProcessBind::getSalesLedgerProductProcessId, SalesLedgerProductProcessBind::getQuantity, (a, b) -> a)); List processList = salesLedgerProductProcessService.listByIds(processIds); processList.forEach(p -> p.setQuantity(processQuantityMap.get(p.getId()))); product.setSalesProductProcessList(processList); } } // 3.查询上传文件 LambdaQueryWrapper salesLedgerFileWrapper = new LambdaQueryWrapper<>(); salesLedgerFileWrapper.eq(CommonFile::getCommonId, salesLedger.getId()) .eq(CommonFile::getType, FileNameType.SALE.getValue()); List salesLedgerFiles = commonFileMapper.selectList(salesLedgerFileWrapper); // 4. 转换 DTO SalesLedgerDto resultDto = new SalesLedgerDto(); BeanUtils.copyProperties(salesLedger, resultDto); if (!products.isEmpty()) { resultDto.setHasChildren(true); resultDto.setProductData(products); resultDto.setSalesLedgerFiles(salesLedgerFiles); } return resultDto; } @Override public List> getSalesNo() { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.select(SalesLedger::getId, SalesLedger::getSalesContractNo, SalesLedger::getProjectName); // 获取原始查询结果 List> result = salesLedgerMapper.selectMaps(queryWrapper); // 将下划线命名转换为驼峰命名 return result.stream().map(map -> map.entrySet().stream() .collect(Collectors.toMap( entry -> underlineToCamel(entry.getKey()), Map.Entry::getValue)) ).collect(Collectors.toList()); } @Override public BigDecimal getContractAmount() { LocalDate now = LocalDate.now(); YearMonth currentMonth = YearMonth.from(now); // 创建LambdaQueryWrapper LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay()) // 大于等于本月第一天 .lt(SalesLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 小于下月第一天 // 执行查询并计算总和 List salesLedgers = salesLedgerMapper.selectList(queryWrapper); BigDecimal totalContractAmount = salesLedgers.stream() .map(SalesLedger::getContractAmount) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); return totalContractAmount; } @Override public List getTopFiveList() { // 查询原始数据 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.select(SalesLedger::getCustomerId, SalesLedger::getCustomerName, SalesLedger::getContractAmount) .orderByDesc(SalesLedger::getContractAmount); List records = salesLedgerMapper.selectList(queryWrapper); // 按客户ID分组并聚合金额 Map groupedMap = new LinkedHashMap<>(); // 使用LinkedHashMap保持排序 for (SalesLedger record : records) { groupedMap.computeIfAbsent(record.getCustomerId(), k -> new GroupedCustomer(record.getCustomerId(), record.getCustomerName())) .addAmount(record.getContractAmount()); } // 转换为结果列表并取前5 return groupedMap.values().stream() .sorted(Comparator.comparing(GroupedCustomer::getTotalAmount).reversed()) .limit(5) .map(customer -> { Map result = new HashMap<>(); result.put("customerId", customer.getCustomerId()); result.put("customerName", customer.getCustomerName()); result.put("totalAmount", customer.getTotalAmount()); return result; }) .collect(Collectors.toList()); } @Override public List getAmountHalfYear(Integer type) { LocalDate now = LocalDate.now(); List result = new ArrayList<>(); for (int i = 5; i >= 0; i--) { YearMonth yearMonth = YearMonth.from(now.minusMonths(i)); LocalDateTime startTime = yearMonth.atDay(1).atStartOfDay(); LocalDateTime endTime = yearMonth.atEndOfMonth().atTime(23, 59, 59); // 回款金额 LambdaQueryWrapper receiptPaymentQuery = new LambdaQueryWrapper<>(); receiptPaymentQuery .ge(ReceiptPayment::getCreateTime, startTime) .le(ReceiptPayment::getCreateTime, endTime); List receiptPayments = receiptPaymentMapper.selectList(receiptPaymentQuery); BigDecimal receiptAmount = receiptPayments.stream() .map(ReceiptPayment::getReceiptPaymentAmount) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); // 开票金额 LambdaQueryWrapper invoiceLedgerQuery = new LambdaQueryWrapper<>(); invoiceLedgerQuery .ge(InvoiceLedger::getCreateTime, startTime) .le(InvoiceLedger::getCreateTime, endTime); List invoiceLedgers = invoiceLedgerMapper.selectList(invoiceLedgerQuery); BigDecimal invoiceAmount = invoiceLedgers.stream() .map(InvoiceLedger::getInvoiceTotal) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); MonthlyAmountDto dto = new MonthlyAmountDto(); dto.setMonth(yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))); dto.setReceiptAmount(receiptAmount); dto.setInvoiceAmount(invoiceAmount); result.add(dto); } return result; } @Override public IPage selectSalesLedgerListPage(Page page, SalesLedgerDto salesLedgerDto) { return salesLedgerMapper.selectSalesLedgerListPage(page, salesLedgerDto); } @Override @Transactional(rollbackFor = Exception.class) public AjaxResult importData(MultipartFile file) { LoginUser loginUser = SecurityUtils.getLoginUser(); try { InputStream inputStream = file.getInputStream(); ExcelUtil salesLedgerImportDtoExcelUtil = new ExcelUtil<>(SalesLedgerImportDto.class); Map> stringListMap = salesLedgerImportDtoExcelUtil.importExcelMultiSheet(Arrays.asList("销售台账数据", "销售产品数据"), inputStream, 0); if (CollectionUtils.isEmpty(stringListMap)) return AjaxResult.error("销售表格为空!"); // 业务层合并 List salesLedgerImportDtoList = stringListMap.get("销售台账数据"); if (CollectionUtils.isEmpty(salesLedgerImportDtoList)) return AjaxResult.error("销售台账数据为空!"); List salesLedgerProductImportDtoList = stringListMap.get("销售产品数据"); if (CollectionUtils.isEmpty(salesLedgerProductImportDtoList)) return AjaxResult.error("销售产品数据为空!"); // 客户数据 List customers = customerMapper.selectList(new LambdaQueryWrapper().in(Customer::getCustomerName, salesLedgerImportDtoList.stream().map(SalesLedgerImportDto::getCustomerName).collect(Collectors.toList()))); // // 规格型号数据 // List productModels = productModelMapper.selectList(new LambdaQueryWrapper().in(ProductModel::getModel, // salesLedgerProductImportDtoList.stream().map(SalesLedgerImportDto::getSpecificationModel).collect(Collectors.toList()))); // // 产品大类数据 // List productList = productMapper.selectList(new LambdaQueryWrapper().in(Product::getProductName, // salesLedgerProductImportDtoList.stream().map(SalesLedgerImportDto::getProductCategory).collect(Collectors.toList()))); List> list = productModelMapper.getProductAndModelList(); // 录入人数据 List sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper().in(SysUser::getNickName, salesLedgerImportDtoList.stream().map(SalesLedgerImportDto::getEntryPerson).collect(Collectors.toList()))); for (SalesLedgerImportDto salesLedgerImportDto : salesLedgerImportDtoList) { SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper() .eq(SalesLedger::getSalesContractNo, salesLedgerImportDto.getSalesContractNo()) .last("LIMIT 1")); if (salesLedger1 != null) { continue; } SalesLedger salesLedger = new SalesLedger(); BeanUtils.copyProperties(salesLedgerImportDto, salesLedger); salesLedger.setExecutionDate(DateUtils.toLocalDate(salesLedgerImportDto.getExecutionDate())); // 通过客户名称查询客户ID,客户合同号 salesLedger.setCustomerId(customers.stream() .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName())) .findFirst() .map(Customer::getId) .orElse(null)); salesLedger.setCustomerContractNo(customers.stream() .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName())) .findFirst() .map(Customer::getTaxpayerIdentificationNumber) .orElse(null)); Long aLong = sysUsers.stream() .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getEntryPerson())) .findFirst() .map(SysUser::getUserId) .orElse(null); if (aLong == null) throw new RuntimeException("录入人:" + salesLedger.getEntryPerson() + ",无对应用户!"); salesLedger.setEntryPerson(aLong.toString()); // 销售产品数据绑定,通过销售单号获取对应销售产品数据 List salesLedgerProductImportDtos = salesLedgerProductImportDtoList.stream() .filter(salesLedgerProductImportDto -> salesLedgerProductImportDto.getSalesContractNo().equals(salesLedger.getSalesContractNo())) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(salesLedgerProductImportDtos)) throw new RuntimeException("销售单号:" + salesLedgerImportDto.getSalesContractNo() + ",无对应产品数据!"); salesLedger.setContractAmount(salesLedgerProductImportDtos.stream() .map(SalesLedgerProductImportDto::getTaxInclusiveTotalPrice) .reduce(BigDecimal.ZERO, BigDecimal::add)); salesLedgerMapper.insert(salesLedger); for (SalesLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) { SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct(); BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct); salesLedgerProduct.setSalesLedgerId(salesLedger.getId()); salesLedgerProduct.setType(1); // 计算不含税总价 salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP)); salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity()); salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxExclusiveTotalPrice()); list.stream() .filter(map -> map.get("productName").equals(salesLedgerProduct.getProductCategory()) && map.get("model").equals(salesLedgerProduct.getSpecificationModel())) .findFirst() .ifPresent(map -> { salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString())); salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString())); }); // salesLedgerProduct.setProductId(productList.stream() // .filter(product -> product.getProductName().equals(salesLedgerProduct.getProductCategory())) // .findFirst() // .map(Product::getId) // .orElse(null)); // salesLedgerProduct.setProductModelId(productModels.stream() // .filter(productModel -> productModel.getModel().equals(salesLedgerProduct.getSpecificationModel())) // .findFirst() // .map(ProductModel::getId) // .orElse(null)); salesLedgerProduct.setRegister(loginUser.getNickName()); salesLedgerProduct.setRegisterDate(LocalDateTime.now()); salesLedgerProduct.setApproveStatus(0); salesLedgerProduct.setPendingInvoiceTotal(salesLedgerProductImportDto.getTaxInclusiveTotalPrice()); salesLedgerProductMapper.insert(salesLedgerProduct); // 添加生产数据 salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct); } } return AjaxResult.success("导入成功"); } catch (Exception e) { e.printStackTrace(); } return AjaxResult.success("导入失败"); } @Override public List getSalesLedgerWithProductsLoss(Long salesLedgerId) { List lossProductModelDtos = salesLedgerProductMapper.selectProductBomStructure(salesLedgerId); return lossProductModelDtos; } @Override public IPage listSalesLedger(SalesLedgerDto salesLedgerDto, Page page) { IPage salesLedgerDtoIPage = salesLedgerMapper.listSalesLedgerAndShipped(page, salesLedgerDto); for (SalesLedgerDto salesLedger : salesLedgerDtoIPage.getRecords()) { LambdaQueryWrapper productWrapper = new LambdaQueryWrapper<>(); productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId()); productWrapper.eq(SalesLedgerProduct::getType, 1); List products = salesLedgerProductMapper.selectList(productWrapper); for (SalesLedgerProduct product : products) { product.setOriginalNoInvoiceNum(product.getNoInvoiceNum()); // 提供临时未开票数,未开票金额供前段计算 product.setTempnoInvoiceAmount(product.getNoInvoiceAmount()); product.setTempNoInvoiceNum(product.getNoInvoiceNum()); product.setRegister(SecurityUtils.getLoginUser().getUser().getNickName()); product.setRegisterDate(LocalDateTime.now()); // 发货信息 ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper() .eq(ShippingInfo::getSalesLedgerProductId, product.getId()) .orderByDesc(ShippingInfo::getCreateTime) .last("limit 1")); product.setShippingCarNumber(shippingInfo.getShippingCarNumber()); product.setShippingDate(shippingInfo.getShippingDate()); if (shippingInfo != null) { product.setShippingStatus(shippingInfo.getStatus()); } } // 过滤只保留发货记录 products = products.stream().filter(product -> "已发货".equals(product.getShippingStatus())).collect(Collectors.toList()); if (!products.isEmpty()) { salesLedger.setHasChildren(true); salesLedger.setProductData(products); } } return salesLedgerDtoIPage; } @Override @Transactional(rollbackFor = Exception.class) public void saleProcessBind(SalesLedgerProcessRoute salesLedgerProcessRoute) { if (salesLedgerProcessRoute == null) { throw new ServiceException("绑定失败,数据不能为空"); } SalesLedger salesLedger = baseMapper.selectById(salesLedgerProcessRoute.getSalesLedgerId()); if (salesLedger == null) { throw new ServiceException("绑定失败,销售订单不存在"); } ProcessRoute processRoute = processRouteMapper.selectById(salesLedgerProcessRoute.getProcessRouteId()); if (processRoute == null) { throw new ServiceException("绑定失败,工艺路线不存在"); } // 清除已绑定的数据 salesLedgerProcessRouteService.remove(new LambdaQueryWrapper().eq(SalesLedgerProcessRoute::getSalesLedgerId, salesLedger.getId())); // 将数据迁移到sales_ledger_process_route List routeItems = processRouteItemMapper.selectList(new LambdaQueryWrapper().eq(ProcessRouteItem::getRouteId, processRoute.getId())); SalesLedgerProcessRoute ledgerProcessRoute; List salesLedgerProcessRouteList = new ArrayList<>(); for (ProcessRouteItem routeItem : routeItems) { ledgerProcessRoute = new SalesLedgerProcessRoute(); ledgerProcessRoute.setProcessRouteId(processRoute.getId()); ledgerProcessRoute.setSalesLedgerId(salesLedger.getId()); ledgerProcessRoute.setProcessRouteItemId(routeItem.getId()); ledgerProcessRoute.setDragSort(routeItem.getDragSort()); salesLedgerProcessRouteList.add(ledgerProcessRoute); } salesLedgerProcessRouteService.saveBatch(salesLedgerProcessRouteList); } /** * 下划线命名转驼峰命名 */ private String underlineToCamel(String param) { if (param == null || "".equals(param.trim())) { return ""; } int len = param.length(); StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; i++) { char c = param.charAt(i); if (c == '_') { if (++i < len) { sb.append(Character.toUpperCase(param.charAt(i))); } } else { sb.append(Character.toLowerCase(c)); } } return sb.toString(); } @Override @Transactional(rollbackFor = Exception.class) public int deleteSalesLedgerByIds(Long[] ids) { List idList = Arrays.stream(ids) .filter(Objects::nonNull) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(idList)) { return 0; } // 删除销售管理数据 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.in(SalesLedgerProduct::getSalesLedgerId, idList) .select(SalesLedgerProduct::getId); List products = salesLedgerProductMapper.selectList(queryWrapper); List productIds = products.stream() .map(SalesLedgerProduct::getId) .collect(Collectors.toList()); //删除生产数据 salesLedgerProductServiceImpl.deleteProductionData(productIds); // 批量删除产品子表 if (!productIds.isEmpty()) { salesLedgerProductMapper.deleteBatchIds(productIds); } // 清除产品的加工 salesLedgerProductProcessBindService.remove(new LambdaQueryWrapper().in(SalesLedgerProductProcessBind::getSalesLedgerProductId, productIds)); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.in(InvoiceRegistrationProduct::getSalesLedgerId, idList); List invoiceRegistrationProducts = invoiceRegistrationProductMapper.selectList(wrapper); List invoiceLedgerIds = new ArrayList<>(); if (CollectionUtils.isNotEmpty(invoiceRegistrationProducts)) { LambdaQueryWrapper wrapperOne = new LambdaQueryWrapper<>(); wrapperOne.in(InvoiceLedger::getInvoiceRegistrationProductId, invoiceRegistrationProducts.stream().map(InvoiceRegistrationProduct::getId).collect(Collectors.toList())); List invoiceLedgers = invoiceLedgerMapper.selectList(wrapperOne); if (CollectionUtils.isNotEmpty(invoiceLedgers)) { invoiceLedgerIds = invoiceLedgers.stream().map(InvoiceLedger::getId).collect(Collectors.toList()); } invoiceLedgerMapper.delete(wrapperOne); } invoiceRegistrationProductMapper.delete(wrapper); LambdaQueryWrapper wrapperTwo = new LambdaQueryWrapper<>(); wrapperTwo.in(InvoiceRegistration::getSalesLedgerId, idList); invoiceRegistrationMapper.delete(wrapperTwo); if (CollectionUtils.isNotEmpty(invoiceLedgerIds)) { LambdaQueryWrapper wrapperTree = new LambdaQueryWrapper<>(); wrapperTree.in(ReceiptPayment::getInvoiceLedgerId, invoiceLedgerIds); receiptPaymentMapper.delete(wrapperTree); } // 删除发货台账记录 List shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper() .in(ShippingInfo::getSalesLedgerId, idList)); if (CollectionUtils.isNotEmpty(shippingInfos)) { shippingInfoServiceImpl.delete(shippingInfos.stream().map(ShippingInfo::getId).collect(Collectors.toList())); } // 删除附件表 commonFileService.deleteByBusinessIds(idList, FileNameType.SALE.getValue()); // 删除生产管控数据 //查询生产报工id ArrayList mainIdList = productionProductMainService.listMain(idList); if (CollectionUtils.isNotEmpty(mainIdList)) { mainIdList.stream().forEach(mainId -> { productionProductMainService.removeProductMain(mainId); }); } // 2. 再删除主表数据 return salesLedgerMapper.deleteBatchIds(idList); } @Override @Transactional(rollbackFor = Exception.class) public int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto) { try { // 1. 校验客户信息 Customer customer = customerMapper.selectById(salesLedgerDto.getCustomerId()); if (customer == null) { throw new BaseException("客户不存在"); } // 2. DTO转Entity SalesLedger salesLedger = convertToEntity(salesLedgerDto); salesLedger.setCustomerName(customer.getCustomerName()); salesLedger.setTenantId(customer.getTenantId()); // 3. 新增或更新主表 if (salesLedger.getId() == null) { String contractNo = generateSalesContractNo(); salesLedger.setSalesContractNo(contractNo); salesLedgerMapper.insert(salesLedger); } else { salesLedgerMapper.updateById(salesLedger); } // 4. 处理子表数据 List productList = salesLedgerDto.getProductData(); if (productList != null && !productList.isEmpty()) { handleSalesLedgerProducts(salesLedger.getId(), productList, EnumUtil.fromCode(SaleEnum.class, salesLedgerDto.getType())); updateMainContractAmount( salesLedger.getId(), productList, SalesLedgerProduct::getTaxInclusiveTotalPrice, salesLedgerMapper, SalesLedger.class ); } // 5. 迁移临时文件到正式目录 if (salesLedgerDto.getTempFileIds() != null && !salesLedgerDto.getTempFileIds().isEmpty()) { migrateTempFilesToFormal(salesLedger.getId(), salesLedgerDto.getTempFileIds()); } return 1; } catch (IOException e) { throw new BaseException("文件迁移失败: " + e.getMessage()); } } /** * 将临时文件迁移到正式目录 * * @param businessId 业务ID(销售台账ID) * @param tempFileIds 临时文件ID列表 * @throws IOException 文件操作异常 */ private void migrateTempFilesToFormal(Long businessId, List tempFileIds) throws IOException { if (CollectionUtils.isEmpty(tempFileIds)) { return; } // 构建正式目录路径(按业务类型和日期分组) String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); Path formalDirPath = Paths.get(formalDir); // 确保正式目录存在(递归创建) if (!Files.exists(formalDirPath)) { Files.createDirectories(formalDirPath); } for (String tempFileId : tempFileIds) { // 查询临时文件记录 TempFile tempFile = tempFileMapper.selectById(tempFileId); if (tempFile == null) { log.warn("临时文件不存在,跳过处理: {}", tempFileId); continue; } // 构建正式文件名(包含业务ID和时间戳,避免冲突) String originalFilename = tempFile.getOriginalName(); String fileExtension = FilenameUtils.getExtension(originalFilename); String formalFilename = businessId + "_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8) + (StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); Path formalFilePath = formalDirPath.resolve(formalFilename); try { // 执行文件迁移(使用原子操作确保安全性) // Files.move( // Paths.get(tempFile.getTempPath()), // formalFilePath, // StandardCopyOption.REPLACE_EXISTING, // StandardCopyOption.ATOMIC_MOVE // ); // 原子移动失败,使用复制+删除 Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); Files.deleteIfExists(Paths.get(tempFile.getTempPath())); log.info("文件迁移成功: {} -> {}", tempFile.getTempPath(), formalFilePath); // 更新文件记录(关联到业务ID) CommonFile fileRecord = new CommonFile(); fileRecord.setCommonId(businessId); fileRecord.setName(originalFilename); fileRecord.setUrl(formalFilePath.toString()); fileRecord.setCreateTime(LocalDateTime.now()); //销售 fileRecord.setType(FileNameType.SALE.getValue()); commonFileMapper.insert(fileRecord); // 删除临时文件记录 tempFileMapper.deleteById(tempFile); log.info("文件迁移成功: {} -> {}", tempFile.getTempPath(), formalFilePath); } catch (IOException e) { log.error("文件迁移失败: {}", tempFile.getTempPath(), e); // 可选择回滚事务或记录失败文件 throw new IOException("文件迁移异常", e); } } } // 文件迁移方法 @Override public void handleSalesLedgerProducts(Long salesLedgerId, List products, SaleEnum type) { // 按ID分组,区分新增和更新的记录 Map> partitionedProducts = products.stream() .peek(p -> p.setSalesLedgerId(salesLedgerId)) .collect(Collectors.partitioningBy(p -> p.getId() != null)); List updateList = partitionedProducts.get(true); List insertList = partitionedProducts.get(false); // 执行更新操作 if (!updateList.isEmpty()) { for (SalesLedgerProduct product : updateList) { product.setType(type.getCode()); salesLedgerProductMapper.updateById(product); // 清空销售产品绑定的加工 salesLedgerProductProcessBindService.updateProductProcessBind(product.getSalesProductProcessList(), product.getId()); } } // 执行插入操作 if (!insertList.isEmpty()) { for (SalesLedgerProduct salesLedgerProduct : insertList) { salesLedgerProduct.setType(type.getCode()); salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity()); salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice()); salesLedgerProduct.setPendingInvoiceTotal(salesLedgerProduct.getTaxInclusiveTotalPrice()); salesLedgerProductMapper.insert(salesLedgerProduct); // 绑定产品额外加工 // 清空销售产品绑定的加工 salesLedgerProductProcessBindService.updateProductProcessBind(salesLedgerProduct.getSalesProductProcessList(), salesLedgerProduct.getId()); // 添加生产数据 salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct); } } } private SalesLedger convertToEntity(SalesLedgerDto dto) { SalesLedger entity = new SalesLedger(); BeanUtils.copyProperties(dto, entity); return entity; } @Transactional(readOnly = true) public String generateSalesContractNo() { LocalDate currentDate = LocalDate.now(); String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE); String lockKey = LOCK_PREFIX + datePart; String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime(); // 唯一标识锁持有者 try { // 1. 尝试获取分布式锁(循环直到超时) long startWaitTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startWaitTime < LOCK_WAIT_TIMEOUT * 1000) { // SET key value NX PX 30000:仅当锁不存在时获取,设置30秒过期 Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { break; // 成功获取锁 } // 短暂休眠避免忙等待 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("获取锁时被中断", e); } } if (Boolean.FALSE.equals(redisTemplate.hasKey(lockKey))) { throw new RuntimeException("获取合同编号生成锁失败:超时"); } // 2. 查询当天/公司已存在的序列号(与原逻辑一致) // Long tenantId = SecurityUtils.getLoginUser().getTenantId(); // if (null != tenantId) { // //获取公司编号 // SysDept sysDept = sysDeptMapper.selectDeptById(tenantId); // if (!ObjectUtils.isEmpty(sysDept)) { // datePart = (StringUtils.isEmpty(sysDept.getDeptNick()) ? "" : sysDept.getDeptNick()) + datePart; // } // } datePart = "D" + datePart; List existingSequences = salesLedgerMapper.selectSequencesByDate(datePart); int nextSequence = findFirstMissingSequence(existingSequences); return datePart + String.format("%03d", nextSequence); } finally { // 3. 释放锁 String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end"; redisTemplate.execute( new DefaultRedisScript<>(luaScript, Long.class), Collections.singletonList(lockKey), lockValue ); } } @Override public SalesLedgerProcessRouteDto salesProcess(Long salesLedgerId) { SalesLedgerProcessRouteDto dto = new SalesLedgerProcessRouteDto(); List list = baseMapper.selectSalesProcess(salesLedgerId); if (CollectionUtils.isNotEmpty(list)) { Long processRouteId = list.get(0).getProcessRouteId(); ProcessRoute processRoute = processRouteMapper.selectById(processRouteId); if (processRoute != null) { dto.setRouteId(processRoute.getId()); dto.setRouteName(processRoute.getProcessRouteName()); } } else { // 要是list查询为空的话,就查询默认的工艺路线返回 ProcessRoute defaultRoute = processRouteMapper.selectOne(new LambdaQueryWrapper().eq(ProcessRoute::getIsDefault, 1).last("limit 1")); if (defaultRoute != null) { dto.setRouteId(defaultRoute.getId()); dto.setRouteName(defaultRoute.getProcessRouteName()); List routeItems = processRouteItemMapper.selectList(new LambdaQueryWrapper().eq(ProcessRouteItem::getRouteId, defaultRoute.getId()).orderByAsc(ProcessRouteItem::getDragSort)); list = routeItems.stream().map(item -> { SalesLedgerProcessRoute salesLedgerProcessRoute = new SalesLedgerProcessRoute(); salesLedgerProcessRoute.setProcessRouteId(defaultRoute.getId()); salesLedgerProcessRoute.setSalesLedgerId(salesLedgerId); salesLedgerProcessRoute.setProcessRouteItemId(item.getId()); salesLedgerProcessRoute.setProcessName(item.getProcessName()); salesLedgerProcessRoute.setDragSort(item.getDragSort()); return salesLedgerProcessRoute; }).collect(Collectors.toList()); } } dto.setList(list); return dto; } @Override public SalesProcessCardDto processCard(Long salesLedgerId) { if (salesLedgerId == null) { throw new ServiceException("流程卡打印失败,打印销售订单不能为空"); } // 查询销售订单 SalesLedger salesLedger = baseMapper.selectById(salesLedgerId); if (salesLedger == null) { throw new ServiceException("流程卡打印失败,销售订单不存在"); } SalesProcessCardDto dto = new SalesProcessCardDto(); dto.setSalesContractNo(salesLedger.getSalesContractNo()); dto.setCustomerName(salesLedger.getCustomerName()); dto.setDeliveryDate(salesLedger.getDeliveryDate()); dto.setRegister(SecurityUtils.getLoginUser().getUser().getNickName()); dto.setRegisterDate(LocalDateTime.now()); dto.setOrderProcessRequirement(salesLedger.getRemarks()); // 查询产品列表 List products = salesLedgerProductMapper.selectList( new LambdaQueryWrapper().eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)); BigDecimal totalQuantity = BigDecimal.ZERO; BigDecimal totalArea = BigDecimal.ZERO; List itemDtos = new ArrayList<>(); for (SalesLedgerProduct p : products) { SalesProcessCardDto.ProcessCardItemDto itemDto = new SalesProcessCardDto.ProcessCardItemDto(); itemDto.setFloorCode(p.getFloorCode()); // 组装产品描述:大类 + (规格) String desc = (p.getProductCategory() != null ? p.getProductCategory() : "") + (StringUtils.isNotBlank(p.getSpecificationModel()) ? " " + p.getSpecificationModel() : ""); itemDto.setProductDescription(desc.trim()); itemDto.setWidth(p.getWidth()); itemDto.setHeight(p.getHeight()); itemDto.setQuantity(p.getQuantity()); // 面积计算(平米) BigDecimal area = p.getActualPieceArea() != null ? p.getActualPieceArea() : p.getSettlePieceArea(); if (area == null && p.getWidth() != null && p.getHeight() != null) { area = p.getWidth().multiply(p.getHeight()).divide(new BigDecimal(1000000), 2, RoundingMode.HALF_UP); } itemDto.setArea(area); itemDto.setProcessRequirement(p.getProcessRequirement()); BigDecimal qty = p.getQuantity() != null ? p.getQuantity() : BigDecimal.ZERO; totalQuantity = totalQuantity.add(qty); if (area != null) { totalArea = totalArea.add(area.multiply(qty)); } itemDtos.add(itemDto); } dto.setItems(itemDtos); dto.setTotalQuantity(totalQuantity); dto.setTotalArea(totalArea.setScale(2, RoundingMode.HALF_UP)); // 工艺路线 List salesLedgerProcessRoutes = salesLedgerProcessRouteService.list( new LambdaQueryWrapper() .eq(SalesLedgerProcessRoute::getSalesLedgerId, salesLedgerId) .orderByAsc(SalesLedgerProcessRoute::getDragSort)); List nodeDtos = new ArrayList<>(); if (CollectionUtils.isEmpty(salesLedgerProcessRoutes)) { // 无自定义路线,取默认路线 ProcessRoute defaultRoute = processRouteMapper.selectOne( new LambdaQueryWrapper().eq(ProcessRoute::getIsDefault, 1).last("LIMIT 1")); if (defaultRoute != null) { List routeItems = processRouteItemMapper.selectList( new LambdaQueryWrapper() .eq(ProcessRouteItem::getRouteId, defaultRoute.getId()) .orderByAsc(ProcessRouteItem::getDragSort)); for (ProcessRouteItem i : routeItems) { SalesProcessCardDto.ProcessNodeDto node = new SalesProcessCardDto.ProcessNodeDto(); node.setProcessRouteItemId(i.getId()); node.setProcessRouteItemName(i.getProcessName()); node.setDragSort(i.getDragSort()); nodeDtos.add(node); } } } else { // 使用自定义路线绑定的节点 List itemIds = salesLedgerProcessRoutes.stream() .map(SalesLedgerProcessRoute::getProcessRouteItemId) .collect(Collectors.toList()); List rawItems = processRouteItemMapper.selectBatchIds(itemIds); Map itemMap = rawItems.stream() .collect(Collectors.toMap(ProcessRouteItem::getId, i -> i, (a, b) -> a)); for (SalesLedgerProcessRoute r : salesLedgerProcessRoutes) { ProcessRouteItem pi = itemMap.get(r.getProcessRouteItemId()); if (pi != null) { SalesProcessCardDto.ProcessNodeDto node = new SalesProcessCardDto.ProcessNodeDto(); node.setProcessRouteItemId(pi.getId()); node.setProcessRouteItemName(pi.getProcessName()); node.setDragSort(r.getDragSort() != null ? r.getDragSort() : pi.getDragSort()); node.setRemark(r.getRemark()); nodeDtos.add(node); } } } if (!nodeDtos.isEmpty()) { // dragSort 进行升序排序 nodeDtos.sort(Comparator.comparing( SalesProcessCardDto.ProcessNodeDto::getDragSort, Comparator.nullsLast(Comparator.naturalOrder()) )); // 重新生成排序后的路径名称列表 List sortedPathNames = nodeDtos.stream() .map(SalesProcessCardDto.ProcessNodeDto::getProcessRouteItemName) .collect(Collectors.toList()); // 拼接字符串 dto.setProcessPathDisplay(String.join(" -> ", sortedPathNames)); // 设置顶层节点的工艺路线 dto.setRouteNodes(nodeDtos); } return dto; } @Override public SalesOrdersDto salesOrders(Long salesLedgerId) { if (salesLedgerId == null) { throw new ServiceException("打印销售订单失败,销售订单ID不能为空"); } SalesLedger salesLedger = baseMapper.selectById(salesLedgerId); if (salesLedger == null) { throw new ServiceException("打印销售订单失败,销售订单不存在"); } SalesOrdersDto dto = new SalesOrdersDto(); dto.setSalesContractNo(salesLedger.getSalesContractNo()); dto.setCustomerName(salesLedger.getCustomerName()); dto.setProjectName(salesLedger.getProjectName()); dto.setSalesman(salesLedger.getSalesman()); dto.setExecutionDate(salesLedger.getExecutionDate() != null ? salesLedger.getExecutionDate().atStartOfDay() : null); dto.setDeliveryDate(salesLedger.getDeliveryDate()); dto.setRemakes(salesLedger.getRemarks()); dto.setCompanyName("鹤壁天沐钢化玻璃厂"); // 送货地址 if (salesLedger.getCustomerId() != null) { Customer customer = customerMapper.selectById(salesLedger.getCustomerId()); if (customer != null) { StringBuilder address = new StringBuilder(); if (customer.getRegionsId() != null) { CustomerRegions regions = customerRegionsService.getById(customer.getRegionsId()); if (regions != null) { address.append(regions.getRegionsName()); } } if (StringUtils.isNotEmpty(customer.getCompanyAddress())) { address.append(customer.getCompanyAddress()); } dto.setCompanyAddress(address.toString()); } } // 制单员 if (StringUtils.isNotEmpty(salesLedger.getEntryPerson())) { try { SysUser user = sysUserMapper.selectUserById(Long.parseLong(salesLedger.getEntryPerson())); if (user != null) { dto.setOrderMaker(user.getNickName()); } } catch (Exception e) { log.error("获取制单员信息失败: {}", e.getMessage()); } } // 制单日期 (底部) dto.setOrderMakerDate(salesLedger.getExecutionDate() != null ? salesLedger.getExecutionDate().atStartOfDay() : null); // 打印信息 LoginUser loginUser = SecurityUtils.getLoginUser(); if (loginUser != null && loginUser.getUser() != null) { dto.setPrintPeople(loginUser.getUser().getNickName()); } dto.setPrintTime(LocalDateTime.now()); // 查询产品列表 List products = salesLedgerProductMapper.selectList( new LambdaQueryWrapper().eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)); if (CollectionUtils.isNotEmpty(products)) { SalesLedgerProduct firstProduct = products.get(0); dto.setProductName(firstProduct.getProductCategory() != null ? firstProduct.getProductCategory() : ""); } List itemDtos = new ArrayList<>(); BigDecimal subtotalQuantity = BigDecimal.ZERO; BigDecimal subtotalArea = BigDecimal.ZERO; BigDecimal subtotalAmount = BigDecimal.ZERO; for (SalesLedgerProduct p : products) { SalesOrdersDto.SalesOrderItemDto itemDto = new SalesOrdersDto.SalesOrderItemDto(); itemDto.setFloorCode(p.getFloorCode()); String desc = (p.getProductCategory() != null ? p.getProductCategory() : "") + (StringUtils.isNotBlank(p.getSpecificationModel()) ? " " + p.getSpecificationModel() : ""); itemDto.setProductDescription(desc.trim()); itemDto.setWidth(p.getWidth()); itemDto.setHeight(p.getHeight()); itemDto.setQuantity(p.getQuantity()); // 面积计算 BigDecimal area = p.getSettleTotalArea() != null ? p.getSettleTotalArea() : p.getActualTotalArea(); if (area == null && p.getWidth() != null && p.getHeight() != null && p.getQuantity() != null) { area = p.getWidth().multiply(p.getHeight()).multiply(p.getQuantity()).divide(new BigDecimal(1000000), 2, RoundingMode.HALF_UP); } itemDto.setArea(area); itemDto.setUnitPrice(p.getTaxInclusiveUnitPrice()); itemDto.setAmount(p.getTaxInclusiveTotalPrice()); itemDto.setRemark(p.getRemark()); itemDto.setProcessRequirement(p.getProcessRequirement()); subtotalQuantity = subtotalQuantity.add(p.getQuantity() != null ? p.getQuantity() : BigDecimal.ZERO); subtotalArea = subtotalArea.add(area != null ? area : BigDecimal.ZERO); subtotalAmount = subtotalAmount.add(p.getTaxInclusiveTotalPrice() != null ? p.getTaxInclusiveTotalPrice() : BigDecimal.ZERO); itemDtos.add(itemDto); } dto.setItems(itemDtos); dto.setSubtotalQuantity(subtotalQuantity); dto.setSubtotalArea(subtotalArea.setScale(2, RoundingMode.HALF_UP)); dto.setSubtotalAmount(subtotalAmount); // 处理其他费用 List productIds = products.stream().map(SalesLedgerProduct::getId).collect(Collectors.toList()); BigDecimal otherFeesTotal = BigDecimal.ZERO; if (CollectionUtils.isNotEmpty(productIds)) { List binds = salesLedgerProductProcessBindService.list( new LambdaQueryWrapper().in(SalesLedgerProductProcessBind::getSalesLedgerProductId, productIds)); if (CollectionUtils.isNotEmpty(binds)) { Map processQuantityMap = binds.stream() .collect(Collectors.groupingBy(SalesLedgerProductProcessBind::getSalesLedgerProductProcessId, Collectors.summingInt(b -> b.getQuantity() != null ? b.getQuantity() : 0))); List processIds = new ArrayList<>(processQuantityMap.keySet()); List processes = salesLedgerProductProcessService.listByIds(processIds); List otherFeeDtos = new ArrayList<>(); for (SalesLedgerProductProcess proc : processes) { SalesOrdersDto.OtherFeeDto feeDto = new SalesOrdersDto.OtherFeeDto(); feeDto.setFeeName(proc.getProcessName()); feeDto.setUnitPrice(proc.getUnitPrice()); Integer qty = processQuantityMap.get(proc.getId()); feeDto.setQuantity(new BigDecimal(qty != null ? qty : 0)); BigDecimal amount = proc.getUnitPrice() != null ? proc.getUnitPrice().multiply(feeDto.getQuantity()) : BigDecimal.ZERO; feeDto.setAmount(amount); otherFeeDtos.add(feeDto); otherFeesTotal = otherFeesTotal.add(amount); } dto.setOtherFees(otherFeeDtos); } } dto.setTotalQuantity(subtotalQuantity); dto.setTotalArea(dto.getSubtotalArea()); dto.setTotalAmount(subtotalAmount.add(otherFeesTotal)); dto.setTotalAmountDisplay(dto.getTotalAmount().setScale(2, RoundingMode.HALF_UP).toString() + "元"); return dto; } private int findFirstMissingSequence(List sequences) { if (sequences.isEmpty()) { return 1; } // 排序后查找第一个缺失的正整数 sequences.sort(Integer::compareTo); int next = 1; for (int seq : sequences) { if (seq == next) { next++; } else if (seq > next) { break; } } return next; } public void updateMainContractAmount( Long mainId, List subList, Function amountGetter, BaseMapper mainMapper, Class mainEntityClass) { if (mainId == null || subList == null || subList.isEmpty()) { return; } // 计算子表金额总和 BigDecimal totalAmount = subList.stream() .map(amountGetter) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); // 构造主表更新对象 try { S entity = mainEntityClass.getDeclaredConstructor().newInstance(); Field idField = mainEntityClass.getDeclaredField("id"); idField.setAccessible(true); idField.set(entity, mainId); Field amountField = mainEntityClass.getDeclaredField("contractAmount"); amountField.setAccessible(true); amountField.set(entity, totalAmount); mainMapper.updateById(entity); } catch (Exception e) { throw new RuntimeException("动态更新主表金额失败", e); } } // 内部类用于存储聚合结果 private static class GroupedCustomer { private final Long customerId; private final String customerName; private BigDecimal totalAmount = BigDecimal.ZERO; public GroupedCustomer(Long customerId, String customerName) { this.customerId = customerId; this.customerName = customerName; } public void addAmount(BigDecimal amount) { if (amount != null) { this.totalAmount = this.totalAmount.add(amount); } } public Long getCustomerId() { return customerId; } public String getCustomerName() { return customerName; } public BigDecimal getTotalAmount() { return totalAmount; } } }