From 69fa98792ce116458095ea79cb134682d33120c2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 14 May 2024 23:46:29 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=EF=BC=9A?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E5=AF=B9=E8=AF=9D=E7=9A=84=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=EF=BC=8880%=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/ErrorCodeConstants.java | 3 +- .../chat/AiChatConversationController.java | 10 +-- .../AiChatConversationRespVO.java | 24 ++++++- .../dal/mysql/AiChatConversationMapper.java | 65 ------------------- .../ai/dal/mysql/AiChatMessageMapper.java | 2 + .../mysql/chat/AiChatConversationMapper.java | 22 +++++++ .../chat/AiChatConversationService.java | 9 +-- .../chat/AiChatConversationServiceImpl.java | 28 ++++---- .../ai/service/impl/AiChatServiceImpl.java | 4 +- 9 files changed, 78 insertions(+), 89 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatConversationMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java index 8948fcba4..a70c58ba2 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java @@ -25,7 +25,8 @@ public interface ErrorCodeConstants { // ========== API 聊天会话 1-040-003-000 ========== - ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "AI 对话不存在!");; + ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "AI 对话不存在!"); + ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整"); // chat ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_022_000_100, "AI 提问的 MessageId 不存在!"); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java index 3e56cb999..dcfe8d386 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -40,11 +42,11 @@ public class AiChatConversationController { return success(true); } - // TODO done @fan:实现一下 @GetMapping("/my-list") - @Operation(summary = "获得聊天会话列表") - public CommonResult> getConversationList() { - return success(chatConversationService.listConversation()); + @Operation(summary = "获得【我的】聊天会话列表") + public CommonResult> getChatConversationMyList() { + List list = chatConversationService.getChatConversationListByUserId(getLoginUserId()); + return success(BeanUtils.toBean(list, AiChatConversationRespVO.class)); } // TODO @fan:实现一下 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java index 70ff21fc5..42c244e19 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java @@ -1,5 +1,11 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; +import com.fhs.core.trans.anno.Trans; +import com.fhs.core.trans.constant.TransType; +import com.fhs.core.trans.vo.VO; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -7,7 +13,7 @@ import lombok.experimental.Accessors; @Schema(description = "管理后台 - AI 聊天会话 Response VO") @Data -public class AiChatConversationRespVO { +public class AiChatConversationRespVO implements VO { @Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; @@ -22,9 +28,12 @@ public class AiChatConversationRespVO { private Boolean pinned; @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") + @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = "avatar", ref = "roleAvatar") private Long roleId; @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"maxTokens", "maxContexts"}, + refs = {"modelMaxTokens", "modelMaxContexts"}) private Long modelId; @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922") @@ -39,4 +48,17 @@ public class AiChatConversationRespVO { @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer maxContexts; + // ========== 关联 role 信息 ========== + + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + private String roleAvatar; + + // ========== 关联 model 信息 ========== + + @Schema(description = "模型的单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") + private Integer modelMaxTokens; + + @Schema(description = "模型的上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer modelMaxContexts; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatConversationMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatConversationMapper.java deleted file mode 100644 index 0333be30d..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatConversationMapper.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Update; -import org.springframework.stereotype.Repository; - -import java.util.List; - -/** - * message mapper - * - * @fansili - * @since v1.0 - */ -@Repository -@Mapper -public interface AiChatConversationMapper extends BaseMapperX { - - /** - * 更新 - chat count - * - * @param id - */ - @Update("update ai_chat_conversation set chat_count = chat_count + 1 where id = #{id}") - void updateIncrChatCount(@Param("id") Long id); - - /** - * 查询 - 最新的对话 - * - * @param loginUserId - */ - default AiChatConversationDO selectLatestConversation(Long loginUserId) { - PageResult pageResult = selectPage(new PageParam().setPageNo(1).setPageSize(1), - new LambdaQueryWrapper() - .eq(AiChatConversationDO::getUserId, loginUserId) - .orderByDesc(AiChatConversationDO::getId)); - if (CollUtil.isEmpty(pageResult.getList())) { - return null; - } - return pageResult.getList().get(0); - } - - /** - * 查询 - 前100 - * - * @param search - */ - default List selectTop100Conversation(Long loginUserId, String search) { - LambdaQueryWrapper queryWrapper - = new LambdaQueryWrapper().eq(AiChatConversationDO::getUserId, loginUserId); - if (!StrUtil.isBlank(search)) { - queryWrapper.like(AiChatConversationDO::getTitle, search); - } - queryWrapper.orderByDesc(AiChatConversationDO::getId); - return selectPage(new PageParam().setPageNo(1).setPageSize(100), queryWrapper).getList(); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatMessageMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatMessageMapper.java index 5f6083fdb..fef19af12 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatMessageMapper.java +++ b/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; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @@ -30,4 +31,5 @@ public interface AiChatMessageMapper extends BaseMapperX { .orderByAsc(AiChatMessageDO::getId) ); } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java new file mode 100644 index 000000000..5d41fb080 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.chat; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * AI 聊天对话 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiChatConversationMapper extends BaseMapperX { + + default List selectListByUserId(Long userId) { + return selectList(AiChatConversationDO::getUserId, userId); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java index ed8fa101c..626f7996b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java +++ b/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 { * * @param createReqVO 创建信息 * @param userId 用户编号 - * @return 聊天会话 + * @return 编号 */ Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId); @@ -32,11 +32,12 @@ public interface AiChatConversationService { void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId); /** - * 获取 - 对话列表 + * 获得【我的】聊天会话列表 * - * @return + * @param userId 用户编号 + * @return 聊天会话列表 */ - List listConversation(); + List getChatConversationListByUserId(Long userId); /** * 获取 - 对话 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java index b04f2b789..22465467e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java +++ b/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; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; @@ -11,7 +12,7 @@ import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper; import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; import jakarta.annotation.Resource; @@ -22,6 +23,7 @@ import org.springframework.validation.annotation.Validated; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*; +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; /** @@ -49,9 +51,10 @@ public class AiChatConversationServiceImpl implements AiChatConversationService : chatRoleService.getRequiredDefaultChatRole(); Assert.notNull(role, "必须找到聊天角色"); // 1.2 获得 AiChatModelDO 聊天模型 - AiChatModelDO model = role.getModelId() != null ? chatModalService.validateChatModel(role.getId()) + AiChatModelDO model = role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId()) : chatModalService.getRequiredDefaultChatModel(); - Assert.notNull(role, "必须找到默认模型"); + Assert.notNull(model, "必须找到默认模型"); + validateChatModel(model); // 2. 创建 AiChatConversationDO 聊天对话 AiChatConversationDO conversation = new AiChatConversationDO() @@ -70,9 +73,10 @@ public class AiChatConversationServiceImpl implements AiChatConversationService throw exception(CHAT_CONVERSATION_NOT_EXISTS); } // 1.2 校验模型是否存在 - AiChatModelDO model = null; + AiChatModelDO model; if (updateReqVO.getModelId() != null) { model = chatModalService.validateChatModel(updateReqVO.getModelId()); + Assert.notNull(model, "必须找到默认模型"); } // 1.3 校验温度参数、Token 数量、消息数量 TODO @@ -81,13 +85,15 @@ public class AiChatConversationServiceImpl implements AiChatConversationService } @Override - public List listConversation() { - // 获取用户id - Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); - // 查询前100对话 - List top100Conversation - = chatConversationMapper.selectTop100Conversation(loginUserId, null); - return AiChatConversationConvert.INSTANCE.covnertChatConversationResList(top100Conversation); + public List getChatConversationListByUserId(Long userId) { + return chatConversationMapper.selectListByUserId(userId); + } + + private void validateChatModel(AiChatModelDO model) { + if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) { + return; + } + throw exception(CHAT_CONVERSATION_MODEL_ERROR); } @Override diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java index d74880dfc..a73531ddd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper; import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; @@ -119,8 +119,6 @@ public class AiChatServiceImpl implements AiChatService { .setMaxContexts(maxContexts); // 增加 chat message 记录 aiChatMessageMapper.insert(insertChatMessageDO); - // chat count 先+1 - aiChatConversationMapper.updateIncrChatCount(conversationId); return insertChatMessageDO; }