From cf2eb689ca59d3f21734e26b8a2564b07b86d219 Mon Sep 17 00:00:00 2001
From: NingHuan <2139614357@qq.com>
Date: Thu, 5 Jun 2025 14:55:22 +0800
Subject: [PATCH] =?UTF-8?q?[=E6=8F=90=E4=BA=A4]:=E5=A2=9E=E5=8A=A0?=
 =?UTF-8?q?=E4=BA=86=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E5=88=B0=E9=98=BF?=
 =?UTF-8?q?=E9=87=8C=E4=BA=91oss=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

# Conflicts:
#	src/main/resources/application.properties
---
 .../config/OssConfiguration.java              |  29 +++
 .../constant/MessageConstant.java             |   4 +
 .../controller/CommonController.java          |  59 +++++
 .../pojo/ModelVersionDTO.java                 |   2 +-
 .../properties/AliOssProperties.java          |  16 ++
 .../service/Impl/ModelServiceImpl.java        |   3 +-
 .../util/AliOssUtil.java                      | 207 ++++++++++++++++++
 src/main/resources/application.properties     |  20 +-
 src/main/resources/mapper/ModelMapper.xml     |  28 ++-
 9 files changed, 350 insertions(+), 18 deletions(-)
 create mode 100644 src/main/java/com/bipt/intelligentapplicationorchestrationservice/config/OssConfiguration.java
 create mode 100644 src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/CommonController.java
 create mode 100644 src/main/java/com/bipt/intelligentapplicationorchestrationservice/properties/AliOssProperties.java
 create mode 100644 src/main/java/com/bipt/intelligentapplicationorchestrationservice/util/AliOssUtil.java

diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/config/OssConfiguration.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/config/OssConfiguration.java
new file mode 100644
index 0000000..9b921f1
--- /dev/null
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/config/OssConfiguration.java
@@ -0,0 +1,29 @@
+package com.bipt.intelligentapplicationorchestrationservice.config;
+
+
+import com.bipt.intelligentapplicationorchestrationservice.properties.AliOssProperties;
+import com.bipt.intelligentapplicationorchestrationservice.util.AliOssUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/*
+ * 配置类,用于创建阿里云文件上传工具类对象
+ */
+@Slf4j
+@Configuration
+public class OssConfiguration {
+    private final AliOssProperties aliOssProperties;
+    public OssConfiguration(AliOssProperties aliOssProperties) {
+        this.aliOssProperties = aliOssProperties;
+    }
+    @Bean
+    public AliOssUtil aliOssUtil(){
+        log.info("开始创建阿里云文件上传工具类对象...");
+        return new AliOssUtil(
+                aliOssProperties.getEndpoint(),
+                aliOssProperties.getAccessKeyId(),
+                aliOssProperties.getAccessKeySecret(),
+                aliOssProperties.getBucketName());
+    }
+}
diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/constant/MessageConstant.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/constant/MessageConstant.java
index 7347c95..0b98993 100644
--- a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/constant/MessageConstant.java
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/constant/MessageConstant.java
@@ -11,4 +11,8 @@ public class MessageConstant {
     public static final String ERROR_ABANDONED_CANNOT_UPDATE = "已废弃的模型只能查看信息,不能更新生命周期";
     public static final String ERROR_TRAINING_INVALID_TRANSITION = "训练中的模型只能调整成设计和评估";
     public static final String UPDATE_FAILURE = "更新模型生命周期失败";
+
+    //文件上传常量
+    public static final String UPLOAD_FAILURE = "上传文件失败";
+    public static final String  FILE_EMPTY= "文件为空";
 }
diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/CommonController.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/CommonController.java
new file mode 100644
index 0000000..6424377
--- /dev/null
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/CommonController.java
@@ -0,0 +1,59 @@
+package com.bipt.intelligentapplicationorchestrationservice.controller;
+
+import com.bipt.intelligentapplicationorchestrationservice.constant.MessageConstant;
+import com.bipt.intelligentapplicationorchestrationservice.pojo.OptResult;
+import com.bipt.intelligentapplicationorchestrationservice.util.AliOssUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.UUID;
+
+@Slf4j
+@RestController
+@RequestMapping("/common")
+@Tag(name = "通用接口")
+@CrossOrigin(origins = "http://localhost:3000")
+public class CommonController {
+    @Autowired
+    private AliOssUtil aliOssUtil;
+    // 默认上传的文件夹
+    private static final String DEFAULT_FOLDER= "File/";
+
+    /**
+     * 文件上传
+     * @param file
+     * @return
+     */
+    @Operation(summary = "文件上传")
+    @PostMapping("/upload")
+    public OptResult uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
+        log.info("收到上传请求");
+        if (file.isEmpty()){
+            return OptResult.error(MessageConstant.FILE_EMPTY);
+        }
+        try  {
+            // 1.生成唯一文件名
+            String originalFilename = file.getOriginalFilename();   //  原始文件名
+            log.info("原始文件名:{}", originalFilename);
+            String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));  // 文件后缀
+            log.info("文件后缀:{}", fileSuffix);
+            String fileName = UUID.randomUUID().toString() + fileSuffix; // 唯一文件名
+            log.info("唯一文件名:{}", fileName);
+            // 2.构建oss存储路径
+            String objectName = DEFAULT_FOLDER + fileName;
+            // 3.调用工具类上传文件
+            String fileUrl = aliOssUtil.upload(file.getInputStream(), objectName);
+            // 4.返回文件URL
+            return OptResult.success(fileUrl);
+        } catch (Exception e) {
+            log.error(MessageConstant.UPLOAD_FAILURE +":{}", e.getMessage(), e);
+            return OptResult.error(MessageConstant.UPLOAD_FAILURE);
+        }
+
+    }
+}
diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/pojo/ModelVersionDTO.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/pojo/ModelVersionDTO.java
index 9180f7f..55618ab 100644
--- a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/pojo/ModelVersionDTO.java
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/pojo/ModelVersionDTO.java
@@ -11,7 +11,7 @@ import java.time.LocalDateTime;
 @NoArgsConstructor
 @AllArgsConstructor
 public class ModelVersionDTO {
-    private Long id;                // 模型id
+    private Long id;                // 模型版本id
     private String version;         // 模型版本
     private Integer datasetId;      // 数据集id
     private String modelConfig;     // 模型配置信息
diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/properties/AliOssProperties.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/properties/AliOssProperties.java
new file mode 100644
index 0000000..7daa080
--- /dev/null
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/properties/AliOssProperties.java
@@ -0,0 +1,16 @@
+package com.bipt.intelligentapplicationorchestrationservice.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "aliyun.oss") // 读取以 aliyun.oss 开头的配置
+public class AliOssProperties {
+    private String endpoint;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private String bucketName;
+}
+
diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/service/Impl/ModelServiceImpl.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/service/Impl/ModelServiceImpl.java
index 8b25ff1..028f214 100644
--- a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/service/Impl/ModelServiceImpl.java
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/service/Impl/ModelServiceImpl.java
@@ -42,7 +42,7 @@ public class ModelServiceImpl implements ModelService {
         modelVersion.setModelId(modelInfo.getId());
         modelVersion.setCreateTime(LocalDateTime.now());
         modelVersion.setUpdateTime(LocalDateTime.now());
-        modelVersion.setOperateUser("zs");
+        modelVersion.setOperateUser("zs");    //这里的写死的,后续需要修改,应该是当前登录用户的id
         modelMapper.insertModelVersion(modelVersion);
     }
 
@@ -81,6 +81,7 @@ public class ModelServiceImpl implements ModelService {
 
     @Override
     public void updateModel(ModelVersionDTO dto) {
+        // 更新模型还需要更新操作人和时间
         log.info("更新模型");
         modelMapper.update(dto);
     }
diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/util/AliOssUtil.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/util/AliOssUtil.java
new file mode 100644
index 0000000..f0f6dce
--- /dev/null
+++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/util/AliOssUtil.java
@@ -0,0 +1,207 @@
+package com.bipt.intelligentapplicationorchestrationservice.util;
+
+import com.aliyun.oss.HttpMethod;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Date;
+
+/**
+ * 阿里云OSS文件操作工具类
+ */
+@Data
+@Slf4j
+@AllArgsConstructor
+public class AliOssUtil {
+
+    private final String endpoint;
+    private final String accessKeyId;
+    private final String accessKeySecret;
+    private final String bucketName;
+
+    /**
+     * 上传文件到OSS
+     * @param file 文件对象
+     * @param objectName 对象名称(OSS中的路径)
+     * @return 文件URL
+     */
+    public String upload(File file, String objectName) {
+        try (InputStream inputStream = new FileInputStream(file)) {
+            return upload(inputStream, objectName);
+        } catch (Exception e) {
+            log.error("上传文件失败: {}", e.getMessage(), e);
+            throw new RuntimeException("上传文件到OSS失败", e);
+        }
+    }
+
+    /**
+     * 上传文件流到OSS
+     * @param inputStream 文件流
+     * @param objectName 对象名称(OSS中的路径)
+     * @return 文件URL
+     */
+    public String upload(InputStream inputStream, String objectName) {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        try {
+            // 创建PutObject请求
+            PutObjectRequest request = new PutObjectRequest(bucketName, objectName, inputStream);
+
+            // 设置对象元数据
+            ObjectMetadata metadata = new ObjectMetadata();
+            metadata.setContentType(getContentType(objectName));
+            request.setMetadata(metadata);
+
+            // 上传文件
+            ossClient.putObject(request);
+            log.info("文件上传成功: {}", objectName);
+
+            // 构建文件URL
+            return "https://" + bucketName + "." + endpoint + "/" + objectName;
+        } catch (Exception e) {
+            log.error("上传文件失败: {}", e.getMessage(), e);
+            throw new RuntimeException("上传文件到OSS失败", e);
+        } finally {
+            // 关闭OSSClient
+            ossClient.shutdown();
+        }
+    }
+
+    /**
+     * 生成临时签名URL,用于访问私有Bucket中的文件
+     * @param objectName 对象名称
+     * @param expirationMinutes 过期时间(分钟)
+     * @return 签名URL
+     */
+    public String generatePresignedUrl(String objectName, int expirationMinutes) {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        try {
+            // 设置URL过期时间
+            Date expiration = new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000);
+
+            // 生成签名URL
+            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.GET);
+            request.setExpiration(expiration);
+
+            URL url = ossClient.generatePresignedUrl(request);
+            log.info("生成临时签名URL: {}", url);
+            return url.toString();
+        } catch (Exception e) {
+            log.error("生成签名URL失败: {}", e.getMessage(), e);
+            throw new RuntimeException("生成签名URL失败", e);
+        } finally {
+            // 关闭OSSClient
+            ossClient.shutdown();
+        }
+    }
+
+    /**
+     * 下载文件到本地
+     * @param objectName 对象名称
+     * @param destinationFile 目标文件
+     */
+    public void download(String objectName, File destinationFile) {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        try {
+            // 下载OSS文件到本地文件
+            ossClient.getObject(new GetObjectRequest(bucketName, objectName), destinationFile);
+            log.info("文件下载成功: {}", objectName);
+        } catch (Exception e) {
+            log.error("下载文件失败: {}", e.getMessage(), e);
+            throw new RuntimeException("下载文件失败", e);
+        } finally {
+            // 关闭OSSClient
+            ossClient.shutdown();
+        }
+    }
+
+    /**
+     * 删除OSS中的文件
+     * @param objectName 对象名称
+     */
+    public void delete(String objectName) {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        try {
+            // 删除文件
+            ossClient.deleteObject(bucketName, objectName);
+            log.info("文件删除成功: {}", objectName);
+        } catch (Exception e) {
+            log.error("删除文件失败: {}", e.getMessage(), e);
+            throw new RuntimeException("删除文件失败", e);
+        } finally {
+            // 关闭OSSClient
+            ossClient.shutdown();
+        }
+    }
+
+    /**
+     * 检查文件是否存在
+     * @param objectName 对象名称
+     * @return 文件是否存在
+     */
+    public boolean doesObjectExist(String objectName) {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        try {
+            return ossClient.doesObjectExist(bucketName, objectName);
+        } catch (Exception e) {
+            log.error("检查文件存在失败: {}", e.getMessage(), e);
+            throw new RuntimeException("检查文件存在失败", e);
+        } finally {
+            // 关闭OSSClient
+            ossClient.shutdown();
+        }
+    }
+
+    /**
+     * 根据文件扩展名确定Content-Type
+     * @param fileName 文件名
+     * @return Content-Type
+     */
+    private String getContentType(String fileName) {
+        if (fileName == null) {
+            return "application/octet-stream";
+        }
+
+        if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
+            return "image/jpeg";
+        } else if (fileName.endsWith(".png")) {
+            return "image/png";
+        } else if (fileName.endsWith(".gif")) {
+            return "image/gif";
+        } else if (fileName.endsWith(".txt")) {
+            return "text/plain";
+        } else if (fileName.endsWith(".pdf")) {
+            return "application/pdf";
+        } else if (fileName.endsWith(".doc")) {
+            return "application/msword";
+        } else if (fileName.endsWith(".docx")) {
+            return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+        } else if (fileName.endsWith(".xls")) {
+            return "application/vnd.ms-excel";
+        } else if (fileName.endsWith(".xlsx")) {
+            return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+        } else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) {
+            return "text/html";
+        } else if (fileName.endsWith(".css")) {
+            return "text/css";
+        } else if (fileName.endsWith(".js")) {
+            return "application/javascript";
+        } else if (fileName.endsWith(".json")) {
+            return "application/json";
+        } else if (fileName.endsWith(".xml")) {
+            return "application/xml";
+        } else if (fileName.endsWith(".mp4")) {
+            return "video/mp4";
+        } else if (fileName.endsWith(".mp3")) {
+            return "audio/mpeg";
+        } else {
+            return "application/octet-stream";
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 7520e47..4d6d6f4 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,7 +1,7 @@
 spring.application.name=intelligent-application-orchestration-service
 
 
-# ?????
+# 数据库配置
 spring.datasource.url=jdbc:kingbase8://116.205.121.200:54321/Ipz
 spring.datasource.username=system
 spring.datasource.password=root
@@ -10,7 +10,7 @@ spring.datasource.hikari.maximum-pool-size=10
 spring.datasource.hikari.minimum-idle=5
 spring.datasource.hikari.connection-timeout=30000
 
-# MyBatis??
+# MyBatis配置
 mybatis.mapper-locations=classpath:mapper/*.xml
 mybatis.type-aliases-package=com.bipt.intelligentapplicationorchestrationservice.pojo
 mybatis.configuration.map-underscore-to-camel-case=true
@@ -26,7 +26,7 @@ spring.data.redis.database=0
 spring.data.redis.timeout=3000
 spring.data.redis.ssl.enabled=false
 
-# ??????
+# 服务路由配置
 spring.cloud.gateway.routes[0].id=request-service-route
 spring.cloud.gateway.routes[0].uri=lb://intelligent-application-orchestration-service
 spring.cloud.gateway.routes[0].predicates[0]=Path=/request
@@ -36,8 +36,18 @@ spring.cloud.gateway.routes[0].predicates[0]=Path=/request
 logging.level.org.springframework.web=DEBUG
 
 
-#SQL ????????
+#SQL语句日志输出配置
 logging.level.com.bipt.intelligentapplicationorchestrationservice.mapper=DEBUG
 mybatis.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl
 
-management.health.rabbit.enabled=false
\ No newline at end of file
+management.health.rabbit.enabled=false
+
+# 文件上传配置
+spring.servlet.multipart.enabled=true
+spring.servlet.multipart.max-file-size=100MB
+spring.servlet.multipart.max-request-size=100MB
+
+# 激活开发环境!告诉 Spring:加载 application-dev.properties 里的配置
+spring.profiles.active=dev
+
+
diff --git a/src/main/resources/mapper/ModelMapper.xml b/src/main/resources/mapper/ModelMapper.xml
index 0d8d4bb..002bcca 100644
--- a/src/main/resources/mapper/ModelMapper.xml
+++ b/src/main/resources/mapper/ModelMapper.xml
@@ -41,19 +41,25 @@
     </select>
 
     <!--更新模型信息-->
-    <update id="update">
+    <update id="updateModel">
         UPDATE model_version
         <set>
-            <if test="modelSize != null">
-                model_size = #{modelSize},
-            </if>
-            <if test="modelSuperArgs != null">
-                model_super_args = #{modelSuperArgs},
-            </if>
-            <if test="modelArgsSize != null">
-                model_args_size = #{modelArgsSize},
-            </if>
+            <if test="datasetId != null">dataset_id = #{datasetId},</if>
+            <if test="modelConfig != null">model_config = #{modelConfig},</if>
+            <if test="modelPath != null">model_path = #{modelPath},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="modelSize != null">model_size = #{modelSize},</if>
+            <if test="dataPreHandleFile != null">data_pre_handle_file = #{dataPreHandleFile},</if>
+            <if test="modelSuperArgs != null">model_super_args = #{modelSuperArgs},</if>
+            <if test="modelArgsSize != null">model_args_size = #{modelArgsSize},</if>
+            <if test="modelSourceCodeUrl != null">model_source_code_url = #{modelSourceCodeUrl},</if>
+            <if test="modelFile != null">model_file = #{modelFile},</if>
+            <if test="modelDesignDocument != null">model_design_document = #{modelDesignDocument},</if>
+            <if test="lifeCycle != null">life_cycle = #{lifeCycle},</if>
+            <if test="operateUser != null">operate_user = #{operateUser},</if>
         </set>
-        WHERE id = #{id
+        WHERE id = #{id}
     </update>
 </mapper>
\ No newline at end of file