Selaa lähdekoodia

【新增】AI:API 密钥管理

YunaiV 1 vuosi sitten
vanhempi
commit
caca47b6d7
12 muutettua tiedostoa jossa 322 lisäystä ja 22 poistoa
  1. 5 3
      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. 27 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
  5. 28 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
  6. 34 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
  7. 0 4
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java
  8. 4 8
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
  9. 26 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java
  10. 54 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
  11. 70 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
  12. 2 2
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java

+ 5 - 3
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;
 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 密钥不存在");
 
     // chat
 
@@ -37,4 +38,5 @@ public interface ErrorCodeConstants {
     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 = "创建AI 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 = "更新AI 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 = "删除AI 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 = "获得AI 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 = "获得AI 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));
+    }
+
+}

+ 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;
+
+}

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

@@ -1,9 +1,5 @@
 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;

+ 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 代理地址
-
 }

+ 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));
+    }
+
+}

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

@@ -0,0 +1,54 @@
+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 {
+
+    /**
+     * 创建AI API 密钥
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);
+
+    /**
+     * 更新AI API 密钥
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);
+
+    /**
+     * 删除AI API 密钥
+     *
+     * @param id 编号
+     */
+    void deleteApiKey(Long id);
+
+    /**
+     * 获得AI API 密钥
+     *
+     * @param id 编号
+     * @return AI API 密钥
+     */
+    AiApiKeyDO getApiKey(Long id);
+
+    /**
+     * 获得AI API 密钥分页
+     *
+     * @param pageReqVO 分页查询
+     * @return AI API 密钥分页
+     */
+    PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
+
+}

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

@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.ai.service.model;
+
+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.API_KEY_NOT_EXISTS;
+
+/**
+ * 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 void validateApiKeyExists(Long id) {
+        if (apiKeyMapper.selectById(id) == null) {
+            throw exception(API_KEY_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public AiApiKeyDO getApiKey(Long id) {
+        return apiKeyMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
+        return apiKeyMapper.selectPage(pageReqVO);
+    }
+
+}

+ 2 - 2
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"),