Commit 12b82b72e33e8ba00c7ef75d79c80953dec2ee45

Authored by YunaiV
1 parent 2d11f085

【新增】AI:对话管理 100%

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&lt;AiChatMessageDO&gt; {
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 }
... ...