|
|
@@ -40,6 +40,7 @@ import com.fs.live.domain.LiveWatchLog;
|
|
|
import com.fs.live.mapper.LiveWatchLogMapper;
|
|
|
import com.fs.qw.domain.*;
|
|
|
import com.fs.qw.mapper.*;
|
|
|
+import com.fs.qw.cache.QwSopCacheService;
|
|
|
import com.fs.qw.param.QwExtCourseSopWatchLog;
|
|
|
import com.fs.qw.param.QwExternalContactVOTime;
|
|
|
import com.fs.qw.param.QwTagSearchParam;
|
|
|
@@ -59,6 +60,7 @@ import com.fs.sop.params.BatchSopUserLogsInfoParam;
|
|
|
import com.fs.sop.params.BatchSopUserLogsInfoParamU;
|
|
|
import com.fs.sop.params.SendUserLogsInfoMsgParam;
|
|
|
import com.fs.sop.params.SopUserLogsParamByDate;
|
|
|
+import com.fs.sop.service.IQwMassSendLogService;
|
|
|
import com.fs.sop.service.IQwSopLogsService;
|
|
|
import com.fs.sop.service.IQwSopService;
|
|
|
import com.fs.sop.service.IQwSopTempVoiceService;
|
|
|
@@ -142,6 +144,10 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
@Autowired
|
|
|
private QwSopMapper qwSopMapper;
|
|
|
|
|
|
+ /** SOP 名称缓存(内部走 QwSopMapper @DataSource(SOP)) */
|
|
|
+ @Autowired
|
|
|
+ private QwSopCacheService qwSopCacheService;
|
|
|
+
|
|
|
@Autowired
|
|
|
private QwSopLogsMapper qwSopLogsMapper;
|
|
|
|
|
|
@@ -185,6 +191,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
@Autowired
|
|
|
private CompanyMapper companyMapper;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private IQwMassSendLogService qwMassSendLogService;
|
|
|
+
|
|
|
@Autowired
|
|
|
private CompanyDeptMapper companyDeptMapper;
|
|
|
|
|
|
@@ -559,6 +568,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
return R.error().put("msg","企业不存在,请联系管理员");
|
|
|
}
|
|
|
List<QwSopLogs> sopLogsList;
|
|
|
+ // 群聊路径客户人数(外部成员数);个人路径为 -1 表示不使用
|
|
|
+ int groupPathCustomerCount = -1;
|
|
|
if(param.getFilterMode() != null && param.getFilterMode() == 2 && param.getChatIds() != null && param.getChatIds().length > 0){
|
|
|
List<QwGroupChat> groupList = qwGroupChatMapper.selectQwGroupChatByChatIds(param.getChatIds());
|
|
|
|
|
|
@@ -577,6 +588,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ // 与个人路径「本次覆盖客户数」语义对齐:统计未退群的外部联系人(type=2),而非选中群数量
|
|
|
+ groupPathCustomerCount = countGroupMassSendCustomerCount(groupUserList, groupList);
|
|
|
+
|
|
|
//没有传值课程和课节 是直播的数据
|
|
|
if(null != param.getCourseId() && null !=param.getVideoId()){
|
|
|
try {
|
|
|
@@ -1390,13 +1404,109 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
}
|
|
|
|
|
|
//批量插入 发送记录
|
|
|
- if (!sopLogsList.isEmpty()) {
|
|
|
+ int customerCount = 0;
|
|
|
+ if (sopLogsList != null && !sopLogsList.isEmpty()) {
|
|
|
processAndInsertQwSopLogsBySendMsg(sopLogsList);
|
|
|
+ if (groupPathCustomerCount >= 0) {
|
|
|
+ // 群路径:sendType=2按人发用实际发送条数;发群消息时一条日志对应一个群,客户人数改用外部成员数
|
|
|
+ if (param.getSendType() != null && param.getSendType() == 2) {
|
|
|
+ customerCount = sopLogsList.size();
|
|
|
+ } else {
|
|
|
+ customerCount = groupPathCustomerCount > 0 ? groupPathCustomerCount : sopLogsList.size();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 个人路径:一条发送记录对应一个客户
|
|
|
+ customerCount = sopLogsList.size();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 记录销售点击一键群发行为(主库),不影响主流程
|
|
|
+ Long companyIdHint = qwSop != null ? qwSop.getCompanyId() : null;
|
|
|
+ // 本方法开头已从 SOP 库查出 QwSop,直接带上名称;若开头未查到则再补一次
|
|
|
+ if (qwSop != null && (param.getSopName() == null || param.getSopName().isEmpty())
|
|
|
+ && qwSop.getName() != null && !qwSop.getName().isEmpty()) {
|
|
|
+ param.setSopName(qwSop.getName());
|
|
|
+ } else {
|
|
|
+ fillParamSopNameFromSopDb(param);
|
|
|
}
|
|
|
+ qwMassSendLogService.saveMassSendClickLog(param, customerCount, 1, companyIdHint);
|
|
|
|
|
|
return R.ok();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 群发参数补齐 SOP 名称:已有则不覆盖;否则按 sopId 查 SOP 库 qw_sop.name
|
|
|
+ * 优先 QwSopCacheService,回退 Mapper(类/方法上已有 @DataSource(SOP) AOP,无需手动切库)
|
|
|
+ */
|
|
|
+ private void fillParamSopNameFromSopDb(SendUserLogsInfoMsgParam param) {
|
|
|
+ if (param == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!StringUtil.strIsNullOrEmpty(param.getSopName())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String sopId = param.getSopId();
|
|
|
+ if (StringUtil.strIsNullOrEmpty(sopId) && param.getSopIds() != null && param.getSopIds().length > 0) {
|
|
|
+ sopId = param.getSopIds()[0];
|
|
|
+ if (!StringUtil.strIsNullOrEmpty(sopId)) {
|
|
|
+ param.setSopId(sopId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (StringUtil.strIsNullOrEmpty(sopId)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ String cached = qwSopCacheService.getQwSopNameBySopId(sopId);
|
|
|
+ if (!StringUtil.strIsNullOrEmpty(cached) && !"-".equals(cached)) {
|
|
|
+ param.setSopName(cached);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("QwSopCacheService补全群发SOP名称失败 sopId={}", sopId, e);
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ String name = qwSopMapper.selectQwSopNameBySopId(sopId);
|
|
|
+ if (!StringUtil.strIsNullOrEmpty(name)) {
|
|
|
+ param.setSopName(name);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ QwSop sop = qwSopMapper.selectQwSopById(sopId);
|
|
|
+ if (sop != null && !StringUtil.strIsNullOrEmpty(sop.getName())) {
|
|
|
+ param.setSopName(sop.getName());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("补全群发SOP名称失败 sopId={}", sopId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 群聊一键群发客户人数:优先统计未退群外部联系人(type=2)数量,
|
|
|
+ * 与个人路径「本次覆盖客户数」语义对齐;成员明细缺失时回退 groupSize 之和,再回退选中群数。
|
|
|
+ */
|
|
|
+ private static int countGroupMassSendCustomerCount(List<QwGroupChatUser> groupUserList, List<QwGroupChat> groupList) {
|
|
|
+ if (groupUserList != null && !groupUserList.isEmpty()) {
|
|
|
+ int externalCount = (int) groupUserList.stream()
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .filter(u -> "2".equals(String.valueOf(u.getType())))
|
|
|
+ .filter(u -> u.getIsOut() == null || u.getIsOut() == 0L)
|
|
|
+ .count();
|
|
|
+ if (externalCount > 0) {
|
|
|
+ return externalCount;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (groupList != null && !groupList.isEmpty()) {
|
|
|
+ int sizeSum = groupList.stream()
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .filter(g -> g.getGroupSize() != null && g.getGroupSize() > 0)
|
|
|
+ .mapToInt(g -> g.getGroupSize().intValue())
|
|
|
+ .sum();
|
|
|
+ if (sizeSum > 0) {
|
|
|
+ return sizeSum;
|
|
|
+ }
|
|
|
+ return groupList.size();
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
private void updateQwUserKey(QwSopLogs sopLogs, String qwUserId, String sopId,String chatId) {
|
|
|
// 设置实际发送人
|
|
|
SopUserLogs qwSopLogs = sopUserLogsMapper.queryUserBySopId(qwUserId,sopId,chatId);
|
|
|
@@ -1430,7 +1540,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
case 1:
|
|
|
break;
|
|
|
case 2:
|
|
|
- processQwSopLogsBySendMsg(param,param.getDraftStrategy());
|
|
|
+ processQwSopLogsBySendMsg(param, param.getDraftStrategy(), 2);
|
|
|
log.info("一键群发操作日志2:{}", JSON.toJSONString(param));
|
|
|
break;
|
|
|
}
|
|
|
@@ -1447,7 +1557,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
String[] ids = sopUserLogsIds.toArray(new String[0]);
|
|
|
param.setIds(ids);
|
|
|
log.info("一键群发操作日志1:{}", JSON.toJSONString(param));
|
|
|
- processQwSopLogsBySendMsg(param,param.getDraftStrategy());
|
|
|
+ processQwSopLogsBySendMsg(param, param.getDraftStrategy(), 3);
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
@@ -1473,7 +1583,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
return watchLogVOList;
|
|
|
}
|
|
|
|
|
|
- private R processQwSopLogsBySendMsg(SendUserLogsInfoMsgParam param,Integer draftStrategy) {
|
|
|
+ private R processQwSopLogsBySendMsg(SendUserLogsInfoMsgParam param, Integer draftStrategy, int sendSource) {
|
|
|
|
|
|
|
|
|
String json = configService.selectConfigByKey("course.config");
|
|
|
@@ -1498,6 +1608,19 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
|
|
|
List<SopUserLogsInfo> sopUserLogs = sopUserLogsMapper.selectSopUserLogsByIds(param.getIds());
|
|
|
|
|
|
+ // 补全 sopId:MsgType/MsgSop 入口常只传 ids 或 sopIds,未传 sopId,导致群发日志 sop_name 为空
|
|
|
+ if (StringUtil.strIsNullOrEmpty(param.getSopId())) {
|
|
|
+ if (param.getSopIds() != null && param.getSopIds().length > 0
|
|
|
+ && !StringUtil.strIsNullOrEmpty(param.getSopIds()[0])) {
|
|
|
+ param.setSopId(param.getSopIds()[0]);
|
|
|
+ } else if (sopUserLogs != null && !sopUserLogs.isEmpty()
|
|
|
+ && !StringUtil.strIsNullOrEmpty(sopUserLogs.get(0).getSopId())) {
|
|
|
+ param.setSopId(sopUserLogs.get(0).getSopId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 与 sendUserLogsInfoMsg 同口径:落库前按 sopId 从 SOP 库补齐名称(前端未传 sopName 时)
|
|
|
+ fillParamSopNameFromSopDb(param);
|
|
|
+
|
|
|
// 查询公司关联小程序数据
|
|
|
List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
|
|
|
Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
|
|
|
@@ -1540,6 +1663,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
sendType = 20;
|
|
|
}
|
|
|
int finalSendType = sendType;
|
|
|
+ // 累计本次点击对应的客户发送条数
|
|
|
+ final int[] customerCountHolder = new int[]{0};
|
|
|
+ final Long[] companyIdHolder = new Long[]{null};
|
|
|
groupedLogs.forEach((key, logs) -> {
|
|
|
|
|
|
QwUser qwUser = qwUserMapper.selectQwUserByCorpIdAndUserId(key.getValue(), key.getKey());
|
|
|
@@ -1549,12 +1675,16 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
}else {
|
|
|
|
|
|
if (qwUser.getCompanyUserId()!=null && qwUser.getCompanyId()!=null){
|
|
|
+ if (companyIdHolder[0] == null) {
|
|
|
+ companyIdHolder[0] = qwUser.getCompanyId();
|
|
|
+ }
|
|
|
List<QwSopLogs> sopLogsList = processInsertSopUserLogsInfo(logs, qwUser, param, words, config, qwCompany, finalSort,
|
|
|
finalSendType,miniMap,companies, deptMiniAppMap, companyUserDeptMap, feishuShardAllocator);
|
|
|
|
|
|
//批量插入 发送记录
|
|
|
if (!sopLogsList.isEmpty()) {
|
|
|
processAndInsertQwSopLogsBySendMsg(sopLogsList);
|
|
|
+ customerCountHolder[0] += sopLogsList.size();
|
|
|
}
|
|
|
|
|
|
}else {
|
|
|
@@ -1566,6 +1696,10 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
|
|
|
|
|
|
});
|
|
|
|
|
|
+ // 落库前再补一次 SOP 名称(防止前文查询失败或前端未传)
|
|
|
+ fillParamSopNameFromSopDb(param);
|
|
|
+ // 记录销售点击一键群发行为(主库),不影响主流程
|
|
|
+ qwMassSendLogService.saveMassSendClickLog(param, customerCountHolder[0], sendSource, companyIdHolder[0]);
|
|
|
|
|
|
return R.ok();
|
|
|
}
|