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 ad57460ca..ed22acf69 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 @@ -104,4 +104,13 @@ public class AiChatConversationController { conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0)))); } + @Operation(summary = "管理员删除对话") + @DeleteMapping("/delete-by-admin") + @Parameter(name = "id", required = true, description = "对话编号", example = "1024") + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:delete')") + public CommonResult deleteChatConversationByAdmin(@RequestParam("id") Long id) { + chatConversationService.deleteChatConversationByAdmin(id); + return success(true); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java index 68559c776..dccc3bfa3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; @@ -21,6 +24,7 @@ import jakarta.annotation.Resource; import jakarta.annotation.security.PermitAll; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; @@ -30,6 +34,7 @@ import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @@ -103,6 +108,28 @@ public class AiChatMessageController { // ========== 对话管理 ========== + @GetMapping("/page") + @Operation(summary = "获得消息分页", description = "用于【对话管理】菜单") + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')") + public CommonResult> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) { + PageResult pageResult = chatMessageService.getChatMessagePage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + // 拼接数据 + Map roleMap = chatRoleService.getChatRoleMap( + convertSet(pageResult.getList(), AiChatMessageDO::getRoleId)); + return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class, + respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName())))); + } + @Operation(summary = "管理员删除消息") + @DeleteMapping("/delete-by-admin") + @Parameter(name = "id", required = true, description = "消息编号", example = "1024") + @PreAuthorize("@ss.hasPermission('ai:chat-message:delete')") + public CommonResult deleteChatMessageByAdmin(@RequestParam("id") Long id) { + chatMessageService.deleteChatMessageByAdmin(id); + return success(true); + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java new file mode 100644 index 000000000..7ccb6aa0b --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - AI 聊天消息的分页 Request VO") +@Data +public class AiChatMessagePageReqVO extends PageParam { + + @Schema(description = "对话编号", example = "2048") + private Long conversationId; + + @Schema(description = "用户编号", example = "1024") + private Long userId; + + @Schema(description = "消息内容", example = "你好") + private String content; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java index 2cb0f9c5c..918609a53 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java +++ b/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 { @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") private Long conversationId; + @Schema(description = "回复消息编号", example = "1024") + private Long replyId; + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role") private String type; // 参见 MessageType 枚举类 @@ -33,14 +36,21 @@ public class AiChatMessageRespVO { @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") private String content; + @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean useContext; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") private LocalDateTime createTime; - // ========= 扩展字段 + // ========== 仅在【对话管理】时加载 ========== @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://xxx") private String userAvatar; - @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://xxx") + @Schema(description = "角色名字", example = "小黄") + private String roleName; + + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") private String roleAvatar; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java index 76db130be..5020f3944 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.ibatis.annotations.Mapper; @@ -43,4 +47,13 @@ public interface AiChatMessageMapper extends BaseMapperX { record -> MapUtil.getInt(record, "count" )); } + default PageResult selectPage(AiChatMessagePageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eqIfPresent(AiChatMessageDO::getConversationId, pageReqVO.getConversationId()) + .eqIfPresent(AiChatMessageDO::getUserId, pageReqVO.getUserId()) + .likeIfPresent(AiChatMessageDO::getContent, pageReqVO.getContent()) + .betweenIfPresent(AiChatMessageDO::getCreateTime, pageReqVO.getCreateTime()) + .orderByDesc(AiChatMessageDO::getId)); + } + } 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 42678a2f9..bce6d435d 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 @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import java.util.List; @@ -56,6 +57,13 @@ public interface AiChatConversationService { */ void deleteChatConversationMy(Long id, Long userId); + /** + * 【管理员】删除聊天对话 + * + * @param id 编号 + */ + void deleteChatConversationByAdmin(Long id); + /** * 校验聊天对话是否存在 * @@ -71,6 +79,12 @@ public interface AiChatConversationService { */ void deleteChatConversationMyByUnpinned(Long userId); + /** + * 获得聊天对话的分页列表 + * + * @param pageReqVO 分页查询 + * @return 聊天对话的分页列表 + */ PageResult getChatConversationPage(AiChatConversationPageReqVO pageReqVO); } 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 41417fbcd..85349b760 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 @@ -25,7 +25,6 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; @@ -108,10 +107,20 @@ public class AiChatConversationServiceImpl implements AiChatConversationService public void deleteChatConversationMy(Long id, Long userId) { // 1. 校验对话是否存在 AiChatConversationDO conversation = validateChatConversationExists(id); - if (ObjUtil.notEqual(conversation.getUserId(), userId)) { + if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), userId)) { throw exception(CHAT_CONVERSATION_NOT_EXISTS); } + // 2. 执行删除 + chatConversationMapper.deleteById(id); + } + @Override + public void deleteChatConversationByAdmin(Long id) { + // 1. 校验对话是否存在 + AiChatConversationDO conversation = validateChatConversationExists(id); + if (conversation == null) { + throw exception(CHAT_CONVERSATION_NOT_EXISTS); + } // 2. 执行删除 chatConversationMapper.deleteById(id); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java index 329e2cb1b..a529a9716 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.ai.service.chat; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import reactor.core.publisher.Flux; @@ -57,6 +59,13 @@ public interface AiChatMessageService { */ void deleteChatMessageByConversationId(Long conversationId, Long userId); + /** + * 【管理员】删除消息 + * + * @param id 消息编号 + */ + void deleteChatMessageByAdmin(Long id); + /** * 获得聊天对话的消息数量 Map * @@ -65,4 +74,12 @@ public interface AiChatMessageService { */ Map getChatMessageCountMap(Collection conversationIds); + /** + * 获得聊天消息的分页 + * + * @param pageReqVO 分页查询 + * @return 聊天消息的分页 + */ + PageResult getChatMessagePage(AiChatMessagePageReqVO pageReqVO); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 55ae7a7a3..5cd9ae94e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/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; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.ErrorCodeConstants; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; @@ -255,9 +258,25 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId)); } + @Override + public void deleteChatMessageByAdmin(Long id) { + // 1. 校验消息存在 + AiChatMessageDO message = chatMessageMapper.selectById(id); + if (message == null) { + throw exception(AI_CHAT_MESSAGE_NOT_EXIST); + } + // 2. 执行删除 + chatMessageMapper.deleteById(id); + } + @Override public Map getChatMessageCountMap(Collection conversationIds) { return chatMessageMapper.selectCountMapByConversationId(conversationIds); } + @Override + public PageResult getChatMessagePage(AiChatMessagePageReqVO pageReqVO) { + return chatMessageMapper.selectPage(pageReqVO); + } + }