maven
2 天以前 5323d2e4d07d5ceaaae6a5bf12254641a2d77174
Merge remote-tracking branch 'origin/master'
已修改11个文件
已添加16个文件
1085 ■■■■■ 文件已修改
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/web/domain/MinioResult.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/other/controller/TempFileController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | 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 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/StorageAttachmentMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/StorageBlobMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | 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/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/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
@@ -178,7 +178,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.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:
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/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>