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<Long> 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<HrmTrainingDO> 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<Long> 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<Long> 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<HrmTrainingParticipantDO> getParticipantsByTrainingId(Long trainingId) {
|
return participantMapper.selectListByTrainingId(trainingId);
|
}
|
|
@Override
|
public Map<Long, Integer> getParticipantCountMap(Collection<Long> 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<HrmTrainingParticipantDO> 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);
|
}
|
}
|
|
}
|