Commit caca47b6d786008a0b94266b17c75eed8e7d1508

Authored by YunaiV
1 parent 04cb9bad

【新增】AI:API 密钥管理

yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
@@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.ai; @@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.ai;
3 import cn.iocoder.yudao.framework.common.exception.ErrorCode; 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 4
5 /** 5 /**
6 - * System 错误码枚举类 6 + * AI 错误码枚举类
7 * 7 *
8 - * system 系统,使用 1-002-000-000 段 8 + * ai 系统,使用 1-040-000-000 段
9 */ 9 */
10 public interface ErrorCodeConstants { 10 public interface ErrorCodeConstants {
11 11
12 - // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ========== 12 + // ========== API 密钥 1-040-000-000 ==========
  13 + ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "AI API 密钥不存在");
13 14
14 // chat 15 // chat
15 16
@@ -37,4 +38,5 @@ public interface ErrorCodeConstants { @@ -37,4 +38,5 @@ public interface ErrorCodeConstants {
37 ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} "); 38 ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} ");
38 ErrorCode AI_MODAL_DISABLE_NOT_USED = new ErrorCode(1_022_000_084, "AI 模型禁用不能使用!"); 39 ErrorCode AI_MODAL_DISABLE_NOT_USED = new ErrorCode(1_022_000_084, "AI 模型禁用不能使用!");
39 40
  41 +
40 } 42 }
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java deleted
1 -/**  
2 - * author: fansili  
3 - * time: 2024/3/3 18:14  
4 - */  
5 -package cn.iocoder.yudao.module.ai;  
6 \ No newline at end of file 0 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
  9 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  10 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +@Tag(name = "管理后台 - AI API 密钥")
  23 +@RestController
  24 +@RequestMapping("/ai/api-key")
  25 +@Validated
  26 +public class AiApiKeyController {
  27 +
  28 + @Resource
  29 + private AiApiKeyService apiKeyService;
  30 +
  31 + @PostMapping("/create")
  32 + @Operation(summary = "创建AI API 密钥")
  33 + @PreAuthorize("@ss.hasPermission('ai:api-key:create')")
  34 + public CommonResult<Long> createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) {
  35 + return success(apiKeyService.createApiKey(createReqVO));
  36 + }
  37 +
  38 + @PutMapping("/update")
  39 + @Operation(summary = "更新AI API 密钥")
  40 + @PreAuthorize("@ss.hasPermission('ai:api-key:update')")
  41 + public CommonResult<Boolean> updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) {
  42 + apiKeyService.updateApiKey(updateReqVO);
  43 + return success(true);
  44 + }
  45 +
  46 + @DeleteMapping("/delete")
  47 + @Operation(summary = "删除AI API 密钥")
  48 + @Parameter(name = "id", description = "编号", required = true)
  49 + @PreAuthorize("@ss.hasPermission('ai:api-key:delete')")
  50 + public CommonResult<Boolean> deleteApiKey(@RequestParam("id") Long id) {
  51 + apiKeyService.deleteApiKey(id);
  52 + return success(true);
  53 + }
  54 +
  55 + @GetMapping("/get")
  56 + @Operation(summary = "获得AI API 密钥")
  57 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  58 + @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
  59 + public CommonResult<AiApiKeyRespVO> getApiKey(@RequestParam("id") Long id) {
  60 + AiApiKeyDO apiKey = apiKeyService.getApiKey(id);
  61 + return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class));
  62 + }
  63 +
  64 + @GetMapping("/page")
  65 + @Operation(summary = "获得AI API 密钥分页")
  66 + @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
  67 + public CommonResult<PageResult<AiApiKeyRespVO>> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) {
  68 + PageResult<AiApiKeyDO> pageResult = apiKeyService.getApiKeyPage(pageReqVO);
  69 + return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class));
  70 + }
  71 +
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - AI API 密钥分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class AiApiKeyPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "名称", example = "文心一言")
  19 + private String name;
  20 +
  21 + @Schema(description = "平台", example = "OpenAI")
  22 + private String platform;
  23 +
  24 + @Schema(description = "状态", example = "1")
  25 + private Integer status;
  26 +
  27 +}
0 \ No newline at end of file 28 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +
  6 +@Schema(description = "管理后台 - AI API 密钥 Response VO")
  7 +@Data
  8 +public class AiApiKeyRespVO {
  9 +
  10 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
  11 + private Long id;
  12 +
  13 + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
  14 + private String name;
  15 +
  16 + @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
  17 + private String apiKey;
  18 +
  19 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  20 + private String platform;
  21 +
  22 + @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
  23 + private String url;
  24 +
  25 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  26 + private Integer status;
  27 +
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import jakarta.validation.constraints.*;
  7 +
  8 +@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO")
  9 +@Data
  10 +public class AiApiKeySaveReqVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
  13 + private Long id;
  14 +
  15 + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
  16 + @NotEmpty(message = "名称不能为空")
  17 + private String name;
  18 +
  19 + @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
  20 + @NotEmpty(message = "密钥不能为空")
  21 + private String apiKey;
  22 +
  23 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  24 + @NotEmpty(message = "平台不能为空")
  25 + private String platform;
  26 +
  27 + @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
  28 + private String url;
  29 +
  30 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + @NotNull(message = "状态不能为空")
  32 + private Integer status;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java
1 package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; 1 package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2
3 -import cn.iocoder.yudao.framework.ai.AiPlatformEnum;  
4 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
5 -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;  
6 -import com.baomidou.mybatisplus.annotation.TableId;  
7 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
8 import lombok.Data; 4 import lombok.Data;
9 import lombok.experimental.Accessors; 5 import lombok.experimental.Accessors;
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
@@ -32,18 +32,16 @@ public class AiApiKeyDO extends BaseDO { @@ -32,18 +32,16 @@ public class AiApiKeyDO extends BaseDO {
32 */ 32 */
33 private String name; 33 private String name;
34 /** 34 /**
  35 + * 密钥
  36 + */
  37 + private String apiKey;
  38 + /**
35 * 平台 39 * 平台
36 * 40 *
37 * 枚举 {@link AiPlatformEnum} 41 * 枚举 {@link AiPlatformEnum}
38 */ 42 */
39 private String platform; 43 private String platform;
40 /** 44 /**
41 - * 用途  
42 - *  
43 - * TODO 芋艿:枚举;chat、image  
44 - */  
45 - private Integer type;  
46 - /**  
47 * API 地址 45 * API 地址
48 */ 46 */
49 private String url; 47 private String url;
@@ -54,6 +52,4 @@ public class AiApiKeyDO extends BaseDO { @@ -54,6 +52,4 @@ public class AiApiKeyDO extends BaseDO {
54 */ 52 */
55 private Integer status; 53 private Integer status;
56 54
57 - // TODO 芋艿:proxyUrl 代理地址  
58 -  
59 } 55 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * AI API 密钥 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface AiApiKeyMapper extends BaseMapperX<AiApiKeyDO> {
  17 +
  18 + default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<AiApiKeyDO>()
  20 + .likeIfPresent(AiApiKeyDO::getName, reqVO.getName())
  21 + .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform())
  22 + .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus())
  23 + .orderByDesc(AiApiKeyDO::getId));
  24 + }
  25 +
  26 +}
0 \ No newline at end of file 27 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
  6 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * AI API 密钥 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface AiApiKeyService {
  15 +
  16 + /**
  17 + * 创建AI API 密钥
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新AI API 密钥
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除AI API 密钥
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteApiKey(Long id);
  37 +
  38 + /**
  39 + * 获得AI API 密钥
  40 + *
  41 + * @param id 编号
  42 + * @return AI API 密钥
  43 + */
  44 + AiApiKeyDO getApiKey(Long id);
  45 +
  46 + /**
  47 + * 获得AI API 密钥分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return AI API 密钥分页
  51 + */
  52 + PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
  53 +
  54 +}
0 \ No newline at end of file 55 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  8 +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.API_KEY_NOT_EXISTS;
  15 +
  16 +/**
  17 + * AI API 密钥 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class AiApiKeyServiceImpl implements AiApiKeyService {
  24 +
  25 + @Resource
  26 + private AiApiKeyMapper apiKeyMapper;
  27 +
  28 + @Override
  29 + public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
  30 + // 插入
  31 + AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);
  32 + apiKeyMapper.insert(apiKey);
  33 + // 返回
  34 + return apiKey.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updateApiKey(AiApiKeySaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validateApiKeyExists(updateReqVO.getId());
  41 + // 更新
  42 + AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class);
  43 + apiKeyMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deleteApiKey(Long id) {
  48 + // 校验存在
  49 + validateApiKeyExists(id);
  50 + // 删除
  51 + apiKeyMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validateApiKeyExists(Long id) {
  55 + if (apiKeyMapper.selectById(id) == null) {
  56 + throw exception(API_KEY_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public AiApiKeyDO getApiKey(Long id) {
  62 + return apiKeyMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
  67 + return apiKeyMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
0 \ No newline at end of file 71 \ No newline at end of file
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; @@ -6,6 +6,7 @@ import lombok.Getter;
6 6
7 import java.util.List; 7 import java.util.List;
8 8
  9 +// TODO 芋艿:这块,看看要不要调整下;
9 /** 10 /**
10 * ai 模型平台 11 * ai 模型平台
11 * 12 *
@@ -16,11 +17,10 @@ import java.util.List; @@ -16,11 +17,10 @@ import java.util.List;
16 @AllArgsConstructor 17 @AllArgsConstructor
17 public enum AiPlatformEnum { 18 public enum AiPlatformEnum {
18 19
19 -  
20 YI_YAN("yiyan", "一言"), 20 YI_YAN("yiyan", "一言"),
21 QIAN_WEN("qianwen", "千问"), 21 QIAN_WEN("qianwen", "千问"),
22 XING_HUO("xinghuo", "星火"), 22 XING_HUO("xinghuo", "星火"),
23 - OPEN_AI("openai", "openAi"), 23 + OPEN_AI("openai", "openAi"), // TODO 芋艿:OpenAI
24 OPEN_AI_DALL("dall", "dall"), 24 OPEN_AI_DALL("dall", "dall"),
25 MIDJOURNEY("midjourney", "midjourney"), 25 MIDJOURNEY("midjourney", "midjourney"),
26 26