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

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java

caoliqin преди 2 дни
родител
ревизия
9acc40b632
променени са 22 файла, в които са добавени 634 реда и са изтрити 62 реда
  1. 130 0
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  2. 14 4
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  3. 26 39
      fs-live-app/src/main/java/com/fs/live/task/LiveCompletionPointsTask.java
  4. 1 1
      fs-live-app/src/main/java/com/fs/live/task/Task.java
  5. 7 2
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  6. 6 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  7. 4 5
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  8. 7 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  9. 3 0
      fs-service/src/main/java/com/fs/live/vo/LiveVo.java
  10. 2 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopCourseFinishTempSetting.java
  11. 2 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting.java
  12. 3 1
      fs-service/src/main/java/com/fs/sop/domain/QwSopLogs.java
  13. 7 0
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  14. 2 2
      fs-service/src/main/resources/application-config-druid-cfryt.yml
  15. 2 2
      fs-service/src/main/resources/application-config-druid-hat.yml
  16. 2 2
      fs-service/src/main/resources/application-config-druid-hsyy.yml
  17. 104 0
      fs-service/src/main/resources/application-config-druid-shdn.yml
  18. 2 2
      fs-service/src/main/resources/application-config-druid-sxjz.yml
  19. 2 1
      fs-service/src/main/resources/application-config-druid-syysy.yml
  20. 2 1
      fs-service/src/main/resources/application-config-druid-yxj.yml
  21. 141 0
      fs-service/src/main/resources/application-config-shdn.yml
  22. 165 0
      fs-service/src/main/resources/application-druid-shdn.yml

+ 130 - 0
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -21,6 +21,8 @@ import com.fs.his.domain.FsUser;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.ipad.IpadSendUtils;
 import com.fs.ipad.vo.*;
+import com.fs.live.domain.LiveWatchLog;
+import com.fs.live.mapper.LiveWatchLogMapper;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwUserVideo;
@@ -63,6 +65,8 @@ public class IpadSendServer {
 
 
     private static final List<String> PROJECT_NAMES = Arrays.asList("济南联志健康", "北京存在文化","宽益堂");
+    private final LiveWatchLogMapper liveWatchLogMapper;
+
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
         // 发送参数原本的appid
         String appid = content.getMiniprogramAppid();
@@ -432,6 +436,132 @@ public class IpadSendServer {
         return true;
     }
 
+    /**
+     * 直播间判定消息
+     * @param qwSopLogs
+     * @param setting
+     * @param qwUser
+     * @return
+     */
+    public boolean isSendLogsLive(QwSopLogs qwSopLogs, QwSopCourseFinishTempSetting setting, QwUser qwUser){
+        if(qwSopLogs.getSendStatus() != 3){
+            log.info("直播状态异常不发送:{}, LOGID: {}", qwUser.getQwUserName(), qwSopLogs.getId());
+            return false;
+        }
+        if(redisCache.getCacheObject("qw:user:id:" + qwUser.getId()) != null){
+            log.info("直播频率异常不发送:{}", qwUser.getQwUserName());
+            return false;
+        }
+
+        boolean noSop = qwSopLogs.getSendType() != 3 && qwSopLogs.getSendType() != 7;
+
+        if (qwSopLogs.getExpiryTime() == null && noSop) {
+            // 作废消息
+            log.warn("直播SOP_LOG_ID:{}, 直播SOP任务被删除", qwSopLogs.getId());
+            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "直播SOP任务被删除");
+            return false;
+        }
+
+        LocalDateTime sendTime = DateUtil.stringToLocalDateTime(qwSopLogs.getSendTime());
+        LocalDateTime expiryDateTime;
+
+        // 判断是否过期
+        if(qwSopLogs.getSendType() == 3 || qwSopLogs.getSendType() == 7){
+            expiryDateTime = sendTime.plusHours(12);
+        }else{
+            expiryDateTime = sendTime.plusHours(qwSopLogs.getExpiryTime());
+        }
+
+        if (LocalDateTime.now().isAfter(expiryDateTime)) {
+            // 作废消息
+            log.warn("直播SOP_LOG_ID:{}, 已过期,不发送", qwSopLogs.getId());
+            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "已过期,不发送");
+            return false;
+        }
+
+        if (setting.getCourseType() == null && noSop && setting.getType() == 2) {
+            log.warn("直播SOP_LOG_ID:{}, 模板未选消息类型,不发送", qwSopLogs.getId());
+            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "直播模板未选消息类型,不发送");
+            return false;
+        }
+
+        // 查询视频是否下架
+//        if(setting.getVideoId()!= null){
+//            FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId( setting.getVideoId().longValue());
+//            if(video != null){
+//                if(video.getIsOnPut()!=null && video.getIsOnPut() == 1){
+//                    log.warn("SOP_LOG_ID:{}, 视频已下架,不发送", qwSopLogs.getId());
+//                    qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "视频已下架,不发送");
+//                    return false;
+//                }
+//            }
+//        }
+        Long queryLiveId = null;
+        queryLiveId = setting.getLiveId();
+        if(null == queryLiveId){
+            for (QwSopCourseFinishTempSetting.Setting a : setting.getSetting()) {
+                if (StringUtils.isNotBlank(a.getLiveId())) {
+                    queryLiveId = Long.valueOf(a.getLiveId());
+                    break;
+                }
+            }
+        }
+        LiveWatchLog liveWatchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(
+                queryLiveId,
+                String.valueOf(qwUser.getId()),
+                qwSopLogs.getExternalId()
+        );
+        Integer courseType = setting.getCourseType();
+        String logId = qwSopLogs.getId();
+        if(null != liveWatchLog){
+                    if (!QwSopLogsServiceImpl.isCourseTypeValid(courseType, liveWatchLog.getLogType())) {
+                        // 作废消息
+                        log.warn("SOP_LOG_ID:{}, 看课状态未满足,不发送", qwSopLogs.getId());
+                        qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "看课状态未满足,不发送");
+                        return false;
+                    }
+        }
+        else{
+            log.warn("SOP_LOG_ID:{}, 无观看记录,不发送", qwSopLogs.getId());
+            qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "无观看记录,不发送");
+            return false;
+        }
+//        if (qwSopLogs.getSendType() != 6 && noSop) {
+//            // 客户的信息
+////            QwExternalContactHParam contactHParam = new QwExternalContactHParam();
+////            contactHParam.setUserId(qwUser.getQwUserId().trim());
+////            contactHParam.setExternalUserId(qwSopLogs.getExternalUserId().trim());
+////            contactHParam.setCorpId(qwUser.getCorpId().trim());
+//            Integer courseType = setting.getCourseType();
+//            if (setting.getType() == 2 && courseType != 0) {// 课程消息,进行复杂的条件判断
+////                log.debug("企微查询:{}", contactHParam);
+////                Long qwExternalContactId = qwExternalContactMapper.getQwExternalContactId(contactHParam);
+//                FsCourseWatchLog watchLog = watchLogService.getWatchCourseLogVideoBySop(
+//                        setting.getVideoId().longValue(),
+//                        String.valueOf(qwUser.getId()),
+//                        qwSopLogs.getExternalId()
+//                );
+//                log.debug("ID:{}-看课记录参数:videoID:{}, qwUserID:{}, extID:{}", qwSopLogs.getId(), setting.getVideoId().longValue(), qwUser.getId(), qwSopLogs.getExternalId());
+//                log.debug("ID:{}-看课记录:{}", qwSopLogs.getId(), watchLog);
+//                String logId = qwSopLogs.getId();
+//                if (watchLog != null) {
+//                    //新逻辑
+//                    if (!QwSopLogsServiceImpl.isCourseTypeValid(courseType, watchLog.getLogType())) {
+//                        // 作废消息
+//                        log.warn("SOP_LOG_ID:{}, 看课状态未满足,不发送", qwSopLogs.getId());
+//                        qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "看课状态未满足,不发送");
+//                        return false;
+//                    }
+//                } else {
+//                    log.warn("SOP_LOG_ID:{}, 无观看记录,不发送", qwSopLogs.getId());
+//                    qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "无观看记录,不发送");
+//                    return false;
+//                }
+//            }
+//        }
+        return true;
+    }
+
     public void send(QwSopCourseFinishTempSetting.Setting content, QwUser qwUser, QwSopLogs qwSopLogs, Map<String, FsCoursePlaySourceConfig> miniMap, BaseVo parentVo) {
         BaseVo vo = new BaseVo();
         vo.setId(Long.parseLong(qwSopLogs.getId()));

+ 14 - 4
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -191,10 +191,20 @@ public class SendMsg {
         for (QwSopLogs qwSopLogs : qwSopLogList) {
             long start2 = System.currentTimeMillis();
             QwSopCourseFinishTempSetting setting = JSON.parseObject(qwSopLogs.getContentJson(), QwSopCourseFinishTempSetting.class);
-            // 判断消息状态是否满足发送条件
-            if (!sendServer.isSendLogs(qwSopLogs, setting, user)) {
-                log.info("销售:{}, 消息发送条件未满足:{}", user.getQwUserName(), qwSopLogs.getId());
-                continue;
+            //直播的sendType:20单独走判断 其他的走以前的逻辑
+            boolean isSendLive = Integer.valueOf(20).equals(qwSopLogs.getSendType());
+            if(isSendLive){
+                if (!sendServer.isSendLogsLive(qwSopLogs, setting, user)) {
+                    log.info("销售:{}, 直播消息发送条件未满足:{}", user.getQwUserName(), qwSopLogs.getId());
+                    continue;
+                }
+            }
+            else{
+                // 判断消息状态是否满足发送条件
+                if (!sendServer.isSendLogs(qwSopLogs, setting, user)) {
+                    log.info("销售:{}, 消息发送条件未满足:{}", user.getQwUserName(), qwSopLogs.getId());
+                    continue;
+                }
             }
             log.info("进入发送消息状态:{}", qwSopLogs.getId());
             String key = "qw:logs:pad:send:id:" + qwSopLogs.getId();

+ 26 - 39
fs-live-app/src/main/java/com/fs/live/task/LiveCompletionPointsTask.java

@@ -62,10 +62,11 @@ public class LiveCompletionPointsTask {
                     
                     // 使用Hash结构获取该直播间所有用户的观看时长
                     String hashKey = "live:watch:duration:hash:" + liveId;
-                    Map<Object, Object> userDurations = redisCache.redisTemplate.opsForHash().entries(hashKey);
+                    Map<Object, Object> userDurations = redisCache.hashEntries(hashKey);
                     
                     if (userDurations == null || userDurations.isEmpty()) {
-                        log.debug("直播间没有观看时长数据, liveId={}, liveName={}", liveId, live.getLiveName());
+                        log.warn("直播间没有观看时长数据, liveId={}, liveName={}, Redis Key: {}, userDurations={}", 
+                                liveId, live.getLiveName(), hashKey, userDurations);
                         continue;
                     }
                     
@@ -76,9 +77,9 @@ public class LiveCompletionPointsTask {
                     for (Map.Entry<Object, Object> entry : userDurations.entrySet()) {
                         try {
                             Long userId = Long.parseLong(entry.getKey().toString());
+                            Long duration = Long.parseLong(entry.getValue().toString());  // 从 Redis 直接获取观看时长
                             
-                            // 4. 检查并创建完课记录(传null,自动累计直播+回放时长)
-                            completionPointsRecordService.checkAndCreateCompletionRecord(liveId, userId, null);
+                            completionPointsRecordService.checkAndCreateCompletionRecord(liveId, userId, duration);
 
                             // 5. 检查是否有新的完课记录待领取,推送弹窗消息(防重复)
                             sendCompletionNotificationOnce(liveId, userId);
@@ -98,43 +99,29 @@ public class LiveCompletionPointsTask {
         }
     }
 
-    /**
-     * 发送完课通知(通过WebSocket推送弹窗) - 防重复版本
-     */
-    private void sendCompletionNotificationOnce(Long liveId, Long userId) {
-        try {
-            // 1. 检查 Redis 是否已推送过(防止每分钟都推送)
-            String notifyKey = "live:completion:notified:" + liveId + ":" + userId;
-            Boolean hasNotified = redisCache.hasKey(notifyKey);
-            
-            if (Boolean.TRUE.equals(hasNotified)) {
-                return;  // 已经推送过,不再重复推送
-            }
-            
-            // 2. 查询未领取的完课记录
-            List<LiveCompletionPointsRecord> unreceivedRecords = 
-                completionPointsRecordService.getUserUnreceivedRecords(liveId, userId);
-            
-            if (unreceivedRecords != null && !unreceivedRecords.isEmpty()) {
-                // 3. 构造弹窗消息
-                SendMsgVo sendMsgVo = new SendMsgVo();
-                sendMsgVo.setLiveId(liveId);
-                sendMsgVo.setUserId(userId);
-                sendMsgVo.setCmd("completionPoints");
-                sendMsgVo.setMsg("完成任务!");
-                sendMsgVo.setData(JSONObject.toJSONString(unreceivedRecords.get(0)));
+   private void sendCompletionNotificationOnce(Long liveId, Long userId) {
+    try {
+        // 查询未领取的完课记录
+        List<LiveCompletionPointsRecord> unreceivedRecords = 
+            completionPointsRecordService.getUserUnreceivedRecords(liveId, userId);
+        
+        if (unreceivedRecords == null || unreceivedRecords.isEmpty()) {
+            return;
+        }
 
-                // 4. 通过WebSocket发送给特定用户
-                webSocketServer.sendCompletionPointsMessage(liveId, userId, sendMsgVo);
-                
-                // 5. 记录已推送,24小时后过期(第二天可以再次推送)
-                redisCache.setCacheObject(notifyKey, "1", 24, TimeUnit.HOURS);
-                
-                log.info("发送完课积分弹窗通知成功, liveId={}, userId={}, points={}", 
-                        liveId, userId, unreceivedRecords.get(0).getPointsAwarded());
-            }
+        SendMsgVo sendMsgVo = new SendMsgVo();
+        sendMsgVo.setLiveId(liveId);
+        sendMsgVo.setUserId(userId);
+        sendMsgVo.setCmd("completionPoints");
+        sendMsgVo.setMsg("完成任务!");
+        sendMsgVo.setData(JSONObject.toJSONString(unreceivedRecords.get(0)));
+        
+        webSocketServer.sendCompletionPointsMessage(liveId, userId, sendMsgVo);
+        
+        log.info("发送完课积分弹窗通知, liveId={}, userId={}, points={}", 
+                liveId, userId, unreceivedRecords.get(0).getPointsAwarded());
         } catch (Exception e) {
-            log.error("发送完课通知失败, liveId={}, userId={}", liveId, userId, e);
+        log.error("发送完课通知失败", e);
         }
     }
 }

+ 1 - 1
fs-live-app/src/main/java/com/fs/live/task/Task.java

@@ -723,7 +723,7 @@ public class Task {
                     
                     // 使用Hash结构存储每个直播间的观看时长
                     String hashKey = "live:watch:duration:hash:" + liveId;
-                    Map<Object, Object> userDurations = redisCache.redisTemplate.opsForHash().entries(hashKey);
+                    Map<Object, Object> userDurations = redisCache.hashEntries(hashKey);
                     
                     if (userDurations == null || userDurations.isEmpty()) {
                         continue;

+ 7 - 2
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -950,6 +950,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             switch (setting.getContentType()) {
                 //直播小程序单独
                 case "12":
+                    //直播发送类型
+                    sopLogs.setSendType(20);
+                    clonedContent.setLiveId(setting.getLiveId());
                     String sortLiveLink;
                     sortLiveLink = "/pages_course/living.html?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + setting.getLiveId() + "&corpId=" + logVo.getCorpId()+"&qwUserId=" + qwUserId;
                     String json = configService.selectConfigByKey("his.config");
@@ -1198,6 +1201,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     break;
                 //直播小程序单独
                 case "12":
+                    sopLogs.setSendType(20);
+                    clonedContent.setLiveId(setting.getLiveId());
                     String sortLiveLink;
                     sortLiveLink = "/pages_course/living.html?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + setting.getLiveId()+"&corpId=" +logVo.getCorpId()+"&qwUserId=" + qwUserId;
                     String json = configService.selectConfigByKey("his.config");
@@ -1984,7 +1989,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     public void batchInsertLiveWatchLog(List<LiveWatchLog> liveWatchLogToInsert) {
         try {
             List<LiveWatchLog> lastInsertList = new ArrayList<>();
-            //判断是否存在数据 liveId + his_qw_external_contact_id 唯一
+            //判断是否存在数据 liveId + his_qw_external_contact_id + qwUserId 唯一
             for (LiveWatchLog liveWatchLog : liveWatchLogToInsert) {
                 //判断是否存在数据 存在的数据直接更新发送时间
                 if(liveWatchLogMapper.updateLiveWatchLogCondition(liveWatchLog) > 0){
@@ -1997,7 +2002,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             }
 //            log.info("批量插入 LiveWatchLog 完成,共插入 {} 条记录。", liveWatchLogToInsert.size());
         } catch (Exception e) {
-            log.error("批量插入 LiveWatchLog 失败: {}", e.getMessage(), e);
+            log.error("批量插入 LiveWatchLog 失败: {}", liveWatchLogToInsert, e);
             // 可选:将失败的数据记录到失败队列或持久化存储以便后续重试
         }
     }

+ 6 - 0
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -246,6 +246,12 @@ public interface FsUserCourseVideoMapper extends BaseMapper<FsUserCourseVideo> {
             "WHERE file_key = #{fileKey}")
     void updateFsUserCourseVideoByFileKey(FsUserCourseVideo courseVideo);
 
+    @Update("UPDATE fs_user_course_video " +
+            "SET line_two = #{lineTwo}, " +
+            "    update_time = NOW() " +  // 添加更新时间
+            "WHERE file_key = #{fileKey}")
+    void updateFsUserCourseVideoByFileKeyForHsy(FsUserCourseVideo courseVideo);
+
     @Select("select title from fs_user_course_video WHERE video_id=#{videoId}")
     String selectFsUserCourseVideoByVideoForTitle(@Param("videoId") Long videoId);
 

+ 4 - 5
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -4293,9 +4293,6 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 System.out.println(resp.getResponseMetadata().getError());
                 System.exit(-1);
             }else {
-                if (StringUtils.isEmpty(resp.getResult().getData().getMediaInfoList(0).getVid())){
-                    return;
-                }
                 FsVideoResource video = new FsVideoResource();
                 video.setId(videoResource.getId());
                 //视频上传失败,清空jobid
@@ -4333,7 +4330,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                     updateMediaPublishStatus(videoResource.getHsyVid());
                 }
                 //更新视频资源
-                String url = cloudHostProper.volcengineUrl+"/"+resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri();
+//                String storeUri = resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri();
+//                String uri = storeUri.substring(storeUri.indexOf("/") + 1);
+                String url = cloudHostProper.volcengineUrl+"/"+resp.getResult().getMediaInfoList(0).getSourceInfo().getFileName();
 
                 FsVideoResource fsVideoResource = new FsVideoResource();
                 fsVideoResource.setId(videoResource.getId());
@@ -4344,7 +4343,7 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 FsUserCourseVideo courseVideo = new FsUserCourseVideo();
                 courseVideo.setFileKey(videoResource.getFileKey());
                 courseVideo.setLineTwo(url);
-                fsUserCourseVideoMapper.updateFsUserCourseVideoByFileKey(courseVideo);
+                fsUserCourseVideoMapper.updateFsUserCourseVideoByFileKeyForHsy(courseVideo);
 
             }
             System.out.println(resp);

+ 7 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -921,6 +921,13 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
             return R.ok("发送红包成功").put("orderCode", transferBatchesResult.getOutBatchNo()).put("batchId", transferBatchesResult.getBatchId()).put("mchId", config.getMchId());
         } catch (Exception e) {
             logger.error("商家转账支付失败:参数: {} :原因: {}",JSON.toJSONString(param), e.getMessage(),e);
+            if (e instanceof WxPayException && "济南联志健康".equals(signProjectName)) {
+                WxPayException wxPayException = (WxPayException) e;
+                String customErrorMsg = wxPayException.getCustomErrorMsg();
+                if (null != customErrorMsg && customErrorMsg.startsWith("商户运营账户资金不足")) {
+                    return R.error("[红包领取] 账户余额不足,请联系管理员!");
+                }
+            }
             throw new RuntimeException(e);
         }
     }

+ 3 - 0
fs-service/src/main/java/com/fs/live/vo/LiveVo.java

@@ -33,6 +33,9 @@ public class LiveVo {
     private String liveImgUrl;
 
     private String liveConfig;
+    
+    /** 直播配置 */
+    private String configJson;
 
     private String videoUrl;
     private Long videoId;

+ 2 - 0
fs-service/src/main/java/com/fs/qw/vo/QwSopCourseFinishTempSetting.java

@@ -23,6 +23,8 @@ public class QwSopCourseFinishTempSetting implements Serializable,Cloneable{
     //课节
     private Integer videoId;
 
+    private Long liveId;
+
     private List<Setting> setting;
 
     @Override

+ 2 - 0
fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting.java

@@ -47,6 +47,8 @@ public class QwSopTempSetting implements Serializable{
         
         private Integer isAtAll;
 
+        private Long liveId;
+
         @Override
         public Content clone() {
             try {

+ 3 - 1
fs-service/src/main/java/com/fs/sop/domain/QwSopLogs.java

@@ -61,7 +61,9 @@ public class QwSopLogs implements Serializable {
     private String msgId;
 
     /**
-    * 发送类型 1企微发送 2 Ai接口发送  3:完课发送  4:AI对话 5一键群发 6一键群发app
+    * 发送类型  1企微发送 2 Ai接口发送  3:完课发送  4:AI对话 5一键群发 6客户群群发
+     * 7欢迎语补发 8AI 9清除草稿 10发送草稿 11 课程模板类型 12 一键群发APP
+     * 13 注册链接 14 福袋  20直播间发送
     */
     private Integer sendType;
 

+ 7 - 0
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -690,6 +690,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                 break;
                             //直播小程序单独
                             case "12":
+                                sopLogs.setSendType(20);
+                                setting.setLiveId(Long.valueOf(st.getLiveId()));
                                 String sortLiveLink = "/pages_course/living.html?companyId=" + qwUser.getCompanyUserId() + "&companyUserId=" + companyUserId + "&liveId=" + st.getLiveId() + "&corpId=" + param.getCorpId()+"&qwUserId=" + qwUser.getId() +"&externalId=" + vo.getId().toString();
                                 st.setContentType("4");
                                 String js = configService.selectConfigByKey("his.config");
@@ -881,6 +883,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                 break;
                             //直播小程序单独
                             case "12":
+                                sopLogs.setSendType(20);
+                                setting.setLiveId(setting.getLiveId());
                                 String sortLiveLink = "/pages_course/living.html?companyId=" + qwUser.getCompanyUserId() + "&companyUserId=" + qwUser.getCompanyUserId() + "&liveId=" + st.getLiveId() + "&corpId=" +param.getCorpId()+"&qwUserId=" + qwUser.getId() + "&chatId=" + groupChat.getChatId();
                                 st.setContentType("4");
                                 String js = configService.selectConfigByKey("his.config");
@@ -1154,6 +1158,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             break;
                         //直播小程序单独
                         case "12":
+                            sopLogs.setSendType(20);
+                            setting.setLiveId(Long.valueOf(st.getLiveId()));
                             String sortLiveLink = "/pages_course/living.html?companyId=" + qwUser.getCompanyUserId() + "&companyUserId=" + qwUser.getCompanyUserId() + "&liveId=" + st.getLiveId() + "&corpId=" + param.getCorpId()+"&qwUserId=" + qwUserId +"&externalId=" + item.getExternalId().toString();
                             st.setContentType("4");
                             String js = configService.selectConfigByKey("his.config");
@@ -1746,6 +1752,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 //直播小程序单独
                 case "12":
                     String sortLiveLink;
+                    sopLogs.setSendType(20);
                     sortLiveLink = "/pages_course/living.html?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + st.getLiveId() + "&corpId=" + param.getCorpId()+"&qwUserId=" + qwUser.getId() +"&externalId=" + item.getExternalId().toString();
 
 

+ 2 - 2
fs-service/src/main/resources/application-config-druid-cfryt.yml

@@ -82,8 +82,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 赤峰润
   projectCode: ryt
-  spaceName:
-  volcengineUrl:
+  spaceName: ryt-2114522511
+  volcengineUrl: https://rytvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://ysy-1329817240.cos.ap-guangzhou.myqcloud.com/ysy/20250820/2c47e4f105b641b4a49df50a77338e32.png

+ 2 - 2
fs-service/src/main/resources/application-config-druid-hat.yml

@@ -87,8 +87,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 恒安图
   projectCode: HAT
-  spaceName:
-  volcengineUrl:
+  spaceName: hat-2114522511
+  volcengineUrl: https://hatvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://hat-1323137866.cos.ap-chongqing.myqcloud.com/fs/20250928/hatlogo.png

+ 2 - 2
fs-service/src/main/resources/application-config-druid-hsyy.yml

@@ -82,8 +82,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 河山医院
   projectCode: heshanyy
-  spaceName:
-  volcengineUrl:
+  spaceName: heshanyy-2114522511
+  volcengineUrl: https://heshanyyvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://hsyy-1348049832.cos.ap-chongqing.myqcloud.com/hsyy.jpg

+ 104 - 0
fs-service/src/main/resources/application-config-druid-shdn.yml

@@ -0,0 +1,104 @@
+baidu:
+  token: 12313231232
+  back-domain: https://www.xxxx.com
+#配置
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  miniapp:
+    configs:
+      - appid: wx94951f52d3ac5e25   #北京存在文化
+        secret: bfe27b20c6e3c4232a1d4ef36228e84b #北京存在文化
+        token: Ncbnd7lJvkripxxna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+  cp:
+    corpId: wwa46ffb9ff6ac35b8 #企业ID北京存在文化
+    appConfigs:
+      - agentId: 1000070       #北京存在文化
+        secret: pu2EFz6gY2Fo2K-aRUxLPaAkKIaMJJRp8ES9JdpHkp4 #北京存在文化
+        token: PPKOdAlCoMO
+        aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  pay:
+    appId:  #微信公众号或者小程序等的appid
+    mchId:  #微信支付商户号
+    mchKey:  #微信支付商户密钥
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl: https://userapp.his.runtzh.com/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+#      - appId: wxce847c8ebe5e62aa # 第一个公众号的appid  //公众号名称:济南联智健康
+#        secret: 37f7c5e3b7ff07794343957f7ced8de4 # 公众号的appsecret--济南联智健康
+      - appId: wxd6905bed94e45ef0 # 第一个公众号的appid  //公众号名称:济南联智健康
+        secret: c1a042a55bac24033535da50ce8ab6a2 # 公众号的appsecret--济南联智健康
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+aifabu:  #爱链接
+  appKey: 7b471be905ab17e00f3b858c6710dd117601d008
+watch:
+  watchUrl: watch.ylrzcloud.com/prod-api
+  #  account: tcloud
+  #  password: mdf-m2h_6yw2$hq
+  account1: ccif #866655060138751
+  password1: cp-t5or_6xw7$mt
+  account2: tcloud #rt500台
+  password2: mdf-m2h_6yw2$hq
+  account3: whr
+  password3: v9xsKuqn_$d2y
+
+fs :
+  commonApi: http://10.206.0.12:7771
+  h5CommonApi: http://10.206.0.12:7771
+  jwt:
+    # 加密秘钥
+    secret: 3e6d9c0b4a7f1e2d5c4e0d3c6b9a2f5e
+    # token有效时长,7天,单位秒
+    expire: 31536000
+    header: AppToken
+nuonuo:
+  key: 10924508
+  secret: A2EB20764D304D16
+
+# 存储捅配置
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: jnlzjk-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: jnlzjk
+tmp_secret_config:
+  secret_id: AKIDCj7NSNAovtqeJpBau8GZ4CGB71thXIxX
+  secret_key: lTB5zwqqz7CNhzDOWivFWedgfTBgxgBT
+  bucket: fs-1319721001
+  app_id: 1319721001
+  region: ap-chongqing
+  proxy: fs
+cloud_host:
+  company_name: 济南联志健康
+  projectCode: LZJK
+  spaceName:
+  volcengineUrl: https://jnlzvolcengine.ylrztop.com
+headerImg:
+  imgUrl:
+
+ipad:
+  ipadUrl: http://ipad.ljhehualu.com
+  aiApi: http://49.232.181.28:3000/api
+  voiceApi: http://129.28.187.88:8667
+  commonApi: http://129.28.187.88:7771
+wx_miniapp_temp:
+  pay_order_temp_id:
+  inquiry_temp_id:
+
+

+ 2 - 2
fs-service/src/main/resources/application-config-druid-sxjz.yml

@@ -87,8 +87,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 今正科技
   projectCode: SXJZ
-  spaceName:
-  volcengineUrl:
+  spaceName: sxjz-2114522511
+  volcengineUrl: https://sxjzvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://jz-cos-1356808054.cos.ap-chengdu.myqcloud.com/fs/20250515/0877754b59814ea8a428fa3697b20e68.png

+ 2 - 1
fs-service/src/main/resources/application-config-druid-syysy.yml

@@ -76,7 +76,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 益善缘
   projectCode: SYYSY
-  spaceName:
+  spaceName: syysy-2114522511
+  volcengineUrl: https://syysyvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://ysy-1329817240.cos.ap-guangzhou.myqcloud.com/ysy/20250820/2c47e4f105b641b4a49df50a77338e32.png

+ 2 - 1
fs-service/src/main/resources/application-config-druid-yxj.yml

@@ -82,7 +82,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 易行健
   projectCode: whyxj
-  spaceName:
+  spaceName: whyxj-2114522511
+  volcengineUrl: https://whyxjvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://yxj-1323137866.cos.ap-chongqing.myqcloud.com/app/yxj.jpg

+ 141 - 0
fs-service/src/main/resources/application-config-shdn.yml

@@ -0,0 +1,141 @@
+#配置
+fsConfig:
+  #快递鸟
+  kdnId: 1886082
+  kdnKeyId: 5a66df03-3d88-469a-ab42-23cb082b57ac
+  kdnUrl: http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx
+  kdnSubscribeUrl: https://api.kdniao.com/api/dist
+  kdnAddressUrl: https://api.kdniao.com/api/dist
+  #ERP配置
+  erpOpen: 1
+  erpAppKey: 108123
+  erpSessionKey: 9caae15474cb443ea22235e7bb86016b
+  erpSecret: 96f774dbd60847b59a16f92fd963a0c8
+  erpUrl: http://v2.api.guanyierp.com/rest/erp_open
+  erpShopCode: test
+  #ERP-hc
+  erpWdAppKey: beiliyou2-gw
+  erpWdAppsecret: 37c7cebf6e5af56c783d865b63553993
+  erpWdSid: beiliyou2
+  erpWdShopCode: ziyou123456
+  erpWdBaseUrl: https://api.wangdian.cn/openapi2/
+  erpWarehouseCode: "02"
+  #第三方支付配置
+  payOpen: 1
+  payPartnerId: 22051909542647100020
+  payKey: f256bd35aa36115d729537e1a1e01b92
+  payGateWayUrl: https://openapi.t2bank.cn/gateway.html
+  payNotifyUrl: https://api.yjf.runtzh.com/app/pay/payNotify
+  refundNotifyUrl: https://api.yjf.runtzh.com/app/pay/refundNotify
+  # 腾讯云IM
+  sdkAppId: 1400693126
+  sdkAppKey: 9afa6e63db943293680e37b3ba032e52cdb238112750806e82e58e9240604b70
+  # 处方接口Test
+  #  prescribeUrl: https://yixian-new-test.yixianmedical.com/platform-shenfang/nethosp/webservice/jsonapi
+  #  actId:  uporder
+  #  appId: 1646204278
+  #  manuId:  3981112bfcc64bf68f7744ffec7e3ca7
+  #  callbackUrl:  https://api.hospital.ifeiyu100.com/app/prescribe/presribeNotify
+  # 处方接口g
+  prescribeUrl: https://app3.nxk520.com/platform-shenfang/nethosp/webservice/jsonapi
+  actId: uporder
+  appId: 1661496555
+  manuId: 0212af1e742b41b09089afeec98f8276
+  callbackUrl: https://api.yjf.runtzh.com/app/prescribe/presribeNotify
+  commonApi: http://192.168.0.224:7011
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  cp:
+    corpId: wwb2a1055fb6c9a7c2
+    appConfigs:
+      - agentId: 1000002
+        secret: bhj3402rPCT0YGcosffyTO3eUMs1G2MFHMspXVBNf-c
+        token: PPKOdAlCoMO
+        aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  miniapp:
+    configs:
+      - appid: wx11a2ce7c2bbc4521   #倍力优会员商城
+        secret: d680dc8ff20258b158c9355f8b7769ae
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+      - appid: wx301ab2fad04c658a   #倍力优看课小程序
+        secret: 35018f10929b84c8c4a225de253bbcc6
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+
+  pay:
+    appId: wx11a2ce7c2bbc4521 #微信公众号或者小程序等的appid
+    mchId: 1703311381 #微信支付商户号
+    mchKey: FotTIbIzn4AisMW7de712LJQIazSqqAl #微信支付商户密钥
+    v3Key: y5Eo99q93qzdQRAs6E2BDKIF7f3EnS3G
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\Tools\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl:  https://userapp.bly.ylrztop.com/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+      - appId: wx568ea6b70350c585 # 第一个公众号的appid  倍力优
+        secret: b14343e22871b1c207df5d3321e826b4 # 公众号的appsecret
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+jpush:
+  appKey: cc9a0120a3e4270c9cba340d
+  masterSecret: cfc2575d3cd7470d584c990c
+  liveTime: 1000
+  apnsProduction: true
+aifabu:  #爱链接
+  appKey: 7b471be905ab17e00f3b858c6710dd117601d008
+
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: beliyo-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: beliyo
+cloud_host:
+  company_name: 倍力优
+  projectCode: BLY
+  spaceName:
+headerImg:
+  imgUrl: https://beiliyo-2025.obs.cn-north-4.myhuaweicloud.com/fs/20250115/1736944490230.png
+
+baidu:
+  token: 1231321232
+  back-domain: admin.muyi88.com
+watch:
+  watchUrl: watch.ylrzcloud.com/prod-api
+  #  account: tcloud
+  #  password: mdf-m2h_6yw2$hq
+  account1: ccif #866655060138751
+  password1: cp-t5or_6xw7$mt
+  account2: tcloud #rt500台
+  password2: mdf-m2h_6yw2$hq
+  account3: whr
+  password3: v9xsKuqn_$d2y
+
+fs:
+  commonApi: http://172.21.76.167:8010
+nuonuo:
+  key: 10924508
+  secret: A2EB20764D304D16
+ipad:
+  ipadUrl:
+  aiApi:
+  voiceApi:
+  commonApi:
+wx_miniapp_temp:
+  pay_order_temp_id: VXEvKaGNPFuJmhWK9O_QPrTZxe9umDCukq-maI8Vdek
+  inquiry_temp_id: 9POPYeqhI48LOPvq-Rfoklze7H-9SlunJKh10Qt4_2I
+

+ 165 - 0
fs-service/src/main/resources/application-druid-shdn.yml

@@ -0,0 +1,165 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-shdn,common
+    # redis 配置
+    redis:
+        host: 10.206.0.10
+        port: 6579
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 30s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 100
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+#        clickhouse:
+#            type: com.alibaba.druid.pool.DruidDataSource
+#            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+#            url: jdbc:clickhouse://1.14.104.71:8123/sop_test?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+#            username: rt_2024
+#            password: Yzx_19860213
+#            initialSize: 10
+#            maxActive: 100
+#            minIdle: 10
+#            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                  url: jdbc:mysql://172.17.0.12:65535/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
+                  username: root
+                  password: QWEqwe123!@#
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://172.17.0.12:65535/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
+                    username: root
+                    password: QWEqwe123!@#
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-33namm2jeq.rocketmq.nj.qcloud.tencenttdmq.com:8080
+    producer:
+        group: my-producer-group
+        access-key: ak33namm2jeq90148878a325 # 替换为实际的 accessKey
+        secret-key: sk0256ccecd9f96742 # 替换为实际的 secretKey
+    consumer:
+        group: common-group
+        access-key: ak33namm2jeq90148878a325 # 替换为实际的 accessKey
+        secret-key: sk0256ccecd9f96742 # 替换为实际的 secretKey
+openIM:
+    secret: openIM123
+    userID: imAdmin
+    url: https://web.im.fbylive.com/api
+#是否使用新im
+im:
+    type: NONE
+#是否为新商户,新商户不走mpOpenId
+isNewWxMerchant: false
+qw:
+    enableAutoTag: 1
+tag:
+    thread:
+        num: 10
+    rate:
+        limit: 50
+