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.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableList;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import shop.alien.entity.result.R;
import shop.alien.entity.store.*;
import shop.alien.entity.store.vo.*;
import shop.alien.entity.storePlatform.StoreOperationalActivity;
import shop.alien.mapper.*;
import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
import shop.alien.store.config.WebSocketProcess;
import shop.alien.store.service.CommonCommentService;
import shop.alien.store.service.CommonRatingService;
import shop.alien.store.service.LifeDiscountCouponStoreFriendService;
import shop.alien.store.service.OrderReviewService;
import shop.alien.store.util.CommonConstant;
import shop.alien.store.util.StoreLikeRecordIdentityHelper;
import shop.alien.store.util.StoreLikeRecordIdentityHelper.StoreLikerScope;
import shop.alien.store.util.ai.AiContentModerationUtil;
import shop.alien.store.util.ai.AiVideoModerationUtil;
import shop.alien.store.util.ai.ReceiptAuditUtil;
import shop.alien.util.common.Constants;
import shop.alien.util.common.DateUtils;
import shop.alien.util.common.constant.CommentSourceTypeEnum;
import shop.alien.util.common.constant.RatingBusinessTypeEnum;
import shop.alien.util.common.safe.TextModerationUtil;
import shop.alien.util.common.safe.TextReviewServiceEnum;
import shop.alien.util.type.LifeCollectIdentityQuery;
import shop.alien.util.type.LifeFansIdentityQuery;
import shop.alien.util.type.LifeNoticeUtil;
import shop.alien.util.type.TypeUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 评价表 服务实现类
 *
 * @author system
 * @since 2025-01-XX
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, CommonRating> implements CommonRatingService {

    @Autowired
    private TextModerationUtil textModerationUtil;

    private final  CommonRatingMapper commonRatingMapper;
    private final StoreInfoMapper storeInfoMapper;
    private final StoreUserMapper storeUserMapper;
    private final LifeNoticeMapper lifeNoticeMapper;
    private final LifeNoticeUtil lifeNoticeUtil;
    private final WebSocketProcess webSocketProcess;
    private final LifeUserMapper lifeUserMapper;
    private final CommonCommentMapper commonCommentMapper;
    private final LifeLikeRecordMapper lifeLikeRecordMapper;
    private final StoreLikeRecordIdentityHelper lifeLikeIdentityHelper;
    private final LifeCollectMapper lifeCollectMapper;
    private final LifeFansMapper lifeFansMapper;
    private final TypeUtil typeUtil;
    private final TagsSynonymMapper tagsSynonymMapper;
    private final CommonCommentService commonCommentService;
    private final StoreCommentAppealMapper storeCommentAppealMapper;
    private final AiContentModerationUtil aiContentModerationUtil;
    @Qualifier("commonVideoTaskExecutor")
    private final ExecutorService commonVideoTaskExecutor;
    private final AiVideoModerationUtil aiVideoModerationUtil;
    private final LifeDiscountCouponStoreFriendService lifeDiscountCouponStoreFriendService;
    private final StoreOperationalActivityMapper storeOperationalActivityMapper;
    private final ReceiptAuditUtil receiptAuditUtil;
    private final StoreClockInMapper storeClockInMapper;
    private final OrderReviewService orderReviewService;

    public static final List<String> SERVICES_LIST = ImmutableList.of(
            TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService(),
            TextReviewServiceEnum.LLM_QUERY_MODERATION.getService()
    );


    /**
     *  新增评价ai审核接口通过后将券发到卡包中
     * @param commonRating 评价信息
     * @return
     */
    @Override
    public Integer saveCommonRating(CommonRating commonRating) {
        // 1. 先进行AI审核，审核通过后再保存（防止绕过审核）
        try {
            // 参数校验：必填字段不能为空
            if (commonRating.getBusinessId() == null) {
                log.error("保存评价失败：businessId不能为空");
                return 1;
            }
            if (commonRating.getBusinessType() == null) {
                log.error("保存评价失败：businessType不能为空");
                return 1;
            }
            if (commonRating.getUserId() == null) {
                log.error("保存评价失败：userId不能为空");
                return 1;
            }
            
            // 强制设置审核状态为待审核，防止用户绕过审核
            commonRating.setAuditStatus(0);
            
            // 手动存评分1,2,3
            if (StringUtils.isNotEmpty(commonRating.getOtherScore())) {
                JSONObject parse = JSONObject.parse(commonRating.getOtherScore());
                commonRating.setScoreOne(parse.getDouble("scoreOne"));
                commonRating.setScoreTwo(parse.getDouble("scoreTwo"));
                commonRating.setScoreThree(parse.getDouble("scoreThree"));
            }
            
            // 2. 处理图片URL列表
            String imageUrlsStr = commonRating.getImageUrls();
            List<String> imageUrlList = new ArrayList<>();
            if (StringUtils.isNotEmpty(imageUrlsStr)) {
                imageUrlList = Arrays.asList(imageUrlsStr.split(","));
            }
            
            // 一次遍历完成分类，避免多次流式处理
            Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(imageUrlList);
            List<String> imageUrls = urlCategoryMap.get("image");
            List<String> videoUrls = urlCategoryMap.get("video");
            
            // 3. 内容审核：评价正文走服务端 moderate；配图/视频由前端直连 AI（util 忽略图，视频审核 util 恒通过）
            AiContentModerationUtil.AuditResult contentAuditResult = new AiContentModerationUtil.AuditResult(true, "");
            if (StringUtils.isNotEmpty(commonRating.getContent()) || !imageUrls.isEmpty()) {
                contentAuditResult = aiContentModerationUtil.auditContent(commonRating.getContent(), null);
            }
            
            // 内容审核不通过，直接判定为审核不通过
            if (!contentAuditResult.isPassed()) {
                log.warn("评论审核失败：内容审核不通过，不保存评论。原因：{}", contentAuditResult.getFailureReason());
                return 2; // 返回2表示内容审核不通过
            }
            
            log.info("评论审核：内容审核通过，继续检查其他条件");
            
            // 4. 内容审核通过后，检查是否满足以下条件之一（满足任意一个即可）：
            // 条件1：当前用户必须在这个店铺打过卡
            // 条件2：当前用户上传的图片中包含该店铺的小票（调用AI审核）
            // 注意：小票审核不通过时，允许创建评论，但审核状态设为不通过并记录审核原因（打卡仅要求存在记录，不校验审核状态）
            
            boolean condition1Passed = false; // 条件1：打过卡
            boolean condition2Passed = false; // 条件2：有小票
            String auditReason = null; // 审核原因
            
            // 检查条件1：用户是否在该店铺有过打卡（未删除即可，不区分打卡内容审核是否通过）
            try {
                LambdaQueryWrapper<StoreClockIn> clockInWrapper = new LambdaQueryWrapper<>();
                clockInWrapper.eq(StoreClockIn::getUserId, commonRating.getUserId())
                        .eq(StoreClockIn::getStoreId, commonRating.getBusinessId())
                        .eq(StoreClockIn::getDeleteFlag, 0)
                        .last("LIMIT 1");
                Integer clockInCount = storeClockInMapper.selectCount(clockInWrapper);
                condition1Passed = clockInCount != null && clockInCount > 0;
                if (condition1Passed) {
                    log.info("评论审核条件1通过：用户在该店铺有打卡记录，userId={}, storeId={}", 
                            commonRating.getUserId(), commonRating.getBusinessId());
                } else {
                    auditReason = "用户未在该店铺打过卡";
                    log.info("评论审核条件1未通过：用户未在该店铺有打卡记录，userId={}, storeId={}", 
                            commonRating.getUserId(), commonRating.getBusinessId());
                }
            } catch (Exception e) {
                log.warn("检查用户打卡记录失败：userId={}, storeId={}", 
                        commonRating.getUserId(), commonRating.getBusinessId(), e);
                // 检查异常时，也记录为未通过
                if (auditReason == null) {
                    auditReason = "检查用户打卡记录失败";
                }
            }
            
            // 检查条件2：图片中包含该店铺的小票（如果有图片）
            // 注意：无论打卡是否通过，只要有图片就进行小票审核
            if (!imageUrls.isEmpty()) {
                try {
                    // 获取店铺名称用于小票审核
                    String storeName = null;
                    StoreInfo storeInfo = storeInfoMapper.selectById(commonRating.getBusinessId());
                    if (storeInfo != null && StringUtils.isNotEmpty(storeInfo.getStoreName())) {
                        storeName = storeInfo.getStoreName();
                    }
                    
                    // 调用小票审核接口
                    ReceiptAuditUtil.ReceiptAuditResult receiptAuditResult = receiptAuditUtil.auditReceipt(imageUrls, storeName);
                    condition2Passed = receiptAuditResult.isPassed();
                    
                    if (condition2Passed) {
                        log.info("评论审核条件2通过：图片中包含该店铺的小票，isValidProof={}, proofType={}", 
                                receiptAuditResult.isValidProof(), receiptAuditResult.getProofType());
                    } else {
                        // 小票审核不通过，记录审核原因
                        String receiptReason = receiptAuditResult.getFailureReason();
                        if (StringUtils.isNotEmpty(receiptReason)) {
                            auditReason = "小票审核不通过：" + receiptReason;
                        } else {
                            auditReason = "小票审核不通过：上传的图片不是有效的消费凭证（小票、支付记录等）";
                        }
                        log.info("评论审核条件2未通过：小票审核失败，reason={}", receiptReason);
                    }
                } catch (Exception e) {
                    log.warn("小票审核异常：userId={}, storeId={}", 
                            commonRating.getUserId(), commonRating.getBusinessId(), e);
                    // 小票审核异常时，也记录为未通过
                    if (auditReason == null) {
                        auditReason = "小票审核异常";
                    }
                    condition2Passed = false; // 异常时视为不通过
                }
            }
            
            // 判断审核结果：如果两个条件都不满足，判定为审核不通过；至少满足一个条件，算审核通过
            boolean bothConditionFailed = !condition1Passed && !condition2Passed;
            if (bothConditionFailed) {
                // 两个条件都不满足，判定为审核不通过
                if (auditReason == null) {
                    if (imageUrls.isEmpty()) {
                        auditReason = "不满足审核条件：用户未在该店铺打过卡";
                    } else {
                        auditReason = "不满足审核条件：1) 用户未在该店铺打过卡；2) 上传的图片不包含该店铺的小票";
                    }
                }
                // 设置审核状态为不通过（2-驳回）
                commonRating.setAuditStatus(2);
                commonRating.setAuditReason(auditReason);
                log.warn("评论审核不通过：内容审核通过，但两个条件都不满足。条件1（打过卡）={}, 条件2（有小票）={}, 审核原因={}", 
                        condition1Passed, condition2Passed, auditReason);
            } else {
                // 至少满足一个条件，审核通过，审核状态保持为待审核（0），等待视频审核
                log.info("评论审核通过：内容审核通过，且满足条件1（打过卡）={} 或 条件2（有小票）={}", 
                        condition1Passed, condition2Passed);
            }
            
            // 5. 文本/图片审核通过，保存评论（状态为待审核，等待视频审核）
            int i = this.save(commonRating) ? 0 : 1;
            if (i != 0) {
                log.error("保存评价失败");
                return 1;
            }
            
            // 6. 打卡与小票均未通过（含未上传有效消费凭证）时发送通知：评价审核失败通知 + 引导文案
            if (commonRating.getAuditStatus() != null && commonRating.getAuditStatus() == 2 && StringUtils.isNotEmpty(auditReason) && bothConditionFailed) {
                try {
                    // 获取用户信息
                    LifeUser lifeUser = lifeUserMapper.selectById(commonRating.getUserId());
                    if (lifeUser != null && StringUtils.isNotEmpty(lifeUser.getUserPhone())) {
                        String receiverId = "user_" + lifeUser.getUserPhone();

                        String storeName = "该";
                        StoreInfo storeForNotice = storeInfoMapper.selectById(commonRating.getBusinessId());
                        if (storeForNotice != null && StringUtils.isNotEmpty(storeForNotice.getStoreName())) {
                            storeName = storeForNotice.getStoreName();
                        }
                        String noticeTitle = "评价审核通知";
                        String fullMessage = "您对" + storeName + "店铺的评价未通过审核。失败原因：" + auditReason
                                + "。您可通过成功发布一条打卡内容，或在评价中上传消费记录两种方式参与店铺评价，审核成功后可在\"我的评价及店铺评价\"中查看。";

                        // 构建通知内容
                        JSONObject contextJson = new JSONObject();
                        contextJson.put("ratingId", commonRating.getId());
                        contextJson.put("storeId", commonRating.getBusinessId());
                        contextJson.put("noticeName", noticeTitle);
                        contextJson.put("message", fullMessage);
                        contextJson.put("auditReason", auditReason);
                        
                        // 保存通知到数据库
                        LifeNotice lifeNotice = new LifeNotice();
                        lifeNotice.setSenderId("system");
                        lifeNotice.setReceiverId(receiverId);

                        lifeNotice.setBusinessId(commonRating.getBusinessId());
                        lifeNotice.setTitle(noticeTitle);
                        lifeNotice.setContext(contextJson.toJSONString());
                        lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
                        lifeNotice.setIsRead(0);
                        lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
                        lifeNoticeMapper.insert(lifeNotice);
                        
                        // 通过WebSocket发送实时通知
                        WebSocketVo webSocketVo = new WebSocketVo();
                        webSocketVo.setSenderId("system");
                        webSocketVo.setReceiverId(receiverId);
                        webSocketVo.setCategory("notice");
                        webSocketVo.setNoticeType("1");
                        webSocketVo.setIsRead(0);
                        webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
                        
                        webSocketProcess.sendMessage(receiverId, JSONObject.from(webSocketVo).toJSONString());
                        log.info("评价审核不通过通知发送成功，ratingId={}, receiverId={}, auditReason={}", 
                                commonRating.getId(), receiverId, auditReason);
                    } else {
                        log.warn("无法发送评价审核通知：用户信息不存在或手机号为空，userId={}", commonRating.getUserId());
                    }
                } catch (Exception e) {
                    log.error("发送评价审核不通过通知失败，ratingId={}, error={}", 
                            commonRating.getId(), e.getMessage(), e);
                }
            }
            
            // 7. 视频由前端审；此处异步分支仍更新状态但 auditVideos 已不再请求后端视频接口（见 AiVideoModerationUtil）
            if (!videoUrls.isEmpty()) {
                CompletableFuture.runAsync(() -> {
                    AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
                    try {
                        // 调用审核接口，增加超时控制（避免接口挂死）
                        videoAuditResult = CompletableFuture.supplyAsync(
                                () -> aiVideoModerationUtil.auditVideos(videoUrls),
                                commonVideoTaskExecutor
                        ).get();

                        // 审核不通过则更新状态和原因
                        if (Objects.nonNull(videoAuditResult) && !videoAuditResult.isPassed()) {
                            // 重新查询最新的rating，避免并发覆盖
                            CommonRating rating = this.getById(commonRating.getId());
                            if (Objects.isNull(rating)) {
                                log.error("视频审核后更新失败，rating ID：{}", commonRating.getId());
                                return;
                            }
                            rating.setAuditStatus(2);
                            // 失败原因
                            rating.setAuditReason(videoAuditResult.getFailureReason());
                            this.saveOrUpdate(rating);
                            log.info("视频审核不通过，已更新状态，ratingID：{}，原因：{}",
                                    rating.getId(), videoAuditResult.getFailureReason());
                        } else if (Objects.nonNull(videoAuditResult) && videoAuditResult.isPassed()) {
                            // 视频审核通过，但需要检查之前的审核状态
                            // 如果之前因为小票/打卡不通过而设置为2（不通过），则保持为2，不改为1
                            CommonRating rating = this.getById(commonRating.getId());
                            if (Objects.nonNull(rating)) {
                                // 只有当前状态为待审核（0）时，才改为通过（1）
                                // 如果已经是2（不通过），说明之前因为小票/打卡不通过，保持为2
                                if (rating.getAuditStatus() == null || rating.getAuditStatus() == 0) {
                                    rating.setAuditStatus(1);
                                    this.saveOrUpdate(rating);
                                    // 对不同的businessType进行不同的处理，
                                    // 使用更新后的rating对象，确保auditStatus正确
                                    doBusinessWithType(rating);
                                    log.info("视频审核通过，已更新状态为通过，ratingID：{}", rating.getId());
                                } else if (rating.getAuditStatus() == 2) {
                                    // 之前因为小票/打卡不通过，即使视频通过，也保持为不通过
                                    log.info("视频审核通过，但之前因小票/打卡不通过，保持审核状态为不通过，ratingID：{}", rating.getId());
                                }
                            }
                        } else {
                            // 视频审核结果为空，可能是没有视频或审核服务异常
                            // 需要检查之前的审核状态，如果之前因为小票/打卡不通过而设置为2，则保持为2
                            CommonRating rating = this.getById(commonRating.getId());
                            if (Objects.nonNull(rating)) {
                                // 只有当前状态为待审核（0）时，才改为通过（1）
                                // 如果已经是2（不通过），说明之前因为小票/打卡不通过，保持为2
                                if (rating.getAuditStatus() == null || rating.getAuditStatus() == 0) {
                                    rating.setAuditStatus(1);
                                    this.saveOrUpdate(rating);
                                    // 使用更新后的rating对象，确保auditStatus正确
                                    doBusinessWithType(rating);
                                    log.info("无视频或视频审核结果为空，文本/图片已通过，设置为审核通过，ratingID：{}", rating.getId());
                                } else if (rating.getAuditStatus() == 2) {
                                    // 之前因为小票/打卡不通过，保持为不通过
                                    log.info("无视频或视频审核结果为空，但之前因小票/打卡不通过，保持审核状态为不通过，ratingID：{}", rating.getId());
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("视频审核接口调用异常，commonRating ID：{}", commonRating.getId(), e);
                        CommonRating rating = this.getById(commonRating.getId());
                        if (Objects.nonNull(rating)) {
                            rating.setAuditStatus(2);
                            rating.setAuditReason("视频审核接口调用异常：" + e.getMessage());
                            this.saveOrUpdate(rating);
                        }
                    }
                }, commonVideoTaskExecutor);
            } else {
                // 7. 没有视频，文本/图片审核已通过，但需要检查之前的审核状态
                // 如果之前因为小票/打卡不通过而设置为2（不通过），则保持为2，不改为1
                CommonRating rating = this.getById(commonRating.getId());
                if (Objects.nonNull(rating)) {
                    // 只有当前状态为待审核（0）时，才改为通过（1）
                    // 如果已经是2（不通过），说明之前因为小票/打卡不通过，保持为2
                    if (rating.getAuditStatus() == null || rating.getAuditStatus() == 0) {
                        rating.setAuditStatus(1);
                        this.saveOrUpdate(rating);
                        // 使用更新后的rating对象，确保auditStatus正确
                        doBusinessWithType(rating);
                        log.info("无视频，文本/图片审核已通过，设置为审核通过，ratingID：{}", rating.getId());
                    } else if (rating.getAuditStatus() == 2) {
                        // 之前因为小票/打卡不通过，保持为不通过
                        log.info("无视频，文本/图片审核已通过，但之前因小票/打卡不通过，保持审核状态为不通过，ratingID：{}", rating.getId());
                    }
                }
            }
            
            return i;
        } catch (Exception e) {
            log.error("CommonRatingService.saveCommonRating ERROR Msg={}", e.getMessage(), e);
            return 1;
        }
    }

    /**
     * 根据不同的businessType进行不同的处理
     * @param commonRating 评价信息
     */
    private void doBusinessWithType(CommonRating commonRating) throws Exception {
        Integer businessId = commonRating.getBusinessId();
        Double score = commonRating.getScore();
        if(1 == commonRating.getBusinessType()){
            // 更新门店评价信息

            StoreInfoScoreVo storeInfoScoreVo = commonRatingMapper.getCommentCountAndScoreInfo(commonRating.getBusinessType(),businessId);

            List<StoreInfoScoreVo>   storeInfoScoreNew = commonRatingMapper.getStoreInfoScoreNew(commonRating.getBusinessType(),businessId);

            Integer commentCount = storeInfoScoreNew.size();

            double total = storeInfoScoreVo.getTotal();
            StoreInfo storeInfo = new StoreInfo();

            //如果有效条数大于10条，则正常进行平均数计算
            if(commentCount > 10){
                double scoreAvg = (total == 0 ? 0 : storeInfoScoreVo.getScore() / total);
                double scoreOne = (total == 0 ? 0 : storeInfoScoreVo.getScoreOne() / total);
                double scoreTwo = (total == 0 ? 0 : storeInfoScoreVo.getScoreTwo() / total);
                double scoreThree = (total == 0 ? 0 : storeInfoScoreVo.getScoreThree() / total);
                storeInfo.setId(businessId);
                storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
                storeInfo.setScoreOne(new BigDecimal(scoreOne).setScale(2, RoundingMode.HALF_UP).doubleValue());
                storeInfo.setScoreTwo(new BigDecimal(scoreTwo).setScale(2, RoundingMode.HALF_UP).doubleValue());
                storeInfo.setScoreThree(new BigDecimal(scoreThree).setScale(2, RoundingMode.HALF_UP).doubleValue());
                storeInfoMapper.updateById(storeInfo);
            }

            //如果有效条数=10，则进行特殊处理
            if(commentCount == 10){
                //判断首次达到10条还是第N次达到10条
                StoreInfo storeInfoNew = storeInfoMapper.selectById(businessId);
                //首次达到10条 平均分是3.0 ，第N次达到10条 4.0
                if(storeInfoNew.getScoreAvg() == 0){
                    double scoreAvg = 3.0;
//                    double scoreOne = 3.0;
//                    double scoreTwo = 3.0;
//                    double scoreThree =3.0;
                    storeInfo.setId(businessId);
                    storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    storeInfoMapper.updateById(storeInfo);
                }else{
                    double scoreAvg = 4.0 ;
                    storeInfo.setId(businessId);
                    storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    storeInfoMapper.updateById(storeInfo);
                }
            }


//            if(total >= 10){
//                double scoreAvg = (total == 0 ? 0 : storeInfoScoreVo.getScore() / total);
//                double scoreOne = (total == 0 ? 0 : storeInfoScoreVo.getScoreOne() / total);
//                double scoreTwo = (total == 0 ? 0 : storeInfoScoreVo.getScoreTwo() / total);
//                double scoreThree = (total == 0 ? 0 : storeInfoScoreVo.getScoreThree() / total);
//                storeInfo.setId(businessId);
//                storeInfo.setScoreAvg(new BigDecimal(scoreAvg).setScale(2, RoundingMode.HALF_UP).doubleValue());
//                storeInfo.setScoreOne(new BigDecimal(scoreOne).setScale(2, RoundingMode.HALF_UP).doubleValue());
//                storeInfo.setScoreTwo(new BigDecimal(scoreTwo).setScale(2, RoundingMode.HALF_UP).doubleValue());
//                storeInfo.setScoreThree(new BigDecimal(scoreThree).setScale(2, RoundingMode.HALF_UP).doubleValue());
//                storeInfoMapper.updateById(storeInfo);
//            }
            // 须用 businessId 查询门店账号：当 total<10 时未执行 storeInfo.setId，storeInfo.getId() 恒为 null，会导致查不到店主、差评通知路径 NPE（好评可提交、差评失败）
            StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, businessId).eq(StoreUser::getDeleteFlag, 0));

            // 如果差评，则发送差评提醒
            if(score != null && score >= 0.5 && score <= 2.5){
                if (storeUser == null || StringUtils.isEmpty(storeUser.getPhone())) {
                    log.warn("差评通知跳过：未找到门店主账号或手机号为空，storeId={}", businessId);
                } else {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String commonDate = simpleDateFormat.format(new Date());
                LifeNotice lifeMessage = new LifeNotice();
                lifeMessage.setReceiverId("store_" + storeUser.getPhone());

                String text = "在"+commonDate+"，您的店铺有一条差评记录，您可查看评价内容是否属实，如不属实，可向平台进行申诉。";
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("message", text);
                lifeMessage.setContext(jsonObject.toJSONString());
                lifeMessage.setTitle("差评通知");
                lifeMessage.setSenderId("system");
                lifeMessage.setIsRead(0);
                lifeMessage.setNoticeType(1);
                lifeNoticeUtil.fillUserTypeAndRefId(lifeMessage);
                lifeNoticeMapper.insert(lifeMessage);

                WebSocketVo websocketVo = new WebSocketVo();
                websocketVo.setSenderId("system");
                websocketVo.setReceiverId("store_" + storeUser.getPhone());
                websocketVo.setCategory("notice");
                websocketVo.setNoticeType("1");
                websocketVo.setIsRead(0);
                websocketVo.setText(JSONObject.from(lifeMessage).toJSONString());
                webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
                }
            }
            // 用户好评且 AI 审核通过后：按运营活动表（评论有礼）配置的 coupon_id/voucher_id 发放，用 participation_limit 限制参与次数，发放成功后扣减库存，按优惠券/代金券发对应通知
            // 支持多个活动同时生效：如果一个商家有多个正在开启的活动（如一个送优惠券，一个送代金券），都会执行
            // 重要：只有在审核通过（auditStatus == 1）的情况下才送券
            if (score != null && score >= 4.5 && commonRating.getUserId() != null && commonRating.getBusinessId() != null 
                    && commonRating.getAuditStatus() != null && commonRating.getAuditStatus() == 1) {
                try {
                    Date now = new Date();
                    // 查询所有符合条件的活动（不再限制为1个）
                    LambdaQueryWrapper<StoreOperationalActivity> activityWrapper = new LambdaQueryWrapper<>();
                    activityWrapper.eq(StoreOperationalActivity::getStoreId, businessId)
                            .eq(StoreOperationalActivity::getActivityType, "COMMENT")
                            .in(StoreOperationalActivity::getStatus, 5, 8)
                            .le(StoreOperationalActivity::getStartTime, now)
                            .ge(StoreOperationalActivity::getEndTime, now)
                            .orderByDesc(StoreOperationalActivity::getId); // 按ID降序，优先处理最新创建的活动
                    List<StoreOperationalActivity> activities = storeOperationalActivityMapper.selectList(activityWrapper);
                    
                    if (activities == null || activities.isEmpty()) {
                        log.info("CommonRatingService 好评送券：未找到符合条件的活动，userId={}, storeId={}", 
                                commonRating.getUserId(), businessId);
                        return;
                    }
                    
                    // 遍历所有活动，分别处理每个活动
                    int totalIssuedCount = 0;
                    for (StoreOperationalActivity activity : activities) {
                        // 跳过没有配置优惠券的活动
                        if (activity.getCouponId() == null || activity.getCouponId() <= 0) {
                            log.info("CommonRatingService 好评送券跳过：活动未配置优惠券，activityId={}, userId={}, storeId={}", 
                                    activity.getId(), commonRating.getUserId(), businessId);
                            continue;
                        }
                        
                        // 参与次数限制（每条活动单独计；统计含本次已过审好评）
                        Integer limit = activity.getParticipationLimit();
                        if (limit != null && limit > 0) {
                            int passedCount = commonRatingMapper.countPassedGoodRatingsByUserAndStore(
                                    commonRating.getUserId(), businessId, activity.getAuditTime());
                            if (passedCount > limit) {
                                log.info("CommonRatingService 好评送券跳过：超过运营活动参与次数 activityId={}, participation_limit={}, count={}, userId={}, storeId={}",
                                        activity.getId(), limit, passedCount, commonRating.getUserId(), businessId);
                                continue;
                            }
                        }

                        int issuedCount = lifeDiscountCouponStoreFriendService.issueCouponForGoodRating(
                                Integer.valueOf(Math.toIntExact(commonRating.getUserId())), businessId,
                                activity.getCouponId(), activity.getCouponQuantity());
                        
                        if (issuedCount > 0) {
                            totalIssuedCount += issuedCount;
                            log.info("CommonRatingService 好评送券成功：activityId={}, 发放数量={}, userId={}, storeId={}, couponId={}",
                                    activity.getId(), issuedCount, commonRating.getUserId(), businessId,
                                    activity.getCouponId());
                        } else {
                            log.warn("CommonRatingService 好评送券失败：activityId={}, 发放数量=0, userId={}, storeId={}, couponId={}",
                                    activity.getId(), commonRating.getUserId(), businessId,
                                    activity.getCouponId());
                        }
                    }
                    
                    if (totalIssuedCount > 0) {
                        log.info("CommonRatingService 好评=送券汇总：总发放数量={}, 活动数={}, userId{}, storeId={}",
                                totalIssuedCount, activities.size(), commonRating.getUserId(), businessId);
                    }
                } catch (Exception ex) {
                    log.warn("CommonRatingService 好评送券异常 userId={}, storeId={}, msg={}", 
                            commonRating.getUserId(), businessId, ex.getMessage(), ex);
                }
            }
        }

    }
    @Override
    public R getRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long businessId, Long userId, Integer auditStatus, Integer searchScore, Integer days, Integer replyStatus, Integer tagId) {
        Page<CommonRating> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
        
        // 业务类型筛选
        if (businessType != null) {
            wrapper.eq(CommonRating::getBusinessType, businessType);
        }
        // 业务ID筛选
        if (businessId != null) {
            wrapper.eq(CommonRating::getBusinessId, businessId);
        }
        // 审核状态筛选
        if (auditStatus != null) {
            wrapper.eq(CommonRating::getAuditStatus, auditStatus);
        }
        // 评分等级筛选
        if (searchScore != null) {
            if(searchScore == 1){
                // 1-好评
                wrapper.ge(CommonRating::getScore, 4.5);
            }else if(searchScore == 2){
                // 2-中评
                wrapper.ge(CommonRating::getScore, 3.0);
                wrapper.le(CommonRating::getScore, 4.0);
            }else if(searchScore == 3){
                // 3-差评
                wrapper.ge(CommonRating::getScore, 0.5);
                wrapper.le(CommonRating::getScore, 2.5);
            }else if(searchScore == 4){
                // 4-有图
                wrapper.isNotNull(CommonRating::getImageUrls);
                wrapper.ne(CommonRating::getImageUrls, "");
                wrapper.apply("TRIM({0}) <> ''", "image_urls");
            }
        }
        // 时间范围筛选
        if (days != null && days > 0) {
            Date date = DateUtils.calcDays(new Date(), -days);
            wrapper.ge(CommonRating::getCreatedTime, date);
        }
        // 回复状态筛选（使用EXISTS子查询在数据库层面筛选）
        if (replyStatus != null && (replyStatus == 1 || replyStatus == 2)) {
            if (replyStatus == 1) {
                // 已回复：存在商户评论
                wrapper.apply("EXISTS (SELECT 1 FROM common_comment cc WHERE cc.source_type = 1 " +
                             "AND cc.source_id = common_rating.id AND cc.comment_type = 2 " +
                             "AND cc.is_show = 1 AND cc.audit_status = 1 AND cc.delete_flag = 0)");
            } else if (replyStatus == 2) {
                // 未回复：不存在商户评论
                wrapper.apply("NOT EXISTS (SELECT 1 FROM common_comment cc WHERE cc.source_type = 1 " +
                             "AND cc.source_id = common_rating.id AND cc.comment_type = 2 " +
                             "AND cc.is_show = 1 AND cc.audit_status = 1 AND cc.delete_flag = 0)");
            }
        }
        // 标签筛选
        if (tagId != null) {
            // 1. 查询标签关联的评论ID列表（tags_synonym.comment_id 关联 common_comment.id）
            LambdaQueryWrapper<TagsSynonym> tagWrapper = new LambdaQueryWrapper<>();
            tagWrapper.eq(TagsSynonym::getMainTagId, tagId)
                     .eq(TagsSynonym::getDeleteFlag, 0)
                     .isNotNull(TagsSynonym::getCommentId);  // 确保comment_id不为空
            List<TagsSynonym> tagsSynonymList = tagsSynonymMapper.selectList(tagWrapper);

            if (CollectionUtils.isNotEmpty(tagsSynonymList)) {
                // 2. 提取评论ID列表（common_comment.id）
                List<Long> commentIdList = tagsSynonymList.stream()
                        .filter(synonym -> synonym != null && synonym.getCommentId() != null)
                        .map(synonym -> synonym.getCommentId().longValue())
                        .distinct()
                        .collect(Collectors.toList());

                if (CollectionUtils.isNotEmpty(commentIdList)) {
                    // 3. 通过评论ID查询common_comment表，获取source_id（评价ID）
                    LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<>();
                    commentWrapper.in(CommonComment::getId, commentIdList)
                                 .eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())  // source_type=1表示评价的评论
                                 .eq(CommonComment::getIsShow, 1)
                                 .eq(CommonComment::getAuditStatus, 1);
                    List<CommonComment> comments = commonCommentMapper.selectList(commentWrapper);

                    if (CollectionUtils.isNotEmpty(comments)) {
                        // 4. 提取评价ID列表（common_comment.source_id = common_rating.id）
                        List<Long> ratingIdList = comments.stream()
                                .filter(comment -> comment.getSourceId() != null)
                                .map(CommonComment::getSourceId)
                                .distinct()
                                .collect(Collectors.toList());

                        if (CollectionUtils.isNotEmpty(ratingIdList)) {
                            wrapper.in(CommonRating::getId, ratingIdList);
                        } else {
                            // 如果没有匹配的评价ID，返回空结果
                            wrapper.eq(CommonRating::getId, -1);
                        }
                    } else {
                        // 如果没有匹配的评论，返回空结果
                        wrapper.eq(CommonRating::getId, -1);
                    }
                } else {
                    // 如果没有评论ID，返回空结果
                    wrapper.eq(CommonRating::getId, -1);
                }
            } else {
                // 如果没有标签关联，返回空结果
                wrapper.eq(CommonRating::getId, -1);
            }
        }
        // 只查询审核通过的评价
        wrapper.eq(CommonRating::getAuditStatus, 1);
        wrapper.eq(CommonRating::getIsShow, 1);
        wrapper.orderByDesc(CommonRating::getId);
        IPage<CommonRating> page1 = this.page(page, wrapper);

        // 处理回复状态筛选
        return doListBusinessWithType(page1, businessType, userId, replyStatus);
    }

    @Override
    public Object getRatingCount(Integer businessId, Integer businessType) {
        // 查询全部评价记录
        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CommonRating::getBusinessId, businessId);
        wrapper.eq(CommonRating::getBusinessType, businessType);
        wrapper.eq(CommonRating::getAuditStatus, 1);
        wrapper.eq(CommonRating::getIsShow, 1);
        List<CommonRating> commonRatings = commonRatingMapper.selectList(wrapper);
        List<Long> collect = commonRatings.stream().map(x -> x.getId()).collect(Collectors.toList());
        // 查询没有回复的差评数量（使用新 wrapper，避免复用导致条件累积问题）
        // 注意：因为 deleteFlag 字段有 @TableLogic 注解，LambdaQueryWrapper.eq() 对该字段可能失效
        // 而 getRatingWithNoReply 是自定义 XML 查询，不会自动补充逻辑删除条件
        // 所以这里使用 .apply() 直接写 SQL 条件，确保 delete_flag = 0 生效
        LambdaQueryWrapper<CommonRating> noReplyWrapper = new LambdaQueryWrapper<>();
        noReplyWrapper.eq(CommonRating::getBusinessId, businessId);
        noReplyWrapper.eq(CommonRating::getBusinessType, businessType);
        noReplyWrapper.eq(CommonRating::getAuditStatus, 1);
        noReplyWrapper.eq(CommonRating::getIsShow, 1);
        noReplyWrapper.apply("delete_flag = 0");
        noReplyWrapper.ge(CommonRating::getScore, 0.5);
        noReplyWrapper.le(CommonRating::getScore, 2.5);
        Integer noReplyCount = commonRatingMapper.getRatingWithNoReply(noReplyWrapper);

        // 如果为空直接返回
        Map<String, Object> ratingCount = new HashMap<>();
        if(commonRatings.isEmpty()){
            return ratingCount;
        }
        // 获取评价统计信息（总评论数、有图评论数、好评数、中评数、差评数）
        ratingCount = commonRatingMapper.getRatingCount(new QueryWrapper<CommonRating>().in("id", collect));
        ratingCount.put("noReplyCount", noReplyCount);
        // 计算好评、中评、差评占比
        // 注意：数据库返回的 count 可能是 BigDecimal、Long 或 Integer 类型，需要安全转换
        int goodCount = getIntValue(ratingCount.get("goodCount"));
        int midCount = getIntValue(ratingCount.get("midCount"));
        int badCount = getIntValue(ratingCount.get("badCount"));
        int totalCount = goodCount + midCount + badCount;

        if (totalCount > 0) {
            // 计算占比（保留2位小数）
            Double goodPercent = Math.round((goodCount * 100.0 / totalCount) * 100.0) / 100.0;
            Double midPercent = Math.round((midCount * 100.0 / totalCount) * 100.0) / 100.0;
            Double badPercent = Math.round((badCount * 100.0 / totalCount) * 100.0) / 100.0;

            ratingCount.put("goodPercent", goodPercent);
            ratingCount.put("midPercent", midPercent);
            ratingCount.put("badPercent", badPercent);
        } else {
            ratingCount.put("goodPercent", 0.0);
            ratingCount.put("midPercent", 0.0);
            ratingCount.put("badPercent", 0.0);
        }

        if(RatingBusinessTypeEnum.STORE_RATING.getBusinessType() == businessType){
            // 1店铺评分
            StoreInfo storeInfo = storeInfoMapper.selectById(businessId);
            ratingCount.put("storeScore", storeInfo.getScoreAvg());

            // 2统计评价数量
            AtomicReference<Integer> count = new AtomicReference<>(0);
            count.updateAndGet(v -> v + collect.size());
            // 1.查询评价的评论的记录
/*            LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<CommonComment>()
                    .eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
                    .in(CommonComment::getSourceId, collect);
            List<Long> collect1 = commonCommentMapper.selectList(commentWrapper).stream().map(x -> x.getId()).collect(Collectors.toList());
            if(!collect1.isEmpty()) {
                count.updateAndGet(v -> v + collect1.size());
                // 2.在再评论的表中查询评论的回复（根评论）
                Integer allCommentsOnCommentsNum = getAllCommentsOnCommentsNum(collect1);
                count.updateAndGet(v -> v + allCommentsOnCommentsNum);
            }*/
            ratingCount.put("totalCount", count.get());

            // 3用户图片
            List<Long> collect2 = commonRatings.stream().filter(i -> i.getScore() >= 4.5).map(CommonRating::getUserId).distinct().limit(6).collect(Collectors.toList());
            if(!collect2.isEmpty()) {
                List<LifeUser> lifeUsers = lifeUserMapper.selectList(new QueryWrapper<LifeUser>().lambda().in(LifeUser::getId, collect2));
                ratingCount.put("img", lifeUsers.stream().map(LifeUser::getUserImage).collect(Collectors.toList()));
            } else {
                ratingCount.put("img", new ArrayList<>());
            }

            // 4. 计算回复率（已回复的评价数 / 总评价数）
            LambdaQueryWrapper<CommonComment> repliedWrapper = new LambdaQueryWrapper<>();
            repliedWrapper.eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
                         .in(CommonComment::getSourceId, collect)
                         .eq(CommonComment::getCommentType, 2)  // 商户评论
                         .eq(CommonComment::getParentId, 0)     // 根评论（直接回复评价）
                         .eq(CommonComment::getDeleteFlag, 0);  // 未删除

            // 获取已回复的评价ID列表（去重）
            Set<Long> repliedRatingIds = commonCommentMapper.selectList(repliedWrapper).stream()
                    .map(CommonComment::getSourceId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

            int repliedCount = repliedRatingIds.size();
            ratingCount.put("repliedCount", repliedCount);

            // 计算回复率（保留2位小数）
            Double replyRate = 0.0;
            int ratingTotalCount = collect.size();
            if (ratingTotalCount > 0) {
                replyRate = Math.round((repliedCount * 100.0 / ratingTotalCount) * 100.0) / 100.0;
            }
            ratingCount.put("replyRate", replyRate);

        }
        return ratingCount;
    }

    @Override
    public Object getRatingDetail(Integer ratingId, Long userId) {
        CommonRating commonRating = commonRatingMapper.selectById(ratingId);
        if(commonRating == null){
            throw new IllegalArgumentException("评价不存在");
        }
        CommonRatingVo commonRatingVo = new CommonRatingVo();
        BeanUtils.copyProperties(commonRating, commonRatingVo);
        LifeUser ratingUser = lifeUserMapper.selectById(Integer.parseInt(commonRating.getUserId().toString()));
        if (ratingUser == null){
            throw new IllegalArgumentException("评价用户不存在");
        }
        // 设置评价用户信息
        commonRatingVo.setUserImage(ratingUser.getUserImage());
        commonRatingVo.setUserName(ratingUser.getUserName());
        commonRatingVo.setPhone(ratingUser.getUserPhone());
        // 查询当前用户点赞列表(仅评价)
        setIsLike(ratingId, userId, commonRatingVo);
        LifeUser loginUser = lifeUserMapper.selectById(userId);
        if (loginUser == null){
            throw new IllegalArgumentException("登录用户不存在");
        }
        // 查询当前用户关注列表
        LifeFansIdentityQuery.Scope followedScope = LifeFansIdentityQuery.resolve("user_".concat(ratingUser.getUserPhone()), typeUtil);
        LifeFansIdentityQuery.Scope fanScope = LifeFansIdentityQuery.resolve("user_".concat(loginUser.getUserPhone()), typeUtil);
        LambdaQueryWrapper<LifeFans> fansQuery = new LambdaQueryWrapper<>();
        fansQuery.eq(LifeFans::getDeleteFlag, 0).eq(LifeFans::getFansType, "1");
        LifeFansIdentityQuery.applyPair(fansQuery, fanScope, followedScope);
        List<LifeFans> lifeFans = lifeFansMapper.selectList(fansQuery);
        // 设置是否关注
        commonRatingVo.setIsFollow(lifeFans.size() > 0 ? 1 : 0);
        // 根据业务类型处理
        extractedWithBusinessType(ratingId, userId, commonRatingVo);
        return commonRatingVo;
    }

    /**
     * 设置是否点赞
     * @param ratingId 评价id
     * @param userId 用户id
     * @param commonRatingVo 评价vo
     */
    private void setIsLike(Integer ratingId, Long userId, CommonRatingVo commonRatingVo) {
        StoreLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId != null ? userId.intValue() : null);
        LambdaQueryWrapper<LifeLikeRecord> likeQueryWrapper = new LambdaQueryWrapper<>();
        likeQueryWrapper.eq(LifeLikeRecord::getHuifuId, ratingId)
                .eq(LifeLikeRecord::getDeleteFlag, 0)
                .eq(LifeLikeRecord::getType, CommonConstant.RATING_LIKE);
        lifeLikeIdentityHelper.applyLikerFilter(likeQueryWrapper, likerScope);
        List<LifeLikeRecord> lifeLikeRecords = lifeLikeRecordMapper.selectList(likeQueryWrapper);
        if(lifeLikeRecords.size() > 0){
            commonRatingVo.setIsLike(1);
        } else {
            commonRatingVo.setIsLike(0);
        }
    }

    /**
     * 根据业务类型处理
     * @param ratingId 评价id
     * @param userId 用户id
     * @param commonRatingVo 评价vo
     */
    private void extractedWithBusinessType(Integer ratingId, Long userId, CommonRatingVo commonRatingVo) {
        if(commonRatingVo.getBusinessType().equals(RatingBusinessTypeEnum.STORE_RATING.getBusinessType())){
            // 1店铺信息
            StoreInfo storeInfo = storeInfoMapper.selectById(commonRatingVo.getBusinessId());
            if(storeInfo == null){
                throw new IllegalArgumentException("店铺不存在");
            }
            if( null != storeInfo) {
                commonRatingVo.setStoreName(storeInfo.getStoreName());
                commonRatingVo.setStoreEvaluate(storeInfo.getStoreEvaluate());
                commonRatingVo.setStoreScore(storeInfo.getScoreAvg());
                commonRatingVo.setScoreOne(storeInfo.getScoreOne());
                commonRatingVo.setScoreTwo(storeInfo.getScoreTwo());
                commonRatingVo.setScoreThree(storeInfo.getScoreThree());
            }
            // 1.1 查询是否收藏该店铺
            LambdaQueryWrapper<LifeCollect> collectWrapper = new LambdaQueryWrapper<LifeCollect>()
                    .eq(LifeCollect::getStoreId, storeInfo.getId())
                    .eq(LifeCollect::getDeleteFlag, 0);
            LifeCollectIdentityQuery.applyUserSide(collectWrapper,
                    LifeCollectIdentityQuery.fromLifeUserId(userId != null ? userId.intValue() : null));
            LifeCollect lifeCollect = lifeCollectMapper.selectOne(collectWrapper);
            if(lifeCollect != null){
                commonRatingVo.setIsCollect(1);
            } else {
                commonRatingVo.setIsCollect(0);
            }
            // 2查询一级评价
            List<CommonCommentVo> commonComments = commonCommentService.getFirstLevelComment(CommentSourceTypeEnum.STORE_COMMENT.getType(), ratingId,null,null, userId,CommonConstant.COMMENT_LIKE);
            commonCommentService.getAllChildComment(userId, commonRatingVo, commonComments,CommonConstant.COMMENT_LIKE);
        }
    }

    @NotNull
    private Integer getAllCommentsOnCommentsNum(List<Long> collect1) {
        LambdaQueryWrapper<CommonComment> commentReplyWrapper = new LambdaQueryWrapper<CommonComment>()
                .in(CommonComment::getParentId, collect1);
        List<Long> collect2 = commonCommentMapper.selectList(commentReplyWrapper).stream().map(x -> x.getId()).collect(Collectors.toList());
        if(collect2.isEmpty()){
            return 0;
        } else {
            return collect2.size() + getAllCommentsOnCommentsNum(collect2);
        }
    }

    /**
     * 根据业务类型处理评价列表
     *
     * @param page1       评价分页数据
     * @param businessType 业务类型
     * @param userId      用户ID
     * @param replyStatus 回复状态(0:全部, 1:已回复, 2:未回复)
     * @return R<IPage<CommonRatingVo>>
     */
    @Override
    public R<IPage<CommonRatingVo>> doListBusinessWithType(IPage<CommonRating> page1, Integer businessType, Long userId, Integer replyStatus) {
        if(businessType == RatingBusinessTypeEnum.STORE_RATING.getBusinessType()){
            IPage<CommonRatingVo> result = new Page<>(page1.getCurrent(), page1.getSize(), page1.getTotal());
            List<CommonRatingVo> resultList = new ArrayList<>();
            if(page1.getRecords().isEmpty()){
                result.setRecords(resultList);
                return R.data(result);
            }

            // 1. 查询评价用户信息
            Set<Long> userIdSet = page1.getRecords().stream()
                    .map(CommonRating::getUserId)
                    .collect(Collectors.toSet());
            List<LifeUser> lifeUsers = new ArrayList<>();
            Map<Integer, LifeUser> lifeUserMap = new HashMap<>();
            if (!userIdSet.isEmpty()){
                lifeUsers = lifeUserMapper.selectList(
                        new QueryWrapper<LifeUser>().lambda()
                                .in(LifeUser::getId, userIdSet));
                lifeUserMap = lifeUsers.stream()
                        .collect(Collectors.toMap(LifeUser::getId, Function.identity()));
            }

            // 2. 查询当前用户点赞列表(仅评价)
            List<LifeLikeRecord> lifeLikeRecords = new ArrayList<>();
            Map<String, LifeLikeRecord> likeRecordMap = new HashMap<>();
            if (userId != null) {
                StoreLikerScope likerScope = lifeLikeIdentityHelper.fromLifeUserId(userId.intValue());
                LambdaQueryWrapper<LifeLikeRecord> likeQueryWrapper = new LambdaQueryWrapper<>();
                likeQueryWrapper.eq(LifeLikeRecord::getType, CommonConstant.RATING_LIKE);
                lifeLikeIdentityHelper.applyLikerFilter(likeQueryWrapper, likerScope);
                lifeLikeRecords = lifeLikeRecordMapper.selectList(likeQueryWrapper);
                likeRecordMap = lifeLikeRecords.stream()
                        .collect(Collectors.toMap(LifeLikeRecord::getHuifuId, Function.identity()));
            }

            // 3. 查询评价的评论记录个数和商户回复状态
            Set<Long> ratingIdSet = page1.getRecords().stream()
                    .map(CommonRating::getId)
                    .collect(Collectors.toSet());

            // 查询所有评论（用于统计评论数）
            LambdaQueryWrapper<CommonComment> commentWrapper = new LambdaQueryWrapper<>();
            commentWrapper.eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
                         .in(CommonComment::getSourceId, ratingIdSet)
                         .eq(CommonComment::getIsShow, 1)
                         .eq(CommonComment::getAuditStatus, 1);
            List<CommonComment> allComments = commonCommentMapper.selectList(commentWrapper);

            // 评价ID -> 该评价下的总评论数
            Map<Long, Long> ratingCommentCountMap = allComments.stream()
                    .collect(Collectors.groupingBy(CommonComment::getSourceId, Collectors.counting()));

            // 查询每个评价的最新一条商户回复（comment_type=2, parent_id=0）
            Map<Long, CommonCommentVo> latestMerchantReplyMap = new HashMap<>();
            if (!ratingIdSet.isEmpty()) {
                QueryWrapper<CommonCommentVo> merchantReplyWrapper = new QueryWrapper<>();
                merchantReplyWrapper.eq("cc.source_type", CommentSourceTypeEnum.STORE_COMMENT.getType())
                                   .in("cc.source_id", ratingIdSet)
                                   .eq("cc.comment_type", 2)  // 商户评论
                                   .eq("cc.parent_id", 0)     // 直接回复评价的根评论
                                   .eq("cc.is_show", 1)
                                   .eq("cc.audit_status", 1)
                                   .eq("cc.delete_flag", 0)
                                   .orderByDesc("cc.created_time");

                // 查询所有商户回复（包含用户信息）
                List<CommonCommentVo> merchantReplies = commonCommentMapper.selectALlComment(
                        null,
                    merchantReplyWrapper,
                    CommonConstant.COMMENT_LIKE,
                    userId != null ? userId : 0L
                );

                // 按评价ID分组，每组只取最新的一条（已按时间倒序排序）
                if (CollectionUtils.isNotEmpty(merchantReplies)) {
                    for (CommonCommentVo reply : merchantReplies) {
                        Long ratingId = reply.getSourceId();
                        // 如果该评价还没有设置回复，则设置（因为已经按时间倒序，第一条就是最新的）
                        if (ratingId != null && !latestMerchantReplyMap.containsKey(ratingId)) {
                            latestMerchantReplyMap.put(ratingId, reply);
                        }
                    }
                }
            }

            // 4. 查询申诉信息（关联 store_comment_appeal 表）
            Map<Long, StoreCommentAppeal> appealMap = new HashMap<>();
            if (!ratingIdSet.isEmpty()) {
                // 将 Long 类型的评价ID转换为 Integer 类型（因为 comment_id 是 Integer）
                List<Integer> ratingIdList = ratingIdSet.stream()
                        .map(Long::intValue)
                        .collect(Collectors.toList());
                
                // 查询申诉记录（comment_id 关联 common_rating.id）
                LambdaQueryWrapper<StoreCommentAppeal> appealWrapper = new LambdaQueryWrapper<>();
                appealWrapper.in(StoreCommentAppeal::getCommentId, ratingIdList)
                            .eq(StoreCommentAppeal::getDeleteFlag, 0);
                List<StoreCommentAppeal> appeals = storeCommentAppealMapper.selectList(appealWrapper);
                
                // 构建评价ID -> 申诉记录的映射（一个评价可能有多条申诉，取最新的一条）
                if (CollectionUtils.isNotEmpty(appeals)) {
                    // 按评价ID分组，每组只取最新的一条申诉记录
                    Map<Integer, StoreCommentAppeal> tempAppealMap = new HashMap<>();
                    for (StoreCommentAppeal appeal : appeals) {
                        Integer commentId = appeal.getCommentId();
                        if (commentId != null) {
                            // 如果该评价还没有申诉记录，或者当前申诉记录更新，则更新
                            if (!tempAppealMap.containsKey(commentId) || 
                                (appeal.getUpdatedTime() != null && 
                                 tempAppealMap.get(commentId).getUpdatedTime() != null &&
                                 appeal.getUpdatedTime().after(tempAppealMap.get(commentId).getUpdatedTime()))) {
                                tempAppealMap.put(commentId, appeal);
                            }
                        }
                    }
                    // 转换为 Long 类型的 key
                    for (Map.Entry<Integer, StoreCommentAppeal> entry : tempAppealMap.entrySet()) {
                        appealMap.put(entry.getKey().longValue(), entry.getValue());
                    }
                }
            }

            // 5. 组装评价列表数据
            for (CommonRating record : page1.getRecords()) {
                CommonRatingVo commonRatingVo = new CommonRatingVo();
                BeanUtil.copyProperties(record, commonRatingVo);

                // 设置用户信息
                if(lifeUserMap.containsKey(Integer.parseInt(record.getUserId().toString()))){
                    LifeUser lifeUser = lifeUserMap.get(Integer.parseInt(record.getUserId().toString()));
                    commonRatingVo.setUserImage(lifeUser.getUserImage());
                    commonRatingVo.setUserName(lifeUser.getUserName());
                }

                // 设置点赞状态
                commonRatingVo.setIsLike(0);
                if(likeRecordMap.containsKey(record.getId().toString())){
                    commonRatingVo.setIsLike(1);
                }

                // 设置评论数
                commonRatingVo.setCommentCount(ratingCommentCountMap.getOrDefault(record.getId(), 0L));

                // 设置商户最新回复（同一评价下，商家只显示最新的一条回复）
                CommonCommentVo latestMerchantReply = latestMerchantReplyMap.get(record.getId());
                if (latestMerchantReply != null) {
                    // 只设置一条最新的商户回复
                    commonRatingVo.setChildCommonComments(Collections.singletonList(latestMerchantReply));
                } else {
                    // 没有商户回复，设置为空列表
                    commonRatingVo.setChildCommonComments(new ArrayList<>());
                }

                // 设置申诉状态和申诉标识
                StoreCommentAppeal appeal = appealMap.get(record.getId());
                if (appeal != null) {
                    // 已申诉，设置申诉状态
                    commonRatingVo.setAppealStatus(appeal.getAppealStatus());
                    commonRatingVo.setAppealFlag(1);
                } else {
                    // 未申诉
                    commonRatingVo.setAppealStatus(null);
                    commonRatingVo.setAppealFlag(0);
                }

                resultList.add(commonRatingVo);
            }

            result.setRecords(resultList);
            return R.data(result);
        }
        // 如果不是店铺评价类型，返回空结果
        IPage<CommonRatingVo> emptyResult = new Page<>(page1.getCurrent(), page1.getSize(), 0);
        return R.data(emptyResult);
    }
    /**
     * 删除评价后更新店铺评分
     * 
     * @param businessId 店铺ID
     */
    @Override
    public void updateStoreScoreAfterDelete(Integer businessId) {
        if (businessId == null) {
            return;
        }
        
        try {
            // 查询该店铺的所有有效评价
            LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(CommonRating::getBusinessType, 1)
                   .eq(CommonRating::getBusinessId, businessId)
                   .eq(CommonRating::getDeleteFlag, 0)
                   .eq(CommonRating::getIsShow, 1)
                   .eq(CommonRating::getAuditStatus, 1);
            List<CommonRating> ratings = commonRatingMapper.selectList(wrapper);

           StoreInfo  storeInfoNew=storeInfoMapper.getStoreInfo(businessId);


            
            int total = ratings.size();

            //如果评价数量小于10，则重置评分为3.5   评价=10  重置评分4.0
            List<StoreInfoScoreVo>   storeInfoScoreNew = commonRatingMapper.getStoreInfoScoreNew(1,businessId);
            Integer commentCount = storeInfoScoreNew.size();


            if (commentCount < 10) {
                if (storeInfoNew.getScoreAvg()!=0.0){
                    // 没有评价，设置默认评分为0
                    StoreInfo storeInfo = new StoreInfo();
                    storeInfo.setId(businessId);
                    storeInfo.setScoreAvg(3.5);
//                storeInfo.setScoreOne(3.0);
//                storeInfo.setScoreTwo(3.0);
//                storeInfo.setScoreThree(3.0);
                    storeInfoMapper.updateById(storeInfo);
                    log.info("店铺有效评价被删除<10条，重置评分为3.5，businessId={}", businessId);
                }
            }else if(commentCount > 10){
                // 计算平均评分
                double scoreSum = ratings.stream().mapToDouble(r -> r.getScore() != null ? r.getScore() : 0.0).sum();
                double scoreOneSum = ratings.stream().mapToDouble(r -> r.getScoreOne() != null ? r.getScoreOne() : 0.0).sum();
                double scoreTwoSum = ratings.stream().mapToDouble(r -> r.getScoreTwo() != null ? r.getScoreTwo() : 0.0).sum();
                double scoreThreeSum = ratings.stream().mapToDouble(r -> r.getScoreThree() != null ? r.getScoreThree() : 0.0).sum();

                double scoreAvg = Math.round((scoreSum / total) * 100.0) / 100.0;
                double scoreOne = Math.round((scoreOneSum / total) * 100.0) / 100.0;
                double scoreTwo = Math.round((scoreTwoSum / total) * 100.0) / 100.0;
                double scoreThree = Math.round((scoreThreeSum / total) * 100.0) / 100.0;

                StoreInfo storeInfo = new StoreInfo();
                storeInfo.setId(businessId);
                storeInfo.setScoreAvg(scoreAvg);
                storeInfo.setScoreOne(scoreOne);
                storeInfo.setScoreTwo(scoreTwo);
                storeInfo.setScoreThree(scoreThree);
                storeInfoMapper.updateById(storeInfo);

                log.info("更新店铺评分成功，businessId={}，评价数={}，scoreAvg={}", businessId, total, scoreAvg);
            }else{
                // 没有评价，设置默认评分为0
                StoreInfo storeInfo = new StoreInfo();
                storeInfo.setId(businessId);
                storeInfo.setScoreAvg(4.0);
//                storeInfo.setScoreOne(3.0);
//                storeInfo.setScoreTwo(3.0);
//                storeInfo.setScoreThree(3.0);
                storeInfoMapper.updateById(storeInfo);
                log.info("店铺有效评价被删除=10条，重置评分为4.0，businessId={}", businessId);

            }

        } catch (Exception e) {
            log.error("更新店铺评分失败，businessId={}，error={}", businessId, e.getMessage());
        }
    }

    @Override
    public R getMyRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long userId, Integer auditStatus) {
        Page<CommonRating> page = new Page<>(pageNum, pageSize);
        // 改成queryWrapper
        QueryWrapper<CommonRating> wrapper = new QueryWrapper<>();
        wrapper.eq(businessType!=null,"cr.business_type", businessType)
                .eq(userId != null, "cr.user_id", userId)
//                .eq("cr.delete_flag", 0)
                .eq(auditStatus != null,"cr.audit_status", auditStatus);
        wrapper.eq("cr.is_show", 1);
        wrapper.orderByDesc("cr.created_time");
        IPage<CommonRating> page1 = commonRatingMapper.getMyRatingList(page, wrapper);
        R<IPage<CommonRatingVo>> iPageR = doListBusinessWithType(page1, businessType, userId, null);
        return iPageR;
    }

    @Override
    public R<IPage<MyMergedReviewItemVo>> getMyMergedRatingList(Integer pageNum, Integer pageSize, Integer businessType,
                                                                Long userId, Integer auditStatus, Integer currentUserId) {
        if (userId == null) {
            return R.fail("userId不能为空");
        }
        int pn = pageNum == null || pageNum < 1 ? 1 : pageNum;
        int ps = pageSize == null || pageSize < 1 ? 10 : pageSize;
        int perSource = Math.min(pn * ps, 1000);
        int biz = businessType != null ? businessType : RatingBusinessTypeEnum.STORE_RATING.getBusinessType();

        List<MyMergedReviewItemVo> merged = new ArrayList<>();

        R storeR = getMyRatingList(1, perSource, biz, userId, auditStatus);
        if (R.isSuccess(storeR) && storeR.getData() != null) {
            IPage<?> sp = (IPage<?>) storeR.getData();
            for (Object o : sp.getRecords()) {
                if (o instanceof CommonRatingVo) {
                    merged.add(toMergedStoreItem((CommonRatingVo) o));
                }
            }
        }

        R<IPage<OrderReviewVo>> lawyerR = orderReviewService.getMyReviewList(1, perSource, userId.intValue(), currentUserId);
        if (R.isSuccess(lawyerR) && lawyerR.getData() != null && lawyerR.getData().getRecords() != null) {
            for (OrderReviewVo vo : lawyerR.getData().getRecords()) {
                merged.add(toMergedLawyerItem(vo));
            }
        }

        merged.sort(Comparator.comparing(MyMergedReviewItemVo::getPublishTime,
                Comparator.nullsLast(Comparator.naturalOrder())).reversed());

        long storeTotal = 0L;
        if (R.isSuccess(storeR) && storeR.getData() != null) {
            storeTotal = ((IPage<?>) storeR.getData()).getTotal();
        }
        long lawyerTotal = 0L;
        if (R.isSuccess(lawyerR) && lawyerR.getData() != null) {
            lawyerTotal = lawyerR.getData().getTotal();
        }
        long total = storeTotal + lawyerTotal;

        int from = (pn - 1) * ps;
        List<MyMergedReviewItemVo> pageSlice;
        if (from >= merged.size()) {
            pageSlice = Collections.emptyList();
        } else {
            int to = Math.min(from + ps, merged.size());
            pageSlice = merged.subList(from, to);
        }

        Page<MyMergedReviewItemVo> page = new Page<>(pn, ps, total);
        page.setRecords(pageSlice);
        return R.data(page);
    }

    private static MyMergedReviewItemVo toMergedStoreItem(CommonRatingVo vo) {
        MyMergedReviewItemVo m = new MyMergedReviewItemVo();
        m.setItemType("STORE");
        m.setStoreRatingId(vo.getId());
        m.setPublishTime(vo.getCreatedTime());
        m.setScore(vo.getScore());
        m.setContent(vo.getContent());
        m.setMediaUrls(splitImageUrlsToMediaList(vo.getImageUrls()));
        m.setStoreId(vo.getBusinessId());
        m.setStoreName(vo.getStoreName());
        m.setStoreIcon(null);
        m.setBusinessType(vo.getBusinessType());
        return m;
    }

    private static MyMergedReviewItemVo toMergedLawyerItem(OrderReviewVo vo) {
        MyMergedReviewItemVo m = new MyMergedReviewItemVo();
        m.setItemType("LAWYER");
        m.setLawyerReviewId(vo.getId());
        m.setPublishTime(vo.getCreatedTime());
        m.setScore(vo.getOverallRating());
        m.setContent(vo.getReviewContent());
        m.setMediaUrls(vo.getReviewImages() != null ? new ArrayList<>(vo.getReviewImages()) : new ArrayList<>());
        m.setLawyerName(vo.getLawyerName());
        m.setLawyerAvatar(vo.getLawyerAvatar());
        m.setLawyerFirm(vo.getLawFirmName());
        m.setRatingTag(lawyerRatingTagText(vo.getOverallRating()));
        m.setLawyerUserId(vo.getLawyerUserId());
        m.setOrderId(vo.getOrderId());
        return m;
    }

    private static List<String> splitImageUrlsToMediaList(String imageUrls) {
        if (imageUrls == null || imageUrls.trim().isEmpty()) {
            return new ArrayList<>();
        }
        return Arrays.stream(imageUrls.split(","))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toList());
    }

    private static String lawyerRatingTagText(Double overallRating) {
        if (overallRating == null) {
            return "我给出评价";
        }
        if (overallRating >= 4.5D) {
            return "我给出超赞";
        }
        if (overallRating >= 3.5D) {
            return "我给出好评";
        }
        if (overallRating >= 2.5D) {
            return "我给出中评";
        }
        return "我给出差评";
    }

/*
    @Override
    public Double getAverageScore(Integer businessType, Long businessId) {
        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CommonRating::getBusinessType, businessType)
                .eq(CommonRating::getBusinessId, businessId)
                .eq(CommonRating::getIsShow, 1)
                .eq(CommonRating::getAuditStatus, 1);
        
        return this.list(wrapper).stream()
                .map(CommonRating::getScore)
                .filter(score -> score != null)
                .mapToDouble(BigDecimal::doubleValue)
                .average()
                .orElse(0.0);
    }

    @Override
    public Long getRatingCount(Integer businessType, Long businessId) {
        LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CommonRating::getBusinessType, businessType)
                .eq(CommonRating::getBusinessId, businessId)
                .eq(CommonRating::getIsShow, 1)
                .eq(CommonRating::getAuditStatus, 1);
        
        return (long) this.count(wrapper);
    }*/

    /**
     * 安全地将数据库返回的数值类型转换为 int
     * 支持 BigDecimal、Long、Integer、Number 等类型
     *
     * @param value 数据库返回的数值对象
     * @return int 值，如果为 null 或无法转换则返回 0
     */
    private int getIntValue(Object value) {
        if (value == null) {
            return 0;
        }

        if (value instanceof Number) {
            return ((Number) value).intValue();
        }

        // 尝试字符串转换
        try {
            if (value instanceof String) {
                return Integer.parseInt((String) value);
            }
        } catch (NumberFormatException e) {
            log.warn("无法将值转换为 int: {}", value, e);
        }

        return 0;
    }
}

