From 35320a7048015f298f533a518d6fd932c0cab0dc Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期二, 08 七月 2025 09:54:03 +0800
Subject: [PATCH] 2025-07-08 minio引入
---
src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java | 91 +++++
src/main/java/com/ruoyi/basic/mapper/StorageAttachmentMapper.java | 18 +
src/main/java/com/ruoyi/basic/service/StorageBlobService.java | 35 ++
src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java | 103 ++++++
src/main/java/com/ruoyi/common/config/MinioConfig.java | 27 +
src/main/java/com/ruoyi/common/utils/MinioUtils.java | 309 +++++++++++++++++++
src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java | 72 ++++
src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java | 3
src/main/java/com/ruoyi/basic/pojo/StorageBlob.java | 67 ++++
pom.xml | 24 +
src/main/java/com/ruoyi/common/constant/StorageAttachmentConstants.java | 17 +
src/main/resources/mapper/basic/StorageBlobMapper.xml | 22 +
src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java | 25 +
src/main/resources/mapper/basic/StorageAttachmentMapper.xml | 22 +
src/main/java/com/ruoyi/framework/web/domain/MinioResult.java | 16 +
src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java | 9
src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java | 18 +
src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java | 43 ++
18 files changed, 921 insertions(+), 0 deletions(-)
diff --git a/pom.xml b/pom.xml
index ae85289..14851a3 100644
--- a/pom.xml
+++ b/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>
diff --git a/src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java b/src/main/java/com/ruoyi/basic/dto/StorageBlobDTO.java
new file mode 100644
index 0000000..b868048
--- /dev/null
+++ b/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;
+}
diff --git a/src/main/java/com/ruoyi/basic/mapper/StorageAttachmentMapper.java b/src/main/java/com/ruoyi/basic/mapper/StorageAttachmentMapper.java
new file mode 100644
index 0000000..12a34d8
--- /dev/null
+++ b/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> {
+
+}
diff --git a/src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java b/src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java
new file mode 100644
index 0000000..756b4b9
--- /dev/null
+++ b/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> {
+
+}
diff --git a/src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java b/src/main/java/com/ruoyi/basic/pojo/StorageAttachment.java
new file mode 100644
index 0000000..0e30cad
--- /dev/null
+++ b/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;
+ /**
+ * 鍏宠仈鐨勮褰昳d
+ */
+ @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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/basic/pojo/StorageBlob.java b/src/main/java/com/ruoyi/basic/pojo/StorageBlob.java
new file mode 100644
index 0000000..8e92865
--- /dev/null
+++ b/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;
+ /**
+ * 璧勬簮绫诲瀷锛屼緥濡侸PG鍥剧墖鐨勮祫婧愮被鍨嬩负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;
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java b/src/main/java/com/ruoyi/basic/service/StorageAttachmentService.java
new file mode 100644
index 0000000..e285063
--- /dev/null
+++ b/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);
+}
diff --git a/src/main/java/com/ruoyi/basic/service/StorageBlobService.java b/src/main/java/com/ruoyi/basic/service/StorageBlobService.java
new file mode 100644
index 0000000..00d8c3b
--- /dev/null
+++ b/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);
+}
diff --git a/src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/StorageAttachmentServiceImpl.java
new file mode 100644
index 0000000..78c8647
--- /dev/null
+++ b/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()));
+ }
+}
diff --git a/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
new file mode 100644
index 0000000..6be4401
--- /dev/null
+++ b/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;
+ }
+}
diff --git a/src/main/java/com/ruoyi/common/config/MinioConfig.java b/src/main/java/com/ruoyi/common/config/MinioConfig.java
new file mode 100644
index 0000000..28f489f
--- /dev/null
+++ b/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();
+ }
+}
diff --git a/src/main/java/com/ruoyi/common/constant/StorageAttachmentConstants.java b/src/main/java/com/ruoyi/common/constant/StorageAttachmentConstants.java
new file mode 100644
index 0000000..1bc8123
--- /dev/null
+++ b/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";
+}
diff --git a/src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java b/src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java
new file mode 100644
index 0000000..8b0597e
--- /dev/null
+++ b/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;
+ }
+}
diff --git a/src/main/java/com/ruoyi/common/utils/MinioUtils.java b/src/main/java/com/ruoyi/common/utils/MinioUtils.java
new file mode 100644
index 0000000..55af1dd
--- /dev/null
+++ b/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);
+ // 鎵ц姝e父鎿嶄綔
+ 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());
+ // 涓篺alse鍙敓鎴�24灏忔椂鏈夋晥鏃堕暱鐨剈rl閾炬帴锛屽彲浠ヨ闂鏂囦欢
+ 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;
+ }
+
+}
diff --git a/src/main/java/com/ruoyi/framework/web/domain/MinioResult.java b/src/main/java/com/ruoyi/framework/web/domain/MinioResult.java
new file mode 100644
index 0000000..8542ec8
--- /dev/null
+++ b/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;
+}
diff --git a/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java b/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
index 1791d23..ae4141b 100644
--- a/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
+++ b/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
@@ -86,6 +86,9 @@
@GetMapping("/getProductRecordById")
public AjaxResult getProductRecordById(Long id) {
+ if (id == null) {
+ return AjaxResult.error("鍙傛暟閿欒");
+ }
return AjaxResult.success(productRecordService.getProductRecordById(id));
}
diff --git a/src/main/resources/mapper/basic/StorageAttachmentMapper.xml b/src/main/resources/mapper/basic/StorageAttachmentMapper.xml
new file mode 100644
index 0000000..a2cc6cf
--- /dev/null
+++ b/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>
\ No newline at end of file
diff --git a/src/main/resources/mapper/basic/StorageBlobMapper.xml b/src/main/resources/mapper/basic/StorageBlobMapper.xml
new file mode 100644
index 0000000..84e3b00
--- /dev/null
+++ b/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>
\ No newline at end of file
--
Gitblit v1.9.3