Commit 12b82b72e33e8ba00c7ef75d79c80953dec2ee45
1 parent
2d11f085
【新增】AI:对话管理 100%
Showing
9 changed files
with
151 additions
and
4 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
| ... | ... | @@ -104,4 +104,13 @@ public class AiChatConversationController { |
| 104 | 104 | conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0)))); |
| 105 | 105 | } |
| 106 | 106 | |
| 107 | + @Operation(summary = "管理员删除对话") | |
| 108 | + @DeleteMapping("/delete-by-admin") | |
| 109 | + @Parameter(name = "id", required = true, description = "对话编号", example = "1024") | |
| 110 | + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:delete')") | |
| 111 | + public CommonResult<Boolean> deleteChatConversationByAdmin(@RequestParam("id") Long id) { | |
| 112 | + chatConversationService.deleteChatConversationByAdmin(id); | |
| 113 | + return success(true); | |
| 114 | + } | |
| 115 | + | |
| 107 | 116 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
| ... | ... | @@ -3,8 +3,11 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat; |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | |
| 10 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; | |
| 8 | 11 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*; |
| 9 | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 10 | 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| ... | ... | @@ -21,6 +24,7 @@ import jakarta.annotation.Resource; |
| 21 | 24 | import jakarta.annotation.security.PermitAll; |
| 22 | 25 | import lombok.extern.slf4j.Slf4j; |
| 23 | 26 | import org.springframework.http.MediaType; |
| 27 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 24 | 28 | import org.springframework.validation.annotation.Validated; |
| 25 | 29 | import org.springframework.web.bind.annotation.*; |
| 26 | 30 | import reactor.core.publisher.Flux; |
| ... | ... | @@ -30,6 +34,7 @@ import java.util.List; |
| 30 | 34 | import java.util.Map; |
| 31 | 35 | |
| 32 | 36 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 37 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 33 | 38 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 34 | 39 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 35 | 40 | |
| ... | ... | @@ -103,6 +108,28 @@ public class AiChatMessageController { |
| 103 | 108 | |
| 104 | 109 | // ========== 对话管理 ========== |
| 105 | 110 | |
| 111 | + @GetMapping("/page") | |
| 112 | + @Operation(summary = "获得消息分页", description = "用于【对话管理】菜单") | |
| 113 | + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')") | |
| 114 | + public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) { | |
| 115 | + PageResult<AiChatMessageDO> pageResult = chatMessageService.getChatMessagePage(pageReqVO); | |
| 116 | + if (CollUtil.isEmpty(pageResult.getList())) { | |
| 117 | + return success(PageResult.empty()); | |
| 118 | + } | |
| 119 | + // 拼接数据 | |
| 120 | + Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap( | |
| 121 | + convertSet(pageResult.getList(), AiChatMessageDO::getRoleId)); | |
| 122 | + return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class, | |
| 123 | + respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName())))); | |
| 124 | + } | |
| 106 | 125 | |
| 126 | + @Operation(summary = "管理员删除消息") | |
| 127 | + @DeleteMapping("/delete-by-admin") | |
| 128 | + @Parameter(name = "id", required = true, description = "消息编号", example = "1024") | |
| 129 | + @PreAuthorize("@ss.hasPermission('ai:chat-message:delete')") | |
| 130 | + public CommonResult<Boolean> deleteChatMessageByAdmin(@RequestParam("id") Long id) { | |
| 131 | + chatMessageService.deleteChatMessageByAdmin(id); | |
| 132 | + return success(true); | |
| 133 | + } | |
| 107 | 134 | |
| 108 | 135 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 7 | + | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - AI 聊天消息的分页 Request VO") | |
| 13 | +@Data | |
| 14 | +public class AiChatMessagePageReqVO extends PageParam { | |
| 15 | + | |
| 16 | + @Schema(description = "对话编号", example = "2048") | |
| 17 | + private Long conversationId; | |
| 18 | + | |
| 19 | + @Schema(description = "用户编号", example = "1024") | |
| 20 | + private Long userId; | |
| 21 | + | |
| 22 | + @Schema(description = "消息内容", example = "你好") | |
| 23 | + private String content; | |
| 24 | + | |
| 25 | + @Schema(description = "创建时间") | |
| 26 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 27 | + private LocalDateTime[] createTime; | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
| ... | ... | @@ -15,6 +15,9 @@ public class AiChatMessageRespVO { |
| 15 | 15 | @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") |
| 16 | 16 | private Long conversationId; |
| 17 | 17 | |
| 18 | + @Schema(description = "回复消息编号", example = "1024") | |
| 19 | + private Long replyId; | |
| 20 | + | |
| 18 | 21 | @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role") |
| 19 | 22 | private String type; // 参见 MessageType 枚举类 |
| 20 | 23 | |
| ... | ... | @@ -33,14 +36,21 @@ public class AiChatMessageRespVO { |
| 33 | 36 | @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") |
| 34 | 37 | private String content; |
| 35 | 38 | |
| 39 | + @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 40 | + private Boolean useContext; | |
| 41 | + | |
| 36 | 42 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") |
| 37 | 43 | private LocalDateTime createTime; |
| 38 | 44 | |
| 39 | - // ========= 扩展字段 | |
| 45 | + // ========== 仅在【对话管理】时加载 ========== | |
| 40 | 46 | |
| 41 | 47 | @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://xxx") |
| 42 | 48 | private String userAvatar; |
| 43 | 49 | |
| 44 | - @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://xxx") | |
| 50 | + @Schema(description = "角色名字", example = "小黄") | |
| 51 | + private String roleName; | |
| 52 | + | |
| 53 | + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 45 | 54 | private String roleAvatar; |
| 55 | + | |
| 46 | 56 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java
| ... | ... | @@ -2,9 +2,13 @@ package cn.iocoder.yudao.module.ai.dal.mysql.chat; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 6 | 7 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 7 | 8 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 9 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | |
| 10 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; | |
| 11 | +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | |
| 8 | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 9 | 13 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| 10 | 14 | import org.apache.ibatis.annotations.Mapper; |
| ... | ... | @@ -43,4 +47,13 @@ public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> { |
| 43 | 47 | record -> MapUtil.getInt(record, "count" )); |
| 44 | 48 | } |
| 45 | 49 | |
| 50 | + default PageResult<AiChatMessageDO> selectPage(AiChatMessagePageReqVO pageReqVO) { | |
| 51 | + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatMessageDO>() | |
| 52 | + .eqIfPresent(AiChatMessageDO::getConversationId, pageReqVO.getConversationId()) | |
| 53 | + .eqIfPresent(AiChatMessageDO::getUserId, pageReqVO.getUserId()) | |
| 54 | + .likeIfPresent(AiChatMessageDO::getContent, pageReqVO.getContent()) | |
| 55 | + .betweenIfPresent(AiChatMessageDO::getCreateTime, pageReqVO.getCreateTime()) | |
| 56 | + .orderByDesc(AiChatMessageDO::getId)); | |
| 57 | + } | |
| 58 | + | |
| 46 | 59 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; |
| 5 | 5 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; |
| 6 | 6 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; |
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; | |
| 7 | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 8 | 9 | |
| 9 | 10 | import java.util.List; |
| ... | ... | @@ -57,6 +58,13 @@ public interface AiChatConversationService { |
| 57 | 58 | void deleteChatConversationMy(Long id, Long userId); |
| 58 | 59 | |
| 59 | 60 | /** |
| 61 | + * 【管理员】删除聊天对话 | |
| 62 | + * | |
| 63 | + * @param id 编号 | |
| 64 | + */ | |
| 65 | + void deleteChatConversationByAdmin(Long id); | |
| 66 | + | |
| 67 | + /** | |
| 60 | 68 | * 校验聊天对话是否存在 |
| 61 | 69 | * |
| 62 | 70 | * @param id 编号 |
| ... | ... | @@ -71,6 +79,12 @@ public interface AiChatConversationService { |
| 71 | 79 | */ |
| 72 | 80 | void deleteChatConversationMyByUnpinned(Long userId); |
| 73 | 81 | |
| 82 | + /** | |
| 83 | + * 获得聊天对话的分页列表 | |
| 84 | + * | |
| 85 | + * @param pageReqVO 分页查询 | |
| 86 | + * @return 聊天对话的分页列表 | |
| 87 | + */ | |
| 74 | 88 | PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO); |
| 75 | 89 | |
| 76 | 90 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
| ... | ... | @@ -25,7 +25,6 @@ import java.util.List; |
| 25 | 25 | |
| 26 | 26 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 27 | 27 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 28 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 29 | 28 | import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; |
| 30 | 29 | import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; |
| 31 | 30 | |
| ... | ... | @@ -108,10 +107,20 @@ public class AiChatConversationServiceImpl implements AiChatConversationService |
| 108 | 107 | public void deleteChatConversationMy(Long id, Long userId) { |
| 109 | 108 | // 1. 校验对话是否存在 |
| 110 | 109 | AiChatConversationDO conversation = validateChatConversationExists(id); |
| 111 | - if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | |
| 110 | + if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), userId)) { | |
| 112 | 111 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| 113 | 112 | } |
| 113 | + // 2. 执行删除 | |
| 114 | + chatConversationMapper.deleteById(id); | |
| 115 | + } | |
| 114 | 116 | |
| 117 | + @Override | |
| 118 | + public void deleteChatConversationByAdmin(Long id) { | |
| 119 | + // 1. 校验对话是否存在 | |
| 120 | + AiChatConversationDO conversation = validateChatConversationExists(id); | |
| 121 | + if (conversation == null) { | |
| 122 | + throw exception(CHAT_CONVERSATION_NOT_EXISTS); | |
| 123 | + } | |
| 115 | 124 | // 2. 执行删除 |
| 116 | 125 | chatConversationMapper.deleteById(id); |
| 117 | 126 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.chat; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | |
| 4 | 6 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*; |
| 5 | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 6 | 8 | import reactor.core.publisher.Flux; |
| ... | ... | @@ -58,6 +60,13 @@ public interface AiChatMessageService { |
| 58 | 60 | void deleteChatMessageByConversationId(Long conversationId, Long userId); |
| 59 | 61 | |
| 60 | 62 | /** |
| 63 | + * 【管理员】删除消息 | |
| 64 | + * | |
| 65 | + * @param id 消息编号 | |
| 66 | + */ | |
| 67 | + void deleteChatMessageByAdmin(Long id); | |
| 68 | + | |
| 69 | + /** | |
| 61 | 70 | * 获得聊天对话的消息数量 Map |
| 62 | 71 | * |
| 63 | 72 | * @param conversationIds 对话编号数组 |
| ... | ... | @@ -65,4 +74,12 @@ public interface AiChatMessageService { |
| 65 | 74 | */ |
| 66 | 75 | Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds); |
| 67 | 76 | |
| 77 | + /** | |
| 78 | + * 获得聊天消息的分页 | |
| 79 | + * | |
| 80 | + * @param pageReqVO 分页查询 | |
| 81 | + * @return 聊天消息的分页 | |
| 82 | + */ | |
| 83 | + PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO); | |
| 84 | + | |
| 68 | 85 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| ... | ... | @@ -6,8 +6,11 @@ import cn.hutool.core.util.StrUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 7 | 7 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 9 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 9 | 10 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | 11 | import cn.iocoder.yudao.module.ai.ErrorCodeConstants; |
| 12 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | |
| 13 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; | |
| 11 | 14 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; |
| 12 | 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 13 | 16 | import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; |
| ... | ... | @@ -256,8 +259,24 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 256 | 259 | } |
| 257 | 260 | |
| 258 | 261 | @Override |
| 262 | + public void deleteChatMessageByAdmin(Long id) { | |
| 263 | + // 1. 校验消息存在 | |
| 264 | + AiChatMessageDO message = chatMessageMapper.selectById(id); | |
| 265 | + if (message == null) { | |
| 266 | + throw exception(AI_CHAT_MESSAGE_NOT_EXIST); | |
| 267 | + } | |
| 268 | + // 2. 执行删除 | |
| 269 | + chatMessageMapper.deleteById(id); | |
| 270 | + } | |
| 271 | + | |
| 272 | + @Override | |
| 259 | 273 | public Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds) { |
| 260 | 274 | return chatMessageMapper.selectCountMapByConversationId(conversationIds); |
| 261 | 275 | } |
| 262 | 276 | |
| 277 | + @Override | |
| 278 | + public PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) { | |
| 279 | + return chatMessageMapper.selectPage(pageReqVO); | |
| 280 | + } | |
| 281 | + | |
| 263 | 282 | } | ... | ... |