Przeglądaj źródła

feat(project): 添加转接客户重粉数据迁移功能

- 在IUserProjectRepeatMaintainService中新增migrateQwUserOnTransfer方法用于转接时重粉数据迁移
- 实现转接客户E级评定延迟逻辑,转接客户需超过3天才参与E级评定
- 在QwExternalContactServiceImpl中添加转接时重粉中间表数据迁移处理
- 完善重粉中间表迁移服务,包括旧记录清理、新记录创建和重复标记刷新
- 优化重粉判断逻辑,排除状态为4的无效客户记录
xw 1 tydzień temu
rodzic
commit
3a1f9a6130

+ 38 - 14
fs-qw-task/src/main/java/com/fs/app/taskService/impl/QwExternalContactRatingMoreSevenDaysServiceImpl.java

@@ -27,6 +27,7 @@ import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.*;
@@ -209,13 +210,8 @@ public class QwExternalContactRatingMoreSevenDaysServiceImpl implements QwExtern
 
             // 如果连续3天没有看课记录,标记为E级
             if (ratingVOS == null || ratingVOS.isEmpty()) {
-                log.info("连续{}天没有看课记录,标记为E级,externalId: {}", config.getNotStudyDays(), externalId);
-                QwExternalContact externalContact = new QwExternalContact();
-                externalContact.setId(externalId);
-                externalContact.setLevel(5);  // E级
-                externalContact.setLevelType(2);  // 降级(从ABCD任何等级变为E级都是降级)
-                externalContact.setIsDaysNotStudy(1);
-                return externalContact;
+                return buildELevelContactIfEligible(externalId, config,
+                        "连续{}天没有看课记录,标记为E级,externalId: {}");
             }
 
             // 判断连续3天的总观看时长是否大于0
@@ -223,13 +219,8 @@ public class QwExternalContactRatingMoreSevenDaysServiceImpl implements QwExtern
 
             if (!hasWatchDuration) {
                 // 有记录但观看时长为0,也认为连续3天未看课,标记为E级
-                log.info("连续{}天看课时长为0,标记为E级,externalId: {}", config.getNotStudyDays(), externalId);
-                QwExternalContact externalContact = new QwExternalContact();
-                externalContact.setId(externalId);
-                externalContact.setLevel(5);  // E级
-                externalContact.setLevelType(2);  // E级类型
-                externalContact.setIsDaysNotStudy(1);
-                return externalContact;
+                return buildELevelContactIfEligible(externalId, config,
+                        "连续{}天看课时长为0,标记为E级,externalId: {}");
             } else {
                 // 有看课记录且时长>0,不是E级,恢复为ABCD评级
                 log.info("最近{}天有看课记录,不是E级,externalId: {}", config.getNotStudyDays(), externalId);
@@ -342,6 +333,39 @@ public class QwExternalContactRatingMoreSevenDaysServiceImpl implements QwExtern
     }
 
 
+    /**
+     * 转接客户(is_transfer=1)需 create_time 超过3天才参与E级评定,其他客户不受影响。
+     */
+    private boolean canRateTransferContactAsELevel(Long externalId) {
+        QwExternalContact contact = qwExternalContactMapper.selectQwExternalContactById(externalId);
+        if (contact == null) {
+            return true;
+        }
+        if (contact.getIsTransfer() == null || contact.getIsTransfer() != 1) {
+            return true;
+        }
+        Date createTime = contact.getCreateTime();
+        if (createTime == null) {
+            return true;
+        }
+        long daysSinceCreate = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - createTime.getTime());
+        return daysSinceCreate > 3;
+    }
+
+    private QwExternalContact buildELevelContactIfEligible(Long externalId, QwRatingConfig config, String logPattern) {
+        if (!canRateTransferContactAsELevel(externalId)) {
+            log.info("转接客户创建未满3天,跳过E级评定,externalId: {}", externalId);
+            return null;
+        }
+        log.info(logPattern, config.getNotStudyDays(), externalId);
+        QwExternalContact externalContact = new QwExternalContact();
+        externalContact.setId(externalId);
+        externalContact.setLevel(5);
+        externalContact.setLevelType(2);
+        externalContact.setIsDaysNotStudy(1);
+        return externalContact;
+    }
+
     //查 E级
     public boolean getScoreMoreStudyLevel(List<QwRatingVO> qwRatingVOS) {
 

+ 5 - 0
fs-service/src/main/java/com/fs/course/service/IUserProjectRepeatMaintainService.java

@@ -25,4 +25,9 @@ public interface IUserProjectRepeatMaintainService {
      * 判断用户在某项目下绑定指定企微销售后是否会成为重粉(写入前判重,走中间表)。
      */
     boolean wouldBeRepeatAfterBind(Long fsUserId, Long projectId, Long qwUserId);
+
+    /**
+     * 转接完成:将重粉中间表里原销售的 qw_user_id 迁移到新销售,并刷新重粉标记。
+     */
+    void migrateQwUserOnTransfer(Long fsUserId, Long oldQwUserId, Long newQwUserId, Long companyId);
 }

+ 52 - 0
fs-service/src/main/java/com/fs/course/service/impl/UserProjectRepeatMaintainServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.course.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fs.course.domain.FsUserProjectQw;
 import com.fs.course.domain.FsUserProjectRepeat;
 import com.fs.course.mapper.FsUserProjectQwMapper;
 import com.fs.course.mapper.FsUserProjectRepeatMapper;
@@ -10,7 +11,10 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 @Slf4j
 @Service
@@ -89,4 +93,52 @@ public class UserProjectRepeatMaintainServiceImpl implements IUserProjectRepeatM
         }
         return fsUserProjectQwMapper.existsQwUserInProjects(fsUserId, qwUserId, equivalentProjectIds) == 0;
     }
+
+    @Override
+    public void migrateQwUserOnTransfer(Long fsUserId, Long oldQwUserId, Long newQwUserId, Long companyId) {
+        if (fsUserId == null || oldQwUserId == null || newQwUserId == null || oldQwUserId.equals(newQwUserId)) {
+            return;
+        }
+        try {
+            List<FsUserProjectQw> oldRows = fsUserProjectQwMapper.selectList(
+                    new QueryWrapper<FsUserProjectQw>()
+                            .eq("fs_user_id", fsUserId)
+                            .eq("qw_user_id", oldQwUserId));
+            if (oldRows.isEmpty()) {
+                return;
+            }
+
+            Set<Long> projectsToRefresh = new HashSet<>();
+            for (FsUserProjectQw row : oldRows) {
+                projectsToRefresh.add(row.getProjectId());
+                FsUserProjectQw existingNew = fsUserProjectQwMapper.selectOne(
+                        new QueryWrapper<FsUserProjectQw>()
+                                .eq("fs_user_id", fsUserId)
+                                .eq("project_id", row.getProjectId())
+                                .eq("qw_user_id", newQwUserId)
+                                .last("LIMIT 1"));
+                if (existingNew != null) {
+                    fsUserProjectQwMapper.deleteById(row.getId());
+                } else {
+                    FsUserProjectQw update = new FsUserProjectQw();
+                    update.setId(row.getId());
+                    update.setQwUserId(newQwUserId);
+                    update.setUpdateTime(new Date());
+                    if (companyId != null) {
+                        update.setCompanyId(companyId);
+                    }
+                    fsUserProjectQwMapper.updateById(update);
+                }
+            }
+
+            for (Long projectId : projectsToRefresh) {
+                maintain(fsUserId, projectId, newQwUserId, companyId);
+            }
+            log.info("转接迁移重粉中间表成功 fsUserId={}, oldQwUserId={}, newQwUserId={}, projects={}",
+                    fsUserId, oldQwUserId, newQwUserId, projectsToRefresh);
+        } catch (Exception e) {
+            log.error("转接迁移重粉中间表失败 fsUserId={}, oldQwUserId={}, newQwUserId={}",
+                    fsUserId, oldQwUserId, newQwUserId, e);
+        }
+    }
 }

+ 174 - 4
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -31,6 +31,7 @@ import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.course.service.IFsCourseLinkService;
 import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.service.IUserProjectRepeatMaintainService;
 import com.fs.course.vo.FsUserCourseVideoQVO;
 import com.fs.crm.domain.CrmCustomer;
 import com.fs.crm.mapper.CrmCustomerMapper;
@@ -217,6 +218,9 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
     @Autowired
     private FsUserCourseMapper fsUserCourseMapper;
 
+    @Autowired
+    private IUserProjectRepeatMaintainService userProjectRepeatMaintainService;
+
     @Autowired
     private IQwExternalErrRetryService errRetryService;
 
@@ -2808,8 +2812,12 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         qwExternalContact.setId(contact.getId());
         if (transferLogListByCheck != null || !auditUserList.isEmpty()) {
             fillTransferContactInfo(qwExternalContact, transferLogListByCheck, transferOldContactId);
+            applyTransferRemarkAndTags(qwExternalContact, transferLogListByCheck, transferOldContactId, needClearTag);
         }
         qwExternalContactMapper.updateQwExternalContact(qwExternalContact);
+        if (transferLogListByCheck != null || !auditUserList.isEmpty()) {
+            migrateTransferProjectRepeat(transferLogListByCheck, transferOldContactId, qwExternalContact);
+        }
         QwOpenidByExternalcontactParams externalcontactParams = new QwOpenidByExternalcontactParams();
         externalcontactParams.setExternal_userid(externalUserID);
         //录入单独的CRM客户信息  没啥用
@@ -2837,8 +2845,10 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         }
         /*只有ddgy加重粉*/
         if (("叮当国医".equals(cloudHostProper.getCompanyName()))) {
-            if (qwExternalContactMapper.selectCount(new LambdaQueryWrapper<QwExternalContact>().eq(QwExternalContact::getExternalUserId, qwExternalContact.getExternalUserId()))
-            >1){
+            long activeCount = qwExternalContactMapper.selectCount(new LambdaQueryWrapper<QwExternalContact>()
+                    .eq(QwExternalContact::getExternalUserId, qwExternalContact.getExternalUserId())
+                    .ne(QwExternalContact::getStatus, 4));
+            if (activeCount > 1){
                 qwExternalContact.setIsRepeat(1);
                 qwExternalContactMapper.update(null,new LambdaUpdateWrapper<QwExternalContact>().eq(QwExternalContact::getId,qwExternalContact.getId())
                         .set(QwExternalContact::getIsRepeat,1));
@@ -3379,8 +3389,13 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         qwExternalContact.setId(contact.getId());
         if (transferLogListByCheck != null) {
             fillTransferContactInfo(qwExternalContact, transferLogListByCheck, transferLogListByCheck.getExternalContactId());
+            boolean needClearTag = transferLogListByCheck.getNeedClearTag() != null && transferLogListByCheck.getNeedClearTag() == 1;
+            applyTransferRemarkAndTags(qwExternalContact, transferLogListByCheck, transferLogListByCheck.getExternalContactId(), needClearTag);
         }
         qwExternalContactMapper.updateQwExternalContact(qwExternalContact);
+        if (transferLogListByCheck != null) {
+            migrateTransferProjectRepeat(transferLogListByCheck, transferLogListByCheck.getExternalContactId(), qwExternalContact);
+        }
         QwOpenidByExternalcontactParams externalcontactParams = new QwOpenidByExternalcontactParams();
         externalcontactParams.setExternal_userid(externalUserID);
         //录入单独的CRM客户信息  没啥用
@@ -4470,6 +4485,32 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         update.setId(newContactId);
         fillTransferContactInfo(update, transferLog, oldContactId);
         qwExternalContactMapper.updateQwExternalContact(update);
+        QwExternalContact newContact = qwExternalContactMapper.selectQwExternalContactById(newContactId);
+        if (newContact != null) {
+            applyTransferRemarkAndTags(newContact, transferLog, oldContactId);
+            updateTransferRemarkAndTagsInDb(newContactId, newContact);
+            newContact.setFsUserId(update.getFsUserId());
+            newContact.setIsTransfer(update.getIsTransfer());
+            migrateTransferProjectRepeat(transferLog, oldContactId, newContact);
+        }
+    }
+
+    private void migrateTransferProjectRepeat(QwExternalContactTransferLog transferLog, Long oldContactId, QwExternalContact newContact) {
+        if (newContact == null || newContact.getFsUserId() == null || newContact.getQwUserId() == null) {
+            return;
+        }
+        Long oldQwUserId = null;
+        if (transferLog != null && transferLog.getHandoverQwUserId() != null) {
+            oldQwUserId = transferLog.getHandoverQwUserId();
+        }
+        if (oldQwUserId == null && oldContactId != null) {
+            QwExternalContact oldContact = qwExternalContactMapper.selectQwExternalContactById(oldContactId);
+            if (oldContact != null) {
+                oldQwUserId = oldContact.getQwUserId();
+            }
+        }
+        userProjectRepeatMaintainService.migrateQwUserOnTransfer(
+                newContact.getFsUserId(), oldQwUserId, newContact.getQwUserId(), newContact.getCompanyId());
     }
 
     private void fillTransferContactInfo(QwExternalContact target, QwExternalContactTransferLog transferLog, Long oldContactId) {
@@ -4484,6 +4525,11 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         if (transferLog != null && transferLog.getFsUserId() != null) {
             return transferLog.getFsUserId();
         }
+        QwExternalContact oldContact = resolveOldTransferContact(transferLog, oldContactId);
+        return oldContact == null ? null : oldContact.getFsUserId();
+    }
+
+    private QwExternalContact resolveOldTransferContact(QwExternalContactTransferLog transferLog, Long oldContactId) {
         Long sourceContactId = oldContactId;
         if (sourceContactId == null && transferLog != null) {
             sourceContactId = transferLog.getExternalContactId();
@@ -4491,8 +4537,132 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         if (sourceContactId == null) {
             return null;
         }
-        QwExternalContact oldContact = qwExternalContactMapper.selectQwExternalContactById(sourceContactId);
-        return oldContact == null ? null : oldContact.getFsUserId();
+        return qwExternalContactMapper.selectQwExternalContactById(sourceContactId);
+    }
+
+    private void applyTransferRemarkAndTags(QwExternalContact newContact, QwExternalContactTransferLog transferLog, Long oldContactId) {
+        applyTransferRemarkAndTags(newContact, transferLog, oldContactId, null);
+    }
+
+    private void applyTransferRemarkAndTags(QwExternalContact newContact, QwExternalContactTransferLog transferLog,
+                                            Long oldContactId, Boolean needClearTagOverride) {
+        if (newContact == null) {
+            return;
+        }
+        QwExternalContact oldContact = resolveOldTransferContact(transferLog, oldContactId);
+        if (oldContact == null) {
+            return;
+        }
+        boolean needClearTag = needClearTagOverride != null
+                ? needClearTagOverride
+                : (transferLog != null && Integer.valueOf(1).equals(transferLog.getNeedClearTag()));
+
+        String userId = newContact.getUserId();
+        String externalUserId = newContact.getExternalUserId();
+        String corpId = newContact.getCorpId();
+        if (StringUtil.strIsNullOrEmpty(userId) || StringUtil.strIsNullOrEmpty(externalUserId) || StringUtil.strIsNullOrEmpty(corpId)) {
+            logger.warn("转接同步备注标签跳过:新客户缺少必要字段 contactId={}", newContact.getId());
+            return;
+        }
+
+        boolean hasRemark = !StringUtil.strIsNullOrEmpty(oldContact.getRemark());
+        boolean hasDescription = !StringUtil.strIsNullOrEmpty(oldContact.getDescription());
+        boolean hasRemarkCorp = !StringUtil.strIsNullOrEmpty(oldContact.getRemarkCorpName());
+        boolean hasRemarkMobiles = !StringUtil.strIsNullOrEmpty(oldContact.getRemarkMobiles())
+                && !"[]".equals(oldContact.getRemarkMobiles().trim());
+
+        if (hasRemark) {
+            newContact.setRemark(oldContact.getRemark());
+        }
+        if (hasDescription) {
+            newContact.setDescription(oldContact.getDescription());
+        }
+        if (hasRemarkCorp) {
+            newContact.setRemarkCorpName(oldContact.getRemarkCorpName());
+        }
+        if (hasRemarkMobiles) {
+            newContact.setRemarkMobiles(oldContact.getRemarkMobiles());
+        }
+
+        List<String> tagList = null;
+        if (!needClearTag && !StringUtil.strIsNullOrEmpty(oldContact.getTagIds()) && !"[]".equals(oldContact.getTagIds().trim())) {
+            try {
+                tagList = JSON.parseArray(oldContact.getTagIds(), String.class);
+                if (tagList != null) {
+                    tagList = tagList.stream()
+                            .filter(t -> !StringUtil.strIsNullOrEmpty(t))
+                            .distinct()
+                            .collect(Collectors.toList());
+                }
+                if (tagList != null && !tagList.isEmpty()) {
+                    newContact.setTagIds(JSON.toJSONString(tagList));
+                } else {
+                    tagList = null;
+                }
+            } catch (Exception e) {
+                logger.error("转接解析原客户标签失败 contactId={}, tagIds={}", oldContact.getId(), oldContact.getTagIds(), e);
+            }
+        }
+
+        if (hasRemark || hasDescription || hasRemarkCorp || hasRemarkMobiles) {
+            QwExternalContactRemarkParam remarkParam = new QwExternalContactRemarkParam();
+            remarkParam.setUserid(userId);
+            remarkParam.setExternal_userid(externalUserId);
+            if (hasRemark) {
+                remarkParam.setRemark(oldContact.getRemark());
+            }
+            if (hasDescription) {
+                remarkParam.setDescription(oldContact.getDescription());
+            }
+            if (hasRemarkCorp) {
+                remarkParam.setRemark_company(oldContact.getRemarkCorpName());
+            }
+            if (hasRemarkMobiles) {
+                try {
+                    remarkParam.setRemark_mobiles(JSON.parseArray(oldContact.getRemarkMobiles(), String.class));
+                } catch (Exception e) {
+                    logger.error("转接解析原客户备注手机号失败 contactId={}", oldContact.getId(), e);
+                }
+            }
+            QwExternalContactRemarkResult remarkResult = qwApiService.externalcontactRemark(remarkParam, corpId);
+            if (remarkResult.getErrcode() == 45035) {
+                insertQwExternalErrRetryTool(corpId, JSON.toJSONString(remarkParam), 2, remarkResult.getErrmsg());
+                logger.error("转接同步备注失败-已加入补偿机制:{}", remarkParam);
+            } else if (remarkResult.getErrcode() != 0) {
+                logger.error("转接同步备注失败 errcode={}, errmsg={}, contactId={}",
+                        remarkResult.getErrcode(), remarkResult.getErrmsg(), newContact.getId());
+            } else {
+                logger.info("转接同步备注成功 contactId={}, remark={}", newContact.getId(), oldContact.getRemark());
+            }
+        }
+
+        if (tagList != null && !tagList.isEmpty()) {
+            QwEditUserTagParam tagParam = new QwEditUserTagParam();
+            tagParam.setUserid(userId);
+            tagParam.setExternal_userid(externalUserId);
+            tagParam.setAdd_tag(tagList);
+            QwResult tagResult = qwApiService.editUserTag(tagParam, corpId);
+            if (tagResult.getErrcode() == 45035) {
+                insertQwExternalErrRetryTool(corpId, JSON.toJSONString(tagParam), 1, tagResult.getErrmsg());
+                logger.error("转接同步标签失败-已加入补偿机制:{}", tagParam);
+            } else if (tagResult.getErrcode() != 0) {
+                logger.error("转接同步标签失败 errcode={}, errmsg={}, contactId={}",
+                        tagResult.getErrcode(), tagResult.getErrmsg(), newContact.getId());
+            } else {
+                logger.info("转接同步标签成功 contactId={}, tags={}", newContact.getId(), tagList);
+            }
+        }
+    }
+
+    private void updateTransferRemarkAndTagsInDb(Long contactId, QwExternalContact source) {
+        QwExternalContact update = new QwExternalContact();
+        update.setId(contactId);
+        update.setRemark(source.getRemark());
+        update.setDescription(source.getDescription());
+        update.setTagIds(source.getTagIds());
+        update.setRemarkMobiles(source.getRemarkMobiles());
+        update.setRemarkCorpName(source.getRemarkCorpName());
+        qwExternalContactMapper.updateQwExternalContact(update);
     }
 
     @Override