package com.hotwj.platform.securityManagement.trainingAnswer.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hotwj.platform.config.examPaperQuestion.domain.vo.ExamPaperQuestionDetailVo;
import com.hotwj.platform.config.examPaperQuestion.mapper.HotExamPaperQuestionMapper;
import com.hotwj.platform.flow.service.ISysFlowService;
import com.hotwj.platform.integration.ocr.SignatureVerifyService;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo;
import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper;
import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo;
import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper;
import com.hotwj.platform.securityManagement.trainingAnswer.domain.HotTrainingAnswerRecord;
import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.SubmitTrainingAnswerBo;
import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.TrainingAnswerRecordBo;
import com.hotwj.platform.securityManagement.trainingAnswer.domain.enums.TrainingSceneType;
import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo;
import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingWrongQuestionReviewVo;
import com.hotwj.platform.securityManagement.trainingAnswer.mapper.HotTrainingAnswerRecordMapper;
import com.hotwj.platform.securityManagement.trainingAnswer.service.IHotTrainingAnswerRecordService;
import com.hotwj.platform.securityManagement.trainingAudit.domain.HotTrainingAudit;
import com.hotwj.platform.securityManagement.trainingAudit.mapper.HotTrainingAuditMapper;
import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo;
import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo;
import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant;
import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper;
import com.hotwj.platform.securityManagement.trainingProgress.service.TrainingProgressService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 培训课程答题详情记录Service业务层处理
 *
 * @author shihongwei
 * @date 2026-02-16
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class HotTrainingAnswerRecordServiceImpl implements IHotTrainingAnswerRecordService {

    private static final String FLOW_CODE_TRAINING_STUDY = "TRAINING_STUDY";
    private final HotTrainingAnswerRecordMapper baseMapper;
    private final IHotTrainingCourseRecordService trainingCourseRecordService;
    private final HotTrainingParticipantMapper trainingParticipantMapper;
    private final ISysFlowService flowService;
    private final IHotSystemNotificationService notificationService;
    private final HotTrainingMapper trainingMapper;
    private final HotCompanySafetyManagerMapper companySafetyManagerMapper;
    private final TrainingProgressService trainingProgressService;
    private final HotTrainingAuditMapper trainingAuditMapper;
    private final SignatureVerifyService signatureVerifyService;
    private final HotExamPaperQuestionMapper examPaperQuestionMapper;

    /**
     * 查询培训课程答题详情记录
     *
     * @param id 主键
     * @return 培训课程答题详情记录
     */
    @Override
    public HotTrainingAnswerRecordVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询培训课程答题详情记录列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 培训课程答题详情记录分页列表
     */
    @Override
    public TableDataInfo<HotTrainingAnswerRecordVo> queryPageList(TrainingAnswerRecordBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HotTrainingAnswerRecord> lqw = buildQueryWrapper(bo);
        Page<HotTrainingAnswerRecordVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的培训课程答题详情记录列表
     *
     * @param bo 查询条件
     * @return 培训课程答题详情记录列表
     */
    @Override
    public List<HotTrainingAnswerRecordVo> queryList(TrainingAnswerRecordBo bo) {
        LambdaQueryWrapper<HotTrainingAnswerRecord> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public List<HotTrainingAnswerRecordVo> queryStudyDetailByTrainingAndUser(Long companyId, Long trainingId, String userId) {
        if (companyId == null || trainingId == null || StringUtils.isBlank(userId)) {
            return List.of();
        }
        List<HotTrainingAnswerRecordVo> list = baseMapper.selectPlanStudyDetailByTrainingAndUser(companyId, trainingId, userId);
        if (list == null || list.isEmpty()) {
            return List.of();
        }
        long examScore = 0L;
        Date completeTime = null;
        for (HotTrainingAnswerRecordVo vo : list) {
            if (vo.getGainedScore() != null) {
                examScore += vo.getGainedScore();
            }
            if (vo.getAnswerTime() != null) {
                if (completeTime == null || vo.getAnswerTime().after(completeTime)) {
                    completeTime = vo.getAnswerTime();
                }
            }
        }
        Boolean passed = null;
        HotTrainingAnswerRecordVo head = list.get(0);
        if (head.getPassScore() != null) {
            passed = examScore >= head.getPassScore();
        }
        for (HotTrainingAnswerRecordVo vo : list) {
            vo.setExamScore(examScore);
            if (completeTime != null) {
                vo.setCompleteTime(completeTime);
            }
            if (passed != null) {
                vo.setPassed(passed);
            }
        }
        return list;
    }

    private LambdaQueryWrapper<HotTrainingAnswerRecord> buildQueryWrapper(TrainingAnswerRecordBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HotTrainingAnswerRecord> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(HotTrainingAnswerRecord::getId);
        lqw.eq(bo.getCompanyId() != null, HotTrainingAnswerRecord::getCompanyId, bo.getCompanyId());
        lqw.eq(bo.getTrainingId() != null, HotTrainingAnswerRecord::getTrainingId, bo.getTrainingId());
        lqw.eq(bo.getCourseId() != null, HotTrainingAnswerRecord::getCourseId, bo.getCourseId());
        lqw.eq(bo.getExamId() != null, HotTrainingAnswerRecord::getExamId, bo.getExamId());
        lqw.eq(bo.getUserId() != null, HotTrainingAnswerRecord::getUserId, bo.getUserId());
        lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotTrainingAnswerRecord::getUserName, bo.getUserName());
        lqw.eq(bo.getQuestionId() != null, HotTrainingAnswerRecord::getQuestionId, bo.getQuestionId());
        lqw.eq(bo.getPaperQuestionId() != null, HotTrainingAnswerRecord::getPaperQuestionId, bo.getPaperQuestionId());
        lqw.eq(bo.getQuestionType() != null, HotTrainingAnswerRecord::getQuestionType, bo.getQuestionType());
        lqw.eq(bo.getQuestionScore() != null, HotTrainingAnswerRecord::getQuestionScore, bo.getQuestionScore());
        lqw.eq(bo.getQuestionOrder() != null, HotTrainingAnswerRecord::getQuestionOrder, bo.getQuestionOrder());
        lqw.eq(StringUtils.isNotBlank(bo.getCorrectAnswers()), HotTrainingAnswerRecord::getCorrectAnswers, bo.getCorrectAnswers());
        lqw.eq(StringUtils.isNotBlank(bo.getSelectedAnswers()), HotTrainingAnswerRecord::getSelectedAnswers, bo.getSelectedAnswers());
        lqw.eq(bo.getIsCorrect() != null, HotTrainingAnswerRecord::getIsCorrect, bo.getIsCorrect());
        lqw.eq(bo.getGainedScore() != null, HotTrainingAnswerRecord::getGainedScore, bo.getGainedScore());
        lqw.eq(bo.getAnswerDuration() != null, HotTrainingAnswerRecord::getAnswerDuration, bo.getAnswerDuration());
        lqw.eq(bo.getAnswerTime() != null, HotTrainingAnswerRecord::getAnswerTime, bo.getAnswerTime());
        lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingAnswerRecord::getCreateByName, bo.getCreateByName());
        lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingAnswerRecord::getUpdateByName, bo.getUpdateByName());
        lqw.eq(bo.getIsDeleted() != null, HotTrainingAnswerRecord::getIsDeleted, bo.getIsDeleted());
        return lqw;
    }

    /**
     * 新增培训课程答题详情记录
     *
     * @param bo 培训课程答题详情记录
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(TrainingAnswerRecordBo bo) {
        HotTrainingAnswerRecord add = MapstructUtils.convert(bo, HotTrainingAnswerRecord.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改培训课程答题详情记录
     *
     * @param bo 培训课程答题详情记录
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(TrainingAnswerRecordBo bo) {
        HotTrainingAnswerRecord update = MapstructUtils.convert(bo, HotTrainingAnswerRecord.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(HotTrainingAnswerRecord entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除培训课程答题详情记录信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public Boolean batchInsert(List<TrainingAnswerRecordBo> bos) {
        if (bos == null || bos.isEmpty()) {
            return Boolean.TRUE;
        }
        for (TrainingAnswerRecordBo bo : bos) {
            HotTrainingAnswerRecord entity = MapstructUtils.convert(bo, HotTrainingAnswerRecord.class);
            validEntityBeforeSave(entity);
            baseMapper.insert(entity);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean submitWithSignature(SubmitTrainingAnswerBo bo) {
        if (bo == null || bo.getAnswers() == null || bo.getAnswers().isEmpty()) {
            return Boolean.FALSE;
        }

        if (TrainingSceneType.PLAN.getCode().equals(bo.getSceneType())) {
            String submitterName = resolveSubmitterName(bo);
            bo.setUserName(submitterName);
            signatureVerifyService.validateSelfSignature(bo.getSignatureOssId(), submitterName);
        }


        // 1. 处理答题明细（存在则更新，不存在则插入）
        // 获取当前用户在该场景下的所有答题记录，用于比对
        LambdaQueryWrapper<HotTrainingAnswerRecord> lqw = new LambdaQueryWrapper<>();
        lqw.eq(HotTrainingAnswerRecord::getUserId, bo.getUserId())
                .eq(HotTrainingAnswerRecord::getSceneType, bo.getSceneType())
                .eq(HotTrainingAnswerRecord::getBusinessId, bo.getBusinessId());

        List<HotTrainingAnswerRecord> existingRecords = baseMapper.selectList(lqw);
        Map<Long, HotTrainingAnswerRecord> existingMap = existingRecords.stream()
                .collect(Collectors.toMap(HotTrainingAnswerRecord::getQuestionSource, Function.identity(), (v1, v2) -> v2));

        List<HotTrainingAnswerRecord> toInsert = new ArrayList<>();
        List<HotTrainingAnswerRecord> toUpdate = new ArrayList<>();

        // 计算总分
        long totalScore = 0L;

        for (TrainingAnswerRecordBo answerBo : bo.getAnswers()) {
            // 设置通用字段
            answerBo.setCompanyId(bo.getCompanyId());
            answerBo.setSceneType(bo.getSceneType());
            answerBo.setBusinessId(bo.getBusinessId());
            answerBo.setUserId(bo.getUserId());
            answerBo.setUserName(bo.getUserName());
            answerBo.setTrainingId(bo.getTrainingId());
            answerBo.setExamId(bo.getExamId());
            // courseId 仅在 LESSON 场景或有值时设置
            if (bo.getCourseId() != null) {
                answerBo.setCourseId(bo.getCourseId());
            }

            // 累加分数 (假设 isCorrect 1=正确)
            if (Long.valueOf(1L).equals(answerBo.getIsCorrect()) && answerBo.getGainedScore() != null) {
                totalScore += answerBo.getGainedScore();
            }

            HotTrainingAnswerRecord entity = MapstructUtils.convert(answerBo, HotTrainingAnswerRecord.class);

            if (entity != null && entity.getQuestionSource() != null && existingMap.containsKey(entity.getQuestionSource())) {
                // 更新
                HotTrainingAnswerRecord existing = existingMap.get(entity.getQuestionSource());
                entity.setId(existing.getId());

                mergeErrorData(entity, existing);

                toUpdate.add(entity);
            } else {
                // 插入
                toInsert.add(entity);
            }
        }

        if (!toInsert.isEmpty() || !toUpdate.isEmpty()) {
            for (HotTrainingAnswerRecord entity : toInsert) {
                validEntityBeforeSave(entity);
                baseMapper.insert(entity);
            }
            for (HotTrainingAnswerRecord entity : toUpdate) {
                validEntityBeforeSave(entity);
                baseMapper.updateById(entity);
            }
        }

        // 2. 根据场景更新对应的记录表（保证 userId+trainingId+courseId 唯一）
        if (TrainingSceneType.LESSON.getCode().equals(bo.getSceneType())) {
            HotTrainingCourseRecordBo courseRecordBo = new HotTrainingCourseRecordBo();
            courseRecordBo.setCompanyId(bo.getCompanyId());
            courseRecordBo.setTrainingId(bo.getTrainingId());
            courseRecordBo.setTrainingCourseId(bo.getCourseId());
            courseRecordBo.setUserId(bo.getUserId());
            courseRecordBo.setExamId(bo.getExamId());
            courseRecordBo.setSignatureOssId(bo.getSignatureOssId());
            courseRecordBo.setExamScore(new BigDecimal(totalScore));
            courseRecordBo.setCheckStatus(1L);
            HotTrainingCourseRecordBo queryBo = new HotTrainingCourseRecordBo();
            queryBo.setCompanyId(bo.getCompanyId());
            queryBo.setTrainingId(bo.getTrainingId());
            queryBo.setTrainingCourseId(bo.getBusinessId());
            queryBo.setUserId(bo.getUserId());

            HotTrainingCourseRecordVo latest = trainingCourseRecordService.queryLatestByUserAndCourse(queryBo);
            if (latest != null) {
                courseRecordBo.setId(latest.getId());
                trainingCourseRecordService.updateByBo(courseRecordBo);
            } else {
                trainingCourseRecordService.insertByBo(courseRecordBo);
            }

            trainingProgressService.syncProgress(bo.getCompanyId(), bo.getTrainingId(), bo.getUserId());

        } else if (TrainingSceneType.PLAN.getCode().equals(bo.getSceneType())) {
            clearPlanAuditRecord(bo);
            // 更新培训计划参与记录 (HotTrainingParticipant)
            // PLAN 场景下，businessId 通常是 trainingId
            LambdaQueryWrapper<HotTrainingParticipant> participantLqw = new LambdaQueryWrapper<>();
            participantLqw.eq(HotTrainingParticipant::getCompanyId, bo.getCompanyId())
                    .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId())
                    .eq(HotTrainingParticipant::getUserId, bo.getUserId())
                    .eq(HotTrainingParticipant::getIsDeleted, 0L);

            HotTrainingParticipant participant = trainingParticipantMapper.selectOne(participantLqw);

            if (participant != null) {
                trainingParticipantMapper.update(
                        null,
                        Wrappers.<HotTrainingParticipant>lambdaUpdate()
                                .eq(HotTrainingParticipant::getId, participant.getId())
                                .set(HotTrainingParticipant::getIsCompleted, 1L)
                                .set(HotTrainingParticipant::getCompleteTime, new Date())
                                .set(HotTrainingParticipant::getIsPass, null)
                                .set(HotTrainingParticipant::getSignatureOssId, bo.getSignatureOssId())
                                .set(HotTrainingParticipant::getLearnProgress, BigDecimal.valueOf(100))
                );
                completeTrainingTodo(participant.getTrainingId(), bo.getUserId());
//                trainingProgressService.syncProgress(bo.getCompanyId(), bo.getTrainingId(), bo.getUserId());

                HotTrainingVo training = trainingMapper.selectVoById(bo.getTrainingId());
                String planName = training != null ? training.getPlanName() : "培训";
                List<HotCompanySafetyManagerVo> managers = companySafetyManagerMapper.selectVoList(
                        Wrappers.<com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, bo.getCompanyId())
                                .eq(HotCompanySafetyManager::getIsResigned, 0)
                                .eq(HotCompanySafetyManager::getStatus, 1)
                                .eq(HotCompanySafetyManager::getIsDeleted, 0)
                );
                List<String> receiverIds = managers == null ? List.of() :
                        managers.stream().map(m -> String.valueOf(m.getId())).collect(Collectors.toList());
                if (receiverIds != null && !receiverIds.isEmpty()) {
                    HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo();
                    msg.setLevel("普通");
                    msg.setSourceType("安全教育培训");
                    msg.setSenderType("SYSTEM");
                    msg.setReceiverType("管理员");
                    msg.setReceiverIds(receiverIds);
                    String traineeName = bo.getUserName();
                    msg.setContent((traineeName != null ? traineeName : "驾驶员") + "已完成" + planName + "培训，需要及时审核，也可进行报表打印查看");
                    msg.setIsDeleted(0L);
                    try {
                        notificationService.insertByBo(msg);
                    } catch (Exception e) {
                        log.warn("培训完成后发送管理员系统消息失败 trainingId={} companyId={} err={}", bo.getTrainingId(), bo.getCompanyId(), e.getMessage());
                    }
                }
            }
        }

        return Boolean.TRUE;
    }

    @Override
    public List<HotTrainingWrongQuestionReviewVo> queryWrongQuestionReview(Long companyId, String userId, TrainingAnswerRecordBo bo) {
        if (companyId == null || StringUtils.isBlank(userId)) {
            return List.of();
        }
        // 1.查询试卷题目
        List<ExamPaperQuestionDetailVo> paperQuestions = loadPlanPaperQuestionList(bo);
        if (CollUtil.isEmpty(paperQuestions)) {
            return List.of();
        }

        // 2.查询本次培训计划中错题信息
        List<HotTrainingWrongQuestionReviewVo> wrongList = baseMapper.selectWrongQuestionReviewList(companyId, userId, bo);
        if (CollUtil.isEmpty(wrongList)) {
            return List.of();
        }

        // 3.组装返回给前端的错题信息
        Map<Long, HotTrainingWrongQuestionReviewVo> wrongByQuestionId = wrongList.stream()
                .filter(v -> v.getQuestionId() != null)
                .collect(Collectors.toMap(HotTrainingWrongQuestionReviewVo::getQuestionId, Function.identity(), (a, b) -> a));

        List<HotTrainingWrongQuestionReviewVo> orderedWrong = new ArrayList<>();
        Set<Long> addedQuestionIds = new HashSet<>();
        for (ExamPaperQuestionDetailVo questionDetail : paperQuestions) {
            if (questionDetail == null || questionDetail.getQuestionId() == null) {
                continue;
            }
            Long questionId = questionDetail.getQuestionId();
            if (!addedQuestionIds.add(questionId)) {
                continue;
            }
            HotTrainingWrongQuestionReviewVo vo = wrongByQuestionId.get(questionId);
            if (vo == null) {
                continue;
            }
            vo.setQuestionType(questionDetail.getQuestionType());
            vo.setQuestionDesc(questionDetail.getQuestionDesc());
            vo.setImageUrl(questionDetail.getImageUrl());
            vo.setOptionA(questionDetail.getOptionA());
            vo.setOptionB(questionDetail.getOptionB());
            vo.setOptionC(questionDetail.getOptionC());
            vo.setOptionD(questionDetail.getOptionD());
            vo.setOptionE(questionDetail.getOptionE());
            vo.setOptionF(questionDetail.getOptionF());
            vo.setCorrectAnswers(questionDetail.getCorrectAnswers());
            vo.setAnalysis(questionDetail.getCaseDesc());
            orderedWrong.add(vo);
        }
        return orderedWrong;
    }

    private List<ExamPaperQuestionDetailVo> loadPlanPaperQuestionList(TrainingAnswerRecordBo bo) {
        if (bo == null || bo.getExamId() == null) {
            return List.of();
        }
        List<ExamPaperQuestionDetailVo> list = examPaperQuestionMapper.selectPaperQuestionDetailList(bo.getExamId());
        if (CollUtil.isEmpty(list)) {
            return List.of();
        }
        return list;
    }

    private void clearPlanAuditRecord(SubmitTrainingAnswerBo bo) {
        if (bo.getCompanyId() == null || bo.getTrainingId() == null || StringUtils.isBlank(bo.getUserId())) {
            return;
        }
        LambdaQueryWrapper<HotTrainingAudit> auditLqw = new LambdaQueryWrapper<>();
        auditLqw.eq(HotTrainingAudit::getCompanyId, bo.getCompanyId())
                .eq(HotTrainingAudit::getTrainingId, bo.getTrainingId())
                .eq(HotTrainingAudit::getUserId, bo.getUserId())
                .eq(HotTrainingAudit::getIsDeleted, 0L);
        trainingAuditMapper.delete(auditLqw);
    }

    private void completeTrainingTodo(Long trainingId, String userId) {
        if (trainingId == null || StringUtils.isBlank(userId)) {
            return;
        }
        try {
            String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_TRAINING_STUDY, String.valueOf(trainingId), userId);
            if (StringUtils.isBlank(taskId)) {
                return;
            }
            flowService.audit(taskId, true, "综合检测已提交", userId);
        } catch (Exception e) {
            log.warn("综合检测提交后完成待办失败 trainingId={} userId={} err={}", trainingId, userId, e.getMessage());
        }
    }

    private String resolveSubmitterName(SubmitTrainingAnswerBo bo) {
        if (StringUtils.isNotBlank(bo.getUserName())) {
            return bo.getUserName().trim();
        }
        if (bo.getCompanyId() != null && bo.getTrainingId() != null && StringUtils.isNotBlank(bo.getUserId())) {
            HotTrainingParticipant participant = trainingParticipantMapper.selectOne(
                    Wrappers.<HotTrainingParticipant>lambdaQuery()
                            .eq(HotTrainingParticipant::getCompanyId, bo.getCompanyId())
                            .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId())
                            .eq(HotTrainingParticipant::getUserId, bo.getUserId())
                            .eq(HotTrainingParticipant::getIsDeleted, 0L)
                            .last("limit 1")
            );
            if (participant != null && StringUtils.isNotBlank(participant.getUserName())) {
                return participant.getUserName().trim();
            }
        }
        throw new ServiceException("无法获取提交人姓名，签名校验失败");
    }

    /**
     * 累加错误次数并拼接最新的错误答案
     * 拼接后的总长度控制在200字符内，保留最新产生的数据（先不要）
     *
     * @param entity   当前传入的答题实体
     * @param existing 数据库中已存在的答题实体
     */
    private void mergeErrorData(HotTrainingAnswerRecord entity, HotTrainingAnswerRecord existing) {
        // 1. 累加错误次数，前端提交一次答题，只会加一次
        int currentErrorCount = entity.getErrorCount() != null ? 1 : 0;
        int existingErrorCount = existing.getErrorCount() != null ? existing.getErrorCount() : 0;
        entity.setErrorCount(existingErrorCount + currentErrorCount);
    }
}
