已修改13个文件
已添加35个文件
2282 ■■■■■ 文件已修改
doc/add.sql 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/StorageAttachmentMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/StorageBlob.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/StorageBlobService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/config/MinioConfig.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/constant/StorageAttachmentConstants.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/MinioUtils.java 309 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/framework/security/LoginUser.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/framework/web/domain/MinioResult.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/other/controller/TempFileController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/Details.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementAddDto.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementDto.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDto.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutAdd.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutPageDto.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementUpdateDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecord.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/ProductRecordDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/mapper/ProductRecordMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/IProductRecordService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/ProductRecordServiceImpl.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-druid.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/StorageAttachmentMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/StorageBlobMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/ProductRecordMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/add.sql
@@ -81,4 +81,52 @@
ALTER TABLE `product-inventory-management`.`device_maintenance`
    ADD COLUMN `device_name` varchar(255) NULL AFTER `tenant_id`,
    ADD COLUMN `device_model` varchar(255) NULL AFTER `device_name`;
    ADD COLUMN `device_model` varchar(255) NULL AFTER `device_name`;
DROP TABLE IF EXISTS storage_attachment;
CREATE TABLE storage_attachment (
                                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
                                    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                                    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                                    deleted BIGINT DEFAULT 0 NOT NULL,
                                    record_type SMALLINT DEFAULT 0 NOT NULL,
                                    record_id BIGINT DEFAULT 0 NOT NULL,
                                    name VARCHAR(100) DEFAULT '' NOT NULL,
                                    storage_blob_id BIGINT DEFAULT 0 NOT NULL
) COMMENT='通用文件上传的附件信息';
ALTER TABLE storage_attachment
    COMMENT '通用文件上传的附件信息';
ALTER TABLE storage_attachment MODIFY record_type SMALLINT COMMENT '关联的记录类型';
ALTER TABLE storage_attachment MODIFY record_id BIGINT COMMENT '关联的记录id';
ALTER TABLE storage_attachment MODIFY name VARCHAR(100) COMMENT '名称, å¦‚: file, avatar (区分同一条记录不同类型的附件)';
ALTER TABLE storage_attachment MODIFY storage_blob_id BIGINT COMMENT '关联storage_blob记录id';
CREATE INDEX idx_storage_attachment_on_record
    ON storage_attachment (record_type, record_id);
CREATE TABLE storage_blob (
                              id BIGINT AUTO_INCREMENT PRIMARY KEY,
                              create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                              key_name VARCHAR(150) DEFAULT '' NOT NULL,
                              content_type VARCHAR(100) DEFAULT '' NOT NULL,
                              original_filename VARCHAR(255) DEFAULT '' NOT NULL,
                              bucket_filename VARCHAR(255) DEFAULT '' NOT NULL,
                              bucket_name VARCHAR(255) DEFAULT '' NOT NULL,
                              byte_size BIGINT DEFAULT 0 NOT NULL,
                              UNIQUE (key_name)
) COMMENT='通用文件上传的附件信息';
ALTER TABLE storage_blob
    COMMENT '通用文件上传的附件信息';
ALTER TABLE storage_blob MODIFY key_name VARCHAR(150) COMMENT '资源id';
ALTER TABLE storage_blob MODIFY content_type VARCHAR(100) COMMENT '资源类型,例如JPG图片的资源类型为image/jpg';
ALTER TABLE storage_blob MODIFY original_filename VARCHAR(255) COMMENT '原文件名称';
ALTER TABLE storage_blob MODIFY bucket_filename VARCHAR(255) COMMENT '存储桶中文件名';
ALTER TABLE storage_blob MODIFY bucket_name VARCHAR(255) COMMENT '存储桶名';
ALTER TABLE storage_blob MODIFY byte_size BIGINT COMMENT '资源尺寸(字节)';
pom.xml
@@ -37,6 +37,8 @@
        <velocity.version>2.3</velocity.version>
        <!-- override dependency version -->
        <tomcat.version>9.0.102</tomcat.version>
        <minio.version>8.4.3</minio.version>
        <okhttp.version>4.9.0</okhttp.version>
        <logback.version>1.2.13</logback.version>
        <spring-security.version>5.7.12</spring-security.version>
        <spring-framework.version>5.3.39</spring-framework.version>
@@ -252,6 +254,28 @@
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- minio -->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>${minio.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.squareup.okhttp3</groupId>
                    <artifactId>okhttp</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- minio依赖okhttp ä¸ç„¶æŠ¥é”™ -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>${okhttp.version}</version>
        </dependency>
    </dependencies>
    <build>
src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.ruoyi.basic.dto;
import com.ruoyi.basic.pojo.StorageBlob;
import lombok.Data;
@Data
public class StorageBlobDTO extends StorageBlob {
    private String url;
}
src/main/java/com/ruoyi/basic/mapper/StorageAttachmentMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.basic.pojo.StorageAttachment;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 Mapper æŽ¥å£
 * </p>
 *
 * @author ruoyi
 * @since 2025-05-29
 */
@Mapper
public interface StorageAttachmentMapper extends BaseMapper<StorageAttachment> {
}
src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.basic.pojo.StorageBlob;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 Mapper æŽ¥å£
 * </p>
 *
 * @author ruoyi
 * @since 2025-05-29
 */
@Mapper
public interface StorageBlobMapper extends BaseMapper<StorageBlob> {
}
src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package com.ruoyi.basic.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.basic.dto.StorageBlobDTO;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 å®žä½“ç±»
 *
 * @author ruoyi
 * @date 2025-05-29
 */
@Data
@TableName("storage_attachment")
public class StorageAttachment implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** æ›´æ–°æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /**
     * é€»è¾‘删除
     */
    @TableField(value = "deleted")
    private Long deleted;
    /**
     * å…³è”的记录类型
     */
    @TableField(value = "record_type")
    private Long recordType;
    /**
     * å…³è”的记录id
     */
    @TableField(value = "record_id")
    private Long recordId;
    /**
     * ç±»åž‹åç§°, å¦‚: file, avatar (区分同一条记录不同类型的附件)
     */
    @TableField(value = "name")
    private String name;
    /**
     * å…³è”storage_blob记录id
     */
    @TableField(value = "storage_blob_id")
    private Long storageBlobId;
    private StorageBlobDTO storageBlobDTO;
    public StorageAttachment(String fileType, Long recordType, Long recordId) {
        this.name = fileType;
        this.recordType = recordType;
        this.recordId = recordId;
    }
}
src/main/java/com/ruoyi/basic/pojo/StorageBlob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package com.ruoyi.basic.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 å®žä½“ç±»
 *
 * @author ruoyi
 * @date 2025-05-29
 */
@Data
@TableName("storage_blob")
public class StorageBlob implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * èµ„源id
     */
    @TableField(value = "key")
    private String key;
    /**
     * èµ„源类型,例如JPG图片的资源类型为image/jpg
     */
    @TableField(value = "content_type")
    private String contentType;
    /**
     * åŽŸæ–‡ä»¶å
     */
    @TableField(value = "original_filename")
    private String originalFilename;
    /**
     * å­˜å‚¨æ¡¶ä¸­
     */
    @TableField(value = "bucket_filename")
    private String bucketFilename;
    /**
     * å­˜å‚¨æ¡¶å
     */
    @TableField(value = "bucket_name")
    private String bucketName;
    /**
     * èµ„源尺寸(字节)
     */
    @TableField(value = "byte_size")
    private Long byteSize;
}
src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package com.ruoyi.basic.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.basic.pojo.StorageAttachment;
import com.ruoyi.common.constant.StorageAttachmentConstants;
import com.ruoyi.common.enums.StorageAttachmentRecordType;
import java.util.List;
/**
 * <p>
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 æœåŠ¡ç±»
 * </p>
 *
 * @author ruoyi
 * @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, StorageAttachmentConstants fileType);
    /**
     * ä¿å­˜é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息
     * @param attachments æ–‡ä»¶ä¿¡æ¯åˆ—表
     * @param recordId ç®¡ç†è®°å½•id
     * @param recordType å…³è”记录类型
     * @param fileType æ–‡ä»¶ç±»åž‹
     */
    public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, StorageAttachmentConstants fileType);
    /**
     * åˆ é™¤é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息
     * @param storageAttachment æ–‡ä»¶ä¿¡æ¯
     * @return åˆ é™¤ç»“æžœ
     */
    public int deleteStorageAttachment(StorageAttachment storageAttachment);
}
src/main/java/com/ruoyi/basic/service/StorageBlobService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.basic.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.pojo.StorageAttachment;
import com.ruoyi.basic.pojo.StorageBlob;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
 * <p>
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 æœåŠ¡ç±»
 * </p>
 *
 * @author ruoyi
 * @since 2025-05-29
 */
public interface StorageBlobService extends IService<StorageBlob> {
    /**
     * æ–‡ä»¶ä¸Šä¼ æŽ¥å£
     * @param files æ–‡ä»¶ä¿¡æ¯
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @return ä¸Šä¼ ç»“æžœ
     */
    List<StorageBlobDTO> updateStorageBlobs(List<MultipartFile> files, String bucketName);
    /**
     * æ‰¹é‡åˆ é™¤æ–‡ä»¶
     * @param attachment
     * @return
     */
    public int deleteStorageBlobs(StorageAttachment attachment);
}
src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package com.ruoyi.basic.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.pojo.StorageAttachment;
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.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.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author ruoyi
 * @since 2025-05-29
 */
@Service
@RequiredArgsConstructor
public class StorageAttachmentServiceImpl extends ServiceImpl<StorageAttachmentMapper, StorageAttachment> implements StorageAttachmentService {
    @Autowired
    private StorageBlobMapper storageBlobMapper;
    @Autowired
    private StorageAttachmentMapper storageAttachmentMapper;
    @Autowired
    private StorageBlobService storageBlobService;
    @Autowired
    private MinioUtils minioUtils;
    @Override
    public List<StorageAttachment> selectStorageAttachments(Long recordId, StorageAttachmentRecordType recordType, StorageAttachmentConstants fileType) {
        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordId, recordId)
                .eq(StorageAttachment::getRecordType, recordType.ordinal())
                .eq(StorageAttachment::getName, fileType.toString()));
        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;
    }
    @Override
    public void saveStorageAttachment(List<StorageAttachment> attachments, Long recordId, StorageAttachmentRecordType recordType, StorageAttachmentConstants fileType) {
        // åˆ é™¤æ—§å›¾
        deleteStorageAttachment(new StorageAttachment(fileType.toString(), (long) recordType.ordinal(), recordId));
        for (StorageAttachment attachment : attachments) {
            // èŽ·å–å…³è”è®°å½•
            StorageBlob storageBlob = attachment.getStorageBlobDTO();
            attachment.setName(fileType.toString());
            attachment.setRecordType((long) recordType.ordinal());
            attachment.setRecordId(recordId);
            attachment.setStorageBlobId(storageBlob.getId());
            storageAttachmentMapper.insert(attachment);
        }
    }
    @Override
    public int deleteStorageAttachment(StorageAttachment storageAttachment) {
        // å…ˆåˆ é™¤æ˜Žç»†è¡¨
        storageBlobService.deleteStorageBlobs(storageAttachment);
        return storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordId, storageAttachment.getRecordId())
                .eq(StorageAttachment::getRecordType, storageAttachment.getRecordType())
                .eq(StorageAttachment::getName, storageAttachment.getName()));
    }
}
src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
package com.ruoyi.basic.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.mapper.StorageAttachmentMapper;
import com.ruoyi.basic.mapper.StorageBlobMapper;
import com.ruoyi.basic.pojo.StorageAttachment;
import com.ruoyi.basic.pojo.StorageBlob;
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MinioUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.web.domain.MinioResult;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * <p>
 * é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author ruoyi
 * @since 2025-05-29
 */
@Service
@RequiredArgsConstructor
public class StorageBlobServiceImpl extends ServiceImpl<StorageBlobMapper, StorageBlob> implements StorageBlobService {
    @Autowired
    private StorageAttachmentMapper storageAttachmentMapper;
    @Autowired
    private StorageBlobMapper storageBlobMapper;
    @Autowired
    private MinioUtils minioUtils;
    @Override
    public List<StorageBlobDTO> updateStorageBlobs(List<MultipartFile> files, String bucketName) {
        // è‹¥æ²¡ä¼ å…¥bucketName,则使用默认bucketName
        if (StringUtils.isEmpty(bucketName)) {
            bucketName  = minioUtils.getDefaultBucket();
        }
        List<StorageBlobDTO> storageBlobDTOs = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                MinioResult res = minioUtils.upload(bucketName, file, false);
                StorageBlobDTO dto = new StorageBlobDTO();
                dto.setContentType(file.getContentType());
                dto.setBucketFilename(res.getBucketFileName());
                dto.setOriginalFilename(res.getOriginalName());
                dto.setByteSize(file.getSize());
                dto.setKey(IdUtils.simpleUUID());
                dto.setBucketName(bucketName);
                dto.setCreateTime(DateUtils.getNowDate());
                dto.setUrl(minioUtils.getPreviewUrl(res.getBucketFileName(), bucketName, false));
                // æ’入数据库
                storageBlobMapper.insert(dto);
                storageBlobDTOs.add(dto);
            } catch (InvalidExtensionException e) {
                throw new RuntimeException("minio文件上传异常:" +  e);
            }
        }
        return storageBlobDTOs;
    }
    @Override
    public int deleteStorageBlobs(StorageAttachment attachment) {
        List<StorageAttachment> attachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordId, attachment.getRecordId())
                .eq(StorageAttachment::getRecordType, attachment.getRecordType())
                .eq(StorageAttachment::getName, attachment.getName()));
        List<Long> ids = attachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
        List<StorageBlob> storageBlobs = storageBlobMapper.selectList(new LambdaQueryWrapper<StorageBlob>()
                .in(StorageBlob::getId, ids));
        if (!storageBlobs.isEmpty()) {
            for (StorageBlob storageBlob : storageBlobs) {
                // ç§»é™¤æ¡¶å†…文件
                minioUtils.removeObjectsResult(storageBlob.getBucketName(), storageBlob.getBucketName());
            }
        }
        if (!ids.isEmpty()) {
            return storageBlobMapper.delete(new QueryWrapper<StorageBlob>().lambda().in(StorageBlob::getId, ids));
        }
        return 0;
    }
}
src/main/java/com/ruoyi/common/config/MinioConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.common.config;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Configuration
@Component
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioConfig {
    private String endpoint;
    private int port;
    private String accessKey;
    private String secretKey;
    private Boolean secure;
    @Bean
    public MinioClient getMinioClient() {
        return MinioClient.builder().endpoint(endpoint, port, secure)
                .credentials(accessKey, secretKey)
                .build();
    }
}
src/main/java/com/ruoyi/common/constant/StorageAttachmentConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.common.constant;
/**
 * é™„件常量
 */
public class StorageAttachmentConstants {
    /**
     * æ–‡ä»¶
     */
    public static final String StorageAttachmentFile = "file";
    /**
     * å›¾ç‰‡
     */
    public static final String StorageAttachmentImage = "image";
}
src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.common.enums;
import lombok.AllArgsConstructor;
/**
 * é™„件记录类型枚举
 *
 */
@AllArgsConstructor
public enum StorageAttachmentRecordType {
    // ä¾‹å­ å®žé™…开发请删除
    Template("Template","范例");
    private final String code;
    private final String info;
    public String getCode() {
        return code;
    }
    public String getInfo() {
        return info;
    }
}
src/main/java/com/ruoyi/common/utils/MinioUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,309 @@
package com.ruoyi.common.utils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.framework.web.domain.MinioResult;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
public class MinioUtils {
    @Autowired
    private MinioClient minioClient;
    @Value("${minio.preview-expiry}")
    private Integer previewExpiry;
    /**
     * -- GETTER --
     *  èŽ·å–é»˜è®¤å­˜å‚¨æ¡¶åç§°
     *
     * @return
     */
    @Getter
    @Value("${minio.default-bucket}")
    private String defaultBucket;
    /**
     * åˆ¤æ–­å­˜å‚¨æ¡¶æ˜¯å¦å­˜åœ¨ï¼Œä¸å­˜åœ¨åˆ™åˆ›å»º
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     */
    public void existBucket(String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * åˆ›å»ºå­˜å‚¨æ¡¶
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @return æ˜¯å¦åˆ›å»ºæˆåŠŸ
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * åˆ é™¤å­˜å‚¨æ¡¶
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * åˆ¤æ–­å¯¹è±¡æ˜¯å¦å­˜åœ¨
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @param originalFileName MinIO中存储对象全路径
     * @return å¯¹è±¡æ˜¯å¦å­˜åœ¨
     */
    public boolean existObject(String bucketName, String originalFileName) {
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(originalFileName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * æ–‡ä»¶ä¸Šä¼ 
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @param file       æ–‡ä»¶
     * @return æ¡¶ä¸­ä½ç½®
     */
    public MinioResult upload(String bucketName, MultipartFile file, Boolean isPreviewExpiry) throws InvalidExtensionException {
        MultipartFile[] fileArr = {file};
        List<MinioResult> fileNames = upload(bucketName, fileArr, isPreviewExpiry);
        return fileNames.isEmpty() ? null : fileNames.get(0);
    }
    /**
     * ä¸Šä¼ æ–‡ä»¶
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @param fileList   æ–‡ä»¶åˆ—表
     * @return æ¡¶ä¸­ä½ç½®åˆ—表
     */
    public List<MinioResult> upload(String bucketName, List<MultipartFile> fileList, Boolean isPreviewExpiry) throws InvalidExtensionException {
        MultipartFile[] fileArr = fileList.toArray(new MultipartFile[0]);
        return upload(bucketName, fileArr, isPreviewExpiry);
    }
    /**
     * description: ä¸Šä¼ æ–‡ä»¶
     *
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @param fileArr    æ–‡ä»¶åˆ—表
     * @return æ¡¶ä¸­ä½ç½®åˆ—表
     */
    public List<MinioResult> upload(String bucketName, MultipartFile[] fileArr, Boolean isPreviewExpiry) throws InvalidExtensionException {
        for (MultipartFile file : fileArr) {
            FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        // ä¿è¯æ¡¶ä¸€å®šå­˜åœ¨
        existBucket(bucketName);
        // æ‰§è¡Œæ­£å¸¸æ“ä½œ
        List<MinioResult> bucketFileNames = new ArrayList<>(fileArr.length);
        for (MultipartFile file : fileArr) {
            // èŽ·å–åŽŸå§‹æ–‡ä»¶åç§°
            String originalFileName = file.getOriginalFilename();
            // èŽ·å–å½“å‰æ—¥æœŸï¼Œæ ¼å¼ä¾‹å¦‚ï¼š2020-11
            String datePath = new SimpleDateFormat("yyyy-MM").format(new Date());
            // æ–‡ä»¶åç§°
            String uuid = IdWorker.get32UUID();
            // èŽ·å–æ–‡ä»¶åŽç¼€
            String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
            String bucketFilePath = datePath + "/" + uuid + suffix;
            // æŽ¨é€æ–‡ä»¶åˆ°MinIO
            try (InputStream in = file.getInputStream()) {
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(bucketFilePath)
                        .stream(in, in.available(), -1)
                        .contentType(file.getContentType())
                        .build()
                );
            } catch (Exception e) {
                throw new UtilException("MinioUtils:上传文件工具类异常:" + e);
            }
            MinioResult minioResult = new MinioResult();
            minioResult.setBucketFileName(bucketFilePath);
            // è¿”回永久预览地址
            if (isPreviewExpiry) {
                String previewUrl = getPreviewUrl(bucketFilePath, bucketName, isPreviewExpiry);
                minioResult.setPreviewExpiry(previewUrl);
            }
            minioResult.setOriginalName(originalFileName);
            bucketFileNames.add(minioResult);
        }
        return bucketFileNames;
    }
    /**
     * æ–‡ä»¶ä¸‹è½½
     *
     * @param bucketName       å­˜å‚¨æ¡¶åç§°
     * @param bucketFileName   æ¡¶ä¸­æ–‡ä»¶åç§°
     * @param originalFileName åŽŸå§‹æ–‡ä»¶åç§°
     * @param response         response对象
     */
    public void download(String bucketName, String bucketFileName, String originalFileName, HttpServletResponse response) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(bucketFileName).build();
        try (GetObjectResponse objResponse = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = objResponse.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                response.setCharacterEncoding("utf-8");
                //设置强制下载不打开
                response.setContentType("application/force-download");
                // è®¾ç½®é™„件名称编码
                originalFileName = new String(originalFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
                // è®¾ç½®é™„件名称
                response.addHeader("Content-Disposition", "attachment;fileName=" + originalFileName);
                // å†™å…¥æ–‡ä»¶
                try (ServletOutputStream stream = response.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            throw new UtilException("MinioUtils:上传文件工具类异常");
        }
    }
    /**
     * èŽ·å–å·²ä¸Šä¼ å¯¹è±¡çš„æ–‡ä»¶æµï¼ˆåŽç«¯å› ä¸ºä¸šåŠ¡éœ€è¦èŽ·å–æ–‡ä»¶æµå¯ä»¥è°ƒç”¨è¯¥æ–¹æ³•ï¼‰
     *
     * @param bucketName     å­˜å‚¨æ¡¶åç§°
     * @param bucketFileName æ¡¶ä¸­æ–‡ä»¶åç§°
     * @return æ–‡ä»¶æµ
     */
    public InputStream getFileStream(String bucketName, String bucketFileName) throws Exception {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(bucketFileName).build();
        return minioClient.getObject(objectArgs);
    }
    /**
     * æ‰¹é‡åˆ é™¤æ–‡ä»¶å¯¹è±¡ç»“æžœ
     *
     * @param bucketName      å­˜å‚¨æ¡¶åç§°
     * @param bucketFileName æ¡¶ä¸­æ–‡ä»¶åç§°
     * @return åˆ é™¤ç»“æžœ
     */
    public DeleteError removeObjectsResult(String bucketName, String bucketFileName) {
        List<DeleteError> results = removeObjectsResult(bucketName, Collections.singletonList(bucketFileName));
        return !results.isEmpty() ? results.get(0) : null;
    }
    /**
     * æ‰¹é‡åˆ é™¤æ–‡ä»¶å¯¹è±¡ç»“æžœ
     *
     * @param bucketName      å­˜å‚¨æ¡¶åç§°
     * @param bucketFileNames æ¡¶ä¸­æ–‡ä»¶åç§°é›†åˆ
     * @return åˆ é™¤ç»“æžœ
     */
    public List<DeleteError> removeObjectsResult(String bucketName, List<String> bucketFileNames) {
        Iterable<Result<DeleteError>> results = removeObjects(bucketName, bucketFileNames);
        List<DeleteError> res = new ArrayList<>();
        for (Result<DeleteError> result : results) {
            try {
                res.add(result.get());
            } catch (Exception e) {
                throw new UtilException("MinioUtils:上传文件工具类异常");
            }
        }
        return res;
    }
    /**
     * æ‰¹é‡åˆ é™¤æ–‡ä»¶å¯¹è±¡
     *
     * @param bucketName      å­˜å‚¨æ¡¶åç§°
     * @param bucketFileNames æ¡¶ä¸­æ–‡ä»¶åç§°é›†åˆ
     */
    private Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> bucketFileNames) {
        List<DeleteObject> dos = bucketFileNames.stream().map(DeleteObject::new).collect(Collectors.toList());
        return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
    }
    /**
     * æŸ¥è¯¢é¢„览url
     * @param bucketFileName minio文件名称
     * @param bucketName å­˜å‚¨æ¡¶åç§°
     * @param isPreviewExpiry æ˜¯å¦éœ€è¦è¿‡æœŸæ—¶é—´ é»˜è®¤24小时
     * @return
     */
    public String getPreviewUrl(String bucketFileName, String bucketName, Boolean isPreviewExpiry) {
        if (StringUtils.isNotBlank(bucketFileName)) {
            try {
                minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(bucketFileName).build());
                // ä¸ºfalse只生成24小时有效时长的url链接,可以访问该文件
                if (isPreviewExpiry){
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(bucketFileName).build());
                }else {
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(bucketFileName).expiry(previewExpiry, TimeUnit.HOURS).build());
                }
            } catch (Exception e) {
                throw new UtilException("MinioUtils:上传文件工具类异常");
            }
        }
        return null;
    }
}
src/main/java/com/ruoyi/framework/security/LoginUser.java
@@ -161,6 +161,11 @@
        return user.getUserName();
    }
    public String getNickName()
    {
        return user.getNickName();
    }
    /**
     * è´¦æˆ·æ˜¯å¦æœªè¿‡æœŸ,过期无法验证
     */
src/main/java/com/ruoyi/framework/web/domain/MinioResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.framework.web.domain;
import lombok.Data;
@Data
public class MinioResult {
    // minio中的文件名称
    private String bucketFileName;
    // æºæ–‡ä»¶åç§°
    private String originalName;
    // é¢„览路径
    private String previewExpiry;
}
src/main/java/com/ruoyi/other/controller/TempFileController.java
@@ -3,8 +3,13 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.other.service.TempFileService;
import com.ruoyi.purchase.dto.ProductRecordDto;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.service.ITicketRegistrationService;
import com.ruoyi.purchase.service.impl.TicketRegistrationServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@@ -17,6 +22,8 @@
    private TempFileService tempFileService;
    private TicketRegistrationServiceImpl ticketRegistrationServiceImpl;
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file, Integer type) {
        try {
@@ -26,4 +33,16 @@
        }
    }
    @PostMapping("uploadFile")
    public AjaxResult uploadFile(@RequestBody ProductRecordDto productRecordDto) {
        try {
            if (!productRecordDto.getTempFileIds().isEmpty()&&productRecordDto.getTicketRegistrationId() != null) {
                ticketRegistrationServiceImpl.migrateTempFilesToFormal(productRecordDto.getTicketRegistrationId(), productRecordDto.getTempFileIds());
            }
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
package com.ruoyi.procurementrecord.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.procurementrecord.dto.ProcurementAddDto;
import com.ruoyi.procurementrecord.dto.ProcurementDto;
import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.purchase.dto.InvoicePurchaseReportDto;
import com.ruoyi.quality.pojo.QualityInspect;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/7/7 14:32
 */
@RestController
@Api(tags = "采购入库")
@RequestMapping("/stockin")
public class ProcurementRecordController extends BaseController {
    @Autowired
    private ProcurementRecordService procurementRecordService;
    @GetMapping("/productlist")
    @Log(title = "采购入库-入库管理-新增入库查询", businessType = BusinessType.OTHER)
    public AjaxResult list(ProcurementDto procurementDto) {
        List<ProcurementDto> result =procurementRecordService.listProcurementBySalesLedgerId(procurementDto);
        return AjaxResult.success(result);
    }
    @PostMapping("/add")
    @Log(title = "采购入库-入库管理-新增入库", businessType = BusinessType.INSERT)
    @Transactional
    public AjaxResult add(@RequestBody ProcurementAddDto procurementDto) {
        return AjaxResult.success(procurementRecordService.add(procurementDto));
    }
    @PostMapping("/update")
    @Log(title = "采购入库-入库管理-修改入库", businessType = BusinessType.UPDATE)
    @Transactional
    public AjaxResult updatePro(@RequestBody ProcurementUpdateDto procurementDto) {
        return AjaxResult.success(procurementRecordService.updatePro(procurementDto));
    }
    @PostMapping("/del")
    @Log(title = "采购入库-入库管理-删除入库", businessType = BusinessType.DELETE)
    @Transactional
    public AjaxResult deletePro(@RequestBody ProcurementUpdateDto procurementDto) {
        return AjaxResult.success(procurementRecordService.deletePro(procurementDto));
    }
    @GetMapping("/listPage")
    @Log(title = "采购入库-入库管理-入库查询", businessType = BusinessType.OTHER)
    public AjaxResult listPage(Page page, ProcurementPageDto procurementDto) {
        IPage<ProcurementPageDto> result =procurementRecordService.listPage(page, procurementDto);
        return AjaxResult.success(result);
    }
    /**
     * å¯¼å‡º
     * @param response
     */
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        procurementRecordService.export(response);
    }
}
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementRecordOutController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package com.ruoyi.procurementrecord.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementRecordOutAdd;
import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
/**
 * @author :yys
 * @date : 2025/7/8 13:30
 */
@RestController
@Api(tags = "采购出库")
@RequestMapping("/stockmanagement")
public class ProcurementRecordOutController extends BaseController {
    @Autowired
    private ProcurementRecordOutService procurementRecordOutService;
    @PostMapping("/stockout")
    @Log(title = "采购入库-出库管理-出库", businessType = BusinessType.INSERT)
    public AjaxResult stockout(@RequestBody ProcurementRecordOutAdd procurementRecordOutAdd) {
        return AjaxResult.success(procurementRecordOutService.stockout(procurementRecordOutAdd));
    }
    @GetMapping("/listPage")
    @Log(title = "采购入库-出库管理-出库查询", businessType = BusinessType.OTHER)
    public AjaxResult listPage(Page page, ProcurementRecordOutPageDto procurementDto) {
        IPage<ProcurementRecordOutPageDto> result = procurementRecordOutService.listPage(page, procurementDto);
        return AjaxResult.success(result);
    }
    @PostMapping("/del")
    @Log(title = "采购入库-出库管理-删除出库", businessType = BusinessType.DELETE)
    public AjaxResult deletePro(@RequestBody ProcurementUpdateDto procurementDto) {
        return AjaxResult.success(procurementRecordOutService.deletePro(procurementDto));
    }
    /**
     * å¯¼å‡º
     * @param response
     */
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        procurementRecordOutService.export(response);
    }
}
src/main/java/com/ruoyi/procurementrecord/dto/Details.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.procurementrecord.dto;
import java.math.BigDecimal;
/**
 * @author :yys
 * @date : 2025/7/7 16:17
 */
public class Details {
    private Integer id;
    private BigDecimal inboundQuantity;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public BigDecimal getInboundQuantity() {
        return inboundQuantity;
    }
    public void setInboundQuantity(BigDecimal inboundQuantity) {
        this.inboundQuantity = inboundQuantity;
    }
}
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementAddDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.procurementrecord.dto;
import lombok.Data;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/7/7 16:07
 */
@Data
public class ProcurementAddDto {
    private List<Details> details;
    private String nickName;
}
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package com.ruoyi.procurementrecord.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * @author :yys
 * @date : 2025/7/7 14:46
 */
@Data
public class ProcurementDto {
    private String purchaseContractNumber;
    private Integer id;
    private Integer recordId;
    /**
     * å…¥åº“数量
     */
    private Integer quantityStock;
    /**
     * ä¾›åº”商名称
     */
    @Excel(name = "供应商名称")
    private String supplierName;
    /**
     * äº§å“å¤§ç±»
     */
    @Excel(name = "产品大类")
    private String productCategory;
    /**
     * è§„格型号
     */
    @Excel(name = "规格型号")
    private String specificationModel;
    /**
     * å•位
     */
    @Excel(name = "单位")
    private String unit;
    /**
     * æ•°é‡
     */
    @Excel(name = "数量")
    private BigDecimal quantity;
    /**
     * å¾…入库数量
     */
    @Excel(name = "待入库数量")
    private BigDecimal quantity0;
    /**
     * ç¨Žçއ
     */
    @Excel(name = "税率")
    private BigDecimal taxRate;
    /**
     * å«ç¨Žå•ä»·
     */
    @Excel(name = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
    /**
     * å«ç¨Žæ€»ä»·
     */
    @Excel(name = "含税总价")
    private BigDecimal taxInclusiveTotalPrice;
    /**
     * ä¸å«ç¨Žæ€»ä»·
     */
    @Excel(name = "不含税总价")
    private BigDecimal taxExclusiveTotalPrice;
}
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementPageDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package com.ruoyi.procurementrecord.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * @author :yys
 * @date : 2025/7/7 17:00
 */
@Data
public class ProcurementPageDto {
    private Integer id;
    /**
     * å…¥åº“批次
     */
    @Excel(name = "入库批次")
    private String inboundBatches;
    /**
     * åˆåŒå·
     */
    private String purchaseContractNumber;
    private String salesLedgerProductId;
    /**
     * å‡ºå…¥åº“数量
     */
    @Excel(name = "入库数量")
    private BigDecimal inboundNum;
    /**
     * å¾…出库数量
     */
    @Excel(name = "待出库数量")
    private BigDecimal inboundNum0;
    /**
     * å‡ºå…¥åº“æ—¶é—´
     */
    @Excel(name = "入库时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createDate;
    /**
     * å‡ºå…¥åº“用户
     */
    @Excel(name = "入库人")
    private String createBy;
    /**
     * ä¾›åº”商名称
     */
    @Excel(name = "供应商名称")
    private String supplierName;
    /**
     * äº§å“å¤§ç±»
     */
    @Excel(name = "产品大类")
    private String productCategory;
    /**
     * è§„格型号
     */
    @Excel(name = "规格型号")
    private String specificationModel;
    /**
     * å•位
     */
    @Excel(name = "单位")
    private String unit;
    /**
     * ç¨Žçއ
     */
    @Excel(name = "税率(%)")
    private BigDecimal taxRate;
    /**
     * å«ç¨Žå•ä»·
     */
    @Excel(name = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
    /**
     * å«ç¨Žæ€»ä»·
     */
    @Excel(name = "含税总价")
    private BigDecimal taxInclusiveTotalPrice;
    /**
     * ä¸å«ç¨Žæ€»ä»·
     */
    @Excel(name = "不含税总价")
    private BigDecimal taxExclusiveTotalPrice;
}
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutAdd.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.procurementrecord.dto;
import lombok.Data;
/**
 * @author :yys
 * @date : 2025/7/8 13:34
 */
@Data
public class ProcurementRecordOutAdd {
    private String quantity;
    private String time;
    private Integer id;
    private Integer userId;
    private Integer salesLedgerProductId;
}
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementRecordOutPageDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package com.ruoyi.procurementrecord.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * @author :yys
 * @date : 2025/7/8 14:13
 */
@Data
public class ProcurementRecordOutPageDto {
    private Integer id;
    /**
     * å‡ºå…¥åº“数量
     */
    @Excel(name = "出库数量")
    private BigDecimal inboundNum;
    /**
     * å‡ºå…¥åº“æ—¶é—´
     */
    @Excel(name = "出库时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createDate;
    /**
     * å‡ºå…¥åº“用户
     */
    @Excel(name = "出库人")
    private String createBy;
    /**
     * ä¾›åº”商名称
     */
    @Excel(name = "供应商名称")
    private String supplierName;
    /**
     * äº§å“å¤§ç±»
     */
    @Excel(name = "产品大类")
    private String productCategory;
    /**
     * è§„格型号
     */
    @Excel(name = "规格型号")
    private String specificationModel;
    /**
     * å•位
     */
    @Excel(name = "单位")
    private String unit;
    /**
     * ç¨Žçއ
     */
    @Excel(name = "税率(%)")
    private BigDecimal taxRate;
    /**
     * å«ç¨Žå•ä»·
     */
    @Excel(name = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
    /**
     * å«ç¨Žæ€»ä»·
     */
    @Excel(name = "含税总价")
    private BigDecimal taxInclusiveTotalPrice;
    /**
     * ä¸å«ç¨Žæ€»ä»·
     */
    @Excel(name = "不含税总价")
    private BigDecimal taxExclusiveTotalPrice;
}
src/main/java/com/ruoyi/procurementrecord/dto/ProcurementUpdateDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.procurementrecord.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/7/8 10:40
 */
@Data
public class ProcurementUpdateDto {
    private Integer id;
    private BigDecimal quantityStock;
    private List<Integer> ids;
}
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.procurementrecord.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.procurementrecord.dto.ProcurementDto;
import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecord;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/7/7 14:30
 */
public interface ProcurementRecordMapper extends BaseMapper<ProcurementRecord> {
    /**
     * æ ¹æ®é‡‡è´­å°è´¦id查询采购入库信息
     *
     * @param procurementDto
     * @return
     */
    List<ProcurementDto> listProcurementBySalesLedgerId(@Param("req") ProcurementDto procurementDto);
    IPage<ProcurementPageDto> listPage(Page page,@Param("req")  ProcurementPageDto procurementDto);
    List<ProcurementPageDto> list();
}
src/main/java/com/ruoyi/procurementrecord/mapper/ProcurementRecordOutMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.procurementrecord.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/7/8 13:26
 */
public interface ProcurementRecordOutMapper extends BaseMapper<ProcurementRecordOut> {
    IPage<ProcurementRecordOutPageDto> listPage(Page page,@Param("req") ProcurementRecordOutPageDto procurementDto);
    List<ProcurementRecordOutPageDto> list();
}
src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecord.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package com.ruoyi.procurementrecord.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * @author :yys
 * @date : 2025/7/7 14:25
 */
@TableName("procurement_record_storage")
@Data
@Builder
public class ProcurementRecord {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * äº§å“ä¿¡æ¯è¡¨id
     */
    private Integer salesLedgerProductId;
    /**
     * å…¥åº“批次
     */
    private String inboundBatches;
    /**
     * å…¥åº“数量
     */
    private BigDecimal inboundNum;
    /**
     * å…¥åº“æ—¶é—´
     */
    private LocalDateTime createDate;
    /**
     * å…¥åº“用户
     */
    private String createBy;
    /**
     * å…¥åº“用户id
     */
    private Long userId;
    /**
     * ç§Ÿæˆ·ID
     */
    private Long tenantId;
}
src/main/java/com/ruoyi/procurementrecord/pojo/ProcurementRecordOut.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.procurementrecord.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * @author :yys
 * @date : 2025/7/8 13:24
 */
@TableName("procurement_record_out")
@Data
@Builder
public class ProcurementRecordOut {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * äº§å“ä¿¡æ¯è¡¨id
     */
    private Integer salesLedgerProductId;
    /**
     * å…¥åº“id
     */
    private Integer procurementRecordStorageId;
    /**
     * å‡ºåº“批次
     */
    private String inboundBatches;
    /**
     * å‡ºå‡ºåº“数量
     */
    private BigDecimal inboundNum;
    /**
     * å‡ºå‡ºåº“æ—¶é—´
     */
    private LocalDateTime createDate;
    /**
     * å‡ºå‡ºåº“用户
     */
    private String createBy;
    /**
     * å‡ºå‡ºåº“用户id
     */
    private Integer userId;
    /**
     * ç§Ÿæˆ·ID
     */
    private Long tenantId;
}
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordOutService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.procurementrecord.service;
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.ProcurementRecordOutAdd;
import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import javax.servlet.http.HttpServletResponse;
/**
 * @author :yys
 * @date : 2025/7/8 13:28
 */
public interface ProcurementRecordOutService extends IService<ProcurementRecordOut> {
    int stockout(ProcurementRecordOutAdd procurementRecordOutAdd);
    IPage<ProcurementRecordOutPageDto> listPage(Page page, ProcurementRecordOutPageDto procurementDto);
    int deletePro(ProcurementUpdateDto procurementDto);
    void export(HttpServletResponse response);
}
src/main/java/com/ruoyi/procurementrecord/service/ProcurementRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.procurementrecord.service;
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.ProcurementAddDto;
import com.ruoyi.procurementrecord.dto.ProcurementDto;
import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.pojo.ProcurementRecord;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * @author :yys
 * @date : 2025/7/7 14:36
 */
public interface ProcurementRecordService extends IService<ProcurementRecord> {
    List<ProcurementDto> listProcurementBySalesLedgerId(ProcurementDto procurementDto);
    int add(ProcurementAddDto procurementDto);
    IPage<ProcurementPageDto> listPage(Page page, ProcurementPageDto procurementDto);
    int updatePro(ProcurementUpdateDto procurementDto);
    int deletePro(ProcurementUpdateDto procurementDto);
    void export(HttpServletResponse response);
}
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordOutServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package com.ruoyi.procurementrecord.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementRecordOutAdd;
import com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto;
import com.ruoyi.procurementrecord.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecord;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author :yys
 * @date : 2025/7/8 13:29
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ProcurementRecordOutServiceImpl extends ServiceImpl<ProcurementRecordOutMapper, ProcurementRecordOut> implements ProcurementRecordOutService {
    public final ProcurementRecordOutMapper procurementRecordOutMapper;
    public final SysUserMapper sysUserMapper;
    @Override
    public int stockout(ProcurementRecordOutAdd procurementRecordOutAdd) {
        SysUser sysUser = sysUserMapper.selectUserById(Long.valueOf(procurementRecordOutAdd.getUserId().toString()));
        if(sysUser == null){
            throw new RuntimeException("出库人不存在");
        }
        // æŸ¥è¯¢é‡‡è´­å‡ºåº“数量
        LambdaQueryWrapper<ProcurementRecordOut> procurementRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordLambdaQueryWrapper.eq(ProcurementRecordOut::getProcurementRecordStorageId, procurementRecordOutAdd.getId());
        Long aLong = procurementRecordOutMapper.selectCount(procurementRecordLambdaQueryWrapper);
        ProcurementRecordOut.ProcurementRecordOutBuilder procurementRecordOut = ProcurementRecordOut.builder()
                .procurementRecordStorageId(procurementRecordOutAdd.getId())
                .salesLedgerProductId(procurementRecordOutAdd.getSalesLedgerProductId())
                .inboundBatches(aLong.equals(0L) ? "第1批次" : "第"+ (aLong + 1) + "批次")
                .inboundNum(new BigDecimal(procurementRecordOutAdd.getQuantity()))
                .createDate(LocalDateTime.now())
                .userId(procurementRecordOutAdd.getUserId())
                .createBy(sysUser.getNickName())
                .tenantId(sysUser.getTenantId());
        this.save(procurementRecordOut.build());
        return 0;
    }
    @Override
    public IPage<ProcurementRecordOutPageDto> listPage(Page page, ProcurementRecordOutPageDto procurementDto) {
        return procurementRecordOutMapper.listPage(page, procurementDto);
    }
    public List<ProcurementRecordOut> getProcurementRecordOutByIds(List<Integer> id) {
        return procurementRecordOutMapper.selectBatchIds(id);
    }
    @Override
    public int deletePro(ProcurementUpdateDto procurementDto) {
        List<ProcurementRecordOut> procurementRecordOutByIds = getProcurementRecordOutByIds(procurementDto.getIds());
        if(CollectionUtils.isEmpty(procurementRecordOutByIds)){
            throw new RuntimeException("未查询到选中的人");
        }
        procurementRecordOutMapper.deleteBatchIds(procurementRecordOutByIds.stream().map(ProcurementRecordOut::getId).collect(Collectors.toList()));
        return 0;
    }
    @Override
    public void export(HttpServletResponse response) {
        List<ProcurementRecordOutPageDto> list =procurementRecordOutMapper.list();
        ExcelUtil<ProcurementRecordOutPageDto> util = new ExcelUtil<>(ProcurementRecordOutPageDto.class);
        util.exportExcel(response, list, "出库台账");
    }
}
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,198 @@
package com.ruoyi.procurementrecord.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.procurementrecord.dto.*;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecord;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author :yys
 * @date : 2025/7/7 14:38
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ProcurementRecordServiceImpl extends ServiceImpl<ProcurementRecordMapper, ProcurementRecord> implements ProcurementRecordService {
    private final ProcurementRecordMapper procurementRecordMapper;
    private final ProcurementRecordOutMapper procurementRecordOutMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    @Override
    public List<ProcurementDto> listProcurementBySalesLedgerId(ProcurementDto procurementDto) {
        List<ProcurementDto> procurementDtos = procurementRecordMapper.listProcurementBySalesLedgerId(procurementDto);
        // è®¡ç®—待入库数量
        // æŸ¥è¯¢é‡‡è´­è®°å½•已入库数量
        List<Integer> collect = procurementDtos.stream().map(ProcurementDto::getId).collect(Collectors.toList());
        if(CollectionUtils.isEmpty( collect)){
            return procurementDtos;
        }
        LambdaQueryWrapper<ProcurementRecord> procurementRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordLambdaQueryWrapper.in(ProcurementRecord::getSalesLedgerProductId, collect);
        List<ProcurementRecord> procurementRecords = procurementRecordMapper.selectList(procurementRecordLambdaQueryWrapper);
        if(CollectionUtils.isEmpty( procurementRecords)){
            return procurementDtos;
        }
        for (ProcurementDto dto : procurementDtos) {
            // æ ¹æ®é‡‡è´­å°è´¦ID筛选对应的入库记录
            List<ProcurementRecord> collect1 = procurementRecords.stream()
                    .filter(procurementRecord -> procurementRecord.getSalesLedgerProductId().equals(dto.getId()))
                    .collect(Collectors.toList());
            // å¦‚果没有相关的入库记录,跳过该条数据
            if(CollectionUtils.isEmpty(collect1)){
                dto.setQuantity0(dto.getQuantity());
                continue;
            }
            // è®¡ç®—已入库数量总和,并设置待入库数量
            BigDecimal totalInboundNum = collect1.stream()
                    .map(ProcurementRecord::getInboundNum)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // å¾…入库数量 = æ€»æ•°é‡ - å·²å…¥åº“数量
            dto.setQuantity0(dto.getQuantity().subtract(totalInboundNum));
        }
        return procurementDtos;
    }
    public ProcurementRecord getProcurementRecordById(Integer id){
        ProcurementRecord procurementRecord = procurementRecordMapper.selectById(id);
        if(procurementRecord == null) {
            throw new RuntimeException("未找到该采购入库记录");
        }
        return procurementRecord;
    }
    public List<ProcurementRecord> getProcurementRecordByIds(List<Integer> id){
        List<ProcurementRecord> procurementRecord = procurementRecordMapper.selectBatchIds(id);
        if(procurementRecord == null) {
            throw new RuntimeException("未找到该采购入库记录");
        }
        return procurementRecord;
    }
    @Override
    public int updatePro(ProcurementUpdateDto procurementDto) {
        ProcurementRecord procurementRecordById = getProcurementRecordById(procurementDto.getId());
        procurementRecordById.setInboundNum(procurementDto.getQuantityStock());
        return procurementRecordMapper.updateById(procurementRecordById);
    }
    @Override
    public int deletePro(ProcurementUpdateDto procurementDto) {
        List<ProcurementRecord> procurementRecordById = getProcurementRecordByIds(procurementDto.getIds());
        procurementRecordMapper.deleteBatchIds(procurementRecordById.stream().map(ProcurementRecord::getId).collect(Collectors.toList()));
        // åˆ é™¤æ‰€æœ‰å¯¹åº”的出库记录
        LambdaQueryWrapper<ProcurementRecordOut> procurementRecordOutLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordOutLambdaQueryWrapper.in(ProcurementRecordOut::getProcurementRecordStorageId, procurementDto.getIds());
        List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(procurementRecordOutLambdaQueryWrapper);
        if(!CollectionUtils.isEmpty(procurementRecordOuts)){
            procurementRecordOutMapper.deleteBatchIds(procurementRecordOuts.stream().map(ProcurementRecordOut::getId).collect(Collectors.toList()));
        }
        return 0;
    }
    @Override
    public void export(HttpServletResponse response) {
        List<ProcurementPageDto> list =procurementRecordMapper.list();
        ExcelUtil<ProcurementPageDto> util = new ExcelUtil<ProcurementPageDto>(ProcurementPageDto.class);
        util.exportExcel(response, list, "入库台账");
    }
    @Override
    public int add(ProcurementAddDto procurementDto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        // æ‰¹é‡æ–°å¢ž
        for (Details detail : procurementDto.getDetails()) {
            // æŸ¥è¯¢é‡‡è´­å…¥åº“数量
            LambdaQueryWrapper<ProcurementRecord> procurementRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
            procurementRecordLambdaQueryWrapper.eq(ProcurementRecord::getSalesLedgerProductId, detail.getId());
            Long aLong = procurementRecordMapper.selectCount(procurementRecordLambdaQueryWrapper);
            ProcurementRecord.ProcurementRecordBuilder procurementRecordBuilder = ProcurementRecord.builder()
                    .salesLedgerProductId(detail.getId())
                    .inboundBatches(aLong.equals(0L) ? "第1批次" : "第"+ (aLong + 1) + "批次")
                    .inboundNum(detail.getInboundQuantity())
                    .createDate(LocalDateTime.now())
                    .userId(loginUser.getUserId())
                    .tenantId(loginUser.getTenantId())
                    .createBy(procurementDto.getNickName());
            this.save(procurementRecordBuilder.build());
            // å…¥åº“成功减掉采购数量
//            LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductLambdaQueryWrapper = new LambdaQueryWrapper<>();
//            salesLedgerProductLambdaQueryWrapper.eq(SalesLedgerProduct::getId, detail.getId());
//            SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectOne(salesLedgerProductLambdaQueryWrapper);
//            if(salesLedgerProduct == null){
//               throw new RuntimeException("未找到该商品");
//            }
//            salesLedgerProduct.setQuantity(salesLedgerProduct.getQuantity().subtract(detail.getInboundQuantity()));
//            salesLedgerProductMapper.updateById(salesLedgerProduct);
        }
        return 1;
    }
    @Override
    public IPage<ProcurementPageDto> listPage(Page page, ProcurementPageDto procurementDto) {
        IPage<ProcurementPageDto> procurementPageDtoIPage = procurementRecordMapper.listPage(page, procurementDto);
        List<ProcurementPageDto> procurementPageDtos = procurementPageDtoIPage.getRecords();
        // è®¡ç®—待入库数量
        // æŸ¥è¯¢é‡‡è´­è®°å½•已入库数量
        List<Integer> collect = procurementPageDtos.stream().map(ProcurementPageDto::getId).collect(Collectors.toList());
        if(CollectionUtils.isEmpty( collect)){
            return procurementPageDtoIPage;
        }
        LambdaQueryWrapper<ProcurementRecordOut> procurementRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordLambdaQueryWrapper.in(ProcurementRecordOut::getProcurementRecordStorageId, collect);
        List<ProcurementRecordOut> procurementRecords = procurementRecordOutMapper.selectList(procurementRecordLambdaQueryWrapper);
        if(CollectionUtils.isEmpty( procurementRecords)){
            return procurementPageDtoIPage;
        }
        for (ProcurementPageDto dto : procurementPageDtos) {
            // æ ¹æ®é‡‡è´­å°è´¦ID筛选对应的出库记录
            List<ProcurementRecordOut> collect1 = procurementRecords.stream()
                    .filter(ProcurementRecordOut -> ProcurementRecordOut.getProcurementRecordStorageId().equals(dto.getId()))
                    .collect(Collectors.toList());
            // å¦‚果没有相关的出库记录,跳过该条数据
            if(CollectionUtils.isEmpty(collect1)){
                dto.setInboundNum0(dto.getInboundNum());
                continue;
            }
            // è®¡ç®—已出库数量总和,并设置待出库数量
            BigDecimal totalInboundNum = collect1.stream()
                    .map(ProcurementRecordOut::getInboundNum)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // å¾…出库数量 = æ€»æ•°é‡ - å·²å‡ºåº“数量
            dto.setInboundNum0(dto.getInboundNum().subtract(totalInboundNum));
        }
        return procurementPageDtoIPage;
    }
}
src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
@@ -19,6 +19,7 @@
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.security.core.parameters.P;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -82,6 +83,22 @@
        return toAjax(ticketRegistrationService.addOrUpdateRegistration(ticketRegistrationDto));
    }
    @GetMapping("/getProductRecordById")
    public AjaxResult getProductRecordById(Long id) {
        if (id == null) {
            return AjaxResult.error("参数错误");
        }
        return AjaxResult.success(productRecordService.getProductRecordById(id));
    }
    @ApiModelProperty("修改来票登记")
    @PostMapping("/updateRegistration")
    public AjaxResult updateRegistration(@RequestBody ProductRecordDto productRecordDto) {
        return productRecordService.updateRecord(productRecordDto);
    }
    /**
     * åˆ é™¤æ¥ç¥¨ç™»è®°
     */
src/main/java/com/ruoyi/purchase/dto/ProductRecordDto.java
@@ -1,5 +1,6 @@
package com.ruoyi.purchase.dto;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.sales.pojo.CommonFile;
import lombok.Data;
@@ -30,4 +31,6 @@
    private String unTicketsPrice = "0";
    private List<CommonFile> commonFiles;
    private List<String> tempFileIds;
}
src/main/java/com/ruoyi/purchase/mapper/ProductRecordMapper.java
@@ -17,4 +17,6 @@
public interface ProductRecordMapper extends BaseMapper<ProductRecord> {
    IPage<ProductRecordDto> productRecordPage(Page page, @Param("c") TicketRegistrationDto ticketRegistrationDto);
    ProductRecordDto getProductRecordById(Long id);
}
src/main/java/com/ruoyi/purchase/service/IProductRecordService.java
@@ -3,6 +3,7 @@
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.framework.web.domain.AjaxResult;
import com.ruoyi.purchase.dto.ProductRecordDto;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.pojo.ProductRecord;
@@ -20,4 +21,8 @@
    List<ProductRecord> selectProductRecordList(TicketRegistrationDto ticketRegistrationDto);
    IPage<ProductRecordDto> productRecordPage(Page page, TicketRegistrationDto ticketRegistrationDto);
    AjaxResult updateRecord(ProductRecordDto productRecordDto);
    ProductRecordDto getProductRecordById(Long id);
}
src/main/java/com/ruoyi/purchase/service/impl/ProductRecordServiceImpl.java
@@ -5,15 +5,21 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.purchase.dto.ProductRecordDto;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.mapper.ProductRecordMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.service.IProductRecordService;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -34,8 +40,10 @@
    @Autowired
    private CommonFileMapper commonFileMapper;
    @Autowired
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private PurchaseLedgerMapper purchaseLedgerMapper;
    /**
@@ -61,4 +69,30 @@
        });
        return productRecordDtoIPage;
    }
    @Override
    public AjaxResult updateRecord(ProductRecordDto productRecordDto) {
        SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(productRecordDto.getSaleLedgerProjectId());
        ProductRecord productRecord = productRecordMapper.selectById(productRecordDto.getId());
        if (salesLedgerProduct != null) {
            salesLedgerProduct.setFutureTicketsAmount(salesLedgerProduct.getFutureTicketsAmount().add(productRecord.getTicketsAmount()).subtract(productRecordDto.getTicketsAmount()));
            salesLedgerProduct.setFutureTickets(salesLedgerProduct.getFutureTickets().add(productRecord.getTicketsNum().subtract(productRecordDto.getTicketsNum())));
            salesLedgerProductMapper.updateById(salesLedgerProduct);
        }
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(productRecord.getPurchaseLedgerId());
        if (purchaseLedger != null) {
            purchaseLedger.setReceiptPaymentAmount(purchaseLedger.getReceiptPaymentAmount());
        }
        BeanUtils.copyProperties(productRecordDto,productRecord);
        productRecordMapper.updateById(productRecord);
        return AjaxResult.success("修改成功");
    }
    @Override
    public ProductRecordDto getProductRecordById(Long id) {
        ProductRecordDto productRecordDto = productRecordMapper.getProductRecordById(id);
        return productRecordDto;
    }
}
src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java
@@ -128,6 +128,8 @@
            throw new IllegalArgumentException("采购台账记录不存在,ID: " + ticketRegistrationDto.getPurchaseLedgerId());
        }
        // 3. åˆ›å»ºæˆ–更新票据登记实体
        TicketRegistration ticketRegistration = new TicketRegistration();
        BeanUtils.copyProperties(ticketRegistrationDto, ticketRegistration);
@@ -150,17 +152,24 @@
        // 6. å¢žåŠ é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•
        List<SalesLedgerProduct> salesLedgerProducts = ticketRegistrationDto.getProductData();
        if (CollectionUtils.isNotEmpty(salesLedgerProducts)) {
            int insert = 0 ;
            for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
                ProductRecord productRecord = new ProductRecord();
                //排除掉开票为0的数据
                if (salesLedgerProduct.getTicketsNum() != null && salesLedgerProduct.getTicketsNum().compareTo(BigDecimal.ZERO) > 0) {
                    ProductRecord productRecord = new ProductRecord();
                productRecord.setTicketRegistrationId(ticketRegistration.getId());
                productRecord.setPurchaseLedgerId(ticketRegistrationDto.getPurchaseLedgerId());
                productRecord.setCreatedAt(DateUtils.getNowDate());
                BeanUtils.copyProperties(salesLedgerProduct, productRecord);
                productRecord.setSaleLedgerProjectId(salesLedgerProduct.getId());
                productRecord.setId(null);
                productRecord.setType("2");
                productRecordMapper.insert(productRecord);
                    productRecord.setTicketRegistrationId(ticketRegistration.getId());
                    productRecord.setPurchaseLedgerId(ticketRegistrationDto.getPurchaseLedgerId());
                    productRecord.setCreatedAt(DateUtils.getNowDate());
                    BeanUtils.copyProperties(salesLedgerProduct, productRecord);
                    productRecord.setSaleLedgerProjectId(salesLedgerProduct.getId());
                    productRecord.setId(null);
                    productRecord.setType("2");
                     insert = productRecordMapper.insert(productRecord);
                }
                if (insert <= 0) {
                    throw new RuntimeException("产品开票数都为0,请检查");
                }
            }
        }
        // è¿ç§»ä¸´æ—¶æ–‡ä»¶åˆ°æ­£å¼ç›®å½•
@@ -178,7 +187,7 @@
     * @param tempFileIds ä¸´æ—¶æ–‡ä»¶ID列表
     * @throws IOException æ–‡ä»¶æ“ä½œå¼‚常
     */
    private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
    public void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
        if (CollectionUtils.isEmpty(tempFileIds)) {
            return;
        }
src/main/resources/application-druid.yml
@@ -7,7 +7,7 @@
            # ä¸»åº“数据源
            master:
#                url: jdbc:mysql://localhost:3306/product-inventory-management?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                url: jdbc:mysql://114.132.189.42:9004/product-inventory-management-copy?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                url: jdbc:mysql://114.132.189.42:9004/product-inventory-management?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            # ä»Žåº“数据源
src/main/resources/application.yml
@@ -38,6 +38,14 @@
    com.ruoyi: warn
    org.springframework: warn
minio:
  endpoint: http://114.132.189.42/
  port: 7019
  secure: false
  accessKey: admin
  secretKey: 12345678
  preview-expiry: 24 # é¢„览地址默认24小时
  default-bucket: uploadPath
# ç”¨æˆ·é…ç½®
user:
  password:
@@ -75,7 +83,7 @@
    # æ•°æ®åº“索引
    database: 0
    # å¯†ç 
    password: root2022!
    password:
#    password: 123456
    # è¿žæŽ¥è¶…æ—¶æ—¶é—´
src/main/resources/mapper/basic/StorageAttachmentMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.basic.mapper.StorageAttachmentMapper">
        <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
        <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.StorageAttachment">
                    <id column="id" property="id" />
                    <result column="create_time" property="createTime" />
                    <result column="update_time" property="updateTime" />
                    <result column="deleted" property="deleted" />
                    <result column="record_type" property="recordType" />
                    <result column="record_id" property="recordId" />
                    <result column="name" property="name" />
                    <result column="storage_blob_id" property="storageBlobId" />
        </resultMap>
        <!-- é€šç”¨æŸ¥è¯¢ç»“果列 -->
        <sql id="Base_Column_List">
            id, create_time, update_time, deleted, record_type, record_id, name, storage_blob_id
        </sql>
</mapper>
src/main/resources/mapper/basic/StorageBlobMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.basic.mapper.StorageBlobMapper">
        <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
        <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.StorageBlob">
                    <id column="id" property="id" />
                    <result column="create_time" property="createTime" />
                    <result column="key" property="key" />
                    <result column="content_type" property="contentType" />
                    <result column="original_filename" property="originalFilename" />
                    <result column="bucket_filename" property="bucketFilename" />
                    <result column="bucket_name" property="bucketName" />
                    <result column="byte_size" property="byteSize" />
        </resultMap>
        <!-- é€šç”¨æŸ¥è¯¢ç»“果列 -->
        <sql id="Base_Column_List">
            id, create_time, key, content_type, original_filename,bucket_filename,bucket_name,  byte_size
        </sql>
</mapper>
src/main/resources/mapper/procurementrecord/ProcurementRecordMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper">
    <select id="listProcurementBySalesLedgerId" resultType="com.ruoyi.procurementrecord.dto.ProcurementDto">
        select
            t1.supplier_name,
            t2.product_category,
            t2.id,
            t3.id as recordId,
            t2.specification_model,
            t2.unit,
            t2.quantity,
            t2.quantity as quantity0,
            t2.tax_rate,
            t2.tax_inclusive_unit_price,
            t2.tax_inclusive_total_price,
            t2.tax_exclusive_total_price,
            t3.inbound_num as quantityStock
        from  purchase_ledger t1
        left join sales_ledger_product t2 on t1.id = t2.sales_ledger_id
        left join procurement_record_storage t3 on t2.id = t3.sales_ledger_product_id
        where t1.purchase_contract_number = #{req.purchaseContractNumber}
        <if test="req.id != null and req.id != ''">
            and t3.id = #{req.id}
        </if>
        group by t2.id
    </select>
    <select id="listPage" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
        select
        t3.supplier_name,
        t3.purchase_contract_number,
        t2.product_category,
        t1.id,
        t1.sales_ledger_product_id,
        t2.specification_model,
        t2.unit,
        t2.tax_rate,
        t2.tax_inclusive_unit_price,
        t2.tax_inclusive_total_price,
        t2.tax_exclusive_total_price,
        t1.inbound_batches,
        t1.inbound_num,
        t1.inbound_num as inboundNum0,
        t1.create_date,
        t1.create_by
        from  procurement_record_storage t1
                  left join sales_ledger_product t2 on t2.id = t1.sales_ledger_product_id
                  left join purchase_ledger t3 on t3.id = t2.sales_ledger_id
        <where>
            1 = 1
            <if test="req.supplierName != null and req.supplierName != ''">
                and t3.supplier_name like  concat('%',#{req.supplierName},'%')
            </if>
        </where>
    </select>
    <select id="list" resultType="com.ruoyi.procurementrecord.dto.ProcurementPageDto">
        select
            t3.supplier_name,
            t3.purchase_contract_number,
            t2.product_category,
            t1.id,
            t2.specification_model,
            t2.unit,
            t2.quantity,
            t2.quantity as quantity0,
            t2.tax_rate,
            t2.tax_inclusive_unit_price,
            t2.tax_inclusive_total_price,
            t2.tax_exclusive_total_price,
            t1.inbound_batches,
            t1.inbound_num,
            t1.create_date,
            t1.create_by
        from  procurement_record_storage t1
                  left join sales_ledger_product t2 on t2.id = t1.sales_ledger_product_id
                  left join purchase_ledger t3 on t3.id = t2.sales_ledger_id
    </select>
</mapper>
src/main/resources/mapper/procurementrecord/ProcurementRecordOutMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper">
    <select id="listPage" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
        select
        t3.supplier_name,
        t2.product_category,
        t1.id,
        t2.specification_model,
        t2.unit,
        t2.tax_rate,
        t2.tax_inclusive_unit_price,
        t2.tax_inclusive_total_price,
        t2.tax_exclusive_total_price,
        t1.inbound_num,
        t1.create_date,
        t1.create_by
        from  procurement_record_out t1
        left join sales_ledger_product t2 on t2.id = t1.sales_ledger_product_id
        left join purchase_ledger t3 on t3.id = t2.sales_ledger_id
        <where>
            1 = 1
            <if test="req.supplierName != null and req.supplierName != ''">
                and t3.supplier_name like  concat('%',#{req.supplierName},'%')
            </if>
        </where>
    </select>
    <select id="list" resultType="com.ruoyi.procurementrecord.dto.ProcurementRecordOutPageDto">
        select
            t3.supplier_name,
            t2.product_category,
            t1.id,
            t2.specification_model,
            t2.unit,
            t2.tax_rate,
            t2.tax_inclusive_unit_price,
            t2.tax_inclusive_total_price,
            t2.tax_exclusive_total_price,
            t1.inbound_num,
            t1.create_date,
            t1.create_by
        from  procurement_record_out t1
                  left join sales_ledger_product t2 on t2.id = t1.sales_ledger_product_id
                  left join purchase_ledger t3 on t3.id = t2.sales_ledger_id
    </select>
</mapper>
src/main/resources/mapper/purchase/ProductRecordMapper.xml
@@ -35,5 +35,28 @@
        <if test="c.createdAtEnd != null and c.createdAtEnd != ''">
            and pr.created_at &lt;= date_format(#{c.createdAtEnd},'%Y-%m-%d hh:mm:ss')
        </if>
        <if test="c.purchaseContractNumber != null and c.purchaseContractNumber != ''">
            and tr.purchase_contract_number like concat('%',#{c.purchaseContractNumber},'%')
        </if>
    </select>
    <select id="getProductRecordById" resultType="com.ruoyi.purchase.dto.ProductRecordDto">
        SELECT
            sl.sales_contract_no,
            sl.customer_contract_no,
            sl.customer_name,
            pm.model AS product_model,
            pl.purchase_contract_number,
            pl.supplier_name,
            pr.*,
            tr.invoice_number,
            ROUND(pr.tickets_amount/(1+pr.tax_rate/100),2 ) as un_tickets_price,
            ROUND(pr.tickets_amount-pr.tickets_amount/(1+pr.tax_rate/100),2 )as invoice_amount
        FROM product_record pr
                 left join purchase_ledger pl on pl.id = pr.purchase_ledger_id
                 left join sales_ledger sl on sl.id = pl.sales_ledger_id
                 left join ticket_registration tr on tr.id = pr.ticket_registration_id
                 left join product_model pm on pm.id = pr.product_model_id
        WHERE type = 2 and pr.id = #{id}
    </select>
</mapper>