Commit 2d11f085c8a1d65d3e7af6e1f196acc3936d1ac8
1 parent
4fddec5f
【新增】AI:对话管理 50%
Showing
16 changed files
with
196 additions
and
68 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.chat; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.chat; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.hutool.core.util.ObjUtil; | 4 | import cn.hutool.core.util.ObjUtil; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; |
| 9 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | ||
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; | 10 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; | 11 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; |
| 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 10 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; | 13 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; |
| 14 | +import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService; | ||
| 11 | import io.swagger.v3.oas.annotations.Operation; | 15 | import io.swagger.v3.oas.annotations.Operation; |
| 12 | import io.swagger.v3.oas.annotations.Parameter; | 16 | import io.swagger.v3.oas.annotations.Parameter; |
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 17 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 18 | import jakarta.annotation.Resource; |
| 15 | import jakarta.validation.Valid; | 19 | import jakarta.validation.Valid; |
| 20 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | import org.springframework.validation.annotation.Validated; | 21 | import org.springframework.validation.annotation.Validated; |
| 17 | import org.springframework.web.bind.annotation.*; | 22 | import org.springframework.web.bind.annotation.*; |
| 18 | 23 | ||
| 19 | import java.util.List; | 24 | import java.util.List; |
| 25 | +import java.util.Map; | ||
| 26 | +import java.util.function.Consumer; | ||
| 20 | 27 | ||
| 21 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 28 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 22 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 30 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 23 | 31 | ||
| 24 | -@Tag(name = "管理后台 - AI 聊天会话") | 32 | +@Tag(name = "管理后台 - AI 聊天对话") |
| 25 | @RestController | 33 | @RestController |
| 26 | @RequestMapping("/ai/chat/conversation") | 34 | @RequestMapping("/ai/chat/conversation") |
| 27 | @Validated | 35 | @Validated |
| @@ -29,30 +37,32 @@ public class AiChatConversationController { | @@ -29,30 +37,32 @@ public class AiChatConversationController { | ||
| 29 | 37 | ||
| 30 | @Resource | 38 | @Resource |
| 31 | private AiChatConversationService chatConversationService; | 39 | private AiChatConversationService chatConversationService; |
| 40 | + @Resource | ||
| 41 | + private AiChatMessageService chatMessageService; | ||
| 32 | 42 | ||
| 33 | @PostMapping("/create-my") | 43 | @PostMapping("/create-my") |
| 34 | - @Operation(summary = "创建【我的】聊天会话") | 44 | + @Operation(summary = "创建【我的】聊天对话") |
| 35 | public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) { | 45 | public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) { |
| 36 | return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId())); | 46 | return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId())); |
| 37 | } | 47 | } |
| 38 | 48 | ||
| 39 | @PutMapping("/update-my") | 49 | @PutMapping("/update-my") |
| 40 | - @Operation(summary = "更新【我的】聊天会话") | 50 | + @Operation(summary = "更新【我的】聊天对话") |
| 41 | public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) { | 51 | public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) { |
| 42 | chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId()); | 52 | chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId()); |
| 43 | return success(true); | 53 | return success(true); |
| 44 | } | 54 | } |
| 45 | 55 | ||
| 46 | @GetMapping("/my-list") | 56 | @GetMapping("/my-list") |
| 47 | - @Operation(summary = "获得【我的】聊天会话列表") | 57 | + @Operation(summary = "获得【我的】聊天对话列表") |
| 48 | public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() { | 58 | public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() { |
| 49 | List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId()); | 59 | List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId()); |
| 50 | return success(BeanUtils.toBean(list, AiChatConversationRespVO.class)); | 60 | return success(BeanUtils.toBean(list, AiChatConversationRespVO.class)); |
| 51 | } | 61 | } |
| 52 | 62 | ||
| 53 | @GetMapping("/get-my") | 63 | @GetMapping("/get-my") |
| 54 | - @Operation(summary = "获得【我的】聊天会话") | ||
| 55 | - @Parameter(name = "id", required = true, description = "会话编号", example = "1024") | 64 | + @Operation(summary = "获得【我的】聊天对话") |
| 65 | + @Parameter(name = "id", required = true, description = "对话编号", example = "1024") | ||
| 56 | public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) { | 66 | public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) { |
| 57 | AiChatConversationDO conversation = chatConversationService.getChatConversation(id); | 67 | AiChatConversationDO conversation = chatConversationService.getChatConversation(id); |
| 58 | if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { | 68 | if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { |
| @@ -62,20 +72,36 @@ public class AiChatConversationController { | @@ -62,20 +72,36 @@ public class AiChatConversationController { | ||
| 62 | } | 72 | } |
| 63 | 73 | ||
| 64 | @DeleteMapping("/delete-my") | 74 | @DeleteMapping("/delete-my") |
| 65 | - @Operation(summary = "删除聊天会话") | ||
| 66 | - @Parameter(name = "id", required = true, description = "会话编号", example = "1024") | 75 | + @Operation(summary = "删除聊天对话") |
| 76 | + @Parameter(name = "id", required = true, description = "对话编号", example = "1024") | ||
| 67 | public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) { | 77 | public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) { |
| 68 | chatConversationService.deleteChatConversationMy(id, getLoginUserId()); | 78 | chatConversationService.deleteChatConversationMy(id, getLoginUserId()); |
| 69 | return success(true); | 79 | return success(true); |
| 70 | } | 80 | } |
| 71 | 81 | ||
| 82 | + // TODO 芋艿:这个 url 可以改下 | ||
| 72 | @DeleteMapping("/delete-my-all-except-pinned") | 83 | @DeleteMapping("/delete-my-all-except-pinned") |
| 73 | @Operation(summary = "删除所有对话(置顶除外)") | 84 | @Operation(summary = "删除所有对话(置顶除外)") |
| 74 | - @Parameter(name = "id", required = true, description = "会话编号", example = "1024") | ||
| 75 | - public CommonResult<Boolean> deleteMyAllExceptPinned() { | ||
| 76 | - chatConversationService.deleteMyAllExceptPinned(getLoginUserId()); | 85 | + public CommonResult<Boolean> deleteChatConversationMyByUnpinned() { |
| 86 | + chatConversationService.deleteChatConversationMyByUnpinned(getLoginUserId()); | ||
| 77 | return success(true); | 87 | return success(true); |
| 78 | } | 88 | } |
| 79 | - // ========== 会话管理 ========== | 89 | + |
| 90 | + // ========== 对话管理 ========== | ||
| 91 | + | ||
| 92 | + @GetMapping("/page") | ||
| 93 | + @Operation(summary = "获得对话分页", description = "用于【对话管理】菜单") | ||
| 94 | + @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')") | ||
| 95 | + public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) { | ||
| 96 | + PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO); | ||
| 97 | + if (CollUtil.isEmpty(pageResult.getList())) { | ||
| 98 | + return success(PageResult.empty()); | ||
| 99 | + } | ||
| 100 | + // 拼接关联数据 | ||
| 101 | + Map<Long, Integer> messageCountMap = chatMessageService.getChatMessageCountMap( | ||
| 102 | + convertList(pageResult.getList(), AiChatConversationDO::getId)); | ||
| 103 | + return success(BeanUtils.toBean(pageResult, AiChatConversationRespVO.class, | ||
| 104 | + conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0)))); | ||
| 105 | + } | ||
| 80 | 106 | ||
| 81 | } | 107 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
| @@ -62,9 +62,9 @@ public class AiChatMessageController { | @@ -62,9 +62,9 @@ public class AiChatMessageController { | ||
| 62 | return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); | 62 | return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | - @Operation(summary = "获得指定会话的消息列表") | 65 | + @Operation(summary = "获得指定对话的消息列表") |
| 66 | @GetMapping("/list-by-conversation-id") | 66 | @GetMapping("/list-by-conversation-id") |
| 67 | - @Parameter(name = "conversationId", required = true, description = "会话编号", example = "1024") | 67 | + @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024") |
| 68 | public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId( | 68 | public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId( |
| 69 | @RequestParam("conversationId") Long conversationId) { | 69 | @RequestParam("conversationId") Long conversationId) { |
| 70 | AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId); | 70 | AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId); |
| @@ -93,12 +93,16 @@ public class AiChatMessageController { | @@ -93,12 +93,16 @@ public class AiChatMessageController { | ||
| 93 | return success(true); | 93 | return success(true); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | - @Operation(summary = "删除指定会话的消息") | 96 | + @Operation(summary = "删除指定对话的消息") |
| 97 | @DeleteMapping("/delete-by-conversation-id") | 97 | @DeleteMapping("/delete-by-conversation-id") |
| 98 | - @Parameter(name = "conversationId", required = true, description = "会话编号", example = "1024") | 98 | + @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024") |
| 99 | public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) { | 99 | public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) { |
| 100 | chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId()); | 100 | chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId()); |
| 101 | return success(true); | 101 | return success(true); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | + // ========== 对话管理 ========== | ||
| 105 | + | ||
| 106 | + | ||
| 107 | + | ||
| 104 | } | 108 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -@Schema(description = "管理后台 - AI 聊天会话创建【我的】 Request VO") | 6 | +@Schema(description = "管理后台 - AI 聊天对话创建【我的】 Request VO") |
| 7 | @Data | 7 | @Data |
| 8 | public class AiChatConversationCreateMyReqVO { | 8 | public class AiChatConversationCreateMyReqVO { |
| 9 | 9 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; | ||
| 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 AiChatConversationPageReqVO extends PageParam { | ||
| 15 | + | ||
| 16 | + @Schema(description = "用户编号", example = "1024") | ||
| 17 | + private Long userId; | ||
| 18 | + | ||
| 19 | + @Schema(description = "对话标题", example = "你好") | ||
| 20 | + private String title; | ||
| 21 | + | ||
| 22 | + @Schema(description = "创建时间") | ||
| 23 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 24 | + private LocalDateTime[] createTime; | ||
| 25 | + | ||
| 26 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
| @@ -10,24 +10,24 @@ import lombok.Data; | @@ -10,24 +10,24 @@ import lombok.Data; | ||
| 10 | 10 | ||
| 11 | import java.time.LocalDateTime; | 11 | import java.time.LocalDateTime; |
| 12 | 12 | ||
| 13 | -@Schema(description = "管理后台 - AI 聊天会话 Response VO") | 13 | +@Schema(description = "管理后台 - AI 聊天对话 Response VO") |
| 14 | @Data | 14 | @Data |
| 15 | public class AiChatConversationRespVO implements VO { | 15 | public class AiChatConversationRespVO implements VO { |
| 16 | 16 | ||
| 17 | - @Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | 17 | + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 18 | private Long id; | 18 | private Long id; |
| 19 | 19 | ||
| 20 | @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | 20 | @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") |
| 21 | private Long userId; | 21 | private Long userId; |
| 22 | 22 | ||
| 23 | - @Schema(description = "会话标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个标题") | 23 | + @Schema(description = "对话标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个标题") |
| 24 | private String title; | 24 | private String title; |
| 25 | 25 | ||
| 26 | @Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | 26 | @Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
| 27 | private Boolean pinned; | 27 | private Boolean pinned; |
| 28 | 28 | ||
| 29 | - @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") | ||
| 30 | - @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = "avatar", ref = "roleAvatar") | 29 | + @Schema(description = "角色编号", example = "1") |
| 30 | + @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = {"name", "avatar"}, refs = {"roleName", "roleAvatar"}) | ||
| 31 | private Long roleId; | 31 | private Long roleId; |
| 32 | 32 | ||
| 33 | @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 33 | @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| @@ -52,12 +52,20 @@ public class AiChatConversationRespVO implements VO { | @@ -52,12 +52,20 @@ public class AiChatConversationRespVO implements VO { | ||
| 52 | @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | 52 | @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| 53 | private Integer maxContexts; | 53 | private Integer maxContexts; |
| 54 | 54 | ||
| 55 | - @Schema(description = "最后更新时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 56 | - private LocalDateTime updateTime; | 55 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 56 | + private LocalDateTime createTime; | ||
| 57 | 57 | ||
| 58 | // ========== 关联 role 信息 ========== | 58 | // ========== 关联 role 信息 ========== |
| 59 | 59 | ||
| 60 | - @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | 60 | + @Schema(description = "角色头像", example = "https://www.iocoder.cn/1.png") |
| 61 | private String roleAvatar; | 61 | private String roleAvatar; |
| 62 | 62 | ||
| 63 | + @Schema(description = "角色名字", example = "小黄") | ||
| 64 | + private String roleName; | ||
| 65 | + | ||
| 66 | + // ========== 仅在【对话管理】时加载 ========== | ||
| 67 | + | ||
| 68 | + @Schema(description = "消息数量", example = "20") | ||
| 69 | + private Integer messageCount; | ||
| 70 | + | ||
| 63 | } | 71 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
| @@ -4,15 +4,15 @@ import io.swagger.v3.oas.annotations.media.Schema; | @@ -4,15 +4,15 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | import jakarta.validation.constraints.NotNull; | 4 | import jakarta.validation.constraints.NotNull; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 | ||
| 7 | -@Schema(description = "管理后台 - AI 聊天会话更新【我的】 Request VO") | 7 | +@Schema(description = "管理后台 - AI 聊天对话更新【我的】 Request VO") |
| 8 | @Data | 8 | @Data |
| 9 | public class AiChatConversationUpdateMyReqVO { | 9 | public class AiChatConversationUpdateMyReqVO { |
| 10 | 10 | ||
| 11 | - @Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 12 | - @NotNull(message = "会话编号不能为空") | 11 | + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 12 | + @NotNull(message = "对话编号不能为空") | ||
| 13 | private Long id; | 13 | private Long id; |
| 14 | 14 | ||
| 15 | - @Schema(description = "会话标题", example = "我是一个标题") | 15 | + @Schema(description = "对话标题", example = "我是一个标题") |
| 16 | private String title; | 16 | private String title; |
| 17 | 17 | ||
| 18 | @Schema(description = "是否置顶", example = "true") | 18 | @Schema(description = "是否置顶", example = "true") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
| @@ -12,7 +12,7 @@ public class AiChatMessageRespVO { | @@ -12,7 +12,7 @@ public class AiChatMessageRespVO { | ||
| 12 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | 12 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 13 | private Long id; | 13 | private Long id; |
| 14 | 14 | ||
| 15 | - @Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | 15 | + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") |
| 16 | private Long conversationId; | 16 | private Long conversationId; |
| 17 | 17 | ||
| 18 | @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role") | 18 | @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
| @@ -13,7 +13,7 @@ import java.time.LocalDateTime; | @@ -13,7 +13,7 @@ import java.time.LocalDateTime; | ||
| 13 | import java.util.Date; | 13 | import java.util.Date; |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | - * AI Chat 会话 DO | 16 | + * AI Chat 对话 DO |
| 17 | * | 17 | * |
| 18 | * 用户每次发起 Chat 聊天时,会创建一个 {@link AiChatConversationDO} 对象,将它的消息关联在一起 | 18 | * 用户每次发起 Chat 聊天时,会创建一个 {@link AiChatConversationDO} 对象,将它的消息关联在一起 |
| 19 | * | 19 | * |
| @@ -45,7 +45,7 @@ public class AiChatConversationDO extends BaseDO { | @@ -45,7 +45,7 @@ public class AiChatConversationDO extends BaseDO { | ||
| 45 | private Long userId; | 45 | private Long userId; |
| 46 | 46 | ||
| 47 | /** | 47 | /** |
| 48 | - * 会话标题 | 48 | + * 对话标题 |
| 49 | * | 49 | * |
| 50 | * 默认由系统自动生成,可用户手动修改 | 50 | * 默认由系统自动生成,可用户手动修改 |
| 51 | */ | 51 | */ |
| @@ -79,7 +79,7 @@ public class AiChatConversationDO extends BaseDO { | @@ -79,7 +79,7 @@ public class AiChatConversationDO extends BaseDO { | ||
| 79 | */ | 79 | */ |
| 80 | private String model; | 80 | private String model; |
| 81 | 81 | ||
| 82 | - // ========== 会话配置 ========== | 82 | + // ========== 对话配置 ========== |
| 83 | 83 | ||
| 84 | /** | 84 | /** |
| 85 | * 角色设定 | 85 | * 角色设定 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
| @@ -32,7 +32,7 @@ public class AiChatMessageDO extends BaseDO { | @@ -32,7 +32,7 @@ public class AiChatMessageDO extends BaseDO { | ||
| 32 | private Long id; | 32 | private Long id; |
| 33 | 33 | ||
| 34 | /** | 34 | /** |
| 35 | - * 会话编号 | 35 | + * 对话编号 |
| 36 | * | 36 | * |
| 37 | * 关联 {@link AiChatConversationDO#getId()} 字段 | 37 | * 关联 {@link AiChatConversationDO#getId()} 字段 |
| 38 | */ | 38 | */ |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java
| @@ -62,7 +62,7 @@ public class AiChatModelDO extends BaseDO { | @@ -62,7 +62,7 @@ public class AiChatModelDO extends BaseDO { | ||
| 62 | */ | 62 | */ |
| 63 | private Integer status; | 63 | private Integer status; |
| 64 | 64 | ||
| 65 | - // ========== 会话配置 ========== | 65 | + // ========== 对话配置 ========== |
| 66 | 66 | ||
| 67 | /** | 67 | /** |
| 68 | * 温度参数 | 68 | * 温度参数 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java
| 1 | package cn.iocoder.yudao.module.ai.dal.mysql.chat; | 1 | package cn.iocoder.yudao.module.ai.dal.mysql.chat; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | ||
| 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 6 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 9 | ||
| @@ -22,4 +24,18 @@ public interface AiChatConversationMapper extends BaseMapperX<AiChatConversation | @@ -22,4 +24,18 @@ public interface AiChatConversationMapper extends BaseMapperX<AiChatConversation | ||
| 22 | .orderByAsc(AiChatConversationDO::getCreateTime)); | 24 | .orderByAsc(AiChatConversationDO::getCreateTime)); |
| 23 | } | 25 | } |
| 24 | 26 | ||
| 27 | + default List<AiChatConversationDO> selectListByUserIdAndPinned(Long userId, boolean pinned) { | ||
| 28 | + return selectList(new LambdaQueryWrapperX<AiChatConversationDO>() | ||
| 29 | + .eq(AiChatConversationDO::getUserId, userId) | ||
| 30 | + .eq(AiChatConversationDO::getPinned, pinned)); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + default PageResult<AiChatConversationDO> selectChatConversationPage(AiChatConversationPageReqVO pageReqVO) { | ||
| 34 | + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatConversationDO>() | ||
| 35 | + .eqIfPresent(AiChatConversationDO::getUserId, pageReqVO.getUserId()) | ||
| 36 | + .likeIfPresent(AiChatConversationDO::getTitle, pageReqVO.getTitle()) | ||
| 37 | + .betweenIfPresent(AiChatConversationDO::getCreateTime, pageReqVO.getCreateTime()) | ||
| 38 | + .orderByDesc(AiChatConversationDO::getId)); | ||
| 39 | + } | ||
| 40 | + | ||
| 25 | } | 41 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java
| 1 | package cn.iocoder.yudao.module.ai.dal.mysql.chat; | 1 | package cn.iocoder.yudao.module.ai.dal.mysql.chat; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.map.MapUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 6 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 7 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 9 | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||
| 6 | import org.apache.ibatis.annotations.Mapper; | 10 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 11 | ||
| 12 | +import java.util.Collection; | ||
| 13 | +import java.util.Collections; | ||
| 8 | import java.util.List; | 14 | import java.util.List; |
| 15 | +import java.util.Map; | ||
| 9 | 16 | ||
| 10 | /** | 17 | /** |
| 11 | * AI 聊天对话 Mapper | 18 | * AI 聊天对话 Mapper |
| @@ -21,4 +28,19 @@ public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> { | @@ -21,4 +28,19 @@ public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> { | ||
| 21 | .orderByAsc(AiChatMessageDO::getId)); | 28 | .orderByAsc(AiChatMessageDO::getId)); |
| 22 | } | 29 | } |
| 23 | 30 | ||
| 31 | + default Map<Long, Integer> selectCountMapByConversationId(Collection<Long> conversationIds) { | ||
| 32 | + // SQL count 查询 | ||
| 33 | + List<Map<String, Object>> result = selectMaps(new QueryWrapper<AiChatMessageDO>() | ||
| 34 | + .select("COUNT(id) AS count, conversation_id AS conversationId") | ||
| 35 | + .in("conversation_id", conversationIds) | ||
| 36 | + .groupBy("conversation_id")); | ||
| 37 | + if (CollUtil.isEmpty(result)) { | ||
| 38 | + return Collections.emptyMap(); | ||
| 39 | + } | ||
| 40 | + // 转换数据 | ||
| 41 | + return CollectionUtils.convertMap(result, | ||
| 42 | + record -> MapUtil.getLong(record, "conversationId"), | ||
| 43 | + record -> MapUtil.getInt(record, "count" )); | ||
| 44 | + } | ||
| 45 | + | ||
| 24 | } | 46 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
| 1 | package cn.iocoder.yudao.module.ai.service.chat; | 1 | package cn.iocoder.yudao.module.ai.service.chat; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 3 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; | 4 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; |
| 5 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | ||
| 4 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; | 6 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; |
| 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 6 | 8 | ||
| @@ -14,7 +16,7 @@ import java.util.List; | @@ -14,7 +16,7 @@ import java.util.List; | ||
| 14 | public interface AiChatConversationService { | 16 | public interface AiChatConversationService { |
| 15 | 17 | ||
| 16 | /** | 18 | /** |
| 17 | - * 创建【我的】聊天会话 | 19 | + * 创建【我的】聊天对话 |
| 18 | * | 20 | * |
| 19 | * @param createReqVO 创建信息 | 21 | * @param createReqVO 创建信息 |
| 20 | * @param userId 用户编号 | 22 | * @param userId 用户编号 |
| @@ -23,7 +25,7 @@ public interface AiChatConversationService { | @@ -23,7 +25,7 @@ public interface AiChatConversationService { | ||
| 23 | Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId); | 25 | Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId); |
| 24 | 26 | ||
| 25 | /** | 27 | /** |
| 26 | - * 更新【我的】聊天会话 | 28 | + * 更新【我的】聊天对话 |
| 27 | * | 29 | * |
| 28 | * @param updateReqVO 更新信息 | 30 | * @param updateReqVO 更新信息 |
| 29 | * @param userId 用户编号 | 31 | * @param userId 用户编号 |
| @@ -31,23 +33,23 @@ public interface AiChatConversationService { | @@ -31,23 +33,23 @@ public interface AiChatConversationService { | ||
| 31 | void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId); | 33 | void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId); |
| 32 | 34 | ||
| 33 | /** | 35 | /** |
| 34 | - * 获得【我的】聊天会话列表 | 36 | + * 获得【我的】聊天对话列表 |
| 35 | * | 37 | * |
| 36 | * @param userId 用户编号 | 38 | * @param userId 用户编号 |
| 37 | - * @return 聊天会话列表 | 39 | + * @return 聊天对话列表 |
| 38 | */ | 40 | */ |
| 39 | List<AiChatConversationDO> getChatConversationListByUserId(Long userId); | 41 | List<AiChatConversationDO> getChatConversationListByUserId(Long userId); |
| 40 | 42 | ||
| 41 | /** | 43 | /** |
| 42 | - * 获得聊天会话 | 44 | + * 获得聊天对话 |
| 43 | * | 45 | * |
| 44 | * @param id 编号 | 46 | * @param id 编号 |
| 45 | - * @return 聊天会话 | 47 | + * @return 聊天对话 |
| 46 | */ | 48 | */ |
| 47 | AiChatConversationDO getChatConversation(Long id); | 49 | AiChatConversationDO getChatConversation(Long id); |
| 48 | 50 | ||
| 49 | /** | 51 | /** |
| 50 | - * 删除【我的】聊天会话 | 52 | + * 删除【我的】聊天对话 |
| 51 | * | 53 | * |
| 52 | * @param id 编号 | 54 | * @param id 编号 |
| 53 | * @param userId 用户编号 | 55 | * @param userId 用户编号 |
| @@ -55,17 +57,20 @@ public interface AiChatConversationService { | @@ -55,17 +57,20 @@ public interface AiChatConversationService { | ||
| 55 | void deleteChatConversationMy(Long id, Long userId); | 57 | void deleteChatConversationMy(Long id, Long userId); |
| 56 | 58 | ||
| 57 | /** | 59 | /** |
| 58 | - * 校验 - 是否存在 | 60 | + * 校验聊天对话是否存在 |
| 59 | * | 61 | * |
| 60 | - * @param id | ||
| 61 | - * @return | 62 | + * @param id 编号 |
| 63 | + * @return 聊天对话 | ||
| 62 | */ | 64 | */ |
| 63 | - AiChatConversationDO validateExists(Long id); | 65 | + AiChatConversationDO validateChatConversationExists(Long id); |
| 64 | 66 | ||
| 65 | /** | 67 | /** |
| 66 | - * 删除 - 所有对话,置顶除外 | 68 | + * 删除【我的】 + 非置顶的聊天对话 |
| 67 | * | 69 | * |
| 68 | - * @param loginUserId | 70 | + * @param userId 用户编号 |
| 69 | */ | 71 | */ |
| 70 | - void deleteMyAllExceptPinned(Long loginUserId); | 72 | + void deleteChatConversationMyByUnpinned(Long userId); |
| 73 | + | ||
| 74 | + PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO); | ||
| 75 | + | ||
| 71 | } | 76 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
| 1 | package cn.iocoder.yudao.module.ai.service.chat; | 1 | package cn.iocoder.yudao.module.ai.service.chat; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.hutool.core.lang.Assert; | 4 | import cn.hutool.core.lang.Assert; |
| 4 | import cn.hutool.core.util.ObjUtil; | 5 | import cn.hutool.core.util.ObjUtil; |
| 5 | import cn.hutool.core.util.ObjectUtil; | 6 | import cn.hutool.core.util.ObjectUtil; |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 7 | -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; | 9 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; |
| 10 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | ||
| 9 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; | 11 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; |
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 11 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; |
| @@ -22,6 +24,8 @@ import java.time.LocalDateTime; | @@ -22,6 +24,8 @@ import java.time.LocalDateTime; | ||
| 22 | import java.util.List; | 24 | import java.util.List; |
| 23 | 25 | ||
| 24 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 26 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 27 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 28 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 25 | import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; | 29 | import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; |
| 26 | import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; | 30 | import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; |
| 27 | 31 | ||
| @@ -69,7 +73,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -69,7 +73,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 69 | @Override | 73 | @Override |
| 70 | public void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId) { | 74 | public void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId) { |
| 71 | // 1.1 校验对话是否存在 | 75 | // 1.1 校验对话是否存在 |
| 72 | - AiChatConversationDO conversation = validateExists(updateReqVO.getId()); | 76 | + AiChatConversationDO conversation = validateChatConversationExists(updateReqVO.getId()); |
| 73 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | 77 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { |
| 74 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); | 78 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| 75 | } | 79 | } |
| @@ -103,7 +107,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -103,7 +107,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 103 | @Override | 107 | @Override |
| 104 | public void deleteChatConversationMy(Long id, Long userId) { | 108 | public void deleteChatConversationMy(Long id, Long userId) { |
| 105 | // 1. 校验对话是否存在 | 109 | // 1. 校验对话是否存在 |
| 106 | - AiChatConversationDO conversation = validateExists(id); | 110 | + AiChatConversationDO conversation = validateChatConversationExists(id); |
| 107 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | 111 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { |
| 108 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); | 112 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| 109 | } | 113 | } |
| @@ -119,7 +123,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -119,7 +123,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 119 | throw exception(CHAT_CONVERSATION_MODEL_ERROR); | 123 | throw exception(CHAT_CONVERSATION_MODEL_ERROR); |
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | - public AiChatConversationDO validateExists(Long id) { | 126 | + public AiChatConversationDO validateChatConversationExists(Long id) { |
| 123 | AiChatConversationDO conversation = chatConversationMapper.selectById(id); | 127 | AiChatConversationDO conversation = chatConversationMapper.selectById(id); |
| 124 | if (conversation == null) { | 128 | if (conversation == null) { |
| 125 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); | 129 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| @@ -128,12 +132,17 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -128,12 +132,17 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 128 | } | 132 | } |
| 129 | 133 | ||
| 130 | @Override | 134 | @Override |
| 131 | - public void deleteMyAllExceptPinned(Long loginUserId) { | ||
| 132 | - chatConversationMapper.delete( | ||
| 133 | - new LambdaQueryWrapperX<AiChatConversationDO>() | ||
| 134 | - .eq(AiChatConversationDO::getUserId, loginUserId) | ||
| 135 | - .eq(AiChatConversationDO::getPinned, false) | ||
| 136 | - ); | 135 | + public void deleteChatConversationMyByUnpinned(Long userId) { |
| 136 | + List<AiChatConversationDO> list = chatConversationMapper.selectListByUserIdAndPinned(userId, false); | ||
| 137 | + if (CollUtil.isEmpty(list)) { | ||
| 138 | + return; | ||
| 139 | + } | ||
| 140 | + chatConversationMapper.deleteBatchIds(convertList(list, AiChatConversationDO::getId)); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + @Override | ||
| 144 | + public PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) { | ||
| 145 | + return chatConversationMapper.selectChatConversationPage(pageReqVO); | ||
| 137 | } | 146 | } |
| 138 | 147 | ||
| 139 | } | 148 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java
| @@ -5,7 +5,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*; | @@ -5,7 +5,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*; | ||
| 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; | 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 6 | import reactor.core.publisher.Flux; | 6 | import reactor.core.publisher.Flux; |
| 7 | 7 | ||
| 8 | +import java.util.Collection; | ||
| 8 | import java.util.List; | 9 | import java.util.List; |
| 10 | +import java.util.Map; | ||
| 9 | 11 | ||
| 10 | /** | 12 | /** |
| 11 | * AI 聊天消息 Service 接口 | 13 | * AI 聊天消息 Service 接口 |
| @@ -32,9 +34,9 @@ public interface AiChatMessageService { | @@ -32,9 +34,9 @@ public interface AiChatMessageService { | ||
| 32 | Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId); | 34 | Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId); |
| 33 | 35 | ||
| 34 | /** | 36 | /** |
| 35 | - * 获得指定会话的消息列表 | 37 | + * 获得指定对话的消息列表 |
| 36 | * | 38 | * |
| 37 | - * @param conversationId 会话编号 | 39 | + * @param conversationId 对话编号 |
| 38 | * @return 消息列表 | 40 | * @return 消息列表 |
| 39 | */ | 41 | */ |
| 40 | List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId); | 42 | List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId); |
| @@ -48,11 +50,19 @@ public interface AiChatMessageService { | @@ -48,11 +50,19 @@ public interface AiChatMessageService { | ||
| 48 | void deleteChatMessage(Long id, Long userId); | 50 | void deleteChatMessage(Long id, Long userId); |
| 49 | 51 | ||
| 50 | /** | 52 | /** |
| 51 | - * 删除指定会话的消息 | 53 | + * 删除指定对话的消息 |
| 52 | * | 54 | * |
| 53 | - * @param conversationId 会话编号 | 55 | + * @param conversationId 对话编号 |
| 54 | * @param userId 用户编号 | 56 | * @param userId 用户编号 |
| 55 | */ | 57 | */ |
| 56 | void deleteChatMessageByConversationId(Long conversationId, Long userId); | 58 | void deleteChatMessageByConversationId(Long conversationId, Long userId); |
| 57 | 59 | ||
| 60 | + /** | ||
| 61 | + * 获得聊天对话的消息数量 Map | ||
| 62 | + * | ||
| 63 | + * @param conversationIds 对话编号数组 | ||
| 64 | + * @return 消息数量 Map | ||
| 65 | + */ | ||
| 66 | + Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds); | ||
| 67 | + | ||
| 58 | } | 68 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| @@ -5,7 +5,6 @@ import cn.hutool.core.util.ObjUtil; | @@ -5,7 +5,6 @@ import cn.hutool.core.util.ObjUtil; | ||
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 7 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; | 7 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; |
| 8 | -import cn.iocoder.yudao.framework.common.exception.ErrorCode; | ||
| 9 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 10 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 11 | import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | 10 | import cn.iocoder.yudao.module.ai.ErrorCodeConstants; |
| @@ -15,7 +14,6 @@ import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | @@ -15,7 +14,6 @@ import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 15 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 14 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 16 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 15 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 17 | import jakarta.annotation.Resource; | 16 | import jakarta.annotation.Resource; |
| 18 | -import org.reactivestreams.Publisher; | ||
| 19 | import org.springframework.ai.chat.ChatResponse; | 17 | import org.springframework.ai.chat.ChatResponse; |
| 20 | import org.springframework.ai.chat.StreamingChatClient; | 18 | import org.springframework.ai.chat.StreamingChatClient; |
| 21 | import org.springframework.ai.chat.messages.*; | 19 | import org.springframework.ai.chat.messages.*; |
| @@ -34,7 +32,6 @@ import lombok.extern.slf4j.Slf4j; | @@ -34,7 +32,6 @@ import lombok.extern.slf4j.Slf4j; | ||
| 34 | import org.springframework.stereotype.Service; | 32 | import org.springframework.stereotype.Service; |
| 35 | import org.springframework.transaction.annotation.Transactional; | 33 | import org.springframework.transaction.annotation.Transactional; |
| 36 | import reactor.core.publisher.Flux; | 34 | import reactor.core.publisher.Flux; |
| 37 | -import reactor.core.scheduler.Schedulers; | ||
| 38 | 35 | ||
| 39 | import java.time.LocalDateTime; | 36 | import java.time.LocalDateTime; |
| 40 | import java.util.*; | 37 | import java.util.*; |
| @@ -115,7 +112,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -115,7 +112,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 115 | @Override | 112 | @Override |
| 116 | public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { | 113 | public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { |
| 117 | // 1.1 校验对话存在 | 114 | // 1.1 校验对话存在 |
| 118 | - AiChatConversationDO conversation = chatConversationService.validateExists(sendReqVO.getConversationId()); | 115 | + AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId()); |
| 119 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | 116 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { |
| 120 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); // TODO 芋艿:异常情况的对接; | 117 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); // TODO 芋艿:异常情况的对接; |
| 121 | } | 118 | } |
| @@ -189,7 +186,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -189,7 +186,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 189 | * n 组:指的是 user + assistant 形成一组 | 186 | * n 组:指的是 user + assistant 形成一组 |
| 190 | * | 187 | * |
| 191 | * @param messages 消息列表 | 188 | * @param messages 消息列表 |
| 192 | - * @param conversation 会话 | 189 | + * @param conversation 对话 |
| 193 | * @param sendReqVO 发送请求 | 190 | * @param sendReqVO 发送请求 |
| 194 | * @return 消息上下文 | 191 | * @return 消息上下文 |
| 195 | */ | 192 | */ |
| @@ -258,4 +255,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -258,4 +255,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 258 | chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId)); | 255 | chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId)); |
| 259 | } | 256 | } |
| 260 | 257 | ||
| 258 | + @Override | ||
| 259 | + public Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds) { | ||
| 260 | + return chatMessageMapper.selectCountMapByConversationId(conversationIds); | ||
| 261 | + } | ||
| 262 | + | ||
| 261 | } | 263 | } |