Commit 2e73959a2c13ec22b983c0e47832fcc6da767233

Authored by YunaiV
1 parent c98495df

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

yudao-module-ai/yudao-module-ai-biz/pom.xml
... ... @@ -32,6 +32,11 @@
32 32 <version>${revision}</version>
33 33 </dependency>
34 34  
  35 + <dependency>
  36 + <groupId>cn.iocoder.boot</groupId>
  37 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  38 + </dependency>
  39 +
35 40 <!-- Web 相关 -->
36 41 <dependency>
37 42 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
... ... @@ -2,6 +2,7 @@
2 2 POST {{baseUrl}}/ai/chat/message/send
3 3 Content-Type: application/json
4 4 Authorization: {{token}}
  5 +tenant-id: {{adminTenentId}}
5 6  
6 7 {
7 8 "conversationId": "1781604279872581724",
... ... @@ -12,9 +13,10 @@ Authorization: {{token}}
12 13 POST {{baseUrl}}/ai/chat/message/send-stream
13 14 Content-Type: application/json
14 15 Authorization: {{token}}
  16 +tenant-id: {{adminTenentId}}
15 17  
16 18 {
17   - "conversationId": "1781604279872581690",
  19 + "conversationId": "1781604279872581724",
18 20 "content": "1+1=?"
19 21 }
20 22  
... ...
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 {
57 57 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
58 58 private LocalDateTime createTime;
59 59  
60   -
61   -
62 60 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
... ... @@ -77,6 +77,11 @@ public class AiImageDO extends BaseDO {
77 77 private Integer status;
78 78  
79 79 /**
  80 + * 完成时间
  81 + */
  82 + private LocalDateTime finishTime;
  83 +
  84 + /**
80 85 * 绘画错误信息
81 86 */
82 87 private String errorMessage;
... ... @@ -112,11 +117,6 @@ public class AiImageDO extends BaseDO {
112 117 */
113 118 private String taskId;
114 119  
115   - /**
116   - * 完成时间
117   - */
118   - private LocalDateTime finishTime;
119   -
120 120 public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> {
121 121  
122 122 @Override
... ...
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;
8 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  11 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
11 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
12 13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
13 14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
... ... @@ -125,11 +126,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
125 126 return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
126 127 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)));
127 128 }).doOnComplete(() -> {
128   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString()));
  129 + // 忽略租户,因为 Flux 异步无法透传租户
  130 + TenantUtils.executeIgnore(() ->
  131 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
129 132 }).doOnError(throwable -> {
130   - // TODO @芋艿:失败的情况下,要不要删除消息
131 133 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
132   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()));
  134 + // 忽略租户,因为 Flux 异步无法透传租户
  135 + TenantUtils.executeIgnore(() ->
  136 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
133 137 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
134 138 }
135 139  
... ...
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;
5 5 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
6 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
8 9 import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
9 10 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
10 11 import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
... ... @@ -75,10 +76,14 @@ public class AiWriteServiceImpl implements AiWriteService {
75 76 // 响应结果
76 77 return success(newContent);
77 78 }).doOnComplete(() -> {
78   - writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString()));
  79 + // 忽略租户,因为 Flux 异步无法透传租户
  80 + TenantUtils.executeIgnore(() ->
  81 + writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString())));
79 82 }).doOnError(throwable -> {
80   - log.error("[AI Write][generateReqVO({}) 发生异常]", generateReqVO, throwable);
81   - writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage()));
  83 + log.error("[generateWriteContent][generateReqVO({}) 发生异常]", generateReqVO, throwable);
  84 + // 忽略租户,因为 Flux 异步无法透传租户
  85 + TenantUtils.executeIgnore(() ->
  86 + writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage())));
82 87 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
83 88 }
84 89  
... ...
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 {
163 163 * 可参考 {@link QianFanAutoConfiguration#qianFanChatModel(QianFanConnectionProperties, QianFanChatProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
164 164 */
165 165 private static QianFanChatModel buildYiYanChatClient(String key) {
166   - // TODO 芋艿:貌似目前设置,request 势必会报错
  166 + // TODO @xin:貌似目前设置,request 势必会报错;看看能不能有办法,参考 buildQianWenChatClient,调用 QianFanAutoConfiguration#qianFanChatModel初始化,当然 key 要用自己的哈
167 167 List<String> keys = StrUtil.split(key, '|');
168 168 Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
169 169 String appKey = keys.get(0);
... ... @@ -191,8 +191,8 @@ public class AiClientFactoryImpl implements AiClientFactory {
191 191 private static TongYiChatModel buildQianWenChatClient(String key) {
192 192 com.alibaba.dashscope.aigc.generation.Generation generation = SpringUtil.getBean(Generation.class);
193 193 TongYiChatProperties chatOptions = SpringUtil.getBean(TongYiChatProperties.class);
194   - // TODO @芋艿:貌似 apiKey 是全局唯一的???得测试下
195   - // TODO @芋艿:貌似阿里云不是增量返回的
  194 + // TODO @xin:貌似 apiKey 是全局唯一的???得测试下
  195 + // TODO @xin:貌似阿里云不是增量返回的
196 196 TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties();
197 197 connectionProperties.setApiKey(key);
198 198 return new TongYiAutoConfiguration().tongYiChatClient(generation, chatOptions, connectionProperties);
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -70,12 +70,6 @@ spring:
70 70 port: 6379 # 端口
71 71 database: 0 # 数据库索引
72 72 # password: dev # 密码,建议生产环境开启
73   -server:
74   - servlet:
75   - encoding:
76   - enabled: true
77   - charset: UTF-8
78   - force: true
79 73  
80 74 --- #################### 定时任务相关配置 ####################
81 75  
... ... @@ -226,8 +220,6 @@ yudao:
226 220 enable: false
227 221 demo: false # 关闭演示模式
228 222 tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
229   - tenant:
230   - enable: false
231 223  
232 224 justauth:
233 225 enabled: true
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -34,6 +34,13 @@ spring:
34 34 redis:
35 35 time-to-live: 1h # 设置过期时间为 1 小时
36 36  
  37 +server:
  38 + servlet:
  39 + encoding:
  40 + enabled: true
  41 + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题
  42 + force: true
  43 +
37 44 --- #################### 接口文档配置 ####################
38 45  
39 46 springdoc:
... ...