package com.fs.sop.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fs.common.core.domain.R;
import com.fs.common.utils.StringUtils;
import com.fs.company.mapper.CompanyUserMapper;
import com.fs.course.config.CourseConfig;
import com.fs.course.domain.FsCourseDomainName;
import com.fs.course.domain.FsCourseLink;
import com.fs.course.domain.FsCourseRealLink;
import com.fs.course.domain.FsCourseWatchLog;
import com.fs.course.mapper.FsCourseDomainNameMapper;
import com.fs.course.mapper.FsCourseLinkMapper;
import com.fs.course.mapper.FsCourseWatchLogMapper;
import com.fs.course.service.IFsCourseLinkService;
import com.fs.fastGpt.domain.FastGptChatReplaceWords;
import com.fs.fastGpt.mapper.FastGptChatReplaceWordsMapper;
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.impl.AsyncSopTestService;
import com.fs.qw.vo.QwSopCourseFinishTempSetting;
import com.fs.sop.domain.QwSop;
import com.fs.sop.domain.QwSopLogs;
import com.fs.sop.domain.SopUserLogs;
import com.fs.sop.domain.SopUserLogsInfo;
import com.fs.sop.mapper.QwSopLogsMapper;
import com.fs.sop.mapper.QwSopMapper;
import com.fs.sop.mapper.SopUserLogsInfoMapper;
import com.fs.sop.params.*;
import com.fs.sop.service.IQwSopService;
import com.fs.sop.service.ISopUserLogsInfoService;
import com.fs.sop.service.ISopUserLogsService;
import com.fs.sop.vo.QwCreateLinkByAppVO;
import com.fs.sop.vo.SopUserLogsInfoVOE;
import com.fs.system.service.ISysConfigService;
import com.fs.voice.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.validation.ConstraintViolationException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;

import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;

@Service
public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {

    private static final String REAL_LINK_PREFIX = "/courseH5/pages/course/learning?course=";
    private static final String SHORT_LINK_PREFIX = "/courseH5/pages/course/learning?s=";

    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 registerLink = "/registerCourse/pages/index?qwExternalId=";
//    private static final String miniappRealLink = "/pages/index/index?course=";

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private FsCourseDomainNameMapper fsCourseDomainNameMapper;

    private static final Logger log = LoggerFactory.getLogger(SopUserLogsInfoServiceImpl.class);
    @Autowired
    private SopUserLogsInfoMapper sopUserLogsInfoMapper;

    @Autowired
    private FsCourseLinkMapper fsCourseLinkMapper;

    @Autowired
    private FastGptChatReplaceWordsMapper fastGptChatReplaceWordsMapper;

    @Autowired
    private IQwSopService qwSopService;

    @Autowired
    private QwUserMapper qwUserMapper;

    @Autowired
    private QwSopMapper qwSopMapper;

    @Autowired
    private QwSopLogsMapper qwSopLogsMapper;

    @Autowired
    private ISopUserLogsService sopUserLogsService;

    @Autowired
    private FsCourseWatchLogMapper fsCourseWatchLogMapper;

    @Autowired
    private AsyncSopTestService asyncSopTestService;

    @Autowired
    private IFsCourseLinkService fsCourseLinkService;

    @Autowired
    private QwExternalContactMapper qwExternalContactMapper;

    @Autowired
    private CompanyUserMapper companyUserMapper;

    @Override
    public void save(SopUserLogsInfo sopUserLogsInfo) {
        sopUserLogsInfoMapper.insertSopUserLogsInfo(sopUserLogsInfo);
    }

    @Override
    public void update(SopUserLogsInfo info) {
        sopUserLogsInfoMapper.updateById(info);
    }

    @Override
    public SopUserLogsInfo getById(String id) {
        return sopUserLogsInfoMapper.selectById(id);
    }

    @Override
    public List<SopUserLogsInfo> getAll() {
        return sopUserLogsInfoMapper.selectAll();
    }

    @Override
    public void deleteById(String id) {
        sopUserLogsInfoMapper.deleteById(id);
    }

    @Override
    public int deleteSopUserLogsInfoByIds(String[] ids) {
        return sopUserLogsInfoMapper.deleteSopUserLogsInfoByIds(ids);
    }

    @Override
    public List<SopUserLogsInfo> selectSopUserLogsInfoList(SopUserLogsInfo info) {
     return  sopUserLogsInfoMapper.selectSopUserLogsInfoList(info);
    }
    @Override
    public List<SopUserLogsInfoVOE> selectSopUserLogsInfoListVO(SopUserLogsInfo info) {
        return  sopUserLogsInfoMapper.selectSopUserLogsInfoListVO(info);
    }


    @Override
    public void batchInsertSopUserLogsInfo(List<SopUserLogsInfo> logsToInsert) {
        sopUserLogsInfoMapper.batchInsertSopUserLogsInfo(logsToInsert);
    }

    @Override
    public void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert) {
        sopUserLogsInfoMapper.insertSopUserLogsInfo(logsToInsert);
    }

    /**
     * 查询sopUserLogsInfo
     *
     * @param id sopUserLogsInfo主键
     * @return sopUserLogsInfo
     */
    @Override
    public SopUserLogsInfo selectSopUserLogsInfoById(String id)
    {
        return sopUserLogsInfoMapper.selectSopUserLogsInfoById(id);
    }

    /**
     * 修改sopUserLogsInfo
     *
     * @param logsInfo sopUserLogsInfo
     * @return 结果
     */
    @Override
    public R updateSopUserLogsInfoToTime(SopUserLogsInfo logsInfo)
    {


        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            logsInfo.setUpdateTime(sdf.format(new Date()));

            //用于查询
            SopUserLogs userLogs=new SopUserLogs();
            userLogs.setSopId(logsInfo.getSopId());
            userLogs.setQwUserId(logsInfo.getQwUserId());
            userLogs.setCorpId(logsInfo.getCorpId());
            userLogs.setStatus(1);
            userLogs.setStartTime(logsInfo.getCreateTime());

            String  unionSopStartId = sopUserLogsService.selectSopUserLogsByUpdate(userLogs);


            SopUserLogsInfo sopUserLogsInfo=new SopUserLogsInfo();
            sopUserLogsInfo.setId(logsInfo.getId());
            sopUserLogsInfo.setQwUserId(logsInfo.getQwUserId());
            sopUserLogsInfo.setCorpId(logsInfo.getCorpId());
            sopUserLogsInfo.setExternalContactId(logsInfo.getExternalContactId());
            sopUserLogsInfo.setExternalId(logsInfo.getExternalId());
            sopUserLogsInfo.setSopId(logsInfo.getSopId());
            sopUserLogsInfo.setExternalUserName(logsInfo.getExternalUserName());
            sopUserLogsInfo.setCreateTime(logsInfo.getCreateTime());
            sopUserLogsInfo.setUpdateTime(sdf.format(new Date()));
            sopUserLogsInfo.setCrtTime(logsInfo.getCrtTime());

            //查询sop任务开始时间营期表 是否有这个营期
            if (!StringUtil.strIsNullOrEmpty(unionSopStartId)){
                //有这个营期，则更新
                sopUserLogsInfo.setUserLogsId(unionSopStartId);

                int i = sopUserLogsInfoMapper.updateSopUserLogsInfoToTime(sopUserLogsInfo);


            } else {
                //如果查不出来营期 ，则新建营期
                //查询sop的模板id
                QwSop qwSop = qwSopService.selectQwSopById(logsInfo.getSopId());

                //查询这个sop任务的员工的信息
                QwUser qwUser = qwUserMapper.selectQwUserByQwUseridAndCorpId(logsInfo.getQwUserId(), logsInfo.getCorpId());

                SopUserLogsParamByDate userLogsParamByDate = new SopUserLogsParamByDate();
                userLogsParamByDate.setSopId(logsInfo.getSopId());
                userLogsParamByDate.setSopTempId(qwSop.getTempId());
                userLogsParamByDate.setQwUserId(logsInfo.getQwUserId());
                userLogsParamByDate.setCorpId(logsInfo.getCorpId());
                userLogsParamByDate.setStartTime(logsInfo.getCreateTime());
                userLogsParamByDate.setStatus(1);
                userLogsParamByDate.setUserId(qwUser.getId()+"|"+qwUser.getCompanyUserId()+"|"+qwUser.getCompanyId());

                //如果没有这个营期没有就先插入
                sopUserLogsService.insertSopUserLogsByDate(userLogsParamByDate);

                userLogs.setSopTempId(qwSop.getTempId());
                //获取新的
                String  unionSopStartIdNew = sopUserLogsService.selectSopUserLogsByUnionSopId(userLogs);

                sopUserLogsInfo.setUserLogsId(unionSopStartIdNew);
                sopUserLogsInfo.setId(logsInfo.getId());
                int i = sopUserLogsInfoMapper.updateSopUserLogsInfoToTime(sopUserLogsInfo);

            }
        }catch (Exception e){
            return R.error().put("msg","修改营期失败："+e.getMessage());
        }

        return R.ok();
    }

    @Override
    public R batchUpdateSopUserLogsInfoToTime(BatchSopUserLogsInfoParam param) {

        try {

                //用于查询
                SopUserLogs userLogs=new SopUserLogs();
                userLogs.setSopId(param.getSopId());
                userLogs.setQwUserId(param.getQwUserId());
                userLogs.setCorpId(param.getCorpId());
                userLogs.setStatus(1);
                userLogs.setStartTime(param.getParamTime());

                String  unionSopStartId = sopUserLogsService.selectSopUserLogsByUpdate(userLogs);

                 BatchSopUserLogsInfoParamU infoParamU=new BatchSopUserLogsInfoParamU();
                    infoParamU.setIds(param.getIds());
                //查询sop任务开始时间营期表 是否有这个营期
                if (!StringUtil.strIsNullOrEmpty(unionSopStartId)){
                    infoParamU.setUserLogsId(unionSopStartId);
                    if (param.getIds().length>0){
                        batchUpdateSopUserLogsInfoToTime(infoParamU);
                    }

                }
                else {
                    //如果查不出来营期 ，则新建营期
                    //查询sop的模板id
                    QwSop qwSop = qwSopService.selectQwSopById(param.getSopId());

                    //查询这个sop任务的员工的信息
                    QwUser qwUser = qwUserMapper.selectQwUserByQwUseridAndCorpId(param.getQwUserId(), param.getCorpId());

                    SopUserLogsParamByDate userLogsParamByDate = new SopUserLogsParamByDate();
                    userLogsParamByDate.setSopId(param.getSopId());
                    userLogsParamByDate.setSopTempId(qwSop.getTempId());
                    userLogsParamByDate.setQwUserId(param.getQwUserId());
                    userLogsParamByDate.setCorpId(param.getCorpId());
                    userLogsParamByDate.setStartTime(param.getParamTime());
                    userLogsParamByDate.setStatus(1);
                    userLogsParamByDate.setUserId(qwUser.getId()+"|"+qwUser.getCompanyUserId()+"|"+qwUser.getCompanyId());

                    //如果没有这个营期没有就先插入
                    int i1 = sopUserLogsService.insertSopUserLogsByDate(userLogsParamByDate);
                    if (i1>0){
                        userLogs.setSopTempId(qwSop.getTempId());
                        //获取新的
                        String  unionSopStartIdNew = sopUserLogsService.selectSopUserLogsByUnionSopId(userLogs);
                        infoParamU.setUserLogsId(unionSopStartIdNew);

                        if (param.getIds().length>0){
                            batchUpdateSopUserLogsInfoToTime(infoParamU);
                        }

                    }



                }
            } catch (DuplicateKeyException e) {
                return R.error().put("msg", "修改营期失败：目标营期已经有此客户，请检查客户信息 是否重复，重复请联系超管 删除此营期数据");
            } catch (Exception e) {
                return R.error().put("msg", "修改营期失败：" + e.getMessage());
        }

        return R.ok();
    }


    public void batchUpdateSopUserLogsInfoToTime(BatchSopUserLogsInfoParamU paramU) {
        // 定义每批次的大小
        int batchSize = 200;
        String[] ids = paramU.getIds();

        for (int i = 0; i < ids.length; i += batchSize) {
            // 计算当前批次的结束索引
            int end = Math.min(i + batchSize, ids.length);

            // 拆分当前批次数据
            String[] batchIds = Arrays.copyOfRange(ids, i, end);

            // 创建新的参数对象
            BatchSopUserLogsInfoParamU batchParam = new BatchSopUserLogsInfoParamU();
            batchParam.setIds(batchIds);
            batchParam.setUserLogsId(paramU.getUserLogsId());

            // 执行批量更新
            sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoToTime(batchParam);
        }
    }


    @Override
    public R sendUserLogsInfoMsg(SendUserLogsInfoMsgParam param) {

        Date createTime=new Date();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        QwSop qwSop = qwSopMapper.selectQwSopById(param.getSopId());

        List<FastGptChatReplaceWords> words = fastGptChatReplaceWordsMapper.selectAllFastGptChatReplaceWords();

//        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIds(param.getIds());
        SopUserLogsInfoByIdsHasUserIdParam userIdParam=new SopUserLogsInfoByIdsHasUserIdParam();
        userIdParam.setIds(param.getIds());
        userIdParam.setIsRegister(param.getIsRegister());

        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIdsHasUserId(userIdParam);

        String[] userKey = param.getUserIdParam().split("\\|");
        String qwUserId = userKey[0].trim();
        String companyUserId = userKey[1].trim();
        String companyId = userKey[2].trim();

        QwUser qwUser = qwUserMapper.selectQwUserByIdByWeComeText(Long.valueOf(qwUserId));

        if (qwUser == null) {
              return R.error().put("msg","企业微信用户不存在:"+qwUserId);
        }

        String json = configService.selectConfigByKey("course.config");
        CourseConfig config = JSON.parseObject(json, CourseConfig.class);

        if (config == null) {
            return R.error().put("msg","课程默认配置为空，请联系管理员");
        }

        //域名
        String domainName = companyUserMapper.selectDomainByUserId(Long.parseLong(companyUserId));
        if (StringUtils.isEmpty(domainName)){
            domainName = config.getRealLinkDomainName();
        }
        String finalDomainName = domainName;

//        if (StringUtils.isEmpty(config.getMiniprogramPage())){
//            return R.error().put("msg","课程默认配置小程序路径为空，请联系管理员");
//        }

        List<QwSopLogs> sopLogsList=new ArrayList<>();

        sopUserLogsInfos.forEach(item->{

            QwSopLogs sopLogs=new QwSopLogs();

            sopLogs.setQwUserid(item.getQwUserId());
            sopLogs.setExternalUserId(item.getExternalContactId());
            sopLogs.setExternalId(item.getExternalId());
            sopLogs.setLogType(2);
            sopLogs.setContentJson(param.getSetting());
            sopLogs.setSendStatus(3L);
            sopLogs.setSendTime(sdf.format(new Date()));
            sopLogs.setCompanyId(qwSop.getCompanyId());
            sopLogs.setReceivingStatus(0L);
            sopLogs.setSopId(param.getSopId());
            sopLogs.setCorpId(item.getCorpId());
            sopLogs.setFsUserId(item.getFsUserId());
            sopLogs.setSort(2);
            sopLogs.setSendType(5);
            sopLogs.setExternalUserName(item.getExternalUserName());

            QwExternalContact contact = qwExternalContactMapper.selectQwExternalContactByIdForStageStatus(item.getExternalId());

            QwSopCourseFinishTempSetting setting=new    QwSopCourseFinishTempSetting();

            List<QwSopCourseFinishTempSetting.Setting> list = JSONArray.parseArray(param.getSetting(),QwSopCourseFinishTempSetting.Setting.class);
//
//            List<QwSopCourseFinishTempSetting.Setting> list = parseSettings(param.getSetting());

            for (QwSopCourseFinishTempSetting.Setting st : list) {

                //过滤违禁词
                if ("1".equals(st.getContentType())){
                    replaceContent(st.getContentType(), st.getValue(), st::setValue, words); // 替换 value
                }
                //过滤违禁词
                if ("3".equals(st.getContentType())){
                    replaceContent(st.getContentType(), st.getLinkTitle(), st::setLinkTitle, words); // 替换 linkTitle
                    replaceContent(st.getContentType(), st.getLinkDescribe(), st::setLinkDescribe, words); // 替换 linkTitle
                }

                switch (st.getContentType()){
                    //文字和短链一起
                    case "1":
                    case "3":
                        if ("1".equals(st.getIsBindUrl())) {

                            addWatchLogIfNeeded(param.getSopId(), param.getVideoId(), param.getCourseId(),item.getFsUserId(), qwUserId, companyUserId, companyId, item.getExternalId(),param.getStartTime(),createTime );

                            String sortLink = generateShortLink(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
                                    qwUserId, companyUserId, companyId,finalDomainName, item.getExternalId(),config,contact.getFsUserId());

                            if (StringUtils.isNotEmpty(sortLink)) {
                                if ("3".equals(st.getContentType())) {
                                    st.setLinkUrl(sortLink);
                                } else {
                                    String currentValue = st.getValue();
                                    if (currentValue == null) {
                                        st.setValue(sortLink);
                                    } else {
                                        st.setValue(currentValue
                                                .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(qwUser.getWelcomeText())?"":qwUser.getWelcomeText())
                                                .replaceAll("#客户称呼#",StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus())
                                                + "\n" + sortLink);
                                    }
                                }
                            } else {
                                log.warn("生成短链失败，跳过设置 URL。");
                            }
                        }else {
                            if ("1".equals(st.getContentType())) {
                                st.setValue(st.getValue()
                                        .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(qwUser.getWelcomeText())?"":qwUser.getWelcomeText())
                                        .replaceAll("#客户称呼#",StringUtil.strIsNullOrEmpty(contact.getStageStatus()) || "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus()));
                            }
                        }

                        break;
                    //小程序单独
                    case "4":
                        addWatchLogIfNeeded(param.getSopId(), param.getVideoId(), param.getCourseId(),item.getFsUserId(), qwUserId, companyUserId, companyId, item.getExternalId(),param.getStartTime(),createTime );

                        String linkByMiniApp = createLinkByMiniApp(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
                                qwUserId, companyUserId, companyId, item.getExternalId(), config,contact.getFsUserId());

                        st.setMiniprogramPage(linkByMiniApp.replaceAll("^[\\s\\u2005]+", ""));
                        break;
                    //app
                    case "9":
                        addWatchLogIfNeeded(param.getSopId(), param.getVideoId(), param.getCourseId(),item.getFsUserId(), qwUserId, companyUserId, companyId, item.getExternalId(),param.getStartTime(),createTime );

                        QwCreateLinkByAppVO linkByApp = createLinkByApp(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
                                qwUserId, companyUserId, companyId, item.getExternalId(), config,qwUser.getQwUserName(),contact.getFsUserId());
                        st.setLinkUrl(linkByApp.getSortLink().replaceAll("^[\\s\\u2005]+", ""));
                        st.setAppLinkUrl(linkByApp.getAppMsgLink().replaceAll("^[\\s\\u2005]+", ""));

                        break;
                    //注册链接
                    case "10":
                        String url=config.getRegisterDomainName()+registerLink+item.getExternalId();
                        st.setLinkUrl(url.replaceAll("^[\\s\\u2005]+", ""));
                        break;
                    default:
                        break;

                }
            }


            setting.setSetting(list);
            setting.setType(2);
            setting.setVideoId(param.getVideoId());
            setting.setCourseId(param.getCourseId());
            setting.setCourseType(param.getCourseType());

            sopLogs.setContentJson(JSON.toJSONString(setting));

            sopLogsList.add(sopLogs);
        });

        //批量插入 发送记录
        if (!sopLogsList.isEmpty()) {
            processAndInsertQwSopLogsBySendMsg(sopLogsList);
        }

        return R.ok();
    }

    private List<QwSopCourseFinishTempSetting.Setting> parseSettings(String jsonData) {
        try {
            if (jsonData.startsWith("[") && jsonData.endsWith("]")) {
                return JSONArray.parseArray(jsonData, QwSopCourseFinishTempSetting.Setting.class);
            } else {
                String fixedJson = JSON.parseObject(jsonData, String.class);
                return JSONArray.parseArray(fixedJson, QwSopCourseFinishTempSetting.Setting.class);
            }
        } catch (Exception e) {
            log.error("解析模板设置失败", e);
            return null;
        }
    }
    // 提取通用替换逻辑的方法
    private void replaceContent(String contentType, String content, Consumer<String> setter, List<FastGptChatReplaceWords> words) {
        if (contentType != null && content != null && !content.isEmpty()) {
            for (FastGptChatReplaceWords word : words) {
                if (content.contains(word.getContent())) {
                    // 如果包含目标字段，则替换
                    setter.accept(content.replace(word.getContent(), word.getChangeCount()));
                }
            }
        }
    }
    private void processAndInsertQwSopLogsBySendMsg(List<QwSopLogs> sopLogsList ) {
        // 定义批量插入的大小
        int batchSize = 500;

        // 循环处理外部用户 ID，每次处理批量大小的子集
        for (int i = 0; i < sopLogsList.size(); i += batchSize) {

            int endIndex = Math.min(i + batchSize, sopLogsList.size());
            List<QwSopLogs> batchList = sopLogsList.subList(i, endIndex);  // 获取当前批次的子集

            // 直接使用批次数据进行批量更新，不需要额外的 List
            try {
                qwSopLogsMapper.batchInsertQwSopLogsOneTouch(batchList);
            } catch (Exception e) {
                // 记录异常日志，方便后续排查问题
                log.error("批量执行一键群发时发生异常，处理的批次起始索引为: " + i, e);
            }
        }
    }

    //插入观看记录
    private void addWatchLogIfNeeded(String sopId, Integer videoId, Integer courseId,
                                     Long fsUserId, String qwUserId, String companyUserId,
                                     String companyId, Long externalId, String startTime,Date createTime) {

        try {
            FsCourseWatchLog watchLog = new FsCourseWatchLog();
            watchLog.setVideoId(videoId != null ? videoId.longValue() : null);
            watchLog.setQwExternalContactId(externalId);
            watchLog.setSendType(2);
            watchLog.setQwUserId(qwUserId);
            watchLog.setSopId(sopId);
            watchLog.setDuration(0L);
            watchLog.setCourseId(courseId != null ? courseId.longValue() : null);
            watchLog.setCompanyUserId(companyUserId != null ? Long.valueOf(companyUserId) : null);
            watchLog.setCompanyId(companyId != null ? Long.valueOf(companyId) : null);
            watchLog.setCreateTime(createTime);
            watchLog.setUpdateTime(createTime);
            watchLog.setLogType(3);
            watchLog.setUserId(fsUserId);
            watchLog.setCampPeriodTime(convertStringToDate(startTime,"yyyy-MM-dd"));

            //存看课记录
            fsCourseWatchLogMapper.insertOrUpdateFsCourseWatchLog(watchLog);
        }catch (Exception e){
            log.error("一键群发失败-插入观看记录失败："+e.getMessage());
        }


    }

    private String generateShortLink(QwSopCourseFinishTempSetting.Setting setting, String corpId, Date sendTime,
                                     Integer courseId, Integer videoId, String qwUserId,
                                     String companyUserId, String companyId,String domainName, Long externalId,CourseConfig config,Long FsUserId) {

        // 手动创建 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(externalId);
//        link.setLinkType(0); //正常链接
//        String randomString = generateRandomStringWithLock();
//        link.setLink(randomString);
//        link.setCreateTime(sendTime);

//        FsCourseRealLink courseMap = new FsCourseRealLink();
//        courseMap.setCompanyId(link.getCompanyId());
//        courseMap.setQwUserId(link.getQwUserId());
//        courseMap.setCompanyUserId(link.getCompanyUserId());
//        courseMap.setVideoId(link.getVideoId());
//        courseMap.setCorpId(link.getCorpId());
//        courseMap.setCourseId(link.getCourseId());
//        courseMap.setQwExternalId(link.getQwExternalId());
//        courseMap.setLinkType(0);

        FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, qwUserId,
                companyUserId, companyId, externalId,0);

        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 = createLink(link,domainName);

        //存短链-
        fsCourseLinkMapper.insertFsCourseLink(link);

        return sortLink.replaceAll("^[\\s\\u2005]+", "");
    }

    private String createLinkByMiniApp(QwSopCourseFinishTempSetting.Setting setting, String corpId, Date sendTime,
                                     Integer courseId, Integer videoId, String qwUserId,
                                     String companyUserId, String companyId, Long externalId,CourseConfig config,Long FsUserId) {

        FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, qwUserId,
                                                companyUserId, companyId, externalId,3);

        FsCourseRealLink courseMap = new FsCourseRealLink();
        BeanUtils.copyProperties(link,courseMap);
        courseMap.setFsUserId(FsUserId);

        String courseJson = JSON.toJSONString(courseMap);
        String realLinkFull = miniappRealLink + courseJson;
        link.setRealLink(realLinkFull);

        Date updateTime = createUpdateTime(setting, sendTime, config);

        link.setUpdateTime(updateTime);
        //存短链-
        fsCourseLinkMapper.insertFsCourseLink(link);
        return link.getRealLink();
    }

    private QwCreateLinkByAppVO createLinkByApp(QwSopCourseFinishTempSetting.Setting setting, String corpId,
                                                Date sendTime, Integer courseId, Integer videoId, String qwUserId,
                                                String companyUserId, String companyId, Long externalId,
                                                CourseConfig config,String qwUserName,Long fsUserId){

        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();

        QwCreateLinkByAppVO byAppVO=new QwCreateLinkByAppVO();
        byAppVO.setSortLink(sortLink);
        byAppVO.setAppMsgLink(appMsgLink);

            //异步生成app链接记录
        asyncSopTestService.createFsCourseSopAppLink(link.getLink(),sendTime,updateTime,companyId,companyUserId,qwUserId,
                qwUserName,corpId,courseId,setting.getLinkTitle(),setting.getLinkImageUrl(),videoId,
                setting.getLinkDescribe(),appMsgLink,externalId);

        //异步给app用户发送消息
        asyncSopTestService.asyncSendMsgBySopAppLink(externalId,setting.getLinkTitle(),setting.getLinkDescribe(),appMsgLink);



        //存短链-
        fsCourseLinkMapper.insertFsCourseLink(link);

        return byAppVO;
    }



    private String createLink(FsCourseLink link,String domain){
        String sortLink = domain + SHORT_LINK_PREFIX + link.getLink();
        return sortLink;
    }

    private Date createUpdateTime(QwSopCourseFinishTempSetting.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;
    }

    public FsCourseLink createFsCourseLink(String corpId, Date sendTime,Integer courseId,Integer videoId, String qwUserId,
                                           String companyUserId, String companyId,Long 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(externalId);
        link.setLinkType(type); //小程序
        String randomString = generateRandomStringWithLock();
        link.setLink(randomString);
        link.setCreateTime(sendTime);

        return link;
    }
    /**
     * 时间字符串转Date时间
     * @param dateString
     * @return
     */
    public static Date convertStringToDate(String dateString,String pattern) {
        if (dateString == null || dateString.isEmpty()) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        LocalDateTime localDateTime;
        LocalDate localDate;
        // 先解析成 LocalDate（只含年月日）
        if (pattern.equals("yyyy-MM-dd")){
            // 先解析成 LocalDate（只含年月日）
            localDate = LocalDate.parse(dateString, formatter);
            // 将 LocalDate 转为当天 00:00:00 的 LocalDateTime
            localDateTime = localDate.atStartOfDay();
        }else {
            localDateTime = LocalDateTime.parse(dateString, formatter);
        }
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }


}
