package shop.alien.store.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import shop.alien.entity.store.*;
import shop.alien.entity.store.vo.ClockInRecordValidateVo;
import shop.alien.entity.store.vo.StoreClockInDeleteFlagVo;
import shop.alien.entity.store.vo.MonthClockInItem;
import shop.alien.entity.store.vo.StoreClockInPageVo;
import shop.alien.entity.store.vo.StoreClockInVo;
import shop.alien.entity.store.vo.WebSocketVo;
import shop.alien.mapper.*;
import shop.alien.store.config.WebSocketProcess;
import shop.alien.store.service.CommonCommentService;
import shop.alien.store.service.StoreClockInService;
import shop.alien.store.service.StoreCommentService;
import shop.alien.util.type.LifeNoticeUtil;
import shop.alien.store.util.ai.AiContentModerationUtil;
import shop.alien.util.common.constant.CommentSourceTypeEnum;
import shop.alien.util.type.LifeFansIdentityQuery;
import shop.alien.util.type.PhoneTypeIdResult;
import shop.alien.util.type.LifeCollectIdentityQuery;
import shop.alien.util.type.TypeUtil;
import shop.alien.store.util.StoreLikeRecordIdentityHelper;
import shop.alien.store.util.StoreLikeRecordIdentityHelper.StoreLikerScope;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 店铺打卡 服务实现类
 * </p>
 *
 * @author ssk
 * @since 2025-05-15
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class StoreClockInServiceImpl extends ServiceImpl<StoreClockInMapper, StoreClockIn> implements StoreClockInService {

    // 1. 自定义图片审核线程池（全局复用，避免频繁创建线程）
    private ExecutorService imgAuditExecutor;

    private final StoreClockInMapper storeClockInMapper;

    private final LifeUserMapper lifeUserMapper;

    private final LifeFansMapper lifeFansMapper;

    private final LifeLikeRecordMapper lifeLikeRecordMapper;

    private final StoreLikeRecordIdentityHelper lifeLikeIdentityHelper;

    private final StoreCommentService storeCommentService;

    private final LifeCollectMapper lifeCollectMapper;

    private final StoreInfoMapper storeInfoMapper;

     private final StoreImgMapper storeImgMapper;

    private final AiContentModerationUtil aiContentModerationUtil;

    private final LifeNoticeMapper lifeNoticeMapper;

    private final LifeNoticeUtil lifeNoticeUtil;

    private final CommonCommentService commonCommentService;

    private final CommonCommentMapper commonCommentMapper;

    private final WebSocketProcess webSocketProcess;

    private final TypeUtil typeUtil;

    // 初始化线程池
    @PostConstruct
    public void initExecutor() {
        // 核心参数根据业务调整，IO密集型任务线程数可设为CPU核心数*2~4
        int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
        int maxPoolSize = Runtime.getRuntime().availableProcessors() * 4;
        imgAuditExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(500), // 任务队列，避免无界队列溢出
                new ThreadFactory() {
                    private int count = 0;
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread t = new Thread(r);
                        t.setName("img-audit-" + count++); // 自定义线程名，便于排查
                        return t;
                    }
                },
                // 任务拒绝策略：记录日志+调用者线程兜底（避免任务丢失）
                new ThreadPoolExecutor.CallerRunsPolicy() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                        log.error("图片审核任务被拒绝，队列已满！当前队列大小：{}，活跃线程数：{}",
                                e.getQueue().size(), e.getActiveCount());
                        super.rejectedExecution(r, e);
                    }
                }
        );

    }

    // 优雅关闭线程池
    @PreDestroy
    public void destroyExecutor() {
        if (Objects.nonNull(imgAuditExecutor)) {
            imgAuditExecutor.shutdown();
            try {
                if (!imgAuditExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                    imgAuditExecutor.shutdownNow();
                }
            } catch (InterruptedException e) {
                imgAuditExecutor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }

    @Override
    public StoreClockIn addStoreClockIn(StoreClockIn storeClockIn) {
        LifeUser user = lifeUserMapper.selectById(storeClockIn.getUserId());
        String phoneId = "user_" + user.getUserPhone();
        storeClockIn.setPhoneId(phoneId);
        PhoneTypeIdResult typeId = typeUtil.resolveTypeAndId(phoneId);
        if (typeId != null) {
            storeClockIn.setPhoneUserType(typeId.getType());
            storeClockIn.setPhoneRefId(typeId.getId());
        }
        storeClockInMapper.insert(storeClockIn);
        return storeClockIn;
    }

    @Override
    public IPage<StoreClockInVo> getStoreClockInList(Integer userId, int page, int size, String phoneId, int mySelf, Integer storeId, Integer year, Integer month, String city) {
        IPage<StoreClockIn> iPage = new Page<>(page, size);
        String cityFilter = normalizeCityFilter(city);
        
        // 优化：提前查询所有需要的数据，避免在循环中查询
        // 查询我的点赞 - 使用Set提高查找效率
        StoreLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
        LambdaQueryWrapper<LifeLikeRecord> likeWrapper = new LambdaQueryWrapper<>();
        likeWrapper.eq(LifeLikeRecord::getType, "5");
        lifeLikeIdentityHelper.applyLikerFilter(likeWrapper, likerScope);
        List<LifeLikeRecord> lifeLikeList = lifeLikeRecordMapper.selectList(likeWrapper);
        Set<String> likeSet = lifeLikeList.stream()
                .map(LifeLikeRecord::getHuifuId)
                .collect(Collectors.toSet());

        // 优化：使用Objects.nonNull判断storeId
        if (Objects.nonNull(storeId)) {
            IPage<StoreClockInVo> storeClockInIPage1 = storeClockInMapper.getStoreClockInList(iPage, new QueryWrapper<StoreClockIn>()
                    .eq("clock.store_id", storeId)
                    .eq("clock.delete_flag", 0)
                    .and(wrapper1 -> wrapper1.isNull("clock.check_flag").or().ne("clock.check_flag", 3))
                    .eq("user.delete_flag", 0)
                    .eq("store.delete_flag", 0)
                    .apply(Objects.nonNull(year), "YEAR(clock.created_time) = {0}", year)
                    .apply(Objects.nonNull(month), "MONTH(clock.created_time) = {0}", month)
                    .eq(StringUtils.isNotBlank(cityFilter), "store.administrative_region_city_name", cityFilter)
                    .isNotNull(0 == mySelf, "clock.img_url")
                    .and(wrapper1 ->
                            wrapper1.eq("clock.permission", 1)
                                    .or(wSub ->
                                            wSub.eq("clock.permission", 2)
                                                    .eq("clock.user_id", userId)
                                    ))
                    .orderByDesc("created_time"));
            
            List<StoreClockInVo> records = storeClockInIPage1.getRecords();
            if (!records.isEmpty()) {
                // 批量查询评论数量
                List<Integer> clockInIds = records.stream()
                        .map(StoreClockInVo::getId)
                        .collect(Collectors.toList());
                Map<Integer, Integer> commentCountMap = batchGetCommentCountMap(clockInIds);
                
                records.forEach(vo -> {
                    vo.setIsLike(likeSet.contains(String.valueOf(vo.getId())) ? "1" : "0");
                    vo.setCommentCount(commentCountMap.getOrDefault(vo.getId(), 0));
                    setStoreTypeNew(vo);
                });
            }
            return attachClockInEchoFields(storeClockInIPage1, userId, year, month, cityFilter);
        }
        
        QueryWrapper<StoreClockIn> wrapper = new QueryWrapper<>();
        wrapper.eq(1 == mySelf, "clock.user_id", userId);
        wrapper.isNotNull(0 == mySelf, "clock.img_url");
        wrapper.eq("clock.delete_flag", 0);
        wrapper.and(wrapper1 -> wrapper1.isNull("clock.check_flag").or().ne("clock.check_flag", 3));
        wrapper.eq("user.delete_flag", 0);
        wrapper.eq("store.delete_flag", 0);
        wrapper.apply(Objects.nonNull(year), "YEAR(clock.created_time) = {0}", year);
        wrapper.apply(Objects.nonNull(month), "MONTH(clock.created_time) = {0}", month);
        wrapper.eq(StringUtils.isNotBlank(cityFilter), "store.administrative_region_city_name", cityFilter);
        wrapper.and(wrapper1 ->
                wrapper1.eq("clock.permission", 1)
                        .or(wSub ->
                                wSub.eq("clock.permission", 2)
                                        .eq("clock.user_id", userId)
                        )
        );
        wrapper.orderByDesc("clock.created_time");

        IPage<StoreClockInVo> storeClockInIPage = storeClockInMapper.getStoreClockInList(iPage, wrapper);
        List<StoreClockInVo> records = storeClockInIPage.getRecords();
        
        if (records.isEmpty()) {
            return attachClockInEchoFields(storeClockInIPage, userId, year, month, cityFilter);
        }

        // 优化：批量查询所有需要的数据
        // 查询我的关注 - 使用Set提高查找效率
        LifeFansIdentityQuery.Scope viewerScope = LifeFansIdentityQuery.resolve(phoneId, typeUtil);
        LambdaQueryWrapper<LifeFans> lifeFansWrapper = new LambdaQueryWrapper<>();
        lifeFansWrapper.eq(LifeFans::getDeleteFlag, 0);
        LifeFansIdentityQuery.applyFansSide(lifeFansWrapper, viewerScope);
        List<LifeFans> lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
        Set<String> followSet = lifeFansList.stream()
                .map(LifeFans::getFollowedId)
                .collect(Collectors.toSet());

        // 查询我的粉丝 - 使用Set提高查找效率
        lifeFansWrapper = new LambdaQueryWrapper<>();
        lifeFansWrapper.eq(LifeFans::getDeleteFlag, 0);
        LifeFansIdentityQuery.applyFollowedSide(lifeFansWrapper, viewerScope);
        lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
        Set<String> fansSet = lifeFansList.stream()
                .map(LifeFans::getFansId)
                .collect(Collectors.toSet());

        // 查询我的收藏 - 使用Set提高查找效率
        LambdaQueryWrapper<LifeCollect> collectWrapper = new LambdaQueryWrapper<>();
        LifeCollectIdentityQuery.applyUserSide(collectWrapper, LifeCollectIdentityQuery.fromLifeUserId(userId));
        List<LifeCollect> lifeCollectList = lifeCollectMapper.selectList(collectWrapper);
        Set<String> collectSet = lifeCollectList.stream()
                .map(LifeCollect::getStoreId)
                .collect(Collectors.toSet());

        // 优化：批量查询评论数量，避免N+1问题
        List<Integer> clockInIds = records.stream()
                .map(StoreClockInVo::getId)
                .collect(Collectors.toList());
        Map<Integer, Integer> commentCountMap = batchGetCommentCountMap(clockInIds);

        // 优化：使用Set进行O(1)查找，而不是List的O(n)查找
        records.forEach(vo -> {
            vo.setIsFollowThis(followSet.contains(vo.getPhoneId()) ? "1" : "0");
            vo.setIsFollowMe(fansSet.contains(vo.getPhoneId()) ? "1" : "0");
            vo.setIsLike(likeSet.contains(String.valueOf(vo.getId())) ? "1" : "0");
            vo.setIsCollect(collectSet.contains(String.valueOf(vo.getStoreId())) ? "1" : "0");
            vo.setCommentCount(commentCountMap.getOrDefault(vo.getId(), 0));
            setStoreTypeNew(vo);
        });

        return attachClockInEchoFields(storeClockInIPage, userId, year, month, cityFilter);
    }

    private IPage<StoreClockInVo> attachClockInEchoFields(IPage<StoreClockInVo> pageData, Integer userId, Integer year, Integer month, String cityFilter) {
        StoreClockInPageVo resultVo = new StoreClockInPageVo();
        resultVo.setCurrent(pageData.getCurrent());
        resultVo.setSize(pageData.getSize());
        resultVo.setTotal(pageData.getTotal());
        resultVo.setRecords(pageData.getRecords());
        List<String> cities = storeClockInMapper.listDistinctClockInCityNamesByYear(userId, year, month);
        resultVo.setClockInCities(cities == null ? Collections.emptyList() : cities);
        Integer placeCount = storeClockInMapper.countDistinctClockInStoreByCondition(userId, year, month, cityFilter);
        resultVo.setPlaceCount(placeCount == null ? 0 : placeCount);
        return resultVo;
    }

    private String normalizeCityFilter(String city) {
        if (StringUtils.isBlank(city)) {
            return null;
        }
        String trimCity = city.trim();
        return "全部城市".equals(trimCity) ? null : trimCity;
    }

    /**
     * 批量查询评论数量
     * 
     * @param clockInIds 打卡记录ID列表
     * @return 打卡记录ID -> 评论数量的映射
     */
    private Map<Integer, Integer> batchGetCommentCountMap(List<Integer> clockInIds) {
        if (clockInIds == null || clockInIds.isEmpty()) {
            return Collections.emptyMap();
        }
        
        try {
            List<shop.alien.entity.store.vo.CommonCommentVo> commentCountList = 
                    commonCommentMapper.batchGetCommentCount(
                            CommentSourceTypeEnum.CLOCK_IN_COMMENT.getType(), 
                            clockInIds
                    );
            
            return commentCountList.stream()
                    .filter(item -> item.getSourceId() != null && item.getCommentCount() != null)
                    .collect(Collectors.toMap(
                            item -> item.getSourceId().intValue(),
                            item -> item.getCommentCount(),
                            (existing, replacement) -> existing
                    ));
        } catch (Exception e) {
            log.error("批量查询评论数量失败", e);
            return Collections.emptyMap();
        }
    }

    /**
     * 设置店铺类型
     * 
     * @param vo 打卡记录VO
     */
    private void setStoreTypeNew(StoreClockInVo vo) {
        String businessSectionName = vo.getBusinessSectionName();
        if (businessSectionName == null) {
            return;
        }
        
        if (Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗").contains(businessSectionName)) {
            vo.setStoreTypeNew(1);
        } else if (Arrays.asList("丽人美发", "运动健身").contains(businessSectionName)) {
            vo.setStoreTypeNew(2);
        } else if (Arrays.asList("特色美食").contains(businessSectionName)) {
            vo.setStoreTypeNew(3);
        }
    }

    @Override
    public int deleteClockIn(Integer id) {
        return storeClockInMapper.deleteById(id);
    }

    @Override
    public int setPermission(int userId, Integer id, int permission) {
        LambdaUpdateWrapper<StoreClockIn> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(StoreClockIn::getPermission, permission);
        wrapper.eq(StoreClockIn::getUserId, userId);
        wrapper.eq(null != id, StoreClockIn::getId, id);
        return storeClockInMapper.update(null, wrapper);
    }

    @Override
    public int setImgAndAiContent(int id, String img,String aiContent) {
        // 1. 先更新打卡状态为"审核中"，然后返回
        LambdaUpdateWrapper<StoreClockIn> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(StoreClockIn::getImgUrl, img);
        wrapper.set(StoreClockIn::getMaybeAiContent, aiContent);
        wrapper.set(StoreClockIn::getCheckFlag, 1); // 1-审核中
        wrapper.eq(StoreClockIn::getId, id);
        int updateResult = storeClockInMapper.update(null, wrapper);
        
        // 2. 异步：配图由前端直连 AI；服务端仅审核 maybeAiContent 文字（无文字则视为通过）
        StoreClockIn storeClockIn = storeClockInMapper.selectById(id);
        if (storeClockIn == null) {
            log.warn("打卡记录不存在，id={}", id);
            return updateResult;
        }
        
        LifeUser lifeUser = lifeUserMapper.selectById(storeClockIn.getUserId());
        if (lifeUser == null) {
            log.warn("用户不存在，userId={}", storeClockIn.getUserId());
            return updateResult;
        }
        
        final String phoneId = "user_" + lifeUser.getUserPhone();
        final Integer clockInId = id;
        final Integer userId = storeClockIn.getUserId();
        
        // 异步执行：仅文本走 AiContentModerationUtil（图片由前端审，不再传 imgList）
        CompletableFuture.runAsync(() -> {
            AiContentModerationUtil.AuditResult imgAuditResult = null;
            try {
                String textForAudit = StringUtils.isNotBlank(aiContent) ? aiContent.trim() : null;
                imgAuditResult = CompletableFuture.supplyAsync(
                        () -> aiContentModerationUtil.auditContent(textForAudit, null),
                        imgAuditExecutor
                ).get();

                // 3. 审核后按照注释修改状态
                LambdaUpdateWrapper<StoreClockIn> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(StoreClockIn::getId, clockInId);
                
                if (imgAuditResult != null && imgAuditResult.isPassed()) {
                    // 配文文字审核通过（配图由前端审）
                    updateWrapper.set(StoreClockIn::getCheckFlag, 2); // 2-审核通过
                    updateWrapper.set(StoreClockIn::getReason, null); // 清除拒绝原因
                    storeClockInMapper.update(null, updateWrapper);
                    log.info("打卡配文审核通过，打卡ID：{}", clockInId);
                } else {
                    String rejectReason = (imgAuditResult != null && StringUtils.isNotEmpty(imgAuditResult.getFailureReason()))
                            ? imgAuditResult.getFailureReason()
                            : "文字内容不符合规范";
                    updateWrapper.set(StoreClockIn::getCheckFlag, 3); // 3-审核拒绝
                    updateWrapper.set(StoreClockIn::getReason, rejectReason); // 记录拒绝原因
                    storeClockInMapper.update(null, updateWrapper);
                    log.warn("打卡配文审核拒绝，打卡ID：{}，原因：{}", clockInId, rejectReason);
                    
                    // 通知用户打卡审核不通过
                    sendAuditRejectNotification(clockInId, userId, phoneId, rejectReason);
                }
            } catch (Exception e) {
                log.error("打卡配文审核异常，打卡ID：{}", clockInId, e);
                // 审核异常时，保持审核中状态，记录错误原因
                LambdaUpdateWrapper<StoreClockIn> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(StoreClockIn::getId, clockInId);
                updateWrapper.set(StoreClockIn::getCheckFlag, 1); // 保持审核中状态
                updateWrapper.set(StoreClockIn::getReason, "审核异常，请稍后重试");
                storeClockInMapper.update(null, updateWrapper);
            }
        }, imgAuditExecutor);
        
        return updateResult;
    }

    /**
     * 发送审核拒绝通知
     *
     * @param clockInId 打卡ID
     * @param userId    用户ID
     * @param phoneId   用户phoneId
     * @param reason    拒绝原因
     */
    private void sendAuditRejectNotification(Integer clockInId, Integer userId, String phoneId, String reason) {
        try {
            LifeNotice lifeNotice = new LifeNotice();
            lifeNotice.setSenderId("system");
            lifeNotice.setBusinessId(clockInId);

            lifeNotice.setReceiverId(phoneId);

            lifeNotice.setTitle("打卡审核通知");
            lifeNotice.setNoticeType(1); // 1-系统通知
            lifeNotice.setIsRead(0);

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("message", "您的打卡审核未通过，原因：" + reason);
            jsonObject.put("clockInId", clockInId);
            jsonObject.put("status", "rejected");
            lifeNotice.setContext(jsonObject.toJSONString());

            // 保存通知
            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
            lifeNoticeMapper.insert(lifeNotice);

            // 发送WebSocket消息
            WebSocketVo webSocketVo = new WebSocketVo();
            webSocketVo.setSenderId("system");
            webSocketVo.setReceiverId(phoneId);
            webSocketVo.setCategory("notice");
            webSocketVo.setNoticeType("1");
            webSocketVo.setIsRead(0);
            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
            webSocketProcess.sendMessage(phoneId, JSONObject.from(webSocketVo).toJSONString());

            log.info("打卡审核拒绝通知发送成功，打卡ID：{}，接收人：{}", clockInId, phoneId);
        } catch (Exception e) {
            log.error("发送打卡审核拒绝通知失败，打卡ID：{}，异常信息：{}", clockInId, e.getMessage(), e);
        }
    }

    /**
     * 获取门店打卡次数
     *
     * @return list
     */
    @Override
    public List<Map<Integer, Integer>> getStoreClockInCount() {
        return storeClockInMapper.getStoreClockInCount();
    }

    /**
     * 获取所有店铺打卡次数（有图片并且设置为可见的）
     *
     * @return map
     */
    @Override
    public List<Map<Integer, Integer>> getStoreClockInWithCanLookCount() {
        return storeClockInMapper.getStoreClockInWithCanLookCount();
    }

    @Override
    public StoreClockInVo getStoreClockInById(Integer id, Integer userId) {
        StoreClockIn storeClockIn = storeClockInMapper.selectById(id);
        if (null == storeClockIn) {
            return null;
        }
        StoreClockInVo storeClockInVo = BeanUtil.copyProperties(storeClockIn, StoreClockInVo.class);
        storeClockInVo.setIsFollowMe("0");
        storeClockInVo.setIsFollowThis("0");
        storeClockInVo.setIsLike("0");

        StoreInfo storeInfo = storeInfoMapper.selectById(storeClockIn.getStoreId());
        if (storeInfo == null) {
            throw new IllegalArgumentException("当前店铺已删除");
        }
        // 店铺名称
        storeClockInVo.setStoreName(storeInfo.getStoreName());
        storeClockInVo.setStoreBlurb(storeInfo.getStoreBlurb());
        // 店铺头像
        LambdaQueryWrapper<StoreImg> eq = new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 1).eq(StoreImg::getStoreId, storeInfo.getId());
        StoreImg storeImg = storeImgMapper.selectOne(eq);
        storeClockInVo.setEntranceImage(storeImg != null ? storeImg.getImgUrl() : "");
        // 店铺评分
        storeClockInVo.setScore(storeInfo.getScoreAvg());
        // 设置店铺大类
        String businessSectionName = storeInfo.getBusinessSectionName();
        if(Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗").contains(businessSectionName)){
            storeClockInVo.setStoreTypeNew(1);
        } else if (Arrays.asList("丽人美发", "运动健身").contains(businessSectionName)){
            storeClockInVo.setStoreTypeNew(2);
        } else if (Arrays.asList("特色美食").contains(businessSectionName)){
            storeClockInVo.setStoreTypeNew(3);
        }
        // 最小分类
        storeClockInVo.setBusinessClassifyName(storeInfo.getBusinessClassifyName());
        // 地址
        storeClockInVo.setStoreAddress(storeInfo.getStoreAddress());
        // 是否收藏 - 使用当前登录用户的ID查询收藏
        storeClockInVo.setIsCollect("0");
        if (userId != null) {
            LambdaQueryWrapper<LifeCollect> collectWrapper = new LambdaQueryWrapper<>();
            LifeCollectIdentityQuery.applyUserSide(collectWrapper, LifeCollectIdentityQuery.fromLifeUserId(userId));
            List<LifeCollect> lifeCollectList = lifeCollectMapper.selectList(collectWrapper);
            List<String> collectList = lifeCollectList.stream().map(LifeCollect::getStoreId).collect(Collectors.toList());
            if (collectList.contains(storeClockInVo.getStoreId().toString())){
                storeClockInVo.setIsCollect("1");
            }

            StoreLikerScope viewerLikerScope = lifeLikeIdentityHelper.fromLifeUserId(userId);
            LambdaQueryWrapper<LifeLikeRecord> likeQw = new LambdaQueryWrapper<>();
            likeQw.eq(LifeLikeRecord::getType, "5")
                    .eq(LifeLikeRecord::getHuifuId, String.valueOf(storeClockIn.getId()));
            lifeLikeIdentityHelper.applyLikerFilter(likeQw, viewerLikerScope);
            storeClockInVo.setIsLike(lifeLikeRecordMapper.selectCount(likeQw) > 0 ? "1" : "0");

            LifeUser viewer = lifeUserMapper.selectById(userId);
            String viewerPhoneId = viewer != null && StringUtils.isNotBlank(viewer.getUserPhone())
                    ? "user_" + viewer.getUserPhone()
                    : null;
            String authorPhoneId = storeClockInVo.getPhoneId();
            if (viewerPhoneId != null && StringUtils.isNotBlank(authorPhoneId)) {
                LifeFansIdentityQuery.Scope viewerScope = LifeFansIdentityQuery.resolve(viewerPhoneId, typeUtil);
                LifeFansIdentityQuery.Scope authorScope = LifeFansIdentityQuery.resolve(authorPhoneId, typeUtil);
                LambdaQueryWrapper<LifeFans> iFollowAuthor = new LambdaQueryWrapper<>();
                iFollowAuthor.eq(LifeFans::getDeleteFlag, 0);
                LifeFansIdentityQuery.applyPair(iFollowAuthor, viewerScope, authorScope);
                storeClockInVo.setIsFollowThis(lifeFansMapper.selectCount(iFollowAuthor) > 0 ? "1" : "0");

                LambdaQueryWrapper<LifeFans> authorFollowsMe = new LambdaQueryWrapper<>();
                authorFollowsMe.eq(LifeFans::getDeleteFlag, 0);
                LifeFansIdentityQuery.applyPair(authorFollowsMe, authorScope, viewerScope);
                storeClockInVo.setIsFollowMe(lifeFansMapper.selectCount(authorFollowsMe) > 0 ? "1" : "0");
            }
        }

        if(storeInfo.getBusinessSection() != null){
            storeClockInVo.setBusinessSection(storeInfo.getBusinessSection().toString());
        }
        storeClockInVo.setBusinessSectionName(storeInfo.getBusinessSectionName());
        return storeClockInVo;
    }

    @Override
    public ClockInRecordValidateVo getClockInRecordValidate(Integer userId, Integer year, String city) {
        ClockInRecordValidateVo vo = new ClockInRecordValidateVo();
        vo.setYears(storeClockInMapper.listDistinctClockInYears(userId));
        vo.setCities(storeClockInMapper.listDistinctClockInCityNames(userId));
        if (year == null) {
            vo.setMonths(Collections.emptyList());
            return vo;
        }
        String cityFilter = StringUtils.isBlank(city) ? null : city.trim();
        List<Integer> monthsWithRecord = storeClockInMapper.listClockInMonthsByYear(userId, year, cityFilter);
        Set<Integer> monthSet = new HashSet<>(monthsWithRecord != null ? monthsWithRecord : Collections.emptyList());
        List<MonthClockInItem> monthItems = new ArrayList<>(12);
        for (int m = 1; m <= 12; m++) {
            MonthClockInItem item = new MonthClockInItem();
            item.setMonth(m);
            item.setHasClockIn(monthSet.contains(m));
            monthItems.add(item);
        }
        vo.setMonths(monthItems);
        return vo;
    }

    @Override
    public StoreClockInDeleteFlagVo getDeleteFlagById(Integer id) {
        if (id == null) {
            return null;
        }
        return storeClockInMapper.selectDeleteFlagAndBusinessStatusById(id);
    }
}
