Commit 285a431616e8cfcf55f6f473cf0ef918c9799bc3

Authored by puhui999
1 parent d89a19a9

MALL-KEFU: 根据代码评审完善相关方法

Showing 19 changed files with 144 additions and 211 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/WebSocketMessageTypeConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.enums;
  2 +
  3 +/**
  4 + * websocket 消息类型枚举类
  5 + *
  6 + * @author HUIHUI
  7 + */
  8 +public interface WebSocketMessageTypeConstants {
  9 +
  10 + //======================= mall 客服 =======================
  11 +
  12 + String KEFU_MESSAGE_TYPE = "kefu_message_type"; // 客服消息类型
  13 + String KEFU_MESSAGE_ADMIN_READ = "kefu_message_read_status_change"; // 客服消息管理员已读
  14 +
  15 +}
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/kehu/KeFuMessageContentTypeEnum.java
@@ -19,6 +19,7 @@ public enum KeFuMessageContentTypeEnum implements IntArrayValuable { @@ -19,6 +19,7 @@ public enum KeFuMessageContentTypeEnum implements IntArrayValuable {
19 IMAGE(2, "图片消息"), 19 IMAGE(2, "图片消息"),
20 VOICE(3, "语音消息"), 20 VOICE(3, "语音消息"),
21 VIDEO(4, "视频消息"), 21 VIDEO(4, "视频消息"),
  22 + SYSTEM(5, "系统消息"),
22 // ========== 商城特殊消息 ========== 23 // ========== 商城特殊消息 ==========
23 PRODUCT(10, "商品消息"), 24 PRODUCT(10, "商品消息"),
24 ORDER(11, "订单消息"); 25 ORDER(11, "订单消息");
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java
@@ -27,12 +27,11 @@ public class KeFuConversationController { @@ -27,12 +27,11 @@ public class KeFuConversationController {
27 @Resource 27 @Resource
28 private KeFuConversationService conversationService; 28 private KeFuConversationService conversationService;
29 29
30 - // TODO @puhui999:updateConversationPinned  
31 - @PostMapping("/update-pinned") 30 + @PostMapping("/update-conversation-pinned")
32 @Operation(summary = "置顶客服会话") 31 @Operation(summary = "置顶客服会话")
33 @PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:update')") 32 @PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:update')")
34 - public CommonResult<Boolean> updatePinned(@Valid @RequestBody KeFuConversationUpdatePinnedReqVO updateReqVO) {  
35 - conversationService.updatePinned(updateReqVO); 33 + public CommonResult<Boolean> updateConversationPinned(@Valid @RequestBody KeFuConversationUpdatePinnedReqVO updateReqVO) {
  34 + conversationService.updateAdminPinned(updateReqVO);
36 return success(true); 35 return success(true);
37 } 36 }
38 37
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuMessageController.java
1 package cn.iocoder.yudao.module.promotion.controller.admin.kefu; 1 package cn.iocoder.yudao.module.promotion.controller.admin.kefu;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -18,7 +19,6 @@ import org.springframework.validation.annotation.Validated; @@ -18,7 +19,6 @@ import org.springframework.validation.annotation.Validated;
18 import org.springframework.web.bind.annotation.*; 19 import org.springframework.web.bind.annotation.*;
19 20
20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
21 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;  
22 22
23 @Tag(name = "管理后台 - 客服消息") 23 @Tag(name = "管理后台 - 客服消息")
24 @RestController 24 @RestController
@@ -37,11 +37,11 @@ public class KeFuMessageController { @@ -37,11 +37,11 @@ public class KeFuMessageController {
37 } 37 }
38 38
39 @PutMapping("/update-read-status") 39 @PutMapping("/update-read-status")
40 - @Operation(summary = "更新客服消息已读状态") 40 + @Operation(summary = "更新会员客服消息已读状态")
41 @Parameter(name = "conversationId", description = "会话编号", required = true) 41 @Parameter(name = "conversationId", description = "会话编号", required = true)
42 @PreAuthorize("@ss.hasPermission('promotion:kefu-message:update')") 42 @PreAuthorize("@ss.hasPermission('promotion:kefu-message:update')")
43 public CommonResult<Boolean> updateKefuMessageReadStatus(@RequestParam("conversationId") Long conversationId) { 43 public CommonResult<Boolean> updateKefuMessageReadStatus(@RequestParam("conversationId") Long conversationId) {
44 - messageService.updateKefuMessageReadStatus(conversationId, getLoginUserId()); 44 + messageService.updateKefuMessageReadStatus(conversationId);
45 return success(true); 45 return success(true);
46 } 46 }
47 47
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java
1 package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation; 1 package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 -import lombok.*; 4 +import lombok.Data;
5 5
6 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
7 7
@@ -18,24 +18,22 @@ public class KeFuConversationRespVO { @@ -18,24 +18,22 @@ public class KeFuConversationRespVO {
18 @Schema(description = "最后聊天时间", requiredMode = Schema.RequiredMode.REQUIRED) 18 @Schema(description = "最后聊天时间", requiredMode = Schema.RequiredMode.REQUIRED)
19 private LocalDateTime lastMessageTime; 19 private LocalDateTime lastMessageTime;
20 20
21 - // TODO @puhui999:, 缺了空格哈  
22 -  
23 - @Schema(description = "最后聊天内容", requiredMode = Schema.RequiredMode.REQUIRED,example = "嗨,您好啊") 21 + @Schema(description = "最后聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "嗨,您好啊")
24 private String lastMessageContent; 22 private String lastMessageContent;
25 23
26 - @Schema(description = "最后发送的消息类型", requiredMode = Schema.RequiredMode.REQUIRED,example = "1") 24 + @Schema(description = "最后发送的消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
27 private Integer lastMessageContentType; 25 private Integer lastMessageContentType;
28 26
29 - @Schema(description = "管理端置顶", requiredMode = Schema.RequiredMode.REQUIRED,example = "false") 27 + @Schema(description = "管理端置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
30 private Boolean adminPinned; 28 private Boolean adminPinned;
31 29
32 - @Schema(description = "用户是否可见", requiredMode = Schema.RequiredMode.REQUIRED,example = "true") 30 + @Schema(description = "用户是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
33 private Boolean userDeleted; 31 private Boolean userDeleted;
34 32
35 - @Schema(description = "管理员是否可见", requiredMode = Schema.RequiredMode.REQUIRED,example = "true") 33 + @Schema(description = "管理员是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
36 private Boolean adminDeleted; 34 private Boolean adminDeleted;
37 35
38 - @Schema(description = "管理员未读消息数", requiredMode = Schema.RequiredMode.REQUIRED,example = "6") 36 + @Schema(description = "管理员未读消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
39 private Integer adminUnreadMessageCount; 37 private Integer adminUnreadMessageCount;
40 38
41 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 39 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/message/KeFuMessagePageReqVO.java
@@ -6,8 +6,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; @@ -6,8 +6,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 6
7 @Schema(description = "管理后台 - 客服消息分页 Request VO") 7 @Schema(description = "管理后台 - 客服消息分页 Request VO")
8 @Data 8 @Data
9 -// TODO @puhui999:不用 @EqualsAndHashCode 哈  
10 -@EqualsAndHashCode(callSuper = true)  
11 @ToString(callSuper = true) 9 @ToString(callSuper = true)
12 public class KeFuMessagePageReqVO extends PageParam { 10 public class KeFuMessagePageReqVO extends PageParam {
13 11
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuConversationController.java deleted
1 -package cn.iocoder.yudao.module.promotion.controller.app.kefu;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
5 -import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;  
6 -import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.conversation.AppKeFuConversationRespVO;  
7 -import cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService;  
8 -import io.swagger.v3.oas.annotations.Operation;  
9 -import io.swagger.v3.oas.annotations.tags.Tag;  
10 -import jakarta.annotation.Resource;  
11 -import org.springframework.validation.annotation.Validated;  
12 -import org.springframework.web.bind.annotation.GetMapping;  
13 -import org.springframework.web.bind.annotation.RequestMapping;  
14 -import org.springframework.web.bind.annotation.RestController;  
15 -  
16 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
17 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;  
18 -  
19 -@Tag(name = "用户 APP - 客户会话")  
20 -@RestController  
21 -@RequestMapping("/promotion/kefu-conversation")  
22 -@Validated  
23 -public class AppKeFuConversationController {  
24 -  
25 - @Resource  
26 - private KeFuConversationService conversationService;  
27 -  
28 - // TODO @puhui999:接口名不对噢;  
29 - @GetMapping("/get")  
30 - @Operation(summary = "获得客服会话")  
31 - @PreAuthenticated  
32 - public CommonResult<AppKeFuConversationRespVO> getDiyPage() {  
33 - // TODO @puhui999:建议获取;和转换,分成 2 个哈;干净一些;  
34 - return success(BeanUtils.toBean(conversationService.getOrCreateConversation(getLoginUserId()), AppKeFuConversationRespVO.class));  
35 - }  
36 -  
37 -}  
38 \ No newline at end of file 0 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuMessageController.java
1 package cn.iocoder.yudao.module.promotion.controller.app.kefu; 1 package cn.iocoder.yudao.module.promotion.controller.app.kefu;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -35,7 +36,8 @@ public class AppKeFuMessageController { @@ -35,7 +36,8 @@ public class AppKeFuMessageController {
35 @Operation(summary = "发送客服消息") 36 @Operation(summary = "发送客服消息")
36 @PreAuthenticated 37 @PreAuthenticated
37 public CommonResult<Long> createKefuMessage(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) { 38 public CommonResult<Long> createKefuMessage(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) {
38 - return success(kefuMessageService.sendKefuMessage(BeanUtils.toBean(sendReqVO, KeFuMessageSendReqVO.class))); 39 + sendReqVO.setSenderId(getLoginUserId()).setSenderType(UserTypeEnum.MEMBER.getValue()); // 设置用户编号和类型
  40 + return success(kefuMessageService.sendKefuMessage(sendReqVO));
39 } 41 }
40 42
41 @PutMapping("/update-read-status") 43 @PutMapping("/update-read-status")
@@ -43,7 +45,7 @@ public class AppKeFuMessageController { @@ -43,7 +45,7 @@ public class AppKeFuMessageController {
43 @Parameter(name = "conversationId", description = "会话编号", required = true) 45 @Parameter(name = "conversationId", description = "会话编号", required = true)
44 @PreAuthenticated 46 @PreAuthenticated
45 public CommonResult<Boolean> updateKefuMessageReadStatus(@RequestParam("conversationId") Long conversationId) { 47 public CommonResult<Boolean> updateKefuMessageReadStatus(@RequestParam("conversationId") Long conversationId) {
46 - kefuMessageService.updateKefuMessageReadStatus(conversationId, getLoginUserId()); 48 + kefuMessageService.updateKefuMessageReadStatus(conversationId);
47 return success(true); 49 return success(true);
48 } 50 }
49 51
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/conversation/AppKeFuConversationRespVO.java deleted
1 -package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.conversation;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.*;  
5 -  
6 -import java.time.LocalDateTime;  
7 -  
8 -@Schema(description = "用户 App - 客服会话 Response VO")  
9 -@Data  
10 -public class AppKeFuConversationRespVO {  
11 -  
12 - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24988")  
13 - private Long id;  
14 -  
15 - @Schema(description = "会话所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "8300")  
16 - private Long userId;  
17 -  
18 - @Schema(description = "最后聊天时间", requiredMode = Schema.RequiredMode.REQUIRED,example = "2024-01-01 00:00:00")  
19 - private LocalDateTime lastMessageTime;  
20 -  
21 - @Schema(description = "最后聊天内容", requiredMode = Schema.RequiredMode.REQUIRED,example = "嗨,您好啊")  
22 - private String lastMessageContent;  
23 -  
24 - @Schema(description = "最后发送的消息类型", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")  
25 - private Integer lastMessageContentType;  
26 -  
27 - @Schema(description = "管理端置顶", requiredMode = Schema.RequiredMode.REQUIRED,example = "false")  
28 - private Boolean adminPinned;  
29 -  
30 - @Schema(description = "用户是否可见", requiredMode = Schema.RequiredMode.REQUIRED,example = "true")  
31 - private Boolean userDeleted;  
32 -  
33 - @Schema(description = "管理员是否可见", requiredMode = Schema.RequiredMode.REQUIRED,example = "true")  
34 - private Boolean adminDeleted;  
35 -  
36 - @Schema(description = "管理员未读消息数", requiredMode = Schema.RequiredMode.REQUIRED,example = "6")  
37 - private Integer adminUnreadMessageCount;  
38 -  
39 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED,example = "2024-01-01 00:00:00")  
40 - private LocalDateTime createTime;  
41 -  
42 -}  
43 \ No newline at end of file 0 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessagePageReqVO.java
@@ -8,8 +8,6 @@ import lombok.ToString; @@ -8,8 +8,6 @@ import lombok.ToString;
8 8
9 @Schema(description = "用户 App - 客服消息分页 Request VO") 9 @Schema(description = "用户 App - 客服消息分页 Request VO")
10 @Data 10 @Data
11 -// TODO @puhui999:不用 @EqualsAndHashCode 哈  
12 -@EqualsAndHashCode(callSuper = true)  
13 @ToString(callSuper = true) 11 @ToString(callSuper = true)
14 public class AppKeFuMessagePageReqVO extends PageParam { 12 public class AppKeFuMessagePageReqVO extends PageParam {
15 13
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessageSendReqVO.java
@@ -12,30 +12,14 @@ public class AppKeFuMessageSendReqVO { @@ -12,30 +12,14 @@ public class AppKeFuMessageSendReqVO {
12 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23202") 12 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23202")
13 private Long id; 13 private Long id;
14 14
15 - @Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12580")  
16 - @NotNull(message = "会话编号不能为空")  
17 - private Long conversationId;  
18 -  
19 @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571") 15 @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571")
20 - @NotNull(message = "发送人编号不能为空")  
21 private Long senderId; 16 private Long senderId;
22 -  
23 @Schema(description = "发送人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 17 @Schema(description = "发送人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
24 - @NotNull(message = "发送人类型不能为空")  
25 private Integer senderType; 18 private Integer senderType;
26 19
27 - @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29124")  
28 - @NotNull(message = "接收人编号不能为空")  
29 - private Long receiverId;  
30 -  
31 - @Schema(description = "接收人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
32 - @NotNull(message = "接收人类型不能为空")  
33 - private Integer receiverType;  
34 -  
35 @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 20 @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
36 @NotNull(message = "消息类型不能为空") 21 @NotNull(message = "消息类型不能为空")
37 private Integer contentType; 22 private Integer contentType;
38 -  
39 @Schema(description = "消息", requiredMode = Schema.RequiredMode.REQUIRED) 23 @Schema(description = "消息", requiredMode = Schema.RequiredMode.REQUIRED)
40 @NotEmpty(message = "消息不能为空") 24 @NotEmpty(message = "消息不能为空")
41 private String content; 25 private String content;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java
@@ -16,24 +16,22 @@ import java.util.List; @@ -16,24 +16,22 @@ import java.util.List;
16 @Mapper 16 @Mapper
17 public interface KeFuConversationMapper extends BaseMapperX<KeFuConversationDO> { 17 public interface KeFuConversationMapper extends BaseMapperX<KeFuConversationDO> {
18 18
19 - // TODO @puhui999:排序可以交给前端,或者 controller;数据库的计算尽量少哈;  
20 - default List<KeFuConversationDO> selectListWithSort() { 19 + default List<KeFuConversationDO> selectConversationList() {
21 return selectList(new LambdaQueryWrapperX<KeFuConversationDO>() 20 return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()
22 .eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE) 21 .eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
23 - .orderByDesc(KeFuConversationDO::getAdminPinned) // 置顶优先  
24 .orderByDesc(KeFuConversationDO::getCreateTime)); 22 .orderByDesc(KeFuConversationDO::getCreateTime));
25 } 23 }
26 24
27 - // TODO @puhui999:是不是置零,用 update 就 ok 拉;然后单独搞个 +1 的方法;  
28 - default void updateAdminUnreadMessageCountByConversationId(Long id, Integer count) {  
29 - LambdaUpdateWrapper<KeFuConversationDO> updateWrapper = new LambdaUpdateWrapper<>();  
30 - updateWrapper.eq(KeFuConversationDO::getId, id);  
31 - if (count != null && count > 0) { // 情况一:会员发送消息时增加管理员的未读消息数  
32 - updateWrapper.setSql("admin_unread_message_count = admin_unread_message_count + 1");  
33 - } else { // 情况二:管理员已读后重置  
34 - updateWrapper.set(KeFuConversationDO::getAdminUnreadMessageCount, 0);  
35 - }  
36 - update(updateWrapper); 25 + default void updateAdminUnreadMessageCountWithZero(Long id) {
  26 + update(new LambdaUpdateWrapper<KeFuConversationDO>()
  27 + .eq(KeFuConversationDO::getId, id)
  28 + .set(KeFuConversationDO::getAdminUnreadMessageCount, 0));
  29 + }
  30 +
  31 + default void updateAdminUnreadMessageCount(Long id) {
  32 + update(new LambdaUpdateWrapper<KeFuConversationDO>()
  33 + .eq(KeFuConversationDO::getId, id)
  34 + .setSql("admin_unread_message_count = admin_unread_message_count + 1"));
37 } 35 }
38 36
39 } 37 }
40 \ No newline at end of file 38 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuMessageMapper.java
@@ -26,20 +26,15 @@ public interface KeFuMessageMapper extends BaseMapperX&lt;KeFuMessageDO&gt; { @@ -26,20 +26,15 @@ public interface KeFuMessageMapper extends BaseMapperX&lt;KeFuMessageDO&gt; {
26 .orderByDesc(KeFuMessageDO::getId)); 26 .orderByDesc(KeFuMessageDO::getId));
27 } 27 }
28 28
29 - default List<KeFuMessageDO> selectListByConversationIdAndReceiverIdAndReadStatus(Long conversationId,  
30 - Long receiverId,  
31 - Boolean readStatus) { 29 + default List<KeFuMessageDO> selectListByConversationIdAndReadStatus(Long conversationId, Boolean readStatus) {
32 return selectList(new LambdaQueryWrapper<KeFuMessageDO>() 30 return selectList(new LambdaQueryWrapper<KeFuMessageDO>()
33 .eq(KeFuMessageDO::getConversationId, conversationId) 31 .eq(KeFuMessageDO::getConversationId, conversationId)
34 - .eq(KeFuMessageDO::getReceiverId, receiverId)  
35 .eq(KeFuMessageDO::getReadStatus, readStatus)); 32 .eq(KeFuMessageDO::getReadStatus, readStatus));
36 } 33 }
37 34
38 - // TODO @puhui999:status 拼写不对哈;ps:是不是搞个 ids + entity 的更新,更通用点  
39 - default void updateReadStstusBatchByIds(Collection<Long> ids, Boolean readStatus) {  
40 - update(new LambdaUpdateWrapper<KeFuMessageDO>()  
41 - .in(KeFuMessageDO::getId, ids)  
42 - .set(KeFuMessageDO::getReadStatus, readStatus)); 35 + default void updateReadStatusBatchByIds(Collection<Long> ids, KeFuMessageDO keFuMessageDO) {
  36 + update(keFuMessageDO, new LambdaUpdateWrapper<KeFuMessageDO>()
  37 + .in(KeFuMessageDO::getId, ids));
43 } 38 }
44 39
45 } 40 }
46 \ No newline at end of file 41 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java
@@ -2,11 +2,10 @@ package cn.iocoder.yudao.module.promotion.service.kefu; @@ -2,11 +2,10 @@ package cn.iocoder.yudao.module.promotion.service.kefu;
2 2
3 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO; 3 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
4 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO; 4 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
  5 +import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
5 6
6 -import java.time.LocalDateTime;  
7 import java.util.List; 7 import java.util.List;
8 8
9 -// TODO @puhui999:可以在每个方法前面,加个【会员】【管理员】区分下  
10 /** 9 /**
11 * 客服会话 Service 接口 10 * 客服会话 Service 接口
12 * 11 *
@@ -15,48 +14,43 @@ import java.util.List; @@ -15,48 +14,43 @@ import java.util.List;
15 public interface KeFuConversationService { 14 public interface KeFuConversationService {
16 15
17 /** 16 /**
18 - * 删除客服会话 17 + * 【管理员】删除客服会话
19 * 18 *
20 * @param id 编号 19 * @param id 编号
21 */ 20 */
22 void deleteKefuConversation(Long id); 21 void deleteKefuConversation(Long id);
23 22
24 - // TODO @puhui999:是不是方法名,体现出更新的是管理员的置顶哈  
25 /** 23 /**
26 - * 客服会话置顶 24 + * 【管理员】客服会话置顶
27 * 25 *
28 * @param updateReqVO 请求 26 * @param updateReqVO 请求
29 */ 27 */
30 - void updatePinned(KeFuConversationUpdatePinnedReqVO updateReqVO); 28 + void updateAdminPinned(KeFuConversationUpdatePinnedReqVO updateReqVO);
31 29
32 - // TODO @puhui999:updateConversationLastMessage 会好点哈  
33 /** 30 /**
34 * 更新会话客服消息冗余信息 31 * 更新会话客服消息冗余信息
35 * 32 *
36 - * @param id 编号  
37 - * @param lastMessageTime 最后聊天时间  
38 - * @param lastMessageContent 最后聊天内容  
39 - * @param lastMessageContentType 最后聊天内容类型 33 + * @param kefuMessage 消息
40 */ 34 */
41 - void updateConversationMessage(Long id, LocalDateTime lastMessageTime, String lastMessageContent, Integer lastMessageContentType); 35 + void updateConversationLastMessage(KeFuMessageDO kefuMessage);
42 36
43 /** 37 /**
44 - * 更新管理员未读消息数 38 + * 【管理员】将管理员未读消息计数更新为零
45 * 39 *
46 - * @param id 编号  
47 - * @param count 数量:0 则重置 1 则消息数加一 40 + * @param id 编号
48 */ 41 */
49 - void updateAdminUnreadMessageCountByConversationId(Long id, Integer count); 42 + void updateAdminUnreadMessageCountWithZero(Long id);
50 43
51 /** 44 /**
52 - * 更新会话对于管理员是否可见 45 + * 【管理员】更新会话对于管理员是否可见
53 * 46 *
  47 + * @param id 编号
54 * @param adminDeleted 管理员是否可见 48 * @param adminDeleted 管理员是否可见
55 */ 49 */
56 void updateConversationAdminDeleted(Long id, Boolean adminDeleted); 50 void updateConversationAdminDeleted(Long id, Boolean adminDeleted);
57 51
58 /** 52 /**
59 - * 获得客服会话列表 53 + * 【管理员】获得客服会话列表
60 * 54 *
61 * @return 会话列表 55 * @return 会话列表
62 */ 56 */
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.kefu; 1 package cn.iocoder.yudao.module.promotion.service.kefu;
2 2
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
3 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
4 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO; 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
5 import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper; 8 import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper;
6 import cn.iocoder.yudao.module.promotion.enums.kehu.KeFuMessageContentTypeEnum; 9 import cn.iocoder.yudao.module.promotion.enums.kehu.KeFuMessageContentTypeEnum;
7 import jakarta.annotation.Resource; 10 import jakarta.annotation.Resource;
8 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
  12 +import org.springframework.transaction.annotation.Transactional;
9 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
10 14
11 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
@@ -36,19 +40,35 @@ public class KeFuConversationServiceImpl implements KeFuConversationService { @@ -36,19 +40,35 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
36 } 40 }
37 41
38 @Override 42 @Override
39 - public void updatePinned(KeFuConversationUpdatePinnedReqVO updateReqVO) { 43 + public void updateAdminPinned(KeFuConversationUpdatePinnedReqVO updateReqVO) {
40 conversationMapper.updateById(new KeFuConversationDO().setId(updateReqVO.getId()).setAdminPinned(updateReqVO.getAdminPinned())); 44 conversationMapper.updateById(new KeFuConversationDO().setId(updateReqVO.getId()).setAdminPinned(updateReqVO.getAdminPinned()));
41 } 45 }
42 46
43 @Override 47 @Override
44 - public void updateConversationMessage(Long id, LocalDateTime lastMessageTime, String lastMessageContent, Integer lastMessageContentType) {  
45 - conversationMapper.updateById(new KeFuConversationDO().setId(id).setLastMessageTime(lastMessageTime)  
46 - .setLastMessageContent(lastMessageContent).setLastMessageContentType(lastMessageContentType)); 48 + @Transactional(rollbackFor = Exception.class)
  49 + public void updateConversationLastMessage(KeFuMessageDO kefuMessage) {
  50 + // 1.1 校验会话是否存在
  51 + KeFuConversationDO conversation = validateKefuConversationExists(kefuMessage.getConversationId());
  52 + // 1.2 更新会话消息冗余
  53 + conversationMapper.updateById(new KeFuConversationDO().setId(kefuMessage.getConversationId())
  54 + .setLastMessageTime(kefuMessage.getCreateTime()).setLastMessageContent(kefuMessage.getContent())
  55 + .setLastMessageContentType(kefuMessage.getContentType()));
  56 +
  57 + // 2.2 更新管理员未读消息数
  58 + if (UserTypeEnum.MEMBER.getValue().equals(kefuMessage.getSenderType())) {
  59 + conversationMapper.updateAdminUnreadMessageCount(kefuMessage.getConversationId());
  60 + }
  61 + // 2.4 会员用户发送消息时,如果管理员删除过会话则进行恢复
  62 + if (UserTypeEnum.MEMBER.getValue().equals(kefuMessage.getSenderType())
  63 + && Boolean.TRUE.equals(conversation.getAdminDeleted())) {
  64 + updateConversationAdminDeleted(kefuMessage.getConversationId(), Boolean.FALSE);
  65 + }
47 } 66 }
48 67
49 @Override 68 @Override
50 - public void updateAdminUnreadMessageCountByConversationId(Long id, Integer count) {  
51 - conversationMapper.updateAdminUnreadMessageCountByConversationId(id, count); 69 + public void updateAdminUnreadMessageCountWithZero(Long id) {
  70 + validateKefuConversationExists(id);
  71 + conversationMapper.updateAdminUnreadMessageCountWithZero(id);
52 } 72 }
53 73
54 @Override 74 @Override
@@ -58,17 +78,16 @@ public class KeFuConversationServiceImpl implements KeFuConversationService { @@ -58,17 +78,16 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
58 78
59 @Override 79 @Override
60 public List<KeFuConversationDO> getKefuConversationList() { 80 public List<KeFuConversationDO> getKefuConversationList() {
61 - return conversationMapper.selectListWithSort(); 81 + return conversationMapper.selectConversationList();
62 } 82 }
63 83
64 - // TODO @puhui999:貌似这个对话,得用户主动创建。不然管理员会看到一个空的对话?  
65 @Override 84 @Override
66 public KeFuConversationDO getOrCreateConversation(Long userId) { 85 public KeFuConversationDO getOrCreateConversation(Long userId) {
67 KeFuConversationDO conversation = conversationMapper.selectOne(KeFuConversationDO::getUserId, userId); 86 KeFuConversationDO conversation = conversationMapper.selectOne(KeFuConversationDO::getUserId, userId);
68 // 没有历史会话,则初始化一个新会话 87 // 没有历史会话,则初始化一个新会话
69 if (conversation == null) { 88 if (conversation == null) {
70 conversation = new KeFuConversationDO().setUserId(userId).setLastMessageTime(LocalDateTime.now()) 89 conversation = new KeFuConversationDO().setUserId(userId).setLastMessageTime(LocalDateTime.now())
71 - .setLastMessageContent("").setLastMessageContentType(KeFuMessageContentTypeEnum.TEXT.getType()) 90 + .setLastMessageContent(StrUtil.EMPTY).setLastMessageContentType(KeFuMessageContentTypeEnum.TEXT.getType())
72 .setAdminPinned(Boolean.FALSE).setUserDeleted(Boolean.FALSE).setAdminDeleted(Boolean.FALSE) 91 .setAdminPinned(Boolean.FALSE).setUserDeleted(Boolean.FALSE).setAdminDeleted(Boolean.FALSE)
73 .setAdminUnreadMessageCount(0); 92 .setAdminUnreadMessageCount(0);
74 conversationMapper.insert(conversation); 93 conversationMapper.insert(conversation);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageService.java
@@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.promotion.service.kefu; @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.promotion.service.kefu;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO; 4 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
6 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
7 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
8 9
9 -// TODO @puhui999:可以在每个方法前面,加个【会员】【管理员】区分下  
10 /** 10 /**
11 * 客服消息 Service 接口 11 * 客服消息 Service 接口
12 * 12 *
@@ -15,7 +15,7 @@ import jakarta.validation.Valid; @@ -15,7 +15,7 @@ import jakarta.validation.Valid;
15 public interface KeFuMessageService { 15 public interface KeFuMessageService {
16 16
17 /** 17 /**
18 - * 发送客服消息 18 + * 【管理员】发送客服消息
19 * 19 *
20 * @param sendReqVO 信息 20 * @param sendReqVO 信息
21 * @return 编号 21 * @return 编号
@@ -23,12 +23,19 @@ public interface KeFuMessageService { @@ -23,12 +23,19 @@ public interface KeFuMessageService {
23 Long sendKefuMessage(@Valid KeFuMessageSendReqVO sendReqVO); 23 Long sendKefuMessage(@Valid KeFuMessageSendReqVO sendReqVO);
24 24
25 /** 25 /**
26 - * 更新消息已读状态 26 + * 【会员】发送客服消息
  27 + *
  28 + * @param sendReqVO 信息
  29 + * @return 编号
  30 + */
  31 + Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO);
  32 +
  33 + /**
  34 + * 【管理员】更新消息已读状态
27 * 35 *
28 * @param conversationId 会话编号 36 * @param conversationId 会话编号
29 - * @param receiverId 用户编号  
30 */ 37 */
31 - void updateKefuMessageReadStatus(Long conversationId, Long receiverId); 38 + void updateKefuMessageReadStatus(Long conversationId);
32 39
33 /** 40 /**
34 * 获得客服消息分页 41 * 获得客服消息分页
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.kefu; 1 package cn.iocoder.yudao.module.promotion.service.kefu;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 5 import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; @@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
9 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 10 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
10 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO; 11 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO;
11 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO; 12 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
  13 +import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
12 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO; 14 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
13 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; 15 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
14 import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuMessageMapper; 16 import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuMessageMapper;
@@ -19,11 +21,11 @@ import org.springframework.stereotype.Service; @@ -19,11 +21,11 @@ import org.springframework.stereotype.Service;
19 import org.springframework.transaction.annotation.Transactional; 21 import org.springframework.transaction.annotation.Transactional;
20 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
21 23
22 -import java.time.LocalDateTime;  
23 import java.util.List; 24 import java.util.List;
24 25
25 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;  
26 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getFirst; 26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  27 +import static cn.iocoder.yudao.module.promotion.enums.WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ;
  28 +import static cn.iocoder.yudao.module.promotion.enums.WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE;
27 29
28 /** 30 /**
29 * 客服消息 Service 实现类 31 * 客服消息 Service 实现类
@@ -34,16 +36,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -34,16 +36,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
34 @Validated 36 @Validated
35 public class KeFuMessageServiceImpl implements KeFuMessageService { 37 public class KeFuMessageServiceImpl implements KeFuMessageService {
36 38
37 - // TODO @puhui999:@芋艿:捉摸要不要拿到一个地方枚举;  
38 - private static final String KEFU_MESSAGE_TYPE = "kefu_message_type"; // 客服消息类型  
39 -  
40 - // TODO @puhui999:kefuMessageMapper;因为 messageMapper 可能会重叠  
41 @Resource 39 @Resource
42 - private KeFuMessageMapper messageMapper;  
43 - 40 + private KeFuMessageMapper keFuMessageMapper;
44 @Resource 41 @Resource
45 private KeFuConversationService conversationService; 42 private KeFuConversationService conversationService;
46 -  
47 @Resource 43 @Resource
48 private AdminUserApi adminUserApi; 44 private AdminUserApi adminUserApi;
49 @Resource 45 @Resource
@@ -55,26 +51,15 @@ public class KeFuMessageServiceImpl implements KeFuMessageService { @@ -55,26 +51,15 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
55 @Transactional(rollbackFor = Exception.class) 51 @Transactional(rollbackFor = Exception.class)
56 public Long sendKefuMessage(KeFuMessageSendReqVO sendReqVO) { 52 public Long sendKefuMessage(KeFuMessageSendReqVO sendReqVO) {
57 // 1.1 校验会话是否存在 53 // 1.1 校验会话是否存在
58 - KeFuConversationDO conversation = conversationService.validateKefuConversationExists(sendReqVO.getConversationId()); 54 + conversationService.validateKefuConversationExists(sendReqVO.getConversationId());
59 // 1.2 校验接收人是否存在 55 // 1.2 校验接收人是否存在
60 validateReceiverExist(sendReqVO.getReceiverId(), sendReqVO.getReceiverType()); 56 validateReceiverExist(sendReqVO.getReceiverId(), sendReqVO.getReceiverType());
61 57
62 // 2.1 保存消息 58 // 2.1 保存消息
63 KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class); 59 KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class);
64 - messageMapper.insert(kefuMessage);  
65 - // TODO @puhui999:是不是 updateConversationMessage,里面统一处理未读、恢复;直接设置 KeFuMessageDO 作为参数好了。。。 60 + keFuMessageMapper.insert(kefuMessage);
66 // 2.2 更新会话消息冗余 61 // 2.2 更新会话消息冗余
67 - conversationService.updateConversationMessage(kefuMessage.getConversationId(), LocalDateTime.now(),  
68 - kefuMessage.getContent(), kefuMessage.getContentType());  
69 - // 2.3 更新管理员未读消息数  
70 - if (UserTypeEnum.ADMIN.getValue().equals(kefuMessage.getReceiverType())) {  
71 - conversationService.updateAdminUnreadMessageCountByConversationId(kefuMessage.getConversationId(), 1);  
72 - }  
73 - // 2.4 会员用户发送消息时,如果管理员删除过会话则进行恢复  
74 - // TODO @puhui999:建议 && 换一行  
75 - if (UserTypeEnum.MEMBER.getValue().equals(kefuMessage.getSenderType()) && Boolean.TRUE.equals(conversation.getAdminDeleted())) {  
76 - conversationService.updateConversationAdminDeleted(kefuMessage.getConversationId(), Boolean.FALSE);  
77 - } 62 + conversationService.updateConversationLastMessage(kefuMessage);
78 63
79 // 3. 发送消息 64 // 3. 发送消息
80 getSelf().sendAsyncMessage(sendReqVO.getReceiverType(), sendReqVO.getReceiverId(), kefuMessage); 65 getSelf().sendAsyncMessage(sendReqVO.getReceiverType(), sendReqVO.getReceiverId(), kefuMessage);
@@ -82,31 +67,44 @@ public class KeFuMessageServiceImpl implements KeFuMessageService { @@ -82,31 +67,44 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
82 } 67 }
83 68
84 @Override 69 @Override
  70 + public Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO) {
  71 + // 1.1 设置会话编号
  72 + KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class);
  73 + KeFuConversationDO conversation = conversationService.getOrCreateConversation(sendReqVO.getSenderId());
  74 + kefuMessage.setConversationId(conversation.getId());
  75 + // 1.2 保存消息
  76 + keFuMessageMapper.insert(kefuMessage);
  77 +
  78 + // 2. 更新会话消息冗余
  79 + conversationService.updateConversationLastMessage(kefuMessage);
  80 + // 3. 发送消息
  81 + getSelf().sendAsyncMessageToAdmin(kefuMessage);
  82 + return kefuMessage.getId();
  83 + }
  84 +
  85 + @Override
85 @Transactional(rollbackFor = Exception.class) 86 @Transactional(rollbackFor = Exception.class)
86 - public void updateKefuMessageReadStatus(Long conversationId, Long receiverId) { 87 + public void updateKefuMessageReadStatus(Long conversationId) {
87 // 1.1 校验会话是否存在 88 // 1.1 校验会话是否存在
88 conversationService.validateKefuConversationExists(conversationId); 89 conversationService.validateKefuConversationExists(conversationId);
89 - // 1.2 查询接收人所有的未读消息  
90 - // TODO @puhui999:应该不能 receiverId 过滤哈。因为多个客服,一个人点了,就都点了。  
91 - List<KeFuMessageDO> messageList = messageMapper.selectListByConversationIdAndReceiverIdAndReadStatus(  
92 - conversationId, receiverId, Boolean.FALSE); 90 + // 1.2 查询会话所有的未读消息 (tips: 多个客服,一个人点了,就都点了)
  91 + List<KeFuMessageDO> messageList = keFuMessageMapper.selectListByConversationIdAndReadStatus(conversationId, Boolean.FALSE);
93 // 1.3 情况一:没有未读消息 92 // 1.3 情况一:没有未读消息
94 if (CollUtil.isEmpty(messageList)) { 93 if (CollUtil.isEmpty(messageList)) {
95 return; 94 return;
96 } 95 }
97 96
98 // 2.1 情况二:更新未读消息状态为已读 97 // 2.1 情况二:更新未读消息状态为已读
99 - messageMapper.updateReadStstusBatchByIds(convertSet(messageList, KeFuMessageDO::getId), Boolean.TRUE);  
100 - // 2.2 更新管理员未读消息数  
101 - KeFuMessageDO message = getFirst(messageList);  
102 - assert message != null;  
103 - if (UserTypeEnum.ADMIN.getValue().equals(message.getReceiverType())) {  
104 - conversationService.updateAdminUnreadMessageCountByConversationId(conversationId, 0);  
105 - } 98 + keFuMessageMapper.updateReadStatusBatchByIds(convertSet(messageList, KeFuMessageDO::getId),
  99 + new KeFuMessageDO().setReadStatus(Boolean.TRUE));
  100 + // 2.2 将管理员未读消息计数更新为零
  101 + conversationService.updateAdminUnreadMessageCountWithZero(conversationId);
106 102
107 - // 2.3 发送消息通知发送者,接收者已读 -> 发送者更新发送的消息状态 103 + // 2.3 发送消息通知会员,管理员已读 -> 会员更新发送的消息状态
108 // TODO @puhui999:待定~ 104 // TODO @puhui999:待定~
109 - getSelf().sendAsyncMessage(message.getSenderType(), message.getSenderId(), "keFuMessageReadStatusChange"); 105 + KeFuMessageDO keFuMessage = getFirst(filterList(messageList, message -> UserTypeEnum.MEMBER.getValue().equals(message.getSenderType())));
  106 + assert keFuMessage != null; // 断言避免警告
  107 + webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), keFuMessage.getSenderId(), KEFU_MESSAGE_ADMIN_READ, StrUtil.EMPTY);
110 } 108 }
111 109
112 private void validateReceiverExist(Long receiverId, Integer receiverType) { 110 private void validateReceiverExist(Long receiverId, Integer receiverType) {
@@ -123,9 +121,14 @@ public class KeFuMessageServiceImpl implements KeFuMessageService { @@ -123,9 +121,14 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
123 webSocketSenderApi.sendObject(userType, userId, KEFU_MESSAGE_TYPE, content); 121 webSocketSenderApi.sendObject(userType, userId, KEFU_MESSAGE_TYPE, content);
124 } 122 }
125 123
  124 + @Async
  125 + public void sendAsyncMessageToAdmin(Object content) {
  126 + webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), KEFU_MESSAGE_TYPE, content);
  127 + }
  128 +
126 @Override 129 @Override
127 public PageResult<KeFuMessageDO> getKefuMessagePage(KeFuMessagePageReqVO pageReqVO) { 130 public PageResult<KeFuMessageDO> getKefuMessagePage(KeFuMessagePageReqVO pageReqVO) {
128 - return messageMapper.selectPage(pageReqVO); 131 + return keFuMessageMapper.selectPage(pageReqVO);
129 } 132 }
130 133
131 private KeFuMessageServiceImpl getSelf() { 134 private KeFuMessageServiceImpl getSelf() {
yudao-server/src/main/resources/application.yaml
@@ -156,7 +156,7 @@ yudao: @@ -156,7 +156,7 @@ yudao:
156 - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录 156 - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录
157 websocket: 157 websocket:
158 enable: true # websocket的开关 158 enable: true # websocket的开关
159 - path: /infra/ws # 路径 159 + path: /infra/ws/ # 路径 // TODO @puhui999: 小程序 socket.io 连接会多一个 / 🤣🤣🤣
160 sender-type: local # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq 160 sender-type: local # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq
161 sender-rocketmq: 161 sender-rocketmq:
162 topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic 162 topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic