yuan
2026-04-24 b4a9e12e00b78e1aef8acda070434de9ffd0d66a
Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro
已添加2个文件
已修改26个文件
已删除2个文件
1640 ■■■■ 文件已修改
src/main/java/com/ruoyi/basic/dto/StorageAttachmentDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/StorageAttachmentVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/utils/FileUtil.java 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/config/MinioConfig.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/MinioUtils.java 433 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/dto/InspectionTaskDto.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionPlanDto.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionPlanImportDto.java 159 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionPlanVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionPlanMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionPlan.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java 316 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/common/CommonController.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionPlanMapper.xml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/StorageAttachmentDTO.java
@@ -10,5 +10,5 @@
    /**
     * å­˜å‚¨æ–‡ä»¶åˆ—表
     */
    private List<StorageBlobVO> storageBlobVOS;
    private List<StorageBlobDTO> storageBlobDTOs;
}
src/main/java/com/ruoyi/basic/dto/StorageAttachmentVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.ruoyi.basic.dto;
import com.ruoyi.basic.pojo.StorageAttachment;
import lombok.Data;
import java.util.List;
@Data
public class StorageAttachmentVO extends StorageAttachment {
    /**
     * å­˜å‚¨æ–‡ä»¶åˆ—表
     */
    private List<StorageBlobVO> storageBlobVOS;
}
src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.basic.dto;
import com.ruoyi.basic.pojo.StorageBlob;
import lombok.Data;
@Data
public class StorageBlobDTO extends StorageBlob {
    /**
     * é¢„览地址
     */
    private String previewURL;
    /**
     * ä¸‹è½½åœ°å€
     */
    private String downloadURL;
}
src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java
@@ -2,7 +2,10 @@
public enum ApplicationTypeEnum {
    IMAGE("image"),
    FILE("file");
    FILE("file"),
    AFTER_FILE("after_file"),
    BEFORE_FILE("before_file");
    private final String type;
    ApplicationTypeEnum(String type) { this.type = type; }
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -1,7 +1,8 @@
package com.ruoyi.basic.enums;
public enum RecordTypeEnum {
    FILE("file");
    SHIPPING_INFO("shipping_info"),
    INSPECTION_TASK("inspection_task");
    private final String type;
    RecordTypeEnum(String type) { this.type = type; }
src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java
@@ -5,7 +5,7 @@
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 å®žä½“ç±»
@@ -23,12 +23,12 @@
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    private LocalDateTime createTime;
    /** æ›´æ–°æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    private LocalDateTime updateTime;
    /**
     * é€»è¾‘删除
@@ -55,4 +55,4 @@
     */
    @TableField(value = "storage_blob_id")
    private Long storageBlobId;
}
}
src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java
@@ -16,14 +16,6 @@
 * @since 2025-05-29
 */
public interface StorageAttachmentService extends IService<StorageAttachment> {
    /**
     * æŸ¥è¯¢é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息
     * @param recordId å…³è”记录id
     * @param recordType å…³è”记录类型
     * @param fileType æ–‡ä»¶ç±»åž‹
     * @return æ–‡ä»¶ä¿¡æ¯åˆ—表
     */
    List<StorageAttachment> selectStorageAttachments(Long recordId, StorageAttachmentRecordType recordType, String fileType);
    /**
     * ä¿å­˜é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息
src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java
@@ -8,7 +8,6 @@
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.common.constant.StorageAttachmentConstants;
import com.ruoyi.common.enums.StorageAttachmentRecordType;
import com.ruoyi.common.utils.MinioUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -31,28 +30,6 @@
    private final StorageBlobService storageBlobService;
    private final MinioUtils minioUtils;
    @Override
    public List<StorageAttachment> selectStorageAttachments(Long recordId, StorageAttachmentRecordType recordType, String fileType) {
        // todo fileChange
//        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
//                .eq(StorageAttachment::getRecordId, recordId)
//                .eq(StorageAttachment::getRecordType, recordType.ordinal())
//                .eq(StorageAttachment::getName, fileType));
//        if (storageAttachments != null) {
//            for (StorageAttachment storageAttachment : storageAttachments) {
//                StorageBlob storageBlob = storageBlobMapper.selectById(storageAttachment.getStorageBlobId());
//                StorageBlobDTO storageBlobDTO = new StorageBlobDTO();
//                BeanUtils.copyProperties(storageBlob, storageBlobDTO);
//                storageBlobDTO.setUrl(minioUtils.getPreviewUrl(storageBlob.getBucketName(), storageBlob.getBucketName(), true));
//                storageAttachment.setStorageBlobDTO(storageBlobDTO);
//            }
//        }
//        return storageAttachments;
        return null;
    }
    @Override
    public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, StorageAttachmentConstants fileType) {
src/main/java/com/ruoyi/basic/utils/FileUtil.java
@@ -2,7 +2,8 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ruoyi.basic.dto.StorageAttachmentDTO;
import com.ruoyi.basic.dto.StorageAttachmentVO;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
@@ -48,10 +49,10 @@
     * @param application     æ–‡ä»¶ç”¨é€”
     * @param recordType      å…³è”记录类型
     * @param recordId        å…³è”记录id
     * @param storageBlobVOS æ–‡ä»¶ä¿¡æ¯
     * @param storageBlobDTOS æ–‡ä»¶ä¿¡æ¯
     */
    public void saveStorageAttachment(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, List<StorageBlobVO> storageBlobVOS) {
        if (CollectionUtils.isEmpty(storageBlobVOS)) {
    public void saveStorageAttachment(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS) {
        if (CollectionUtils.isEmpty(storageBlobDTOS)) {
            throw new RuntimeException("文件信息不能为空");
        }
        if (application == null) {
@@ -66,13 +67,14 @@
        // åˆ é™¤æ—§é™„件信息
        deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
        List<StorageAttachment> storageAttachments = new ArrayList<>();
        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
        for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
            StorageAttachment storageAttachment = new StorageAttachment();
            storageAttachment.setApplication(application.getType());
            storageAttachment.setRecordType(recordType.getType());
            storageAttachment.setRecordId(recordId);
            storageAttachment.setStorageBlobId(storageBlobVO.getId());
            storageAttachment.setStorageBlobId(storageBlobDTO.getId());
            storageAttachment.setDeleted(0L);
            storageAttachments.add(storageAttachment);
        }
        storageAttachmentMapper.insert(storageAttachments);
    }
@@ -102,10 +104,10 @@
     *
     * @param application æ–‡ä»¶ç”¨é€”
     * @param recordType  å…³è”记录类型
     * @param recordId    å…³è”记录id
     * @param recordIds    å…³è”记录id
     */
    public void deleteStorageBlobsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
        if (recordId == null || recordId <= 0) {
    public void deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds) {
        if (recordIds == null || recordIds.isEmpty()) {
            throw new RuntimeException("关联记录id不能为空");
        }
        if (application == null) {
@@ -116,7 +118,7 @@
        }
        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordType, recordType.getType())
                .eq(StorageAttachment::getRecordId, recordId)
                .in(StorageAttachment::getRecordId, recordIds)
                .eq(StorageAttachment::getApplication, application.getType()));
        if (CollectionUtils.isNotEmpty(storageAttachments)) {
            List<Long> storageAttachmentIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId)
@@ -135,6 +137,13 @@
        storageAttachmentMapper.deleteByIds(storageAttachmentIds);
    }
    /**
     * åˆ é™¤æ–‡ä»¶å…³è”信息
     *
     * @param application æ–‡ä»¶ç”¨é€”
     * @param recordType  å…³è”记录类型
     * @param recordId    å…³è”记录id
     */
    public void deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
        if (recordId == null || recordId <= 0) {
            throw new RuntimeException("关联记录id不能为空");
@@ -145,7 +154,7 @@
        if (recordType == null) {
            throw new RuntimeException("关联记录类型不能为空");
        }
        deleteStorageBlobsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
        deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(application, recordType, Arrays.asList(recordId));
        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordType, recordType.getType())
                .eq(StorageAttachment::getRecordId, recordId)
@@ -153,7 +162,31 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息
     * æ‰¹é‡åˆ é™¤æ–‡ä»¶å…³è”信息 attachment
     *
     * @param application æ–‡ä»¶ç”¨é€”
     * @param recordType  å…³è”记录类型
     * @param recordIds   å…³è”记录id
     */
    public void deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds) {
        if (recordIds == null || recordIds.isEmpty()) {
            throw new RuntimeException("关联记录id不能为空");
        }
        if (application == null) {
            throw new RuntimeException("文件用途不能为空");
        }
        if (recordType == null) {
            throw new RuntimeException("关联记录类型不能为空");
        }
        deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(application, recordType, recordIds);
        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordType, recordType.getType())
                .in(StorageAttachment::getRecordId, recordIds)
                .eq(StorageAttachment::getApplication, application.getType()));
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息 attachment
     *
     * @param storageAttachmentIds æ–‡ä»¶id
     */
@@ -165,7 +198,7 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶ç”¨é€”、关联记录类型、关联记录id获取文件关联信息
     * é€šè¿‡æ–‡ä»¶ç”¨é€”、关联记录类型、关联记录id获取文件关联信息 attachment
     *
     * @param application æ–‡ä»¶ç”¨é€”
     * @param recordType  å…³è”记录类型
@@ -187,11 +220,11 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息 blob
     *
     * @param storageAttachmentIds æ–‡ä»¶id
     */
    public List<StorageBlobVO> getStorageBlobDTOsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
    public List<StorageBlobVO> getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return null;
@@ -210,12 +243,63 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息存在过期时间
     * é€šè¿‡æ–‡ä»¶ç”¨é€”、关联记录类型、关联记录id获取文件信息 blob
     *
     * @param application æ–‡ä»¶ç”¨é€”
     * @param recordType  å…³è”记录类型
     * @param recordId    å…³è”记录id
     */
    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return null;
        }
        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
        for (StorageBlob storageBlob : storageBlobs) {
            StorageBlobVO storageBlobVO = new StorageBlobVO();
            BeanUtils.copyProperties(storageBlob, storageBlobVO);
            storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO));
            storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO));
            storageBlobDTOS.add(storageBlobVO);
        }
        return storageBlobDTOS;
    }
    /**
     * é€šè¿‡æ–‡ä»¶ç”¨é€”、关联记录类型、关联记录id获取文件信息 è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰ blob
     *
     * @param application æ–‡ä»¶ç”¨é€”
     * @param recordType  å…³è”记录类型
     * @param recordId    å…³è”记录id
     * @param expired     è¿‡æœŸæ—¶é—´
     */
    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return null;
        }
        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
        for (StorageBlob storageBlob : storageBlobs) {
            StorageBlobVO storageBlobVO = new StorageBlobVO();
            BeanUtils.copyProperties(storageBlob, storageBlobVO);
            storageBlobVO.setPreviewURL(buildSignedUrl(storageBlobVO, "/preview/", expired));
            storageBlobVO.setDownloadURL(buildSignedUrl(storageBlobVO, "/download/", expired));
            storageBlobDTOS.add(storageBlobVO);
        }
        return storageBlobDTOS;
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息存在过期时间 è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰ blob
     *
     * @param storageAttachmentIds æ–‡ä»¶id
     * @param expired              è¿‡æœŸæ—¶é—´
     */
    public List<StorageBlobVO> getStorageBlobDTOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
    public List<StorageBlobVO> getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return null;
@@ -234,109 +318,109 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息 attachment
     *
     * @param storageAttachmentIds æ–‡ä»¶id
     */
    public List<StorageAttachmentDTO> getStorageAttachmentDTOsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
    public List<StorageAttachmentVO> getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return new ArrayList<>();
        }
        List<StorageAttachmentDTO> storageAttachmentDTOS = new ArrayList<>();
        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
        for (StorageAttachment storageAttachment : storageAttachments) {
            StorageAttachmentDTO storageAttachmentDTO = new StorageAttachmentDTO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentDTO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
            if (CollectionUtils.isEmpty(storageBlobVOS)) {
                storageAttachmentDTO.setStorageBlobVOS(new ArrayList<>());
                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
            } else {
                storageAttachmentDTO.setStorageBlobVOS(storageBlobVOS);
                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
            }
            storageAttachmentDTOS.add(storageAttachmentDTO);
            storageAttachmentVOS.add(storageAttachmentVO);
        }
        return storageAttachmentDTOS;
        return storageAttachmentVOS;
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息存在过期时间
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息存在过期时间 è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰ attachment
     *
     * @param storageAttachmentIds æ–‡ä»¶id
     * @param expired              è¿‡æœŸæ—¶é—´
     */
    public List<StorageAttachmentDTO> getStorageAttachmentDTOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
    public List<StorageAttachmentVO> getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return new ArrayList<>();
        }
        List<StorageAttachmentDTO> storageAttachmentDTOS = new ArrayList<>();
        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
        for (StorageAttachment storageAttachment : storageAttachments) {
            StorageAttachmentDTO storageAttachmentDTO = new StorageAttachmentDTO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentDTO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
            if (CollectionUtils.isEmpty(storageBlobVOS)) {
                storageAttachmentDTO.setStorageBlobVOS(new ArrayList<>());
                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
            } else {
                storageAttachmentDTO.setStorageBlobVOS(storageBlobVOS);
                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
            }
            storageAttachmentDTOS.add(storageAttachmentDTO);
            storageAttachmentVOS.add(storageAttachmentVO);
        }
        return storageAttachmentDTOS;
        return storageAttachmentVOS;
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息 attachment
     *
     * @param application åº”用
     * @param recordType  è®°å½•类型
     * @param recordId    è®°å½•id
     */
    public List<StorageAttachmentDTO> getStorageAttachmentDTOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
    public List<StorageAttachmentVO> getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return new ArrayList<>();
        }
        List<StorageAttachmentDTO> storageAttachmentDTOS = new ArrayList<>();
        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
        for (StorageAttachment storageAttachment : storageAttachments) {
            StorageAttachmentDTO storageAttachmentDTO = new StorageAttachmentDTO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentDTO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
            if (CollectionUtils.isEmpty(storageBlobVOS)) {
                storageAttachmentDTO.setStorageBlobVOS(new ArrayList<>());
                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
            } else {
                storageAttachmentDTO.setStorageBlobVOS(storageBlobVOS);
                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
            }
            storageAttachmentDTOS.add(storageAttachmentDTO);
            storageAttachmentVOS.add(storageAttachmentVO);
        }
        return storageAttachmentDTOS;
        return storageAttachmentVOS;
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息存在过期时间
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件信息存在过期时间 è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰ attachment
     *
     * @param application åº”用
     * @param recordType  è®°å½•类型
     * @param recordId    è®°å½•id
     * @param expired     è¿‡æœŸæ—¶é—´
     */
    public List<StorageAttachmentDTO> getStorageAttachmentDTOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
    public List<StorageAttachmentVO> getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
        if (CollectionUtils.isEmpty(storageAttachments)) {
            return new ArrayList<>();
        }
        List<StorageAttachmentDTO> storageAttachmentDTOS = new ArrayList<>();
        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
        for (StorageAttachment storageAttachment : storageAttachments) {
            StorageAttachmentDTO storageAttachmentDTO = new StorageAttachmentDTO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentDTO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
            if (CollectionUtils.isEmpty(storageBlobVOS)) {
                storageAttachmentDTO.setStorageBlobVOS(new ArrayList<>());
                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
            } else {
                storageAttachmentDTO.setStorageBlobVOS(storageBlobVOS);
                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
            }
            storageAttachmentDTOS.add(storageAttachmentDTO);
            storageAttachmentVOS.add(storageAttachmentVO);
        }
        return storageAttachmentDTOS;
        return storageAttachmentVOS;
    }
    /**
@@ -346,7 +430,7 @@
     */
    public List<String> getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds) {
        List<String> res = new ArrayList<>();
        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
            res.add(buildSignedPreviewUrl(storageBlobVO));
        }
@@ -354,14 +438,14 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件预览地址存在过期时间
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件预览地址存在过期时间 è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰
     *
     * @param storageAttachmentIds æ–‡ä»¶å…³è”id
     * @param expired              è¿‡æœŸæ—¶é—´
     */
    public List<String> getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
        List<String> res = new ArrayList<>();
        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
            res.add(buildSignedUrl(storageBlobVO, "/preview/", expired));
        }
@@ -375,7 +459,7 @@
     */
    public List<String> getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds) {
        List<String> res = new ArrayList<>();
        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
            res.add(buildSignedDownloadUrl(storageBlobVO));
        }
@@ -383,14 +467,14 @@
    }
    /**
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件下载地址存在过期时间
     * é€šè¿‡æ–‡ä»¶å…³è”id获取文件下载地址存在过期时间 è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´ï¼ˆåˆ†é’Ÿï¼‰
     *
     * @param storageAttachmentIds æ–‡ä»¶å…³è”id
     * @param expired              è¿‡æœŸæ—¶é—´
     */
    public List<String> getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
        List<String> res = new ArrayList<>();
        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
            res.add(buildSignedUrl(storageBlobVO, "/download/", expired));
        }
@@ -489,7 +573,7 @@
    }
    /**
     * åŽ‹ç¼©æ–‡ä»¶
     * åŽ‹ç¼©æ–‡ä»¶ å›¾ç‰‡
     *
     * @param file æ–‡ä»¶
     * @return åŽ‹ç¼©åŽçš„æ–‡ä»¶
src/main/java/com/ruoyi/common/config/MinioConfig.java
ÎļþÒÑɾ³ý
src/main/java/com/ruoyi/common/utils/MinioUtils.java
ÎļþÒÑɾ³ý
src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
@@ -59,7 +59,7 @@
    @PostMapping("/addOrEditInspectionTask")
    @Operation(summary = "巡检任务表新增修改")
    @Transactional(rollbackFor = Exception.class)
    public R addOrEditInspectionTask(@RequestBody InspectionTaskDto inspectionTaskDto) throws IOException {
    public R addOrEditInspectionTask(@RequestBody InspectionTaskDto inspectionTaskDto) {
        return R.ok(inspectionTaskService.addOrEditInspectionTask(inspectionTaskDto));
    }
src/main/java/com/ruoyi/inspectiontask/dto/InspectionTaskDto.java
@@ -1,7 +1,8 @@
package com.ruoyi.inspectiontask.dto;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
import com.ruoyi.sales.pojo.CommonFile;
import lombok.Data;
import java.util.List;
@@ -16,9 +17,12 @@
//
//    private List<StorageAttachment> attachments;
    private List<String> tempFileIds;
    private List<CommonFile> commonFileList; //生产中
    private List<CommonFile> commonFileListAfter;  //生产后
    private List<CommonFile> commonFileListBefore; //生产前
    private List<StorageBlobDTO> commonFileListDTO; //生产中
    private List<StorageBlobDTO> commonFileListAfterDTO; //生产后
    private List<StorageBlobDTO> commonFileListBeforeDTO; //生产前
    private List<StorageBlobVO> commonFileListVO; //生产中
    private List<StorageBlobVO> commonFileListAfterVO; //生产后
    private List<StorageBlobVO> commonFileListBeforeVO; //生产前
}
src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java
@@ -6,8 +6,6 @@
import com.ruoyi.inspectiontask.dto.InspectionTaskDto;
import com.ruoyi.inspectiontask.pojo.InspectionTask;
import java.io.IOException;
/**
 * @author :yys
 * @date : 2025/9/19 10:49
@@ -16,7 +14,7 @@
    IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto);
    int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto) throws IOException;
    int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto);
    int delByIds(Long[] ids);
}
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
@@ -5,13 +5,9 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.mapper.StorageAttachmentMapper;
import com.ruoyi.basic.mapper.StorageBlobMapper;
import com.ruoyi.basic.pojo.StorageBlob;
import com.ruoyi.basic.service.StorageAttachmentService;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.utils.MinioUtils;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
@@ -21,18 +17,13 @@
import com.ruoyi.inspectiontask.service.InspectionTaskService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -48,19 +39,9 @@
    private final InspectionTaskMapper inspectionTaskMapper;
    private final StorageAttachmentService storageAttachmentService;
    private final StorageBlobMapper storageBlobMapper;
    private final StorageAttachmentMapper storageAttachmentMapper;
    private final MinioUtils minioUtils;
    private final SysUserMapper sysUserMapper;
    private final CommonFileMapper commonFileMapper;
    private final CommonFileServiceImpl commonFileService;
    private final FileUtil fileUtil;
    @Override
    public IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) {
@@ -114,16 +95,6 @@
                        SysUser::getUserId,
                        SysUser::getNickName,
                        (existing, replacement) -> existing));
        //处理附件
        List<CommonFile> commonFiles = commonFileMapper.selectList(new LambdaQueryWrapper<CommonFile>()
                .in(CommonFile::getCommonId, ids)
                .in(CommonFile::getType, Arrays.asList(FileNameType.INSPECTION.getValue(), FileNameType.INSPECTION_PRODUCTION_BEFORE.getValue(), FileNameType.INSPECTION_PRODUCTION_AFTER.getValue())));
        if(commonFiles == null){
            commonFiles = new ArrayList<>();
        }
        AtomicReference<List<CommonFile>> finalCommonFiles = new AtomicReference<>(commonFiles);
        List<InspectionTaskDto> dtoList = entityPage.getRecords().stream().map(inspectionTask -> {
            InspectionTaskDto dto = new InspectionTaskDto();
            BeanUtils.copyProperties(inspectionTask, dto);  // å¤åˆ¶ä¸»å¯¹è±¡å±žæ€§
@@ -150,11 +121,10 @@
            }
            dto.setDateStr(inspectionTask.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            finalCommonFiles.set(finalCommonFiles.get().stream().filter(commonFile -> commonFile.getCommonId().equals(inspectionTask.getId())).collect(Collectors.toList()));
            // åˆå§‹åŒ–三个附件列表
            dto.setCommonFileList(finalCommonFiles.get().stream().filter(commonFile -> commonFile.getType().equals(FileNameType.INSPECTION.getValue())).collect(Collectors.toList()));
            dto.setCommonFileListAfter(finalCommonFiles.get().stream().filter(commonFile -> commonFile.getType().equals(FileNameType.INSPECTION_PRODUCTION_AFTER.getValue())).collect(Collectors.toList()));
            dto.setCommonFileListBefore(finalCommonFiles.get().stream().filter(commonFile -> commonFile.getType().equals(FileNameType.INSPECTION_PRODUCTION_BEFORE.getValue())).collect(Collectors.toList()));
            dto.setCommonFileListVO(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId()));
            dto.setCommonFileListAfterVO(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.AFTER_FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId()));
            dto.setCommonFileListBeforeVO(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.BEFORE_FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId()));
            return dto;
@@ -167,32 +137,9 @@
        return resultPage;
    }
    // æå–创建BlobDTO的公共方法
    private StorageBlobVO createBlobDto(StorageBlob blob) {
        StorageBlobVO dto = new StorageBlobVO();
        BeanUtils.copyProperties(blob, dto);
        // todo fileChange
//        // è®¾ç½®URL
//        dto.setUrl(minioUtils.getPreviewUrls(
//                blob.getBucketFilename(),
//                blob.getBucketName(),
//                true
//        ));
//
//        // è®¾ç½®ä¸‹è½½URL
//        dto.setDownloadUrl(minioUtils.getDownloadUrls(
//                blob.getBucketFilename(),
//                blob.getBucketName(),
//                blob.getOriginalFilename(),
//                true
//        ));
        return dto;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto) throws IOException {
    public int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto) {
        InspectionTask inspectionTask = new InspectionTask();
        BeanUtils.copyProperties(inspectionTaskDto, inspectionTask);
        inspectionTask.setRegistrantId(SecurityUtils.getLoginUser().getUserId());
@@ -203,7 +150,11 @@
        } else {
            i = inspectionTaskMapper.updateById(inspectionTask);
        }
        commonFileService.migrateTempFilesToFormal(inspectionTask.getId(),inspectionTaskDto.getTempFileIds());
        // ä¿å­˜æ–‡ä»¶
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId(), inspectionTaskDto.getCommonFileListDTO());
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.AFTER_FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId(), inspectionTaskDto.getCommonFileListAfterDTO());
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.BEFORE_FILE, RecordTypeEnum.INSPECTION_TASK, inspectionTask.getId(), inspectionTaskDto.getCommonFileListBeforeDTO());
        return i;
    }
@@ -214,9 +165,10 @@
        if (ids == null || ids.length == 0) {
            return 0;
        }
        commonFileService.deleteByBusinessIds(Arrays.asList(ids),FileNameType.INSPECTION.getValue());
        commonFileService.deleteByBusinessIds(Arrays.asList(ids),FileNameType.INSPECTION_PRODUCTION_BEFORE.getValue());
        commonFileService.deleteByBusinessIds(Arrays.asList(ids),FileNameType.INSPECTION_PRODUCTION_AFTER.getValue());
        // åˆ é™¤æ–‡ä»¶
        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.INSPECTION_TASK, Arrays.asList(ids));
        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.AFTER_FILE, RecordTypeEnum.INSPECTION_TASK, Arrays.asList(ids));
        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.BEFORE_FILE, RecordTypeEnum.INSPECTION_TASK, Arrays.asList(ids));
        return inspectionTaskMapper.deleteBatchIds(Arrays.asList(ids));
    }
src/main/java/com/ruoyi/production/bean/dto/ProductionPlanDto.java
@@ -16,30 +16,10 @@
@Data
public class ProductionPlanDto extends ProductionPlan {
    /**
     * ç‰©æ–™ç¼–码
     */
    @Schema(description = "物料编码")
    @Excel(name = "物料编码")
    private String materialCode;
    /**
     * äº§å“åç§°
     */
    @Schema(description = "产品名称")
    @Excel(name = "产品名称")
    private String productName;
    /**
     * å®¢æˆ·åç§°
     */
    @Schema(description = "客户名称")
    @Excel(name = "客户名称")
    private String customerName;
    /**
     * äº§å“è§„æ ¼
     */
    @Schema(description = "产品规格")
    @Excel(name = "产品规格")
    private String model;
@@ -59,17 +39,7 @@
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate planCompleteTime;
    /**
     * å…³è”物料信息表
     */
    @Schema(description = "关联物料信息表ID")
    private Long productMaterialId;
    /**
     * å¼ºåº¦
     */
    @Schema(description = "强度")
    @Excel(name = "强度")
    private String strength;
    @Schema(description = "产品ID")
    private Long productId;
}
src/main/java/com/ruoyi/production/bean/dto/ProductionPlanImportDto.java
@@ -1,12 +1,11 @@
package com.ruoyi.production.bean.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.time.LocalDate;
/**
 * <br>
@@ -20,126 +19,43 @@
@Data
@Schema(name = "销售生产需求 Excel导入导出DTO")
public class ProductionPlanImportDto {
    /**
     * ç”³è¯·å•编号
     */
    @Schema(description = "申请单编号")
    @Excel(name = "申请单编号")
    private String applyNo;
    /**
     * å®¢æˆ·åç§°
     */
    @Schema(description = "客户名称")
    @Excel(name = "客户名称")
    private String customerName;
    @Schema(description = "主生产计划号")
    @Excel(name = "主生产计划号")
    private String mpsNo;
    /**
     * ç‰©æ–™ç¼–码
     */
    @Schema(description = "物料编码")
    @Excel(name = "物料编码")
    private String materialCode;
    @Schema(description = "需求日期")
    @Excel(name = "需求日期")
    private LocalDate requiredDate;
    /**
     * äº§å“åç§°
     */
    @Schema(description = "备注")
    @Excel(name = "备注")
    private String remark;
    @Schema(description = "需求数量")
    @Excel(name = "需求数量")
    private BigDecimal qtyRequired;
    @Schema(description = "来源 é”€å”®/内部")
    @Excel(name = "来源 é”€å”®/内部")
    private String source;
    @Schema(description = "承诺日期")
    @Excel(name = "承诺日期")
    private LocalDate promisedDeliveryDate;
    @Schema(description = "产品名称")
    @Excel(name = "产品名称")
    private String productName;
    /**
     * äº§å“è§„æ ¼
     */
    @Schema(description = "产品规格")
    @Excel(name = "产品规格")
    private String productSpec;
    @Schema(description = "规格型号")
    @Excel(name = "规格型号")
    private String model;
    /**
     * é•¿
     */
    @Schema(description = "长")
    @Excel(name = "长(mm)")
    private Integer length;
    @Schema(description = "单位")
    @Excel(name = "单位")
    private String unit;
    /**
     * å®½
     */
    @Schema(description = "宽")
    @Excel(name = "宽(mm)")
    private Integer width;
    /**
     * é«˜
     */
    @Schema(description = "高")
    @Excel(name = "高(mm)")
    private Integer height;
    /**
     * å—æ•°
     */
    @Schema(description = "块数")
    @Excel(name = "块数")
    private Integer quantity;
    /**
     * æ–¹æ•°
     */
    @Schema(description = "方数")
    @Excel(name = "方数")
    private BigDecimal volume;
    /**
     * å¼ºåº¦
     */
    @Schema(description = "强度")
    @Excel(name = "强度")
    private String strength;
    /**
     * å¼€å§‹æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @Schema(description = "开始日期")
    @Excel(name = "开始日期", width = 20, dateFormat = "yyyy-MM-dd")
    private Date startDate;
    /**
     * ç»“束日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @Schema(description = "结束日期")
    @Excel(name = "结束日期", width = 20, dateFormat = "yyyy-MM-dd")
    private Date endDate;
    /**
     * æäº¤äºº
     */
    @Schema(description = "提交人")
    @Excel(name = "提交人")
    private String submitter;
    /**
     * æäº¤äººç»„织
     */
    @Schema(description = "提交人组织")
    @Excel(name = "提交人组织")
    private String submitOrg;
    /**
     * å¤‡æ³¨1
     */
    @Schema(description = "备注1")
    @Excel(name = "备注1")
    private String remarkOne;
    /**
     * å¤‡æ³¨2
     */
    @Schema(description = "备注2")
    @Excel(name = "备注2")
    private String remarkTwo;
    /**
     * åˆ›å»ºäºº
@@ -155,22 +71,11 @@
    @Excel(name = "修改人", type = Excel.Type.EXPORT)
    private String modifierName;
    /**
     * æ•°æ®åŒæ­¥ç±»åž‹ï¼š1=手动 2=定时任务
     */
    @Schema(description = "数据同步类型:1=手动 2=定时任务")
    private Integer dataSyncType;
    /**
     * æ•°æ®æ¥æºç±»åž‹ï¼š1=同步 2=新增
     * å·²ä¸‹å‘数量
     */
    @Schema(description = "数据来源类型:1=同步 2=新增")
    private Integer dataSourceType;
    /**
     * ä¸‹å‘数量
     */
    @Schema(description = "下发数量")
    @Excel(name = "下发数量", type = Excel.Type.EXPORT)
    @Schema(description = "已下发数量")
    @Excel(name = "已下发数量", type = Excel.Type.EXPORT)
    private BigDecimal assignedQuantity;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionPlanVo.java
@@ -9,8 +9,6 @@
@Data
@Schema(name = "ProductionPlanVo", description = "生产计划返回对象")
public class ProductionPlanVo extends ProductionPlan {
    @Schema(description = "物料编码")
    private String materialCode;
    @Schema(description = "产品名称")
    private String productName;
@@ -21,6 +19,6 @@
    @Schema(description = "单位")
    private String unit;
    @Schema(description = "关联产品物料ID")
    private Long productMaterialId;
    @Schema(description = "产品ID")
    private Long productId;
}
src/main/java/com/ruoyi/production/mapper/ProductionPlanMapper.java
@@ -27,5 +27,4 @@
    List<ProductionPlanDto> selectWithMaterialByIds(@Param("ids") List<Long> ids);
    ProductionPlanDto selectProductionPlanDtoById(@Param("productionPlanId") Long productionPlanId);
}
src/main/java/com/ruoyi/production/pojo/ProductionPlan.java
@@ -30,6 +30,12 @@
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @Schema(description = "销售台账id")
    private Long salesLedgerId;
    @Schema(description = "销售产品规格id")
    private Long salesLedgerProductId;
    @Schema(description = "主生产计划号")
    private String mpsNo;
@@ -55,6 +61,10 @@
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @Schema(description = "部门ID")
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @Schema(description = "产品型号id")
    private Long productModelId;
@@ -64,7 +74,7 @@
    @Schema(description = "是否下发制造订单")
    private Boolean issued;
    @Schema(description = "来源")
    @Schema(description = "来源 é”€å”®/内部")
    private String source;
    @Schema(description = "审核状态")
@@ -72,9 +82,6 @@
    @Schema(description = "承诺日期")
    private LocalDate promisedDeliveryDate;
    @Schema(description = "申请单编号")
    private String applyNo;
    @Schema(description = "状态 0未下发 1部分下发 2已下发")
    private Integer status;
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.bean.BeanUtil;
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;
@@ -62,7 +63,7 @@
    private final FileUtil fileUtil;
    @Override
    public com.baomidou.mybatisplus.core.metadata.IPage<ProductionOrderVo> pageProductionOrder(Page<ProductionOrderDto> page, ProductionOrderDto dto) {
    public IPage<ProductionOrderVo> pageProductionOrder(Page<ProductionOrderDto> page, ProductionOrderDto dto) {
        Page<ProductionOrderVo> result = (Page<ProductionOrderVo>) baseMapper.pageProductionOrder(page, dto);
        fillProductImages(result.getRecords());
        return result;
@@ -359,18 +360,18 @@
        if (defaultDecimal(productionOrder.getQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("quantity must be greater than 0");
        }
        if (productionOrder.getTechnologyRoutingId() == null) {
            // æœªæ˜¾å¼æŒ‡å®šå·¥è‰ºè·¯çº¿æ—¶ï¼ŒæŒ‰äº§å“è§„格选最新一条工艺作为默认路线。
            TechnologyRouting technologyRouting = technologyRoutingMapper.selectOne(
                    Wrappers.<TechnologyRouting>lambdaQuery()
                            .eq(TechnologyRouting::getProductModelId, productionOrder.getProductModelId())
                            .orderByDesc(TechnologyRouting::getId)
                            .last("limit 1"));
            if (technologyRouting == null) {
                throw new ServiceException("No technology routing found for the product model");
            }
            productionOrder.setTechnologyRoutingId(technologyRouting.getId());
        }
//        if (productionOrder.getTechnologyRoutingId() == null) {
//            // æœªæ˜¾å¼æŒ‡å®šå·¥è‰ºè·¯çº¿æ—¶ï¼ŒæŒ‰äº§å“è§„格选最新一条工艺作为默认路线。
//            TechnologyRouting technologyRouting = technologyRoutingMapper.selectOne(
//                    Wrappers.<TechnologyRouting>lambdaQuery()
//                            .eq(TechnologyRouting::getProductModelId, productionOrder.getProductModelId())
//                            .orderByDesc(TechnologyRouting::getId)
//                            .last("limit 1"));
//            if (technologyRouting == null) {
//                throw new ServiceException("No technology routing found for the product model");
//            }
//            productionOrder.setTechnologyRoutingId(technologyRouting.getId());
//        }
        if (oldOrder != null && ProductOrderStatusEnum.isStarted(oldOrder.getStatus())) {
            // å¼€å·¥åŽåªå…è®¸ä¿®æ­£éžæ ¸å¿ƒå­—段,核心生产依据锁定。
            if (!Objects.equals(oldOrder.getProductModelId(), productionOrder.getProductModelId())
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -17,6 +18,7 @@
import com.ruoyi.production.mapper.ProductionPlanMapper;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionPlan;
import com.ruoyi.production.service.ProductionOrderService;
import com.ruoyi.production.service.ProductionPlanService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@@ -24,20 +26,24 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ProductionPlanServiceImpl extends ServiceImpl<ProductionPlanMapper, ProductionPlan> implements ProductionPlanService {
    private final ProductionPlanMapper productionPlanMapper;
    private static final int PLAN_STATUS_WAIT = 0;
    private static final int PLAN_STATUS_PARTIAL = 1;
    private static final int PLAN_STATUS_ISSUED = 2;
    private final ProductionPlanMapper productionPlanMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderService productionOrderService;
    @Override
    public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) {
@@ -45,133 +51,98 @@
    }
    /**
     * åˆå¹¶ç”Ÿäº§è®¡åˆ’
     * åˆå¹¶ç”Ÿäº§è®¡åˆ’并下发生产订单。
     * ä¸šåŠ¡çº¦æŸï¼š
     * 1. ä»…允许同一产品型号的计划合并;
     * 2. å·²ä¸‹å‘或部分下发的计划禁止再次合并;
     * 3. ä¸‹å‘数量不能大于所选计划需求总量;
     * 4. è®¢å•创建统一复用 ProductionOrderService.saveProductionOrder,确保工艺/BOM/领料主单等后续逻辑一致。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean combine(ProductionPlanDto productionPlanDto) {
        if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
        if (productionPlanDto == null || productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
            return false;
        }
        //  æŸ¥è¯¢ä¸»ç”Ÿäº§è®¡åˆ’
        List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(productionPlanDto.getIds());
        if (plans == null || plans.isEmpty()) {
            throw new ServiceException("下发失败,生产计划不存在");
        List<Long> planIds = productionPlanDto.getIds().stream()
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (planIds.isEmpty()) {
            throw new ServiceException("下发失败,未选择生产计划");
        }
        //  æ ¡éªŒæ˜¯å¦å­˜åœ¨ä¸åŒçš„产品名称
        String firstProductName = plans.get(0).getProductName();
        if (plans.stream().anyMatch(p -> p.getProductName() == null || !p.getProductName().equals(firstProductName))) {
            throw new BaseException("合并失败,存在不同的产品名称");
        List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(planIds);
        if (plans == null || plans.isEmpty() || plans.size() != planIds.size()) {
            throw new ServiceException("下发失败,生产计划不存在或已被删除");
        }
        // æ ¡éªŒæ˜¯å¦å­˜åœ¨ä¸åŒçš„产品规格
        String firstProductSpec = plans.get(0).getModel();
        if (plans.stream().anyMatch(p -> p.getModel() == null || !p.getModel().equals(firstProductSpec))) {
            throw new BaseException("合并失败,存在不同的产品规格");
        ProductionPlanDto firstPlan = plans.getFirst();
        if (firstPlan.getProductModelId() == null) {
            throw new ServiceException("下发失败,生产计划缺少产品型号");
        }
        // åˆ›å»ºç”Ÿäº§è®¢å•
        boolean hasDifferentModel = plans.stream()
                .anyMatch(item -> !Objects.equals(item.getProductModelId(), firstPlan.getProductModelId()));
        if (hasDifferentModel) {
            throw new BaseException("合并失败,所选生产计划的产品型号不一致");
        }
        boolean hasIssuedPlan = plans.stream()
                .anyMatch(item -> item.getStatus() != null && item.getStatus() != PLAN_STATUS_WAIT);
        if (hasIssuedPlan) {
            throw new BaseException("合并失败,所选生产计划存在已下发或部分下发数据");
        }
        BigDecimal totalRequiredQuantity = plans.stream()
                .map(ProductionPlan::getQtyRequired)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (totalRequiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("下发失败,所选生产计划需求总量必须大于0");
        }
        BigDecimal assignedQuantity = productionPlanDto.getTotalAssignedQuantity();
        if (assignedQuantity == null || assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("下发失败,下发数量必须大于0");
        }
        if (assignedQuantity.compareTo(totalRequiredQuantity) > 0) {
            throw new ServiceException("下发失败,下发数量不能大于计划需求总量");
        }
        ProductionOrder productionOrder = new ProductionOrder();
        productionOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity());
        productionOrder.setProductionPlanIds(formatPlanIds(planIds));
        productionOrder.setProductModelId(firstPlan.getProductModelId());
        productionOrder.setQuantity(assignedQuantity);
        productionOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
//        // å åŠ å‰©ä½™æ–¹æ•°
//        BigDecimal totalRemainingVolume = plans.stream()
//                .map(ProductionPlan::getRemainingVolume)
//                .filter(Objects::nonNull)
//                .reduce(BigDecimal.ZERO, BigDecimal::add);
//        // åˆ¤æ–­ä¸‹å‘数量是否大于等于剩余方数
//        if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalRemainingVolume) > 0) {
//            throw new BaseException("操作失败,下发数量不能大于剩余方数");
//        }
//
//        // åˆ›å»ºç”Ÿäº§è®¢å•
//        ProductOrder productOrder = new ProductOrder();
//        productOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity());
//        productOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
//        productOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
//        productOrder.setStrength(productionPlanDto.getStrength());
//        productOrder.setProductMaterialSkuId(plans.get(0).getProductMaterialSkuId());
//
//        Long orderId = productOrderService.insertProductOrder(productOrder);
//
//        //  å½“下发的产品为砌块或板材,就拉取BOM子集与工艺路线子集数据存入到附表中
//        if ("砌块".equals(productionPlanDto.getProductName())) {
//            productOrder.setRouteId(productionOrderAppendixService.populateBlocks(orderId, productionPlanDto));
//        }
//        if ("板材".equals(productionPlanDto.getProductName())) {
//            productOrder.setRouteId(productionOrderAppendixService.populatePlates(orderId, productionPlanDto));
//        }
//        //  æ›´æ–°ç»‘定的工艺路线
//        productOrderService.updateById(productOrder);
//
//        // æ ¹æ®ä¸‹å‘数量,从第一个生产计划开始分配方数
//        BigDecimal assignedVolume = BigDecimal.ZERO;
//        for (ProductionPlan plan : plans) {
//            BigDecimal volume = plan.getVolume();
//            if (volume == null) {
//                continue;
//            }
//            // è®¡ç®—剩余方数
//            BigDecimal remainingVolume = plan.getRemainingVolume();
//            if (remainingVolume.compareTo(BigDecimal.ZERO) <= 0) {
//                continue;
//            }
//
//            ProductOrderPlan productOrderPlan = new ProductOrderPlan();
//            productOrderPlan.setProductOrderId(productOrder.getId());
//            productOrderPlan.setProductionPlanId(plan.getId());
//
//            if (assignedVolume.add(remainingVolume).compareTo(productionPlanDto.getTotalAssignedQuantity()) >= 0) {
//                // æœ€åŽä¸€ä¸ªè®¡åˆ’,分配剩余方数
//                BigDecimal lastRemainingVolume = productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume);
//                BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO).add(lastRemainingVolume);
//                plan.setAssignedQuantity(assignedQuantity);
//                plan.setStatus(assignedQuantity.compareTo(plan.getVolume()) >= 0 ? 2 : 1);
//                productOrderPlan.setAssignedQuantity(lastRemainingVolume);
//                productionPlanMapper.updateById(plan);
//                productOrderPlanMapper.insert(productOrderPlan);
//                break;
//            }
//
//            // åˆ†é…å½“前计划方数
//            BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO).add(remainingVolume);
//            plan.setAssignedQuantity(assignedQuantity);
//            plan.setStatus(assignedQuantity.compareTo(plan.getVolume()) >= 0 ? 2 : 1);
//            productOrderPlan.setAssignedQuantity(remainingVolume);
//            // æ›´æ–°ç”Ÿäº§è®¡åˆ’
//            productionPlanMapper.updateById(plan);
//            // åˆ›å»ºå…³è”关系
//            productOrderPlanMapper.insert(productOrderPlan);
//            assignedVolume = assignedVolume.add(remainingVolume);
//        }
//
//        for (ProductionPlan plan : plans) {
//            BigDecimal assignedQuantity = Optional.ofNullable(plan.getAssignedQuantity()).orElse(BigDecimal.ZERO);
//            BigDecimal volume = Optional.ofNullable(plan.getVolume()).orElse(BigDecimal.ZERO);
//            if (assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
//                plan.setStatus(0);
//            } else if (assignedQuantity.compareTo(volume) >= 0) {
//                plan.setStatus(2);
//            } else {
//                plan.setStatus(1);
//            }
//            productionPlanMapper.updateById(plan);
//        }
        boolean saved = productionOrderService.saveProductionOrder(productionOrder);
        if (!saved) {
            throw new ServiceException("下发失败,生产订单保存失败");
        }
        int targetStatus = assignedQuantity.compareTo(totalRequiredQuantity) < 0 ? PLAN_STATUS_PARTIAL : PLAN_STATUS_ISSUED;
        List<ProductionPlan> updates = planIds.stream().map(id -> {
            ProductionPlan update = new ProductionPlan();
            update.setId(id);
            update.setStatus(targetStatus);
            return update;
        }).collect(Collectors.toList());
        if (!updates.isEmpty()) {
            this.updateBatchById(updates);
        }
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(ProductionPlanDto dto) {
        if (StringUtils.isBlank(dto.getApplyNo())) {
            throw new ServiceException("新增失败,申请单编号不能为空");
        }
        checkApplyNoUnique(dto.getApplyNo(), null);
        dto.setStatus(0);
        if (StringUtils.isBlank(dto.getMpsNo())) {
            dto.setMpsNo(generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        }else checkMpsNoUnique(dto.getMpsNo(), null);
        dto.setStatus(PLAN_STATUS_WAIT);
        dto.setSource("内部");
        return productionPlanMapper.insert(dto) > 0;
    }
@@ -179,51 +150,49 @@
    @Transactional(rollbackFor = Exception.class)
    public boolean update(ProductionPlanDto dto) {
        if (dto == null || dto.getId() == null) {
            throw new ServiceException("编辑失败,数据不能为空");
            throw new ServiceException("编辑失败,数据不能为空");
        }
        ProductionPlan old = getById(dto.getId());
        if (old == null) {
            throw new ServiceException("编辑失败,主生产计划不存在");
            throw new ServiceException("编辑失败,生产计划不存在");
        }
        // çŠ¶æ€æ ¡éªŒ
        if (old.getStatus() != 0) {
            throw new BaseException("编辑失败,该生产计划已下发或部分下发状态,禁止编辑");
        if (old.getStatus() != PLAN_STATUS_WAIT) {
            throw new BaseException("编辑失败,该生产计划已下发或部分下发,禁止编辑");
        }
        // applyNo变更才校验
        if (StringUtils.isNotBlank(dto.getApplyNo())
                && !dto.getApplyNo().equals(old.getApplyNo())) {
            checkApplyNoUnique(dto.getApplyNo(), dto.getId());
        if (StringUtils.isNotBlank(dto.getMpsNo()) && !dto.getMpsNo().equals(old.getMpsNo())) {
            checkMpsNoUnique(dto.getMpsNo(), dto.getId());
        }
        return productionPlanMapper.updateById(dto) > 0;
    }
    private void checkApplyNoUnique(String applyNo, Long excludeId) {
    private void checkMpsNoUnique(String mpsNo, Long excludeId) {
        LambdaQueryWrapper<ProductionPlan> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ProductionPlan::getApplyNo, applyNo);
        wrapper.eq(ProductionPlan::getMpsNo, mpsNo);
        if (excludeId != null) {
            wrapper.ne(ProductionPlan::getId, excludeId);
        }
        if (productionPlanMapper.selectCount(wrapper) > 0) {
            throw new ServiceException("申请单编号 " + applyNo + " å·²å­˜åœ¨");
            throw new ServiceException("生产计划号 " + mpsNo + " å·²å­˜åœ¨");
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(List<Long> ids) {
        // å¦‚果存在已下发的计划,则不能删除
        if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids)).stream().anyMatch(p -> p.getStatus() == 1 || p.getStatus() == 2)) {
        if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids))
                .stream()
                .anyMatch(p -> p.getStatus() == PLAN_STATUS_PARTIAL || p.getStatus() == PLAN_STATUS_ISSUED)) {
            throw new BaseException("删除失败,存在已下发或部分下发的计划");
        }
        // å¦‚果有关联订单,则不能删除
        if (productionOrderMapper.selectList(Wrappers.<ProductionOrder>lambdaQuery().in(ProductionOrder::getProductionPlanIds, ids)).stream().anyMatch(p -> p.getId() != null)) {
            throw new BaseException("删除失败,存在关联订单");
        if (productionOrderMapper.selectList(Wrappers.<ProductionOrder>lambdaQuery().in(ProductionOrder::getProductionPlanIds, ids))
                .stream()
                .anyMatch(p -> p.getId() != null)) {
            throw new BaseException("删除失败,存在关联生产订单");
        }
        return productionPlanMapper.deleteBatchIds(ids) > 0;
@@ -240,80 +209,81 @@
        try {
            list = excelUtil.importExcel(file.getInputStream());
        } catch (Exception e) {
            log.error("生产需求Excel导入失败", e);
            throw new ServiceException("Excel解析失败");
        }
        if (list == null || list.isEmpty()) {
            throw new ServiceException("Excel没有数据");
        }
        Set<String> applyNos = new HashSet<>();
        Set<String> materialCodes = new HashSet<>();
        Set<String> mpsNos = new HashSet<>();
        for (int i = 0; i < list.size(); i++) {
            ProductionPlanImportDto dto = list.get(i);
            String applyNo = dto.getApplyNo();
            String materialCode = dto.getMaterialCode();
            if (StringUtils.isEmpty(applyNo)) {
                throw new ServiceException("导入失败:第 " + (i + 2) + " è¡Œç”³è¯·å•编号不能为空");
            String mpsNo = dto.getMpsNo();
            if (StringUtils.isEmpty(mpsNo)) {
                generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
            }
            if (!applyNos.add(applyNo)) {
                throw new ServiceException("导入失败:Excel ä¸­å­˜åœ¨é‡å¤çš„申请单编号: " + applyNo);
            if (!mpsNos.add(mpsNo)) {
                throw new ServiceException("导入失败:Excel ä¸­å­˜åœ¨é‡å¤çš„申请单编号 " + mpsNo);
            }
            if (StringUtils.isEmpty(materialCode)) {
                throw new ServiceException("导入失败:第 " + (i + 2) + " è¡Œç‰©æ–™ç¼–码不能为空");
            }
            String strength = dto.getStrength();
            if (StringUtils.isNotEmpty(strength)) {
                if (!"A3.5".equals(strength) && !"A5.0".equals(strength)) {
                    throw new ServiceException("导入失败:第 " + (i + 2) + " è¡Œå¼ºåº¦åªèƒ½æ˜¯ A3.5 æˆ– A5.0");
                }
            }
            materialCodes.add(materialCode);
        }
        //  ç”³è¯·å•编号是否已存在
        Long existApplyNoCount = baseMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery()
                .in(ProductionPlan::getApplyNo, applyNos));
                .in(ProductionPlan::getMpsNo, mpsNos));
        if (existApplyNoCount > 0) {
            List<String> existApplyNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery()
                            .in(ProductionPlan::getApplyNo, applyNos))
                    .stream().map(ProductionPlan::getApplyNo).collect(Collectors.toList());
            throw new ServiceException("导入失败,申请单编号已存在: " + String.join(", ", existApplyNos));
            List<String> existMpsNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery()
                            .in(ProductionPlan::getMpsNo, mpsNos))
                    .stream()
                    .map(ProductionPlan::getMpsNo)
                    .collect(Collectors.toList());
            throw new ServiceException("导入失败,生产计划号已存在: " + String.join(", ", existMpsNos));
        }
        LocalDateTime now = LocalDateTime.now();
        List<ProductionPlan> entityList = list.stream().map(dto -> {
            ProductionPlan entity = new ProductionPlan();
            BeanUtils.copyProperties(dto, entity);
            entity.setStatus(0);
            entity.setStatus(PLAN_STATUS_WAIT);
            entity.setSource("内部");
            entity.setCreateTime(now);
            entity.setUpdateTime(now);
            return entity;
        }).collect(Collectors.toList());
        this.saveBatch(entityList);
    }
    @Override
    public void exportProdData(HttpServletResponse response, List<Long> ids) {
        List<ProductionPlan> list;
        if (ids != null && !ids.isEmpty()) {
            list = baseMapper.selectBatchIds(ids);
        } else {
            list = baseMapper.selectList(null);
        }
        List<ProductionPlanDto> list = productionPlanMapper.selectWithMaterialByIds(ids);
        List<ProductionPlanImportDto> exportList = new ArrayList<>();
        for (ProductionPlan entity : list) {
        for (ProductionPlanDto entity : list) {
            ProductionPlanImportDto dto = new ProductionPlanImportDto();
            BeanUtils.copyProperties(entity, dto);
            exportList.add(dto);
        }
        ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class);
        util.exportExcel(response, exportList, "销售生产需求数据");
        util.exportExcel(response, exportList, "主生产计划");
    }
    private String formatPlanIds(List<Long> planIds) {
        return planIds.stream()
                .filter(Objects::nonNull)
                .distinct()
                .map(String::valueOf)
                .collect(Collectors.joining(",", "[", "]"));
    }
    private String generateNextPlanNo(String datePrefix) {
        QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("mps_no", "JH" + datePrefix);
        queryWrapper.orderByDesc("mps_no");
        queryWrapper.last("LIMIT 1");
        ProductionPlan latestPlan = productionPlanMapper.selectOne(queryWrapper);
        int sequence = 1;
        if (latestPlan != null && latestPlan.getMpsNo() != null && !latestPlan.getMpsNo().isEmpty()) {
            String sequenceStr = latestPlan.getMpsNo().substring(("JH" + datePrefix).length());
            try {
                sequence = Integer.parseInt(sequenceStr) + 1;
            } catch (NumberFormatException e) {
                sequence = 1;
            }
        }
        return "JH" + datePrefix + String.format("%04d", sequence);
    }
}
src/main/java/com/ruoyi/project/common/CommonController.java
@@ -3,10 +3,9 @@
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
@@ -36,9 +35,6 @@
public class CommonController {
    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
    private ServerConfig serverConfig;
    private static final String FILE_DELIMETER = ",";
    private final StorageBlobService storageBlobService;
    private final FileUtil fileUtil;
@@ -77,16 +73,6 @@
//    }
//
//    /**
//     * minio通用上传请求(多个)
//     */
//    @PostMapping("/minioUploads")
//    @Operation(summary = "minio通用上传请求")
//    public AjaxResult minioUploadFiles(List<MultipartFile> files, String bucketName, Long type) throws Exception
//    {
//        return AjaxResult.success();
//    }
//
//    /**
//     * é€šç”¨ä¸Šä¼ è¯·æ±‚(单个)
//     */
//    @PostMapping("/upload")
@@ -104,43 +90,6 @@
//            ajax.put("fileName", fileName);
//            ajax.put("newFileName", FileUtils.getName(fileName));
//            ajax.put("originalFilename", file.getOriginalFilename());
//            return ajax;
//        }
//        catch (Exception e)
//        {
//            return AjaxResult.error(e.getMessage());
//        }
//    }
//
//    /**
//     * é€šç”¨ä¸Šä¼ è¯·æ±‚(多个)
//     */
//    @PostMapping("/uploads")
//    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
//    {
//        try
//        {
//            // ä¸Šä¼ æ–‡ä»¶è·¯å¾„
//            String filePath = RuoYiConfig.getUploadPath();
//            List<String> urls = new ArrayList<String>();
//            List<String> fileNames = new ArrayList<String>();
//            List<String> newFileNames = new ArrayList<String>();
//            List<String> originalFilenames = new ArrayList<String>();
//            for (MultipartFile file : files)
//            {
//                // ä¸Šä¼ å¹¶è¿”回新文件名称
//                String fileName = FileUploadUtils.upload(filePath, file);
//                String url = serverConfig.getUrl() + fileName;
//                urls.add(url);
//                fileNames.add(fileName);
//                newFileNames.add(FileUtils.getName(fileName));
//                originalFilenames.add(file.getOriginalFilename());
//            }
//            AjaxResult ajax = AjaxResult.success();
//            ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
//            ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
//            ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
//            ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
//            return ajax;
//        }
//        catch (Exception e)
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -1,10 +1,10 @@
package com.ruoyi.sales.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.ShippingInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@@ -33,4 +33,7 @@
    private String productName;
    private List<StorageBlobDTO> storageBlobDTOs;
    private List<StorageBlobVO> storageBlobVOs;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -232,6 +232,7 @@
    private BigDecimal ticketsTotal = BigDecimal.ZERO;
    @Schema(description = "是否质检")
    //针对采购台账,是否质检
    private Boolean isChecked;
    @TableField(exist = false)
@@ -251,4 +252,8 @@
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @Schema(description = "是否生产")
    //针对销售台账,是否生产
    private Boolean isProduction;
}
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
@@ -3,13 +3,10 @@
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.procurementrecord.dto.ReturnSaleProductDto;
import com.ruoyi.sales.dto.SalesLedgerProductDto;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.pojo.ShippingInfo;
import org.apache.ibatis.annotations.Param;
import java.io.IOException;
import java.util.List;
/**
@@ -19,7 +16,7 @@
public interface ShippingInfoService extends IService<ShippingInfo>{
    IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req);
    boolean deductStock(ShippingInfoDto req) throws IOException;
    boolean deductStock(ShippingInfoDto req);
    boolean delete(List<Long> ids);
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -6,20 +6,14 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.pojo.ProductionAccount;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.pojo.ProductionPlan;
import com.ruoyi.production.service.ProductionOrderService;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.sales.dto.InvoiceRegistrationProductDto;
import com.ruoyi.sales.dto.SalesLedgerProductDto;
import com.ruoyi.sales.mapper.InvoiceRegistrationProductMapper;
@@ -68,7 +62,7 @@
    private final ProductionAccountMapper productionAccountMapper;
    private final SalesLedgerMapper salesLedgerMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionPlanMapper productionPlanMapper;
    private final ProductionOperationTaskMapper productionOperationTaskMapper;
    private final ProductionOrderService productionOrderService;
    private final TechnologyRoutingMapper technologyRoutingMapper;
@@ -256,70 +250,38 @@
     * æ–°å¢žç”Ÿäº§æ•°æ®
     */
    public void addProductionData(SalesLedgerProduct salesLedgerProduct) {
        ProductionOrder productionOrder = new ProductionOrder();
        productionOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        productionOrder.setProductModelId(salesLedgerProduct.getProductModelId());
        productionOrder.setSaleLedgerProductId(salesLedgerProduct.getId().intValue());
        productionOrder.setNpsNo(generateNextOrderNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        productionOrder.setQuantity(salesLedgerProduct.getQuantity());
        productionOrder.setCompleteQuantity(BigDecimal.ZERO);
        //先判断该产品是否需要生产
        if (!salesLedgerProduct.getIsProduction()) {
            return;
        }
        ProductionPlan productionPlan = new ProductionPlan();
        productionPlan.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        productionPlan.setSalesLedgerProductId(salesLedgerProduct.getId());
        productionPlan.setMpsNo(generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        productionPlan.setProductModelId(salesLedgerProduct.getProductModelId());
        productionPlan.setQtyRequired(salesLedgerProduct.getQuantity());
        productionPlan.setSource("销售");
        productionPlan.setStatus(0);
        productionPlanMapper.insert(productionPlan);
        TechnologyRouting routing = technologyRoutingMapper.selectOne(
                new QueryWrapper<TechnologyRouting>().lambda()
                        .eq(TechnologyRouting::getProductModelId, salesLedgerProduct.getProductModelId())
                        .orderByDesc(TechnologyRouting::getCreateTime)
                        .last("LIMIT 1"));
        if (routing != null) {
            productionOrder.setTechnologyRoutingId(routing.getId());
        }
        productionOrderMapper.insert(productionOrder);
        if (productionOrder.getTechnologyRoutingId() != null) {
            productionOrderService.syncProductionOrderSnapshot(productionOrder.getId());
        }
    }
    /**
     * åˆ é™¤ç”Ÿäº§æ•°æ®
     * åˆ é™¤ç”Ÿäº§è®¡åˆ’
     */
    public void deleteProductionData(List<Long> productIds) {
        List<ProductionOrder> productionOrders = productionOrderMapper.selectList(
                new LambdaQueryWrapper<ProductionOrder>()
                        .in(ProductionOrder::getSaleLedgerProductId, productIds.stream().map(Long::intValue).collect(Collectors.toList())));
        if (org.springframework.util.CollectionUtils.isEmpty(productionOrders)) {
        List<ProductionPlan> productionPlans = productionPlanMapper.selectList(
                new LambdaQueryWrapper<ProductionPlan>()
                        .in(ProductionPlan::getSalesLedgerProductId, productIds.stream().map(Long::intValue).collect(Collectors.toList())));
        if (org.springframework.util.CollectionUtils.isEmpty(productionPlans)) {
            return;
        }
        List<Long> orderIds = productionOrders.stream().map(ProductionOrder::getId).collect(Collectors.toList());
        List<Long> taskIds = productionOperationTaskMapper.selectList(
                        new LambdaQueryWrapper<ProductionOperationTask>()
                                .in(ProductionOperationTask::getProductionOrderId, orderIds))
                .stream().map(ProductionOperationTask::getId).collect(Collectors.toList());
        if (!taskIds.isEmpty()) {
            List<ProductionProductMain> productMains = productionProductMainMapper.selectList(
                    new LambdaQueryWrapper<ProductionProductMain>()
                            .in(ProductionProductMain::getProductionOperationTaskId, taskIds));
            List<Long> productMainIds = productMains.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
            if (!productMainIds.isEmpty()) {
                List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                        new LambdaQueryWrapper<QualityInspect>().in(QualityInspect::getProductMainId, productMainIds));
                qualityInspects.forEach(qualityInspect -> {
                    if (qualityInspect.getInspectState() == 1) {
                        throw new RuntimeException("已提交的检验单不能删除");
                    }
                });
                productionProductOutputMapper.deleteByProductMainIds(productMainIds);
                productionProductInputMapper.deleteByProductMainIds(productMainIds);
                qualityInspectMapper.deleteByProductMainIds(productMainIds);
                productionAccountMapper.delete(new LambdaQueryWrapper<ProductionAccount>()
                        .in(ProductionAccount::getProductionProductMainId, productMainIds));
                for (Long productMainId : productMainIds) {
                    stockUtils.deleteStockOutRecord(productMainId, StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
                    stockUtils.deleteStockInRecord(productMainId, StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
                }
            }
            productionProductMainMapper.delete(new LambdaQueryWrapper<ProductionProductMain>()
                    .in(ProductionProductMain::getProductionOperationTaskId, taskIds));
        //如果生产计划已下发则不能删除
        if (productionPlans.stream().anyMatch(productionPlan -> productionPlan.getStatus() != 0)) {
            throw new RuntimeException("生产计划已下发,不能删除该销售产品");
        }
        productionOrderService.removeProductionOrder(orderIds);
        List<Long> ids = productionPlans.stream().map(ProductionPlan::getId).collect(Collectors.toList());
        productionPlanMapper.deleteByIds(ids);
    }
    @Override
@@ -432,21 +394,21 @@
        return R.ok();
    }
    private String generateNextOrderNo(String datePrefix) {
        QueryWrapper<ProductionOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("nps_no", "SC" + datePrefix);
        queryWrapper.orderByDesc("nps_no");
    private String generateNextPlanNo(String datePrefix) {
        QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("mps_no", "JH" + datePrefix);
        queryWrapper.orderByDesc("mps_no");
        queryWrapper.last("LIMIT 1");
        ProductionOrder latestOrder = productionOrderMapper.selectOne(queryWrapper);
        ProductionPlan latestPlan = productionPlanMapper.selectOne(queryWrapper);
        int sequence = 1;
        if (latestOrder != null && latestOrder.getNpsNo() != null && !latestOrder.getNpsNo().isEmpty()) {
            String sequenceStr = latestOrder.getNpsNo().substring(("SC" + datePrefix).length());
        if (latestPlan != null && latestPlan.getMpsNo() != null && !latestPlan.getMpsNo().isEmpty()) {
            String sequenceStr = latestPlan.getMpsNo().substring(("JH" + datePrefix).length());
            try {
                sequence = Integer.parseInt(sequenceStr) + 1;
            } catch (NumberFormatException e) {
                sequence = 1;
            }
        }
        return "SC" + datePrefix + String.format("%04d", sequence);
        return "JH" + datePrefix + String.format("%04d", sequence);
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -520,7 +520,7 @@
        List<Long> productIds = products.stream()
                .map(SalesLedgerProduct::getId)
                .collect(Collectors.toList());
        //删除生产数据
        //删除生产计划
        salesLedgerProductServiceImpl.deleteProductionData(productIds);
        // æ‰¹é‡åˆ é™¤äº§å“å­è¡¨
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -6,6 +6,9 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.other.service.impl.TempFileServiceImpl;
@@ -22,7 +25,6 @@
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -41,18 +43,19 @@
    private final StockUtils stockUtils;
    private final CommonFileServiceImpl commonFileService;
    private final ApproveProcessServiceImpl approveProcessService;
    private final FileUtil fileUtil;
    @Override
    public IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req) {
        IPage<ShippingInfoDto> listPage = shippingInfoMapper.listPage(page, req);
        listPage.getRecords().forEach(item ->{
            item.setCommonFileList(commonFileService.getFileListByBusinessId(item.getId(), FileNameType.SHIP.getValue()));
            item.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, item.getId()));
        });
        return listPage;
    }
    @Override
    public boolean deductStock(ShippingInfoDto req) throws IOException {
    public boolean deductStock(ShippingInfoDto req) {
        ShippingInfo byId = this.getById(req.getId());
        if (byId == null) {
            throw new RuntimeException("发货信息不存在");
@@ -68,10 +71,8 @@
        byId.setShippingCarNumber(req.getShippingCarNumber());
        byId.setShippingDate(req.getShippingDate());
        boolean update = this.updateById(byId);
        // è¿ç§»æ–‡ä»¶
        if(CollectionUtils.isNotEmpty(req.getTempFileIds())){
            tempFileService.migrateTempFilesToFormal(req.getId(), req.getTempFileIds(), FileNameType.SHIP.getValue());
        }
        // ä¿å­˜æ–‡ä»¶
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, req.getId(), req.getStorageBlobDTOs());
        return update ;
    }
src/main/resources/mapper/production/ProductionPlanMapper.xml
@@ -23,31 +23,25 @@
    <select id="listPage" resultType="com.ruoyi.production.bean.vo.ProductionPlanVo">
        SELECT
        pp.*,
        pms.material_code AS materialCode,
        pmdl.model,
        pms.product_id AS productMaterialId,
        pm.model,
        p.id as productId,
        p.product_name AS productName,
        pmdl.unit
        pm.unit
        FROM production_plan pp
        left join product_material_sku pms on pp.product_material_sku_id = pms.id
        left join product_model pmdl on pp.product_model_id = pmdl.id
        left join product p on pmdl.product_id = p.id
        ORDER BY COALESCE(pp.form_modified_time, pp.id) DESC
        left join product_model pm on pp.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        ORDER BY COALESCE(pp.id) DESC
    </select>
    <select id="selectWithMaterialByIds" resultType="com.ruoyi.production.bean.dto.ProductionPlanDto">
        SELECT
        pp.*,
        pms.material_code AS materialCode,
        pmdl.model,
        pm.model,
        p.product_name AS productName,
        pmdl.unit,
        pms.product_id AS productMaterialId
        pm.unit
        FROM production_plan pp
        LEFT JOIN product_material_sku pms ON pp.product_material_sku_id = pms.id
        LEFT JOIN product_model pmdl ON pp.product_model_id = pmdl.id
        LEFT JOIN product p ON pmdl.product_id = p.id
        LEFT JOIN product_model pm ON pp.product_model_id = pm.id
        LEFT JOIN product p ON pm.product_id = p.id
        WHERE pp.id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
@@ -57,15 +51,12 @@
    <select id="selectProductionPlanDtoById" resultType="com.ruoyi.production.bean.dto.ProductionPlanDto">
        SELECT
        pp.*,
        pms.material_code AS materialCode,
        pmdl.model,
        pms.product_id AS productMaterialId,
        pm.model,
        p.product_name AS productName,
        pmdl.unit
        pm.unit
        FROM production_plan pp
        left join product_material_sku pms on pp.product_material_sku_id = pms.id
        left join product_model pmdl on pp.product_model_id = pmdl.id
        left join product p on pmdl.product_id = p.id
        left join product_model pm on pp.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        WHERE pp.id = #{productionPlanId}
    </select>
</mapper>