Parcourir la source

Merge remote-tracking branch 'origin/master-jdk21-ai' into master-jdk21-ai

# Conflicts:
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java
cherishsince il y a 11 mois
Parent
commit
aaf1599f7a
41 fichiers modifiés avec 799 ajouts et 496 suppressions
  1. 11 11
      yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
  2. 0 5
      yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java
  3. 72 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
  4. 51 40
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java
  5. 11 12
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
  6. 27 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
  7. 28 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
  8. 34 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
  9. 22 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java
  10. 45 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java
  11. 50 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java
  12. 0 50
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalRespVO.java
  13. 0 63
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalUpdateReqVO.java
  14. 0 53
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelAddReqVO.java
  15. 0 22
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListReqVO.java
  16. 0 54
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java
  17. 0 57
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelUpdateReqVO.java
  18. 0 8
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java
  19. 0 57
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatModelConvert.java
  20. 2 2
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
  21. 4 8
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
  22. 15 6
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java
  23. 10 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java
  24. 26 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java
  25. 0 1
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java
  26. 1 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java
  27. 0 2
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java
  28. 5 5
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java
  29. 10 15
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
  30. 2 2
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java
  31. 6 7
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java
  32. 62 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
  33. 82 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
  34. 2 6
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatConversationServiceImpl.java
  35. 63 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java
  36. 92 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java
  37. 1 1
      yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http
  38. 3 3
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java
  39. 1 1
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/imagine.json
  40. 54 0
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/requestTestJson/imageGenerationProcess/test2.json
  41. 7 5
      yudao-server/src/main/resources/application-local.yaml

+ 11 - 11
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java

@@ -3,17 +3,23 @@ package cn.iocoder.yudao.module.ai;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 
 /**
- * System 错误码枚举类
+ * AI 错误码枚举类
  *
- * system 系统,使用 1-002-000-000 段
+ * ai 系统,使用 1-040-000-000 段
  */
 public interface ErrorCodeConstants {
 
-    // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ==========
+    // ========== API 密钥 1-040-000-000 ==========
+    ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "AI API 密钥不存在");
+    ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "AI API 密钥已禁用!");
 
-    // chat
+    // ========== API 聊天模型 1-040-001-000 ==========
 
-    ErrorCode AI_MODULE_NOT_SUPPORTED = new ErrorCode(1_022_000_000, "AI 模型暂不支持!");
+    ErrorCode CHAT_MODAL_NOT_EXIST = new ErrorCode(1_040_001_000, "AI 模型不存在!");
+    ErrorCode CHAT_MODAL_DISABLE = new ErrorCode(1_040_001_001, "AI 模型({})已禁用!");
+
+    //    ErrorCode AI_MODAL_CONFIG_PARAMS_INCORRECT = new ErrorCode(1_022_000_081, "AI 模型 config 参数不正确! {} ");
+//    ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} ");
 
     // conversation
 
@@ -30,11 +36,5 @@ public interface ErrorCodeConstants {
     ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "AI 角色不存在!");
     ErrorCode AI_CHAT_ROLE_NOT_PUBLIC = new ErrorCode(1_022_000_060, "AI 角色未公开!");
 
-    // modal
-
-    ErrorCode AI_MODAL_NOT_EXIST = new ErrorCode(1_022_000_080, "AI 模型不存在!");
-    ErrorCode AI_MODAL_CONFIG_PARAMS_INCORRECT = new ErrorCode(1_022_000_081, "AI 模型 config 参数不正确! {} ");
-    ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} ");
-    ErrorCode AI_MODAL_DISABLE_NOT_USED = new ErrorCode(1_022_000_084, "AI 模型禁用不能使用!");
 
 }

+ 0 - 5
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java

@@ -1,5 +0,0 @@
-/**
- * author: fansili
- * time: 2024/3/3 18:14
- */
-package cn.iocoder.yudao.module.ai;

+ 72 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
+import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
+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.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - AI API 密钥")
+@RestController
+@RequestMapping("/ai/api-key")
+@Validated
+public class AiApiKeyController {
+
+    @Resource
+    private AiApiKeyService apiKeyService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建 API 密钥")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:create')")
+    public CommonResult<Long> createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) {
+        return success(apiKeyService.createApiKey(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新 API 密钥")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:update')")
+    public CommonResult<Boolean> updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) {
+        apiKeyService.updateApiKey(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除 API 密钥")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:api-key:delete')")
+    public CommonResult<Boolean> deleteApiKey(@RequestParam("id") Long id) {
+        apiKeyService.deleteApiKey(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得 API 密钥")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
+    public CommonResult<AiApiKeyRespVO> getApiKey(@RequestParam("id") Long id) {
+        AiApiKeyDO apiKey = apiKeyService.getApiKey(id);
+        return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得 API 密钥分页")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
+    public CommonResult<PageResult<AiApiKeyRespVO>> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) {
+        PageResult<AiApiKeyDO> pageResult = apiKeyService.getApiKeyPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class));
+    }
+
+}

+ 51 - 40
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java

@@ -2,60 +2,71 @@ package cn.iocoder.yudao.module.ai.controller.admin.model;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelAddReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelListReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelListRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelUpdateReqVO;
-import cn.iocoder.yudao.module.ai.service.AiChatModelService;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
+import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-// TODO @fan:调整下接口;相关 vo 的命名等等;modal => model
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
-/**
- * ai 模型
- *
- * @author fansili
- * @time 2024/4/24 19:42
- * @since 1.0
- */
-@Tag(name = "A6-AI模型")
+@Tag(name = "管理后台 - AI 聊天模型")
 @RestController
-@RequestMapping("/ai/chat/model")
-@Slf4j
-@AllArgsConstructor
+@RequestMapping("/ai/chat-model")
+@Validated
 public class AiChatModelController {
 
-    private final AiChatModelService aiChatModelService;
+    @Resource
+    private AiChatModelService chatModelService;
 
-    @Operation(summary = "ai模型 - 模型列表")
-    @GetMapping("/list")
-    public PageResult<AiChatModelListRespVO> list(@ModelAttribute AiChatModelListReqVO req) {
-        return aiChatModelService.list(req);
+    @PostMapping("/create")
+    @Operation(summary = "创建聊天模型")
+    @PreAuthorize("@ss.hasPermission('ai:chat-model:create')")
+    public CommonResult<Long> createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) {
+        return success(chatModelService.createChatModel(createReqVO));
     }
 
-    @Operation(summary = "ai模型 - 添加")
-    @PutMapping("/add")
-    public CommonResult<Void> add(@RequestBody @Validated AiChatModelAddReqVO req) {
-        aiChatModelService.add(req);
-        return CommonResult.success(null);
+    @PutMapping("/update")
+    @Operation(summary = "更新聊天模型")
+    @PreAuthorize("@ss.hasPermission('ai:chat-model:update')")
+    public CommonResult<Boolean> updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) {
+        chatModelService.updateChatModel(updateReqVO);
+        return success(true);
     }
 
-    @Operation(summary = "ai模型 - 修改")
-    @PostMapping("/update")
-    public CommonResult<Void> update(@RequestBody @Validated AiChatModelUpdateReqVO req) {
-        aiChatModelService.update(req);
-        return CommonResult.success(null);
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除聊天模型")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:chat-model:delete')")
+    public CommonResult<Boolean> deleteChatModel(@RequestParam("id") Long id) {
+        chatModelService.deleteChatModel(id);
+        return success(true);
     }
 
-    @Operation(summary = "ai模型 - 删除")
-    @DeleteMapping("/delete")
-    public CommonResult<Void> delete(@RequestParam("id") Long id) {
-        aiChatModelService.delete(id);
-        return CommonResult.success(null);
+    @GetMapping("/get")
+    @Operation(summary = "获得聊天模型")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
+    public CommonResult<AiChatModelRespVO> getChatModel(@RequestParam("id") Long id) {
+        AiChatModelDO chatModel = chatModelService.getChatModel(id);
+        return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class));
     }
-}
+
+    @GetMapping("/page")
+    @Operation(summary = "获得聊天模型分页")
+    @PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
+    public CommonResult<PageResult<AiChatModelRespVO>> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) {
+        PageResult<AiChatModelDO> pageResult = chatModelService.getChatModelPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class));
+    }
+
+}

+ 11 - 12
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java

@@ -6,26 +6,20 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.*;
 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-// TODO @fan:调整下接口;相关 vo 的命名等等;modal => model
-/**
- * ai chat 角色
- *
- * @fansili
- * @since v1.0
- */
-@Tag(name = "A4-chat角色")
+@Tag(name = "管理后台 - AI 聊天角色")
 @RestController
-@RequestMapping("/ai/chat/role")
-@Slf4j
-@AllArgsConstructor
+@RequestMapping("/ai/chat-role")
+@Validated
 public class AiChatRoleController {
 
-    private final AiChatRoleService chatRoleService;
+    @Resource
+    private AiChatRoleService chatRoleService;
 
     @Operation(summary = "chat角色 - 角色列表")
     @GetMapping("/list")
@@ -60,4 +54,9 @@ public class AiChatRoleController {
         chatRoleService.delete(id);
         return CommonResult.success(null);
     }
+
+    // ========== 角色管理 ==========
+
+
+
 }

+ 27 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI API 密钥分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AiApiKeyPageReqVO extends PageParam {
+
+    @Schema(description = "名称", example = "文心一言")
+    private String name;
+
+    @Schema(description = "平台", example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "状态", example = "1")
+    private Integer status;
+
+}

+ 28 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+@Schema(description = "管理后台 - AI API 密钥 Response VO")
+@Data
+public class AiApiKeyRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
+    private Long id;
+
+    @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
+    private String name;
+
+    @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
+    private String apiKey;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
+    private String url;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status;
+
+}

+ 34 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import jakarta.validation.constraints.*;
+
+@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO")
+@Data
+public class AiApiKeySaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
+    private Long id;
+
+    @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
+    @NotEmpty(message = "名称不能为空")
+    private String name;
+
+    @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
+    @NotEmpty(message = "密钥不能为空")
+    private String apiKey;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    @NotEmpty(message = "平台不能为空")
+    private String platform;
+
+    @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
+    private String url;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+}

+ 22 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - API 聊天模型分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AiChatModelPageReqVO extends PageParam {
+
+    @Schema(description = "模型名字", example = "张三")
+    private String name;
+
+    @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
+    private String model;
+
+    @Schema(description = "模型平台", example = "OpenAI")
+    private String platform;
+
+}

+ 45 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 聊天模型 Response VO")
+@Data
+public class AiChatModelRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
+    private Long id;
+
+    @Schema(description = "API 秘钥编号", example = "22042")
+    private Long keyId;
+
+    @Schema(description = "模型名字", example = "张三")
+    private String name;
+
+    @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
+    private String model;
+
+    @Schema(description = "模型平台", example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "排序", example = "1")
+    private Integer sort;
+
+    @Schema(description = "状态", example = "2")
+    private Integer status;
+
+    @Schema(description = "温度参数", example = "1")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", example = "8192")
+    private Integer maxContexts;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 50 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import jakarta.validation.constraints.*;
+
+@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO")
+@Data
+public class AiChatModelSaveReqVO {
+
+    @Schema(description = "编号", example = "2630")
+    private Long id;
+
+    @Schema(description = "API 秘钥编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22042")
+    @NotNull(message = "API 秘钥编号不能为空")
+    private Long keyId;
+
+    @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+    @NotEmpty(message = "模型名字不能为空")
+    private String name;
+
+    @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125")
+    @NotEmpty(message = "模型标识不能为空")
+    private String model;
+
+    @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    @NotEmpty(message = "模型平台不能为空")
+    private String platform;
+
+    @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "排序不能为空")
+    private Integer sort;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @InEnum(CommonStatusEnum.class)
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+    @Schema(description = "温度参数", example = "1")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", example = "8192")
+    private Integer maxContexts;
+
+}

+ 0 - 50
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalRespVO.java

@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Size;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * modal list
- *
- * @author fansili
- * @time 2024/4/24 19:56
- * @since 1.0
- */
-@Data
-@Accessors(chain = true)
-public class AiChatModalRespVO {
-
-    @Schema(description = "编号")
-    private Long id;
-
-    @Schema(description = "API 秘钥编号")
-    private Long keyId;
-
-    @Schema(description = "模型名字")
-    private String name;
-
-    @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)")
-    private String model;
-
-    @Size(max = 32, message = "模型平台最大32个字符")
-    private String platform;
-
-    @Schema(description = "排序")
-    private Integer sort;
-
-    @Schema(description = "状态")
-    private Integer status;
-
-    // ========== 会话配置 ==========
-
-    @Schema(description = "温度参数")
-    private Integer temperature;
-
-    @Schema(description = "单条回复的最大 Token 数量")
-    private Integer maxTokens;
-
-    @Schema(description = "上下文的最大 Message 数量")
-    private Integer maxContexts;
-}

+ 0 - 63
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalUpdateReqVO.java

@@ -1,63 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * ai chat modal
- *
- * @author fansili
- * @time 2024/4/24 19:47
- * @since 1.0
- */
-@Data
-@Accessors(chain = true)
-public class AiChatModalUpdateReqVO {
-
-    @Schema(description = "编号")
-    @Size(max = 32, message = "编号最大32个字符")
-    @NotNull(message = "编号不能为空")
-    private Long id;
-
-    @Schema(description = "API 秘钥编号")
-    @Size(max = 32, message = "API 秘钥编号最大32个字符")
-    @NotNull(message = "API 秘钥编号不能为空!")
-    private Long keyId;
-
-    @Schema(description = "模型名字")
-    @Size(max = 60, message = "模型名字最大60个字符")
-    @NotNull(message = "模型名字不能为空!")
-    private String name;
-
-    @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)")
-    @Size(max = 32, message = "模型类型最大32个字符")
-    @NotNull(message = "model模型不能为空!")
-    private String model;
-
-    @Size(max = 32, message = "模型平台最大32个字符")
-    @Schema(description = "模型平台 参考 AiPlatformEnum")
-    @NotNull(message = "平台不能为空!")
-    private String platform;
-
-    @Schema(description = "排序")
-    @NotNull(message = "sort排序不能为空!")
-    private Integer sort;
-
-    @Schema(description = "状态")
-    @NotNull(message = "状态不能为空!")
-    private Integer status;
-
-    // ========== 会话配置 ==========
-
-    @Schema(description = "温度参数")
-    private Integer temperature;
-
-    @Schema(description = "单条回复的最大 Token 数量")
-    private Integer maxTokens;
-
-    @Schema(description = "上下文的最大 Message 数量")
-    private Integer maxContexts;
-}

+ 0 - 53
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelAddReqVO.java

@@ -1,53 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * ai chat modal
- *
- * @author fansili
- * @time 2024/4/24 19:47
- * @since 1.0
- */
-@Data
-@Accessors(chain = true)
-public class AiChatModelAddReqVO {
-
-    @Schema(description = "API 秘钥编号")
-    @NotNull(message = "API 秘钥编号不能为空!")
-    private Long keyId;
-
-    @Schema(description = "模型名字")
-    @Size(max = 60, message = "模型名字最大60个字符")
-    @NotNull(message = "模型名字不能为空!")
-    private String name;
-
-    @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)")
-    @Size(max = 32, message = "模型类型最大32个字符")
-    @NotNull(message = "model模型不能为空!")
-    private String model;
-
-    @Size(max = 32, message = "模型平台最大32个字符")
-    @Schema(description = "模型平台 参考 AiPlatformEnum")
-    @NotNull(message = "平台不能为空!")
-    private String platform;
-
-    @Schema(description = "排序")
-    @NotNull(message = "sort排序不能为空!")
-    private Integer sort;
-
-    // ========== 会话配置 ==========
-
-    @Schema(description = "温度参数")
-    private Integer temperature;
-
-    @Schema(description = "单条回复的最大 Token 数量")
-    private Integer maxTokens;
-
-    @Schema(description = "上下文的最大 Message 数量")
-    private Integer maxContexts;
-}

+ 0 - 22
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListReqVO.java

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * modal list
- *
- * @author fansili
- * @time 2024/4/24 19:56
- * @since 1.0
- */
-@Data
-@Accessors(chain = true)
-public class AiChatModelListReqVO extends PageParam {
-
-    @Schema(description = "名字搜搜")
-    private String search;
-
-}

+ 0 - 54
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java

@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-
-import cn.iocoder.yudao.framework.ai.AiPlatformEnum;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
-import com.baomidou.mybatisplus.annotation.TableId;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * modal list
- *
- * @author fansili
- * @time 2024/4/24 19:56
- * @since 1.0
- */
-@Data
-@Accessors(chain = true)
-public class AiChatModelListRespVO {
-
-    @Schema(description = "编号")
-    private Long id;
-
-    @Schema(description = "API 秘钥编号")
-    private Long keyId;
-
-    @Schema(description = "模型名称")
-    private String name;
-
-    @Schema(description = "模型标志")
-    private String model;
-
-    @Schema(description = "平台")
-    private String platform;
-
-    @Schema(description = "排序值")
-    private Integer sort;
-
-    @Schema(description = "状态")
-    private Integer status;
-
-    // ========== 会话配置 ==========
-
-    @Schema(description = "温度参数")
-    private Double temperature;
-
-    @Schema(description = "单条回复的最大 Token 数量")
-    private Integer maxTokens;
-
-    @Schema(description = "上下文的最大 Message 数量")
-    private Integer maxContexts;
-
-}

+ 0 - 57
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelUpdateReqVO.java

@@ -1,57 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * ai chat modal
- *
- * @author fansili
- * @time 2024/4/24 19:47
- * @since 1.0
- */
-@Data
-@Accessors(chain = true)
-public class AiChatModelUpdateReqVO {
-
-    @Schema(description = "编号")
-    @NotNull(message = "编号不能为空")
-    private Long id;
-
-    @Schema(description = "API 秘钥编号")
-    @NotNull(message = "API 秘钥编号不能为空!")
-    private Long keyId;
-
-    @Schema(description = "模型名字")
-    @Size(max = 60, message = "模型名字最大60个字符")
-    @NotNull(message = "模型名字不能为空!")
-    private String name;
-
-    @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)")
-    @Size(max = 32, message = "模型类型最大32个字符")
-    @NotNull(message = "model模型不能为空!")
-    private String model;
-
-    @Size(max = 32, message = "模型平台最大32个字符")
-    @Schema(description = "模型平台 参考 AiPlatformEnum")
-    @NotNull(message = "平台不能为空!")
-    private String platform;
-
-    @Schema(description = "排序")
-    @NotNull(message = "sort排序不能为空!")
-    private Integer sort;
-
-    // ========== 会话配置 ==========
-
-    @Schema(description = "温度参数")
-    private Integer temperature;
-
-    @Schema(description = "单条回复的最大 Token 数量")
-    private Integer maxTokens;
-
-    @Schema(description = "上下文的最大 Message 数量")
-    private Integer maxContexts;
-}

+ 0 - 8
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java

@@ -19,14 +19,6 @@ public interface AiChatMessageConvert {
 
     AiChatMessageConvert INSTANCE = Mappers.getMapper(AiChatMessageConvert.class);
 
-    /**
-     * 转换 ChatMessageListRes
-     *
-     * @param list
-     * @return
-     */
-    List<AiChatMessageRespVO> convert(List<AiChatMessageDO> list);
-
     /**
      * 转换 AiChatMessageRespVO
      *

+ 0 - 57
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatModelConvert.java

@@ -1,57 +0,0 @@
-package cn.iocoder.yudao.module.ai.convert;
-
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelAddReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelListRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelUpdateReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-import java.util.List;
-
-/**
- * 聊天 modal
- *
- * @author fansili
- * @time 2024/4/18 16:39
- * @since 1.0
- */
-@Mapper
-public interface AiChatModelConvert {
-
-    AiChatModelConvert INSTANCE = Mappers.getMapper(AiChatModelConvert.class);
-
-    /**
-     * 转换 - AiChatModalListRes
-     *
-     * @param list
-     * @return
-     */
-    List<AiChatModelListRespVO> convertAiChatModalListRes(List<AiChatModelDO> list);
-
-    /**
-     * 转换 - AiChatModalDO
-     *
-     * @param req
-     * @return
-     */
-    AiChatModelDO convertAiChatModalDO(AiChatModelAddReqVO req);
-
-    /**
-     * 转换 - AiChatModalDO
-     *
-     * @param req
-     * @return
-     */
-    AiChatModelDO convertAiChatModalDO(AiChatModelUpdateReqVO req);
-
-    /**
-     * 转换 - AiChatModalRes
-     *
-     * @param aiChatModalDO
-     * @return
-     */
-    AiChatModalRespVO convertAiChatModalRes(AiChatModelDO aiChatModalDO);
-
-}

+ 2 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java

@@ -49,8 +49,8 @@ public class AiImageDO extends BaseDO {
 
     // ============ mj 需要字段
 
-    @Schema(description = "用户操作的消息编号(MJ返回)")
-    private String mjMessageId;
+    @Schema(description = "用户操作的Nonce编号(MJ返回)")
+    private String mjNonceId;
 
     @Schema(description = "用户操作的操作编号(MJ返回)")
     private String mjOperationId;

+ 4 - 8
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java

@@ -31,18 +31,16 @@ public class AiApiKeyDO extends BaseDO {
      * 名称
      */
     private String name;
+    /**
+     * 密钥
+     */
+    private String apiKey;
     /**
      * 平台
      *
      * 枚举 {@link AiPlatformEnum}
      */
     private String platform;
-    /**
-     * 用途
-     *
-     * TODO 芋艿:枚举;chat、image
-     */
-    private Integer type;
     /**
      * API 地址
      */
@@ -54,6 +52,4 @@ public class AiApiKeyDO extends BaseDO {
      */
     private Integer status;
 
-    // TODO 芋艿:proxyUrl 代理地址
-
 }

+ 15 - 6
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java

@@ -5,25 +5,23 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
 import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
 
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
 /**
- * chat modal
+ * API 聊天模型 Mapper
  *
  * @author fansili
- * @time 2024/4/24 19:41
- * @since 1.0
  */
-@Repository
 @Mapper
 public interface AiChatModelMapper extends BaseMapperX<AiChatModelDO> {
 
+    // TODO 芋艿:要搞一下
     /**
      * 查询 - 第一个modal
      *
@@ -46,5 +44,16 @@ public interface AiChatModelMapper extends BaseMapperX<AiChatModelDO> {
      * @param modalIds
      * @return
      */
-    List<AiChatModelDO> selectByIds(Collection<Long> modalIds);
+    default List<AiChatModelDO> selectByIds(Collection<Long> modalIds) {
+        return this.selectList(new LambdaQueryWrapperX<AiChatModelDO>().eq(AiChatModelDO::getId, modalIds));
+    }
+
+
+    default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>()
+                .likeIfPresent(AiChatModelDO::getName, reqVO.getName())
+                .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel())
+                .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform())
+                .orderByDesc(AiChatModelDO::getId));
+    }
 }

+ 10 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.ai.dal.mysql;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
 import org.apache.ibatis.annotations.Mapper;
 import org.springframework.stereotype.Repository;
@@ -17,4 +18,13 @@ import org.springframework.stereotype.Repository;
 public interface AiImageMapper extends BaseMapperX<AiImageDO> {
 
 
+    /**
+     * 更新 - 根据 messageId
+     *
+     * @param mjNonceId
+     * @param aiImageDO
+     */
+    default void updateByMjNonce(Long mjNonceId, AiImageDO aiImageDO) {
+        this.update(aiImageDO, new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getMjNonceId, mjNonceId));
+    }
 }

+ 26 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.ai.dal.mysql.model;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * AI API 密钥 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface AiApiKeyMapper extends BaseMapperX<AiApiKeyDO> {
+
+    default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiApiKeyDO>()
+                .likeIfPresent(AiApiKeyDO::getName, reqVO.getName())
+                .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform())
+                .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus())
+                .orderByDesc(AiApiKeyDO::getId));
+    }
+
+}

+ 0 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java

@@ -75,5 +75,4 @@ public interface AiChatModelService {
      * @return
      */
     List<AiChatModelDO> getModalByIds(Set<Long> modalIds);
-
 }

+ 1 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java

@@ -72,4 +72,5 @@ public interface AiChatRoleService {
      * @param aiChatRoleDO
      */
     void validateIsPublic(AiChatRoleDO aiChatRoleDO);
+
 }

+ 0 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java

@@ -17,7 +17,6 @@ import cn.iocoder.yudao.module.ai.service.AiChatModelService;
 import jakarta.validation.ConstraintViolation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.mybatis.spring.annotation.MapperScannerRegistrar;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -36,7 +35,6 @@ import java.util.Set;
 public class AiChatModalServiceImpl implements AiChatModelService {
 
     private final AiChatModelMapper aiChatModelMapper;
-    private final MapperScannerRegistrar mapperScannerRegistrar;
 
     @Override
     public PageResult<AiChatModelListRespVO> list(AiChatModelListReqVO req) {

+ 5 - 5
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java

@@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.ai.convert.AiChatRoleConvert;
 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatRoleMapper;
 import cn.iocoder.yudao.module.ai.enums.AiChatRoleCategoryEnum;
-import cn.iocoder.yudao.module.ai.service.AiChatModelService;
+import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -57,8 +57,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
     public void add(AiChatRoleAddReqVO req) {
         // 转换enum,并校验enum
         AiChatRoleCategoryEnum.valueOfCategory(req.getCategory());
-        // 校验模型是否存在
-        aiChatModalService.validateExists(req.getModelId());
+        // 校验模型是否存在 TODO
+//        aiChatModalService.validateExists(req.getModelId());
         // 转换do
         AiChatRoleDO insertAiChatRoleDO = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req);
         insertAiChatRoleDO.setUserId(SecurityFrameworkUtils.getLoginUserId());
@@ -73,8 +73,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
         validateExists(req.getId());
         // 转换enum,并校验enum
         AiChatRoleCategoryEnum.valueOfCategory(req.getCategory());
-        // 校验模型是否存在
-        aiChatModalService.validateExists(req.getModelId());
+        // 校验模型是否存在 TODO
+//        aiChatModalService.validateExists(req.getModelId());
         // 转换do
         AiChatRoleDO updateChatRole = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req);
         updateChatRole.setId(req.getId());

+ 10 - 15
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java

@@ -12,7 +12,6 @@ import cn.iocoder.yudao.module.ai.config.AiChatClientFactory;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRespVO;
 import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert;
 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
@@ -20,7 +19,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper;
 import cn.iocoder.yudao.module.ai.service.AiChatConversationService;
-import cn.iocoder.yudao.module.ai.service.AiChatModelService;
+import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
 import cn.iocoder.yudao.module.ai.service.AiChatService;
 import lombok.AllArgsConstructor;
@@ -61,9 +60,7 @@ public class AiChatServiceImpl implements AiChatService {
         // 查询对话
         AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
         // 获取对话模型
-        AiChatModalRespVO chatModal = aiChatModalService.getChatModalOfValidate(conversation.getModelId());
-        // 对话模型是否可用
-        aiChatModalService.validateAvailable(chatModal);
+        AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
         // 获取角色信息
         AiChatRoleDO aiChatRoleDO = null;
         if (conversation.getRoleId() != null) {
@@ -72,10 +69,10 @@ public class AiChatServiceImpl implements AiChatService {
         // 校验角色是否公开
         aiChatRoleService.validateIsPublic(aiChatRoleDO);
         // 获取 client 类型
-        AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getPlatform());
+        AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform());
         // 保存 chat message
         insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
-                chatModal.getModel(), chatModal.getId(), req.getContent(),
+                chatModel.getModel(), chatModel.getId(), req.getContent(),
                 null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
         String content = null;
         int tokens = 0;
@@ -97,7 +94,7 @@ public class AiChatServiceImpl implements AiChatService {
         } finally {
             // 保存 chat message
             insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
-                    chatModal.getModel(), chatModal.getId(), content,
+                    chatModel.getModel(), chatModel.getId(), content,
                     tokens, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
         }
         return new AiChatMessageRespVO().setContent(content);
@@ -132,9 +129,7 @@ public class AiChatServiceImpl implements AiChatService {
         // 查询对话
         AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
         // 获取对话模型
-        AiChatModalRespVO chatModal = aiChatModalService.getChatModalOfValidate(conversation.getModelId());
-        // 对话模型是否可用
-        aiChatModalService.validateAvailable(chatModal);
+        AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
         // 获取角色信息
         AiChatRoleDO aiChatRoleDO = null;
         if (conversation.getRoleId() != null) {
@@ -149,10 +144,10 @@ public class AiChatServiceImpl implements AiChatService {
 //        req.setTemperature(req.getTemperature());
         // 保存 chat message
         insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
-                chatModal.getModel(), chatModal.getId(), req.getContent(),
+                chatModel.getModel(), chatModel.getId(), req.getContent(),
                 null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
         // 获取 client 类型
-        AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getPlatform());
+        AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform());
         StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum);
         Flux<ChatResponse> streamResponse = streamingChatClient.stream(prompt);
         // 转换 flex AiChatMessageRespVO
@@ -171,7 +166,7 @@ public class AiChatServiceImpl implements AiChatService {
                 log.info("发送完成!");
                 // 保存 chat message
                 insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
-                        chatModal.getModel(), chatModal.getId(), contentBuffer.toString(),
+                        chatModel.getModel(), chatModel.getId(), contentBuffer.toString(),
                         tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
             }
         }).doOnError(new Consumer<Throwable>() {
@@ -180,7 +175,7 @@ public class AiChatServiceImpl implements AiChatService {
                 log.error("发送错误 {}!", throwable.getMessage());
                 // 保存 chat message
                 insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
-                        chatModal.getModel(), chatModal.getId(), throwable.getMessage(),
+                        chatModel.getModel(), chatModel.getId(), throwable.getMessage(),
                         tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
             }
         });

+ 2 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java

@@ -145,7 +145,7 @@ public class AiImageServiceImpl implements AiImageService {
         // 校验 OperateId 是否存在
         AiImageMidjourneyOperationsVO midjourneyOperationsVO = validateMidjourneyOperationsExists(midjourneyOperations, req.getOperateId());
         // 校验 messageId
-        validateMessageId(aiImageDO.getMjMessageId(), req.getMessageId());
+        validateMessageId(aiImageDO.getMjNonceId(), req.getMessageId());
         // 获取 mjOperationName
         String mjOperationName = midjourneyOperationsVO.getLabel();
         // 保存一个 image 任务记录
@@ -222,7 +222,7 @@ public class AiImageServiceImpl implements AiImageService {
         aiImageDO.setDrawingImageUrl(drawingImageUrl);
         aiImageDO.setDrawingErrorMessage(drawingErrorMessage);
         //
-        aiImageDO.setMjMessageId(mjMessageId);
+        aiImageDO.setMjNonceId(mjMessageId);
         aiImageDO.setMjOperationId(mjOperationId);
         aiImageDO.setMjOperationName(mjOperationName);
         aiImageMapper.insert(aiImageDO);

+ 6 - 7
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java

@@ -62,12 +62,11 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler {
 
     private void errorHandler(MidjourneyMessage midjourneyMessage) {
         // image 编号
-        Long aiImageId = Long.valueOf(midjourneyMessage.getNonce());
+        Long nonceId = Long.valueOf(midjourneyMessage.getNonce());
         // 获取 error message
         String errorMessage = getErrorMessage(midjourneyMessage);
-        aiImageMapper.updateById(
+        aiImageMapper.updateByMjNonce(nonceId,
                 new AiImageDO()
-                        .setId(aiImageId)
                         .setDrawingErrorMessage(errorMessage)
                         .setDrawingStatus(AiImageDrawingStatusEnum.FAIL.getStatus())
         );
@@ -83,7 +82,8 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler {
 
     private void successHandler(MidjourneyMessage midjourneyMessage) {
         // 获取id
-        Long aiImageId = Long.valueOf(midjourneyMessage.getNonce());
+        Long nonceId = Long.valueOf(midjourneyMessage.getNonce());
+        // TODO @芋艿 这个地方有问题,不能根据 nonce来更新,不返回这个信息(别人获取了 image-xxx-xx 后面一段hash,由于没有mj账号测试,暂不清楚。)
         // 获取生成 url
         String imageUrl = null;
         if (CollUtil.isNotEmpty(midjourneyMessage.getAttachments())) {
@@ -102,12 +102,11 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler {
         // 获取 midjourneyOperations
         List<AiImageMidjourneyOperationsVO> midjourneyOperations = getMidjourneyOperationsList(midjourneyMessage);
         // 更新数据库
-        aiImageMapper.updateById(
+        aiImageMapper.updateByMjNonce(nonceId,
                 new AiImageDO()
-                        .setId(aiImageId)
                         .setDrawingImageUrl(imageUrl)
                         .setDrawingStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus())
-                        .setMjMessageId(midjourneyMessage.getId())
+                        .setMjNonceId(midjourneyMessage.getId())
                         .setMjOperations(JsonUtils.toJsonString(midjourneyOperations))
         );
     }

+ 62 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.ai.service.model;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
+import jakarta.validation.Valid;
+
+/**
+ * AI API 密钥 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface AiApiKeyService {
+
+    /**
+     * 创建 API 密钥
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);
+
+    /**
+     * 更新 API 密钥
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);
+
+    /**
+     * 删除 API 密钥
+     *
+     * @param id 编号
+     */
+    void deleteApiKey(Long id);
+
+    /**
+     * 获得 API 密钥
+     *
+     * @param id 编号
+     * @return API 密钥
+     */
+    AiApiKeyDO getApiKey(Long id);
+
+    /**
+     * 校验 API 密钥
+     *
+     * @param id 比那好
+     * @return API 密钥
+     */
+    AiApiKeyDO validateApiKey(Long id);
+
+    /**
+     * 获得 API 密钥分页
+     *
+     * @param pageReqVO 分页查询
+     * @return API 密钥分页
+     */
+    PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
+
+}

+ 82 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.ai.service.model;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
+import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*;
+
+/**
+ * AI API 密钥 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class AiApiKeyServiceImpl implements AiApiKeyService {
+
+    @Resource
+    private AiApiKeyMapper apiKeyMapper;
+
+    @Override
+    public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
+        // 插入
+        AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);
+        apiKeyMapper.insert(apiKey);
+        // 返回
+        return apiKey.getId();
+    }
+
+    @Override
+    public void updateApiKey(AiApiKeySaveReqVO updateReqVO) {
+        // 校验存在
+        validateApiKeyExists(updateReqVO.getId());
+        // 更新
+        AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class);
+        apiKeyMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteApiKey(Long id) {
+        // 校验存在
+        validateApiKeyExists(id);
+        // 删除
+        apiKeyMapper.deleteById(id);
+    }
+
+    private AiApiKeyDO validateApiKeyExists(Long id) {
+        AiApiKeyDO apiKey = apiKeyMapper.selectById(id);
+        if (apiKey == null) {
+            throw exception(API_KEY_NOT_EXISTS);
+        }
+        return apiKey;
+    }
+
+    @Override
+    public AiApiKeyDO getApiKey(Long id) {
+        return apiKeyMapper.selectById(id);
+    }
+
+    @Override
+    public AiApiKeyDO validateApiKey(Long id) {
+        AiApiKeyDO apiKey = validateApiKeyExists(id);
+        if (CommonStatusEnum.isDisable(apiKey.getStatus())) {
+            throw exception(API_KEY_DISABLE);
+        }
+        return apiKey;
+    }
+
+    @Override
+    public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
+        return apiKeyMapper.selectPage(pageReqVO);
+    }
+
+}

+ 2 - 6
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatConversationServiceImpl.java → yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatConversationServiceImpl.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.ai.service.impl;
+package cn.iocoder.yudao.module.ai.service.model;
 
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleRespVO;
 import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert;
 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
@@ -15,7 +14,6 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper;
 import cn.iocoder.yudao.module.ai.service.AiChatConversationService;
-import cn.iocoder.yudao.module.ai.service.AiChatModelService;
 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -87,9 +85,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
         // 校验对话是否存在
         validateExists(updateReqVO.getId());
         // 获取模型信息并验证
-        AiChatModalRespVO chatModal = aiChatModalService.getChatModalOfValidate(updateReqVO.getModelId());
-        // 校验modal是否可用
-        aiChatModalService.validateAvailable(chatModal);
+        aiChatModalService.validateChatModel(updateReqVO.getModelId());
         // 更新对话信息
         AiChatConversationDO updateAiChatConversationDO
                 = AiChatConversationConvert.INSTANCE.convertAiChatConversationDO(updateReqVO);

+ 63 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java

@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.ai.service.model;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
+import jakarta.validation.Valid;
+
+/**
+ * AI 聊天模型 Service 接口
+ *
+ * @author fansili
+ * @since 2024/4/24 19:42
+ */
+public interface AiChatModelService {
+
+    /**
+     * 创建聊天模型
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO);
+
+    /**
+     * 更新聊天模型
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO);
+
+    /**
+     * 删除聊天模型
+     *
+     * @param id 编号
+     */
+    void deleteChatModel(Long id);
+
+    /**
+     * 获得聊天模型
+     *
+     * @param id 编号
+     * @return API 聊天模型
+     */
+    AiChatModelDO getChatModel(Long id);
+
+    /**
+     * 获得聊天模型分页
+     *
+     * @param pageReqVO 分页查询
+     * @return API 聊天模型分页
+     */
+    PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO);
+
+    /**
+     * 校验聊天模型
+     *
+     * @param id 编号
+     * @return 聊天模型
+     */
+    AiChatModelDO validateChatModel(Long id);
+
+}

+ 92 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java

@@ -0,0 +1,92 @@
+package cn.iocoder.yudao.module.ai.service.model;
+
+import cn.iocoder.yudao.framework.ai.AiPlatformEnum;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
+import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*;
+
+/**
+ * AI 聊天模型 Service 实现类
+ *
+ * @author fansili
+ */
+@Service
+@Validated
+public class AiChatModelServiceImpl implements AiChatModelService {
+
+    @Resource
+    private AiApiKeyService apiKeyService;
+
+    @Resource
+    private AiChatModelMapper chatModelMapper;
+
+    @Override
+    public Long createChatModel(AiChatModelSaveReqVO createReqVO) {
+        // 1. 校验
+        AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
+        apiKeyService.validateApiKey(createReqVO.getKeyId());
+
+        // 2. 插入
+        AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class);
+        chatModelMapper.insert(chatModel);
+        return chatModel.getId();
+    }
+
+    @Override
+    public void updateChatModel(AiChatModelSaveReqVO updateReqVO) {
+        // 1. 校验
+        validateChatModelExists(updateReqVO.getId());
+        AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
+        apiKeyService.validateApiKey(updateReqVO.getKeyId());
+
+        // 2. 更新
+        AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class);
+        chatModelMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteChatModel(Long id) {
+        // 校验存在
+        validateChatModelExists(id);
+        // 删除
+        chatModelMapper.deleteById(id);
+    }
+
+    private AiChatModelDO validateChatModelExists(Long id) {
+        AiChatModelDO model = chatModelMapper.selectById(id);
+        if (chatModelMapper.selectById(id) == null) {
+            throw exception(CHAT_MODAL_NOT_EXIST);
+        }
+        return model;
+    }
+
+    @Override
+    public AiChatModelDO getChatModel(Long id) {
+        return chatModelMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO) {
+        return chatModelMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public AiChatModelDO validateChatModel(Long id) {
+        AiChatModelDO model = validateChatModelExists(id);
+        if (CommonStatusEnum.isDisable(model.getStatus())) {
+            throw exception(CHAT_MODAL_DISABLE);
+        }
+        return model;
+    }
+
+}

+ 1 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http

@@ -20,5 +20,5 @@ Content-Type: application/json
 Authorization: {{token}}
 
 {
-  "prompt": "Cute cartoon style mobile game scene, a colorful camping car with an outdoor table and chairs next to it on the road in a spring forest, the simple structure of the camper van, soft lighting, C4D rendering, 3d model in the style of a cartoon, cute shape, a pastel color scheme, closeup view from the side angle, high resolution, bright colors, a happy atmosphere. --ar 1:2 --v 6.0"
+  "prompt": "Cute cartoon style mobile game scene, a colorful camping car with an outdoor table and chairs next to it on the road in a spring forest, the simple structure of the camper van, soft lighting, C4D rendering, 3d model in the style of a cartoon, cute shape, a pastel color scheme, closeup view from the side angle, high resolution, bright colors, a happy atmosphere."
 }

+ 3 - 3
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java

@@ -6,6 +6,7 @@ import lombok.Getter;
 
 import java.util.List;
 
+// TODO 芋艿:这块,看看要不要调整下;
 /**
  * ai 模型平台
  *
@@ -16,11 +17,10 @@ import java.util.List;
 @AllArgsConstructor
 public enum AiPlatformEnum {
 
-
     YI_YAN("yiyan", "一言"),
     QIAN_WEN("qianwen", "千问"),
     XING_HUO("xinghuo", "星火"),
-    OPEN_AI("openai", "openAi"),
+    OPEN_AI("openai", "openAi"), // TODO 芋艿:OpenAI
     OPEN_AI_DALL("dall", "dall"),
     MIDJOURNEY("midjourney", "midjourney"),
 
@@ -41,7 +41,7 @@ public enum AiPlatformEnum {
             AiPlatformEnum.MIDJOURNEY
     );
 
-    public static AiPlatformEnum valueOfPlatform(String platform) {
+    public static AiPlatformEnum validatePlatform(String platform) {
         for (AiPlatformEnum itemEnum : AiPlatformEnum.values()) {
             if (itemEnum.getPlatform().equals(platform)) {
                 return itemEnum;

+ 1 - 1
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/imagine.json

@@ -6,7 +6,7 @@
   "session_id": "$session_id",
   "nonce": "$nonce",
   "data": {
-    "version": "1166847114203123795",
+    "version": "1237876415471554623",
     "id": "938956540159881230",
     "name": "imagine",
     "type": 1,

+ 54 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/requestTestJson/imageGenerationProcess/test2.json

@@ -0,0 +1,54 @@
+{
+  "type": 2,
+  "application_id": "936929561302675456",
+  "guild_id": "1237948819677904956",
+  "channel_id": "1237948819677904960",
+  "session_id": "4bdb0c32158f625bbd7f0a54bfbb54aa",
+  "data": {
+    "version": "1237876415471554623",
+    "id": "938956540159881230",
+    "name": "imagine",
+    "type": 1,
+    "options": [
+      {
+        "type": 3,
+        "name": "prompt",
+        "value": "哈哈哈"
+      }
+    ],
+    "application_command": {
+      "id": "938956540159881230",
+      "type": 1,
+      "application_id": "936929561302675456",
+      "version": "1237876415471554623",
+      "name": "imagine",
+      "description": "Create images with Midjourney",
+      "options": [
+        {
+          "type": 3,
+          "name": "prompt",
+          "description": "The prompt to imagine",
+          "required": true,
+          "description_localized": "The prompt to imagine",
+          "name_localized": "prompt"
+        }
+      ],
+      "dm_permission": true,
+      "contexts": [
+        0,
+        1,
+        2
+      ],
+      "integration_types": [
+        0,
+        1
+      ],
+      "global_popularity_rank": 1,
+      "description_localized": "Create images with Midjourney",
+      "name_localized": "imagine"
+    },
+    "attachments": []
+  },
+  "nonce": "1238062212925358080",
+  "analytics_location": "slash_ui"
+}

+ 7 - 5
yudao-server/src/main/resources/application-local.yaml

@@ -52,7 +52,7 @@ spring:
           #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例
           #          url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
           username: root
-          password: 123456
+          password: root
         #          username: sa # SQL Server 连接的示例
         #          password: Yudao@2024 # SQL Server 连接的示例
         #          username: SYSDBA # DM 连接的示例
@@ -61,7 +61,7 @@ spring:
           lazy: true # 开启懒加载,保证启动速度
           url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: root
-          password: 123456
+          password: root
 
   # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
   data:
@@ -248,9 +248,9 @@ yudao:
       style: vivid
     midjourney:
       enable: true
-      token: OTc1MzcyNDg1OTcxMzEyNzAw.G2iiSo.OqW9vToC5dokiyb1QOWnCwRPsYpOjLyNcf9--M
-      guild-id: 1234355413420347402
-      channel-id: 1234380679576424448
+      token: MTE4MjE3MjY2MjkxNTY3ODIzOA.GEV1SG.c49F8lZoGCUHwsj8O0UdodmM6nyQHvuD2fXflw
+      guild-id: 1237948819677904956
+      channel-id: 1237948819677904960
   captcha:
     enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试;
   security:
@@ -267,6 +267,8 @@ yudao:
     enable: false
   demo: false # 关闭演示模式
   tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
+  tenant:
+    enable: false
 
 justauth:
   enabled: true