Prechádzať zdrojové kódy

聊天机器人修改

zrd 3 mesiacov pred
rodič
commit
91ea7e1974
16 zmenil súbory, kde vykonal 345 pridanie a 19 odobranie
  1. 9 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java
  2. 70 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuConversationController.java
  3. 7 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuMessageController.java
  4. 2 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessageSendReqVO.java
  5. 5 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java
  6. 32 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java
  7. 67 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/job/kefu/KefuMessageJob.java
  8. 23 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/coupon/CouponTakeByRegisterConsumer.java
  9. 9 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java
  10. 72 8
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java
  11. 2 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageService.java
  12. 31 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageServiceImpl.java
  13. 2 0
      yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java
  14. 6 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java
  15. 2 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
  16. 6 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java

+ 9 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java

@@ -14,11 +14,16 @@ public class KeFuConversationRespVO {
 
     @Schema(description = "会话所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "8300")
     private Long userId;
+    private Long relUserId;
     @Schema(description = "会话所属用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://yudao.com/images/avatar.jpg")
     private String userAvatar;
     @Schema(description = "会话所属用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
     private String userNickname;
-
+    @Schema(description = "会话所属用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://yudao" +
+            ".com/images/avatar.jpg")
+    private String relUserAvatar;
+    @Schema(description = "会话所属用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+    private String relUserNickname;
     @Schema(description = "最后聊天时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime lastMessageTime;
 
@@ -42,5 +47,7 @@ public class KeFuConversationRespVO {
 
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
-
+    private Integer relUnreadMessageCount;
+    private LocalDateTime relLastMessageTime;
+    private String relLastMessageContent;
 }

+ 70 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuConversationController.java

@@ -1,13 +1,17 @@
 package cn.iocoder.yudao.module.promotion.controller.app.kefu;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
+import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
 import cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService;
+import cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -16,6 +20,7 @@ import jakarta.validation.Valid;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -33,7 +38,8 @@ public class AppKeFuConversationController {
     private KeFuConversationService conversationService;
     @Resource
     private MemberUserApi memberUserApi;
-    
+    @Resource
+    private KeFuMessageService kefuMessageService;
     @GetMapping("/get")
     @Operation(summary = "获得客服会话")
     @Parameter(name = "id", description = "编号", required = true, example = "1024")
@@ -52,6 +58,16 @@ public class AppKeFuConversationController {
         return success(result);
     }
     
+    @GetMapping("/init")
+    @Operation(summary = "获得客服会话")
+    public CommonResult<String> init() {
+        init("欢迎!", 1L);
+        init("欢迎1!", 2L);
+        init("欢迎2!", 3L);
+        init("欢迎3!", 4L);
+        return success("1");
+    }
+    
     @PutMapping("/update-conversation-pinned")
     @Operation(summary = "置顶/取消置顶客服会话")
     public CommonResult<Boolean> updateConversationPinned(@Valid @RequestBody KeFuConversationUpdatePinnedReqVO updateReqVO) {
@@ -82,4 +98,57 @@ public class AppKeFuConversationController {
         return success(respList);
     }
     
+    private void init(String conteng, Long userId) {
+        KeFuConversationRespVO keFuConversationRespVO = new KeFuConversationRespVO();
+        List<MemberUserRespDTO> allUser = memberUserApi.getUserAll();
+        
+        Collection<Long> uids = convertSet(allUser,
+                MemberUserRespDTO::getId);
+        
+        keFuConversationRespVO.setUserId(userId);
+        List<KeFuConversationDO> conversationDOList = conversationService.getList(keFuConversationRespVO);
+        Collection<Long> relids = convertSet(conversationDOList,
+                KeFuConversationDO::getRelUserId);
+        
+        uids.removeAll(relids);
+        if (CollUtil.isNotEmpty(uids)) {
+            
+            //查询消息  并发送给ai
+            for (Long id : uids) {
+                if (id > 4) {
+                    AppKeFuMessageSendReqVO sendReqVO = new AppKeFuMessageSendReqVO();
+                    sendReqVO.setSenderId(userId).setSenderType(UserTypeEnum.ADMIN.getValue()); //
+                    // 设置用户编号和类型
+                    sendReqVO.setContent("{\"text\":\"" + conteng + "\"}");
+                    sendReqVO.setContentType(1);
+                    sendReqVO.setRelUserId(id);
+                    kefuMessageService.sendMessage(sendReqVO);
+                }
+                
+                
+            }
+        }
+    }
+    
+    @GetMapping("/listNew")
+    @Operation(summary = "获得客服会话列表新")
+    public CommonResult<List<KeFuConversationRespVO>> getConversationListNew() {
+        // 查询会话列表
+        List<KeFuConversationRespVO> respList = BeanUtils.toBean(conversationService.getConversationList(),
+                KeFuConversationRespVO.class);
+        
+        // 拼接数据
+        Collection<Long> ids = convertSet(respList,
+                KeFuConversationRespVO::getUserId);
+        Collection<Long> relids = convertSet(respList,
+                KeFuConversationRespVO::getRelUserId);
+        ids.addAll(relids);
+        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(ids);
+        respList.forEach(item -> findAndThen(userMap, item.getUserId(),
+                memberUser -> item.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname())));
+        respList.forEach(item -> findAndThen(userMap, item.getRelUserId(),
+                memberUser -> item.setRelUserAvatar(memberUser.getAvatar()).setRelUserNickname(memberUser.getNickname())));
+        return success(respList);
+    }
+    
 }

+ 7 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuMessageController.java

@@ -45,6 +45,13 @@ public class AppKeFuMessageController {
         sendReqVO.setSenderId(getLoginUserId()).setSenderType(UserTypeEnum.MEMBER.getValue()); // 设置用户编号和类型
         return success(kefuMessageService.sendKefuMessage(sendReqVO));
     }
+    
+    @PostMapping("/sendNew")
+    @Operation(summary = "发送消息")
+    public CommonResult<Long> sendNew(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) {
+        sendReqVO.setSenderId(getLoginUserId()).setSenderType(UserTypeEnum.MEMBER.getValue()); // 设置用户编号和类型
+        return success(kefuMessageService.sendMessage(sendReqVO));
+    }
 
     @PutMapping("/update-read-status")
     @Operation(summary = "更新客服消息已读状态")

+ 2 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessageSendReqVO.java

@@ -20,10 +20,11 @@ public class AppKeFuMessageSendReqVO {
 
     @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571", hidden = true)
     private Long senderId;
-    @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571", hidden = true)
+    @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571")
     private Long relUserId;
     
     @Schema(description = "发送人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1", hidden = true)
     private Integer senderType;
+    @Schema(description = "会话id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1", hidden = true)
     private Long conversationId;
 }

+ 5 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java

@@ -45,10 +45,12 @@ public class KeFuConversationDO extends BaseDO {
      * 最后聊天时间
      */
     private LocalDateTime lastMessageTime;
+
     /**
      * 最后聊天内容
      */
     private String lastMessageContent;
+    
     /**
      * 最后发送的消息类型
      *
@@ -83,5 +85,7 @@ public class KeFuConversationDO extends BaseDO {
      * 用户发送消息时增加,管理员查看后扣减
      */
     private Integer adminUnreadMessageCount;
-
+    private Integer relUnreadMessageCount;
+    private LocalDateTime relLastMessageTime;
+    private String relLastMessageContent;
 }

+ 32 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java

@@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.kefu;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
@@ -33,6 +36,29 @@ public interface KeFuConversationMapper extends BaseMapperX<KeFuConversationDO>
                 .ge(KeFuConversationDO::getAdminUnreadMessageCount, 1)
                 .le(KeFuConversationDO::getLastMessageTime, tenMinutesAgo)
                 .orderByDesc(KeFuConversationDO::getCreateTime));
+        
+        
+    }
+    
+    default List<KeFuConversationDO> getList(KeFuConversationRespVO reqVO) {
+        //判断getLastMessageTime跟当前时间 比差10分钟以上
+        LocalDateTime now = LocalDateTime.now();
+        return selectList(new MPJLambdaWrapperX<KeFuConversationDO>()
+                .selectAll(KeFuConversationDO.class)
+                
+                .eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
+                .apply("t.rel_user_id>4")
+                .apply("t.id is null")
+                .orderByDesc(KeFuConversationDO::getCreateTime));
+    }
+    
+    default List<KeFuConversationDO> getConversationList() {
+        //判断getLastMessageTime跟当前时间 比差10分钟以上
+        return selectList(new LambdaQueryWrapperX<KeFuConversationDO>()
+                .eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
+                .eq(KeFuConversationDO::getUserId, SecurityFrameworkUtils.getLoginUserId())
+                .or().eq(KeFuConversationDO::getRelUserId, SecurityFrameworkUtils.getLoginUserId())
+                .orderByDesc(KeFuConversationDO::getCreateTime));
     }
 
     default void updateAdminUnreadMessageCountIncrement(Long id) {
@@ -40,6 +66,12 @@ public interface KeFuConversationMapper extends BaseMapperX<KeFuConversationDO>
                 .eq(KeFuConversationDO::getId, id)
                 .setSql("admin_unread_message_count = admin_unread_message_count + 1"));
     }
+    
+    default void updateRelUnreadMessageCountIncrement(Long id) {
+        update(new LambdaUpdateWrapper<KeFuConversationDO>()
+                .eq(KeFuConversationDO::getId, id)
+                .setSql("rel_unread_message_count = rel_unread_message_count + 1"));
+    }
 
     default KeFuConversationDO selectByUserId(Long userId) {
         return selectOne(KeFuConversationDO::getUserId, userId);

+ 67 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/job/kefu/KefuMessageJob.java

@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.promotion.job.kefu;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.infra.api.ai.AiApi;
+import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
+import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
+import cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService;
+import cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 物理删除 N 天前的任务日志的 Job
+ *
+ * @author j-sentinel
+ */
+@Slf4j
+@Component
+public class KefuMessageJob implements JobHandler {
+    
+    @Resource
+    private AiApi aiApi;
+    @Resource
+    private KeFuConversationService keFuConversationService;
+    @Resource
+    private KeFuMessageService kefuMessageService;
+    
+    @Override
+    @TenantIgnore
+    public String execute(String param) {
+        //查询有未读消息的会话
+        init("欢迎!", 1L);
+        init("欢迎1!", 2L);
+        init("欢迎2!", 3L);
+        init("欢迎3!", 4L);
+        return String.format("定时执行清理定时任务日志数量 %s 个", 1);
+    }
+    
+    private void init(String conteng, Long userId) {
+        KeFuConversationRespVO keFuConversationRespVO = new KeFuConversationRespVO();
+        keFuConversationRespVO.setUserId(userId);
+        List<KeFuConversationDO> conversationDOList = keFuConversationService.getList(keFuConversationRespVO);
+        if (CollUtil.isNotEmpty(conversationDOList)) {
+            //查询消息  并发送给ai
+            for (KeFuConversationDO conversationDO : conversationDOList) {
+                
+                
+                AppKeFuMessageSendReqVO sendReqVO = new AppKeFuMessageSendReqVO();
+                sendReqVO.setSenderId(userId).setSenderType(UserTypeEnum.ADMIN.getValue()); //
+                // 设置用户编号和类型
+                sendReqVO.setConversationId(conversationDO.getId());
+                sendReqVO.setContent("{\"text\":\"" + conteng + "\"}");
+                sendReqVO.setContentType(1);
+//                sendReqVO.setRelUserId(conversationDO.getUid());
+                kefuMessageService.sendMessage(sendReqVO);
+            }
+        }
+    }
+    
+}

+ 23 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/coupon/CouponTakeByRegisterConsumer.java

@@ -1,14 +1,16 @@
 package cn.iocoder.yudao.module.promotion.mq.consumer.coupon;
 
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.module.member.message.user.MemberUserCreateMessage;
+import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
+import cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
-import jakarta.annotation.Resource;
-
 /**
  * 用户注册时,发送优惠劵的消费者,基 {@link MemberUserCreateMessage} 消息
  *
@@ -20,12 +22,30 @@ public class CouponTakeByRegisterConsumer {
 
     @Resource
     private CouponService couponService;
-
+    @Resource
+    private KeFuMessageService kefuMessageService;
     @EventListener
     @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步
     public void onMessage(MemberUserCreateMessage message) {
         log.info("[onMessage][消息内容({})]", message);
         couponService.takeCouponByRegister(message.getUserId());
+        //管理员发送消息
+        sendMessage("{\"text\":\"欢迎加入\"}", 1L, message.getUserId());
+        sendMessage("{\"text\":\"我是贷款专家\"}", 2L, message.getUserId());
+        sendMessage("{\"text\":\"律师相关\"}", 3L, message.getUserId());
+        sendMessage("{\"text\":\"其他业务\"}", 4L, message.getUserId());
+    }
+    
+    
+    private void sendMessage(String content, Long userId, Long relUserId) {
+        AppKeFuMessageSendReqVO sendReqVO = new AppKeFuMessageSendReqVO();
+        sendReqVO.setSenderType(UserTypeEnum.MEMBER.getValue());
+        // 设置用户编号和类型
+        sendReqVO.setContent(content);
+        sendReqVO.setContentType(1);
+        sendReqVO.setRelUserId(relUserId);
+        sendReqVO.setSenderId(userId);
+        kefuMessageService.sendMessage(sendReqVO);
     }
 
 }

+ 9 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.promotion.service.kefu;
 
+import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
@@ -48,6 +49,8 @@ public interface KeFuConversationService {
      * @param id 编号
      */
     void updateAdminUnreadMessageCountToZero(Long id);
+    
+    void updateRelUnreadMessageCountToZero(Long id);
 
     /**
      * 【管理员】更新会话对于管理员是否可见
@@ -65,6 +68,10 @@ public interface KeFuConversationService {
     List<KeFuConversationDO> getKefuConversationList();
     
     List<KeFuConversationDO> getKefuConversationAiList();
+    
+    List<KeFuConversationDO> getConversationList();
+    
+    List<KeFuConversationDO> getList(KeFuConversationRespVO reqVO);
 
     /**
      * 【会员】获得或创建会话
@@ -75,6 +82,8 @@ public interface KeFuConversationService {
      * @return 客服会话
      */
     KeFuConversationDO getOrCreateConversation(Long userId);
+    
+    KeFuConversationDO getOrCreateConversation(Long userId, Long relUserId);
 
     /**
      * 校验客服会话是否存在

+ 72 - 8
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java

@@ -1,7 +1,9 @@
 package cn.iocoder.yudao.module.promotion.service.kefu;
 
 import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
@@ -14,6 +16,7 @@ import org.springframework.validation.annotation.Validated;
 
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.KEFU_CONVERSATION_NOT_EXISTS;
@@ -59,14 +62,23 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
         // 1.1 校验会话是否存在
         KeFuConversationDO conversation = validateKefuConversationExists(kefuMessage.getConversationId());
         // 1.2 更新会话消息冗余
-        conversationMapper.updateById(new KeFuConversationDO().setId(kefuMessage.getConversationId())
-                .setLastMessageTime(kefuMessage.getCreateTime()).setLastMessageContent(kefuMessage.getContent())
-                .setLastMessageContentType(kefuMessage.getContentType()));
-
-        // 2.1 更新管理员未读消息数
-        if (UserTypeEnum.MEMBER.getValue().equals(kefuMessage.getSenderType())) {
+        //如果发送人为登录账号
+        if (Objects.equals(kefuMessage.getSenderId(), SecurityFrameworkUtils.getLoginUserId())) {
+            conversationMapper.updateById(new KeFuConversationDO().setId(kefuMessage.getConversationId())
+                    .setLastMessageTime(kefuMessage.getCreateTime()).
+                    setLastMessageContent(kefuMessage.getContent())
+                    .setLastMessageContentType(kefuMessage.getContentType()));
+            
             conversationMapper.updateAdminUnreadMessageCountIncrement(kefuMessage.getConversationId());
         }
+        if (Objects.equals(kefuMessage.getReceiverId(), SecurityFrameworkUtils.getLoginUserId())) {
+            conversationMapper.updateById(new KeFuConversationDO().setId(kefuMessage.getConversationId())
+                    .setRelLastMessageTime(kefuMessage.getCreateTime()).setRelLastMessageContent(kefuMessage.getContent())
+                    .setLastMessageContentType(kefuMessage.getContentType()));
+            
+            conversationMapper.updateRelUnreadMessageCountIncrement(kefuMessage.getConversationId());
+        }
+        
         // 2.2 会员用户发送消息时,如果管理员删除过会话则进行恢复
         if (Boolean.TRUE.equals(conversation.getAdminDeleted())) {
             updateConversationAdminDeleted(kefuMessage.getConversationId(), Boolean.FALSE);
@@ -81,6 +93,15 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
         // 管理员未读消息数归零
         conversationMapper.updateById(new KeFuConversationDO().setId(id).setAdminUnreadMessageCount(0));
     }
+    
+    @Override
+    public void updateRelUnreadMessageCountToZero(Long id) {
+        // 校验存在
+        validateKefuConversationExists(id);
+        
+        // 管理员未读消息数归零
+        conversationMapper.updateById(new KeFuConversationDO().setId(id).setRelUnreadMessageCount(0));
+    }
 
     @Override
     public void updateConversationAdminDeleted(Long id, Boolean adminDeleted) {
@@ -96,10 +117,21 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
     public List<KeFuConversationDO> getKefuConversationAiList() {
         return conversationMapper.selectConversationAiList();
     }
-
+    
+    @Override
+    public List<KeFuConversationDO> getConversationList() {
+        return conversationMapper.getConversationList();
+    }
+    
+    @Override
+    public List<KeFuConversationDO> getList(KeFuConversationRespVO reqVO) {
+        return conversationMapper.getList(reqVO);
+    }
+    
     @Override
     public KeFuConversationDO getOrCreateConversation(Long userId) {
         KeFuConversationDO conversation = conversationMapper.selectOne(KeFuConversationDO::getUserId, userId);
+
         // 没有历史会话,则初始化一个新会话
         if (conversation == null) {
             conversation = new KeFuConversationDO().setUserId(userId).setLastMessageTime(LocalDateTime.now())
@@ -110,6 +142,38 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
         }
         return conversation;
     }
+    
+    @Override
+    public KeFuConversationDO getOrCreateConversation(Long userId, Long relUserId) {
+        // 尝试获取用户和关联用户之间的会话
+        KeFuConversationDO conversation = conversationMapper.selectOne(new LambdaQueryWrapperX<KeFuConversationDO>()
+                .eq(KeFuConversationDO::getUserId, userId)
+                .eq(KeFuConversationDO::getRelUserId, relUserId));
+        
+        // 如果没有找到,则尝试获取关联用户和用户之间的会话
+        if (conversation == null) {
+            conversation = conversationMapper.selectOne(new LambdaQueryWrapperX<KeFuConversationDO>()
+                    .eq(KeFuConversationDO::getUserId, relUserId)
+                    .eq(KeFuConversationDO::getRelUserId, userId));
+        }
+        
+        // 如果仍然没有找到,则创建一个新的会话
+        if (conversation == null) {
+            conversation = new KeFuConversationDO()
+                    .setUserId(userId)
+                    .setRelUserId(relUserId)
+                    .setLastMessageTime(LocalDateTime.now())
+                    .setLastMessageContent(StrUtil.EMPTY)
+                    .setLastMessageContentType(KeFuMessageContentTypeEnum.TEXT.getType())
+                    .setAdminPinned(Boolean.FALSE)
+                    .setUserDeleted(Boolean.FALSE)
+                    .setAdminDeleted(Boolean.FALSE)
+                    .setAdminUnreadMessageCount(0);
+            conversationMapper.insert(conversation);
+        }
+        
+        return conversation;
+    }
 
     @Override
     public KeFuConversationDO validateKefuConversationExists(Long id) {

+ 2 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageService.java

@@ -31,6 +31,8 @@ public interface KeFuMessageService {
      * @return 编号
      */
     Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO);
+    
+    Long sendMessage(AppKeFuMessageSendReqVO sendReqVO);
 
     /**
      * 【管理员】更新消息已读状态

+ 31 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
@@ -97,6 +98,29 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
         getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_TYPE, message);
         return kefuMessage.getId();
     }
+    
+    @Override
+    public Long sendMessage(AppKeFuMessageSendReqVO sendReqVO) {
+        // 1.1 设置会话编号
+        KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class);
+        if (ObjUtil.isNull(kefuMessage.getConversationId())) {
+            KeFuConversationDO conversation = conversationService.getOrCreateConversation(sendReqVO.getSenderId(),
+                    sendReqVO.getRelUserId());
+            kefuMessage.setConversationId(conversation.getId());
+        }
+        kefuMessage.setReceiverId(sendReqVO.getRelUserId());
+        // 1.2 保存消息
+        keFuMessageMapper.insert(kefuMessage);
+        
+        // 2. 更新会话消息冗余
+        conversationService.updateConversationLastMessage(kefuMessage);
+        // 3. 通知所有管理员更新对话
+        MemberUserRespDTO user = memberUserApi.getUser(kefuMessage.getSenderId());
+        KeFuMessageRespVO message =
+                BeanUtils.toBean(kefuMessage, KeFuMessageRespVO.class).setSenderAvatar(user.getAvatar());
+        getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_TYPE, message);
+        return kefuMessage.getId();
+    }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -112,12 +136,17 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
         if (CollUtil.isEmpty(messageList)) {
             return;
         }
-
+        
         // 2.1 情况二:更新未读消息状态为已读
         keFuMessageMapper.updateReadStatusBatchByIds(convertSet(messageList, KeFuMessageDO::getId),
                 new KeFuMessageDO().setReadStatus(Boolean.TRUE));
         // 2.2 将管理员未读消息计数更新为零
-        conversationService.updateAdminUnreadMessageCountToZero(conversationId);
+        if (SecurityFrameworkUtils.getLoginUserId().equals(conversation.getUserId())) {
+            conversationService.updateAdminUnreadMessageCountToZero(conversationId);
+        }
+        if (SecurityFrameworkUtils.getLoginUserId().equals(conversation.getRelUserId())) {
+            conversationService.updateRelUnreadMessageCountToZero(conversationId);
+        }
 
         // 2.3 发送消息通知会员,管理员已读 -> 会员更新发送的消息状态
         KeFuMessageDO keFuMessage = getFirst(filterList(messageList, message -> UserTypeEnum.MEMBER.getValue().equals(message.getSenderType())));

+ 2 - 0
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java

@@ -30,6 +30,8 @@ public interface MemberUserApi {
      * @return 用户信息们
      */
     List<MemberUserRespDTO> getUserList(Collection<Long> ids);
+    
+    List<MemberUserRespDTO> getUserAll();
 
     /**
      * 获得会员用户 Map

+ 6 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java

@@ -4,10 +4,10 @@ import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import jakarta.annotation.Resource;
 import java.util.Collection;
 import java.util.List;
 
@@ -36,6 +36,11 @@ public class MemberUserApiImpl implements MemberUserApi {
     public List<MemberUserRespDTO> getUserList(Collection<Long> ids) {
         return MemberUserConvert.INSTANCE.convertList2(userService.getUserList(ids));
     }
+    
+    @Override
+    public List<MemberUserRespDTO> getUserAll() {
+        return MemberUserConvert.INSTANCE.convertList2(userService.getUserAll());
+    }
 
     @Override
     public List<MemberUserRespDTO> getUserListByNickname(String nickname) {

+ 2 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java

@@ -83,6 +83,8 @@ public interface MemberUserService {
      * @return 用户对象信息数组
      */
     List<MemberUserDO> getUserList(Collection<Long> ids);
+    
+    List<MemberUserDO> getUserAll();
 
     /**
      * 【会员】修改基本信息

+ 6 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java

@@ -156,6 +156,12 @@ public class MemberUserServiceImpl implements MemberUserService {
         }
         return memberUserMapper.selectBatchIds(ids);
     }
+    
+    @Override
+    public List<MemberUserDO> getUserAll() {
+        
+        return memberUserMapper.selectList();
+    }
 
     @Override
     public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) {