瀏覽代碼

【代码新增】AI:开启 AI 租户的能力

YunaiV 9 月之前
父節點
當前提交
2e73959a2c

+ 5 - 0
yudao-module-ai/yudao-module-ai-biz/pom.xml

@@ -32,6 +32,11 @@
             <version>${revision}</version>
         </dependency>
 
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
+        </dependency>
+
         <!-- Web 相关 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>

+ 3 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http

@@ -2,6 +2,7 @@
 POST {{baseUrl}}/ai/chat/message/send
 Content-Type: application/json
 Authorization: {{token}}
+tenant-id: {{adminTenentId}}
 
 {
   "conversationId": "1781604279872581724",
@@ -12,9 +13,10 @@ Authorization: {{token}}
 POST {{baseUrl}}/ai/chat/message/send-stream
 Content-Type: application/json
 Authorization: {{token}}
+tenant-id: {{adminTenentId}}
 
 {
-  "conversationId": "1781604279872581690",
+  "conversationId": "1781604279872581724",
   "content": "1+1=?"
 }
 

+ 0 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java

@@ -57,6 +57,4 @@ public class AiImageRespVO {
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-
-
 }

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

@@ -76,6 +76,11 @@ public class AiImageDO extends BaseDO {
      */
     private Integer status;
 
+    /**
+     * 完成时间
+     */
+    private LocalDateTime finishTime;
+
     /**
      * 绘画错误信息
      */
@@ -112,11 +117,6 @@ public class AiImageDO extends BaseDO {
      */
     private String taskId;
 
-    /**
-     * 完成时间
-     */
-    private LocalDateTime finishTime;
-
     public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> {
 
         @Override

+ 7 - 3
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java

@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
 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.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
@@ -125,11 +126,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
             return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
                     .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)));
         }).doOnComplete(() -> {
-            chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString()));
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
         }).doOnError(throwable -> {
-            // TODO @芋艿:失败的情况下,要不要删除消息
             log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
-            chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()));
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
         }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
     }
 

+ 8 - 3
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
@@ -75,10 +76,14 @@ public class AiWriteServiceImpl implements AiWriteService {
             // 响应结果
             return success(newContent);
         }).doOnComplete(() -> {
-            writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString()));
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString())));
         }).doOnError(throwable -> {
-            log.error("[AI Write][generateReqVO({}) 发生异常]", generateReqVO, throwable);
-            writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage()));
+            log.error("[generateWriteContent][generateReqVO({}) 发生异常]", generateReqVO, throwable);
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage())));
         }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
     }
 

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

@@ -163,7 +163,7 @@ public class AiClientFactoryImpl implements AiClientFactory {
      * 可参考 {@link QianFanAutoConfiguration#qianFanChatModel(QianFanConnectionProperties, QianFanChatProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
      */
     private static QianFanChatModel buildYiYanChatClient(String key) {
-        // TODO 芋艿:貌似目前设置,request 势必会报错
+        // TODO @xin:貌似目前设置,request 势必会报错;看看能不能有办法,参考 buildQianWenChatClient,调用 QianFanAutoConfiguration#qianFanChatModel初始化,当然 key 要用自己的哈
         List<String> keys = StrUtil.split(key, '|');
         Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
         String appKey = keys.get(0);
@@ -191,8 +191,8 @@ public class AiClientFactoryImpl implements AiClientFactory {
     private static TongYiChatModel buildQianWenChatClient(String key) {
         com.alibaba.dashscope.aigc.generation.Generation generation = SpringUtil.getBean(Generation.class);
         TongYiChatProperties chatOptions = SpringUtil.getBean(TongYiChatProperties.class);
-        // TODO @芋艿:貌似 apiKey 是全局唯一的???得测试下
-        // TODO @芋艿:貌似阿里云不是增量返回的
+        // TODO @xin:貌似 apiKey 是全局唯一的???得测试下
+        // TODO @xin:貌似阿里云不是增量返回的
         TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties();
         connectionProperties.setApiKey(key);
         return new TongYiAutoConfiguration().tongYiChatClient(generation, chatOptions, connectionProperties);

+ 0 - 8
yudao-server/src/main/resources/application-local.yaml

@@ -70,12 +70,6 @@ spring:
       port: 6379 # 端口
       database: 0 # 数据库索引
 #    password: dev # 密码,建议生产环境开启
-server:
-  servlet:
-    encoding:
-      enabled: true
-      charset: UTF-8
-      force: true
 
 --- #################### 定时任务相关配置 ####################
 
@@ -226,8 +220,6 @@ 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

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

@@ -34,6 +34,13 @@ spring:
     redis:
       time-to-live: 1h # 设置过期时间为 1 小时
 
+server:
+  servlet:
+    encoding:
+      enabled: true
+      charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题
+      force: true
+
 --- #################### 接口文档配置 ####################
 
 springdoc: