Ver código fonte

fix:sop发福袋

ct 2 dias atrás
pai
commit
95f3a3b8dc

+ 27 - 1
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -98,6 +98,32 @@ public class IpadSendServer {
     private static final List<String> PROJECT_NAMES = Arrays.asList("济南联志健康", "北京存在文化","宽益堂");
     private final LiveWatchLogMapper liveWatchLogMapper;
 
+    /**
+     * 福袋
+     * @param vo
+     * @param content
+     * @param miniMap
+     */
+    private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content,Map<String, FsCoursePlaySourceConfig> miniMap) {
+        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(content.getMiniprogramAppid());
+        // 小程序
+        MiniProgramVo miniProgramVo = MiniProgramVo.builder()
+                .desc(content.getMiniprogramTitle().replaceFirst("-.*", ""))
+                .title(courseMaConfig.getName())
+                .weappIconUrl(courseMaConfig.getImg())
+                .imgUrl(content.getMiniprogramPicUrl())
+                .username(courseMaConfig.getOriginalId() + "@app")
+                .pagepath(content.getMiniprogramPage())
+                .appid(content.getMiniprogramAppid())
+                .build();
+        miniProgramVo.setBase(vo);
+        WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> resp = ipadSendUtils.sendMiniProgram(miniProgramVo);
+        if (resp.getErrcode() != 0) {
+            log.debug("ID:{}-ipad接口请求返回异常:{}", vo.getId(), resp.getErrmsg());
+            content.setSendStatus(2);
+            content.setSendRemarks("发送失败:" + resp.getErrmsg());
+        }
+    }
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
         // 发送参数原本的appid
         String appid = content.getMiniprogramAppid();
@@ -735,7 +761,7 @@ public class IpadSendServer {
                     // 记录福袋发送记录
                     addLuckyBagCollectRecord(vo, content,qwSopLogs);
                     // 福袋
-                    sendMiniProgram(vo, content, miniMap,qwUser.getCompanyId());
+                    sendMiniProgram(vo, content, miniMap);
                     break;
                 case "99":
                     // 群发

+ 1 - 1
fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java

@@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
 @EnableTransactionManagement
 @EnableAsync
-@EnableScheduling
+//@EnableScheduling
 public class FsQwTaskApplication
 {
     public static void main(String[] args){

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

@@ -3,6 +3,7 @@ package com.fs.app.taskService.impl;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.app.taskService.SopLogsTaskService;
 import com.fs.common.config.FSSysConfig;
@@ -20,9 +21,13 @@ import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
 import com.fs.course.service.IFsCourseLinkService;
 import com.fs.course.service.IFsUserCompanyBindService;
+import com.fs.his.domain.FsUser;
+import com.fs.his.mapper.FsUserMapper;
 import com.fs.live.domain.LiveWatchLog;
 import com.fs.live.mapper.LiveWatchLogMapper;
 import com.fs.qw.domain.*;
+import com.fs.qw.mapper.LuckyBagCollectRecordMapper;
+import com.fs.qw.mapper.LuckyBagMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwCompanyService;
@@ -41,6 +46,7 @@ import com.fs.sop.service.IQwSopTempRulesService;
 import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.sop.vo.QwCreateLinkByAppVO;
 import com.fs.sop.vo.SopUserLogsVo;
+import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import lombok.extern.slf4j.Slf4j;
@@ -78,6 +84,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     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=";
+    private static final String appActivitlLink = "/pages_course/activity.html?link=";
+    private static final String registeredRealLink = "/pages_course/register.html?link=";
 
 //    private static final String miniappRealLink = "/pages/index/index?course=";
 
@@ -191,6 +199,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     @Autowired
     LiveWatchLogMapper liveWatchLogMapper;
 
+    @Autowired
+    private LuckyBagMapper luckyBagMapper;
+
+    @Autowired
+    private FsUserMapper fsUserMapper;
+
+    @Autowired
+    private LuckyBagCollectRecordMapper luckyBagCollectRecordMapper;
+
     @PostConstruct
     public void init() {
         loadCourseConfig();
@@ -950,6 +967,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             return;
         }
         // 顺序处理每个 Setting,避免过多的并行导致线程开销
+        String json = configService.selectConfigByKey("his.config");
+        FSSysConfig sysConfig = JSON.parseObject(json, FSSysConfig.class);
         for (QwSopTempSetting.Content.Setting setting : settings) {
             switch (setting.getContentType()) {
                 //直播小程序单独
@@ -959,8 +978,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     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");
-                    FSSysConfig sysConfig = JSON.parseObject(json, FSSysConfig.class);
                     if (isGroupChat) {
                         try {
                             groupChat.getChatUserList().stream().filter(e -> e.getUserList() != null && !e.getUserList().isEmpty()).forEach(e -> {
@@ -997,6 +1014,136 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                         log.error("赋值-小程序封面地址失败-" + e);
                     }
 
+                    break;
+                case "14":
+                    try {
+                        // 1. 检查必要对象是否为空
+                        if (sopLogs == null) {
+                            log.warn("sopLogs为空,跳过福袋处理");
+                        }
+                        // 查询福袋信息
+                        Long fsUserId = sopLogs.getFsUserId();
+
+                        LuckyBag luckyBag = luckyBagMapper.selectLuckyBagById(setting.getLuckyBagId());
+                        if(ObjectUtil.isNotEmpty(luckyBag)&&luckyBag.getDataStatus().equals("0")){
+                            setSopLogsStatus(sopLogs, 5L, 0L, "福袋配置被禁用");
+                        }else if (ObjectUtil.isNotEmpty(fsUserId)){
+                            // 2. 获取系统配置
+                            SysConfig luckBagsysConfig = configService.selectConfigByConfigKey("luckyBag.config");
+                            if (ObjectUtil.isEmpty(luckBagsysConfig) || StringUtil.strIsNullOrEmpty(luckBagsysConfig.getConfigValue())) {
+                                log.warn("福袋配置为空,设置发送状态为失败");
+                                setSopLogsStatus(sopLogs, 5L, 0L, "福袋配置不存在");
+                            }
+
+                            // 3. 解析配置
+                            JSONObject jsonObject = null;
+                            try {
+                                jsonObject = JSON.parseObject(luckBagsysConfig.getConfigValue());
+                            } catch (Exception e) {
+                                log.error("解析福袋配置JSON失败: {}", luckBagsysConfig.getConfigValue(), e);
+                                setSopLogsStatus(sopLogs, 5L, 0L, "福袋配置格式错误");
+                            }
+
+                            // 4. 获取周限制次数
+                            Integer count = null;
+                            try {
+                                Object weekLimitObj = jsonObject.get("weekLimit");
+                                if (weekLimitObj != null) {
+                                    count = Integer.valueOf(weekLimitObj.toString());
+                                }
+                            } catch (NumberFormatException e) {
+                                log.error("周限制次数格式错误: {}", jsonObject.get("weekLimit"), e);
+                            }
+
+                            if (count == null) {
+                                log.warn("周限制次数配置为空");
+                                setSopLogsStatus(sopLogs, 5L, 0L, "周限制次数配置错误");
+                            }
+
+                            // 5. 查询用户福袋收集记录
+                            LuckyBagCollectRecord luckyBagCollectRecord = new LuckyBagCollectRecord();
+                            luckyBagCollectRecord.setUserId(fsUserId);
+                            // 动态计算时间范围
+                            LocalDate endDate = LocalDate.now();
+                            LocalDate startDate = endDate.minusDays(6); // 包含今天
+
+                            Map<String, Object> params = new HashMap<>();
+                            params.put("beginSendTime", startDate.toString());
+                            params.put("endSendTime", endDate.toString());
+                            luckyBagCollectRecord.setParams(params);
+                            luckyBagCollectRecord.setCollectType("1");
+                            List<LuckyBagCollectRecord> luckyBagCollectRecords;
+                            try {
+                                luckyBagCollectRecords = luckyBagCollectRecordMapper.selectLuckyBagCollectRecordList(luckyBagCollectRecord);
+                            } catch (Exception e) {
+                                log.error("查询用户福袋记录失败, userId: {}", fsUserId, e);
+                                luckyBagCollectRecords = Collections.emptyList();
+                            }
+
+
+                            // 6. 检查次数限制
+                            int recordCount = luckyBagCollectRecords != null ? luckyBagCollectRecords.size() : 0;
+                            if (recordCount >= count) {
+                                log.info("用户福袋次数已达上限, userId: {}, 当前次数: {}, 限制次数: {}", fsUserId, recordCount, count);
+                                setSopLogsStatus(sopLogs, 5L, 0L, "超过福袋发放次数");
+                            }
+                        }
+
+                        // 7. 生成活动链接
+                        String link;
+                        try {
+                            if (isGroupChat && groupChat != null) {
+                                link = createActivityLinkByMiniApp(setting, sopLogs, sopLogs.getCorpId(), new Date(),
+                                        content.getCourseId(), content.getVideoId(), qwUserId, companyUserId, companyId, cachedCourseConfig, logVo.getChatId());
+                            } else {
+                                link = createActivityLinkByMiniApp(setting, sopLogs, sopLogs.getCorpId(), new Date(),
+                                        content.getCourseId(), content.getVideoId(), qwUserId, companyUserId, companyId, cachedCourseConfig, null);
+                            }
+                        } catch (Exception e) {
+                            log.error("生成活动链接失败", e);
+                            setSopLogsStatus(sopLogs, 5L, 0L, "生成活动链接失败");
+                            return;
+                        }
+
+                        // 8. 获取小程序ID
+                        String finalAppId = null;
+                        Company company = companyMapper.selectCompanyById(Long.valueOf(companyId));
+                        List<String> miniAppMaster = company.getMiniAppMaster();
+                        if (ObjectUtil.isNotEmpty(miniAppMaster)) {
+                            finalAppId = miniAppMaster.get(0);
+                        } else {
+                            finalAppId = sysConfig.getAppId();
+                        }
+//                        try {
+//                            finalAppId = getAppIdFromMiniMap(miniMap, companyId, sendMsgType, grade);
+//                            if (StringUtil.strIsNullOrEmpty(finalAppId)) {
+//                                finalAppId = miniAppId;
+//                            }
+//                        } catch (Exception e) {
+//                            log.error("获取小程序ID失败,使用默认值", e);
+//                            finalAppId = miniAppId;
+//                        }
+
+                        if (StringUtil.strIsNullOrEmpty(finalAppId)) {
+                            log.error("公司的小程序id为空,sopId: {}", sopLogs.getSopId());
+                            setSopLogsStatus(sopLogs, 5L, 0L, "小程序配置错误");
+                            return;
+                        }
+
+                        // 10. 设置小程序参数
+                        setting.setMiniprogramAppid(finalAppId);
+                        setting.setMiniprogramTitle("福袋发放");
+                        setting.setMiniprogramPage(link);
+
+                        log.info("福袋配置成功,userId: {}, appId: {}", fsUserId, finalAppId);
+
+                    } catch (Exception e) {
+                        log.error("任务模板福袋发放失败", e);
+                        // 确保在最终异常时也设置状态
+                        if (sopLogs != null) {
+                            setSopLogsStatus(sopLogs, 5L, 0L, "福袋发放系统异常");
+                        }
+                    }
                     break;
                 default:
                     break;
@@ -1325,6 +1472,32 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                         log.error("赋值-小程序封面地址失败-" + e);
                     }
 
+                    break;
+                case "14":
+
+                    String linkBy = createActivityLinkByMiniApp(setting,sopLogs,sopLogs.getCorpId(),new Date(), courseId, videoId,
+                            qwUserId, companyUserId, companyId,cachedCourseConfig,logVo.getChatId());
+
+                    if(sopLogs.getSendType()==1){
+                        setting.setMiniprogramAppid(miniAppId);
+                    }
+                    else {
+                        //算主备小程序
+                        String finalAppId = getAppIdFromMiniMap(miniMap, companyId, sendMsgType, grade);
+
+                        if (StringUtil.strIsNullOrEmpty(finalAppId)) {
+                            finalAppId = miniAppId;
+                        }
+
+                        if (!StringUtil.strIsNullOrEmpty(finalAppId)) {
+                            setting.setMiniprogramAppid(finalAppId);
+                        } else {
+                            log.error("公司的小程序id为空:采用了前端传的固定值" + sopLogs.getSopId());
+                        }
+                    }
+                    setting.setMiniprogramTitle("福袋发放");
+                    setting.setMiniprogramPage(linkBy);
+                    setting.setContentType("14");
                     break;
                 default:
                     break;
@@ -2640,4 +2813,217 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private boolean isValidExternalContact(QwExternalContact externalContact) {
         return externalContact.getStatus() == 0 || externalContact.getStatus() == 2 || externalContact.getStatus() == 3;
     }
+
+    /**
+     * 创建福袋链接
+     * @param st
+     * @param sopLogs
+     * @param corpId
+     * @param sendTime
+     * @param courseId
+     * @param videoId
+     * @param qwUserId
+     * @param companyUserId
+     * @param companyId
+     * @param config
+     * @param chatId
+     * @return
+     */
+    public String createActivityLinkByMiniApp(QwSopTempSetting.Content.Setting st, QwSopLogs sopLogs, String corpId, Date sendTime, Long courseId, Long videoId, String qwUserId, String companyUserId, String companyId, CourseConfig config, String chatId) {
+        FsCourseLink link = createActivityByLink(corpId, sendTime, courseId, videoId, qwUserId.toString(),
+                companyUserId, companyId, null, 3);
+        link.setChatId(chatId);
+        Date updateTime = createUpdateTime(st, sendTime, config);
+        link.setUpdateTime(updateTime);
+        FsCourseRealLink courseMap = new FsCourseRealLink();
+        BeanUtils.copyProperties(link, courseMap);
+        Long businessId = addLuckyBagCollectRecord(st,sopLogs,updateTime,companyUserId,companyId,chatId);
+        courseMap.setBusinessId(String.valueOf(businessId));
+        st.setBusinessId(String.valueOf(businessId));
+        st.setExternalUserId(sopLogs.getExternalUserId());
+        String json = configService.selectConfigByKey("luckyBag.config");
+        Map<String, Object> luckyBagConfig = JSON.parseObject(json, Map.class);
+        Object miniprogramPicUrl = luckyBagConfig.get("miniprogramPicUrl");
+        if(miniprogramPicUrl != null){
+            st.setMiniprogramPicUrl(miniprogramPicUrl.toString());
+        }
+        courseMap.setQwExternalId(sopLogs.getExternalId());
+        String realLinkFull = appActivitlLink + JSON.toJSONString(courseMap);
+        link.setRealLink(realLinkFull);
+        log.error("存入fs_course_link:" + registeredRealLink );
+        log.error("QwSopCourseFinishTempSetting.Setting:{}" ,st );
+        //存短链-
+        enqueueCourseLink(link);
+        return link.getRealLink();
+    }
+
+    public FsCourseLink createActivityByLink(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(Long.valueOf(qwUserId));
+        link.setCompanyUserId(Long.parseLong(companyUserId));
+        link.setVideoId(videoId);
+        link.setCorpId(corpId);
+        link.setCourseId(courseId);
+        link.setQwExternalId(StringUtils.isNotEmpty(externalId)?Long.parseLong(externalId):null);
+        link.setLinkType(type); //小程序
+        link.setUNo(UUID.randomUUID().toString()); //小程序
+
+        String randomString = generateRandomStringWithLock();
+        if (StringUtil.strIsNullOrEmpty(randomString)) {
+            link.setLink(UUID.randomUUID().toString().replace("-", ""));
+        } else {
+            link.setLink(randomString);
+        }
+
+        link.setCreateTime(sendTime);
+
+        return link;
+    }
+
+    /**
+     * 增加福袋发放记录、领取记录
+     *
+     * @param content
+     * @param qwSopLogs
+     * @param sendTime
+     * @param companyUserId
+     * @param companyId
+     * @param chatId
+     */
+    private Long addLuckyBagCollectRecord(QwSopTempSetting.Content.Setting content,
+                                          QwSopLogs qwSopLogs,
+                                          Date sendTime,
+                                          String companyUserId,
+                                          String companyId,
+                                          String chatId) {
+        try {
+            // 参数校验
+            if (content == null || qwSopLogs == null || sendTime == null) {
+                log.warn("添加福袋记录失败:必要参数为空 [content:{}, qwSopLogs:{}, sendTime:{}]",
+                        content, qwSopLogs, sendTime);
+                return null;
+            }
+
+            if (StringUtils.isEmpty(companyId) || StringUtils.isEmpty(companyUserId)) {
+                log.warn("公司ID或用户ID为空 [companyId:{}, companyUserId:{}]", companyId, companyUserId);
+                return null;
+            }
+
+            // 验证福袋ID
+            if (content.getLuckyBagId() == null) {
+                log.warn("福袋ID为空");
+                return null;
+            }
+
+            // 查询福袋信息
+            LuckyBag luckyBag = luckyBagMapper.selectLuckyBagById(content.getLuckyBagId());
+            if (luckyBag == null) {
+                log.warn("未找到对应的福袋信息 [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            // 检查福袋状态
+            if (luckyBag.getDataStatus() != null && luckyBag.getDataStatus().equals(0)) {
+                log.warn("福袋被禁用 [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            // 查询公司信息
+            Company company = companyMapper.selectCompanyById(Long.valueOf(companyId));
+            if (company == null) {
+                log.warn("未找到对应的公司信息 [companyId:{}]", companyId);
+                return null;
+            }
+
+            // 构建福袋记录
+            LuckyBagCollectRecord luckyBagCollectRecord = buildLuckyBagRecord(content, qwSopLogs, sendTime,
+                    companyUserId, companyId, chatId, company, luckyBag);
+
+            // 插入记录并返回ID
+            int result = luckyBagCollectRecordMapper.insertLuckyBagCollectRecord(luckyBagCollectRecord);
+            if (result <= 0) {
+                log.warn("福袋记录插入失败 [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            // 返回新增记录的ID
+            Long recordId = luckyBagCollectRecord.getId();
+            if (recordId == null) {
+                log.warn("福袋记录插入成功但未返回ID [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            log.info("福袋记录添加成功 [recordId:{}, luckyBagId:{}]", recordId, content.getLuckyBagId());
+            return recordId;
+
+        } catch (NumberFormatException e) {
+            log.error("ID转换失败 [companyId:{}, companyUserId:{}]", companyId, companyUserId, e);
+            return null;
+        } catch (Exception e) {
+            log.error("ID:" + (content != null ? content.getLuckyBagId() : "unknown") + "-添加福袋记录失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 构建福袋记录对象
+     */
+    private LuckyBagCollectRecord buildLuckyBagRecord(QwSopTempSetting.Content.Setting content,
+                                                      QwSopLogs qwSopLogs,
+                                                      Date sendTime,
+                                                      String companyUserId,
+                                                      String companyId,
+                                                      String chatId,
+                                                      Company company,
+                                                      LuckyBag luckyBag) {
+        LuckyBagCollectRecord record = new LuckyBagCollectRecord();
+        if (luckyBag.getType().equals("1")) {
+            record.setCoinAmount(luckyBag.getAmount());
+        }
+        QwUser qwUser = qwUserMapper.selectQwUserEntityByQwUserIdAndCorId(qwSopLogs.getQwUserid(),qwSopLogs.getCorpId());
+        record.setQwUserId(qwUser.getQwUserId());
+        record.setQwUserName(qwUser.getQwUserName());
+        record.setLuckyBagId(content.getLuckyBagId());
+        record.setExpiryTime(sendTime);
+        record.setCollectType("3");
+        record.setCompanyId(Long.valueOf(companyId));
+        record.setUserId(qwSopLogs.getFsUserId());
+        if (ObjectUtil.isNotEmpty(qwSopLogs.getFsUserId())){
+            FsUser fsUser = fsUserMapper.selectFsUserByUserId(qwSopLogs.getFsUserId());
+            record.setUserName(ObjectUtil.isNotEmpty(fsUser)?fsUser.getNickName():null);
+        }
+        record.setCompanyName(company.getCompanyName());
+        record.setCompanyUserId(Long.valueOf(companyUserId));
+        record.setSendLink(content.getMiniprogramPage());
+
+        // 设置奖励类型和聊天信息
+        if (StringUtils.isNotEmpty(chatId)) {
+            record.setRewardType(1L);
+            record.setChatId(chatId);
+            record.setExternalUserName(qwSopLogs.getExternalUserName());
+        } else {
+            record.setRewardType(2L);
+        }
+
+        // 设置币种金额
+        if (luckyBag.getRewardType() != null && luckyBag.getRewardType().equals("1")) {
+            record.setCoinAmount(luckyBag.getAmount());
+        }
+
+        return record;
+    }
+
+    /**
+     * 设置SOP日志状态的辅助方法
+     */
+    private void setSopLogsStatus(QwSopLogs sopLogs, Long sendStatus, Long receivingStatus, String remark) {
+        if (sopLogs != null) {
+            sopLogs.setSendStatus(sendStatus);
+            sopLogs.setReceivingStatus(receivingStatus);
+            sopLogs.setRemark(remark);
+        }
+    }
 }

+ 286 - 9
fs-service/src/main/java/com/fs/qw/service/AsyncQwAiChatSopService.java

@@ -1,23 +1,28 @@
 package com.fs.qw.service;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyMiniapp;
+import com.fs.company.mapper.CompanyMapper;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.FsCourseLink;
-import com.fs.course.domain.FsCourseRealLink;
-import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.domain.*;
 import com.fs.course.mapper.FsCourseLinkMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.fastGpt.domain.FastGptChatReplaceWords;
 import com.fs.fastGpt.mapper.FastGptChatReplaceWordsMapper;
+import com.fs.his.domain.FsUser;
+import com.fs.his.mapper.FsUserMapper;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwExternalContactInfo;
 import com.fs.qw.domain.QwUser;
-import com.fs.qw.mapper.QwExternalContactInfoMapper;
-import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.mapper.*;
 import com.fs.qw.vo.QwSopRuleTimeVO;
 import com.fs.qw.vo.QwSopTempSetting;
 import com.fs.sop.domain.QwSopLogs;
@@ -42,10 +47,10 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
 
 @Slf4j
 @Service
@@ -54,6 +59,11 @@ public class AsyncQwAiChatSopService {
 
     private static final String miniappRealLink = "/pages_course/video.html?course=";
 
+
+    private static final String registeredRealLink = "/pages_course/register.html?link=";
+
+    private static final String appActivitlLink = "/pages_course/activity.html?link=";
+
     @Autowired
     private QwSopMapper qwSopMapper;
 
@@ -91,6 +101,23 @@ public class AsyncQwAiChatSopService {
 
     @Autowired
     private QwExternalContactInfoMapper qwExternalContactInfoMapper;
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private LuckyBagMapper luckyBagMapper;
+
+    @Autowired
+    private CompanyMapper companyMapper;
+
+    @Autowired
+    private QwUserMapper qwUserMapper;
+
+    @Autowired
+    private LuckyBagCollectRecordMapper luckyBagCollectRecordMapper;
+
+    @Autowired
+    private FsUserMapper fsUserMapper;
 
     @Async("threadPoolTaskExecutor")
     public void executeQwAiChatSop(QwSopAutoByTags qwSopAutoByTags, String userID,
@@ -204,6 +231,59 @@ public class AsyncQwAiChatSopService {
 
                             createVoiceUrlByNewChat(setting, qwUser.getCompanyUserId());
                             break;
+                        case "14":
+                            String miniAppId = null;
+                            List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
+                            Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
+                            Map<Integer, List<CompanyMiniapp>> integerListMap = miniMap.get(qwCompany.getId());
+                            if (integerListMap != null) {
+                                int listIndex = 1;
+                                List<CompanyMiniapp> miniapps = integerListMap.get(listIndex);
+                                if (miniapps != null && !miniapps.isEmpty()) {
+                                    CompanyMiniapp companyMiniapp = miniapps.get(0);
+                                    if (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId())) {
+                                        miniAppId = companyMiniapp.getAppId();
+                                    }
+                                }
+                            }
+
+                            if (StringUtil.strIsNullOrEmpty(miniAppId) && !StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())) {
+                                miniAppId = qwCompany.getMiniAppId();
+                            }
+                            try {
+                                Integer cacheValue = redisCache.getCacheObject("sopCourse:video:isPause:" + content.getVideoId());
+                                int isPause = (cacheValue != null) ? cacheValue : 0;
+                                if (isPause == 1){
+                                    sopLogs.setSendStatus(5L);
+                                    sopLogs.setRemark("含暂停课程,不发送");
+                                }
+
+
+                                // 查询福袋信息
+                                LuckyBag luckyBag = luckyBagMapper.selectLuckyBagById(setting.getLuckyBagId());
+                                if(ObjectUtil.isNotEmpty(luckyBag)&&luckyBag.getDataStatus().equals("0")){
+                                    setSopLogsStatus(sopLogs, 5L, 0L, "福袋配置被禁用");
+                                }
+
+                                // 8. 生成活动链接
+
+                                String link = createActivityLinkByMiniApp(setting, sopLogs,qwUser.getCorpId(), new Date(),
+                                        Long.valueOf(content.getCourseId()), Long.valueOf(content.getVideoId()) , userID, String.valueOf(qwUser.getCompanyUserId()),String.valueOf(qwUser.getCompanyId()), config, null);
+                                String finalMiniAppId = miniAppId;
+                                setting.setMiniprogramAppid(finalMiniAppId);
+                                setting.setMiniprogramTitle("福袋发放");
+                                setting.setMiniprogramPage(link);
+
+                                log.info("福袋配置成功,userId: {}, appId: {}", fsUserId, finalMiniAppId);
+
+                            } catch (Exception e) {
+                                log.error("任务模板福袋发放失败", e);
+                                // 确保在最终异常时也设置状态
+                                if (sopLogs != null) {
+                                    setSopLogsStatus(sopLogs, 5L, 0L, "福袋发放系统异常");
+                                }
+                            }
+                            break;
                         default:
                             break;
 
@@ -233,6 +313,42 @@ public class AsyncQwAiChatSopService {
 
     }
 
+    public String createActivityLinkByMiniApp(QwSopTempSetting.Content.Setting st, QwSopLogs sopLogs, String corpId, Date sendTime, Long courseId, Long videoId, String qwUserId, String companyUserId, String companyId, CourseConfig config, String chatId) {
+        FsCourseLink link = createActivityByLink(corpId, sendTime, courseId, videoId, qwUserId.toString(),
+                companyUserId, companyId, null, 3);
+        link.setChatId(chatId);
+        Date updateTime = createUpdateTime(st, sendTime, config);
+        link.setUpdateTime(updateTime);
+        FsCourseRealLink courseMap = new FsCourseRealLink();
+        BeanUtils.copyProperties(link, courseMap);
+        Long businessId = addLuckyBagCollectRecord(st,sopLogs,updateTime,companyUserId,companyId,chatId);
+        courseMap.setBusinessId(String.valueOf(businessId));
+        st.setBusinessId(String.valueOf(businessId));
+        st.setExternalUserId(sopLogs.getExternalUserId());
+        String json = configService.selectConfigByKey("luckyBag.config");
+        Map<String, Object> luckyBagConfig = JSON.parseObject(json, Map.class);
+        Object miniprogramPicUrl = luckyBagConfig.get("miniprogramPicUrl");
+        if(miniprogramPicUrl != null){
+            st.setMiniprogramPicUrl(miniprogramPicUrl.toString());
+        }
+        courseMap.setQwExternalId(sopLogs.getExternalId());
+        String realLinkFull = appActivitlLink + JSON.toJSONString(courseMap);
+        link.setRealLink(realLinkFull);
+        log.error("存入fs_course_link:" + registeredRealLink );
+        log.error("QwSopCourseFinishTempSetting.Setting:{}" ,st );
+        //存短链-
+        fsCourseLinkMapper.insertFsCourseLink(link);
+        return link.getRealLink();
+    }
+
+    private void setSopLogsStatus(QwSopLogs sopLogs, Long sendStatus, Long receivingStatus, String remark) {
+        if (sopLogs != null) {
+            sopLogs.setSendStatus(sendStatus);
+            sopLogs.setReceivingStatus(receivingStatus);
+            sopLogs.setRemark(remark);
+        }
+    }
+
     private void processAndInsertQwSopLogsBySendMsg(List<QwSopLogs> sopLogsList) {
         // 定义批量插入的大小
         int batchSize = 500;
@@ -361,5 +477,166 @@ public class AsyncQwAiChatSopService {
 
     }
 
+    public FsCourseLink createActivityByLink(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(Long.valueOf(qwUserId));
+        link.setCompanyUserId(Long.parseLong(companyUserId));
+        link.setVideoId(videoId);
+        link.setCorpId(corpId);
+        link.setCourseId(courseId);
+        link.setQwExternalId(StringUtils.isNotEmpty(externalId)?Long.parseLong(externalId):null);
+        link.setLinkType(type); //小程序
+        link.setUNo(UUID.randomUUID().toString()); //小程序
+
+        String randomString = generateRandomStringWithLock();
+        if (StringUtil.strIsNullOrEmpty(randomString)) {
+            link.setLink(UUID.randomUUID().toString().replace("-", ""));
+        } else {
+            link.setLink(randomString);
+        }
+
+        link.setCreateTime(sendTime);
+
+        return link;
+    }
+
+    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 Long addLuckyBagCollectRecord(QwSopTempSetting.Content.Setting content,
+                                          QwSopLogs qwSopLogs,
+                                          Date sendTime,
+                                          String companyUserId,
+                                          String companyId,
+                                          String chatId) {
+        try {
+            // 参数校验
+            if (content == null || qwSopLogs == null || sendTime == null) {
+                log.warn("添加福袋记录失败:必要参数为空 [content:{}, qwSopLogs:{}, sendTime:{}]",
+                        content, qwSopLogs, sendTime);
+                return null;
+            }
+
+            if (StringUtils.isEmpty(companyId) || StringUtils.isEmpty(companyUserId)) {
+                log.warn("公司ID或用户ID为空 [companyId:{}, companyUserId:{}]", companyId, companyUserId);
+                return null;
+            }
+
+            // 验证福袋ID
+            if (content.getLuckyBagId() == null) {
+                log.warn("福袋ID为空");
+                return null;
+            }
+
+            // 查询福袋信息
+            LuckyBag luckyBag = luckyBagMapper.selectLuckyBagById(content.getLuckyBagId());
+            if (luckyBag == null) {
+                log.warn("未找到对应的福袋信息 [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            // 检查福袋状态
+            if (luckyBag.getDataStatus() != null && luckyBag.getDataStatus().equals(0)) {
+                log.warn("福袋被禁用 [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            // 查询公司信息
+            Company company = companyMapper.selectCompanyById(Long.valueOf(companyId));
+            if (company == null) {
+                log.warn("未找到对应的公司信息 [companyId:{}]", companyId);
+                return null;
+            }
+
+            // 构建福袋记录
+            LuckyBagCollectRecord luckyBagCollectRecord = buildLuckyBagRecord(content, qwSopLogs, sendTime,
+                    companyUserId, companyId, chatId, company, luckyBag);
+
+            // 插入记录并返回ID
+            int result = luckyBagCollectRecordMapper.insertLuckyBagCollectRecord(luckyBagCollectRecord);
+            if (result <= 0) {
+                log.warn("福袋记录插入失败 [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            // 返回新增记录的ID
+            Long recordId = luckyBagCollectRecord.getId();
+            if (recordId == null) {
+                log.warn("福袋记录插入成功但未返回ID [luckyBagId:{}]", content.getLuckyBagId());
+                return null;
+            }
+
+            log.info("福袋记录添加成功 [recordId:{}, luckyBagId:{}]", recordId, content.getLuckyBagId());
+            return recordId;
+
+        } catch (NumberFormatException e) {
+            log.error("ID转换失败 [companyId:{}, companyUserId:{}]", companyId, companyUserId, e);
+            return null;
+        } catch (Exception e) {
+            log.error("ID:" + (content != null ? content.getLuckyBagId() : "unknown") + "-添加福袋记录失败", e);
+            return null;
+        }
+    }
+
+    private LuckyBagCollectRecord buildLuckyBagRecord(QwSopTempSetting.Content.Setting content,
+                                                      QwSopLogs qwSopLogs,
+                                                      Date sendTime,
+                                                      String companyUserId,
+                                                      String companyId,
+                                                      String chatId,
+                                                      Company company,
+                                                      LuckyBag luckyBag) {
+        LuckyBagCollectRecord record = new LuckyBagCollectRecord();
+        if (luckyBag.getType().equals("1")) {
+            record.setCoinAmount(luckyBag.getAmount());
+        }
+        QwUser qwUser = qwUserMapper.selectQwUserEntityByQwUserIdAndCorId(qwSopLogs.getQwUserid(),qwSopLogs.getCorpId());
+        record.setQwUserId(qwUser.getQwUserId());
+        record.setQwUserName(qwUser.getQwUserName());
+        record.setLuckyBagId(content.getLuckyBagId());
+        record.setExpiryTime(sendTime);
+        record.setCollectType("3");
+        record.setCompanyId(Long.valueOf(companyId));
+        record.setUserId(qwSopLogs.getFsUserId());
+        if (ObjectUtil.isNotEmpty(qwSopLogs.getFsUserId())){
+            FsUser fsUser = fsUserMapper.selectFsUserByUserId(qwSopLogs.getFsUserId());
+            record.setUserName(ObjectUtil.isNotEmpty(fsUser)?fsUser.getNickName():null);
+        }
+        record.setCompanyName(company.getCompanyName());
+        record.setCompanyUserId(Long.valueOf(companyUserId));
+        record.setSendLink(content.getMiniprogramPage());
+
+        // 设置奖励类型和聊天信息
+        if (StringUtils.isNotEmpty(chatId)) {
+            record.setRewardType(1L);
+            record.setChatId(chatId);
+            record.setExternalUserName(qwSopLogs.getExternalUserName());
+        } else {
+            record.setRewardType(2L);
+        }
+
+        // 设置币种金额
+        if (luckyBag.getRewardType() != null && luckyBag.getRewardType().equals("1")) {
+            record.setCoinAmount(luckyBag.getAmount());
+        }
+
+        return record;
+    }
+
 
 }