package cn.iocoder.yudao.module.hrm.service.training; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.hrm.controller.admin.training.vo.HrmTrainingPageReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.training.vo.HrmTrainingParticipantUpdateReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.training.vo.HrmTrainingSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.certificate.HrmCertificateDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.training.HrmTrainingDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.training.HrmTrainingParticipantDO; import cn.iocoder.yudao.module.hrm.dal.mysql.certificate.HrmCertificateMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.training.HrmTrainingMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.training.HrmTrainingParticipantMapper; import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDate; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.hrm.dal.dataobject.training.HrmTrainingDO.*; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; /** * HRM 培训活动 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class HrmTrainingServiceImpl implements HrmTrainingService { @Resource private HrmTrainingMapper trainingMapper; @Resource private HrmTrainingParticipantMapper participantMapper; @Resource private HrmCertificateMapper certificateMapper; @Resource private HrmNoRedisDAO noRedisDAO; @Override public Long createTraining(HrmTrainingSaveReqVO createReqVO) { validateIssueCertificate(createReqVO.getTrainingType(), createReqVO.getIssueCertificate(), createReqVO.getCertValidityMonths()); HrmTrainingDO training = BeanUtils.toBean(createReqVO, HrmTrainingDO.class); training.setId(null); training.setTrainingNo(noRedisDAO.generateTrainingNo()); training.setStatus(STATUS_DRAFT); trainingMapper.insert(training); return training.getId(); } @Override public void updateTraining(HrmTrainingSaveReqVO updateReqVO) { HrmTrainingDO training = validateTrainingExists(updateReqVO.getId()); ensureMutable(training); validateIssueCertificate(updateReqVO.getTrainingType(), updateReqVO.getIssueCertificate(), updateReqVO.getCertValidityMonths()); HrmTrainingDO update = BeanUtils.toBean(updateReqVO, HrmTrainingDO.class); update.setTrainingNo(training.getTrainingNo()); // 编号不可改 update.setStatus(null); // 状态走 start/complete/cancel 流转,不在此修改 trainingMapper.updateById(update); } @Override public void deleteTraining(Collection ids) { if (CollUtil.isEmpty(ids)) { return; } for (Long id : ids) { HrmTrainingDO training = validateTrainingExists(id); ensureMutable(training); participantMapper.deleteByTrainingId(id); trainingMapper.deleteById(id); } } @Override public HrmTrainingDO getTraining(Long id) { return trainingMapper.selectById(id); } @Override public PageResult getTrainingPage(HrmTrainingPageReqVO pageReqVO) { return trainingMapper.selectPage(pageReqVO); } @Override public void startTraining(Long id) { HrmTrainingDO training = validateTrainingExists(id); if (training.getStatus() != null && !training.getStatus().equals(STATUS_DRAFT)) { throw exception(TRAINING_COMPLETE_FAIL_STATUS); // 仅草稿可开始 } trainingMapper.updateById(HrmTrainingDO.builder().id(id).status(STATUS_PLANNED).build()); } @Override @Transactional(rollbackFor = Exception.class) public void completeTraining(Long id) { HrmTrainingDO training = validateTrainingExists(id); if (training.getStatus() == null || (!training.getStatus().equals(STATUS_DRAFT) && !training.getStatus().equals(STATUS_PLANNED))) { throw exception(TRAINING_COMPLETE_FAIL_STATUS); } trainingMapper.updateById(HrmTrainingDO.builder().id(id).status(STATUS_COMPLETED).build()); // 联动发证:培训完成后对合格人员自动生成证书(幂等,二次完成不重复发) if (Boolean.TRUE.equals(training.getIssueCertificate())) { issueCertificatesForPassed(training); } } @Override public void cancelTraining(Long id) { HrmTrainingDO training = validateTrainingExists(id); if (training.getStatus() == null || training.getStatus().equals(STATUS_COMPLETED) || training.getStatus().equals(STATUS_CANCELED)) { throw exception(TRAINING_LOCKED); } trainingMapper.updateById(HrmTrainingDO.builder().id(id).status(STATUS_CANCELED).build()); } @Override @Transactional(rollbackFor = Exception.class) public int addParticipants(Long trainingId, Collection employeeIds) { HrmTrainingDO training = validateTrainingExists(trainingId); ensureMutable(training); if (CollUtil.isEmpty(employeeIds)) { return 0; } int count = 0; for (Long employeeId : employeeIds) { if (participantMapper.selectByTrainingIdAndEmployeeId(trainingId, employeeId) != null) { continue; // 已在名单中,跳过不重复添加 } HrmTrainingParticipantDO participant = HrmTrainingParticipantDO.builder() .trainingId(trainingId) .employeeId(employeeId) .attended(false) .isPass(false) .build(); participantMapper.insert(participant); count++; } return count; } @Override @Transactional(rollbackFor = Exception.class) public void updateParticipant(HrmTrainingParticipantUpdateReqVO updateReqVO) { HrmTrainingParticipantDO participant = validateParticipantExists(updateReqVO.getId()); HrmTrainingDO training = validateTrainingExists(participant.getTrainingId()); ensureMutable(training); HrmTrainingParticipantDO update = HrmTrainingParticipantDO.builder() .id(participant.getId()) .attended(updateReqVO.getAttended()) .score(updateReqVO.getScore()) .isPass(updateReqVO.getIsPass()) .remark(updateReqVO.getRemark()) .build(); participantMapper.updateById(update); } @Override @Transactional(rollbackFor = Exception.class) public void deleteParticipants(Collection ids) { if (CollUtil.isEmpty(ids)) { return; } for (Long id : ids) { HrmTrainingParticipantDO participant = validateParticipantExists(id); HrmTrainingDO training = validateTrainingExists(participant.getTrainingId()); ensureMutable(training); participantMapper.deleteById(id); } } @Override public List getParticipantsByTrainingId(Long trainingId) { return participantMapper.selectListByTrainingId(trainingId); } @Override public Map getParticipantCountMap(Collection trainingIds) { if (CollUtil.isEmpty(trainingIds)) { return Collections.emptyMap(); } return participantMapper.selectListByTrainingIds(trainingIds).stream() .collect(Collectors.groupingBy(HrmTrainingParticipantDO::getTrainingId, Collectors.summingInt(p -> 1))); } // ========== 私有方法 ========== private void issueCertificatesForPassed(HrmTrainingDO training) { if (training.getCertValidityMonths() == null || training.getCertValidityMonths() <= 0) { throw exception(TRAINING_CERT_VALIDITY_REQUIRED); } List participants = participantMapper.selectListByTrainingId(training.getId()); if (CollUtil.isEmpty(participants)) { return; } Integer certType = convertToCertificateType(training.getTrainingType()); LocalDate issueDate = LocalDate.now(); LocalDate expireDate = issueDate.plusMonths(training.getCertValidityMonths()); for (HrmTrainingParticipantDO participant : participants) { if (!Boolean.TRUE.equals(participant.getIsPass()) || participant.getCertificateId() != null) { continue; } HrmCertificateDO certificate = HrmCertificateDO.builder() .employeeId(participant.getEmployeeId()) .certType(certType) .certName(training.getName()) .issueDate(issueDate) .expireDate(expireDate) .remark("由培训【" + training.getTrainingNo() + "】" + training.getName() + "完成自动发放") .build(); certificateMapper.insert(certificate); participantMapper.updateCertificateId(participant.getId(), certificate.getId()); } } /** * 培训类型 → 证书资质类型映射:安全培训→4 安全培训合格证,技能培训→3 技能等级证书 */ private Integer convertToCertificateType(Integer trainingType) { return switch (trainingType == null ? 0 : trainingType) { case TYPE_SAFETY -> 4; case TYPE_SKILL -> 3; default -> null; }; } private void validateIssueCertificate(Integer trainingType, Boolean issueCertificate, Integer certValidityMonths) { if (Boolean.TRUE.equals(issueCertificate)) { if (trainingType == null || (!trainingType.equals(TYPE_SAFETY) && !trainingType.equals(TYPE_SKILL))) { throw exception(TRAINING_CERT_TYPE_NOT_SUPPORT); } if (certValidityMonths == null || certValidityMonths <= 0) { throw exception(TRAINING_CERT_VALIDITY_REQUIRED); } } } private HrmTrainingDO validateTrainingExists(Long id) { HrmTrainingDO training = trainingMapper.selectById(id); if (training == null) { throw exception(TRAINING_NOT_EXISTS); } return training; } private HrmTrainingParticipantDO validateParticipantExists(Long id) { HrmTrainingParticipantDO participant = participantMapper.selectById(id); if (participant == null) { throw exception(TRAINING_PARTICIPANT_NOT_EXISTS); } return participant; } /** * 已完成/已取消的培训不允许再修改 */ private void ensureMutable(HrmTrainingDO training) { if (training.getStatus() != null && (training.getStatus().equals(STATUS_COMPLETED) || training.getStatus().equals(STATUS_CANCELED))) { throw exception(TRAINING_LOCKED); } } }