Преглед изворни кода

1.file-service 添加阿里云 OSS
2.OssConfig 中添加一个 bucketName 字段
3.添加 AliyunFile model, 表示存储在 aliyun oss 中的文件

reghao пре 1 недеља
родитељ
комит
ae6ffc3f3c

+ 6 - 0
file/file-service/pom.xml

@@ -121,6 +121,12 @@
             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.8.0</version>
+        </dependency>
+
         <dependency>
             <groupId>com.itextpdf</groupId>
             <artifactId>itextpdf</artifactId>

+ 12 - 1
file/file-service/src/main/java/cn/reghao/tnb/file/app/config/ClientAvailabilityAspect.java

@@ -1,6 +1,7 @@
 package cn.reghao.tnb.file.app.config;
 
 import cn.reghao.tnb.file.app.rpc.OssClientManager;
+import cn.reghao.tnb.file.app.service.AliyunOssManager;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -14,9 +15,11 @@ import org.springframework.stereotype.Component;
 @Component
 public class ClientAvailabilityAspect {
     private final OssClientManager ossClientManager;
+    private final AliyunOssManager aliyunOssManager;
 
-    public ClientAvailabilityAspect(OssClientManager ossClientManager) {
+    public ClientAvailabilityAspect(OssClientManager ossClientManager, AliyunOssManager aliyunOssManager) {
         this.ossClientManager = ossClientManager;
+        this.aliyunOssManager = aliyunOssManager;
     }
 
     // 拦截 StoreServiceWrapperRouter 下的所有公共方法
@@ -27,4 +30,12 @@ public class ClientAvailabilityAspect {
         }
         return joinPoint.proceed();
     }
+
+    @Around("execution(* cn.reghao.tnb.file.app.service.AliyunOssService.*(..))")
+    public Object checkAliyunOssAvailability(ProceedingJoinPoint joinPoint) throws Throwable {
+        if (aliyunOssManager.getClient() == null) {
+            throw new RuntimeException("AliyunOss 未初始化");
+        }
+        return joinPoint.proceed();
+    }
 }

+ 57 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/controller/AliyunOssController.java

@@ -0,0 +1,57 @@
+package cn.reghao.tnb.file.app.controller;
+
+import cn.reghao.tnb.common.auth.AuthUser;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.common.web.WebResult;
+import cn.reghao.tnb.file.app.model.dto.GetSignedUrl;
+import cn.reghao.tnb.file.app.model.dto.UploadedObject;
+import cn.reghao.tnb.file.app.service.AliyunFileService;
+import cn.reghao.tnb.file.app.service.AliyunOssService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2024-04-19 20:47:14
+ */
+@AuthUser
+@Tag(name = "AliyunOSS 接口")
+@RestController
+@RequestMapping("/api/file/aliyun")
+public class AliyunOssController {
+    private final AliyunOssService aliyunOssService;
+    private final AliyunFileService aliyunFileService;
+
+    public AliyunOssController(AliyunOssService aliyunOssService, AliyunFileService aliyunFileService) {
+        this.aliyunOssService = aliyunOssService;
+        this.aliyunFileService = aliyunFileService;
+    }
+
+    @Operation(summary = "获取向阿里云 oss 上传文件需要的签名", description = "N")
+    @GetMapping(value = "/signature", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getOssSignature() {
+        Map<String, Object> map = aliyunOssService.getOssSignature();
+        return WebResult.success(map);
+    }
+
+    @Operation(summary = "获取阿里云 oss 中对象的签名 url", description = "N")
+    @PostMapping(value = "/signed_url", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getSignedUrl(@RequestBody @Validated GetSignedUrl getSignedUrl) {
+        String signedUrl = aliyunOssService.getSignedUrl(getSignedUrl.getObjectName());
+        return WebResult.success(signedUrl);
+    }
+
+    @Operation(summary = "已上传到阿里云 oss 的文件", description = "N")
+    @PostMapping(value = "/uploaded", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String uploadedObject(@RequestBody @Validated UploadedObject uploadedObject) {
+        long loginUser = UserContext.getUserId();
+        String objectId = aliyunFileService.addAliyunFile(uploadedObject, loginUser);
+        Map<String, String> map = Map.of("objectId", objectId);
+        return WebResult.success(map);
+    }
+}

+ 14 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/db/mapper/AliyunFileMapper.java

@@ -0,0 +1,14 @@
+package cn.reghao.tnb.file.app.db.mapper;
+
+import cn.reghao.jutil.jdk.web.db.BaseMapper;
+import cn.reghao.tnb.file.app.model.po.AliyunFile;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 17:35:50
+ */
+@Mapper
+public interface AliyunFileMapper extends BaseMapper<AliyunFile> {
+    AliyunFile findByObjectId(String objectId);
+}

+ 16 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/model/dto/GetSignedUrl.java

@@ -0,0 +1,16 @@
+package cn.reghao.tnb.file.app.model.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author reghao
+ * @date 2024-08-30 17:06:36
+ */
+@Setter
+@Getter
+public class GetSignedUrl {
+    @NotBlank
+    private String objectName;
+}

+ 16 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/model/dto/UploadedObject.java

@@ -0,0 +1,16 @@
+package cn.reghao.tnb.file.app.model.dto;
+
+import lombok.Data;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 17:24:01
+ */
+@Data
+public class UploadedObject {
+    private String sha256sum;
+    private String objectName;
+    private String objectUrl;
+    private String filename;
+    private Long size;
+}

+ 36 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/model/po/AliyunFile.java

@@ -0,0 +1,36 @@
+package cn.reghao.tnb.file.app.model.po;
+
+import cn.reghao.jutil.jdk.web.db.BaseObject;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.file.app.model.dto.UploadedObject;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 17:34:18
+ */
+public class AliyunFile extends BaseObject<Integer> {
+    @NotBlank
+    private String objectId;
+    @NotBlank
+    private String objectName;
+    @NotBlank
+    private String sha256sum;
+    @NotNull
+    private Integer fileType;
+    @NotBlank
+    private String filename;
+    @NotNull
+    private Long size;
+    private Long owner;
+
+    public AliyunFile(String objectId, UploadedObject uploadedObject) {
+        this.objectId = objectId;
+        this.objectName = uploadedObject.getObjectName();
+        this.sha256sum = uploadedObject.getSha256sum();
+        this.filename = uploadedObject.getFilename();
+        this.size = uploadedObject.getSize();
+        this.owner = UserContext.getUserId();
+    }
+}

+ 4 - 1
file/file-service/src/main/java/cn/reghao/tnb/file/app/model/po/OssConfig.java

@@ -1,6 +1,7 @@
 package cn.reghao.tnb.file.app.model.po;
 
 import cn.reghao.jutil.jdk.web.db.BaseObject;
+import cn.reghao.tnb.file.app.model.constant.OssType;
 import cn.reghao.tnb.file.app.model.dto.OssConfigDto;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
@@ -18,11 +19,13 @@ public class OssConfig extends BaseObject<Integer> {
     private String endpoint;
     private String accessKeyId;
     private String accessKeySecret;
+    private String bucketName;
 
     public OssConfig(OssConfigDto ossConfigDto) {
-        this.ossType = 1;
+        this.ossType = OssType.localOss.getCode();
         this.endpoint = ossConfigDto.getEndpoint();
         this.accessKeyId = ossConfigDto.getAccessKeyId();
         this.accessKeySecret = ossConfigDto.getAccessKeySecret();
+        this.bucketName = "";
     }
 }

+ 28 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/service/AliyunFileService.java

@@ -0,0 +1,28 @@
+package cn.reghao.tnb.file.app.service;
+
+import cn.reghao.tnb.file.app.db.mapper.AliyunFileMapper;
+import cn.reghao.tnb.file.app.model.dto.UploadedObject;
+import cn.reghao.tnb.file.app.model.po.AliyunFile;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 17:27:37
+ */
+@Service
+public class AliyunFileService {
+    private final AliyunFileMapper aliyunFileMapper;
+
+    public AliyunFileService(AliyunFileMapper aliyunFileMapper) {
+        this.aliyunFileMapper = aliyunFileMapper;
+    }
+
+    public String addAliyunFile(UploadedObject uploadedObject, long userId) {
+        String objectId = UUID.randomUUID().toString().replace("-", "");
+        AliyunFile aliyunFile = new AliyunFile(objectId, uploadedObject);
+        aliyunFileMapper.save(aliyunFile);
+        return objectId;
+    }
+}

+ 78 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/service/AliyunOss.java

@@ -0,0 +1,78 @@
+package cn.reghao.tnb.file.app.service;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.common.utils.BinaryUtil;
+import com.aliyun.oss.model.PolicyConditions;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 15:25:46
+ */
+@Slf4j
+public class AliyunOss {
+    private final OSS ossClient;
+    private String endpoint;
+    private String accessKeyId;
+    private String bucketName;
+    private final String host;
+
+    public AliyunOss(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
+        this.ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        this.endpoint = endpoint;
+        this.accessKeyId = accessKeyId;
+        this.bucketName = bucketName;
+        this.host = "https://" + bucketName + "." + endpoint;
+    }
+
+    public Map<String, Object> getOssSignature() {
+        try {
+            long expireTime = 3600; // 签名有效期 1 小时 (秒)
+            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
+            Date expiration = new Date(expireEndTime);
+
+            // 设置上传限制条件
+            PolicyConditions policyConds = new PolicyConditions();
+            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 104857600); // 100MB
+
+            // 生成 Policy 字符串
+            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
+            byte[] binaryData = postPolicy.getBytes("utf-8");
+            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
+
+            // 生成加密签名 Signature
+            String postSignature = ossClient.calculatePostSignature(postPolicy);
+
+            // 组装返回给前端的 JSON 格式
+            Map<String, Object> respMap = new LinkedHashMap<>();
+            respMap.put("host", host);
+            respMap.put("OSSAccessKeyId", accessKeyId);
+            respMap.put("policy", encodedPolicy);
+            respMap.put("signature", postSignature);
+            respMap.put("expire", expireEndTime / 1000);
+            return respMap;
+
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return null;
+        }
+    }
+
+    public String getSignedUrl(String objectName) {
+        int expireSecond = 3600;
+        long timestamp = System.currentTimeMillis() + expireSecond*1000L;
+        Date expiration = new Date(timestamp);
+        URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
+        return url.toString();
+    }
+
+    public void close() {
+        ossClient.shutdown();
+    }
+}

+ 60 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/service/AliyunOssManager.java

@@ -0,0 +1,60 @@
+package cn.reghao.tnb.file.app.service;
+
+import cn.reghao.tnb.file.app.db.mapper.OssConfigMapper;
+import cn.reghao.tnb.file.app.model.constant.OssType;
+import cn.reghao.tnb.file.app.model.po.OssConfig;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 16:59:18
+ */
+@Slf4j
+@Component
+public class AliyunOssManager {
+    private final OssConfigMapper ossConfigMapper;
+    private AliyunOss client;
+
+    public AliyunOssManager(OssConfigMapper ossConfigMapper) {
+        this.ossConfigMapper = ossConfigMapper;
+    }
+
+    public AliyunOss getClient() {
+        return this.client;
+    }
+
+    @PostConstruct
+    public void init() {
+        OssConfig ossConfig = ossConfigMapper.findByOssType(OssType.aliyunOss.getCode());
+        if (ossConfig != null) {
+            updateClient(ossConfig);
+        }
+    }
+
+    /**
+     * 动态初始化或更新 Client
+     */
+    public synchronized void updateClient(OssConfig ossConfig) {
+        log.info("正在初始化 AliyunOss: {}", ossConfig.getEndpoint());
+        this.destroyClient();
+
+        String endpoint = ossConfig.getEndpoint();
+        String ak = ossConfig.getAccessKeyId();
+        String sk = ossConfig.getAccessKeySecret();
+        String bucketName = ossConfig.getBucketName();
+        this.client = new AliyunOss(endpoint, ak, sk, bucketName);
+    }
+
+    public void destroyClient() {
+        if (this.client != null) {
+            try {
+                this.client.close(); // 确保资源释放
+            } catch (Exception e) {
+                log.error("销毁 AliyunOss 失败", e);
+            }
+            this.client = null;
+        }
+    }
+}

+ 27 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/service/AliyunOssService.java

@@ -0,0 +1,27 @@
+package cn.reghao.tnb.file.app.service;
+
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2026-06-15 17:09:36
+ */
+@Service
+public class AliyunOssService {
+    private final AliyunOssManager aliyunOssManager;
+
+    public AliyunOssService(AliyunOssManager aliyunOssManager) {
+        this.aliyunOssManager = aliyunOssManager;
+    }
+
+    public Map<String, Object> getOssSignature() {
+        Map<String, Object> map = aliyunOssManager.getClient().getOssSignature();
+        return map;
+    }
+
+    public String getSignedUrl(String objectName) {
+        return aliyunOssManager.getClient().getSignedUrl(objectName);
+    }
+}

+ 17 - 0
file/file-service/src/main/resources/mapper/file/AliyunFileMapper.xml

@@ -0,0 +1,17 @@
+<?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="cn.reghao.tnb.file.app.db.mapper.AliyunFileMapper">
+    <insert id="save" useGeneratedKeys="true" keyProperty="id">
+        insert into file_aliyun_file
+        (`object_id`,`object_name`,`sha256sum`,`file_type`,`filename`,`size`,`owner`)
+        values
+        (#{objectId},#{objectName},#{sha256sum},#{fileType},#{filename},#{size},#{owner})
+    </insert>
+
+    <select id="findByObjectId" resultType="cn.reghao.tnb.file.app.model.po.AliyunFile">
+        select *
+        from file_aliyun_file
+        where object_id=#{objectId}
+    </select>
+</mapper>

+ 2 - 2
file/file-service/src/main/resources/mapper/file/OssConfigMapper.xml

@@ -4,9 +4,9 @@
 <mapper namespace="cn.reghao.tnb.file.app.db.mapper.OssConfigMapper">
     <insert id="save" useGeneratedKeys="true" keyProperty="id">
         insert into file_oss_config
-        (`oss_type`,`endpoint`,`access_key_id`,`access_key_secret`)
+        (`oss_type`,`endpoint`,`access_key_id`,`access_key_secret`,`bucket_name`)
         values
-        (#{ossType},#{endpoint},#{accessKeyId},#{accessKeySecret})
+        (#{ossType},#{endpoint},#{accessKeyId},#{accessKeySecret},#{bucketName})
     </insert>