Commit 69fa98792ce116458095ea79cb134682d33120c2

Authored by YunaiV
1 parent 1fa799f6

【新增】AI:聊天对话的新建(80%)

yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
... ... @@ -25,7 +25,8 @@ public interface ErrorCodeConstants {
25 25  
26 26 // ========== API 聊天会话 1-040-003-000 ==========
27 27  
28   - ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "AI 对话不存在!");;
  28 + ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "AI 对话不存在!");
  29 + ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
29 30  
30 31 // chat
31 32 ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_022_000_100, "AI 提问的 MessageId 不存在!");
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
5 6 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
6 7 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
7 9 import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
8 10 import io.swagger.v3.oas.annotations.Operation;
9 11 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -40,11 +42,11 @@ public class AiChatConversationController {
40 42 return success(true);
41 43 }
42 44  
43   - // TODO done @fan:实现一下
44 45 @GetMapping("/my-list")
45   - @Operation(summary = "获得聊天会话列表")
46   - public CommonResult<List<AiChatConversationRespVO>> getConversationList() {
47   - return success(chatConversationService.listConversation());
  46 + @Operation(summary = "获得【我的】聊天会话列表")
  47 + public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() {
  48 + List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId());
  49 + return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
48 50 }
49 51  
50 52 // TODO @fan:实现一下
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
2 2  
  3 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  6 +import com.fhs.core.trans.anno.Trans;
  7 +import com.fhs.core.trans.constant.TransType;
  8 +import com.fhs.core.trans.vo.VO;
3 9 import io.swagger.v3.oas.annotations.media.Schema;
4 10 import jakarta.validation.constraints.NotNull;
5 11 import lombok.Data;
... ... @@ -7,7 +13,7 @@ import lombok.experimental.Accessors;
7 13  
8 14 @Schema(description = "管理后台 - AI 聊天会话 Response VO")
9 15 @Data
10   -public class AiChatConversationRespVO {
  16 +public class AiChatConversationRespVO implements VO {
11 17  
12 18 @Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
13 19 private Long id;
... ... @@ -22,9 +28,12 @@ public class AiChatConversationRespVO {
22 28 private Boolean pinned;
23 29  
24 30 @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
  31 + @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = "avatar", ref = "roleAvatar")
25 32 private Long roleId;
26 33  
27 34 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"maxTokens", "maxContexts"},
  36 + refs = {"modelMaxTokens", "modelMaxContexts"})
28 37 private Long modelId;
29 38  
30 39 @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
... ... @@ -39,4 +48,17 @@ public class AiChatConversationRespVO {
39 48 @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
40 49 private Integer maxContexts;
41 50  
  51 + // ========== 关联 role 信息 ==========
  52 +
  53 + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  54 + private String roleAvatar;
  55 +
  56 + // ========== 关联 model 信息 ==========
  57 +
  58 + @Schema(description = "模型的单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
  59 + private Integer modelMaxTokens;
  60 +
  61 + @Schema(description = "模型的上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  62 + private Integer modelMaxContexts;
  63 +
42 64 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatConversationMapper.java deleted
1   -package cn.iocoder.yudao.module.ai.dal.mysql;
2   -
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.util.StrUtil;
5   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
6   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
7   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
8   -import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
9   -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
10   -import org.apache.ibatis.annotations.Mapper;
11   -import org.apache.ibatis.annotations.Param;
12   -import org.apache.ibatis.annotations.Update;
13   -import org.springframework.stereotype.Repository;
14   -
15   -import java.util.List;
16   -
17   -/**
18   - * message mapper
19   - *
20   - * @fansili
21   - * @since v1.0
22   - */
23   -@Repository
24   -@Mapper
25   -public interface AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {
26   -
27   - /**
28   - * 更新 - chat count
29   - *
30   - * @param id
31   - */
32   - @Update("update ai_chat_conversation set chat_count = chat_count + 1 where id = #{id}")
33   - void updateIncrChatCount(@Param("id") Long id);
34   -
35   - /**
36   - * 查询 - 最新的对话
37   - *
38   - * @param loginUserId
39   - */
40   - default AiChatConversationDO selectLatestConversation(Long loginUserId) {
41   - PageResult<AiChatConversationDO> pageResult = selectPage(new PageParam().setPageNo(1).setPageSize(1),
42   - new LambdaQueryWrapper<AiChatConversationDO>()
43   - .eq(AiChatConversationDO::getUserId, loginUserId)
44   - .orderByDesc(AiChatConversationDO::getId));
45   - if (CollUtil.isEmpty(pageResult.getList())) {
46   - return null;
47   - }
48   - return pageResult.getList().get(0);
49   - }
50   -
51   - /**
52   - * 查询 - 前100
53   - *
54   - * @param search
55   - */
56   - default List<AiChatConversationDO> selectTop100Conversation(Long loginUserId, String search) {
57   - LambdaQueryWrapper<AiChatConversationDO> queryWrapper
58   - = new LambdaQueryWrapper<AiChatConversationDO>().eq(AiChatConversationDO::getUserId, loginUserId);
59   - if (!StrUtil.isBlank(search)) {
60   - queryWrapper.like(AiChatConversationDO::getTitle, search);
61   - }
62   - queryWrapper.orderByDesc(AiChatConversationDO::getId);
63   - return selectPage(new PageParam().setPageNo(1).setPageSize(100), queryWrapper).getList();
64   - }
65   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatMessageMapper.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.mysql;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
5 6 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
6 7 import org.apache.ibatis.annotations.Mapper;
7 8 import org.springframework.stereotype.Repository;
... ... @@ -30,4 +31,5 @@ public interface AiChatMessageMapper extends BaseMapperX&lt;AiChatMessageDO&gt; {
30 31 .orderByAsc(AiChatMessageDO::getId)
31 32 );
32 33 }
  34 +
33 35 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.chat;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * AI 聊天对话 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {
  17 +
  18 + default List<AiChatConversationDO> selectListByUserId(Long userId) {
  19 + return selectList(AiChatConversationDO::getUserId, userId);
  20 + }
  21 +
  22 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
... ... @@ -19,7 +19,7 @@ public interface AiChatConversationService {
19 19 *
20 20 * @param createReqVO 创建信息
21 21 * @param userId 用户编号
22   - * @return 聊天会话
  22 + * @return 编号
23 23 */
24 24 Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId);
25 25  
... ... @@ -32,11 +32,12 @@ public interface AiChatConversationService {
32 32 void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId);
33 33  
34 34 /**
35   - * 获取 - 对话列表
  35 + * 获得【我的】聊天会话列表
36 36 *
37   - * @return
  37 + * @param userId 用户编号
  38 + * @return 聊天会话列表
38 39 */
39   - List<AiChatConversationRespVO> listConversation();
  40 + List<AiChatConversationDO> getChatConversationListByUserId(Long userId);
40 41  
41 42 /**
42 43 * 获取 - 对话
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.chat;
2 2  
3 3 import cn.hutool.core.lang.Assert;
4 4 import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.core.util.ObjectUtil;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
7 8 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
... ... @@ -11,7 +12,7 @@ import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert;
11 12 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
12 13 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
13 14 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
14   -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
  15 +import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
15 16 import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
16 17 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
17 18 import jakarta.annotation.Resource;
... ... @@ -22,6 +23,7 @@ import org.springframework.validation.annotation.Validated;
22 23 import java.util.List;
23 24  
24 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*;
  26 +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;
25 27 import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
26 28  
27 29 /**
... ... @@ -49,9 +51,10 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
49 51 : chatRoleService.getRequiredDefaultChatRole();
50 52 Assert.notNull(role, "必须找到聊天角色");
51 53 // 1.2 获得 AiChatModelDO 聊天模型
52   - AiChatModelDO model = role.getModelId() != null ? chatModalService.validateChatModel(role.getId())
  54 + AiChatModelDO model = role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId())
53 55 : chatModalService.getRequiredDefaultChatModel();
54   - Assert.notNull(role, "必须找到默认模型");
  56 + Assert.notNull(model, "必须找到默认模型");
  57 + validateChatModel(model);
55 58  
56 59 // 2. 创建 AiChatConversationDO 聊天对话
57 60 AiChatConversationDO conversation = new AiChatConversationDO()
... ... @@ -70,9 +73,10 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
70 73 throw exception(CHAT_CONVERSATION_NOT_EXISTS);
71 74 }
72 75 // 1.2 校验模型是否存在
73   - AiChatModelDO model = null;
  76 + AiChatModelDO model;
74 77 if (updateReqVO.getModelId() != null) {
75 78 model = chatModalService.validateChatModel(updateReqVO.getModelId());
  79 + Assert.notNull(model, "必须找到默认模型");
76 80 }
77 81 // 1.3 校验温度参数、Token 数量、消息数量 TODO
78 82  
... ... @@ -81,13 +85,15 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
81 85 }
82 86  
83 87 @Override
84   - public List<AiChatConversationRespVO> listConversation() {
85   - // 获取用户id
86   - Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
87   - // 查询前100对话
88   - List<AiChatConversationDO> top100Conversation
89   - = chatConversationMapper.selectTop100Conversation(loginUserId, null);
90   - return AiChatConversationConvert.INSTANCE.covnertChatConversationResList(top100Conversation);
  88 + public List<AiChatConversationDO> getChatConversationListByUserId(Long userId) {
  89 + return chatConversationMapper.selectListByUserId(userId);
  90 + }
  91 +
  92 + private void validateChatModel(AiChatModelDO model) {
  93 + if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
  94 + return;
  95 + }
  96 + throw exception(CHAT_CONVERSATION_MODEL_ERROR);
91 97 }
92 98  
93 99 @Override
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
... ... @@ -20,7 +20,7 @@ import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert;
20 20 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
21 21 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
22 22 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
23   -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
  23 +import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
24 24 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper;
25 25 import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
26 26 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
... ... @@ -119,8 +119,6 @@ public class AiChatServiceImpl implements AiChatService {
119 119 .setMaxContexts(maxContexts);
120 120 // 增加 chat message 记录
121 121 aiChatMessageMapper.insert(insertChatMessageDO);
122   - // chat count 先+1
123   - aiChatConversationMapper.updateIncrChatCount(conversationId);
124 122 return insertChatMessageDO;
125 123 }
126 124  
... ...