From 29cdf6c581a6e7d4843635ef343b5d856a37d324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=A0=E5=B1=B1?= <17738440858@163.com> Date: Fri, 27 Feb 2026 11:01:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E6=B7=BB=E5=8A=A0AI=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=AE=A1=E7=90=86=E5=92=8C=E5=88=86=E6=AE=B5=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建AI模型版本管理表tz_ai_model并添加相关索引 - 添加AiModelController、AiModelService等完整的模型管理接口 - 实现模型创建、更新、删除、分页查询等功能 - 添加模型状态更新功能支持启用禁用等状态变更 - 在S3FileClient中实现分段上传uploadMultipart方法 - 扩展FileApi接口增加createFileMultipart分段上传接口 - 修改Nacos配置将命名空间从dev改为具体ID值 - 在SecurityConfiguration中开放AI模型管理接口权限 --- sql/mysql/ai-manage.sql | 23 ++++++ .../cloud/fileapi/api/file/FileApi.java | 6 ++ .../api/file/dto/FileCreateReqDTO.java | 9 +- .../admin/model/AiModelController.java | 82 +++++++++++++++++++ .../admin/model/vo/AiModelPageReqVO.java | 24 ++++++ .../admin/model/vo/AiModelRespVO.java | 40 +++++++++ .../admin/model/vo/AiModelSaveReqVO.java | 35 ++++++++ .../ai/dal/dataobject/model/AiModelDO.java | 57 +++++++++++++ .../ai/dal/mysql/model/AiModelMapper.java | 26 ++++++ .../config/SecurityConfiguration.java | 2 + .../ai/service/model/AiModelService.java | 62 ++++++++++++++ .../ai/service/model/AiModelServiceImpl.java | 61 ++++++++++++++ .../src/main/resources/application-local.yaml | 7 +- .../cloud/file/api/file/FileApiImpl.java | 8 ++ .../file/core/client/s3/S3FileClient.java | 30 ++++++- .../cloud/file/service/file/FileService.java | 3 + .../file/service/file/FileServiceImpl.java | 25 ++++++ .../src/main/resources/application-local.yaml | 6 +- .../src/main/resources/application-local.yaml | 4 +- .../src/main/resources/application-local.yaml | 4 +- 20 files changed, 498 insertions(+), 16 deletions(-) create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/AiModelController.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelPageReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelRespVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelSaveReqVO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/model/AiModelDO.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/model/AiModelMapper.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelService.java create mode 100644 tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelServiceImpl.java diff --git a/sql/mysql/ai-manage.sql b/sql/mysql/ai-manage.sql index 3cc9615..92df0ad 100644 --- a/sql/mysql/ai-manage.sql +++ b/sql/mysql/ai-manage.sql @@ -111,3 +111,26 @@ CREATE TABLE `tz_ai_dialog_message` ) ENGINE = InnoDB COMMENT ='ai-对话消息表'; +DROP TABLE IF EXISTS `tz_ai_model`; +CREATE TABLE `tz_ai_model` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `model_name` varchar(128) NOT NULL COMMENT '模型名称', + `version` varchar(32) NOT NULL COMMENT '版本号', + `load_percentage` decimal(5,4) NULL DEFAULT 0.0000 COMMENT '负载百分比', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态(0-禁用 1-启用 2-测试中 3-已废弃)', + `description` varchar(500) NULL DEFAULT '' COMMENT '版本描述', + `creator` varchar(64) NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_model_name` (`model_name` ASC) USING BTREE, + INDEX `idx_version` (`version` ASC) USING BTREE, + INDEX `idx_status` (`status` ASC) USING BTREE, + INDEX `idx_create_time` (`create_time` ASC) USING BTREE +) ENGINE = InnoDB COMMENT = 'AI模型版本管理表'; + + diff --git a/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/FileApi.java b/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/FileApi.java index 13cdef7..b9241f5 100644 --- a/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/FileApi.java +++ b/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/FileApi.java @@ -59,4 +59,10 @@ public interface FileApi { */ @PostMapping(PREFIX + "/create") CommonResult createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO); + + /** + * 分段上传文件 + */ + @PostMapping(PREFIX + "/create-multipart") + CommonResult createFileMultipart(@Valid @RequestBody FileCreateReqDTO createReqDTO); } diff --git a/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/dto/FileCreateReqDTO.java b/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/dto/FileCreateReqDTO.java index 0146bbb..59e4b0b 100644 --- a/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/dto/FileCreateReqDTO.java +++ b/tashow-feign/tashow-file-api/src/main/java/com/tashow/cloud/fileapi/api/file/dto/FileCreateReqDTO.java @@ -2,9 +2,11 @@ package com.tashow.cloud.fileapi.api.file.dto; import jakarta.validation.constraints.NotEmpty; import lombok.Data; +import lombok.experimental.Accessors; /** RPC 服务 - 文件创建 Request DTO */ @Data +@Accessors(chain = true) public class FileCreateReqDTO { /** 原文件名称 */ @@ -13,9 +15,10 @@ public class FileCreateReqDTO { /** 文件路径 */ private String path; - /** - * 文件内容 - */ + /** 文件访问地址 */ + private String url; + + /** 文件内容 */ @NotEmpty(message = "文件内容不能为空") private byte[] content; } diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/AiModelController.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/AiModelController.java new file mode 100644 index 0000000..550bdc4 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/AiModelController.java @@ -0,0 +1,82 @@ +package com.tashow.cloud.ai.controller.admin.model; + +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelPageReqVO; +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelRespVO; +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.model.AiModelDO; +import com.tashow.cloud.ai.service.model.AiModelService; +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.tashow.cloud.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - AI模型管理") +@RestController +@RequestMapping("/ai/model") +@Validated +public class AiModelController { + + @Resource + private AiModelService modelService; + + @PostMapping("/create") + @Operation(summary = "创建模型") + @PermitAll + public CommonResult createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) { + return success(modelService.createModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新模型") + @PermitAll + public CommonResult updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) { + modelService.updateModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除模型") + @Parameter(name = "id", description = "编号", required = true) + @PermitAll + public CommonResult deleteModel(@RequestParam("id") Long id) { + modelService.deleteModel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得模型") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PermitAll + public CommonResult getModel(@RequestParam("id") Long id) { + AiModelDO model = modelService.getModel(id); + return success(BeanUtils.toBean(model, AiModelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得模型分页") + @PermitAll + public CommonResult> getModelPage(@Valid AiModelPageReqVO pageReqVO) { + PageResult pageResult = modelService.getModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiModelRespVO.class)); + } + + @PutMapping("/update-status") + @Operation(summary = "更新状态") + @Parameter(name = "id", description = "编号", required = true) + @Parameter(name = "status", description = "状态(0-禁用 1-启用 2-测试中 3-已废弃)", required = true) + @PermitAll + public CommonResult updateStatus(@RequestParam("id") Long id, @RequestParam("status") Integer status) { + modelService.updateStatus(id, status); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelPageReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelPageReqVO.java new file mode 100644 index 0000000..f84b569 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelPageReqVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.ai.controller.admin.model.vo; + +import com.tashow.cloud.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - AI模型分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiModelPageReqVO extends PageParam { + + @Schema(description = "模型名称(模糊查询)", example = "宠物声音翻译") + private String modelName; + + @Schema(description = "版本号(精确查询)", example = "v1.0.0") + private String version; + + @Schema(description = "状态(0-禁用 1-启用 2-测试中 3-已废弃)", example = "1") + private Integer status; + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelRespVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelRespVO.java new file mode 100644 index 0000000..9b1a44d --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelRespVO.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.ai.controller.admin.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - AI模型 Response VO") +@Data +public class AiModelRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "模型名称", example = "模型") + private String modelName; + + @Schema(description = "版本号", example = "v1.0.0") + private String version; + + @Schema(description = "负载", example = "0.75") + private BigDecimal loadPercentage; + + @Schema(description = "状态(0-禁用 1-启用 2-测试中 3-已废弃)", example = "1") + private Integer status; + + @Schema(description = "版本描述", example = "描述") + private String description; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime updateTime; + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelSaveReqVO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelSaveReqVO.java new file mode 100644 index 0000000..7b670fd --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/controller/admin/model/vo/AiModelSaveReqVO.java @@ -0,0 +1,35 @@ +package com.tashow.cloud.ai.controller.admin.model.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - AI模型新增/修改 Request VO") +@Data +public class AiModelSaveReqVO { + + @Schema(description = "主键", example = "1") + private Long id; + + @Schema(description = "模型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "宠物声音翻译模型") + @NotBlank(message = "模型名称不能为空") + private String modelName; + + @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "v1.0.0") + @NotBlank(message = "版本号不能为空") + private String version; + + @Schema(description = "负载百分比(0.0-1.0)", example = "0.75") + private BigDecimal loadPercentage; + + @Schema(description = "状态(0-禁用 1-启用 2-测试中 3-已废弃)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "版本描述", example = "优化了翻译准确率") + private String description; + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/model/AiModelDO.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/model/AiModelDO.java new file mode 100644 index 0000000..e96fcaf --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/dataobject/model/AiModelDO.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.ai.dal.dataobject.model; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.math.BigDecimal; + +/** + * AI模型管理 DO + * + * @author tashow + */ +@TableName("tz_ai_model") +@KeySequence("tz_ai_model_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiModelDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + + /** + * 模型名称 + */ + private String modelName; + + /** + * 版本号 + */ + private String version; + + /** + * 负载百分比(0.0-1.0) + */ + private BigDecimal loadPercentage; + + /** + * 状态(0-禁用 1-启用 2-测试中 3-已废弃) + */ + private Integer status; + + /** + * 版本描述 + */ + private String description; + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/model/AiModelMapper.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/model/AiModelMapper.java new file mode 100644 index 0000000..68d06a5 --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/dal/mysql/model/AiModelMapper.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.ai.dal.mysql.model; + +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelPageReqVO; +import com.tashow.cloud.ai.dal.dataobject.model.AiModelDO; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI模型管理 Mapper + * + * @author tashow + */ +@Mapper +public interface AiModelMapper extends BaseMapperX { + + default PageResult selectPage(AiModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiModelDO::getModelName, reqVO.getModelName()) + .eqIfPresent(AiModelDO::getVersion, reqVO.getVersion()) + .eqIfPresent(AiModelDO::getStatus, reqVO.getStatus()) + .orderByDesc(AiModelDO::getCreateTime)); + } + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/framework/security/config/SecurityConfiguration.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/framework/security/config/SecurityConfiguration.java index b2deb33..8bbb02c 100644 --- a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/framework/security/config/SecurityConfiguration.java +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/framework/security/config/SecurityConfiguration.java @@ -32,6 +32,8 @@ public class SecurityConfiguration { .requestMatchers(adminSeverContextPath + "/**").permitAll(); // 文件读取 registry.requestMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll(); + // AI模型管理接口 + registry.requestMatchers("/ai/model/**").permitAll(); // RPC 服务的安全配置 registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); } diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelService.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelService.java new file mode 100644 index 0000000..65ab92e --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelService.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.ai.service.model; + +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelPageReqVO; +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.model.AiModelDO; +import com.tashow.cloud.common.pojo.PageResult; +import jakarta.validation.Valid; + +/** + * AI模型管理 Service 接口 + * + * @author tashow + */ +public interface AiModelService { + + /** + * 创建模型 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createModel(@Valid AiModelSaveReqVO createReqVO); + + /** + * 更新模型 + * + * @param updateReqVO 更新信息 + */ + void updateModel(@Valid AiModelSaveReqVO updateReqVO); + + /** + * 删除模型 + * + * @param id 编号 + */ + void deleteModel(Long id); + + /** + * 获得模型 + * + * @param id 编号 + * @return 模型 + */ + AiModelDO getModel(Long id); + + /** + * 获得模型分页 + * + * @param pageReqVO 分页查询 + * @return 模型分页 + */ + PageResult getModelPage(AiModelPageReqVO pageReqVO); + + /** + * 更新状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateStatus(Long id, Integer status); + +} diff --git a/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelServiceImpl.java b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelServiceImpl.java new file mode 100644 index 0000000..618650f --- /dev/null +++ b/tashow-module/tashow-module-ai/src/main/java/com/tashow/cloud/ai/service/model/AiModelServiceImpl.java @@ -0,0 +1,61 @@ +package com.tashow.cloud.ai.service.model; + +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelPageReqVO; +import com.tashow.cloud.ai.controller.admin.model.vo.AiModelSaveReqVO; +import com.tashow.cloud.ai.dal.dataobject.model.AiModelDO; +import com.tashow.cloud.ai.dal.mysql.model.AiModelMapper; +import com.tashow.cloud.common.pojo.PageResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * AI模型管理 Service 实现类 + * + * @author tashow + */ +@Service +@Validated +public class AiModelServiceImpl implements AiModelService { + + @Resource + private AiModelMapper modelMapper; + + @Override + public Long createModel(AiModelSaveReqVO createReqVO) { + AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class); + modelMapper.insert(model); + return model.getId(); + } + + @Override + public void updateModel(AiModelSaveReqVO updateReqVO) { + AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class); + modelMapper.updateById(updateObj); + } + + @Override + public void deleteModel(Long id) { + modelMapper.deleteById(id); + } + + @Override + public AiModelDO getModel(Long id) { + return modelMapper.selectById(id); + } + + @Override + public PageResult getModelPage(AiModelPageReqVO pageReqVO) { + return modelMapper.selectPage(pageReqVO); + } + + @Override + public void updateStatus(Long id, Integer status) { + AiModelDO updateObj = new AiModelDO(); + updateObj.setId(id); + updateObj.setStatus(status); + modelMapper.updateById(updateObj); + } + +} diff --git a/tashow-module/tashow-module-ai/src/main/resources/application-local.yaml b/tashow-module/tashow-module-ai/src/main/resources/application-local.yaml index fa98814..7f75503 100644 --- a/tashow-module/tashow-module-ai/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-ai/src/main/resources/application-local.yaml @@ -7,12 +7,11 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP - # 添加字符编码配置,解决YAML解析时的字符编码问题 - encode: UTF-8 + diff --git a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/api/file/FileApiImpl.java b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/api/file/FileApiImpl.java index 902f236..4234f7a 100644 --- a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/api/file/FileApiImpl.java +++ b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/api/file/FileApiImpl.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController; import static com.tashow.cloud.common.pojo.CommonResult.success; + @RestController // 提供 RESTful API 接口,给 Feign 调用 @Validated public class FileApiImpl implements FileApi { @@ -24,4 +25,11 @@ public class FileApiImpl implements FileApi { createReqDTO.getContent())); } + @Override + public CommonResult createFileMultipart(FileCreateReqDTO createReqDTO) { + return success(fileService.createFileMultipart(createReqDTO.getName(), createReqDTO.getPath(), + createReqDTO.getContent())); + } + + } diff --git a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/framework/file/core/client/s3/S3FileClient.java b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/framework/file/core/client/s3/S3FileClient.java index 14c1c2e..8a236d1 100644 --- a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/framework/file/core/client/s3/S3FileClient.java +++ b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/framework/file/core/client/s3/S3FileClient.java @@ -11,10 +11,13 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.*; import com.tashow.cloud.file.framework.file.core.client.AbstractFileClient; import java.io.ByteArrayInputStream; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -92,7 +95,32 @@ public class S3FileClient extends AbstractFileClient { objectMetadata); // 拼接返回路径 - return config.getDomain() + "/" + path; + return config.getDomain() + path; + } + + /** 分段上传 */ + public String uploadMultipart(byte[] content, String path, String type) throws Exception { + int partSize = 5 * 1024 * 1024; + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(type); + String uploadId = client.initiateMultipartUpload(new InitiateMultipartUploadRequest(config.getBucket(), path, metadata)).getUploadId(); + try { + List partETags = new ArrayList<>(); + for (int i = 0, partNum = 1; i < content.length; i += partSize, partNum++) { + int size = Math.min(partSize, content.length - i); + byte[] part = new byte[size]; + System.arraycopy(content, i, part, 0, size); + UploadPartResult result = client.uploadPart(new UploadPartRequest() + .withBucketName(config.getBucket()).withKey(path).withUploadId(uploadId) + .withPartNumber(partNum).withInputStream(new ByteArrayInputStream(part)).withPartSize(size)); + partETags.add(result.getPartETag()); + } + client.completeMultipartUpload(new CompleteMultipartUploadRequest(config.getBucket(), path, uploadId, partETags)); + return config.getDomain() + path; + } catch (Exception e) { + client.abortMultipartUpload(new AbortMultipartUploadRequest(config.getBucket(), path, uploadId)); + throw e; + } } @Override diff --git a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileService.java b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileService.java index 4dafa01..006f59f 100644 --- a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileService.java +++ b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileService.java @@ -31,6 +31,9 @@ public interface FileService { */ String createFile(String name, String path, byte[] content); + /** 分段上传文件 */ + String createFileMultipart(String name, String path, byte[] content); + /** * 创建文件 * diff --git a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileServiceImpl.java b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileServiceImpl.java index d7b8131..aab41f6 100644 --- a/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileServiceImpl.java +++ b/tashow-module/tashow-module-file/src/main/java/com/tashow/cloud/file/service/file/FileServiceImpl.java @@ -12,6 +12,7 @@ import com.tashow.cloud.file.dal.dataobject.file.FileDO; import com.tashow.cloud.file.dal.mysql.file.FileMapper; import com.tashow.cloud.file.framework.file.core.client.FileClient; import com.tashow.cloud.file.framework.file.core.client.s3.FilePresignedUrlRespDTO; +import com.tashow.cloud.file.framework.file.core.client.s3.S3FileClient; import com.tashow.cloud.file.framework.file.core.utils.FileTypeUtils; import jakarta.annotation.Resource; import lombok.SneakyThrows; @@ -70,6 +71,30 @@ public class FileServiceImpl implements FileService { return url; } + @Override + @SneakyThrows + public String createFileMultipart(String name, String path, byte[] content) { + String type = FileTypeUtils.getMineType(content, name); + if (StrUtil.isEmpty(path)) { + path = FileUtils.generatePath(content, name); + } + if (StrUtil.isEmpty(name)) { + name = path; + } + FileClient client = fileConfigService.getMasterFileClient(); + Assert.notNull(client, "客户端(master) 不能为空"); + String url = ((S3FileClient) client).uploadMultipart(content, path, type); + FileDO file = new FileDO(); + file.setConfigId(client.getId()); + file.setName(name); + file.setPath(path); + file.setUrl(url); + file.setType(type); + file.setSize(content.length); + fileMapper.insert(file); + return url; + } + @Override public Long createFile(FileCreateReqVO createReqVO) { FileDO file = BeanUtils.toBean(createReqVO, FileDO.class); diff --git a/tashow-module/tashow-module-file/src/main/resources/application-local.yaml b/tashow-module/tashow-module-file/src/main/resources/application-local.yaml index 3aa0efb..7f75503 100644 --- a/tashow-module/tashow-module-file/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-file/src/main/resources/application-local.yaml @@ -7,13 +7,11 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP - - diff --git a/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml b/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml index a781954..7f75503 100644 --- a/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml @@ -7,11 +7,11 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP diff --git a/tashow-module/tashow-module-system/src/main/resources/application-local.yaml b/tashow-module/tashow-module-system/src/main/resources/application-local.yaml index 03bada2..48ae00e 100644 --- a/tashow-module/tashow-module-system/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-system/src/main/resources/application-local.yaml @@ -5,11 +5,11 @@ spring: username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: dev # 命名空间。这里使用 dev 开发环境 + namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP