package com.fs.live.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fs.common.constant.LiveKeysConstant;
import com.fs.common.core.domain.R;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.StringUtils;
import com.fs.course.domain.FsCourseLink;
import com.fs.course.service.impl.FsUserCourseVideoServiceImpl;
import com.fs.enums.ExceptionCodeEnum;
import com.fs.course.domain.FsCourseLink;
import com.fs.course.service.impl.FsUserCourseVideoServiceImpl;
import com.fs.his.domain.FsUser;
import com.fs.his.mapper.FsUserMapper;
import com.fs.his.service.IFsUserService;
import com.fs.hisStore.domain.FsUserScrm;
import com.fs.hisStore.service.IFsUserScrmService;
import com.fs.live.domain.Live;
import com.fs.live.domain.LiveVideo;
import com.fs.live.domain.LiveWatchLog;
import com.fs.live.domain.LiveWatchUser;
import com.fs.live.mapper.*;
import com.fs.live.param.LiveIsAddKfParam;
import com.fs.live.service.ILiveWatchUserService;
import com.fs.live.vo.*;
import com.fs.qw.domain.QwExternalContact;
import com.fs.qwApi.domain.QwResult;
import com.fs.qwApi.param.QwEditUserTagParam;
import com.fs.qwApi.service.QwApiService;
import com.fs.qw.domain.QwGroupChat;
import com.fs.qw.domain.QwGroupChatUser;
import com.fs.qw.mapper.QwExternalContactMapper;
import com.fs.qw.mapper.QwGroupChatMapper;
import com.fs.qw.mapper.QwGroupChatUserMapper;
import com.fs.sop.domain.SopUserLogsInfo;
import com.fs.sop.service.ISopUserLogsInfoService;
import com.fs.live.vo.*;
import com.fs.qw.domain.QwExternalContact;
import com.fs.qwApi.domain.QwResult;
import com.fs.qwApi.param.QwEditUserTagParam;
import com.fs.qwApi.param.QwExternalContactRemarkParam;
import com.fs.qwApi.domain.QwExternalContactRemarkResult;
import com.fs.qwApi.service.QwApiService;
import com.fs.qw.domain.QwGroupChat;
import com.fs.qw.domain.QwGroupChatUser;
import com.fs.qw.mapper.QwExternalContactMapper;
import com.fs.qw.mapper.QwGroupChatMapper;
import com.fs.qw.mapper.QwGroupChatUserMapper;
import com.fs.sop.domain.SopUserLogsInfo;
import com.fs.sop.service.ISopUserLogsInfoService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 直播间观看用户Service业务层处理
 *
 * @author fs
 * @date 2025-01-18
 */
@Service
@Slf4j
public class LiveWatchUserServiceImpl implements ILiveWatchUserService {

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IFsUserScrmService fsUserService;
    @Autowired
    private LiveWatchUserMapper baseMapper;
    @Autowired
    private LiveMapper liveMapper;
    @Autowired
    private LiveVideoMapper liveVideoMapper;
    @Autowired
    private FsUserMapper fsUserMapper;
    @Autowired
    private QwGroupChatMapper qwGroupChatMapper;
    @Autowired
    private QwGroupChatUserMapper qwGroupChatUserMapper;
    @Autowired
    private QwExternalContactMapper qwExternalContactMapper;
    @Autowired
    private LiveWatchLogMapper liveWatchLogMapper;
    @Autowired
    private ISopUserLogsInfoService iSopUserLogsInfoService;

    @Autowired
    LiveTagConfigMapper liveTagConfigMapper;
    @Autowired
    private com.fs.qwApi.service.QwApiService qwApiService;


    private static final Logger logger = LoggerFactory.getLogger(LiveWatchUserServiceImpl.class);

    /**
     * 查询直播间观看用户
     *
     * @param id 直播间观看用户主键
     * @return 直播间观看用户
     */
    @Override
    public LiveWatchUser selectLiveWatchUserById(Long id)
    {
        return baseMapper.selectLiveWatchUserById(id);
    }

    /**
     * 查询企业直播间观看用户
     * @param id                直播间用户ID
     * @param companyId         企业ID
     * @param companyUserId     企业用户ID
     * @return LiveWatchUser
     */
    @Override
    public LiveWatchUser selectLiveWatchUserByIdAndCompanyIdAndCompanyUserId(Long id, Long companyId, Long companyUserId)
    {
        return baseMapper.selectLiveWatchUserByIdAndCompanyIdAndCompanyUserId(id, companyId, companyUserId);
    }

    /**
     * 查询直播间观看用户列表
     *
     * @param liveWatchUser 直播间观看用户
     * @return 直播间观看用户
     */
    @Override
    public List<LiveWatchUser> selectLiveWatchUserList(LiveWatchUser liveWatchUser)
    {
        if ("online".equals(liveWatchUser.getTabName())) {
            liveWatchUser.setOnline(0);
        } else if ("offline".equals(liveWatchUser.getTabName())) {
            liveWatchUser.setOnline(1);
        } else if ("silenced".equals(liveWatchUser.getTabName())) {
            liveWatchUser.setMsgStatus(1);
        }
        // 先查缓存，获取liveFlag和replayFlag
        if (liveWatchUser != null && liveWatchUser.getLiveId() != null) {
            Map<String, Integer> flagMap = getLiveFlagWithCache(liveWatchUser.getLiveId());
            liveWatchUser.setLiveFlag(flagMap.get("liveFlag"));
            liveWatchUser.setReplayFlag(flagMap.get("replayFlag"));
        }
        return baseMapper.selectLiveWatchUserList(liveWatchUser);
    }

    /**
     * 新增直播间观看用户
     *
     * @param liveWatchUser 直播间观看用户
     * @return 结果
     */
    @Override
    public int insertLiveWatchUser(LiveWatchUser liveWatchUser)
    {
        liveWatchUser.setCreateTime(DateUtils.getNowDate());
        liveWatchUser.setUpdateTime(DateUtils.getNowDate());
        return baseMapper.insertLiveWatchUser(liveWatchUser);
    }

    /**
     * 修改直播间观看用户
     *
     * @param liveWatchUser 直播间观看用户
     * @return 结果
     */
    @Override
    public int updateLiveWatchUser(LiveWatchUser liveWatchUser)
    {
        liveWatchUser.setUpdateTime(DateUtils.getNowDate());
        return baseMapper.updateLiveWatchUser(liveWatchUser);
    }

    /**
     * 批量更新直播间观看用户
     * @param liveWatchUsers 需要更新的观看用户列表
     * @return 更新的记录数
     */
    @Override
    public int batchUpdateLiveWatchUser(List<LiveWatchUser> liveWatchUsers) {
        if (liveWatchUsers == null || liveWatchUsers.isEmpty()) {
            return 0;
        }
        Date now = DateUtils.getNowDate();
        // 设置统一的更新时间
        for (LiveWatchUser user : liveWatchUsers) {
            if (user.getUpdateTime() == null) {
                user.setUpdateTime(now);
            }
        }
        return baseMapper.batchUpdateLiveWatchUser(liveWatchUsers);
    }

    /**
     * 批量插入直播间观看用户
     * @param liveWatchUsers 需要插入的观看用户列表
     * @return 插入的记录数
     */
    @Override
    public int batchInsertLiveWatchUser(List<LiveWatchUser> liveWatchUsers) {
        if (liveWatchUsers == null || liveWatchUsers.isEmpty()) {
            return 0;
        }
        Date now = DateUtils.getNowDate();
        // 设置统一的创建时间和更新时间
        for (LiveWatchUser user : liveWatchUsers) {
            if (user.getCreateTime() == null) {
                user.setCreateTime(now);
            }
            if (user.getUpdateTime() == null) {
                user.setUpdateTime(now);
            }
        }
        return baseMapper.batchInsertLiveWatchUser(liveWatchUsers);
    }

    /**
     * 清理直播间状态缓存
     * @param liveId 直播间ID
     */
    @Override
    public void clearLiveFlagCache(Long liveId) {
        String cacheKey = String.format(LiveKeysConstant.LIVE_FLAG_CACHE, liveId);
        redisCache.deleteObject(cacheKey);
    }

    @Override
    public List<LiveWatchUser> selectAllWatchUser(LiveWatchUser queryUser) {
        return baseMapper.selectLiveWatchUserList(queryUser);
    }

    /**
     * 批量删除直播间观看用户
     *
     * @param ids 需要删除的直播间观看用户主键
     * @return 结果
     */
    @Override
    public int deleteLiveWatchUserByIds(Long[] ids)
    {
        return baseMapper.deleteLiveWatchUserByIds(ids);
    }

    /**
     * 删除直播间观看用户信息
     *
     * @param id 直播间观看用户主键
     * @return 结果
     */
    @Override
    public int deleteLiveWatchUserById(Long id)
    {
        return baseMapper.deleteLiveWatchUserById(id);
    }

    @Override
    public List<LiveWatchUser> getByLiveIdAndUserId(long liveId, long userId) {
        // 先查缓存，获取liveFlag和replayFlag
        Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
        Map<String, Object> params = new HashMap<>();
        params.put("liveId", liveId);
        params.put("userId", userId);
        params.put("liveFlag", flagMap.get("liveFlag"));
        params.put("replayFlag", flagMap.get("replayFlag"));
        return baseMapper.selectUserByLiveIdAndUserId(params);
    }

    /**
     * 获取直播间的直播/回放状态（带缓存）
     * @param liveId 直播间ID
     * @return Map包含liveFlag和replayFlag
     */
    @Override
    public Map<String, Integer> getLiveFlagWithCache(Long liveId) {
        String cacheKey = String.format(LiveKeysConstant.LIVE_FLAG_CACHE, liveId);

        // 先查缓存
        Map<String, Integer> cached = redisCache.getCacheObject(cacheKey);
        if (cached != null && cached.containsKey("liveFlag") && cached.containsKey("replayFlag")) {
            return cached;
        }

        // 缓存不存在，查询数据库
        Integer liveFlag = liveMapper.selectLiveFlagByLiveId(liveId);
        if (liveFlag == null) {
            liveFlag = 0;
        }
        Integer replayFlag = 1 - liveFlag; // 反数

        // 构建结果
        Map<String, Integer> result = new HashMap<>();
        result.put("liveFlag", liveFlag);
        result.put("replayFlag", replayFlag);

        // 缓存结果，过期时间1分钟
        redisCache.setCacheObject(cacheKey, result, LiveKeysConstant.LIVE_FLAG_CACHE_EXPIRE, TimeUnit.SECONDS);

        return result;
    }

    @Override
    public LiveWatchUser join(FsUserScrm fsUser,long liveId, long userId, String location) {

        // 缓存直播间信息，过期时间4小时
        String liveCacheKey = "live:" + liveId;
        Live live = redisCache.getCacheObject(liveCacheKey);
        if (live == null) {
            live = liveMapper.selectLiveByLiveId(liveId);
            if (live != null) {
                redisCache.setCacheObject(liveCacheKey, live, 4, TimeUnit.HOURS);
            }
        }
        if (live == null) {
            throw new RuntimeException("直播间不存在");
        }
        Date now = DateUtils.getNowDate();

        // 获取直播/回放状态（带缓存）
        Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
        Integer liveFlag = flagMap.get("liveFlag");
        Integer replayFlag = flagMap.get("replayFlag");

        // 使用唯一索引查询：live_id, user_id, live_flag, replay_flag
        LiveWatchUser liveWatchUser = baseMapper.selectByUniqueIndex(liveId, userId, liveFlag, replayFlag);

        if (liveWatchUser != null) {
            // 存在则更新
            liveWatchUser.setUpdateTime(now);
            liveWatchUser.setOnline(0);
            if (StringUtils.isNotEmpty(location)) {
                liveWatchUser.setLocation(location);
            }
            baseMapper.updateLiveWatchUser(liveWatchUser);
        } else {
            // 不存在则插入
            liveWatchUser = new LiveWatchUser();
            liveWatchUser.setLiveId(liveId);
            liveWatchUser.setUserId(userId);
            liveWatchUser.setAvatar(fsUser.getAvatar());
            liveWatchUser.setMsgStatus(0);
            liveWatchUser.setOnline(0);
            liveWatchUser.setLocation(location);
            liveWatchUser.setLiveFlag(liveFlag);
            liveWatchUser.setReplayFlag(replayFlag);
            liveWatchUser.setCreateTime(now);
            liveWatchUser.setUpdateTime(now);
            baseMapper.insertLiveWatchUser(liveWatchUser);
        }

        liveWatchUser.setAvatar(fsUser.getAvatar());
        liveWatchUser.setNickName(fsUser.getNickname());
        String hashKey = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);
        redisCache.hashPut(hashKey, String.valueOf(userId), JSON.toJSONString(liveWatchUser));
        return liveWatchUser;
    }

    @Override
    public LiveWatchUser joinWithoutLocation(FsUserScrm fsUser,long liveId, long userId) {
        FsUserScrm fsUserVO = fsUserService.selectFsUserByUserId(userId);
        Date now = DateUtils.getNowDate();

        // 查询直播间信息
        Live live = liveMapper.selectLiveByLiveId(liveId);
        if (live == null) {
            throw new RuntimeException("直播间不存在");
        }

        // 获取直播/回放状态（带缓存）
        Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
        Integer liveFlag = flagMap.get("liveFlag");
        Integer replayFlag = flagMap.get("replayFlag");

        // 使用唯一索引查询：live_id, user_id, live_flag, replay_flag
        LiveWatchUser liveWatchUser = baseMapper.selectByUniqueIndex(liveId, userId, liveFlag, replayFlag);

        if (liveWatchUser != null) {
            // 存在则更新
            liveWatchUser.setUpdateTime(now);
            liveWatchUser.setOnline(0);
            baseMapper.updateLiveWatchUser(liveWatchUser);
        } else {
            // 不存在则插入
            liveWatchUser = new LiveWatchUser();
            liveWatchUser.setLiveId(liveId);
            liveWatchUser.setUserId(userId);
            liveWatchUser.setAvatar(fsUserVO.getAvatar());
            liveWatchUser.setMsgStatus(0);
            liveWatchUser.setOnline(0);
            liveWatchUser.setLiveFlag(liveFlag);
            liveWatchUser.setReplayFlag(replayFlag);
            liveWatchUser.setCreateTime(now);
            liveWatchUser.setUpdateTime(now);
            baseMapper.insertLiveWatchUser(liveWatchUser);
        }

        liveWatchUser.setAvatar(fsUserVO.getAvatar());
        liveWatchUser.setNickName(fsUserVO.getNickname());
        String hashKey = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);
        redisCache.hashPut(hashKey, String.valueOf(userId), JSON.toJSONString(liveWatchUser));
        return liveWatchUser;
    }
    private static final String USER_ENTRY_TIME_KEY = "live:user:entry:time:%s:%s";
    @Override
    public LiveWatchUser close(FsUserScrm fsUser,long liveId, long userId) {
        // 查询直播间信息
        Live live = liveMapper.selectLiveByLiveId(liveId);
        if (live == null) {
            throw new RuntimeException("直播间不存在");
        }
        try {
            // 从 Redis 获取用户进入时间
            String entryTimeKey = String.format(USER_ENTRY_TIME_KEY, liveId, userId);
            Object entryTimeObj = redisCache.getCacheObject(entryTimeKey);
            Long entryTime = null;
            if (entryTimeObj != null) {
                if (entryTimeObj instanceof Long) {
                    entryTime = (Long) entryTimeObj;
                } else if (entryTimeObj instanceof String) {
                    try {
                        entryTime = Long.parseLong((String) entryTimeObj);
                    } catch (NumberFormatException e) {
                        log.warn("无法解析进入时间字符串为Long: {}", entryTimeObj);
                    }
                } else if (entryTimeObj instanceof Number) {
                    entryTime = ((Number) entryTimeObj).longValue();
                }
            }
            // 获取当前直播/回放状态
            Map<String, Integer> flagMap = this.getLiveFlagWithCache(liveId);
            Integer currentLiveFlag = flagMap.get("liveFlag");
            Integer currentReplayFlag = flagMap.get("replayFlag");
            // 使用唯一索引查询：live_id, user_id, live_flag, replay_flag
            LiveWatchUser liveWatchUser = baseMapper.selectByUniqueIndex(liveId, userId, currentLiveFlag, currentReplayFlag);
            if (liveWatchUser == null) {
                return null;
            }
            long currentTimeMillis = System.currentTimeMillis();
            if (entryTime == null) {
                // 如果没有进入时间记录，可使用用户更新时间
                if (liveWatchUser.getUpdateTime() == null) {
                    entryTime = currentTimeMillis;
                } else {
                    entryTime = liveWatchUser.getUpdateTime().getTime();
                }
            }

            Date now = new Date();
            // 计算在线时长（秒）
            long durationSeconds = (currentTimeMillis - entryTime) / 1000;
            if (durationSeconds <= 0) {
                return liveWatchUser;
            }
            if (liveWatchUser != null) {
                // 累加在线时长
                Long onlineSeconds = liveWatchUser.getOnlineSeconds();
                if (onlineSeconds == null) {
                    onlineSeconds = 0L;
                }
                liveWatchUser.setOnlineSeconds(onlineSeconds + durationSeconds);
                liveWatchUser.setUpdateTime(now);
                liveWatchUser.setOnline(1);
                baseMapper.updateLiveWatchUser(liveWatchUser);
                String hashKey  = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);
                redisCache.hashDelete(hashKey, String.valueOf(userId));
                // 删除 Redis 中的进入时间记录
                redisCache.deleteObject(entryTimeKey);
                return liveWatchUser;
                // 更新数据库
//                liveWatchUserService.updateLiveWatchUserEntry(liveWatchUser);
                // 如果 LiveWatchUserEntry 存在，并且当前是直播状态（liveFlag = 1），更新 LiveWatchLog
//                if (currentLiveFlag != null && currentLiveFlag == 1
//                        && liveWatchUser.getCompanyId() != null && liveWatchUser.getCompanyId() > 0
//                        && liveWatchUser.getCompanyUserId() != null && liveWatchUser.getCompanyUserId() > 0) {
//                    updateLiveWatchLogTypeByDuration(liveId, userId,
//                            liveWatchUser.getCompanyId(), liveWatchUser.getCompanyUserId(),
//                            liveWatchUser.getOnlineSeconds());
//                }
            }

        } catch (Exception e) {
            log.error("更新用户在线时长异常：liveId={}, userId={}, error={}",
                    liveId, userId, e.getMessage(), e);
        }
        return null;
    }

    /**
     * 查询直播间在线用户列表
     * @param params 参数
     * @return list
     */
    @Override
    public List<LiveWatchUserVO> selectWatchUserList(Map<String, Object> params) {
        // 先查缓存，获取liveFlag和replayFlag
        if (params != null && params.get("liveId") != null) {
            Long liveId = Long.valueOf(params.get("liveId").toString());
            Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
            params.put("liveFlag", flagMap.get("liveFlag"));
            params.put("replayFlag", flagMap.get("replayFlag"));
        }
        return baseMapper.selectWatchUserListByLiveId(params);
    }

    /**
     * 修改直播间用户禁言状态
     * @param liveId    直播间ID
     * @param userId    用户ID
     * @return  受影响条数
     */
    @Override
    public int changeUserState(Long liveId, Long userId) {
        List<LiveWatchUser> liveWatchUser = getByLiveIdAndUserId(liveId, userId);
        if (!liveWatchUser.isEmpty()) {
            for (LiveWatchUser watchUser : liveWatchUser) {
                watchUser.setMsgStatus(Math.abs(1 - watchUser.getMsgStatus()));
                watchUser.setUpdateTime(DateUtils.getNowDate());
                baseMapper.updateLiveWatchUser(watchUser);
            }
            return 1;
        }
        return 0;
    }

    /**
     * 根据直播间ID和用户ID查询观看用户信息
     * @param liveId    直播间ID
     * @param userId    观看用户ID
     * @return LiveWatchUserVO
     */
    @Override
    public LiveWatchUserVO selectWatchUserByLiveIdAndUserId(Long liveId, Long userId) {
        // 先查缓存，获取liveFlag和replayFlag
        Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
        Map<String, Object> params = new HashMap<>();
        params.put("liveId", liveId);
        params.put("userId", userId);
        params.put("liveFlag", flagMap.get("liveFlag"));
        params.put("replayFlag", flagMap.get("replayFlag"));
        return baseMapper.selectWatchUserByLiveIdAndUserId(params);
    }

    @Override
    public List<LiveWatchUserVO> selectOnlineUserList(LiveWatchUser param) {
        // 先查缓存，获取liveFlag和replayFlag
        if (param != null && param.getLiveId() != null) {
            Map<String, Integer> flagMap = getLiveFlagWithCache(param.getLiveId());
            param.setLiveFlag(flagMap.get("liveFlag"));
            param.setReplayFlag(flagMap.get("replayFlag"));
        }
        return baseMapper.selectOnlineUserList(param);
    }

    @Override
    public List<LiveWatchUser> checkOnlineNoRewardUser(Long liveId, Date now) {
        // 先查缓存，获取liveFlag和replayFlag
        Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
        Map<String, Object> params = new HashMap<>();
        params.put("liveId", liveId);
        params.put("now", now);
        params.put("liveFlag", flagMap.get("liveFlag"));
        params.put("replayFlag", flagMap.get("replayFlag"));
        return baseMapper.checkOnlineNoRewardUser(params);
    }

    @Override
    public int blockUser(Long userId) {
        FsUserScrm fsUser = new FsUserScrm();
        fsUser.setUserId(userId);
        fsUser.setStatus(0);
        return fsUserService.updateFsUser(fsUser);
    }

    @Override
    public List<LiveWatchUser> selectLiveWatchAndRegisterUser(Long liveId, Long lotteryId) {
        // 先查缓存，获取liveFlag和replayFlag
        Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
        Map<String, Object> params = new HashMap<>();
        params.put("liveId", liveId);
        params.put("lotteryId", lotteryId);
        params.put("liveFlag", flagMap.get("liveFlag"));
        params.put("replayFlag", flagMap.get("replayFlag"));
        return baseMapper.selectLiveWatchAndRegisterUser(params);
    }

    @Override
    public List<LiveWatchUserVO> asyncToCache(Long liveId) {
        LiveWatchUser liveWatchUser = new LiveWatchUser();
        liveWatchUser.setLiveId(liveId);
        List<LiveWatchUserVO> liveWatchUserVOS = selectOnlineUserList(liveWatchUser);

        if (CollUtil.isNotEmpty(liveWatchUserVOS)){
            ThreadUtil.execute(()->{
                String hashKey  = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);
                // 清空数据
                redisCache.deleteObject(hashKey);
                Map<String, String> collect = liveWatchUserVOS.stream()
                        .collect(Collectors.toMap(
                                watchUserVO -> String.valueOf(watchUserVO.getUserId()),
                                JSON::toJSONString
                        ));
                redisCache.hashPut(hashKey,collect);
            });
        }
        return liveWatchUserVOS;
    }

    @Override
    public R liveUserTotals(LiveWatchUser liveWatchUser) {
        LiveWatchUserStatistics liveWatchUserStatistics = baseMapper.liveUserTotals(liveWatchUser);
        if(liveWatchUserStatistics == null)
        {
            liveWatchUserStatistics = new LiveWatchUserStatistics();
            liveWatchUserStatistics.setOnline(0);
            liveWatchUserStatistics.setOffline(0);
            liveWatchUserStatistics.setSilenced(0);
            liveWatchUserStatistics.setAl(0);
            return R.ok().put("data", liveWatchUserStatistics);
        }
        if(liveWatchUserStatistics.getOnline() == null)
        {
            liveWatchUserStatistics.setOnline(0);
        }
        if (liveWatchUserStatistics.getOffline() == null) {
            liveWatchUserStatistics.setOffline(0);
        }
        if (liveWatchUserStatistics.getSilenced() == null) {
            liveWatchUserStatistics.setSilenced(0);
        }
        if (liveWatchUserStatistics.getAl() == null) {
            liveWatchUserStatistics.setAl(0);
        }
        return R.ok().put("data", liveWatchUserStatistics);
    }

    @Override
    public List<LiveWatchUserVO> dashBoardWatchUserList(Map<String, Object> param) {
        // 先查缓存，获取liveFlag和replayFlag
        if (param != null && param.get("liveId") != null) {
            Long liveId = Long.valueOf(param.get("liveId").toString());
            Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
            param.put("liveFlag", flagMap.get("liveFlag"));
            param.put("replayFlag", flagMap.get("replayFlag"));
        }
        if (param != null && param.containsKey("all") && "1".equals(param.get("all"))) {
            return baseMapper.selectWatchUserListAllByLiveId(param);
        } else {
            if(param != null && param.containsKey("msgStatus") && "1".equals(param.get("msgStatus")) && param.containsKey("online") ){
                param.remove("online");
            }
            return baseMapper.selectWatchUserListByLiveId(param);
        }
    }

    @Override
    public void updateGlobalVisible(long liveId, int status) {
        baseMapper.updateGlobalVisible(liveId, status);
    }

    @Override
    public void updateSingleVisible(long liveId, Integer status, long userId) {
        baseMapper.updateSingleVisible(liveId, status,userId);
    }

    @Override
    public LiveWatchUser selectLiveWatchUserByFlag(Long liveId, Long userId, Integer liveFlag, Integer replayFlag) {
        return baseMapper.selectByUniqueIndex(liveId, userId, liveFlag, replayFlag);
    }

    /**
     * 直播链接打开判定是否添加客户
     * @param param
     * @return
     */
    @Override
    public R liveIsAddKf(LiveIsAddKfParam param) {

        logger.info("【直播判断添加客服】：{}", param);
        //查询用户
        FsUser fsUser = fsUserMapper.selectFsUserByUserId(param.getUserId());
        //用户不存在唤起重新授权
        if (fsUser == null) {
            return R.error(ExceptionCodeEnum.USER_NOT_FOUND.getCode(), ExceptionCodeEnum.USER_NOT_FOUND.getDescription());
        }
        if (fsUser.getStatus() == 0) {
            return R.error(ExceptionCodeEnum.MEMBER_DISABLED.getCode(), ExceptionCodeEnum.MEMBER_DISABLED.getDescription());
        }
        //未注册提示
        String noRegisterMsg = "由于您还未完成注册，请联系伴学助手完成注册即可观看!";
        //非独属链接提示
        String noMemberMsg = "此链接已被绑定，请联系伴学助手领取您的专属链接，专属链接请勿分享哦!";

        if (StringUtils.isNotBlank(param.getChatId())) {
            return handleLiveChat(param,fsUser, noMemberMsg, noRegisterMsg);
        } else if (null != param.getQwExternalId()) {
            return handleLivePerson(param,fsUser, noMemberMsg, noRegisterMsg);
        } else {
            return R.error(ExceptionCodeEnum.LIVE_PARAM_ERROR.getCode(), ExceptionCodeEnum.LIVE_PARAM_ERROR.getDescription());
        }

    }

    @Override
    public LiveWatchUserEntry selectLiveWatchAndCompanyUserByFlag(Long liveId, Long userId, Integer liveFlag, Integer replayFlag) {
        return baseMapper.selectLiveWatchAndCompanyUserByFlag(liveId,userId,liveFlag,replayFlag);
    }

    @Override
    public void updateLiveWatchUserEntry(LiveWatchUserEntry liveWatchUser) {
        LiveWatchUser updateEntity = new LiveWatchUser();
        BeanUtil.copyProperties(updateEntity, liveWatchUser);
        baseMapper.updateLiveWatchUser(updateEntity);
    }

    /**
     * 处理发送群聊逻辑
     * @param param
     * @param user
     * @param noMemberMsg
     * @param noRegisterMsg
     * @return
     */
    public R handleLiveChat(LiveIsAddKfParam param,FsUser user, String noMemberMsg,String noRegisterMsg){

        QwGroupChat qwGroupChat = qwGroupChatMapper.selectQwGroupChatByChatId(param.getChatId());
        if (qwGroupChat == null) {
            return R.error("直播群参数异常");
        }
        SopUserLogsInfo sopUserLogsInfo = new SopUserLogsInfo();
        sopUserLogsInfo.setChatId(param.getChatId());
        List<QwGroupChatUser> qwGroupChatUsers = qwGroupChatUserMapper.selectByChatId(sopUserLogsInfo);
        if (qwGroupChatUsers == null || qwGroupChatUsers.isEmpty()) {
            return R.error("直播群参数异常");
        }

        QwExternalContact qwExternalContact = null;
        if (null != param.getUserId() && null == qwExternalContact) {
            try {
                qwExternalContact = qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
                        .eq("user_id", qwGroupChat.getOwner())
                        .eq("fs_user_id", param.getUserId())
                        .eq("corp_id", param.getCorpId())
                        .eq("status", 0));
            } catch (Exception e) {
                log.error("直播群聊用户id匹配异常,参数user_id：{},fs_user_id:{},corp_id:{}", qwGroupChat.getOwner(), param.getUserId(), param.getCorpId(), e);
            }
        }
        if (StringUtils.isNotBlank(param.getChatId()) && null == qwExternalContact) {
            List<QwExternalContact> groupChatUserByChatIdAndUserName = qwExternalContactMapper.getGroupChatUserByChatIdAndUserName(qwGroupChat.getOwner(), user.getNickName(), param.getCorpId(), param.getChatId());
            log.info("直播群聊用户查询结果,参数user_id：{},name:{},corp_id:{},chatId:{},groupChatUserByChatIdAndUserName:{}", qwGroupChat.getOwner(), user.getNickName(), param.getCorpId(), param.getChatId(), groupChatUserByChatIdAndUserName);
            //没找到用户 || 找到的用户数量大于1 使用userid查询匹配
            if (null == groupChatUserByChatIdAndUserName || groupChatUserByChatIdAndUserName.isEmpty() || groupChatUserByChatIdAndUserName.size() > 1) {
                log.error("直播群聊用户昵称匹配异常,参数user_id：{},name:{},corp_id:{},chatId:{}", qwGroupChat.getOwner(), user.getNickName(), param.getCorpId(), param.getChatId());
            } else {
                qwExternalContact = groupChatUserByChatIdAndUserName.get(0);
            }
        }
        if(qwExternalContact==null){
            return R.error(noRegisterMsg);
        }
        QwExternalContact finalQwExternalContact = qwExternalContact;
        if (qwGroupChatUsers.stream().noneMatch(e -> e.getUserId().equals(finalQwExternalContact.getExternalUserId()))) {
            log.error("直播客户不在群：{}，里面：{}", qwGroupChat.getChatId(), qwExternalContact.getExternalUserId());
            return R.error(noRegisterMsg);
        }
        Long qwExternalId = qwExternalContact.getId();

        LiveWatchLog liveWatchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(param.getLiveId(), param.getQwUserId(),qwExternalId);
        if (liveWatchLog==null ){
            return R.error(noRegisterMsg);
        }
        //判断外部联系人有没有绑定userId
        if (qwExternalContact.getFsUserId() != null) {
            //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
            if (!qwExternalContact.getFsUserId().equals(param.getUserId())) {
                return R.error(noRegisterMsg);
            }
            List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());
            //匹配客户公司id
            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
                return R.error(noRegisterMsg);
            }

            //看课记录中userId为0绑定userId
            if (liveWatchLog.getUserId() == null || liveWatchLog.getUserId().equals(0L) || !liveWatchLog.getUserId().equals(param.getUserId())) {
                liveWatchLog.setUserId(param.getUserId());
            }

            liveWatchLog.setUpdateTime(new Date());

            liveWatchLogMapper.updateLiveWatchLog(liveWatchLog);
            iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId, param.getUserId());


        } else {
            //没绑定fsUser直接绑定fsUser
            QwExternalContact contact = new QwExternalContact();
            contact.setId(qwExternalId);
            contact.setFsUserId(param.getUserId());
            qwExternalContactMapper.updateQwExternalContact(contact);
            iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId, param.getUserId());

            FsUser fsUser = new FsUser();
            fsUser.setUserId(user.getUserId());
            fsUser.setIsAddQw(1);
            fsUserMapper.updateFsUser(fsUser);
            //绑定上之后 更新观看记录
            //看课记录中userId为0绑定userId
            liveWatchLog.setUserId(param.getUserId());
            liveWatchLog.setUpdateTime(new Date());
            liveWatchLogMapper.updateLiveWatchLog(liveWatchLog);
        }

        return R.ok();
    }

    /**
     * 处理发送个人逻辑
     * @param param
     * @param noMemberMsg
     * @param noRegisterMsg
     * @return
     */
    public R handleLivePerson(LiveIsAddKfParam param,FsUser user,String noMemberMsg,String noRegisterMsg){

        Long qwExternalId = param.getQwExternalId();

        LiveWatchLog liveWatchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(param.getLiveId(), param.getQwUserId(),qwExternalId);

        if (liveWatchLog==null ){
            return R.error(noMemberMsg);
        }
        //查询是否有添加客服
        QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(qwExternalId);

        //用小程序id查询外部联系人
        List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());

        //判断外部联系人有没有绑定userId
        if (externalContact.getFsUserId() != null) {
            //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
            if (!externalContact.getFsUserId().equals(param.getUserId())) {
                return R.error(noMemberMsg);
            }
            //匹配客户公司id
            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
                return R.error(noMemberMsg);
            }

            //看课记录中userId为0绑定userId
            if (liveWatchLog.getUserId() == null || liveWatchLog.getUserId().equals(0L) || !liveWatchLog.getUserId().equals(param.getUserId())) {
                liveWatchLog.setUserId(param.getUserId());
            }

            liveWatchLog.setUpdateTime(new Date());

            liveWatchLogMapper.updateLiveWatchLog(liveWatchLog);
            iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId, param.getUserId());

        } else { //没绑定fsUser直接绑定fsUser
            QwExternalContact contact = new QwExternalContact();
            contact.setId(qwExternalId);
            contact.setFsUserId(param.getUserId());
            qwExternalContactMapper.updateQwExternalContact(contact);
            iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId, param.getUserId());

            FsUser fsUser = new FsUser();
            fsUser.setUserId(user.getUserId());
            fsUser.setIsAddQw(1);
            fsUserMapper.updateFsUser(fsUser);
            //绑定上之后 更新观看记录
            //看课记录中userId为0绑定userId
            liveWatchLog.setUserId(param.getUserId());
            liveWatchLog.setUpdateTime(new Date());
            liveWatchLogMapper.updateLiveWatchLog(liveWatchLog);
        }

        return R.ok();

    }

    /**
     * 根据用户直播看课记录来打标签
     */
    @Override
    @Async
    public void qwTagMarkByLiveWatchLog(Long liveId) {
        //休眠2分钟 等待看课中断的所有看课记录状态被正确处理标记
        try {
            Thread.sleep(120000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("处理直播间打标签: liveId={}", liveId);
        //查询直播间的标签配置
        List<LiveTagItemVO> liveTagConfig = liveTagConfigMapper.getLiveTagListByliveId(liveId);
        log.info("处理直播间打标签: liveTagConfig={}", liveTagConfig);
        if(null == liveTagConfig || liveTagConfig.isEmpty()){
            return;
        }
        /**
         * 8	回放已下单
         * 7	直播已下单
         * 6	回放已完课
         * 5	直播已完课
         * 4	回放到课未完课
         * 3	直播到课未完课
         * 2	回放未到课
         * 1	直播未到课
         */
        Map<Integer, LiveTagItemVO> liveTagMp = liveTagConfig.stream()
                .collect(Collectors.toMap(
                        LiveTagItemVO::getMarkType,
                        Function.identity(),
                        (existing, replacement) -> existing
                ));
        //查询直播间的看课记录
        List<LiveWatchLog> liveWatchLogs = liveWatchLogMapper.selectLiveWatchLogByLiveId(liveId);
        log.info("处理直播间打标签: liveWatchLogs={}", liveWatchLogs);
        if(null == liveWatchLogs || liveWatchLogs.isEmpty()){
            return;
        }
        //根据配置给每位用户打上标签
        List<HandleUserTagVO> handleUserTagVOS = new ArrayList<>();
        liveWatchLogs.forEach(liveLog -> {
            HandleUserTagVO addItem = new HandleUserTagVO();
            addItem.setLiveId(liveId);
            addItem.setExternalId(liveLog.getExternalContactId());
            LiveTagItemVO liveTagItemVO = null;
            List<String> tags = new ArrayList<>();
            switch (liveLog.getLogType()) {
                //1看课中
                case 1:
                    //打标签 直播到课未完课
                    liveTagItemVO = liveTagMp.get(3);
                    if (null != liveTagItemVO) {
                        tags.add(liveTagItemVO.getQwTagRealId());
                    }
                    break;
                //2完课
                case 2:
                    //打标签 直播已完课
                    liveTagItemVO = liveTagMp.get(5);
                    if (null != liveTagItemVO) {
                        tags.add(liveTagItemVO.getQwTagRealId());
                    }
                    // 设置完课标志
                    addItem.setIsCompleted(true);
                    break;
                //3待看课
                case 3:

                    //打标签 直播未到课
                    liveTagItemVO = liveTagMp.get(1);
                    if (null != liveTagItemVO) {
                        tags.add(liveTagItemVO.getQwTagRealId());
                    }
                    break;
                //4看课中断
                case 4:
                    //打标签 直播未到课
                    liveTagItemVO = liveTagMp.get(3);
                    if (null != liveTagItemVO) {
                        tags.add(liveTagItemVO.getQwTagRealId());
                    }
                    break;
                default:
                    break;
            }
            addItem.setTags(tags);
            if (null != liveLog.getLiveBuy() && liveLog.getLiveBuy().equals(1)) {
                liveTagItemVO = liveTagMp.get(7);
                if (null != liveTagItemVO) {
                    tags.add(liveTagItemVO.getQwTagRealId());
                }
            }
            handleUserTagVOS.add(addItem);
        });
        log.info("处理直播间打标签最终打标签：{}",handleUserTagVOS);
        handleUserTags2Qw(handleUserTagVOS);
    }

    /**
     * 对企微用户打标签
     * @param userTagVOS 用户标签列表，包含外部联系人ID和要添加的标签列表
     */
    private void handleUserTags2Qw(List<HandleUserTagVO> userTagVOS) {
        if (CollUtil.isEmpty(userTagVOS)) {
            log.warn("用户标签列表为空，跳过打标签操作");
            return;
        }

        int successCount = 0;
        int failCount = 0;

        for (HandleUserTagVO userTagVO : userTagVOS) {
            try {
                // 参数校验
                if (userTagVO.getExternalId() == null) {
                    log.warn("外部联系人ID为空，跳过该用户");
                    failCount++;
                    continue;
                }

                if (CollUtil.isEmpty(userTagVO.getTags())) {
                    log.warn("标签列表为空，跳过该用户: externalId={}", userTagVO.getExternalId());
                    failCount++;
                    continue;
                }

                // 根据外部联系人ID查询企微外部联系人信息
                QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactById(userTagVO.getExternalId());
                if (qwExternalContact == null) {
                    log.warn("未找到企微外部联系人: externalId={}", userTagVO.getExternalId());
                    failCount++;
                    continue;
                }

                // 校验必要字段
                if (StringUtils.isEmpty(qwExternalContact.getUserId())
                        || StringUtils.isEmpty(qwExternalContact.getExternalUserId())
                        || StringUtils.isEmpty(qwExternalContact.getCorpId())) {
                    log.warn("企微外部联系人信息不完整: externalId={}, userId={}, externalUserId={}, corpId={}",
                            userTagVO.getExternalId(),
                            qwExternalContact.getUserId(),
                            qwExternalContact.getExternalUserId(),
                            qwExternalContact.getCorpId());
                    failCount++;
                    continue;
                }

                // 构建打标签参数
                QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
                qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
                qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
                qwEditUserTagParam.setAdd_tag(userTagVO.getTags());

                // 调用企微API打标签
                QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, qwExternalContact.getCorpId());

                if (qwResult != null && qwResult.getErrcode() == 0) {
                    // 打标签成功，更新数据库中的标签信息
                    String existingTagIds = qwExternalContact.getTagIds();
                    Set<String> uniqueTagIds = new HashSet<>();

                    // 合并现有标签
                    if (StringUtils.isNotEmpty(existingTagIds)) {
                        try {
                            List<String> parsedTags = JSON.parseArray(existingTagIds, String.class);
                            if (CollUtil.isNotEmpty(parsedTags)) {
                                uniqueTagIds.addAll(parsedTags);
                            }
                        } catch (Exception e) {
                            log.warn("解析现有标签失败: externalId={}, tagIds={}, error={}",
                                    userTagVO.getExternalId(), existingTagIds, e.getMessage());
                        }
                    }

                    // 添加新标签
                    uniqueTagIds.addAll(userTagVO.getTags());

                    // 更新数据库
                    QwExternalContact updateContact = new QwExternalContact();
                    updateContact.setId(qwExternalContact.getId());
                    updateContact.setTagIds(JSON.toJSONString(new ArrayList<>(uniqueTagIds)));
                    qwExternalContactMapper.updateQwExternalContact(updateContact);

                    // 处理完课备注更新逻辑
                    Boolean isCompleted = userTagVO.getIsCompleted() != null && userTagVO.getIsCompleted();
                    updateRemarkForCompletedCourse(userTagVO.getLiveId(), userTagVO.getExternalId(), qwExternalContact, isCompleted);

                    successCount++;
                    log.info("成功为用户打标签: externalId={}, userId={}, externalUserId={}, tags={}",
                            userTagVO.getExternalId(),
                            qwExternalContact.getUserId(),
                            qwExternalContact.getExternalUserId(),
                            userTagVO.getTags());
                } else {
                    // 打标签失败
                    failCount++;
                    String errorMsg = qwResult != null ? qwResult.getErrmsg() : "未知错误";
                    log.error("为用户打标签失败: externalId={}, userId={}, externalUserId={}, error={}",
                            userTagVO.getExternalId(),
                            qwExternalContact.getUserId(),
                            qwExternalContact.getExternalUserId(),
                            errorMsg);
                }
            } catch (Exception e) {
                failCount++;
                log.error("为用户打标签异常: externalId={}, error={}",
                        userTagVO.getExternalId(), e.getMessage(), e);
            }
        }

        log.info("打标签操作完成: 总数={}, 成功={}, 失败={}",
                userTagVOS.size(), successCount, failCount);
    }

    /**
     * 更新完课备注
     * 如果用户完课，在备注中添加或更新完课日期
     * 格式：MMdd完课-原有备注
     *
     * @param liveId 直播间ID
     * @param externalId 外部联系人ID
     * @param qwExternalContact 企微外部联系人信息
     * @param isCompleted 完课标志（如果为true，不再查询数据库）
     */
    private void updateRemarkForCompletedCourse(Long liveId, Long externalId, QwExternalContact qwExternalContact, Boolean isCompleted) {
        try {
            // 如果liveId为空，跳过
            if (liveId == null || externalId == null) {
                return;
            }

            //
            boolean completed = false;
            if (isCompleted != null) {
                // 使用传入的完课标志
                completed = isCompleted;
            }

            if (!completed) {
                // 未完课，不改动备注
                log.debug("用户未完课，不更新备注: liveId={}, externalId={}",
                        liveId, externalId);
                return;
            }

            // 完课，需要更新备注
            String currentRemark = qwExternalContact.getRemark();
            String newRemark = buildCompletedCourseRemark(currentRemark);

            // 如果备注没有变化，跳过更新
            if (newRemark.equals(currentRemark)) {
                log.debug("备注无需更新: liveId={}, externalId={}, remark={}",
                        liveId, externalId, currentRemark);
                return;
            }

            // 调用企微API更新备注
            QwExternalContactRemarkParam remarkParam = new QwExternalContactRemarkParam();
            remarkParam.setUserid(qwExternalContact.getUserId());
            remarkParam.setExternal_userid(qwExternalContact.getExternalUserId());
            remarkParam.setRemark(newRemark);

            QwExternalContactRemarkResult remarkResult = qwApiService.externalcontactRemark(remarkParam, qwExternalContact.getCorpId());

            if (remarkResult != null && remarkResult.getErrcode() == 0) {
                // 更新成功，同步更新数据库
                QwExternalContact updateContact = new QwExternalContact();
                updateContact.setId(qwExternalContact.getId());
                updateContact.setRemark(newRemark);
                qwExternalContactMapper.updateQwExternalContact(updateContact);

                log.info("成功更新完课备注: liveId={}, externalId={}, oldRemark={}, newRemark={}",
                        liveId, externalId, currentRemark, newRemark);
            } else {
                String errorMsg = remarkResult != null ? remarkResult.getErrmsg() : "未知错误";
                log.error("更新完课备注失败: liveId={}, externalId={}, error={}",
                        liveId, externalId, errorMsg);
            }
        } catch (Exception e) {
            log.error("更新完课备注异常: liveId={}, externalId={}, error={}",
                    liveId, externalId, e.getMessage(), e);
        }
    }

    /**
     * 构建完课备注
     * 格式：MMdd完课-原有备注
     * 如果已有完课标记，则更新日期部分
     *
     * @param currentRemark 当前备注
     * @return 新的备注
     */
    private String buildCompletedCourseRemark(String currentRemark) {
        // 获取当前日期（MMdd格式）
        SimpleDateFormat dateFormat = new SimpleDateFormat("MMdd");
        String todayDate = dateFormat.format(new Date());
        String completedPrefix = todayDate + "完课";

        // 如果备注为空，直接返回完课标记
        if (StringUtils.isEmpty(currentRemark)) {
            return completedPrefix;
        }

        // 使用正则表达式匹配已有的完课标记格式：MMdd完课-xxx
        // 匹配模式：4位数字（MMdd）+ "完课" + "-" + 后续内容
        Pattern pattern = Pattern.compile("^(\\d{4})完课-(.+)$");
        Matcher matcher = pattern.matcher(currentRemark);

        if (matcher.matches()) {
            // 已有完课标记，更新日期部分
            String existingDate = matcher.group(1);
            String remainingRemark = matcher.group(2);

            // 如果日期相同，不需要更新
            if (todayDate.equals(existingDate)) {
                return currentRemark;
            }

            // 更新日期
            return completedPrefix + "-" + remainingRemark;
        } else {
            // 没有完课标记，添加完课标记到前面
            return completedPrefix + "-" + currentRemark;
        }
    }

    /**
     * 更新用户观看时长（心跳时调用）- 异步执行
     * @param liveId 直播间ID
     * @param userId 用户ID
     * @param liveFlag 直播标记
     * @param replayFlag 回放标记
     * @param duration 观看时长（秒）
     */
    @Override
    @Async
    public void updateWatchDuration(Long liveId, Long userId, Integer liveFlag, Integer replayFlag, Long duration) {
        try {

            LiveWatchUser liveWatchUser = baseMapper.selectByUniqueIndex(liveId, userId, liveFlag, replayFlag);

            if (liveWatchUser != null) {
                if (liveWatchUser.getOnlineSeconds() == null || duration > liveWatchUser.getOnlineSeconds()) {
                    liveWatchUser.setOnlineSeconds(duration);
                    liveWatchUser.setUpdateTime(DateUtils.getNowDate());
                    baseMapper.updateLiveWatchUser(liveWatchUser);
                    log.debug("更新观看时长成功: liveId={}, userId={}, liveFlag={}, replayFlag={}, duration={}",
                            liveId, userId, liveFlag, replayFlag, duration);
                }
            } else {
                log.warn("未找到观看记录，无法更新时长: liveId={}, userId={}, liveFlag={}, replayFlag={}",
                        liveId, userId, liveFlag, replayFlag);
            }
        } catch (Exception e) {
            log.error("更新观看时长失败: liveId={}, userId={}, liveFlag={}, replayFlag={}, duration={}",
                    liveId, userId, liveFlag, replayFlag, duration, e);
        }
    }

    /**
     * 获取用户在某直播间的总观看时长（直播 + 回放）
     * @param liveId 直播间ID
     * @param userId 用户ID
     * @return 总观看时长（秒）
     */
    @Override
    public Long getTotalWatchDuration(Long liveId, Long userId) {
        try {
            long totalDuration = 0L;

            // 1. 查询直播观看记录（liveFlag=1, replayFlag=0）
            LiveWatchUser liveRecord = baseMapper.selectByUniqueIndex(liveId, userId, 1, 0);
            if (liveRecord != null && liveRecord.getOnlineSeconds() != null) {
                totalDuration += liveRecord.getOnlineSeconds();
            }

            // 2. 查询回放观看记录（liveFlag=0, replayFlag=1）
            LiveWatchUser replayRecord = baseMapper.selectByUniqueIndex(liveId, userId, 0, 1);
            if (replayRecord != null && replayRecord.getOnlineSeconds() != null) {
                totalDuration += replayRecord.getOnlineSeconds();
            }

            log.debug("查询总观看时长: liveId={}, userId={}, liveDuration={}, replayDuration={}, total={}",
                    liveId, userId,
                    liveRecord != null ? liveRecord.getOnlineSeconds() : 0,
                    replayRecord != null ? replayRecord.getOnlineSeconds() : 0,
                    totalDuration);

            return totalDuration;
        } catch (Exception e) {
            log.error("查询总观看时长失败: liveId={}, userId={}", liveId, userId, e);
            return 0L;
        }
    }

}
