Преглед на файлове

Merge remote-tracking branch 'origin/sit' into sit

李亚非 преди 2 месеца
родител
ревизия
d56ab433ac

+ 1 - 0
alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivityAchievementMapper.xml

@@ -17,6 +17,7 @@
                 ELSE act.status
             END AS activityStatus,
             u.user_name AS nickName,
+            u.user_image AS userImage,
             su.head_img AS storeUserHeadImg,
             su.nick_name AS storeUserNickName,
             SUBSTRING_INDEX(ach.media_urls, ',', 1) AS firstMediaUrl,

+ 137 - 14
alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackServiceImpl.java

@@ -45,6 +45,7 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
     private final WebSocketProcess webSocketProcess;
     private final AiFeedbackAssignUtils aiFeedbackAssignUtils;
     private final LifeSysMapper lifeSysMapper;
+    private final LifeUserMapper lifeUserMapper;
 
     @Override
     public R<String> submitFeedback(LifeFeedbackDto dto) {
@@ -606,10 +607,18 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
                 return R.fail("反馈ID不能为空");
             }
 
-            // 2. 更新状态为已解决
+            // 2. 查询原始反馈记录(用于发送通知)
+            LifeFeedback feedback = lifeFeedbackMapper.selectById(statusDto.getFeedbackId());
+            if (feedback == null) {
+                return R.fail("反馈记录不存在");
+            }
+
+            // 3. 更新状态
             LifeFeedback updateFeedback = new LifeFeedback();
             updateFeedback.setId(statusDto.getFeedbackId());
-            updateFeedback.setHandleStatus(1); // 已解决
+            // 如果DTO中有状态值,使用DTO的值;否则默认为已解决(1)
+            Integer handleStatus = statusDto.getHandleStatus() != null ? statusDto.getHandleStatus() : 1;
+            updateFeedback.setHandleStatus(handleStatus);
             updateFeedback.setUpdateTime(new Date());
 
             boolean result = this.updateById(updateFeedback);
@@ -621,6 +630,11 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
             String logContent = "问题已解决";
             saveFeedbackLog(statusDto.getFeedbackId(), 0, logContent);
 
+            // 5. 如果状态为已解决,发送问题已解决通知给用户
+            if (handleStatus == 1) {
+                sendFeedbackResolvedNotice(feedback);
+            }
+
             return R.success("更新成功");
         } catch (Exception e) {
             log.error("中台-更新反馈状态失败", e);
@@ -656,6 +670,7 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
     private void sendFeedbackReplyNotice(LifeFeedback feedback, String replyContent) {
         try {
             String receiverId = null;
+            String userPhone = null;
             
             // 根据反馈来源判断是用户端还是商家端
             if (feedback.getFeedbackSource() == null) {
@@ -663,25 +678,30 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
                 return;
             }
             
-            // userId对应store_user表的id,统一从store_user表查询
             if (feedback.getUserId() == null) {
                 log.warn("用户ID为空,无法发送通知,feedbackId={}", feedback.getId());
                 return;
             }
             
-            StoreUser storeUser = storeUserMapper.selectById(feedback.getUserId());
-            if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
-                log.warn("未找到商户用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
-                return;
-            }
-            
-            // 根据feedbackSource设置不同的接收者ID格式
+            // 根据feedbackSource查询对应的用户表
             if (feedback.getFeedbackSource() == 0) {
-                // 用户端 - 使用user_手机号格式
-                receiverId = "user_" + storeUser.getPhone();
+                // 用户端 - 查询life_user表
+                LifeUser lifeUser = lifeUserMapper.selectById(feedback.getUserId());
+                if (lifeUser == null || lifeUser.getUserPhone() == null || lifeUser.getUserPhone().trim().isEmpty()) {
+                    log.warn("未找到用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
+                    return;
+                }
+                userPhone = lifeUser.getUserPhone();
+                receiverId = "user_" + userPhone;
             } else if (feedback.getFeedbackSource() == 1) {
-                // 商家端 - 使用store_手机号格式
-                receiverId = "store_" + storeUser.getPhone();
+                // 商家端 - 查询store_user表
+                StoreUser storeUser = storeUserMapper.selectById(feedback.getUserId());
+                if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
+                    log.warn("未找到商户用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
+                    return;
+                }
+                userPhone = storeUser.getPhone();
+                receiverId = "store_" + userPhone;
             } else {
                 log.warn("未知的反馈来源,feedbackSource={}, feedbackId={}", feedback.getFeedbackSource(), feedback.getId());
                 return;
@@ -728,6 +748,109 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
     }
 
     /**
+     * 发送问题已解决通知给用户
+     * @param feedback 反馈记录
+     */
+    private void sendFeedbackResolvedNotice(LifeFeedback feedback) {
+        try {
+            String receiverId = null;
+            
+            // 根据反馈来源判断是用户端还是商家端
+            if (feedback.getFeedbackSource() == null) {
+                log.warn("反馈来源为空,无法发送通知,feedbackId={}", feedback.getId());
+                return;
+            }
+            
+            if (feedback.getUserId() == null) {
+                log.warn("用户ID为空,无法发送通知,feedbackId={}", feedback.getId());
+                return;
+            }
+            
+            // 根据feedbackSource查询对应的用户表
+            if (feedback.getFeedbackSource() == 0) {
+                // 用户端 - 查询life_user表
+                LifeUser lifeUser = lifeUserMapper.selectById(feedback.getUserId());
+                if (lifeUser == null || lifeUser.getUserPhone() == null || lifeUser.getUserPhone().trim().isEmpty()) {
+                    log.warn("未找到用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
+                    return;
+                }
+                receiverId = "user_" + lifeUser.getUserPhone();
+            } else if (feedback.getFeedbackSource() == 1) {
+                // 商家端 - 查询store_user表
+                StoreUser storeUser = storeUserMapper.selectById(feedback.getUserId());
+                if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
+                    log.warn("未找到商户用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
+                    return;
+                }
+                receiverId = "store_" + storeUser.getPhone();
+            } else {
+                log.warn("未知的反馈来源,feedbackSource={}, feedbackId={}", feedback.getFeedbackSource(), feedback.getId());
+                return;
+            }
+            
+            // 根据反馈类型生成通知消息
+            String feedbackTypeText = "";
+            if (feedback.getFeedbackType() != null) {
+                switch (feedback.getFeedbackType()) {
+                    case 0:
+                        feedbackTypeText = "BUG";
+                        break;
+                    case 1:
+                        feedbackTypeText = "优化";
+                        break;
+                    case 2:
+                        feedbackTypeText = "功能";
+                        break;
+                    default:
+                        feedbackTypeText = "BUG/优化/功能";
+                        break;
+                }
+            } else {
+                feedbackTypeText = "BUG/优化/功能";
+            }
+            String message = "您反馈的" + feedbackTypeText + "问题已解决,感谢您的反馈";
+            
+            // 构建通知消息
+            JSONObject messageJson = new JSONObject();
+            messageJson.put("feedbackId", feedback.getId());
+            messageJson.put("message", message);
+
+            // 创建通知记录
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setContext(messageJson.toJSONString());
+            lifeNotice.setTitle("意见反馈解决通知");
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1); // 1-系统通知
+            lifeNotice.setBusinessId(feedback.getId());
+
+            // 保存通知
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 通过WebSocket发送实时通知
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSONObject.toJSONString(lifeNotice));
+
+            try {
+                webSocketProcess.sendMessage(receiverId, JSONObject.toJSONString(webSocketVo));
+                log.info("问题已解决通知发送成功,feedbackId={}, receiverId={}", feedback.getId(), receiverId);
+            } catch (Exception e) {
+                log.error("发送WebSocket通知失败,feedbackId={}, receiverId={}, error={}",
+                        feedback.getId(), receiverId, e.getMessage());
+            }
+
+        } catch (Exception e) {
+            log.error("发送问题已解决通知异常,feedbackId={}, error={}", feedback.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
      * 调用AI分配跟踪人员
      * @param feedback 反馈记录
      * @return 分配的跟踪人员ID(对应life_sys表的id),失败返回null

+ 190 - 2
alien-store/src/main/java/shop/alien/store/service/impl/TrackEventServiceImpl.java

@@ -44,6 +44,9 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
     private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
     private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
     private final LifeDiscountCouponStoreFriendMapper lifeDiscountCouponStoreFriendMapper;
+    private final LifeBlacklistMapper lifeBlacklistMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final StoreInfoMapper storeInfoMapper;
     
     private static final String REDIS_QUEUE_KEY = "track:event:queue";
 
@@ -343,9 +346,21 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
     /**
      * 计算好友数量(互相关注的用户数)
      * 注意:好友数量是累计数据,统计截止到endDate的所有互相关注关系
+     * 需要过滤被拉黑的用户,与用户资料页面保持一致
      */
     private long calculateFriendCount(String storePhoneId, Date startDate, Date endDate) {
         try {
+            // 获取店铺的拉黑信息(用于过滤被拉黑的用户)
+            StoreUser storeUser = getStoreUserByPhoneId(storePhoneId);
+            if (storeUser == null) {
+                return 0L;
+            }
+            String blockerType = "1"; // 店铺类型
+            String blockerId = String.valueOf(storeUser.getId());
+            
+            // 获取被拉黑的用户ID集合
+            Set<String> blockedUserIds = getBlockedUserIds(blockerType, blockerId);
+            
             // 查询店铺关注的所有用户(截止到统计日期)
             LambdaQueryWrapper<LifeFans> followWrapper = new LambdaQueryWrapper<>();
             followWrapper.eq(LifeFans::getFansId, storePhoneId)
@@ -353,6 +368,7 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
                     .eq(LifeFans::getDeleteFlag, 0);
             Set<String> followedIds = lifeFansMapper.selectList(followWrapper).stream()
                     .map(LifeFans::getFollowedId)
+                    .filter(id -> isUserValid(id) && !isBlocked(id, blockedUserIds))
                     .collect(Collectors.toSet());
             
             if (followedIds.isEmpty()) {
@@ -366,6 +382,7 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
                     .eq(LifeFans::getDeleteFlag, 0);
             Set<String> fansIds = lifeFansMapper.selectList(fansWrapper).stream()
                     .map(LifeFans::getFansId)
+                    .filter(id -> isUserValid(id) && !isBlocked(id, blockedUserIds))
                     .collect(Collectors.toSet());
             
             // 互相关注的用户数 = 交集
@@ -380,14 +397,35 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
     /**
      * 计算关注数量(店铺用户关注的人数)
      * 注意:关注数量是累计数据,统计截止到endDate的所有关注关系
+     * 需要过滤被拉黑的用户,与用户资料页面保持一致
      */
     private long calculateFollowCount(String storePhoneId, Date startDate, Date endDate) {
         try {
+            // 获取店铺的拉黑信息(用于过滤被拉黑的用户)
+            StoreUser storeUser = getStoreUserByPhoneId(storePhoneId);
+            if (storeUser == null) {
+                return 0L;
+            }
+            String blockerType = "1"; // 店铺类型
+            String blockerId = String.valueOf(storeUser.getId());
+            
+            // 获取被拉黑的用户ID集合
+            Set<String> blockedUserIds = getBlockedUserIds(blockerType, blockerId);
+            
+            // 查询店铺关注的所有用户(截止到统计日期),并过滤被拉黑的用户
             LambdaQueryWrapper<LifeFans> wrapper = new LambdaQueryWrapper<>();
             wrapper.eq(LifeFans::getFansId, storePhoneId)
                     .lt(LifeFans::getCreatedTime, endDate)
                     .eq(LifeFans::getDeleteFlag, 0);
-            return lifeFansMapper.selectCount(wrapper);
+            List<LifeFans> fansList = lifeFansMapper.selectList(wrapper);
+            
+            // 过滤被拉黑的用户和已删除的用户
+            long count = fansList.stream()
+                    .map(LifeFans::getFollowedId)
+                    .filter(id -> isUserValid(id) && !isBlocked(id, blockedUserIds))
+                    .count();
+            
+            return count;
         } catch (Exception e) {
             log.error("计算关注数量失败: storePhoneId={}", storePhoneId, e);
             return 0L;
@@ -397,14 +435,35 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
     /**
      * 计算粉丝数量(关注店铺用户的人数)
      * 注意:粉丝数量是累计数据,统计截止到endDate的所有粉丝关系
+     * 需要过滤被拉黑的用户,与用户资料页面保持一致
      */
     private long calculateFansCount(String storePhoneId, Date startDate, Date endDate) {
         try {
+            // 获取店铺的拉黑信息(用于过滤被拉黑的用户)
+            StoreUser storeUser = getStoreUserByPhoneId(storePhoneId);
+            if (storeUser == null) {
+                return 0L;
+            }
+            String blockerType = "1"; // 店铺类型
+            String blockerId = String.valueOf(storeUser.getId());
+            
+            // 获取被拉黑的用户ID集合
+            Set<String> blockedUserIds = getBlockedUserIds(blockerType, blockerId);
+            
+            // 查询关注店铺的所有用户(截止到统计日期),并过滤被拉黑的用户
             LambdaQueryWrapper<LifeFans> wrapper = new LambdaQueryWrapper<>();
             wrapper.eq(LifeFans::getFollowedId, storePhoneId)
                     .lt(LifeFans::getCreatedTime, endDate)
                     .eq(LifeFans::getDeleteFlag, 0);
-            return lifeFansMapper.selectCount(wrapper);
+            List<LifeFans> fansList = lifeFansMapper.selectList(wrapper);
+            
+            // 过滤被拉黑的用户和已删除的用户
+            long count = fansList.stream()
+                    .map(LifeFans::getFansId)
+                    .filter(id -> isUserValid(id) && !isBlocked(id, blockedUserIds))
+                    .count();
+            
+            return count;
         } catch (Exception e) {
             log.error("计算粉丝数量失败: storePhoneId={}", storePhoneId, e);
             return 0L;
@@ -1053,5 +1112,134 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
             return 0L;
         }
     }
+    
+    /**
+     * 根据phoneId获取StoreUser
+     */
+    private StoreUser getStoreUserByPhoneId(String storePhoneId) {
+        try {
+            if (storePhoneId == null || !storePhoneId.startsWith("store_")) {
+                return null;
+            }
+            String phone = storePhoneId.substring(6); // 去掉 "store_" 前缀
+            LambdaQueryWrapper<StoreUser> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StoreUser::getPhone, phone)
+                    .eq(StoreUser::getDeleteFlag, 0)
+                    .last("LIMIT 1");
+            return storeUserMapper.selectOne(wrapper);
+        } catch (Exception e) {
+            log.error("获取StoreUser失败: storePhoneId={}", storePhoneId, e);
+            return null;
+        }
+    }
+    
+    /**
+     * 获取被拉黑的用户phoneId集合
+     * @param blockerType 拉黑方类型("1"=店铺, "2"=用户)
+     * @param blockerId 拉黑方ID
+     * @return 被拉黑的用户phoneId集合
+     */
+    private Set<String> getBlockedUserIds(String blockerType, String blockerId) {
+        try {
+            LambdaQueryWrapper<LifeBlacklist> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(LifeBlacklist::getBlockerType, blockerType)
+                    .eq(LifeBlacklist::getBlockerId, blockerId)
+                    .eq(LifeBlacklist::getDeleteFlag, 0);
+            List<LifeBlacklist> blacklist = lifeBlacklistMapper.selectList(wrapper);
+            
+            // 将被拉黑的用户ID转换为phoneId集合
+            Set<String> blockedPhoneIds = new HashSet<>();
+            for (LifeBlacklist item : blacklist) {
+                String blockedType = item.getBlockedType();
+                String blockedId = item.getBlockedId();
+                
+                // 根据blockedType和blockedId查询对应的phoneId
+                if ("1".equals(blockedType)) {
+                    // 店铺类型
+                    StoreUser storeUser = storeUserMapper.selectById(blockedId);
+                    if (storeUser != null && storeUser.getPhone() != null) {
+                        blockedPhoneIds.add("store_" + storeUser.getPhone());
+                    }
+                } else if ("2".equals(blockedType)) {
+                    // 用户类型 - 直接查询LifeUser表获取phoneId
+                    LifeUser lifeUser = lifeUserMapper.selectById(blockedId);
+                    if (lifeUser != null && lifeUser.getUserPhone() != null) {
+                        blockedPhoneIds.add("user_" + lifeUser.getUserPhone());
+                    }
+                }
+            }
+            return blockedPhoneIds;
+        } catch (Exception e) {
+            log.error("获取被拉黑用户列表失败: blockerType={}, blockerId={}", blockerType, blockerId, e);
+            return new HashSet<>();
+        }
+    }
+    
+    /**
+     * 判断phoneId是否被拉黑
+     * @param phoneId 用户phoneId
+     * @param blockedUserIds 被拉黑的用户phoneId集合
+     * @return true表示被拉黑,false表示未被拉黑
+     */
+    private boolean isBlocked(String phoneId, Set<String> blockedUserIds) {
+        return blockedUserIds != null && blockedUserIds.contains(phoneId);
+    }
+    
+    /**
+     * 检查用户是否存在且未被删除(与用户资料页面的SQL逻辑保持一致)
+     * @param phoneId 用户phoneId(格式:store_手机号 或 user_手机号)
+     * @return true表示用户存在且未被删除,false表示用户不存在或已被删除
+     */
+    private boolean isUserValid(String phoneId) {
+        try {
+            if (phoneId == null || !phoneId.contains("_")) {
+                return false;
+            }
+            
+            String[] parts = phoneId.split("_", 2);
+            if (parts.length < 2) {
+                return false;
+            }
+            
+            String type = parts[0];
+            String phone = parts[1];
+            
+            if ("store".equals(type)) {
+                // 检查店铺用户是否存在且未被删除
+                LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
+                userWrapper.eq(StoreUser::getPhone, phone)
+                        .eq(StoreUser::getDeleteFlag, 0)
+                        .last("LIMIT 1");
+                StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
+                
+                if (storeUser == null || storeUser.getStoreId() == null) {
+                    return false;
+                }
+                
+                // 检查店铺信息是否存在且未被删除
+                LambdaQueryWrapper<shop.alien.entity.store.StoreInfo> infoWrapper = new LambdaQueryWrapper<>();
+                infoWrapper.eq(shop.alien.entity.store.StoreInfo::getId, storeUser.getStoreId())
+                        .eq(shop.alien.entity.store.StoreInfo::getDeleteFlag, 0)
+                        .last("LIMIT 1");
+                shop.alien.entity.store.StoreInfo storeInfo = storeInfoMapper.selectOne(infoWrapper);
+                
+                return storeInfo != null;
+            } else if ("user".equals(type)) {
+                // 检查普通用户是否存在且未被删除
+                LambdaQueryWrapper<LifeUser> userWrapper = new LambdaQueryWrapper<>();
+                userWrapper.eq(LifeUser::getUserPhone, phone)
+                        .eq(LifeUser::getDeleteFlag, 0)
+                        .last("LIMIT 1");
+                LifeUser lifeUser = lifeUserMapper.selectOne(userWrapper);
+                
+                return lifeUser != null;
+            }
+            
+            return false;
+        } catch (Exception e) {
+            log.error("检查用户有效性失败: phoneId={}", phoneId, e);
+            return false;
+        }
+    }
 
 }