package com.ruoyi.purchase.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.mapper.ProductMapper; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.mapper.SupplierManageMapper; import com.ruoyi.basic.pojo.Product; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.basic.pojo.SupplierManage; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.other.mapper.TempFileMapper; import com.ruoyi.other.pojo.TempFile; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.purchase.dto.PurchaseLedgerDto; import com.ruoyi.purchase.mapper.ProductRecordMapper; import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; import com.ruoyi.purchase.mapper.TicketRegistrationMapper; import com.ruoyi.purchase.pojo.ProductRecord; import com.ruoyi.purchase.pojo.PurchaseLedger; import com.ruoyi.purchase.pojo.TicketRegistration; import com.ruoyi.purchase.service.IPurchaseLedgerService; import com.ruoyi.sales.mapper.CommonFileMapper; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.sales.pojo.CommonFile; import com.ruoyi.sales.pojo.SalesLedger; import com.ruoyi.sales.pojo.SalesLedgerProduct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; import java.math.BigDecimal; 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.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; /** * 采购台账Service业务层处理 * * @author ruoyi * @date 2025-05-09 */ @Service @RequiredArgsConstructor @Slf4j public class PurchaseLedgerServiceImpl extends ServiceImpl implements IPurchaseLedgerService { private final PurchaseLedgerMapper purchaseLedgerMapper; private final SalesLedgerMapper salesLedgerMapper; private final SalesLedgerProductMapper salesLedgerProductMapper; private final SysUserMapper userMapper; private final TempFileMapper tempFileMapper; private final CommonFileMapper commonFileMapper; private final SupplierManageMapper supplierManageMapper; private final ProductMapper productMapper; private final ProductModelMapper productModelMapper; private final TicketRegistrationMapper ticketRegistrationMapper; private final ProductRecordMapper productRecordMapper; @Value("${file.upload-dir}") private String uploadDir; @Override public List selectPurchaseLedgerList(PurchaseLedger purchaseLedger) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotBlank(purchaseLedger.getPurchaseContractNumber())) { queryWrapper.like(PurchaseLedger::getPurchaseContractNumber, purchaseLedger.getPurchaseContractNumber()); } return purchaseLedgerMapper.selectList(queryWrapper); } @Override public int addOrEditPurchase(PurchaseLedgerDto purchaseLedgerDto) throws IOException { SalesLedger salesLedger = salesLedgerMapper.selectById(purchaseLedgerDto.getSalesLedgerId()); if (salesLedger == null) { throw new BaseException("销售台账不存在"); } //录入人 SysUser sysUser = userMapper.selectUserById(purchaseLedgerDto.getRecorderId()); SupplierManage supplierManage = supplierManageMapper.selectById(purchaseLedgerDto.getSupplierId()); // DTO转Entity PurchaseLedger purchaseLedger = new PurchaseLedger(); BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger); purchaseLedger.setTenantId(salesLedger.getTenantId()); purchaseLedger.setSalesContractNo(salesLedger.getSalesContractNo()); purchaseLedger.setSupplierName(supplierManage.getSupplierName()); purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId()); purchaseLedger.setRecorderName(sysUser.getNickName()); purchaseLedger.setPhoneNumber(sysUser.getPhonenumber()); // 3. 新增或更新主表 if (purchaseLedger.getId() == null) { purchaseLedgerMapper.insert(purchaseLedger); } else { purchaseLedgerMapper.updateById(purchaseLedger); } // 4. 处理子表数据 List productList = purchaseLedgerDto.getProductData(); if (productList != null && !productList.isEmpty()) { handleSalesLedgerProducts(purchaseLedger.getId(), productList, purchaseLedgerDto.getType()); } // 5. 迁移临时文件到正式目录 if (purchaseLedgerDto.getTempFileIds() != null && !purchaseLedgerDto.getTempFileIds().isEmpty()) { migrateTempFilesToFormal(purchaseLedger.getId(), purchaseLedgerDto.getTempFileIds()); } return 1; } private void handleSalesLedgerProducts(Long salesLedgerId, List products, Integer type) { if (products == null || products.isEmpty()) { throw new BaseException("产品信息不存在"); } // 提前收集所有需要查询的ID Set productIds = products.stream() .map(SalesLedgerProduct::getProductId) .filter(Objects::nonNull) .collect(Collectors.toSet()); Set modelIds = products.stream() .map(SalesLedgerProduct::getProductModelId) .filter(Objects::nonNull) .collect(Collectors.toSet()); // 一次性查询产品和型号信息 Map productMap = new HashMap<>(); if (!productIds.isEmpty()) { List productList = productMapper.selectBatchIds(productIds); productList.forEach(p -> productMap.put(p.getId(), p.getProductName())); } Map modelMap = new HashMap<>(); if (!modelIds.isEmpty()) { List modelList = productModelMapper.selectBatchIds(modelIds); modelList.forEach(m -> modelMap.put(m.getId(), m.getModel())); } // 设置字段 for (SalesLedgerProduct product : products) { product.setSalesLedgerId(salesLedgerId); Long productId = product.getProductId(); if (productId != null && productMap.containsKey(productId)) { product.setProductCategory(productMap.get(productId)); } Long productModelId = product.getProductModelId(); if (productModelId != null && modelMap.containsKey(productModelId)) { product.setSpecificationModel(modelMap.get(productModelId)); } } // 分组处理 Map> partitionedProducts = products.stream() .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); salesLedgerProductMapper.updateById(product); } } // 执行插入操作 if (!insertList.isEmpty()) { for (SalesLedgerProduct salesLedgerProduct : insertList) { salesLedgerProduct.setType(type); salesLedgerProductMapper.insert(salesLedgerProduct); } } // 计算总含税金额 BigDecimal totalTaxInclusiveAmount = products.stream() .map(SalesLedgerProduct::getTaxInclusiveTotalPrice) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); // 更新主表的总金额字段 if (salesLedgerId != null) { // 直接更新指定ID的记录的contractAmount字段为totalTaxInclusiveAmount purchaseLedgerMapper.updateContractAmountById(salesLedgerId, totalTaxInclusiveAmount); } } /** * 将临时文件迁移到正式目录 * * @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 ); 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("2"); 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 int deletePurchaseLedgerByIds(Long[] ids) { if (ids == null || ids.length == 0) { throw new BaseException("请选中至少一条数据"); } // 批量删除关联的采购台账产品 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.in(SalesLedgerProduct::getSalesLedgerId, ids) .eq(SalesLedgerProduct::getType, "2"); salesLedgerProductMapper.delete(queryWrapper); // 批量删除关联的采购台账的来票登记 LambdaQueryWrapper ticketRegistrationLambdaQueryWrapper = new LambdaQueryWrapper<>(); ticketRegistrationLambdaQueryWrapper.in(TicketRegistration::getSalesLedgerId,ids); ticketRegistrationMapper.delete(ticketRegistrationLambdaQueryWrapper); // 批量删除关联的采购台账的来票登记记录 LambdaQueryWrapper productRecordLambdaQueryWrapper = new LambdaQueryWrapper<>(); productRecordLambdaQueryWrapper.in(ProductRecord::getPurchaseLedgerId,ids); productRecordMapper.delete(productRecordLambdaQueryWrapper); // 批量删除采购台账 return purchaseLedgerMapper.deleteBatchIds(Arrays.asList(ids)); } @Override public PurchaseLedgerDto getPurchaseById(PurchaseLedgerDto purchaseLedgerDto) { // 1. 查询主表 PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(purchaseLedgerDto.getId()); if (purchaseLedger == null) { throw new BaseException("采购台账不存在"); } // 2. 查询子表 LambdaQueryWrapper productWrapper = new LambdaQueryWrapper<>(); productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()) .eq(SalesLedgerProduct::getType, purchaseLedgerDto.getType()); List products = salesLedgerProductMapper.selectList(productWrapper); // 3.查询上传文件 LambdaQueryWrapper salesLedgerFileWrapper = new LambdaQueryWrapper<>(); salesLedgerFileWrapper.eq(CommonFile::getCommonId, purchaseLedger.getId()); List salesLedgerFiles = commonFileMapper.selectList(salesLedgerFileWrapper); // 4. 转换 DTO PurchaseLedgerDto resultDto = new PurchaseLedgerDto(); BeanUtils.copyProperties(purchaseLedger, resultDto); if (!products.isEmpty()) { resultDto.setHasChildren(true); resultDto.setProductData(products); resultDto.setSalesLedgerFiles(salesLedgerFiles); } return resultDto; } @Override public List getProduct(PurchaseLedgerDto purchaseLedgerDto) { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.select(PurchaseLedger::getId, PurchaseLedger::getPurchaseContractNumber); // 获取原始查询结果 List> result = purchaseLedgerMapper.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 PurchaseLedgerDto getInfo(PurchaseLedgerDto purchaseLedgerDto) { PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(purchaseLedgerDto.getId()); if (purchaseLedger == null) { throw new BaseException("采购台账不存在"); } // 创建并填充DTO PurchaseLedgerDto resultDto = new PurchaseLedgerDto(); resultDto.setSalesContractNoId(purchaseLedger.getSalesLedgerId()); resultDto.setSalesContractNo(purchaseLedger.getSalesContractNo()); resultDto.setSupplierName(purchaseLedger.getSupplierName()); resultDto.setProjectName(purchaseLedger.getProjectName()); // 查询并设置关联产品 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()) .eq(SalesLedgerProduct::getType, 2); List productList = salesLedgerProductMapper.selectList(queryWrapper); productList.forEach(product -> { product.setFutureTickets(product.getFutureTickets() != null ? product.getFutureTickets() : product.getQuantity().longValue()); product.setFutureTicketsAmount(product.getFutureTicketsAmount() != null ? product.getFutureTicketsAmount() : product.getTaxInclusiveTotalPrice()); product.setTicketsNum(null); product.setTicketsAmount(null); }); resultDto.setProductData(productList); return resultDto; } @Override public List getPurchasesNo() { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.select(PurchaseLedger::getId, PurchaseLedger::getPurchaseContractNumber, PurchaseLedger::getSupplierId); // 获取原始查询结果 List> result = purchaseLedgerMapper.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 PurchaseLedgerDto getPurchaseNoById(Long id) { PurchaseLedgerDto purchaseLedgerDto = new PurchaseLedgerDto(); PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id); BeanUtils.copyProperties(purchaseLedger, purchaseLedgerDto); // TicketRegistration ticketRegistration = ticketRegistrationMapper.selectOne(new LambdaQueryWrapper().eq(TicketRegistration::getPurchaseLedgerId, id)); // if (ticketRegistration != null) { // purchaseLedgerDto.setInvoiceNumber(ticketRegistration.getInvoiceNumber()); // purchaseLedgerDto.setInvoiceAmount(ticketRegistration.getInvoiceAmount()); // purchaseLedgerDto.setTicketRegistrationId(ticketRegistration.getId()); // } return purchaseLedgerDto; } /** * 下划线命名转驼峰命名 */ 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(); } }