package shop.alien.store.service.impl;

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.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
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.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import shop.alien.entity.store.*;
import shop.alien.entity.store.excelVo.util.ExcelExporter;
import shop.alien.entity.store.vo.*;
import shop.alien.mapper.*;
import shop.alien.store.config.WebSocketProcess;
import shop.alien.store.service.CommonRatingService;
import shop.alien.store.service.StoreCommentAppealService;
import shop.alien.store.util.FileUploadUtil;
import shop.alien.store.util.ai.AiAuthTokenUtil;
import shop.alien.util.common.constant.CommentSourceTypeEnum;
import shop.alien.util.common.netease.TextCheckUtil;
import shop.alien.util.common.safe.TextModerationUtil;
import shop.alien.util.common.safe.TextReviewServiceEnum;
import shop.alien.util.common.constant.CommentSourceTypeEnum;
import shop.alien.util.type.LifeNoticeUtil;

import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.temporal.ChronoUnit;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 评论申诉表 服务实现类
 *
 * @author ssk
 * @since 2025-01-02
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
@RefreshScope
public class StoreCommentAppealServiceImpl extends ServiceImpl<StoreCommentAppealMapper, StoreCommentAppeal> implements StoreCommentAppealService {

    private final StoreCommentAppealMapper storeCommentAppealMapper;

    private final StoreCommentMapper storeCommentMapper;

    private final StoreImgMapper storeImgMapper;

    private final StoreCommentAppealLogMapper storeCommentAppealLogMapper;

    private final FileUploadUtil fileUploadUtil;

    private final WebAuditMapper webAuditMapper;

    private final StoreDictionaryMapper storeDictionaryMapper;

    private final StoreInfoMapper storeInfoMapper;

    @Autowired
    private TextModerationUtil textModerationUtil;

    private final LifeNoticeMapper lifeNoticeMapper;

    private final LifeNoticeUtil lifeNoticeUtil;

    private final LifeUserMapper lifeUserMapper;

    private final StoreCommentAppealSupplementMapper storeCommentAppealSupplementMapper;

    private final StoreUserMapper storeUserMapper;

    private final WebSocketProcess webSocketProcess;

    private final CommonRatingMapper commonRatingMapper;

    private final CommonCommentMapper commonCommentMapper;

    private final LifeLikeRecordMapper lifeLikeRecordMapper;

    private final AiAuthTokenUtil aiAuthTokenUtil;

    private final RestTemplate restTemplate;

    private final CommonRatingService commonRatingService;

    @Value("${third-party-ai-analyze.base-url}")
    private String analyzeUrl;

    @Value("${third-party-ai-resultUrl.base-url}")
    private String resultUrl;

    /**
     * 懒得查, 留着导出Excel
     */
    private List<StoreCommentAppealVo> toExcel = new ArrayList<>();

    /**
     * 申诉历史-分页
     *
     * @param pageNum      页数
     * @param pageSize     页容
     * @param storeId      门店id
     * @param appealStatus 申诉状态: 0:待处理, 1:已驳回, 2:已同意
     * @return IPage<StoreCommentAppeal>
     */
    @Override
    public IPage<StoreCommentAppealVo> getAppealHistory(Integer pageNum, Integer pageSize, Integer storeId, String appealStatus) {
        QueryWrapper<StoreCommentAppealVo> wrapper = new QueryWrapper<>();
        wrapper.eq("a.store_id", storeId)
//                .eq(!appealStatus.isEmpty() && !"null".equals(appealStatus), "a.appeal_status", appealStatus)
                .eq("a.delete_flag", 0)
                .orderByDesc("a.created_time");

        if (StringUtils.isNotEmpty(appealStatus) && !"null".equals(appealStatus)) {
            if ("0".equals(appealStatus)) {
                wrapper.in("a.appeal_status", 0, 3);
            } else {
                wrapper.eq("a.appeal_status", appealStatus);
            }
        }

        IPage<StoreCommentAppealVo> storeCommentAppealPage = storeCommentAppealMapper.getStoreCommentAppealPage(new Page<>(pageNum, pageSize), wrapper);
        storeCommentAppealPage.getRecords().forEach(item -> {
            item.setImgList(buildAppealImgList(item.getImgId()));

            if (StringUtils.isNotEmpty(item.getCommentImgId())) {
                String[] imgId = item.getCommentImgId().split(",");
                List<String> commentImg = new ArrayList<>();
                for (String s : imgId) {
                    StoreImg storeImg = storeImgMapper.selectById(s);
                    if (null != storeImg) {
                        commentImg.add(storeImg.getImgUrl());
                    }
                }
                item.setCommentImgList(commentImg);
            }
        });
        return storeCommentAppealPage;
    }

    @Override
    public Map<String, Integer> getAppealHistoryCountStatus(Integer storeId) {
        Map<String, Integer> map = new HashMap<>();
        //全部
        map.put("-1", storeCommentAppealMapper.selectCount(new LambdaQueryWrapper<StoreCommentAppeal>().eq(StoreCommentAppeal::getStoreId, storeId).eq(StoreCommentAppeal::getDeleteFlag, 0)));
        //审核中
        map.put("0", storeCommentAppealMapper.selectCount(new LambdaQueryWrapper<StoreCommentAppeal>().eq(StoreCommentAppeal::getStoreId, storeId).eq(StoreCommentAppeal::getDeleteFlag, 0).eq(StoreCommentAppeal::getAppealStatus, 0)));
        //驳回
        map.put("1", storeCommentAppealMapper.selectCount(new LambdaQueryWrapper<StoreCommentAppeal>().eq(StoreCommentAppeal::getStoreId, storeId).eq(StoreCommentAppeal::getDeleteFlag, 0).eq(StoreCommentAppeal::getAppealStatus, 1)));
        // 通过
        map.put("2", storeCommentAppealMapper.selectCount(new LambdaQueryWrapper<StoreCommentAppeal>().eq(StoreCommentAppeal::getStoreId, storeId).eq(StoreCommentAppeal::getDeleteFlag, 0).eq(StoreCommentAppeal::getAppealStatus, 2)));
        return map;
    }

    /**
     * 新增申诉
     *
     * @param multipartRequest 文件
     * @param storeId          门店id
     * @param commentId        评论id
     * @param appealReason     申诉原因
     * @return Integer(0 : 申诉成功, 1 : 申诉失败, 2 : 申诉已存在, 3 : 文本内容异常)
     */
    @Override
    public Integer addAppeal(MultipartRequest multipartRequest, Integer storeId, Integer commentId, String appealReason) {
        try {
            /*Map<String, String> checkText = TextCheckUtil.check(appealReason);
            if (null == checkText || checkText.get("result").equals("1")) {
                return 3;
            }*/

//            List<String> servicesList = Lists.newArrayList();
//            servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
//            servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
//            TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(appealReason, servicesList);
//            if ("high".equals(textCheckResult.getRiskLevel())) {
//                return 3;
//            }

            List<String> fileNameSet = new ArrayList<>(multipartRequest.getMultiFileMap().keySet());
            LambdaQueryWrapper<StoreCommentAppeal> wrapper = new LambdaQueryWrapper<>();
            //待审批, 已通过
            List<Integer> list = new ArrayList<>();
            list.add(0);
            list.add(2);
            wrapper.eq(StoreCommentAppeal::getStoreId, storeId)
                    .eq(StoreCommentAppeal::getCommentId, commentId)
                    .in(StoreCommentAppeal::getAppealStatus, list)
                    .orderByDesc(StoreCommentAppeal::getCreatedTime)
                    .last("limit 1");
            /*if (this.getOne(wrapper) != null) {
                return 2;
            }*/
            StoreCommentAppeal storeCommentAppeal = new StoreCommentAppeal();
            StringBuilder imgId = new StringBuilder();
            for (int i = 0; i < fileNameSet.size(); i++) {
                MultipartFile multipartFile = multipartRequest.getFileMap().get(fileNameSet.get(i));
                if (null != multipartFile) {
                    StoreImg storeImg = new StoreImg();
                    storeImg.setStoreId(storeId);
                    storeImg.setImgType(9);
                    storeImg.setImgSort(i + 1);
                    storeImg.setImgUrl(fileUploadUtil.uploadOneFile(multipartFile));
                    storeImgMapper.insert(storeImg);
                    imgId.append(storeImg.getId()).append(",");
                }
            }
            if (!imgId.toString().isEmpty()) {
                storeCommentAppeal.setImgId(imgId.substring(0, imgId.length() - 1));
            }
            storeCommentAppeal.setStoreId(storeId);
            storeCommentAppeal.setCommentId(commentId);
            storeCommentAppeal.setAppealReason(appealReason);
            storeCommentAppeal.setAppealStatus(0);
            boolean storeCommentAppealSave = this.save(storeCommentAppeal);
            //商家申诉
            StoreCommentAppealLog storeCommentAppealLog = new StoreCommentAppealLog();
            storeCommentAppealLog.setAppealId(storeCommentAppeal.getId());
            storeCommentAppealLog.setProcessType(0);
            storeCommentAppealLog.setDeleteFlag(0);
            storeCommentAppealLog.setCreatedTime(storeCommentAppeal.getCreatedTime());
            boolean result = storeCommentAppealSave && storeCommentAppealLogMapper.insert(storeCommentAppealLog) > 0;
            //系统审核
            StoreCommentAppealLog storeCommentAppealLogSystem = new StoreCommentAppealLog();
            storeCommentAppealLogSystem.setAppealId(storeCommentAppeal.getId());
            storeCommentAppealLogSystem.setProcessType(1);
            storeCommentAppealLogSystem.setDeleteFlag(0);
            storeCommentAppealLogSystem.setCreatedTime(storeCommentAppeal.getCreatedTime());
            boolean resultSystem = storeCommentAppealSave && storeCommentAppealLogMapper.insert(storeCommentAppealLogSystem) > 0;
            return result && resultSystem ? 0 : 1;
        } catch (Exception e) {
            return 1;
        }
    }

    /**
     * 新增申诉
     *
     * @param imgUrls       申诉图片OSS地址，多个用英文逗号分隔
     * @param storeId       门店id
     * @param commentId     评论id
     * @param appealReason  申诉原因
     * @return StoreCommentAppealInfoVo(result: 0申诉成功, 1申诉失败, 2申诉已存在, 3文本内容异常)
     */
    @Override
    public StoreCommentAppealInfoVo addAppealNew(String imgUrls, Integer storeId, Integer commentId, String appealReason) {
        StoreCommentAppealInfoVo storeCommentAppealInfoVo = new StoreCommentAppealInfoVo();
        try {
            Map<String, String> checkText = TextCheckUtil.check(appealReason);
            // todo 暂时去掉校验，防止校验不通过
//            if (null == checkText || checkText.get("result").equals("1")) {
//                storeCommentAppealInfoVo.setResult(3);
//                return storeCommentAppealInfoVo;
//            }

//            List<String> servicesList = Lists.newArrayList();
//            servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
//            servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
//            TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(appealReason, servicesList);
//            if ("high".equals(textCheckResult.getRiskLevel())) {
//                storeCommentAppealInfoVo.setResult(3);
//                return storeCommentAppealInfoVo;
//            }

            LambdaQueryWrapper<StoreCommentAppeal> wrapper = new LambdaQueryWrapper<>();
            //待审批, 已通过
            List<Integer> list = new ArrayList<>();
            list.add(0);
            list.add(2);
            wrapper.eq(StoreCommentAppeal::getStoreId, storeId)
                    .eq(StoreCommentAppeal::getCommentId, commentId)
                    .in(StoreCommentAppeal::getAppealStatus, list)
                    .orderByDesc(StoreCommentAppeal::getCreatedTime)
                    .last("limit 1");
            if (this.getOne(wrapper) != null) {
                storeCommentAppealInfoVo.setResult(2);
                return storeCommentAppealInfoVo;
            }
            /*if (this.getOne(wrapper) != null) {
                return 2;
            }*/
            StoreCommentAppeal storeCommentAppeal = new StoreCommentAppeal();
            if (StringUtils.isNotEmpty(imgUrls)) {
                storeCommentAppeal.setImgId(normalizeAppealImgUrls(imgUrls));
            }
            storeCommentAppeal.setStoreId(storeId);
            storeCommentAppeal.setCommentId(commentId);
            storeCommentAppeal.setAppealReason(appealReason);
            storeCommentAppeal.setAppealStatus(0);

            int insertResult = storeCommentAppealMapper.insert(storeCommentAppeal);
            boolean storeCommentAppealSave = insertResult > 0;

            BeanUtils.copyProperties(storeCommentAppeal, storeCommentAppealInfoVo);
            //商家申诉
            StoreCommentAppealLog storeCommentAppealLog = new StoreCommentAppealLog();
            storeCommentAppealLog.setAppealId(storeCommentAppeal.getId());
            storeCommentAppealLog.setProcessType(0);
            storeCommentAppealLog.setDeleteFlag(0);
            storeCommentAppealLog.setCreatedTime(storeCommentAppeal.getCreatedTime());
            boolean result = storeCommentAppealSave && storeCommentAppealLogMapper.insert(storeCommentAppealLog) > 0;
            //系统审核
            StoreCommentAppealLog storeCommentAppealLogSystem = new StoreCommentAppealLog();
            storeCommentAppealLogSystem.setAppealId(storeCommentAppeal.getId());
            storeCommentAppealLogSystem.setProcessType(1);
            storeCommentAppealLogSystem.setDeleteFlag(0);
            storeCommentAppealLogSystem.setCreatedTime(storeCommentAppeal.getCreatedTime());
            boolean resultSystem = storeCommentAppealSave && storeCommentAppealLogMapper.insert(storeCommentAppealLogSystem) > 0;
            
            // ========== 新增：调用AI分析接口并查询结果 ==========
            // 新增修改，商家申诉后，不需要调用ai接口进行分析了，定时任务会再25小时后调用ai接口 ---- 2025-5-29新增需求
//            if (result && resultSystem) {
//                try {
//                    // 1. 调用AI分析接口提交分析任务
//                    Integer recordId = invokeAiAnalyze(storeCommentAppeal);
//                    if (recordId != null) {
//                        // 2. 更新申诉记录，保存record_id
//                        StoreCommentAppeal updateAppeal = new StoreCommentAppeal();
//                        updateAppeal.setId(storeCommentAppeal.getId());
//                        updateAppeal.setRecordId(recordId);
//                        storeCommentAppealMapper.updateById(updateAppeal);
//
//                        // 3. 立即查询AI分析结果
//                        boolean queryResult = queryAiAnalysisResult(storeCommentAppeal.getId(), recordId);
//                        if (queryResult) {
//                            log.info("AI分析任务提交并查询结果成功，申诉ID: {}，recordId: {}",
//                                storeCommentAppeal.getId(), recordId);
//                        } else {
//                            log.warn("AI分析任务提交成功，但查询结果失败，申诉ID: {}，recordId: {}，将由定时任务后续查询",
//                                storeCommentAppeal.getId(), recordId);
//                        }
//
//                        // 更新返回对象（recordId已通过BeanUtils.copyProperties复制）
//                    } else {
//                        log.warn("AI分析任务提交失败，但申诉记录已保存，申诉ID: {}，状态保持: 待处理(0)",
//                            storeCommentAppeal.getId());
//                        // AI调用失败不影响申诉提交，保持原状态（0:待处理）
//                    }
//                } catch (Exception e) {
//                    log.error("调用AI分析接口异常，申诉ID: {}", storeCommentAppeal.getId(), e);
//                    // AI调用失败不影响申诉提交，保持原状态（0:待处理）
//                }
//            }
            // ========== 新增代码结束 ==========

            if (result && resultSystem) {
                sendMerchantAppealNotificationToUser(storeCommentAppeal);
            }

            storeCommentAppealInfoVo.setResult(result && resultSystem ? 0 : 1);
        } catch (Exception e) {
            log.error("新增申诉异常", e);
            storeCommentAppealInfoVo.setResult(1);
        }
        return storeCommentAppealInfoVo;
    }

    /**
     * 申诉详情
     *
     * @param id 申诉id
     * @return StoreCommentAppealVo
     */
    @Override
    public StoreCommentAppealVo getStoreCommentAppealDetail(Integer id) {
        QueryWrapper<StoreCommentAppealVo> storeCommentAppealVoQueryWrapper = new QueryWrapper<>();
        storeCommentAppealVoQueryWrapper.eq("a.id", id);
        StoreCommentAppealVo commentDetail = storeCommentAppealMapper.getCommentDetail(storeCommentAppealVoQueryWrapper);

        commentDetail.setImgList(buildAppealImgList(commentDetail.getImgId()));

        // 处理评价图片（common_rating.image_urls 已经是URL字符串，不是ID）
        if (StringUtils.isNotEmpty(commentDetail.getCommentImgId())) {
            String[] split2 = commentDetail.getCommentImgId().split(",");
            List<String> imgList2 = new ArrayList<>();
            for (String s : split2) {
                String trimmedUrl = s.trim();
                if (StringUtils.isNotEmpty(trimmedUrl)) {
                    // 如果已经是URL格式（包含http://或https://），直接使用
                    if (trimmedUrl.startsWith("http://") || trimmedUrl.startsWith("https://")) {
                        imgList2.add(trimmedUrl);
                    } else {
                        // 否则尝试作为ID查询（兼容旧数据）
                        StoreImg storeImg = storeImgMapper.selectById(trimmedUrl);
                        if (null != storeImg) {
                            imgList2.add(storeImg.getImgUrl());
                        }
                    }
                }
            }
            commentDetail.setCommentImgList(imgList2);
        } else {
            commentDetail.setCommentImgList(new ArrayList<>());
        }

        List<StoreCommentAppealLogVo> storeCommentAppealLogVo = storeCommentAppealLogMapper.getStoreCommentAppealLogVo(commentDetail.getId());
        commentDetail.setStoreCommentAppealLogList(storeCommentAppealLogVo);
        if (ObjectUtils.isNotEmpty(storeCommentAppealLogVo)) {
            commentDetail.setLogRemark(storeCommentAppealLogVo.get(storeCommentAppealLogVo.size() - 1).getLogRemark());
        }
        fillMerchantInfo(commentDetail);
        return commentDetail;
    }

    /**
     * 填充商家名称与头像（门店名称来自 store_info，头像优先主账号 store_user.head_img）
     */
    private void fillMerchantInfo(StoreCommentAppealVo commentDetail) {
        if (commentDetail == null || commentDetail.getStoreId() == null) {
            return;
        }
        StoreInfo storeInfo = storeInfoMapper.selectById(commentDetail.getStoreId());
        if (storeInfo != null) {
            commentDetail.setStoreName(storeInfo.getStoreName());
        }
        List<StoreUser> storeUsers = storeUserMapper.selectList(new LambdaQueryWrapper<StoreUser>()
                .eq(StoreUser::getStoreId, commentDetail.getStoreId())
                .eq(StoreUser::getDeleteFlag, 0));
        String storeImage = null;
        for (StoreUser storeUser : storeUsers) {
            if (storeUser.getAccountType() != null && storeUser.getAccountType() == 1
                    && StringUtils.isNotEmpty(storeUser.getHeadImg())) {
                storeImage = storeUser.getHeadImg();
                break;
            }
            if (storeImage == null && StringUtils.isNotEmpty(storeUser.getHeadImg())) {
                storeImage = storeUser.getHeadImg();
            }
        }
        commentDetail.setStoreImage(storeImage);
    }

    @Override
    public StoreCommentAppealVo getStoreCommentAppealDetailByCommentId(Integer commentId) {
        QueryWrapper<StoreCommentAppealVo> wrapper = new QueryWrapper<>();
        wrapper.eq("a.comment_id", commentId)
                .eq("a.delete_flag", 0)
                .orderByDesc("a.created_time")
                .last("limit 1");
        StoreCommentAppealVo commentDetail = storeCommentAppealMapper.getCommentDetail(wrapper);
        if (commentDetail == null) {
            return null;
        }
        StoreCommentAppealVo detail = getStoreCommentAppealDetail(commentDetail.getId());
        fillSupplementList(detail);
        return detail;
    }

    /**
     * 填充用户补充内容列表（按申诉ID查询，无数据时返回空列表）
     */
    private void fillSupplementList(StoreCommentAppealVo appealVo) {
        if (appealVo == null || appealVo.getId() == null) {
            return;
        }
        List<StoreCommentAppealSupplementVo> supplementList =
                storeCommentAppealSupplementMapper.listByAppealId(appealVo.getId());
        if (supplementList == null || supplementList.isEmpty()) {
            appealVo.setSupplementList(new ArrayList<>());
            return;
        }
        supplementList.forEach(this::fillSupplementImgList);
        appealVo.setSupplementList(supplementList);
    }

    private void fillSupplementImgList(StoreCommentAppealSupplementVo vo) {
        if (vo == null || StringUtils.isEmpty(vo.getImageUrls())) {
            if (vo != null) {
                vo.setImgList(new ArrayList<>());
            }
            return;
        }
        List<String> imgList = new ArrayList<>();
        for (String item : vo.getImageUrls().split(",")) {
            String trimmed = item.trim();
            if (StringUtils.isNotEmpty(trimmed)) {
                imgList.add(trimmed);
            }
        }
        vo.setImgList(imgList);
    }

    /**
     * web-申诉分页查询
     *
     * @param pageNum      页数
     * @param pageSize     页容
     * @param storeName    门店名称
     * @param storeContact 门店联系人
     * @param storePhone   门店电话
     * @param appealStatus 申诉状态: 0:待处理, 1:已驳回, 2:已同意
     * @return IPage<StoreCommentAppealVo>
     */
    @Override
    public IPage<StoreCommentAppealVo> getStoreAppealPage(Integer pageNum, Integer pageSize, String storeName, String storeContact, String storePhone, String appealStatus) {
        IPage<StoreCommentAppealVo> storePage = new Page<>(pageNum, pageSize);
        QueryWrapper<StoreCommentAppealVo> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(!storeName.isEmpty(), "e.store_name", storeName);
        queryWrapper.like(!storeContact.isEmpty(), "f.name", storeContact);
        queryWrapper.like(!storePhone.isEmpty(), "f.phone", storePhone);
        queryWrapper.like(!appealStatus.isEmpty(), "a.appeal_status", appealStatus);
        queryWrapper.orderByDesc("a.created_time");
        IPage<StoreCommentAppealVo> storeCommentAppealPage = storeCommentAppealMapper.getStoreCommentAppealPage(storePage, queryWrapper);
        List<StoreCommentAppealVo> records = storeCommentAppealPage.getRecords();
        if (!records.isEmpty()) {
            // 提前查询所有需要的字典数据
            Set<String> allTypes = records.stream()
                    .map(StoreCommentAppealVo::getStoreType)
                    .flatMap(type -> Arrays.stream(type.split(",")))
                    .collect(Collectors.toSet());
            List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
                    new LambdaQueryWrapper<StoreDictionary>()
                            .eq(StoreDictionary::getTypeName, "storeType")
                            .in(StoreDictionary::getDictId, allTypes)
            );
            Map<String, String> typeMap = storeDictionaries.stream()
                    .collect(Collectors.toMap(
                            StoreDictionary::getDictId,
                            StoreDictionary::getDictDetail
                    ));
            storeCommentAppealPage.getRecords().forEach(item -> {
                String[] types = item.getStoreType().split(",");
                List<String> typeDetails = Arrays.stream(types)
                        .map(typeMap::get)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());
                item.setStoreTypeStr(String.join(",", typeDetails));
                item.setImgList(buildAppealImgList(item.getImgId()));
            });
        }
        toExcel = storeCommentAppealPage.getRecords();
        return storeCommentAppealPage;
    }

    /**
     * 修改申诉状态
     *
     * @param id           申诉id
     * @param appealStatus 申诉状态: 0:待处理, 1:已驳回, 2:已同意
     * @return boolean
     */
    @Override
    public boolean setAppealStatus(Integer id, Integer appealStatus, String logRemark) {
        StoreCommentAppeal sca = storeCommentAppealMapper.selectById(id);
        if (sca == null){
            return false;
        }
        Integer storeId = sca.getStoreId();
        StoreCommentAppeal storeCommentAppeal = new StoreCommentAppeal();
        storeCommentAppeal.setId(id);
        storeCommentAppeal.setAppealStatus(appealStatus);
        switch (appealStatus) {
            case 0:
                storeCommentAppeal.setFinalResult("待处理");
                break;
            case 1:
                storeCommentAppeal.setFinalResult("已驳回");
                break;
            case 2:
                storeCommentAppeal.setFinalResult("已同意");
                // 审核通过后删除评价（common_rating）和评论（common_comment）
                StoreCommentAppeal byId = this.getById(id);
                Integer ratingId = byId.getCommentId(); // commentId 实际存储的是评价ID（common_rating.id）
                
                if (ratingId != null) {
                    // 1. 删除评价（逻辑删除 common_rating 表）
                    // 注意：因为 deleteFlag 字段有 @TableLogic 注解，必须使用原生 SQL 绕过限制
                    CommonRating rating = commonRatingMapper.selectById(ratingId);
                    if (rating != null) {
                        // 使用原生 SQL 方法直接更新 delete_flag
                        int rows = commonRatingMapper.logicDeleteById(ratingId);
                        log.info("删除评价结果，ratingId={}，影响行数={}", ratingId, rows);
                    }
                    
                    // 2. 查询该评价下的所有评论ID（用于后续删除点赞记录）
                    LambdaQueryWrapper<CommonComment> commentQueryWrapper = new LambdaQueryWrapper<>();
                    commentQueryWrapper.eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
                                      .eq(CommonComment::getSourceId, ratingId)
                                      .eq(CommonComment::getDeleteFlag, 0);
                    List<CommonComment> comments = commonCommentMapper.selectList(commentQueryWrapper);
                    List<Long> commentIds = comments.stream().map(CommonComment::getId).collect(Collectors.toList());
                    
                    // 3. 删除该评价下的所有评论（使用原生 SQL 绕过 @TableLogic 限制）
                    int commentRows = commonCommentMapper.logicDeleteBySourceId(
                            CommentSourceTypeEnum.STORE_COMMENT.getType(), ratingId);
                    log.info("删除评价下的评论结果，ratingId={}，影响行数={}", ratingId, commentRows);
                    
                    // 4. 删除评价的点赞记录（type=7 表示评价点赞）
                    LambdaUpdateWrapper<LifeLikeRecord> ratingLikeWrapper = new LambdaUpdateWrapper<>();
                    ratingLikeWrapper.eq(LifeLikeRecord::getType, "7")
                                    .eq(LifeLikeRecord::getHuifuId, String.valueOf(ratingId))
                                    .eq(LifeLikeRecord::getDeleteFlag, 0);
                    ratingLikeWrapper.set(LifeLikeRecord::getDeleteFlag, 1);
                    ratingLikeWrapper.set(LifeLikeRecord::getUpdatedTime, new Date());
                    lifeLikeRecordMapper.update(null, ratingLikeWrapper);
                    log.info("删除评价的点赞记录，ratingId={}", ratingId);
                    
                    // 5. 删除评论的点赞记录（type=1 表示评论点赞）
                    if (!commentIds.isEmpty()) {
                        for (Long commentId : commentIds) {
                            LambdaUpdateWrapper<LifeLikeRecord> commentLikeWrapper = new LambdaUpdateWrapper<>();
                            commentLikeWrapper.eq(LifeLikeRecord::getType, "1")
                                             .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
                                             .eq(LifeLikeRecord::getDeleteFlag, 0);
                            commentLikeWrapper.set(LifeLikeRecord::getDeleteFlag, 1);
                            commentLikeWrapper.set(LifeLikeRecord::getUpdatedTime, new Date());
                            lifeLikeRecordMapper.update(null, commentLikeWrapper);
                        }
                        log.info("删除评论的点赞记录，评论数={}", commentIds.size());
                    }
                    
                    // 6. 重新统计店铺评分（删除评价后需要更新门店评分）
                    if (rating != null && rating.getBusinessType() == 1) {
                        updateStoreScore(rating.getBusinessId());
                    }
                }
                
                // 7. 不删除申诉记录，保留以便申诉历史能显示已通过的记录
                // 申诉记录保留，只更新状态为"已同意"，评价快照数据仍可在申诉历史中查看
                log.info("申诉审核通过，保留申诉记录，申诉ID={}，评价ID={}", id, ratingId);
                break;
        }
        //商家申诉
        //处理中台审核记录
        LambdaUpdateWrapper<WebAudit> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(WebAudit::getStoreCommentAppealId, storeCommentAppeal.getId());
        wrapper.set(WebAudit::getStatus, "1");
        webAuditMapper.update(null, wrapper);

        StoreCommentAppealLog storeCommentAppealLog = new StoreCommentAppealLog();
        storeCommentAppealLog.setAppealId(storeCommentAppeal.getId());
        if (appealStatus == 1) {
            storeCommentAppealLog.setProcessType(4);
        } else if (appealStatus == 2) {
            storeCommentAppealLog.setProcessType(5);
        }
        storeCommentAppealLog.setDeleteFlag(0);
        storeCommentAppealLog.setCreatedTime(new Date());
        storeCommentAppealLog.setLogRemark(logRemark);
        storeCommentAppealLogMapper.insert(storeCommentAppealLog);
        boolean result = this.updateById(storeCommentAppeal);

        // 发送通知
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String commonDate = simpleDateFormat.format(new Date());
        String text = "";
        if(appealStatus==2) {
            text = "您在"+commonDate+"提交了申诉删除差评，经核实，确为不实评价，申诉成功，已将此条评价删除。";
        } else if(appealStatus==1){
            text = "您在"+commonDate+"提交了申诉删除差评，经核实，确为顾客真实感受，申诉失败，此条评价不会被删除。";
        }
        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
        if(StringUtils.isEmpty(text) || storeInfo == null || StringUtils.isEmpty(storeInfo.getStoreTel())) {
            return result;
        }
        StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeInfo.getId()).eq(StoreUser::getDeleteFlag, 0));

        LifeNotice lifeMessage = new LifeNotice();
        lifeMessage.setReceiverId("store_" + storeUser.getPhone());

        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());
        try {
            webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
        } catch (Exception e) {
            log.error("StoreCommentAppealServiceImpl webSocketProcess Stack={}", e);
        }

        return result;
    }

    /**
     * 导出Excel
     *
     * @return ResponseEntity
     */
    @Override
    public ResponseEntity<byte[]> exportToExcel() {
        try {
            String[] headers = {"商家名称", "联系人", "联系电话", "经营种类", "申诉时间", "顾客评论", "申诉理由", "状态"};
            byte[] excelData = ExcelExporter.exportToExcel(toExcel, headers, "申诉管理列表");
            HttpHeaders responseHeaders = new HttpHeaders();
            responseHeaders.add("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode("申诉管理列表.xlsx") + "\";charset=utf-8");
            responseHeaders.set("charset", "UTF-8");
            return new ResponseEntity<>(excelData, responseHeaders, HttpStatus.OK);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断用户是否可以评价
     * 规则：近30天申诉通过次数不超过3次，且距最近一次申诉通过未满10天不可评价
     */
    @Override
    public Map<String, Object> canRate(Integer userId) {
        log.info("StoreCommentAppealServiceImpl.canRate, userId={}", userId);
        Map<String, Object> resultMap = new HashMap<>();
        List<StoreCommentAppeal> passedAppealList = storeCommentAppealMapper.canRate(userId);

        int passedCount = passedAppealList.size();
        // 列表已按 updated_time 降序，第一条即为最新通过记录
        StoreCommentAppeal latestPassedAppeal = passedAppealList.isEmpty() ? null : passedAppealList.get(0);
        String countdown = "0天";
        long remainingDays = 0L;
        if (passedCount > 3) {
            remainingDays = resolveAppealPassCooldownRemainingDays(latestPassedAppeal);
            countdown = calculateAppealPassCooldownCountdown(remainingDays);
        }

        boolean canRate = passedCount <= 3 || remainingDays <= 0;
        resultMap.put("canRate", canRate);
        resultMap.put("passedCount", passedCount);
        resultMap.put("latestPassedAppeal", latestPassedAppeal);
        resultMap.put("countdown", countdown);

        log.info("StoreCommentAppealServiceImpl.canRate result, userId={}, canRate={}, passedCount={}, countdown={}",
                userId, canRate, passedCount, countdown);
        return resultMap;
    }

    /**
     * 计算申诉通过后的10天冷却倒计时（仅天数）
     */
    private String calculateAppealPassCooldownCountdown(long remainingDays) {
        String countdown = remainingDays + "天";
        log.debug("calculateAppealPassCooldownCountdown, remainingDays={}, countdown={}", remainingDays, countdown);
        return countdown;
    }

    /**
     * 解析申诉通过后10天冷却剩余天数（按自然日计算，不含小时）
     * 以申诉更新日期（通过日期）为起点，满10个自然日后冷却结束
     */
    private long resolveAppealPassCooldownRemainingDays(StoreCommentAppeal latestPassedAppeal) {
        if (latestPassedAppeal == null || latestPassedAppeal.getUpdatedTime() == null) {
            return 0L;
        }
        LocalDate passDate = latestPassedAppeal.getUpdatedTime().toInstant()
                .atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate cooldownEndDate = passDate.plusDays(10);
        LocalDate today = LocalDate.now();
        if (!today.isBefore(cooldownEndDate)) {
            log.debug("resolveAppealPassCooldownRemainingDays: 冷却已结束, passDate={}, cooldownEndDate={}",
                    passDate, cooldownEndDate);
            return 0L;
        }
        return ChronoUnit.DAYS.between(today, cooldownEndDate);
    }

    /**
     * 更新店铺评分（删除评价后重新计算门店评分）
     *
     * @param businessId 店铺ID
     */
    private void updateStoreScore(Integer businessId) {
        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);
            
            int total = ratings.size();
            if (total == 0) {
                // 没有评价，设置默认评分为0
                StoreInfo storeInfo = new StoreInfo();
                storeInfo.setId(businessId);
                storeInfo.setScoreAvg(0.0);
                storeInfo.setScoreOne(0.0);
                storeInfo.setScoreTwo(0.0);
                storeInfo.setScoreThree(0.0);
                storeInfoMapper.updateById(storeInfo);
                log.info("店铺无有效评价，重置评分为0，businessId={}", businessId);
                return;
            }
            
            // 计算平均评分
            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);
        } catch (Exception e) {
            log.error("更新店铺评分失败，businessId={}，error={}", businessId, e.getMessage());
        }
    }

    /**
     * 规范化申诉图片URL（逗号分隔）
     */
    private String normalizeAppealImgUrls(String imgUrls) {
        return Arrays.stream(imgUrls.split(","))
                .map(String::trim)
                .filter(StringUtils::isNotEmpty)
                .collect(Collectors.joining(","));
    }

    /**
     * 解析申诉图片：新数据为OSS URL，旧数据为 store_img 主键
     */
    private List<String> buildAppealImgList(String imgId) {
        List<String> imgList = new ArrayList<>();
        if (StringUtils.isEmpty(imgId)) {
            return imgList;
        }
        for (String item : imgId.split(",")) {
            String trimmed = item.trim();
            if (StringUtils.isEmpty(trimmed)) {
                continue;
            }
            if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
                imgList.add(trimmed);
            } else {
                StoreImg storeImg = storeImgMapper.selectById(trimmed);
                if (storeImg != null && StringUtils.isNotEmpty(storeImg.getImgUrl())) {
                    imgList.add(storeImg.getImgUrl());
                }
            }
        }
        return imgList;
    }

    /**
     * 调用AI分析接口提交申诉分析任务
     *
     * @param storeCommentAppeal 申诉记录
     * @return recordId AI返回的记录ID，失败返回null
     */
    private Integer invokeAiAnalyze(StoreCommentAppeal storeCommentAppeal) {
        log.info("开始调用AI差评申诉置信度分析接口，申诉ID: {}", storeCommentAppeal.getId());
        
//        // 1. 获取访问令牌（使用工具类）
//        String accessToken = aiAuthTokenUtil.getAccessToken();
//        if (!org.springframework.util.StringUtils.hasText(accessToken)) {
//            log.error("调用AI分析接口失败，获取accessToken失败，申诉ID: {}", storeCommentAppeal.getId());
//            return null;
//        }

        // 2. 查询评论信息（CommonRating）
        CommonRating rating = commonRatingMapper.selectById(storeCommentAppeal.getCommentId());
        if (rating == null) {
            log.error("查询评论信息失败，commentId: {}", storeCommentAppeal.getCommentId());
            return null;
        }

        // 3. 构建请求参数
        Map<String, Object> analyzeRequest = new HashMap<>();
        
        // 商家申诉材料（必填）
        analyzeRequest.put("merchant_material", 
            org.springframework.util.StringUtils.hasText(storeCommentAppeal.getAppealReason()) ? 
                storeCommentAppeal.getAppealReason() : "");
        
        // 用户举证材料（必填）- 使用评价内容
        analyzeRequest.put("user_material", 
            org.springframework.util.StringUtils.hasText(rating.getContent()) ? rating.getContent() : "");
        
        // 商家图片：暂时传空数组，先测试文本分析流程
        // TODO: 后续需要将图片转换为Base64
        List<String> merchantImages = buildAppealImgList(storeCommentAppeal.getImgId());
        analyzeRequest.put("merchant_images", merchantImages);
        
        // 用户图片：暂时传空数组，先测试文本分析流程
        // TODO: 后续需要将图片转换为Base64
        List<String> userImages = new ArrayList<>();
         if (org.springframework.util.StringUtils.hasText(rating.getImageUrls())) {
             String[] imageUrls = rating.getImageUrls().split(",");
             for (String imageUrl : imageUrls) {
                 String trimmedUrl = imageUrl.trim();
//                 if (org.springframework.util.StringUtils.hasText(trimmedUrl)) {
//                     String base64 = convertImageToBase64(trimmedUrl);
//                     if (org.springframework.util.StringUtils.hasText(base64)) {
//                         userImages.add(base64);
//                     }
//                 }
                 userImages.add(trimmedUrl);
             }
         }
        analyzeRequest.put("user_images", userImages);
        
        // 其他字段（可选）
        analyzeRequest.put("case_id", storeCommentAppeal.getCommentId() != null ? 
            storeCommentAppeal.getCommentId().toString() : "");
        analyzeRequest.put("order_id", rating.getBusinessId() != null ? 
            rating.getBusinessId().toString() : "");

        // 4. 设置请求头
        HttpHeaders analyzeHeaders = new HttpHeaders();
        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
//        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
        HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);

        // 5. 调用AI接口
        try {
            log.info("调用AI分析接口，URL: {}，申诉ID: {}，商家图片数: {}，用户图片数: {}", 
                analyzeUrl, storeCommentAppeal.getId(), merchantImages.size(), userImages.size());
            ResponseEntity<String> analyzeResp = restTemplate.postForEntity(analyzeUrl, analyzeEntity, String.class);
            
            if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
                String analyzeBody = analyzeResp.getBody();
                log.info("AI差评申诉置信度分析提交成功，申诉ID: {}，返回: {}", storeCommentAppeal.getId(), analyzeBody);

                JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
                JSONObject dataJsonObj = analyzeJson.getJSONObject("data");

                if (dataJsonObj != null) {
                    // 获取record_id用于后续查询
                    Integer recordId = dataJsonObj.getInteger("record_id");
                    if (recordId != null) {
                        log.info("AI分析任务提交成功，申诉ID: {}，recordId: {}", storeCommentAppeal.getId(), recordId);
                        return recordId;
                    } else {
                        log.error("AI分析接口返回record_id为空，申诉ID: {}，响应: {}", 
                            storeCommentAppeal.getId(), analyzeBody);
                    }
                } else {
                    log.error("AI分析接口返回data为空，申诉ID: {}，响应: {}", 
                        storeCommentAppeal.getId(), analyzeBody);
                }
            } else {
                log.error("调用AI分析接口失败，申诉ID: {}，http状态: {}", 
                    storeCommentAppeal.getId(), analyzeResp != null ? analyzeResp.getStatusCode() : null);
            }
        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
            log.error("调用AI分析接口返回503 Service Unavailable错误，申诉ID: {}，错误: {}", 
                storeCommentAppeal.getId(), e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("调用AI分析接口异常，申诉ID: {}", storeCommentAppeal.getId(), e);
        }
        
        return null;
    }

    /**
     * 查询AI分析结果并更新申诉状态
     * 差值>=10则同意，差值<10则驳回
     *
     * @param appealId 申诉ID
     * @param recordId AI返回的记录ID
     * @return 是否查询成功
     */
    private boolean queryAiAnalysisResult(Integer appealId, Integer recordId) {
        log.info("开始查询AI分析结果，申诉ID: {}，recordId: {}", appealId, recordId);
        
        // 1. 获取访问令牌
        String accessToken = aiAuthTokenUtil.getAccessToken();
        if (!org.springframework.util.StringUtils.hasText(accessToken)) {
            log.error("查询AI分析结果失败，获取accessToken失败，申诉ID: {}，recordId: {}", appealId, recordId);
            return false;
        }

        // 2. 构建查询URL
        String completedUrl = buildCompletedUrl(recordId);
        
        // 3. 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + accessToken);
        HttpEntity<String> requestEntity = new HttpEntity<>(headers);

        try {
            log.info("查询AI分析结果，URL: {}，申诉ID: {}，recordId: {}", completedUrl, appealId, recordId);
            ResponseEntity<String> response = restTemplate.exchange(completedUrl, 
                org.springframework.http.HttpMethod.GET, requestEntity, String.class);

            if (response != null && response.getStatusCodeValue() == 200) {
                String responseBody = response.getBody();
                log.info("AI分析结果查询返回，申诉ID: {}，recordId: {}，返回: {}", appealId, recordId, responseBody);

                JSONObject responseJson = JSONObject.parseObject(responseBody);
                Integer respCode = responseJson.getInteger("code");
                JSONObject dataJsonObj = responseJson.getJSONObject("data");

                // 检查AI响应码：202表示分析尚未完成
                if (respCode != null && respCode == 202) {
                    // 从details中获取分析状态
                    JSONObject detailsObj = responseJson.getJSONObject("details");
                    String analysisStatus = detailsObj != null ? detailsObj.getString("analysis_status") : "unknown";
                    log.info("AI分析尚未完成（code=202），申诉ID: {}，recordId: {}，状态: {}，将由定时任务后续查询", 
                        appealId, recordId, analysisStatus);
                    return false; // 分析未完成，返回false，等待定时任务后续查询
                }

                // 检查AI响应码：200表示分析完成
                if (respCode == null || respCode != 200) {
                    log.warn("AI分析接口返回非200响应码，申诉ID: {}，recordId: {}，code: {}，message: {}", 
                        appealId, recordId, respCode, responseJson.getString("message"));
                    return false;
                }

                if (dataJsonObj != null) {
                    // 检查分析状态
                    String analysisStatus = dataJsonObj.getString("analysis_status");
                    if (!"completed".equals(analysisStatus)) {
                        log.info("AI分析尚未完成，申诉ID: {}，recordId: {}，状态: {}", 
                            appealId, recordId, analysisStatus);
                        return false; // 分析未完成，返回false，等待定时任务后续查询
                    }

                    // 获取置信度值
                    Double userConfidence = dataJsonObj.getDouble("user_confidence");
                    Double merchantConfidence = dataJsonObj.getDouble("merchant_confidence");

                    if (userConfidence == null || merchantConfidence == null) {
                        log.error("AI分析结果返回置信度为空，申诉ID: {}，recordId: {}，user_confidence: {}，merchant_confidence: {}", 
                            appealId, recordId, userConfidence, merchantConfidence);
                        return false;
                    }

                    // 计算差值：差值>=10则同意，差值<10则驳回
                    double confidenceDiff = Math.abs(userConfidence - merchantConfidence);
                    log.info("AI分析结果，申诉ID: {}，recordId: {}，user_confidence: {}，merchant_confidence: {}，差值: {}", 
                        appealId, recordId, userConfidence, merchantConfidence, confidenceDiff);

                    // 更新申诉状态
                    StoreCommentAppeal updateAppeal = new StoreCommentAppeal();
                    updateAppeal.setId(appealId);
//                    updateAppeal.setAppealAiApproval(dataJsonObj.toJSONString());

                    if (confidenceDiff >= 10) {
                        // 差值>=10则同意
                        updateAppeal.setAppealStatus(2);
                        updateAppeal.setFinalResult("已同意");
                        log.info("AI分析结果：差值>=10，申诉通过，申诉ID: {}，差值: {}", appealId, confidenceDiff);
                        
                        // 审核通过后删除评价（common_rating）和评论（common_comment）
                        StoreCommentAppeal appeal = this.getById(appealId);
                        if (appeal != null) {
                            Integer ratingId = appeal.getCommentId(); // commentId 实际存储的是评价ID（common_rating.id）
                            if (ratingId != null) {
                                // 调用删除评价的逻辑（复用setAppealStatus中的逻辑）
                                deleteRatingAndComments(ratingId);
                            }
                        }
                    } else {
                        // 差值<10则驳回
                        updateAppeal.setAppealStatus(1);
                        updateAppeal.setFinalResult("已驳回");
                        log.info("AI分析结果：差值<10，申诉驳回，申诉ID: {}，差值: {}", appealId, confidenceDiff);
                    }

                    // 更新申诉记录
                    boolean updateResult = this.updateById(updateAppeal);
                    
                    // 创建审核日志
                    StoreCommentAppealLog storeCommentAppealLog = new StoreCommentAppealLog();
                    storeCommentAppealLog.setAppealId(appealId);
                    if (confidenceDiff >= 10) {
                        storeCommentAppealLog.setProcessType(5); // 已同意
                    } else {
                        storeCommentAppealLog.setProcessType(4); // 已驳回
                    }
                    storeCommentAppealLog.setDeleteFlag(0);
                    storeCommentAppealLog.setCreatedTime(new Date());
                    storeCommentAppealLog.setLogRemark(String.format("AI自动审核：用户置信度=%.2f，商家置信度=%.2f，差值=%.2f", 
                        userConfidence, merchantConfidence, confidenceDiff));
                    storeCommentAppealLogMapper.insert(storeCommentAppealLog);

                    // 发送通知给商家
                    sendAppealResultNotification(appealId, confidenceDiff >= 10);

                    return updateResult;
                } else {
                    log.error("AI分析结果返回data为空，申诉ID: {}，recordId: {}，响应: {}", 
                        appealId, recordId, responseBody);
                }
            } else {
                log.error("查询AI分析结果失败，申诉ID: {}，recordId: {}，http状态: {}", 
                    appealId, recordId, response != null ? response.getStatusCode() : null);
            }
        } catch (Exception e) {
            log.error("查询AI分析结果异常，申诉ID: {}，recordId: {}", appealId, recordId, e);
        }
        
        return false;
    }

    /**
     * 构建查询已完成分析结果的URL
     *
     * @param recordId AI返回的记录ID
     * @return 完整的查询URL
     */
    private String buildCompletedUrl(Integer recordId) {
        String baseUrl = resultUrl;
        if (!org.springframework.util.StringUtils.hasText(baseUrl)) {
            throw new IllegalStateException("差评申述分析接口地址未配置");
        }
        // 移除末尾的斜杠
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        // 直接拼接 /{record_id}/completed
        return baseUrl + "/" + recordId + "/completed";
    }

    /**
     * 删除评价和评论（审核通过时调用）
     *
     * @param ratingId 评价ID
     */
    private void deleteRatingAndComments(Integer ratingId) {
        try {
            // 1. 删除评价（逻辑删除 common_rating 表）
            CommonRating rating = commonRatingMapper.selectById(ratingId);
            if (rating != null) {
                commonRatingService.updateStoreScoreAfterDelete(rating.getBusinessId());
//                int rows = commonRatingMapper.logicDeleteById(ratingId);
                log.info("删除评价结果，ratingId={}，影响行数={}", ratingId);
            }
            
            // 2. 查询该评价下的所有评论ID（用于后续删除点赞记录）
            LambdaQueryWrapper<CommonComment> commentQueryWrapper = new LambdaQueryWrapper<>();
            commentQueryWrapper.eq(CommonComment::getSourceType, CommentSourceTypeEnum.STORE_COMMENT.getType())
                              .eq(CommonComment::getSourceId, ratingId)
                              .eq(CommonComment::getDeleteFlag, 0);
            List<CommonComment> comments = commonCommentMapper.selectList(commentQueryWrapper);
            List<Long> commentIds = comments.stream().map(CommonComment::getId).collect(Collectors.toList());
            
            // 3. 删除该评价下的所有评论（使用原生 SQL 绕过 @TableLogic 限制）
            int commentRows = commonCommentMapper.logicDeleteBySourceId(
                    CommentSourceTypeEnum.STORE_COMMENT.getType(), ratingId);
            log.info("删除评价下的评论结果，ratingId={}，影响行数={}", ratingId, commentRows);
            
            // 4. 删除评价的点赞记录（type=7 表示评价点赞）
            LambdaUpdateWrapper<LifeLikeRecord> ratingLikeWrapper = new LambdaUpdateWrapper<>();
            ratingLikeWrapper.eq(LifeLikeRecord::getType, "7")
                            .eq(LifeLikeRecord::getHuifuId, String.valueOf(ratingId))
                            .eq(LifeLikeRecord::getDeleteFlag, 0);
            ratingLikeWrapper.set(LifeLikeRecord::getDeleteFlag, 1);
            ratingLikeWrapper.set(LifeLikeRecord::getUpdatedTime, new Date());
            lifeLikeRecordMapper.update(null, ratingLikeWrapper);
            log.info("删除评价的点赞记录，ratingId={}", ratingId);
            
            // 5. 删除评论的点赞记录（type=1 表示评论点赞）
            if (!commentIds.isEmpty()) {
                for (Long commentId : commentIds) {
                    LambdaUpdateWrapper<LifeLikeRecord> commentLikeWrapper = new LambdaUpdateWrapper<>();
                    commentLikeWrapper.eq(LifeLikeRecord::getType, "1")
                                     .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
                                     .eq(LifeLikeRecord::getDeleteFlag, 0);
                    commentLikeWrapper.set(LifeLikeRecord::getDeleteFlag, 1);
                    commentLikeWrapper.set(LifeLikeRecord::getUpdatedTime, new Date());
                    lifeLikeRecordMapper.update(null, commentLikeWrapper);
                }
                log.info("删除评论的点赞记录，评论数={}", commentIds.size());
            }
            
            // 6. 重新统计店铺评分（删除评价后需要更新门店评分）
            if (rating != null && rating.getBusinessType() == 1) {
                updateStoreScore(rating.getBusinessId());
            }
        } catch (Exception e) {
            log.error("删除评价和评论异常，ratingId={}", ratingId, e);
        }
    }

    /**
     * 商家发起申诉后，向评价用户发送系统通知
     */
    private void sendMerchantAppealNotificationToUser(StoreCommentAppeal appeal) {
        try {
            CommonRating rating = commonRatingMapper.selectById(appeal.getCommentId());
            if (rating == null || rating.getUserId() == null) {
                log.warn("商家申诉通知跳过：评价不存在或无用户，commentId={}", appeal.getCommentId());
                return;
            }
            LifeUser lifeUser = lifeUserMapper.selectById(rating.getUserId().intValue());
            if (lifeUser == null || StringUtils.isEmpty(lifeUser.getUserPhone())) {
                log.warn("商家申诉通知跳过：用户不存在或手机号为空，userId={}", rating.getUserId());
                return;
            }

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date appealTime = appeal.getCreatedTime() != null ? appeal.getCreatedTime() : new Date();
            String appealTimeStr = simpleDateFormat.format(appealTime);
            String message = "商家于" + appealTimeStr + "发起评论申诉";

            String receiverId = "user_" + lifeUser.getUserPhone();
            JSONObject contextJson = new JSONObject();
            contextJson.put("message", message);
            contextJson.put("appealId", appeal.getId());
            contextJson.put("commentId", appeal.getCommentId());

            LifeNotice lifeNotice = new LifeNotice();
            lifeNotice.setSenderId("system");
            lifeNotice.setReceiverId(receiverId);
            lifeNotice.setBusinessId(appeal.getId());
            lifeNotice.setTitle("商家申诉通知");
            lifeNotice.setContext(contextJson.toJSONString());
            lifeNotice.setNoticeType(1);
            lifeNotice.setIsRead(0);
            lifeNoticeUtil.fillUserTypeAndRefId(lifeNotice);
            lifeNoticeMapper.insert(lifeNotice);

            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("商家申诉通知发送成功，appealId={}, receiverId={}", appeal.getId(), receiverId);
        } catch (Exception e) {
            log.error("发送商家申诉通知失败，appealId={}", appeal.getId(), e);
        }
    }

    /**
     * 发送申诉结果通知给商家
     *
     * @param appealId 申诉ID
     * @param approved 是否通过
     */
    private void sendAppealResultNotification(Integer appealId, boolean approved) {
        try {
            StoreCommentAppeal appeal = this.getById(appealId);
            if (appeal == null) {
                return;
            }

            StoreInfo storeInfo = storeInfoMapper.selectById(appeal.getStoreId());
            if (storeInfo == null || !org.springframework.util.StringUtils.hasText(storeInfo.getStoreTel())) {
                return;
            }

            StoreUser storeUser = storeUserMapper.selectOne(
                new LambdaQueryWrapper<StoreUser>()
                    .eq(StoreUser::getStoreId, storeInfo.getId())
                    .eq(StoreUser::getDeleteFlag, 0));

            if (storeUser == null) {
                return;
            }

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String commonDate = simpleDateFormat.format(new Date());
            String text = "";
            if (approved) {
                text = "您在" + commonDate + "提交了申诉删除差评，经AI审核，确为不实评价，申诉成功，已将此条评价删除。";
            } else {
                text = "您在" + commonDate + "提交了申诉删除差评，经AI审核，确为顾客真实感受，申诉失败，此条评价不会被删除。";
            }

            LifeNotice lifeMessage = new LifeNotice();
            lifeMessage.setReceiverId("store_" + storeUser.getPhone());
            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());
            try {
                webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
            } catch (Exception e) {
                log.error("发送WebSocket通知异常，申诉ID: {}", appealId, e);
            }
        } catch (Exception e) {
            log.error("发送申诉结果通知异常，申诉ID: {}", appealId, e);
        }
    }

    /**
     * 将图片URL转换为Base64编码
     *
     * @param imageUrl 图片URL
     * @return Base64编码字符串
     */
    private String convertImageToBase64(String imageUrl) {
        if (!org.springframework.util.StringUtils.hasText(imageUrl)) {
            log.warn("图片URL为空");
            return "";
        }

        try {
            // 下载图片并转换为Base64
            byte[] imageBytes = restTemplate.getForObject(imageUrl, byte[].class);
            if (imageBytes != null) {
                return java.util.Base64.getEncoder().encodeToString(imageBytes);
            }
        } catch (org.springframework.web.client.HttpClientErrorException.NotFound e) {
            log.warn("图片不存在 (404), URL: {}", imageUrl);
            return "";
        } catch (Exception e) {
            log.error("图片转换为Base64失败, URL: {}", imageUrl, e);
            return "";
        }
        return "";
    }
}
