package com.ruoyi.purchase.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.dto.StorageBlobDTO; import com.ruoyi.basic.dto.StorageBlobVO; import com.ruoyi.basic.enums.ApplicationTypeEnum; import com.ruoyi.basic.enums.RecordTypeEnum; 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.basic.utils.FileUtil; import com.ruoyi.common.enums.PurchaseApplicationStatusEnum; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.OrderUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.purchase.dto.PurchaseApplicationDto; import com.ruoyi.purchase.dto.PurchaseLedgerDto; import com.ruoyi.purchase.mapper.PurchaseApplicationMapper; import com.ruoyi.purchase.mapper.PurchaseApplicationProductMapper; import com.ruoyi.purchase.pojo.PurchaseApplication; import com.ruoyi.purchase.pojo.PurchaseApplicationProduct; import com.ruoyi.purchase.service.IPurchaseLedgerService; import com.ruoyi.purchase.service.PurchaseApplicationService; import com.ruoyi.sales.pojo.SalesLedgerProduct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * 采购申请单 Service 业务层处理 */ @Service @Slf4j @RequiredArgsConstructor public class PurchaseApplicationServiceImpl extends ServiceImpl implements PurchaseApplicationService { private static final String APPLICATION_NO_PREFIX = "CGSQ"; private final PurchaseApplicationMapper purchaseApplicationMapper; private final PurchaseApplicationProductMapper purchaseApplicationProductMapper; private final IPurchaseLedgerService purchaseLedgerService; private final SupplierManageMapper supplierManageMapper; private final SysUserMapper sysUserMapper; private final ProductMapper productMapper; private final ProductModelMapper productModelMapper; private final FileUtil fileUtil; @Override public List selectApplicationList(PurchaseApplicationDto purchaseApplicationDto) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.like(StringUtils.isNotBlank(purchaseApplicationDto.getApplicationNo()), PurchaseApplication::getApplicationNo, purchaseApplicationDto.getApplicationNo()) .like(StringUtils.isNotBlank(purchaseApplicationDto.getProjectName()), PurchaseApplication::getProjectName, purchaseApplicationDto.getProjectName()) .eq(purchaseApplicationDto.getSupplierId() != null, PurchaseApplication::getSupplierId, purchaseApplicationDto.getSupplierId()) .eq(purchaseApplicationDto.getStatus() != null, PurchaseApplication::getStatus, purchaseApplicationDto.getStatus()) .ge(StringUtils.isNotBlank(purchaseApplicationDto.getStartDate()), PurchaseApplication::getApplyDate, purchaseApplicationDto.getStartDate()) .le(StringUtils.isNotBlank(purchaseApplicationDto.getEndDate()), PurchaseApplication::getApplyDate, purchaseApplicationDto.getEndDate()) .orderByDesc(PurchaseApplication::getId); return this.list(queryWrapper); } @Override public PurchaseApplicationDto getDetail(Long id) { PurchaseApplication application = this.getById(id); if (application == null) { throw new ServiceException("采购申请不存在"); } PurchaseApplicationDto resultDto = new PurchaseApplicationDto(); BeanUtils.copyProperties(application, resultDto); resultDto.setProductData(listProducts(id)); resultDto.setStorageBlobVOS(listAttachments(id)); return resultDto; } @Override @Transactional(rollbackFor = Exception.class) public Long saveApplication(PurchaseApplicationDto purchaseApplicationDto) { PurchaseApplication application = new PurchaseApplication(); BeanUtils.copyProperties(purchaseApplicationDto, application); application.setId(null); application.setStatus(PurchaseApplicationStatusEnum.DRAFT.getCode()); application.setPurchaseLedgerId(null); application.setConvertTime(null); application.setApplicationNo(OrderUtils.countTodayByCreateTime( purchaseApplicationMapper, APPLICATION_NO_PREFIX, "application_no", LocalDateTime.now())); fillSupplierName(application); fillApplicant(application, purchaseApplicationDto); this.save(application); saveProducts(application.getId(), purchaseApplicationDto.getProductData()); saveAttachments(application.getId(), purchaseApplicationDto.getStorageBlobDTOS()); return application.getId(); } @Override @Transactional(rollbackFor = Exception.class) public boolean updateApplication(PurchaseApplicationDto purchaseApplicationDto) { if (purchaseApplicationDto.getId() == null) { throw new ServiceException("采购申请id不能为空"); } PurchaseApplication existing = this.getById(purchaseApplicationDto.getId()); if (existing == null) { throw new ServiceException("采购申请不存在"); } if (!PurchaseApplicationStatusEnum.DRAFT.getCode().equals(existing.getStatus())) { throw new ServiceException("已生成订单的采购申请不允许修改"); } PurchaseApplication application = new PurchaseApplication(); BeanUtils.copyProperties(purchaseApplicationDto, application); // 单号、状态、订单关联由系统维护,不允许前端改动;置空后 MyBatis-Plus 不会更新这些列 application.setApplicationNo(null); application.setStatus(null); application.setPurchaseLedgerId(null); application.setConvertTime(null); application.setTenantId(null); application.setCreateUser(null); application.setCreateTime(null); fillSupplierName(application); boolean updated = this.updateById(application); if (updated) { purchaseApplicationProductMapper.delete(Wrappers.lambdaQuery() .eq(PurchaseApplicationProduct::getPurchaseApplicationId, purchaseApplicationDto.getId())); saveProducts(purchaseApplicationDto.getId(), purchaseApplicationDto.getProductData()); saveAttachments(purchaseApplicationDto.getId(), purchaseApplicationDto.getStorageBlobDTOS()); } return updated; } @Override @Transactional(rollbackFor = Exception.class) public boolean deleteApplications(List ids) { if (ids == null || ids.isEmpty()) { throw new ServiceException("请选择要删除的采购申请"); } List orderedNos = this.listByIds(ids).stream() .filter(application -> !PurchaseApplicationStatusEnum.DRAFT.getCode().equals(application.getStatus())) .map(PurchaseApplication::getApplicationNo) .filter(Objects::nonNull) .collect(Collectors.toList()); if (!orderedNos.isEmpty()) { throw new ServiceException("已生成订单的采购申请不允许删除:" + String.join("、", orderedNos)); } purchaseApplicationProductMapper.delete(Wrappers.lambdaQuery() .in(PurchaseApplicationProduct::getPurchaseApplicationId, ids)); return this.removeByIds(ids); } @Override @Transactional(rollbackFor = Exception.class) public Long convertToOrder(Long id) { if (id == null) { throw new ServiceException("采购申请id不能为空"); } PurchaseApplication application = this.getById(id); if (application == null) { throw new ServiceException("采购申请不存在"); } if (application.getSupplierId() == null) { throw new ServiceException("请先选择供应商再转订单"); } SupplierManage supplierManage = supplierManageMapper.selectById(application.getSupplierId()); if (supplierManage == null) { throw new ServiceException("供应商不存在,请重新选择"); } List products = listProducts(id); if (products.isEmpty()) { throw new ServiceException("采购申请没有产品明细,无法转订单"); } // 条件更新抢占草稿状态,避免并发下重复生成订单 boolean converted = this.update(Wrappers.lambdaUpdate() .eq(PurchaseApplication::getId, id) .eq(PurchaseApplication::getStatus, PurchaseApplicationStatusEnum.DRAFT.getCode()) .set(PurchaseApplication::getStatus, PurchaseApplicationStatusEnum.ORDER_GENERATED.getCode())); if (!converted) { throw new ServiceException("该采购申请已生成订单,请勿重复转换"); } PurchaseLedgerDto purchaseLedgerDto = buildPurchaseLedgerDto(application, supplierManage, products); try { // 复用采购台账新增逻辑:生成采购合同号、保存台账与产品明细、提交 OA 采购审批 purchaseLedgerService.addOrEditPurchase(purchaseLedgerDto); } catch (Exception e) { log.error("采购申请转订单失败, applicationId={}, applicationNo={}", id, application.getApplicationNo(), e); throw new ServiceException("转订单失败:" + e.getMessage()); } Long purchaseLedgerId = purchaseLedgerDto.getId(); if (purchaseLedgerId == null) { throw new ServiceException("转订单失败:未生成采购订单"); } this.update(Wrappers.lambdaUpdate() .eq(PurchaseApplication::getId, id) .set(PurchaseApplication::getPurchaseLedgerId, purchaseLedgerId) .set(PurchaseApplication::getConvertTime, LocalDateTime.now())); return purchaseLedgerId; } /** * 保存附件:先清空该申请单的旧关联,再按前端提交的文件全量重建 */ private void saveAttachments(Long applicationId, List storageBlobDTOS) { fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_APPLICATION, applicationId, storageBlobDTOS); } private List listAttachments(Long applicationId) { return fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId( ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_APPLICATION, applicationId); } private List listProducts(Long applicationId) { return purchaseApplicationProductMapper.selectList(Wrappers.lambdaQuery() .eq(PurchaseApplicationProduct::getPurchaseApplicationId, applicationId) .orderByAsc(PurchaseApplicationProduct::getId)); } private void saveProducts(Long applicationId, List products) { if (products == null || products.isEmpty()) { return; } fillProductNames(products); for (PurchaseApplicationProduct product : products) { product.setId(null); product.setPurchaseApplicationId(applicationId); product.setTenantId(null); product.setCreateUser(null); product.setCreateTime(null); purchaseApplicationProductMapper.insert(product); } } /** * 补全产品大类与规格型号,便于申请单直接展示产品名称 */ private void fillProductNames(List products) { Set productIds = products.stream() .map(PurchaseApplicationProduct::getProductId) .filter(Objects::nonNull) .collect(Collectors.toSet()); Set modelIds = products.stream() .map(PurchaseApplicationProduct::getProductModelId) .filter(Objects::nonNull) .collect(Collectors.toSet()); Map productMap = new HashMap<>(); if (!productIds.isEmpty()) { for (Product product : productMapper.selectBatchIds(productIds)) { productMap.put(product.getId(), product.getProductName()); } } Map modelMap = new HashMap<>(); if (!modelIds.isEmpty()) { for (ProductModel model : productModelMapper.selectBatchIds(modelIds)) { modelMap.put(model.getId(), model.getModel()); } } for (PurchaseApplicationProduct product : products) { String productName = productMap.get(product.getProductId()); if (productName != null) { product.setProductCategory(productName); } String model = modelMap.get(product.getProductModelId()); if (model != null) { product.setSpecificationModel(model); } } } private void fillSupplierName(PurchaseApplication application) { if (application.getSupplierId() == null) { return; } SupplierManage supplierManage = supplierManageMapper.selectById(application.getSupplierId()); if (supplierManage != null) { application.setSupplierName(supplierManage.getSupplierName()); } } private void fillApplicant(PurchaseApplication application, PurchaseApplicationDto purchaseApplicationDto) { Long applicantId = purchaseApplicationDto.getApplicantId() != null ? purchaseApplicationDto.getApplicantId() : SecurityUtils.getUserId(); application.setApplicantId(applicantId); if (StringUtils.isBlank(purchaseApplicationDto.getApplicantName())) { SysUser applicant = sysUserMapper.selectUserById(applicantId); application.setApplicantName(applicant != null ? applicant.getNickName() : SecurityUtils.getUsername()); } if (application.getApplyDate() == null) { application.setApplyDate(LocalDate.now()); } } /** * 组装采购台账入参:供应商、销售合同、产品明细等;采购合同号由采购台账侧生成 */ private PurchaseLedgerDto buildPurchaseLedgerDto(PurchaseApplication application, SupplierManage supplierManage, List products) { PurchaseLedgerDto purchaseLedgerDto = new PurchaseLedgerDto(); purchaseLedgerDto.setSupplierId(application.getSupplierId()); purchaseLedgerDto.setSupplierName(supplierManage.getSupplierName()); purchaseLedgerDto.setRecorderId(application.getRecorderId() != null ? application.getRecorderId() : application.getApplicantId()); purchaseLedgerDto.setRecorderName(application.getApplicantName()); purchaseLedgerDto.setSalesContractNo(application.getSalesContractNo()); purchaseLedgerDto.setSalesLedgerId(application.getSalesLedgerId()); purchaseLedgerDto.setProjectName(application.getProjectName()); purchaseLedgerDto.setRemarks(application.getRemarks()); purchaseLedgerDto.setPaymentMethod(application.getPaymentMethod()); purchaseLedgerDto.setContractAmount(application.getContractAmount()); purchaseLedgerDto.setPurchaseContractNumber(application.getPurchaseContractNumber()); purchaseLedgerDto.setExecutionDate(toDateOrNull(application.getExecutionDate())); purchaseLedgerDto.setBestArrivalDate(toDateOrNull(application.getBestArrivalDate())); purchaseLedgerDto.setApplicationSubmitTime(toDateOrNull(application.getApplicationSubmitTime())); purchaseLedgerDto.setPurchaseCategory(application.getPurchaseCategory()); purchaseLedgerDto.setLogisticsMethod(application.getLogisticsMethod()); purchaseLedgerDto.setPickupMethod(application.getPickupMethod()); purchaseLedgerDto.setOilDepotId(application.getOilDepotId()); purchaseLedgerDto.setCustomerName(application.getCustomerName()); purchaseLedgerDto.setPurchaseQuantity(application.getPurchaseQuantity()); purchaseLedgerDto.setPurchaseOrderStatus(application.getPurchaseOrderStatus()); LocalDate entryDate = application.getEntryDate() != null ? application.getEntryDate() : (application.getApplyDate() != null ? application.getApplyDate() : LocalDate.now()); purchaseLedgerDto.setEntryDate(DateUtils.toDate(entryDate)); purchaseLedgerDto.setProductData(products.stream() .map(this::toSalesLedgerProduct) .collect(Collectors.toList())); purchaseLedgerDto.setStorageBlobDTOS(toStorageBlobDtos(listAttachments(application.getId()))); return purchaseLedgerDto; } private static Date toDateOrNull(LocalDate date) { return date == null ? null : DateUtils.toDate(date); } private static Date toDateOrNull(LocalDateTime dateTime) { return dateTime == null ? null : DateUtils.toDate(dateTime); } /** * 申请单附件原样关联到生成的采购台账:复用同一份 storage_blob,只改关联记录 */ private List toStorageBlobDtos(List storageBlobVOS) { if (storageBlobVOS == null || storageBlobVOS.isEmpty()) { return Collections.emptyList(); } return storageBlobVOS.stream() .map(vo -> { StorageBlobDTO dto = new StorageBlobDTO(); dto.setId(vo.getId()); return dto; }) .collect(Collectors.toList()); } private SalesLedgerProduct toSalesLedgerProduct(PurchaseApplicationProduct source) { SalesLedgerProduct target = new SalesLedgerProduct(); target.setProductId(source.getProductId()); target.setProductModelId(source.getProductModelId()); target.setProductCategory(source.getProductCategory()); target.setSpecificationModel(source.getSpecificationModel()); target.setUnit(source.getUnit()); target.setQuantity(source.getQuantity()); target.setWarnNum(source.getWarnNum()); target.setTaxRate(source.getTaxRate()); target.setTaxInclusiveUnitPrice(source.getTaxInclusiveUnitPrice()); target.setTaxInclusiveTotalPrice(source.getTaxInclusiveTotalPrice()); target.setTaxExclusiveTotalPrice(source.getTaxExclusiveTotalPrice()); target.setInvoiceType(source.getInvoiceType()); target.setIsChecked(source.getIsChecked()); return target; } }