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

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.fasterxml.jackson.core.type.TypeReference;
import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo;
import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper;
import com.hotwj.platform.flow.service.ISysFlowService;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
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.trainingParticipant.domain.HotTrainingParticipant;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantAddBo;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.DriverLearningRecordVo;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.TrainingParticipantStatVo;
import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper;
import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
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.math.RoundingMode;
import java.util.*;

/**
 * 培训参与人员Service业务层处理
 *
 * @author shihongwei
 * @date 2025-12-26
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class HotTrainingParticipantServiceImpl implements IHotTrainingParticipantService {

    private static final String FLOW_CODE_TRAINING_STUDY = "TRAINING_STUDY";
    private final HotTrainingParticipantMapper baseMapper;
    private final IHotSystemNotificationService notificationService;
    private final ISysFlowService flowService;
    private final HotTrainingMapper trainingMapper;
    private final HotCompanySafetyManagerMapper managerMapper;
    private final HotDriverMapper driverMapper;

    /**
     * 查询培训参与人员
     *
     * @param id 主键
     * @return 培训参与人员
     */
    @Override
    public HotTrainingParticipantVo queryById(Long id) {
        HotTrainingParticipantVo vo = baseMapper.selectVoById(id);
        fillCanAuditStatus(vo);
        return vo;
    }

    /**
     * 分页查询培训参与人员列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 培训参与人员分页列表
     */
    @Override
    public TableDataInfo<HotTrainingParticipantVo> queryPageList(HotTrainingParticipantBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HotTrainingParticipant> lqw = buildQueryWrapper(bo);
        Page<HotTrainingParticipantVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        if (result.getRecords() != null) {
            result.getRecords().forEach(this::fillCanAuditStatus);
            result.getRecords().forEach(this::fillDriverPhotoImgUrl);

        }
        return TableDataInfo.build(result);
    }

    @Override
    public TableDataInfo<DriverLearningRecordVo> queryDriverLearningRecordPage(Long companyId, String userId, PageQuery pageQuery) {
        Page<DriverLearningRecordVo> page = pageQuery.build();
        List<DriverLearningRecordVo> rows = baseMapper.selectDriverLearningRecordPage(page, companyId, userId, new Date());
        page.setRecords(rows);
        return TableDataInfo.build(page);
    }

    @Override
    public List<TrainingParticipantStatVo> queryTrainingParticipantStats(List<Long> trainingIds) {
        if (trainingIds == null || trainingIds.isEmpty()) {
            return List.of();
        }
        return baseMapper.selectTrainingParticipantStats(trainingIds);
    }

    /**
     * 查询符合条件的培训参与人员列表
     *
     * @param bo 查询条件
     * @return 培训参与人员列表
     */
    @Override
    public List<HotTrainingParticipantVo> queryList(HotTrainingParticipantBo bo) {
        LambdaQueryWrapper<HotTrainingParticipant> lqw = buildQueryWrapper(bo);
        List<HotTrainingParticipantVo> list = baseMapper.selectVoList(lqw);
        if (list != null) {
            list.forEach(this::fillCanAuditStatus);
        }
        return list;
    }

    private void fillCanAuditStatus(HotTrainingParticipantVo vo) {
        if (vo == null) {
            return;
        }
        boolean canAudit = Long.valueOf(1L).equals(vo.getIsCompleted()) && vo.getIsPass() == null;
        vo.setCanAudit(canAudit ? 1L : 0L);
    }

    private void fillDriverPhotoImgUrl(HotTrainingParticipantVo vo) {
        if (vo == null) {
            return;
        }
        vo.setDriverPhotoImgUrl(resolveStudyPhotoImgUrl(vo.getStudyPhotoOssId()));
    }

    private String resolveStudyPhotoImgUrl(String studyPhotoOssId) {
        if (StringUtils.isBlank(studyPhotoOssId)) {
            return null;
        }
        try {
            LinkedHashMap<String, Object> photoMap = JsonUtils.parseObject(
                    studyPhotoOssId,
                    new TypeReference<LinkedHashMap<String, Object>>() {
                    }
            );
            if (photoMap != null && !photoMap.isEmpty()) {
                String latestPhotoOssId = null;
                for (Object value : photoMap.values()) {
                    if (value != null && StringUtils.isNotBlank(String.valueOf(value))) {
                        latestPhotoOssId = String.valueOf(value);
                    }
                }
                return latestPhotoOssId;
            }
        } catch (Exception e) {
            log.warn("解析驾驶员学习拍照图片失败 studyPhotoOssId={} err={}", studyPhotoOssId, e.getMessage());
        }
        return studyPhotoOssId;
    }

    private LambdaQueryWrapper<HotTrainingParticipant> buildQueryWrapper(HotTrainingParticipantBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HotTrainingParticipant> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(HotTrainingParticipant::getId);
        lqw.eq(bo.getCompanyId() != null, HotTrainingParticipant::getCompanyId, bo.getCompanyId());
        lqw.eq(bo.getTrainingId() != null, HotTrainingParticipant::getTrainingId, bo.getTrainingId());
        lqw.eq(StringUtils.isNotBlank(bo.getUserId()), HotTrainingParticipant::getUserId, bo.getUserId());
        lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotTrainingParticipant::getUserName, bo.getUserName());
        lqw.eq(StringUtils.isNotBlank(bo.getGender()), HotTrainingParticipant::getGender, bo.getGender());
        lqw.eq(StringUtils.isNotBlank(bo.getIdCardNo()), HotTrainingParticipant::getIdCardNo, bo.getIdCardNo());
        lqw.eq(bo.getLearnProgress() != null, HotTrainingParticipant::getLearnProgress, bo.getLearnProgress());
        lqw.eq(bo.getIsCompleted() != null, HotTrainingParticipant::getIsCompleted, bo.getIsCompleted());
        lqw.eq(bo.getCompleteTime() != null, HotTrainingParticipant::getCompleteTime, bo.getCompleteTime());
        lqw.eq(bo.getIsDeleted() != null, HotTrainingParticipant::getIsDeleted, bo.getIsDeleted());
        lqw.eq(bo.getPersonType() != null, HotTrainingParticipant::getPersonType, bo.getPersonType());
        lqw.like(StringUtils.isNotBlank(bo.getPhone()), HotTrainingParticipant::getPhone, bo.getPhone());
        lqw.eq(bo.getIsPass() != null, HotTrainingParticipant::getIsPass, bo.getIsPass());
        if (bo.getCanAudit() != null) {
            if (Long.valueOf(1L).equals(bo.getCanAudit())) {
                lqw.eq(HotTrainingParticipant::getIsCompleted, 1L)
                        .isNull(HotTrainingParticipant::getIsPass);
            } else if (Long.valueOf(0L).equals(bo.getCanAudit())) {
                lqw.and(w -> w.ne(HotTrainingParticipant::getIsCompleted, 1L)
                        .or()
                        .isNotNull(HotTrainingParticipant::getIsPass)
                        .or()
                        .isNull(HotTrainingParticipant::getIsCompleted));
            }
        }
        lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingParticipant::getCreateByName, bo.getCreateByName());
        lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingParticipant::getUpdateByName, bo.getUpdateByName());
        return lqw;
    }

    /**
     * 新增培训参与人员
     *
     * @param bo 培训参与人员
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(HotTrainingParticipantBo bo) {
        if (bo.getLearnProgress() != null) {
            bo.setLearnProgress(limitLearnProgress(bo.getLearnProgress()));
        }
        HotTrainingParticipant add = MapstructUtils.convert(bo, HotTrainingParticipant.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            HotTrainingVo training = trainingMapper.selectVoById(add.getTrainingId());
            String planName = training != null ? training.getPlanName() : "培训";
            pushTrainingTodo(add.getTrainingId(), add.getCompanyId(), add.getUserId(), planName);
            sendTrainingNotificationIfVisible(training, List.of(add.getUserId()), "驾驶员");
        }
        return flag;
    }

    /**
     * 修改培训参与人员
     *
     * @param bo 培训参与人员
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(HotTrainingParticipantBo bo) {
        if (bo.getLearnProgress() != null) {
            bo.setLearnProgress(limitLearnProgress(bo.getLearnProgress()));
        }
        HotTrainingParticipant before = null;
        if (bo.getId() != null) {
            before = baseMapper.selectById(bo.getId());
        }
        HotTrainingParticipant update = MapstructUtils.convert(bo, HotTrainingParticipant.class);
        validEntityBeforeSave(update);
        boolean ok = baseMapper.updateById(update) > 0;
        if (ok) {
            boolean notify = false;
            if (before != null) {
                boolean trainingChanged = update.getTrainingId() != null && !update.getTrainingId().equals(before.getTrainingId());
                boolean userChanged = update.getUserId() != null && !update.getUserId().equals(before.getUserId());
                boolean reopened = update.getIsCompleted() != null && update.getIsCompleted() == 0L && (before.getIsCompleted() == null || before.getIsCompleted() == 1L);
                boolean progressReset = update.getLearnProgress() != null && update.getLearnProgress().compareTo(java.math.BigDecimal.valueOf(100)) < 0 && (before.getLearnProgress() == null || before.getLearnProgress().compareTo(java.math.BigDecimal.valueOf(100)) >= 0);
                notify = trainingChanged || userChanged || reopened || progressReset;
            } else {
                notify = true;
            }
            if (notify) {
                Long resolvedTrainingId = update.getTrainingId();
                if (resolvedTrainingId == null && before != null) {
                    resolvedTrainingId = before.getTrainingId();
                }
                HotTrainingVo training = trainingMapper.selectVoById(resolvedTrainingId);
                String planName = training != null ? training.getPlanName() : "培训";
                String receiver = update.getUserId();
                if (org.dromara.common.core.utils.StringUtils.isNotBlank(receiver)) {
                    Long todoTrainingId = update.getTrainingId();
                    if (todoTrainingId == null && before != null) {
                        todoTrainingId = before.getTrainingId();
                    }
                    pushTrainingTodo(todoTrainingId, update.getCompanyId(), receiver, planName);
                    sendTrainingNotificationIfVisible(training, List.of(receiver), "驾驶员");
                }
            }
        }
        return ok;
    }

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

    private BigDecimal limitLearnProgress(BigDecimal value) {
        if (value == null) {
            return null;
        }
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        if (value.compareTo(BigDecimal.valueOf(100)) > 0) {
            return BigDecimal.valueOf(100);
        }
        return value;
    }

    /**
     * 批量添加培训参与人员
     *
     * @param bo 添加参数
     * @return 是否添加成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addParticipants(HotTrainingParticipantAddBo bo) {
        if (bo == null || bo.getTrainingId() == null || bo.getPersonList() == null || bo.getPersonList().isEmpty()) {
            return false;
        }

        HotTrainingVo training = trainingMapper.selectVoById(bo.getTrainingId());
        if (training == null) {
            throw new RuntimeException("培训计划不存在");
        }

        // 解析前端传入的人员列表，拆解出三份数据结构：
        // 1) userIdSet：去重后的所有用户ID（字符串形式，适用于驾驶员与管理员统一使用）
        // 2) userTypeMap：用户ID -> 人员类型(driver/manager)，用于分支逻辑
        // 3) userIdLongMap：仅管理员使用的ID映射（manager 的 Long 型数值ID），driver 不存入此表
        ParsedPersons parsed = parseParticipantItems(bo.getPersonList());
        Set<String> userIdSet = parsed.userIdSet;
        Map<String, String> userTypeMap = parsed.userTypeMap;
        Map<String, Long> userIdLongMap = parsed.managerIdMap;

        if (userIdSet.isEmpty()) {
            return true;
        }

        // 加载已存在的参与人员（含已删），构建 userId -> participant 的映射
        Map<String, HotTrainingParticipant> existingParticipantMap = loadExistingParticipants(bo.getTrainingId(), userIdSet);


        // 计算课程总数（作为汇总宽表的初始 totalCourses）

        List<HotTrainingParticipant> addList = new ArrayList<>();
        List<String> notifyDriverIds = new ArrayList<>();
        List<String> notifyManagerIds = new ArrayList<>();

        for (String userIdStr : userIdSet) {
            String type = userTypeMap.get(userIdStr);
            Long userIdLong = userIdLongMap.get(userIdStr); // 仅管理员使用

            // 查询人员基础信息（姓名/证件号/电话、人员类型）
            PersonInfo person = loadPersonInfo(type, userIdStr, userIdLong);
            if (person == null) {
                continue;
            }
            String userName = person.userName;
            String idCardNo = person.idCardNo;
            String phone = person.phone;
            Long personType = person.personType;

            HotTrainingParticipant existing = existingParticipantMap.get(userIdStr);
            boolean activated = false;
            if (existing == null) {
                // 新增参与人员（初始状态）
                HotTrainingParticipant participant = new HotTrainingParticipant();
                participant.setCompanyId(training.getCompanyId());
                participant.setTrainingId(bo.getTrainingId());
                participant.setUserId(userIdStr);
                participant.setUserName(userName);
                participant.setIdCardNo(idCardNo);
                participant.setPhone(phone);
                participant.setPersonType(personType);
                participant.setLearnProgress(BigDecimal.ZERO);
                participant.setIsCompleted(0L);
                participant.setIsDeleted(0L);
                participant.setIsPass(null);
                addList.add(participant);
                activated = true;
            } else if (existing.getIsDeleted() != null && existing.getIsDeleted() == 1L) {
                int updated = baseMapper.update(
                        null,
                        Wrappers.<HotTrainingParticipant>lambdaUpdate()
                                .set(HotTrainingParticipant::getIsDeleted, 0L)
                                .set(StringUtils.isNotBlank(userName), HotTrainingParticipant::getUserName, userName)
                                .set(StringUtils.isNotBlank(idCardNo), HotTrainingParticipant::getIdCardNo, idCardNo)
                                .set(StringUtils.isNotBlank(phone), HotTrainingParticipant::getPhone, phone)
                                .set(HotTrainingParticipant::getPersonType, personType)
                                .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId())
                                .set(HotTrainingParticipant::getLearnProgress, BigDecimal.ZERO)
                                .set(HotTrainingParticipant::getIsCompleted, 0L)
                                .set(HotTrainingParticipant::getCompleteTime, null)
                                .set(HotTrainingParticipant::getIsPass, null)
                                .set(HotTrainingParticipant::getSignatureOssId, null)
                                .set(HotTrainingParticipant::getStudyPhotoOssId, null)
                                .eq(HotTrainingParticipant::getUserId, userIdStr)
                                .eq(HotTrainingParticipant::getIsDeleted, 1L)
                );
                activated = updated > 0;
            }


            if (activated) {
                pushTrainingTodo(bo.getTrainingId(), training.getCompanyId(), userIdStr, training.getPlanName());
                if ("driver".equals(type)) {
                    notifyDriverIds.add(userIdStr);
                } else if ("manager".equals(type)) {
                    notifyManagerIds.add(userIdStr);
                }
            }
        }

        boolean success = true;
        if (!addList.isEmpty()) {
            success = baseMapper.insertBatch(addList);
        }

        if (!notifyManagerIds.isEmpty()) {
            sendTrainingNotificationIfVisible(training, notifyManagerIds, "管理员");
        }
        if (!notifyDriverIds.isEmpty()) {
            sendTrainingNotificationIfVisible(training, notifyDriverIds, "驾驶员");
        }

        return success;
    }

    /**
     * 解析前端传入的人员列表，构建用户ID集合/类型映射/管理员ID映射
     */
    private ParsedPersons parseParticipantItems(List<HotTrainingParticipantAddBo.ParticipantItem> items) {
        ParsedPersons result = new ParsedPersons();
        if (items == null) {
            return result;
        }
        for (HotTrainingParticipantAddBo.ParticipantItem item : items) {
            if (item == null || StringUtils.isBlank(item.getType()) || StringUtils.isBlank(item.getIds())) {
                continue;
            }
            String[] ids = item.getIds().split(",");
            for (String raw : ids) {
                if (StringUtils.isBlank(raw)) {
                    continue;
                }
                String uid = raw.trim();
                // 驾驶员ID为32位uuid字符串；管理员ID为Long数值
                if ("driver".equals(item.getType())) {
                    result.userIdSet.add(uid);
                    result.userTypeMap.put(uid, "driver");
                } else if ("manager".equals(item.getType())) {
                    try {
                        Long userIdLong = Long.valueOf(uid);
                        result.userIdSet.add(uid);
                        result.userTypeMap.put(uid, "manager");
                        result.managerIdMap.put(uid, userIdLong);
                    } catch (Exception e) {
                        log.warn("添加培训参与人员时管理员ID格式错误: {}", uid);
                    }
                } else {
                    log.warn("未知的人员类型: {}, id={}", item.getType(), uid);
                }
            }
        }
        return result;
    }

    /**
     * 查询人员基础信息（姓名/证件号/电话、人员类型）
     */
    private PersonInfo loadPersonInfo(String type, String userIdStr, Long userIdLong) {
        if ("driver".equals(type)) {
            HotDriverVo driver = driverMapper.selectVoById(userIdStr);
            if (driver == null) return null;
            PersonInfo p = new PersonInfo();
            p.userName = driver.getName();
            p.idCardNo = driver.getIdCardNo();
            p.phone = driver.getPhone();
            p.personType = 1L;
            return p;
        } else if ("manager".equals(type)) {
            if (userIdLong == null) return null;
            HotCompanySafetyManagerVo manager = managerMapper.selectVoById(userIdLong);
            if (manager == null) return null;
            PersonInfo p = new PersonInfo();
            p.userName = manager.getName();
            p.idCardNo = manager.getIdCardNo();
            p.phone = manager.getPhone();
            p.personType = 2L;
            return p;
        }
        return null;
    }

    /**
     * 加载已存在的参与人员（含已删除）
     */
    private Map<String, HotTrainingParticipant> loadExistingParticipants(Long trainingId, Set<String> userIdSet) {
        Map<String, HotTrainingParticipant> map = new HashMap<>();
        List<HotTrainingParticipant> list = baseMapper.selectList(
                Wrappers.<HotTrainingParticipant>lambdaQuery()
                        .eq(HotTrainingParticipant::getTrainingId, trainingId)
                        .in(HotTrainingParticipant::getUserId, userIdSet)
                        .in(HotTrainingParticipant::getIsDeleted, 0L, 1L)
        );
        if (list != null) {
            for (HotTrainingParticipant p : list) {
                if (p != null && StringUtils.isNotBlank(p.getUserId())) {
                    map.put(p.getUserId(), p);
                }
            }
        }
        return map;
    }

    private void pushTrainingTodo(Long trainingId, Long companyId, String userId, String planName) {
        if (trainingId == null || companyId == null || StringUtils.isBlank(userId)) {
            return;
        }
        String businessId = String.valueOf(trainingId);
        if (flowService.hasPendingTask(FLOW_CODE_TRAINING_STUDY, businessId, userId)) {
            return;
        }
        String title = "安全教育培训：" + StringUtils.blankToDefault(planName, "培训");
        try {
            flowService.pushDirectTodo(FLOW_CODE_TRAINING_STUDY, businessId, title, companyId, userId);
        } catch (Exception e) {
            log.warn("推送安全教育培训待办失败 trainingId={} userId={} err={}", trainingId, userId, e.getMessage());
        }
    }

    private void sendTrainingNotificationIfVisible(HotTrainingVo training, List<String> receiverIds, String receiverType) {
        if (!isTrainingStudyAccessible(training, new Date()) || receiverIds == null || receiverIds.isEmpty()) {
            return;
        }
        HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo();
        msg.setLevel("普通");
        msg.setSourceType("安全教育培训");
        msg.setSenderType("SYSTEM");
        msg.setReceiverType(receiverType);
        msg.setReceiverIds(receiverIds);
        msg.setContent("您有一个" + StringUtils.blankToDefault(training.getPlanName(), "培训") + "培训需要进行学习");
        msg.setIsDeleted(0L);
        try {
            notificationService.insertByBo(msg);
        } catch (Exception e) {
            log.warn("发送安全教育培训系统消息失败 trainingId={} receiverType={} count={} err={}",
                    training.getId(), receiverType, receiverIds.size(), e.getMessage());
        }
    }

    private boolean isTrainingStudyAccessible(HotTrainingVo training, Date now) {
        if (training == null || now == null) {
            return false;
        }
        if (!Long.valueOf(1L).equals(training.getIsEnabled())) {
            return false;
        }
        if (training.getStartTime() == null || training.getEndTime() == null) {
            return false;
        }
        if (training.getStartTime().after(now)) {
            return false;
        }
        if (!training.getEndTime().before(now)) {
            return true;
        }
        return Long.valueOf(1L).equals(training.getIsMakeUp());
    }

    /**
     * 人员信息载体（用于封装姓名/证件/电话/人员类型）
     */
    private static class PersonInfo {
        String userName;
        String idCardNo;
        String phone;
        Long personType;
    }

    /**
     * 解析人员ID结果载体
     */
    private static class ParsedPersons {
        Set<String> userIdSet = new HashSet<>();
        Map<String, String> userTypeMap = new HashMap<>();
        Map<String, Long> managerIdMap = new HashMap<>();
    }

    /**
     * 校验并批量删除培训参与人员信息
     *
     * @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 updateStudyPhotoOssId(Long participantId, Long trainingCourseId, Long studyPhotoOssId) {
        if (participantId == null || trainingCourseId == null || studyPhotoOssId == null) {
            return false;
        }
        HotTrainingParticipant participant = baseMapper.selectById(participantId);
        if (participant == null) {
            return false;
        }
        Map<Long, Long> photoMap = null;
        if (StringUtils.isNotBlank(participant.getStudyPhotoOssId())) {
            try {
                photoMap = JsonUtils.parseObject(participant.getStudyPhotoOssId(), new TypeReference<Map<Long, Long>>() {
                });
            } catch (Exception e) {
                photoMap = new HashMap<>();
            }
        }
        if (photoMap == null) {
            photoMap = new HashMap<>();
        }
        photoMap.put(trainingCourseId, studyPhotoOssId);
        participant.setStudyPhotoOssId(JsonUtils.toJsonString(photoMap));
        return baseMapper.updateById(participant) > 0;
    }

    @Override
    public Map<String, String> calculateProgressBatch(Long trainingId, Long companyId, List<String> userIds) {
        Map<String, String> result = new HashMap<>();
        if (trainingId == null || companyId == null) {
            return result;
        }

        HotTrainingParticipantBo participantBo = new HotTrainingParticipantBo();
        participantBo.setTrainingId(trainingId);
        participantBo.setCompanyId(companyId);
        participantBo.setIsDeleted(0L);
        List<HotTrainingParticipantVo> participants = queryList(participantBo);
        if (participants == null || participants.isEmpty()) {
            return result;
        }

        Set<String> userIdSet = null;
        if (userIds != null && !userIds.isEmpty()) {
            userIdSet = new HashSet<>(userIds.size());
            for (String uid : userIds) {
                if (StringUtils.isNotBlank(uid)) {
                    userIdSet.add(uid);
                }
            }
        }

        for (HotTrainingParticipantVo p : participants) {
            if (p == null || StringUtils.isBlank(p.getUserId())) {
                continue;
            }
            if (userIdSet != null && !userIdSet.contains(p.getUserId())) {
                continue;
            }

            BigDecimal progress = p.getLearnProgress() == null ? BigDecimal.ZERO : p.getLearnProgress();
            if (progress.compareTo(BigDecimal.ZERO) < 0) progress = BigDecimal.ZERO;
            if (progress.compareTo(BigDecimal.valueOf(100)) > 0) progress = BigDecimal.valueOf(100);

            BigDecimal rounded = progress.setScale(0, RoundingMode.HALF_UP);
            result.put(p.getUserId(), rounded.toPlainString() + "%");
        }

        return result;
    }
}
