| 
					
				 | 
			
			
				@@ -12,28 +12,31 @@ import com.fs.company.domain.CompanyUser; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.company.service.ICompanyUserService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.course.config.CourseConfig; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.course.domain.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.fs.course.mapper.FsCourseDomainNameMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.fs.course.mapper.FsCourseFinishTempMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.fs.course.mapper.FsCourseLinkMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import com.fs.course.mapper.FsCourseWatchLogMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.course.mapper.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.fastgptApi.util.AudioUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.fastgptApi.vo.AudioVO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.domain.QwExternalContact; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.qw.domain.QwUser; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.mapper.QwExternalContactMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.mapper.QwUserMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.service.IQwExternalContactService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.qw.service.impl.QwExternalContactServiceImpl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.vo.QwSopCourseFinishTempSetting; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.vo.QwSopRuleTimeVO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.qw.vo.QwSopTempSetting; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.domain.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.sop.dto.QwCreateLinkByAppDTO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.mapper.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.sop.params.SopUserLogsInfoParam; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.service.IQwSopLogsService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.service.IQwSopTempContentService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.service.IQwSopTempRulesService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.service.IQwSopTempVoiceService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.sop.vo.SopUserLogsVo; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.fs.system.service.ISysConfigService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fs.voice.utils.StringUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import lombok.extern.slf4j.Slf4j; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.beans.BeanUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.beans.factory.annotation.Autowired; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.retry.annotation.Backoff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.retry.annotation.Retryable; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -65,6 +68,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private static final String QWSOP_KEY_PREFIX = "qwsop:"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String miniappRealLink = "/pages_course/video.html?course="; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String appRealLink = "/pages/courseAnswer/index?link="; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final String appLink = "https://jump.ylrztop.com/jumpapp/pages/index/index?link="; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // Cached configurations and domain names 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private CourseConfig cachedCourseConfig; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -83,8 +89,12 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private QwSopTagMapper qwSopTagMapper ; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private QwSopMapper sopMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private IQwExternalContactService iQwExternalContactService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    private IQwExternalContactService qwExternalContactService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private QwExternalContactServiceImpl qwExternalContactService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private FsCourseWatchLogMapper fsCourseWatchLogMapper; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -97,6 +107,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private FsCourseLinkMapper fsCourseLinkMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private FsCourseSopAppLinkMapper fsCourseSopAppLinkMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Autowired 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private ISysConfigService configService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -118,11 +132,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final BlockingQueue<QwSopLogs> qwSopLogsQueue = new LinkedBlockingQueue<>(20000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final BlockingQueue<FsCourseWatchLog> watchLogsQueue = new LinkedBlockingQueue<>(20000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final BlockingQueue<FsCourseLink> linkQueue = new LinkedBlockingQueue<>(20000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final BlockingQueue<FsCourseSopAppLink> sopAppLinks = new LinkedBlockingQueue<>(20000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // Executors for consumer threads 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private ExecutorService qwSopLogsExecutor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private ExecutorService watchLogsExecutor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private ExecutorService courseLinkExecutor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private ExecutorService courseSopAppLinkExecutor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // Shutdown flags 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private volatile boolean running = true; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -181,9 +197,17 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return t; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        courseSopAppLinkExecutor = Executors.newSingleThreadExecutor(r -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Thread t = new Thread(r, "courseSopAppLinkConsumer"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            t.setDaemon(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return t; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         qwSopLogsExecutor.submit(this::consumeQwSopLogs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         watchLogsExecutor.submit(this::consumeWatchLogs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         courseLinkExecutor.submit(this::consumeCourseLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        courseSopAppLinkExecutor.submit(this::consumeCourseSopAppLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // Scheduled tasks to refresh configurations and domain names periodically 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -332,7 +356,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         QwSopTemp qwSopTemp = qwSopTempMapper.selectQwSopTempById(ruleTimeVO.getTempId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (qwSopTemp == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            sopUserLogsMapper.deleteSopUserLogsBySopId(sopId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            sopUserLogsMapper.deleteSopUserLogsBySopId(sopId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             log.info("SOP ID {} 模板不存在,相关日志已清除。", sopId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -340,10 +364,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ruleTimeVO.setTempGap(qwSopTemp.getGap()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (ruleTimeVO.getStatus() == 0 || "0".equals(ruleTimeVO.getTempStatus())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            SopUserLogs sopUserLogs = new SopUserLogs(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            sopUserLogs.setSopId(sopId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            sopUserLogs.setStatus(2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            SopUserLogs sopUserLogs = new SopUserLogs(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            sopUserLogs.setSopId(sopId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            sopUserLogs.setStatus(2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             log.info("SOP ID {} 的状态为停用,相关日志状态已更新。", sopId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -417,9 +441,22 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String[] userKey = logVo.getUserId().split("\\|"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (userKey.length < 3) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String qwUserId = userKey[0].trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String companyUserId = userKey[1].trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String companyId = userKey[2].trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            //寻找时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//            LocalDateTime currentTime = LocalDateTime.of(2024, 12, 25,23 , 40); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //获取企业微信员工的称呼//从redis里或者从库里取 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            QwUser qwUserByRedis = qwExternalContactService.getQwUserByRedis(logVo.getCorpId(),logVo.getQwUserId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (qwUserByRedis==null){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.error("无企微员工信息 {} 跳过处理。:{}", logVo.getUserId(),logVo.getCorpId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // 先算好 60分钟后 ~ 再60分钟后的时间段 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             LocalDateTime startRangeFirst = currentTime.plusMinutes(60); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -438,17 +475,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }else{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     day++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                // 再次验证 intervalDay 是否在范围内 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                if (intervalDay < 0 || intervalDay >= tempSettings.size()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    log.info("跨天后,intervalDay={} 超出 TempSettings 范围,跳过。", intervalDay); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                if (daysBetween % tempGap != 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    log.error("天数差 {} 不是 tempGap {} 的整数倍,跳过操作,SopId {} ", daysBetween, tempGap,logVo.getSopId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 // 重新拿新的 “天” 的 Setting 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 contents = getDay(tempSettings, day); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -497,32 +523,40 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         // 将 LocalDateTime 转换为 Date 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         Date sendTime = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        SopUserLogsInfo userLogsInfo=new SopUserLogsInfo(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        userLogsInfo.setSopId(logVo.getSopId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        userLogsInfo.setUserLogsId(logVo.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        SopUserLogsInfo userLogsInfo=new SopUserLogsInfo(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        userLogsInfo.setSopId(logVo.getSopId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        userLogsInfo.setUserLogsId(logVo.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoList(userLogsInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if(logVo.getIsRegister() == 1){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            List<Long> externalContactIdList = PubFun.listToNewList(sopUserLogsInfos, SopUserLogsInfo::getExternalId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            List<QwExternalContact> list = qwExternalContactService.list(new QueryWrapper<QwExternalContact>().isNotNull("fs_user_id").in("id", externalContactIdList)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            Map<Long, QwExternalContact> map = PubFun.listToMapByGroupObject(list, QwExternalContact::getId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            sopUserLogsInfos = sopUserLogsInfos.stream().filter(e -> map.containsKey(e.getExternalId())).collect(Collectors.toList()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        SopUserLogsInfoParam logsInfoParam=new SopUserLogsInfoParam(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        logsInfoParam.setSopId(logVo.getSopId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        logsInfoParam.setUserLogsId(logVo.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        logsInfoParam.setIsRegister(logVo.getIsRegister()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoListByIsRegister(logsInfoParam); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        if(logVo.getIsRegister() == 1){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            List<Long> externalContactIdList = PubFun.listToNewList(sopUserLogsInfos, SopUserLogsInfo::getExternalId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            List<QwExternalContact> list = qwExternalContactService.list(new QueryWrapper<QwExternalContact>().isNotNull("fs_user_id").in("id", externalContactIdList)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            Map<Long, QwExternalContact> map = PubFun.listToMapByGroupObject(list, QwExternalContact::getId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            sopUserLogsInfos = sopUserLogsInfos.stream().filter(e -> map.containsKey(e.getExternalId())).collect(Collectors.toList()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         // 获取fsUserId 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        Set<Long> externalIds = sopUserLogsInfos.stream().map(SopUserLogsInfo::getExternalId).collect(Collectors.toSet()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if (!externalIds.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            List<QwExternalContact> externalContactList = qwExternalContactService.list(Wrappers.<QwExternalContact>lambdaQuery().in(QwExternalContact::getId, externalIds)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            sopUserLogsInfos.forEach(s -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                QwExternalContact qwExternalContact = externalContactList.stream().filter(e -> Objects.equals(s.getExternalId(), e.getId())).findFirst().orElse(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                if (Objects.nonNull(qwExternalContact)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    s.setFsUserId(qwExternalContact.getFsUserId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        Set<Long> externalIds = sopUserLogsInfos.stream().map(SopUserLogsInfo::getExternalId).collect(Collectors.toSet()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        if (!externalIds.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            List<QwExternalContact> externalContactList = qwExternalContactService.list(Wrappers.<QwExternalContact>lambdaQuery().in(QwExternalContact::getId, externalIds)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            sopUserLogsInfos.forEach(s -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                QwExternalContact qwExternalContact = externalContactList.stream().filter(e -> Objects.equals(s.getExternalId(), e.getId())).findFirst().orElse(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                if (Objects.nonNull(qwExternalContact)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                    s.setFsUserId(qwExternalContact.getFsUserId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId, companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } catch (Exception e) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -561,7 +595,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     //消息处理 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private void insertSopUserLogs(List<SopUserLogsInfo> sopUserLogsInfos, SopUserLogsVo logVo, Date sendTime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                   QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                String qwUserId,String companyUserId,String companyId,String welcomeText,String qwUserName) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         String formattedSendTime = sendTime.toInstant() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 .atZone(ZoneId.systemDefault()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 .format(DATE_TIME_FORMATTER); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -569,24 +605,17 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Long courseId = content.getCourseId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Long videoId = content.getVideoId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String[] userKey = logVo.getUserId().split("\\|"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (userKey.length < 3) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String qwUserId = userKey[0].trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String companyUserId = userKey[1].trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String companyId = userKey[2].trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Integer isOfficial = content.getIsOfficial() != null ? Integer.valueOf(content.getIsOfficial()) : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // 发送语言 start 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if(content.getSetting() == null){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        List<QwSopTempSetting.Content.Setting> setting = content.getSetting().stream().filter(e -> "7".equals(e.getContentType())).collect(Collectors.toList()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if(!setting.isEmpty()){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            List<String> valuesList = PubFun.listToNewList(setting, QwSopTempSetting.Content.Setting::getValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            List<QwSopTempVoice> voiceList = qwSopTempVoiceService.getVoiceByText(Long.parseLong(companyUserId), valuesList); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Map<Long, Map<String, Optional<QwSopTempVoice>>> collect = voiceList.stream().collect(Collectors.groupingBy(QwSopTempVoice::getCompanyUserId, Collectors.groupingBy(QwSopTempVoice::getVoiceTxt, Collectors.reducing((e, e1) -> e)))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        List<QwSopTempSetting.Content.Setting> setting = content.getSetting().stream().filter(e -> "7".equals(e.getContentType())).collect(Collectors.toList()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        if(!setting.isEmpty()){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            List<String> valuesList = PubFun.listToNewList(setting, QwSopTempSetting.Content.Setting::getValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            List<QwSopTempVoice> voiceList = qwSopTempVoiceService.getVoiceByText(Long.parseLong(companyUserId), valuesList); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            Map<Long, Map<String, Optional<QwSopTempVoice>>> collect = voiceList.stream().collect(Collectors.groupingBy(QwSopTempVoice::getCompanyUserId, Collectors.groupingBy(QwSopTempVoice::getVoiceTxt, Collectors.reducing((e, e1) -> e)))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //            Consumer<QwSopTempSetting.Content.Setting> buildVoid = st -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //                try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //                    AudioVO audioVO = AudioUtils.transferAudioSilkFromText(st.getValue(), Long.valueOf(companyUserId), false); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -596,22 +625,22 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //                    log.info("音频生成失败-: "+companyUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            setting.parallelStream().filter(e -> "7".equals(e.getContentType())).forEach(st -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Map<String, Optional<QwSopTempVoice>> map = collect.get(Long.parseLong(companyUserId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if(map == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                buildVoid.accept(st); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Optional<QwSopTempVoice> optional = map.get(st.getValue()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if(!optional.isPresent()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                buildVoid.accept(st); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                QwSopTempVoice voice = optional.get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                st.setVoiceUrl(voice.getVoiceUrl()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                st.setVoiceDuration(voice.getDuration()+""); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            setting.parallelStream().filter(e -> "7".equals(e.getContentType())).forEach(st -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                Map<String, Optional<QwSopTempVoice>> map = collect.get(Long.parseLong(companyUserId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                if(map == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+////                buildVoid.accept(st); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                Optional<QwSopTempVoice> optional = map.get(st.getValue()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                if(!optional.isPresent()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+////                buildVoid.accept(st); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                QwSopTempVoice voice = optional.get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                st.setVoiceUrl(voice.getVoiceUrl()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                st.setVoiceDuration(voice.getDuration()+""); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // 发送语言 end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (content.getType()==5){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             sopAddTag(logVo,content,sendTime); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -626,7 +655,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 Long fsUserId = contactId.getFsUserId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        type, qwUserId, companyUserId, companyId, externalId, fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        type, qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -678,14 +707,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                       SopUserLogsVo logVo, Date sendTime, Long courseId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                       Long videoId, int type, String qwUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                      String companyUserId, String companyId, String externalId, Long fsUserId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      String companyUserId, String companyId, String externalId,String welcomeText, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      String qwUserName,Long fsUserId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         switch (type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             case 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 handleNormalMessage(sopLogs, content,companyUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             case 2: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        qwUserId, companyUserId, companyId, externalId, fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             case 3: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 handleOrderMessage(sopLogs, content); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -717,7 +747,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                      SopUserLogsVo logVo, Date sendTime, Long courseId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                      Long videoId, String qwUserId, String companyUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                     String companyId, String externalId, Long fsUserId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     String companyId, String externalId,String welcomeText, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     String qwUserName,Long fsUserId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // 深拷贝 Content 对象,避免使用 JSON 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         QwSopTempSetting.Content clonedContent = deepCopyContent(content); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (clonedContent == null) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -737,30 +768,231 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // 顺序处理每个 Setting,避免过多的并行导致线程开销 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for (QwSopTempSetting.Content.Setting setting : settings) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if ("1".equals(setting.getIsBindUrl())&&("3".equals(setting.getContentType())||"1".equals(setting.getContentType()))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        qwUserId, companyUserId, companyId, externalId, fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (StringUtils.isNotEmpty(sortLink)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if ("3".equals(setting.getContentType())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        setting.setLinkUrl(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        String currentValue = setting.getValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if (currentValue == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            setting.setValue(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            switch (setting.getContentType()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //文字和短链一起 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                case "1": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                case "3": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if ("1".equals(setting.getIsBindUrl())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                qwUserId, companyUserId, companyId, externalId,fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (StringUtils.isNotEmpty(sortLink)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if ("3".equals(setting.getContentType())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                setting.setLinkUrl(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                String currentValue = setting.getValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                if (currentValue == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    setting.setValue(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                    setting.setValue(currentValue + "\n" + sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    setting.setValue(currentValue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            + "\n" + sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            setting.setValue(currentValue + "\n" + sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            log.error("生成短链失败,跳过设置 URL。"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if ("1".equals(setting.getContentType())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            setting.setValue(setting.getValue() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    log.error("生成短链失败,跳过设置 URL。"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //小程序单独 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                case "4": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            qwUserId, companyUserId, companyId, externalId,fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    setting.setMiniprogramPage(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl())?"https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png":setting.getMiniprogramPicUrl()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    }catch (Exception e){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        log.error("赋值-小程序封面地址失败-"+e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //app 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                case "9": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    QwCreateLinkByAppDTO linkByApp = createLinkByApp(setting, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            qwUserId, companyUserId, companyId, externalId,sopLogs.getCorpId(),qwUserName,fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    setting.setLinkUrl(linkByApp.getSortLink()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    setting.setAppLinkUrl(linkByApp.getAppMsgLink()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            if ("1".equals(setting.getIsBindUrl())&&("3".equals(setting.getContentType())||"1".equals(setting.getContentType()))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        qwUserId, companyUserId, companyId, externalId, fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                if (StringUtils.isNotEmpty(sortLink)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    if ("3".equals(setting.getContentType())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        setting.setLinkUrl(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        String currentValue = setting.getValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        if (currentValue == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            setting.setValue(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                            setting.setValue(currentValue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                    .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                                    + "\n" + sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                    log.error("生成短链失败,跳过设置 URL。"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         sopLogs.setContentJson(JSON.toJSONString(clonedContent)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         enqueueQwSopLogs(sopLogs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private QwCreateLinkByAppDTO createLinkByApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                 Long courseId, Long videoId, String qwUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                 String companyUserId, String companyId, String externalId, String corpId, String qwUserName,Long fsUserId){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 获取缓存的配置 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CourseConfig config; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        synchronized(configLock) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            config = cachedCourseConfig; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (config == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("CourseConfig is not loaded."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, qwUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                companyUserId, companyId, externalId, 4); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseRealLink courseMap = new FsCourseRealLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        BeanUtils.copyProperties(link,courseMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        courseMap.setFsUserId(fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String courseJson = JSON.toJSONString(courseMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String realLinkFull = REAL_LINK_PREFIX + courseJson; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setRealLink(realLinkFull); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Date updateTime = createUpdateTime(setting, sendTime, config); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setUpdateTime(updateTime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String sortLink = appLink+link.getLink()+"&videoId="+videoId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String appMsgLink=appRealLink+link.getLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        QwCreateLinkByAppDTO byAppDTO=new QwCreateLinkByAppDTO(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        byAppDTO.setSortLink(sortLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        byAppDTO.setAppMsgLink(appMsgLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseSopAppLink fsCourseSopAppLink = createFsCourseSopAppLink(link.getLink(), sendTime, updateTime, companyId, companyUserId, qwUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                qwUserName, corpId, courseId, setting.getLinkTitle(), setting.getLinkImageUrl(), videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                setting.getLinkDescribe(), appMsgLink, externalId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        enqueueCourseSopAppLink(fsCourseSopAppLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        enqueueCourseLink(link); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return byAppDTO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public FsCourseSopAppLink createFsCourseSopAppLink(String link, Date sendTime, Date updateTime, String companyId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                       String companyUserId,String qwUserId,String qwUserName,String corpId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                       Long courseId,String linkTile,String linkImageUrl,Long videoId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                       String linkDescribe,String appMsgLink,String externalId){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseSopAppLink sopAppLink=new FsCourseSopAppLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setLink(link); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCreateTime(sendTime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setUpdateTime(updateTime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCompanyId(Long.parseLong(companyId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCompanyUserId(Long.parseLong(companyUserId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setQwUserId(Long.parseLong(qwUserId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setQwUserName(qwUserName); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCorpId(corpId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCourseId(courseId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCourseTitle(linkTile); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setCourseUrl(linkImageUrl); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setVideoId(videoId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setVideoTitle(linkDescribe); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setAppRealLink(appMsgLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sopAppLink.setQwExternalId(Long.parseLong(externalId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return sopAppLink; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private String createLinkByMiniApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                       Long courseId, Long videoId, String qwUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                       String companyUserId, String companyId, String externalId,Long fsUserId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 获取缓存的配置 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CourseConfig config; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        synchronized(configLock) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            config = cachedCourseConfig; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (config == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("CourseConfig is not loaded."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        if (StringUtils.isEmpty(config.getMiniprogramPage())){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            log.error("miniprogramPage is not loaded."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//            return ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseLink link = new FsCourseLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCompanyId(Long.parseLong(companyId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setQwUserId(qwUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCompanyUserId(Long.parseLong(companyUserId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setVideoId(videoId.longValue()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCorpId(logVo.getCorpId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCourseId(courseId.longValue()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setQwExternalId(Long.parseLong(externalId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setLinkType(3); //正常链接 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String randomString = generateRandomStringWithLock(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setLink(randomString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCreateTime(sendTime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseRealLink courseMap = new FsCourseRealLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        BeanUtils.copyProperties(link,courseMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        courseMap.setFsUserId(fsUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String courseJson = JSON.toJSONString(courseMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String realLinkFull = miniappRealLink + courseJson; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        String realLinkFull = config.getMiniprogramPage() + courseJson; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setRealLink(realLinkFull); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ? config.getVideoLinkExpireDate() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                : setting.getExpiresDays(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 使用 Java 8 时间 API 计算过期时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setUpdateTime(updateTime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        //存短链- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        enqueueCourseLink(link); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return link.getRealLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 深拷贝 Content 对象,避免使用 JSON 进行序列化和反序列化 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -771,12 +1003,45 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return content.clone(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Date createUpdateTime(QwSopTempSetting.Content.Setting setting,Date sendTime,CourseConfig config){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ? config.getVideoLinkExpireDate() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                : setting.getExpiresDays(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         使用 Java 8 时间 API 计算过期时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return updateTime; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private void handleOrderMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         sopLogs.setContentJson(JSON.toJSONString(content)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         enqueueQwSopLogs(sopLogs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public FsCourseLink createFsCourseLink(String corpId, Date sendTime,Long courseId,Long videoId, String qwUserId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                           String companyUserId, String companyId,String externalId,Integer type){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FsCourseLink link = new FsCourseLink(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCompanyId(Long.parseLong(companyId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setQwUserId(qwUserId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCompanyUserId(Long.parseLong(companyUserId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setVideoId(videoId.longValue()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCorpId(corpId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCourseId(courseId.longValue()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setQwExternalId(Long.parseLong(externalId)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setLinkType(type); //小程序 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String randomString = generateRandomStringWithLock(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setLink(randomString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        link.setCreateTime(sendTime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return link; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private String generateShortLink(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -934,6 +1199,23 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 将 FsCourseSopAppLing 放入队列 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void enqueueCourseSopAppLink(FsCourseSopAppLink sopAppLink) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            boolean offered = sopAppLinks.offer(sopAppLink, 5, TimeUnit.SECONDS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!offered) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.error("FsCourseSopAppLink 队列已满,无法添加日志: {}", JSON.toJSONString(sopAppLink)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 处理队列已满的情况,例如记录到失败队列或持久化存储 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (InterruptedException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Thread.currentThread().interrupt(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("插入 FsCourseLink 队列时被中断: {}", e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 消费 QwSopLogs 队列并进行批量插入 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1021,6 +1303,36 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 消费 FsCourseSopAppLink 队列并进行批量插入 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void consumeCourseSopAppLink() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<FsCourseSopAppLink> batch = new ArrayList<>(BATCH_SIZE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while (running || !sopAppLinks.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                FsCourseSopAppLink courseSopAppLink = sopAppLinks.poll(1, TimeUnit.SECONDS); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (courseSopAppLink != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    batch.add(courseSopAppLink); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (batch.size() >= BATCH_SIZE || (!batch.isEmpty() && courseSopAppLink == null)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (!batch.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        batchInsertFsCourseSopAppLink(new ArrayList<>(batch)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        batch.clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } catch (InterruptedException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Thread.currentThread().interrupt(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.error("FsCourseSopAppLink 消费线程被中断: {}", e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 处理剩余的数据 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!batch.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            batchInsertFsCourseSopAppLink(batch); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 批量插入 QwSopLogs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1080,6 +1392,26 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 批量插入 FsCourseSopAppLink 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Transactional 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Retryable( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            value = { Exception.class }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            maxAttempts = 3, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            backoff = @Backoff(delay = 2000) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public void batchInsertFsCourseSopAppLink(List<FsCourseSopAppLink> courseSopAppLinkToInsert) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            fsCourseSopAppLinkMapper.insertFsCourseSopAppLinkBatch(courseSopAppLinkToInsert); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.info("批量插入 FsCourseSopAppLink 完成,共插入 {} 条记录。", courseSopAppLinkToInsert.size()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("批量插入 FsCourseSopAppLink 失败: {}", e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 可选:将失败的数据记录到失败队列或持久化存储以便后续重试 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void updateSopLogsByCancel() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         List<QwSopLogs> sopLogs = qwSopLogsMapper.selectQwSopLogsByCancel(); 
			 |