diff --git a/pom.xml b/pom.xml index b0733c5..da8a52b 100644 --- a/pom.xml +++ b/pom.xml @@ -165,6 +165,30 @@ com.fasterxml.jackson.core jackson-databind + + + + com.aliyun.oss + aliyun-sdk-oss + 3.15.1 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + javax.activation + activation + 1.1.1 + + + + org.glassfish.jaxb + jaxb-runtime + 2.3.3 + 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..2c1d3a4 100644 --- a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/constant/MessageConstant.java +++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/constant/MessageConstant.java @@ -10,5 +10,10 @@ public class MessageConstant { public static final String ERROR_DEPLOYED_TO_DESIGNING = "已部署的模型不能直接调整成设计,需先下线再设计"; 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 LIFECYCLE_UPDATE_FAILURE = "更新模型生命周期失败"; + public static final String LIFECYCLE_UPDATE_SUCCESS = "生命周期更新成功"; + + //文件上传常量 + 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/controller/EvaluationController.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/EvaluationController.java index 95bd64a..b50c74f 100644 --- a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/EvaluationController.java +++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/EvaluationController.java @@ -7,12 +7,10 @@ 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.GetMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @Tag(name = "模型评估相关接口") +@CrossOrigin(origins = "http://localhost:3000") @RestController @RequestMapping("/evaluation") @Slf4j diff --git a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/ModelController.java b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/ModelController.java index f78cebd..c51155f 100644 --- a/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/ModelController.java +++ b/src/main/java/com/bipt/intelligentapplicationorchestrationservice/controller/ModelController.java @@ -1,5 +1,6 @@ package com.bipt.intelligentapplicationorchestrationservice.controller; +import com.bipt.intelligentapplicationorchestrationservice.constant.MessageConstant; import com.bipt.intelligentapplicationorchestrationservice.pojo.*; import com.bipt.intelligentapplicationorchestrationservice.service.ModelService; import io.swagger.v3.oas.annotations.Operation; @@ -52,14 +53,16 @@ public class ModelController { } @Operation(summary = "模型更新") - @PutMapping("/updateModel") - public OptResult updateModel(@RequestBody ModelVersionDTO dto){ - log.info("模型更新"); + @PutMapping("/updateModel/{id}") + public OptResult updateModel(@PathVariable("id") Long id, @RequestBody ModelVersionDTO dto) { + log.info("模型更新,id: {}", id); + dto.setId(id); modelService.updateModel(dto); return OptResult.success(); } + @Operation(summary = "模型版本删除") @DeleteMapping("/deleteModelVersion") public OptResult deleteModelVersion(Long id){ @@ -70,10 +73,18 @@ public class ModelController { @Operation(summary = "更新生命周期") @PutMapping("/updateLifeCycle") - public OptResult updateLifeCycle(Long id,String lifeCycle){ + public OptResult updateLifeCycle(@RequestParam Long id, @RequestParam String lifeCycle){ log.info("更新生命周期"); - modelService.updateLifeCycle(id,lifeCycle); - return OptResult.success(); + try { + modelService.updateLifeCycle(id,lifeCycle); + return OptResult.success(MessageConstant.LIFECYCLE_UPDATE_SUCCESS); + } catch (IllegalArgumentException e) { + return OptResult.error(e.getMessage()); + } catch (RuntimeException e) { + return OptResult.error(e.getMessage()); + } catch (Exception e){ + return OptResult.error(MessageConstant.UNKNOWN_ERROR); + } } @Operation(summary = "查询生命周期列表") 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..141e131 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); } @@ -110,14 +111,14 @@ public class ModelServiceImpl implements ModelService { ModelLifecycle currentLifeCycle; ModelLifecycle targetLifeCycle; try { - currentLifeCycle = ModelLifecycle.valueOf(currentLifeCycleStr.trim()); // 数据库中是英文 - targetLifeCycle = ModelLifecycle.fromDescription((lifeCycleDescription).trim()); // 前端传中文 + currentLifeCycle = ModelLifecycle.valueOf(currentLifeCycleStr.trim()); // 数据库中是英文 + targetLifeCycle = ModelLifecycle.valueOf(lifeCycleDescription.trim()); // 前端传英文代码,直接转换 } catch (IllegalArgumentException e) { log.error(MessageConstant.LifeCycle_Undefined + ":{}", e.getMessage()); throw e; } - // 2. 业务逻辑校验 + // 2. 业务逻辑校验(保持不变) switch (currentLifeCycle) { case DEPLOYED: if (targetLifeCycle == ModelLifecycle.DESIGNING) { @@ -142,7 +143,7 @@ public class ModelServiceImpl implements ModelService { int affectedRows = modelMapper.updateLifeCycleById(id, targetLifeCycle.getDbValue()); if (affectedRows == 0) { log.error("更新模型生命周期失败"); - throw new RuntimeException(MessageConstant.UPDATE_FAILURE); + throw new RuntimeException(MessageConstant.LIFECYCLE_UPDATE_FAILURE); } log.info("模型生命周期更新成功,新状态为: {}", targetLifeCycle); 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/EvaluationMapper.xml b/src/main/resources/mapper/EvaluationMapper.xml index e0614d6..9053b83 100644 --- a/src/main/resources/mapper/EvaluationMapper.xml +++ b/src/main/resources/mapper/EvaluationMapper.xml @@ -18,6 +18,6 @@ status=#{status} - where model_id=#{id} + where id=#{id} \ No newline at end of file diff --git a/src/main/resources/mapper/ModelMapper.xml b/src/main/resources/mapper/ModelMapper.xml index 0d8d4bb..10d5c6d 100644 --- a/src/main/resources/mapper/ModelMapper.xml +++ b/src/main/resources/mapper/ModelMapper.xml @@ -44,16 +44,22 @@ UPDATE model_version - - model_size = #{modelSize}, - - - model_super_args = #{modelSuperArgs}, - - - model_args_size = #{modelArgsSize}, - + dataset_id = #{datasetId}, + model_config = #{modelConfig}, + model_path = #{modelPath}, + status = #{status}, + create_time = #{createTime}, + update_time = #{updateTime}, + model_size = #{modelSize}, + data_pre_handle_file = #{dataPreHandleFile}, + model_super_args = #{modelSuperArgs}, + model_args_size = #{modelArgsSize}, + model_source_code_url = #{modelSourceCodeUrl}, + model_file = #{modelFile}, + model_design_document = #{modelDesignDocument}, + life_cycle = #{lifeCycle}, + operate_user = #{operateUser}, - WHERE id = #{id + WHERE id = #{id} \ No newline at end of file