| src/main/java/com/ruoyi/basic/dto/BusinessDescriptionDto.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/basic/dto/StorageBlobVO.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/basic/service/impl/BusinessOpportunityServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/basic/utils/FileUtil.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/sales/pojo/SalesLedger.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/main/java/com/ruoyi/basic/dto/BusinessDescriptionDto.java
@@ -9,7 +9,7 @@ public class BusinessDescriptionDto extends BusinessDescription { private List<StorageBlobDTO> storageBlobDTOS; private List<StorageBlobDTO> businessCommonFiles; private List<StorageBlobVO> storageBlobVO; } src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java
@@ -15,6 +15,9 @@ */ private String downloadURL; private String url; private String name; /** * 文件类型 */ src/main/java/com/ruoyi/basic/dto/StorageBlobVO.java
@@ -15,5 +15,8 @@ */ private String downloadURL; private String url; private String name; private Long storageAttachmentId; } src/main/java/com/ruoyi/basic/service/impl/BusinessOpportunityServiceImpl.java
@@ -2,7 +2,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; 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.basic.dto.BusinessDescriptionDto; @@ -56,11 +55,41 @@ LoginUser loginUser = SecurityUtils.getLoginUser(); IPage<BusinessOpportunityDto> businessOpportunityDtoIPage = businessOpportunityMapper.listPage(page, businessOpportunityDto); businessOpportunityDtoIPage.getRecords().forEach(item -> { item.setBusinessCommonFiles(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.BUSINESS_OPPORTUNITY, item.getId())); item.setBusinessDescription(businessDescriptionMapper.selectList(Wrappers.lambdaQuery(BusinessDescription.class) .eq(BusinessDescription::getBusinessOpportunityId, item.getId()) .orderByDesc(BusinessDescription::getCreateTime))); businessOpportunityDtoIPage.getRecords().forEach(opportunity -> { ArrayList<StorageBlobVO> storageBlobVOS = new ArrayList<>(); // 查询业务描述列表 List<BusinessDescription> businessDescriptions = businessDescriptionMapper.selectList( new LambdaQueryWrapper<BusinessDescription>() .eq(BusinessDescription::getBusinessOpportunityId, opportunity.getId()) .orderByDesc(BusinessDescription::getCreateTime) ); // 收集每个业务描述的文件 if (businessDescriptions != null && !businessDescriptions.isEmpty()) { businessDescriptions.forEach(description -> { List<StorageBlobVO> files = fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId( ApplicationTypeEnum.FILE, RecordTypeEnum.BUSINESS_DESCRIPTION, Long.valueOf(description.getId()) ); if (files != null && !files.isEmpty()) { storageBlobVOS.addAll(files); } }); } // 查询商机主表的文件 List<StorageBlobVO> opportunityFiles = fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId( ApplicationTypeEnum.FILE, RecordTypeEnum.BUSINESS_OPPORTUNITY, opportunity.getId() ); if (opportunityFiles != null && !opportunityFiles.isEmpty()) { storageBlobVOS.addAll(opportunityFiles); } opportunity.setBusinessCommonFiles(storageBlobVOS); opportunity.setBusinessDescription(businessDescriptions); }); return businessOpportunityDtoIPage; @@ -130,7 +159,8 @@ unipushService.sendClientMessage(sysNoticeList); } int insert = businessDescriptionMapper.insert(businessDescription); fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.BUSINESS_DESCRIPTION, businessDescription.getBusinessOpportunityId(), businessDescription.getStorageBlobDTOS()); fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.BUSINESS_DESCRIPTION, Long.valueOf(businessDescription.getId()), businessDescription.getBusinessCommonFiles()); return insert > 0 ? R.ok() : R.fail(); } src/main/java/com/ruoyi/basic/utils/FileUtil.java
@@ -1,9 +1,7 @@ package com.ruoyi.basic.utils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.basic.dto.StorageAttachmentDTO; import com.ruoyi.basic.dto.StorageAttachmentVO; import com.ruoyi.basic.dto.StorageBlobDTO; @@ -344,6 +342,8 @@ StorageBlobVO storageBlobVO = new StorageBlobVO(); BeanUtils.copyProperties(storageBlob, storageBlobVO); storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO)); storageBlobVO.setUrl(buildSignedPreviewUrl(storageBlobVO)); storageBlobVO.setName(storageBlob.getOriginalFilename()); storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO)); storageBlobVO.setStorageAttachmentId(blobIdToAttachmentIdMap.get(storageBlob.getId())); storageBlobDTOS.add(storageBlobVO); @@ -387,16 +387,18 @@ List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList()); List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds); List<StorageBlobVO> storageBlobDTOS = new ArrayList<>(); List<StorageBlobVO> storageBlobVOS = new ArrayList<>(); for (StorageBlob storageBlob : storageBlobs) { StorageBlobVO storageBlobVO = new StorageBlobVO(); BeanUtils.copyProperties(storageBlob, storageBlobVO); storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO)); storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO)); storageBlobVO.setUrl(buildSignedDownloadUrl(storageBlobVO)); storageBlobVO.setName(storageBlob.getOriginalFilename()); storageBlobVO.setStorageAttachmentId(blobIdToAttachmentIdMap.get(storageBlob.getId())); storageBlobDTOS.add(storageBlobVO); storageBlobVOS.add(storageBlobVO); } return storageBlobDTOS; return storageBlobVOS; } /** @@ -417,6 +419,8 @@ StorageBlobVO storageBlobVO = new StorageBlobVO(); BeanUtils.copyProperties(storageBlob, storageBlobVO); storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO)); storageBlobVO.setUrl(buildSignedPreviewUrl(storageBlobVO)); storageBlobVO.setName(storageBlob.getOriginalFilename()); storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO)); storageBlobDTOS.add(storageBlobVO); } src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -192,11 +192,11 @@ */ @Log(title = "销售台账", businessType = BusinessType.DELETE) @DeleteMapping("/delLedger") public AjaxResult remove(@RequestBody Long[] ids) { public R remove(@RequestBody Long[] ids) { if (ids == null || ids.length == 0) { return AjaxResult.error("请传入要删除的ID"); return R.fail("请传入要删除的ID"); } return toAjax(salesLedgerService.deleteSalesLedgerByIds(ids)); return R.ok(salesLedgerService.deleteSalesLedgerByIds(ids)); } /** @@ -216,11 +216,11 @@ */ @Log(title = "销售台账附件删除", businessType = BusinessType.DELETE) @DeleteMapping("/delLedgerFile") public AjaxResult delLedgerFile(@RequestBody Long[] ids) { public R delLedgerFile(@RequestBody Long[] ids) { if (ids == null || ids.length == 0) { return AjaxResult.error("请传入要删除的ID"); return R.fail("请传入要删除的ID"); } return toAjax(commonFileService.deleteSalesLedgerByIds(ids)); return R.ok(salesLedgerService.delLdgerFile(ids)); } /** src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -1,9 +1,5 @@ package com.ruoyi.sales.pojo; import java.math.BigDecimal; import java.time.LocalDate; import java.util.Date; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.framework.aspectj.lang.annotation.Excel; @@ -11,6 +7,10 @@ import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDate; import java.util.Date; /** * 销售台账对象 sales_ledger * src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
@@ -3,7 +3,6 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.aftersalesservice.pojo.AfterSalesService; import com.ruoyi.common.enums.SaleEnum; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.sales.dto.LossProductModelDto; @@ -12,10 +11,10 @@ import com.ruoyi.sales.pojo.SalesLedger; import com.ruoyi.sales.pojo.SalesLedgerProduct; import com.ruoyi.sales.vo.SalesLedgerVo; import org.springframework.web.multipart.MultipartFile; import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotNull; import org.springframework.web.multipart.MultipartFile; import java.math.BigDecimal; import java.util.List; @@ -55,4 +54,6 @@ List<LossProductModelDto> getSalesLedgerWithProductsLoss(Long salesLedgerId); IPage<SalesLedgerDto> listSalesLedger(SalesLedgerDto salesLedgerDto, Page page); Boolean delLdgerFile(Long[] ids); } src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -9,10 +9,13 @@ 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.enums.ApplicationTypeEnum; import com.ruoyi.basic.enums.RecordTypeEnum; 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.utils.FileUtil; import com.ruoyi.common.enums.FileNameType; import com.ruoyi.common.enums.SaleEnum; import com.ruoyi.common.exception.base.BaseException; @@ -24,8 +27,9 @@ 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.mapper.ProductionProductInputMapper; import com.ruoyi.production.mapper.ProductionProductMainMapper; import com.ruoyi.production.mapper.ProductionProductOutputMapper; import com.ruoyi.production.service.ProductionProductMainService; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.domain.SysUser; @@ -41,7 +45,6 @@ import com.ruoyi.sales.vo.SalesLedgerVo; 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; @@ -52,15 +55,10 @@ 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; @@ -118,6 +116,8 @@ ; @Autowired private SysUserMapper sysUserMapper; @Autowired private FileUtil fileUtil; @Override public List<SalesLedger> selectSalesLedgerList(SalesLedgerDto salesLedgerDto) { @@ -487,6 +487,12 @@ return salesLedgerDtoIPage; } @Override public Boolean delLdgerFile(Long[] ids) { fileUtil.deleteStorageBlobsByStorageAttachmentIds(List.of(ids)); return true; } /** * 下划线命名转驼峰命名 */ @@ -582,125 +588,47 @@ @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<SalesLedgerProduct> 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()); // 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<SalesLedgerProduct> 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 ); } //业务和附件绑定 fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs()); // if (salesLedgerDto.getTempFileIds() != null && !salesLedgerDto.getTempFileIds().isEmpty()) { // migrateTempFilesToFormal(salesLedger.getId(), salesLedgerDto.getTempFileIds()); // } return 1; } /** * 将临时文件迁移到正式目录 * * @param businessId 业务ID(销售台账ID) * @param tempFileIds 临时文件ID列表 * @throws IOException 文件操作异常 */ private void migrateTempFilesToFormal(Long businessId, List<String> 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<SalesLedgerProduct> products, SaleEnum type) {