소스 검색

1、sop发送直播卡片

yys 3 일 전
부모
커밋
f6d61400be

+ 17 - 15
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -804,7 +804,7 @@ public class IpadSendServer {
                     sendAppShortLink(vo, content, miniMap);
                     break;
                 case "25":
-                    // APP直播卡片走 OpenIM WebSocket,企微侧跳过
+                    // APP直播卡片走 OpenIM IM 发送,企微侧跳过
                     content.setSendStatus(0);
                     content.setSendRemarks("APP待发送");
                     break;
@@ -1286,6 +1286,10 @@ public class IpadSendServer {
             log.info("不包含app课程:{}, LOGID: {}", qwUser.getQwUserName(), qwSopLogs.getId());
             return false;
         }
+        if (qwSopLogs.getSendStatus() != null && qwSopLogs.getSendStatus() != 3L) {
+            log.info("状态异常不发送APP:{}, LOGID: {}, sendStatus: {}", qwUser.getQwUserName(), qwSopLogs.getId(), qwSopLogs.getSendStatus());
+            return false;
+        }
         if(redisCache.getCacheObject("qw:user:id:" + qwUser.getId()) != null){
             log.info("频率异常不发送:{}", qwUser.getQwUserName());
             return false;
@@ -1345,20 +1349,18 @@ public class IpadSendServer {
                     }
                 }
             }
-            if (queryLiveId != null) {
-                LiveWatchLog liveWatchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(
-                        queryLiveId, String.valueOf(qwUser.getId()), qwSopLogs.getExternalId());
-                if (liveWatchLog == null) {
-                    log.warn("SOP_LOG_ID:{}, APP直播卡片无观看记录,不发送", qwSopLogs.getId());
-                    qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "无观看记录,不发送");
-                    return false;
-                }
-                Integer courseType = setting.getCourseType();
-                if (courseType != null && courseType != 0
-                        && !QwSopLogsServiceImpl.isCourseTypeValid(courseType, liveWatchLog.getLogType())) {
-                    qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "看课状态未满足,不发送");
-                    return false;
-                }
+            LiveWatchLog liveWatchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(
+                    queryLiveId, String.valueOf(qwUser.getId()), qwSopLogs.getExternalId());
+            if (liveWatchLog == null) {
+                log.warn("SOP_LOG_ID:{}, APP直播卡片无观看记录,不发送", qwSopLogs.getId());
+                qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "无观看记录,不发送");
+                return false;
+            }
+            Integer courseType = setting.getCourseType();
+            if (courseType != null && courseType != 0
+                    && !QwSopLogsServiceImpl.isCourseTypeValid(courseType, liveWatchLog.getLogType())) {
+                qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "看课状态未满足,不发送");
+                return false;
             }
             return true;
         }

+ 94 - 6
fs-ipad-task/src/main/java/com/fs/app/task/SendAppMsg.java

@@ -8,6 +8,9 @@ import com.fs.app.service.IpadSendServer;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.PubFun;
+import com.fs.live.domain.LiveWatchLog;
+import com.fs.live.mapper.LiveWatchLogMapper;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwIpadServer;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.*;
@@ -48,6 +51,8 @@ public class SendAppMsg {
     private final QwIpadServerMapper qwIpadServerMapper;
     private final RedisCacheT<Long> redisCache;
     private final QwPushCountMapper qwPushCountMapper;
+    private final QwExternalContactMapper qwExternalContactMapper;
+    private final LiveWatchLogMapper liveWatchLogMapper;
     @Value("${group-no}")
     private String groupNo;
     private final List<QwUser> qwUserList = Collections.synchronizedList(new ArrayList<>());
@@ -58,7 +63,7 @@ public class SendAppMsg {
     private ThreadPoolTaskExecutor customThreadPool;
 
 
-    public SendAppMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, AsyncSopTestService asyncSopTestService, QwIpadServerMapper qwIpadServerMapper, RedisCacheT<Long> redisCache, QwPushCountMapper qwPushCountMapper) {
+    public SendAppMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, AsyncSopTestService asyncSopTestService, QwIpadServerMapper qwIpadServerMapper, RedisCacheT<Long> redisCache, QwPushCountMapper qwPushCountMapper, QwExternalContactMapper qwExternalContactMapper, LiveWatchLogMapper liveWatchLogMapper) {
         this.qwUserMapper = qwUserMapper;
         this.qwSopLogsMapper = qwSopLogsMapper;
         this.sendServer = sendServer;
@@ -68,6 +73,76 @@ public class SendAppMsg {
         this.qwIpadServerMapper = qwIpadServerMapper;
         this.redisCache = redisCache;
         this.qwPushCountMapper = qwPushCountMapper;
+        this.qwExternalContactMapper = qwExternalContactMapper;
+        this.liveWatchLogMapper = liveWatchLogMapper;
+    }
+
+    private Long resolveLiveId(QwSopCourseFinishTempSetting setting) {
+        if (setting == null) {
+            return null;
+        }
+        if (setting.getLiveId() != null) {
+            return setting.getLiveId();
+        }
+        if (setting.getSetting() == null) {
+            return null;
+        }
+        for (QwSopCourseFinishTempSetting.Setting st : setting.getSetting()) {
+            if (StringUtils.hasText(st.getLiveId())) {
+                return Long.valueOf(st.getLiveId());
+            }
+        }
+        return null;
+    }
+
+    private QwExternalContact loadExternalContact(QwSopLogs qwSopLogs) {
+        if (qwSopLogs.getExternalId() == null) {
+            return null;
+        }
+        return qwExternalContactMapper.selectQwExternalContactById(qwSopLogs.getExternalId());
+    }
+
+    private LiveWatchLog loadLiveWatchLog(QwSopLogs qwSopLogs, QwUser qwUser, QwSopCourseFinishTempSetting setting) {
+        Long liveId = resolveLiveId(setting);
+        if (liveId == null || qwSopLogs.getExternalId() == null || qwUser == null) {
+            return null;
+        }
+        return liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(
+                liveId, String.valueOf(qwUser.getId()), qwSopLogs.getExternalId());
+    }
+
+    /**
+     * 优先发送记录 fsUserId,其次客户 fs_user_id,再次 live_watch_log.user_id
+     */
+    private Long resolveFsUserId(QwSopLogs qwSopLogs, QwUser qwUser, QwSopCourseFinishTempSetting setting) {
+        Long fsUserId = qwSopLogs.getFsUserId();
+        if (fsUserId != null && fsUserId > 0) {
+            return fsUserId;
+        }
+        QwExternalContact contact = loadExternalContact(qwSopLogs);
+        if (contact != null && contact.getFsUserId() != null && contact.getFsUserId() > 0) {
+            return contact.getFsUserId();
+        }
+        LiveWatchLog watchLog = loadLiveWatchLog(qwSopLogs, qwUser, setting);
+        if (watchLog != null && watchLog.getUserId() != null && watchLog.getUserId() > 0) {
+            return watchLog.getUserId();
+        }
+        return null;
+    }
+
+    private Long resolveCompanyUserId(QwUser qwUser, QwSopLogs qwSopLogs, QwSopCourseFinishTempSetting setting) {
+        if (qwUser != null && qwUser.getCompanyUserId() != null) {
+            return qwUser.getCompanyUserId();
+        }
+        QwExternalContact contact = loadExternalContact(qwSopLogs);
+        if (contact != null && contact.getCompanyUserId() != null) {
+            return contact.getCompanyUserId();
+        }
+        LiveWatchLog watchLog = loadLiveWatchLog(qwSopLogs, qwUser, setting);
+        if (watchLog != null && watchLog.getCompanyUserId() != null) {
+            return watchLog.getCompanyUserId();
+        }
+        return null;
     }
 
     private List<QwUser> getQwUserList() {
@@ -148,6 +223,9 @@ public class SendAppMsg {
         // 循环待发送消息
         for (QwSopLogs qwSopLogs : qwSopLogList) {
             long start2 = System.currentTimeMillis();
+            if (qwSopLogs.getSendStatus() != null && qwSopLogs.getSendStatus() != 3L) {
+                continue;
+            }
             QwSopCourseFinishTempSetting setting = JSON.parseObject(qwSopLogs.getContentJson(), QwSopCourseFinishTempSetting.class);
             // 判断消息状态是否满足发送条件
             if (!sendServer.isSendAppLogs(qwSopLogs, setting, user)) {
@@ -166,12 +244,22 @@ public class SendAppMsg {
             if (setting.getType() != 4 && !CollectionUtils.isEmpty(settings) && settings.stream().anyMatch(e -> typeList.contains(e.getContentType()))) {
                 // 发送前次数限制校验
                 Long qwUserId = qwUser.getId();
-                Long customerId = qwSopLogs.getFsUserId();
+                Long fsUserId = resolveFsUserId(qwSopLogs, user, setting);
+                Long resolvedCompanyUserId = resolveCompanyUserId(user, qwSopLogs, setting);
                 Long companyId = qwSopLogs.getCompanyId();
+                if (fsUserId == null || resolvedCompanyUserId == null) {
+                    log.warn("SendAppMsg-缺少IM发送参数, logId={}, fsUserId={}, companyUserId={}, externalId={}",
+                            qwSopLogs.getId(), fsUserId, resolvedCompanyUserId, qwSopLogs.getExternalId());
+                    continue;
+                }
                 boolean txtSendStatus = true;
                 boolean mp3SendStatus = true;
                 boolean courseSendStatus = true;
                 boolean liveCardSendStatus = true;
+                boolean hasLiveCard = allContent.stream().anyMatch(e -> "25".equals(e.getContentType()));
+                if (hasLiveCard) {
+                    liveCardSendStatus = false;
+                }
 //                for (QwSopCourseFinishTempSetting.Setting content : allContent) {
 //                    String contentType = content.getContentType();
 //                    if (!typeList.contains(contentType)) {
@@ -212,24 +300,24 @@ public class SendAppMsg {
                         List<QwSopCourseFinishTempSetting.Setting> linkList = allContent.stream().filter(e -> "9".equals(e.getContentType())).collect(Collectors.toList());
 
                         if (!linkList.isEmpty()) {
-                            courseSendStatus = asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(linkList, qwSopLogs.getCorpId(), user.getCompanyUserId(), qwSopLogs.getFsUserId(), qwSopLogs.getId());
+                            courseSendStatus = asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(linkList, qwSopLogs.getCorpId(), resolvedCompanyUserId, fsUserId, qwSopLogs.getId());
                         }
                         //app文本消息
                         log.info("开始发送app文本消息消息开始,消息{},用户{}", com.alibaba.fastjson.JSONObject.toJSONString(allContent), user.getQwUserName());
                         List<QwSopCourseFinishTempSetting.Setting> txtList = allContent.stream().filter(e -> "15".equals(e.getContentType())).collect(Collectors.toList());
 
                         if (!txtList.isEmpty()) {
-                            txtSendStatus = asyncSopTestService.asyncSendMsgBySopAppTxtNormalIM(txtList, qwSopLogs.getCorpId(), qwUser.getCompanyUserId(), qwSopLogs.getFsUserId(), qwSopLogs.getId());
+                            txtSendStatus = asyncSopTestService.asyncSendMsgBySopAppTxtNormalIM(txtList, qwSopLogs.getCorpId(), resolvedCompanyUserId, fsUserId, qwSopLogs.getId());
                         }
                         //app语音消息
                         log.info("开始发送app语音消息消息开始,消息{},用户{}", com.alibaba.fastjson.JSONObject.toJSONString(allContent), user.getQwUserName());
                         List<QwSopCourseFinishTempSetting.Setting> voiceList = allContent.stream().filter(e -> "16".equals(e.getContentType())).collect(Collectors.toList());
                         if (!voiceList.isEmpty()) {
-                            mp3SendStatus = asyncSopTestService.asyncSendMsgBySopAppMP3NormalIM(voiceList, qwSopLogs.getCorpId(), qwUser.getCompanyUserId(), qwSopLogs.getFsUserId(), qwSopLogs.getId());
+                            mp3SendStatus = asyncSopTestService.asyncSendMsgBySopAppMP3NormalIM(voiceList, qwSopLogs.getCorpId(), resolvedCompanyUserId, fsUserId, qwSopLogs.getId());
                         }
                         List<QwSopCourseFinishTempSetting.Setting> liveCardList = allContent.stream().filter(e -> "25".equals(e.getContentType())).collect(Collectors.toList());
                         if (!liveCardList.isEmpty()) {
-                            liveCardSendStatus = asyncSopTestService.asyncSendMsgBySopAppLiveCardNormalIM(liveCardList, qwSopLogs.getCorpId(), user.getCompanyUserId(), qwSopLogs.getFsUserId(), qwSopLogs.getId());
+                            liveCardSendStatus = asyncSopTestService.asyncSendMsgBySopAppLiveCardNormalIM(liveCardList, qwSopLogs.getCorpId(), resolvedCompanyUserId, fsUserId, companyId, qwSopLogs.getExternalId(), user.getId(), qwSopLogs.getId());
                         }
                         // 发送成功后记录次数
                         // 发送成功后记录次数(只记录真正发送的 content)

+ 38 - 2
fs-service/src/main/java/com/fs/gtPush/service/impl/uniPush2ServiceImpl.java

@@ -29,6 +29,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import com.fs.gtPush.utils.PushUtils;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
+import com.fs.live.domain.LiveWatchLog;
+import com.fs.live.mapper.LiveWatchLogMapper;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwExternalContactMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -60,6 +64,12 @@ public class uniPush2ServiceImpl implements uniPush2Service {
     @Autowired
     private IMConfig imConfig;
 
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+
+    @Autowired
+    private LiveWatchLogMapper liveWatchLogMapper;
+
     @Override
     public PushResult pushMessage(PushReqBean push) {
         SysConfig config = iSysConfigService.selectConfigByConfigKey("his.config");
@@ -105,7 +115,26 @@ public class uniPush2ServiceImpl implements uniPush2Service {
     }
 
     @Override
-    public OpenImResponseDTO pushSopAppLiveCardMsgByExternalIM(String cropId, String title, String linkImageUrl, String link, Long companyUserId, Long fsUserId) throws JsonProcessingException {
+    public OpenImResponseDTO pushSopAppLiveCardMsgByExternalIM(String cropId, String title, String appRealLink, Long liveId, Long companyUserId, Long fsUserId, Long companyId, Long externalId, Long qwUserKey) throws JsonProcessingException {
+        if ((fsUserId == null || fsUserId == 0) && externalId != null) {
+            QwExternalContact contact = qwExternalContactMapper.selectQwExternalContactById(externalId);
+            if (contact != null && contact.getFsUserId() != null && contact.getFsUserId() > 0) {
+                fsUserId = contact.getFsUserId();
+            }
+            if (companyUserId == null && contact != null && contact.getCompanyUserId() != null) {
+                companyUserId = contact.getCompanyUserId();
+            }
+        }
+        if ((fsUserId == null || fsUserId == 0) && liveId != null && externalId != null && qwUserKey != null) {
+            LiveWatchLog watchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(
+                    liveId, String.valueOf(qwUserKey), externalId);
+            if (watchLog != null && watchLog.getUserId() != null && watchLog.getUserId() > 0) {
+                fsUserId = watchLog.getUserId();
+            }
+            if (companyUserId == null && watchLog != null && watchLog.getCompanyUserId() != null) {
+                companyUserId = watchLog.getCompanyUserId();
+            }
+        }
         if (companyUserId == null || fsUserId == null || fsUserId == 0) {
             OpenImResponseDTO errorResponse = new OpenImResponseDTO();
             errorResponse.setErrCode(-1);
@@ -113,6 +142,13 @@ public class uniPush2ServiceImpl implements uniPush2Service {
             errorResponse.setErrDlt("缺少必要参数");
             return errorResponse;
         }
+        if (liveId == null) {
+            OpenImResponseDTO errorResponse = new OpenImResponseDTO();
+            errorResponse.setErrCode(-1);
+            errorResponse.setErrMsg("参数错误:直播间ID为空");
+            errorResponse.setErrDlt("缺少liveId");
+            return errorResponse;
+        }
 
         FsUser fsUser = userService.selectFsUserByUserId(fsUserId);
         if (fsUser == null) {
@@ -123,7 +159,7 @@ public class uniPush2ServiceImpl implements uniPush2Service {
             return errorResponse;
         }
 
-        return openIMService.sendLive(fsUserId, companyUserId, link, title, linkImageUrl, cropId);
+        return openIMService.sendLive(fsUserId, companyUserId, appRealLink, title, liveId, cropId, companyId);
     }
 
     /**

+ 1 - 1
fs-service/src/main/java/com/fs/gtPush/service/uniPush2Service.java

@@ -14,6 +14,6 @@ public interface uniPush2Service {
     PushReqBean getParam(Long userId,String purl,String title,String content,Float type,Integer desType,String imJsonString);
 //    void pushSopAppLinkMsgByExternalIM(String cropId,String linkTile,String linkDescribe,String linkImageUrl,String link,Long companyUserId,Long fsUserId) throws JsonProcessingException;
     OpenImResponseDTO pushSopAppLinkMsgByExternalIM(String cropId, String linkTile, String linkDescribe, String linkImageUrl, String link, Long companyUserId, Long fsUserId) throws JsonProcessingException;
-    OpenImResponseDTO pushSopAppLiveCardMsgByExternalIM(String cropId, String title, String linkImageUrl, String link, Long companyUserId, Long fsUserId) throws JsonProcessingException;
+    OpenImResponseDTO pushSopAppLiveCardMsgByExternalIM(String cropId, String title, String appRealLink, Long liveId, Long companyUserId, Long fsUserId, Long companyId, Long externalId, Long qwUserKey) throws JsonProcessingException;
     void pushIm(Long userId, Long businessId, String purl, String title, String content, Float type, Integer desType,String imJsonString);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/dto/PayloadDTO.java

@@ -1,6 +1,7 @@
 package com.fs.his.dto;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fs.live.domain.Live;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -36,6 +37,8 @@ public class PayloadDTO implements Serializable {
         private Long companyUserId;
         private Long doctorId;
         private Long userInformationId;
+        private Long liveId;
+        private Live live;
     }
 
 }

+ 1 - 1
fs-service/src/main/java/com/fs/im/service/OpenIMService.java

@@ -34,7 +34,7 @@ public interface OpenIMService {
     R accountCheck(String userId, String type);
     void checkAndImportFriend(Long companyUserId,String fsUserId);
     OpenImResponseDTO sendCourse(Long userId,Long companyUserId,String url,String title,String linkImageUrl,String cropId) throws JsonProcessingException;
-    OpenImResponseDTO sendLive(Long userId, Long companyUserId, String url, String title, String linkImageUrl, String cropId) throws JsonProcessingException;
+    OpenImResponseDTO sendLive(Long userId, Long companyUserId, String appRealLink, String title, Long liveId, String cropId, Long companyId) throws JsonProcessingException;
     void checkAndImportFriendByDianBo(Long companyUserId,String fsUserId,String cropId, boolean isUpdate);
 
     OpenImResponseDTO updateUserInfo(CompanyUser companyUser);

+ 33 - 5
fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java

@@ -44,6 +44,7 @@ import com.fs.im.mapper.ImSendLogMapper;
 import com.fs.im.service.OpenIMService;
 import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.im.vo.OpenImResponseDTOTest;
+import com.fs.live.domain.Live;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.github.pagehelper.util.StringUtil;
 import com.google.common.base.Joiner;
@@ -560,7 +561,9 @@ public class OpenIMServiceImpl implements OpenIMService {
     }
 
     @Override
-    public OpenImResponseDTO sendLive(Long userId, Long companyUserId, String url, String title, String linkImageUrl, String cropId) throws JsonProcessingException {
+    public OpenImResponseDTO sendLive(Long userId, Long companyUserId, String appRealLink, String title, Long liveId, String cropId, Long companyId) throws JsonProcessingException {
+        Company company = companyId != null ? companyMapper.selectCompanyById(companyId) : null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
         ObjectMapper objectMapper = new ObjectMapper();
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
         checkAndImportFriendByDianBo(companyUserId, userId.toString(), cropId, true);
@@ -569,10 +572,18 @@ public class OpenIMServiceImpl implements OpenIMService {
         PayloadDTO payload = new PayloadDTO();
         PayloadDTO.Extension extension = new PayloadDTO.Extension();
         payload.setData("live");
+        Live live = buildLiveCardPayload(liveId, title, appRealLink, companyId, companyUserId);
         extension.setTitle(title);
-        extension.setAppRealLink(url);
+        extension.setAppRealLink(appRealLink);
         extension.setSendTime(new Date());
-        extension.setCourseUrl(linkImageUrl);
+        extension.setLiveId(liveId);
+        if (company != null) {
+            extension.setCompanyId(company.getCompanyId());
+        } else if (companyId != null) {
+            extension.setCompanyId(companyId);
+        }
+        extension.setCompanyUserId(companyUserId);
+        extension.setLive(live);
         payload.setExtension(extension);
         imData.setPayload(payload);
         String imJson = objectMapper.writeValueAsString(imData);
@@ -580,8 +591,9 @@ public class OpenIMServiceImpl implements OpenIMService {
 
         OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
         offlinePushInfo.setDesc(title);
-        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
-        offlinePushInfo.setTitle(StringUtils.isNotEmpty(companyUser.getImNickName()) ? companyUser.getImNickName() : companyUser.getNickName());
+        if (companyUser != null) {
+            offlinePushInfo.setTitle(StringUtils.isNotEmpty(companyUser.getImNickName()) ? companyUser.getImNickName() : companyUser.getNickName());
+        }
         offlinePushInfo.setIOSBadgeCount(true);
         offlinePushInfo.setIOSPushSound("");
 
@@ -596,6 +608,22 @@ public class OpenIMServiceImpl implements OpenIMService {
         return openIMSendMsg(openImMsgDTO);
     }
 
+    /**
+     * 组装 IM 直播卡片 payload,避免 fs-ipad-task 等模块查询 SLAVE 库 live 表
+     */
+    private Live buildLiveCardPayload(Long liveId, String title, String appRealLink, Long companyId, Long companyUserId) {
+        if (liveId == null) {
+            return null;
+        }
+        Live live = new Live();
+        live.setLiveId(liveId);
+        live.setLiveName(title);
+        live.setLiveImgUrl(appRealLink);
+        live.setCompanyId(companyId);
+        live.setCompanyUserId(companyUserId);
+        return live;
+    }
+
     @Override
     public OpenImResponseDTO sendPackageUtil(String sendID, String recvID, Integer contentType, String payloadData,String packageName,String packageId){
         try {

+ 5 - 2
fs-service/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java

@@ -567,6 +567,9 @@ public class AsyncSopTestService {
             String cropId,
             Long companyUserId,
             Long fsUserId,
+            Long companyId,
+            Long externalId,
+            Long qwUserKey,
             String logId) {
 
         boolean success = true;
@@ -577,11 +580,11 @@ public class AsyncSopTestService {
             item.setSendRemarks("APP直播卡片发送失败");
 
             try {
-                String liveLink = StringUtils.isNotEmpty(item.getAppLinkUrl()) ? item.getAppLinkUrl() : item.getMiniprogramPage();
                 String title = item.getMiniprogramTitle();
                 String coverUrl = item.getMiniprogramPicUrl();
+                Long liveId = StringUtils.isNotEmpty(item.getLiveId()) ? Long.valueOf(item.getLiveId()) : null;
                 OpenImResponseDTO resp = push2Service.pushSopAppLiveCardMsgByExternalIM(
-                        cropId, title, coverUrl, liveLink, companyUserId, fsUserId);
+                        cropId, title, coverUrl, liveId, companyUserId, fsUserId, companyId, externalId, qwUserKey);
                 if (resp != null && resp.getErrCode() != null && resp.getErrCode() == 0) {
                     item.setSendStatus(1);
                     item.setSendRemarks("发送成功");

+ 94 - 13
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -490,6 +490,10 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
     }
     @Override
     public R sendUserLogsInfoMsg(SendUserLogsInfoMsgParam param) {
+        Long resolvedLiveId = resolveLiveIdFromParam(param);
+        if (resolvedLiveId != null) {
+            param.setLiveId(resolvedLiveId);
+        }
         Boolean sendLiveMsg = Boolean.FALSE;
         if(null != param.getLiveId()){
             sendLiveMsg = Boolean.TRUE;
@@ -709,7 +713,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                 //todo 发个人看课记录处理
                                 try {
                                     if (vo != null && vo.getId() != null) {
-                                        createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),vo.getId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUser.getId().toString(),param.getCorpId());
+                                        createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),vo.getId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUser.getId().toString(),param.getCorpId(), vo.getFsUserId());
                                     }
                                 } catch (Exception e) {
                                     log.error("群聊创建直播看课记录失败!", e);
@@ -815,7 +819,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                             sysConfigLive.getAppId(),
                                             2,
                                             String.valueOf(qwUser.getId()),
-                                            param.getCorpId());
+                                            param.getCorpId(),
+                                            StringUtils.isNotEmpty(groupUser.getFsUserId())
+                                                    ? Long.valueOf(groupUser.getFsUserId()) : null);
                                 } catch (Exception e) {
                                     log.error("APP直播模板解析失败:" + e);
                                 }
@@ -825,10 +831,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                 if (vo == null || vo.getId() == null || StringUtil.strIsNullOrEmpty(st.getLiveId())) {
                                     break;
                                 }
+                                sopLogs.setSendType(20);
                                 String appLiveLink = createAppLiveShortLink(st, param.getCorpId(), qwUser.getId(),
                                         companyUserId, companyId, vo.getId(), createTime);
                                 st.setMiniprogramPage(appLiveLink);
-                                fillAppLiveSetting(st, param.getCorpId(), qwUser, vo.getId());
+                                fillAppLiveSetting(st, param.getCorpId(), qwUser, vo.getId(), vo.getFsUserId());
                                 if ("25".equals(st.getContentType())) {
                                     st.setAppLinkUrl(appLiveLink);
                                     sopLogs.setIsHaveApp(1);
@@ -992,7 +999,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                             Map<String, GroupUserExternalVo> userMap = PubFun.listToMapByGroupObject(e.getUserList(), GroupUserExternalVo::getUserId);
                                             GroupUserExternalVo vo = userMap.get(groupChat.getOwner());
                                             if (vo != null && vo.getId() != null) {
-                                                createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),vo.getId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUser.getId().toString(),param.getCorpId());
+                                                createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),vo.getId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUser.getId().toString(),param.getCorpId(), vo.getFsUserId());
                                             }
                                         });
                                     });
@@ -1123,7 +1130,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 sopLogs.setReceivingStatus(0L);
                 sopLogs.setSopId(param.getSopId());
                 sopLogs.setCorpId(item.getCorpId());
-                sopLogs.setFsUserId(item.getFsUserId());
+                sopLogs.setFsUserId(resolveFsUserIdForLiveWatch(item.getExternalId(), item.getFsUserId()));
                 sopLogs.setSort(30000000);
                 sopLogs.setSendType(5);
                 sopLogs.setExternalUserName(item.getExternalUserName());
@@ -1270,7 +1277,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             FSSysConfig sysConfig= JSON.parseObject(js,FSSysConfig.class);
                             //发个人看课记录处理
                             try {
-                                    createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),item.getExternalId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUserId,param.getCorpId());
+                                    createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),item.getExternalId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUserId,param.getCorpId(), item.getFsUserId());
 
                             } catch (Exception e) {
                                 log.error("群聊创建直播看课记录失败!", e);
@@ -1443,21 +1450,23 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             if (StringUtil.strIsNullOrEmpty(st.getLiveId())) {
                                 throw new BaseException("APP直播短链配置缺少直播间");
                             }
+                            sopLogs.setSendType(20);
                             String appLiveLink = createAppLiveShortLink(st, param.getCorpId(), qwUser.getId(),
                                     companyUserId, companyId, item.getExternalId(), createTime);
                             st.setMiniprogramPage(appLiveLink);
-                            fillAppLiveSetting(st, param.getCorpId(), qwUser, item.getExternalId());
+                            fillAppLiveSetting(st, param.getCorpId(), qwUser, item);
                             break;
                         }
                         case "25": {
                             if (StringUtil.strIsNullOrEmpty(st.getLiveId())) {
                                 throw new BaseException("APP直播卡片配置缺少直播间");
                             }
+                            sopLogs.setSendType(20);
                             String appLiveLink = createAppLiveShortLink(st, param.getCorpId(), qwUser.getId(),
                                     companyUserId, companyId, item.getExternalId(), createTime);
                             st.setMiniprogramPage(appLiveLink);
                             st.setAppLinkUrl(appLiveLink);
-                            fillAppLiveSetting(st, param.getCorpId(), qwUser, item.getExternalId());
+                            fillAppLiveSetting(st, param.getCorpId(), qwUser, item);
                             sopLogs.setIsHaveApp(1);
                             sopLogs.setAppSendStatus(0);
                             break;
@@ -1540,6 +1549,10 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
     @Override
     public R sendUserLogsInfoMsgSop(SendUserLogsInfoMsgParam param) {
+        Long resolvedLiveId = resolveLiveIdFromParam(param);
+        if (resolvedLiveId != null) {
+            param.setLiveId(resolvedLiveId);
+        }
         List<String> sopUserLogsIds = sopUserLogsMapper.getSopUserLogsIds(param);
         if(sopUserLogsIds == null || sopUserLogsIds.isEmpty()){
             throw new BaseException("SOP没有可发送的营期数据");
@@ -1634,8 +1647,10 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
         // 遍历分组
         int finalSort = sort;
-        if(null != param.getLiveId()){
+        Long resolvedLiveId = resolveLiveIdFromParam(param);
+        if (resolvedLiveId != null) {
             sendType = 20;
+            param.setLiveId(resolvedLiveId);
         }
         int finalSendType = sendType;
         groupedLogs.forEach((key, logs) -> {
@@ -1713,7 +1728,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             sopLogs.setReceivingStatus(0L);
             sopLogs.setSopId(item.getSopId());
             sopLogs.setCorpId(item.getCorpId());
-            sopLogs.setFsUserId(item.getFsUserId());
+            sopLogs.setFsUserId(resolveFsUserIdForLiveWatch(item.getExternalId(), item.getFsUserId()));
 
             sopLogs.setSort(finalSort);
             sopLogs.setSendType(finalSendType);
@@ -1952,7 +1967,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     FSSysConfig sysConfig= JSON.parseObject(json,FSSysConfig.class);
                     //todo 发个人看课记录处理
                     try {
-                        createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),item.getExternalId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, String.valueOf(qwUser.getId()),param.getCorpId());
+                        createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),item.getExternalId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, String.valueOf(qwUser.getId()),param.getCorpId(), item.getFsUserId());
                     } catch (Exception e) {
                         log.error("群聊创建直播看课记录失败!", e);
                     }
@@ -2131,6 +2146,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     if (StringUtil.strIsNullOrEmpty(st.getLiveId())) {
                         throw new BaseException("APP直播短链配置缺少直播间");
                     }
+                    sopLogs.setSendType(20);
                     String appLiveLink = createAppLiveShortLink(st, param.getCorpId(), qwUser.getId(),
                             companyUserId, companyId, item.getExternalId(), dataTime);
                     st.setMiniprogramPage(appLiveLink);
@@ -2141,6 +2157,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     if (StringUtil.strIsNullOrEmpty(st.getLiveId())) {
                         throw new BaseException("APP直播卡片配置缺少直播间");
                     }
+                    sopLogs.setSendType(20);
                     String appLiveLink = createAppLiveShortLink(st, param.getCorpId(), qwUser.getId(),
                             companyUserId, companyId, item.getExternalId(), dataTime);
                     st.setMiniprogramPage(appLiveLink);
@@ -2421,11 +2438,18 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
     private void fillAppLiveSetting(QwSopCourseFinishTempSetting.Setting st, String corpId, QwUser qwUser,
                                     SopUserLogsInfo item) {
-        fillAppLiveSetting(st, corpId, qwUser, item != null ? item.getExternalId() : null);
+        fillAppLiveSetting(st, corpId, qwUser,
+                item != null ? item.getExternalId() : null,
+                item != null ? item.getFsUserId() : null);
     }
 
     private void fillAppLiveSetting(QwSopCourseFinishTempSetting.Setting st, String corpId, QwUser qwUser,
                                     Long externalId) {
+        fillAppLiveSetting(st, corpId, qwUser, externalId, null);
+    }
+
+    private void fillAppLiveSetting(QwSopCourseFinishTempSetting.Setting st, String corpId, QwUser qwUser,
+                                    Long externalId, Long fsUserId) {
         String liveConfigJson = configService.selectConfigByKey("his.config");
         FSSysConfig liveSysConfig = JSON.parseObject(liveConfigJson, FSSysConfig.class);
         if (liveSysConfig != null && !StringUtil.strIsNullOrEmpty(liveSysConfig.getAppId())) {
@@ -2441,7 +2465,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             try {
                 createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),
                         externalId.toString(), Long.valueOf(st.getLiveId()), liveSysConfig.getAppId(), 2,
-                        String.valueOf(qwUser.getId()), corpId);
+                        String.valueOf(qwUser.getId()), corpId, fsUserId);
             } catch (Exception e) {
                 log.error("APP直播一键群发创建看课记录失败", e);
             }
@@ -2451,6 +2475,38 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 : st.getMiniprogramPicUrl());
     }
 
+    /**
+     * 从请求参数或规则配置中解析直播间ID
+     */
+    private Long resolveLiveIdFromParam(SendUserLogsInfoMsgParam param) {
+        if (param.getLiveId() != null) {
+            return param.getLiveId();
+        }
+        if (StringUtil.strIsNullOrEmpty(param.getSetting())) {
+            return null;
+        }
+        try {
+            List<QwSopCourseFinishTempSetting.Setting> list = JSONArray.parseArray(
+                    param.getSetting(), QwSopCourseFinishTempSetting.Setting.class);
+            if (list == null) {
+                return null;
+            }
+            for (QwSopCourseFinishTempSetting.Setting st : list) {
+                if (st.getContentType() == null || StringUtil.strIsNullOrEmpty(st.getLiveId())) {
+                    continue;
+                }
+                String contentType = st.getContentType();
+                if ("12".equals(contentType) || "18".equals(contentType) || "19".equals(contentType)
+                        || "24".equals(contentType) || "25".equals(contentType)) {
+                    return Long.valueOf(st.getLiveId());
+                }
+            }
+        } catch (Exception e) {
+            log.error("解析直播间ID失败", e);
+        }
+        return null;
+    }
+
     private String createAppLiveShortLink(QwSopCourseFinishTempSetting.Setting st, String corpId, Long qwUserId,
                                           String companyUserId, String companyId, Long externalId, Date sendTime) {
         FsCourseLink link = new FsCourseLink();
@@ -2541,7 +2597,12 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
      * @param corpId
      */
     public void createLiveWatchLogAndInsert(String companyId,String companyUserId,String externalId,Long liveId,String appId,Integer logSource,String qwUserId,String corpId){
+        createLiveWatchLogAndInsert(companyId, companyUserId, externalId, liveId, appId, logSource, qwUserId, corpId, null);
+    }
+
+    public void createLiveWatchLogAndInsert(String companyId,String companyUserId,String externalId,Long liveId,String appId,Integer logSource,String qwUserId,String corpId, Long fsUserId){
         try{
+            Long resolvedUserId = resolveFsUserIdForLiveWatch(Long.valueOf(externalId), fsUserId);
             // 写入对应数据源的记录表
             LiveWatchLog itemLiveWatchLog = new LiveWatchLog();
             itemLiveWatchLog.setLiveId(liveId);
@@ -2554,6 +2615,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             itemLiveWatchLog.setQwUserId(qwUserId);
             itemLiveWatchLog.setExternalContactId(Long.valueOf(externalId));
             itemLiveWatchLog.setCorpId(corpId);
+            if (resolvedUserId != null) {
+                itemLiveWatchLog.setUserId(resolvedUserId);
+            }
             if(liveWatchLogMapper.updateLiveWatchLogCondition(itemLiveWatchLog) > 0){
 
             }else{
@@ -2568,6 +2632,23 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
     }
 
+    /**
+     * 优先使用营期 fsUserId,为空时从 qw_external_contact.fs_user_id 回填
+     */
+    private Long resolveFsUserIdForLiveWatch(Long externalId, Long fsUserId) {
+        if (fsUserId != null && fsUserId > 0) {
+            return fsUserId;
+        }
+        if (externalId == null) {
+            return null;
+        }
+        QwExternalContact contact = qwExternalContactMapper.selectQwExternalContactById(externalId);
+        if (contact != null && contact.getFsUserId() != null && contact.getFsUserId() > 0) {
+            return contact.getFsUserId();
+        }
+        return null;
+    }
+
     /**
      * 构建福袋记录对象
      */

+ 1 - 0
fs-service/src/main/resources/mapper/live/LiveWatchLogMapper.xml

@@ -198,6 +198,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             sop_create_time = NOW(),
             send_app_id = #{liveWatchLog.sendAppId},
             log_source = #{liveWatchLog.logSource}
+            <if test="liveWatchLog.userId != null">, user_id = #{liveWatchLog.userId}</if>
         where external_contact_id = #{liveWatchLog.externalContactId}
             and live_id = #{liveWatchLog.liveId}
             and qw_user_id = #{liveWatchLog.qwUserId}

+ 4 - 1
fs-service/src/main/resources/mapper/sop/QwSopLogsMapper.xml

@@ -230,7 +230,9 @@
             send_status = 5,
             receiving_status = 4,
             remark = #{remark},
-            real_send_time = NOW()
+            real_send_time = NOW(),
+            app_send_status = CASE WHEN is_have_app = 1 THEN 2 ELSE app_send_status END,
+            app_send_remark = CASE WHEN is_have_app = 1 THEN #{remark} ELSE app_send_remark END
         WHERE
             id = #{id}
     </update>
@@ -893,6 +895,7 @@
           AND ql.send_type > 1
           AND ql.is_have_app = 1
           AND ql.app_send_status = 0
+          AND ql.send_status = 3
         <![CDATA[
           AND ql.send_time <= now()
         ]]>