package cn.iocoder.yudao.module.hrm.service.remind;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.certificate.HrmCertificateDO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.certificate.HrmCertificateMapper;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeContractMapper;
|
import cn.iocoder.yudao.module.hrm.dal.redis.HrmExpiryRemindRedisDAO;
|
import cn.iocoder.yudao.module.hrm.enums.HrmEmployeeStatusEnum;
|
import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService;
|
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
|
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
|
import java.time.LocalDate;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
|
/**
|
* HRM 证书/合同到期提醒 Service 实现类
|
*
|
* <p>参照 MES 计量器具到期提醒实现:Redis 日锁防多实例并发重复扫描、按业务记录+到期日去重,
|
* 向员工系统账号(hrm_employee.userId)发送站内信;提醒天数从 infra_config 读取,默认 30 天。
|
*
|
* @author 超级管理员
|
*/
|
@Slf4j
|
@Service
|
public class HrmExpiryRemindServiceImpl implements HrmExpiryRemindService {
|
|
/**
|
* 业务标识:证书资质
|
*/
|
private static final String BIZ_CERT = "cert";
|
/**
|
* 业务标识:员工合同
|
*/
|
private static final String BIZ_CONTRACT = "contract";
|
|
/**
|
* 证书到期提醒天数配置 KEY(infra_config)
|
*/
|
private static final String CERT_REMIND_DAYS_KEY = "hrm.certificate.remind-days";
|
/**
|
* 合同到期提醒天数配置 KEY(infra_config)
|
*/
|
private static final String CONTRACT_REMIND_DAYS_KEY = "hrm.contract.remind-days";
|
/**
|
* 提醒天数默认值
|
*/
|
private static final int DEFAULT_REMIND_DAYS = 30;
|
|
/**
|
* 证书到期提醒站内信模板编号
|
*/
|
private static final String CERT_REMIND_TEMPLATE = "hrm_certificate_expire_remind";
|
/**
|
* 合同到期提醒站内信模板编号
|
*/
|
private static final String CONTRACT_REMIND_TEMPLATE = "hrm_contract_expire_remind";
|
|
@Resource
|
private HrmCertificateMapper certificateMapper;
|
@Resource
|
private HrmEmployeeContractMapper contractMapper;
|
@Resource
|
private HrmExpiryRemindRedisDAO expiryRemindRedisDAO;
|
@Resource
|
private ConfigApi configApi;
|
@Resource
|
private NotifyMessageSendApi notifyMessageSendApi;
|
@Resource
|
private HrmEmployeeService employeeService;
|
|
@Override
|
public void sendAll() {
|
sendCertificateExpireRemind();
|
sendContractExpireRemind();
|
}
|
|
@Override
|
public void sendCertificateExpireRemind() {
|
try {
|
LocalDate today = LocalDate.now();
|
if (!expiryRemindRedisDAO.tryAcquireScanLock(BIZ_CERT, today)) {
|
return;
|
}
|
int remindDays = getRemindDays(CERT_REMIND_DAYS_KEY);
|
List<HrmCertificateDO> list = certificateMapper.selectListByExpireDateBetween(
|
today, today.plusDays(remindDays));
|
if (CollUtil.isEmpty(list)) {
|
return;
|
}
|
Map<Long, HrmEmployeeDO> employeeMap = buildEmployeeMap(
|
convertSet(list, HrmCertificateDO::getEmployeeId));
|
for (HrmCertificateDO certificate : list) {
|
if (certificate.getExpireDate() == null) {
|
continue;
|
}
|
HrmEmployeeDO employee = employeeMap.get(certificate.getEmployeeId());
|
if (!canNotify(employee)) {
|
continue;
|
}
|
if (!expiryRemindRedisDAO.tryMarkReminded(BIZ_CERT, certificate.getId(), certificate.getExpireDate())) {
|
continue;
|
}
|
sendCertificateRemind(certificate, employee);
|
}
|
} catch (Exception ex) {
|
// best-effort:提醒失败不影响列表查询/定时任务整体
|
log.error("[sendCertificateExpireRemind][发送证书到期提醒失败]", ex);
|
}
|
}
|
|
@Override
|
public void sendContractExpireRemind() {
|
try {
|
LocalDate today = LocalDate.now();
|
if (!expiryRemindRedisDAO.tryAcquireScanLock(BIZ_CONTRACT, today)) {
|
return;
|
}
|
int remindDays = getRemindDays(CONTRACT_REMIND_DAYS_KEY);
|
List<HrmEmployeeContractDO> list = contractMapper.selectListExpiringBetween(
|
today, today.plusDays(remindDays));
|
if (CollUtil.isEmpty(list)) {
|
return;
|
}
|
Map<Long, HrmEmployeeDO> employeeMap = buildEmployeeMap(
|
convertSet(list, HrmEmployeeContractDO::getEmployeeId));
|
for (HrmEmployeeContractDO contract : list) {
|
if (contract.getEndDate() == null) {
|
continue;
|
}
|
HrmEmployeeDO employee = employeeMap.get(contract.getEmployeeId());
|
if (!canNotify(employee)) {
|
continue;
|
}
|
if (!expiryRemindRedisDAO.tryMarkReminded(BIZ_CONTRACT, contract.getId(), contract.getEndDate())) {
|
continue;
|
}
|
sendContractRemind(contract, employee);
|
}
|
} catch (Exception ex) {
|
// best-effort:提醒失败不影响列表查询/定时任务整体
|
log.error("[sendContractExpireRemind][发送合同到期提醒失败]", ex);
|
}
|
}
|
|
private boolean canNotify(HrmEmployeeDO employee) {
|
// 仅向有系统账号且在岗/试用员工发送提醒;离职员工(账号多已停用)不打扰
|
return employee != null && employee.getUserId() != null
|
&& !HrmEmployeeStatusEnum.LEAVE.getStatus().equals(employee.getEmployeeStatus());
|
}
|
|
private void sendCertificateRemind(HrmCertificateDO certificate, HrmEmployeeDO employee) {
|
Map<String, Object> params = new HashMap<>();
|
params.put("employeeName", StrUtil.nullToEmpty(employee.getName()));
|
params.put("certTypeName", StrUtil.nullToEmpty(convertCertTypeName(certificate.getCertType())));
|
params.put("certName", StrUtil.nullToEmpty(certificate.getCertName()));
|
params.put("expireDate", StrUtil.nullToEmpty(certificate.getExpireDate() == null ? null : certificate.getExpireDate().toString()));
|
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
|
.setUserId(employee.getUserId())
|
.setTemplateCode(CERT_REMIND_TEMPLATE)
|
.setTemplateParams(params));
|
}
|
|
private void sendContractRemind(HrmEmployeeContractDO contract, HrmEmployeeDO employee) {
|
Map<String, Object> params = new HashMap<>();
|
params.put("employeeName", StrUtil.nullToEmpty(employee.getName()));
|
params.put("contractNo", StrUtil.nullToEmpty(contract.getContractNo()));
|
params.put("endDate", StrUtil.nullToEmpty(contract.getEndDate() == null ? null : contract.getEndDate().toString()));
|
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
|
.setUserId(employee.getUserId())
|
.setTemplateCode(CONTRACT_REMIND_TEMPLATE)
|
.setTemplateParams(params));
|
}
|
|
private Map<Long, HrmEmployeeDO> buildEmployeeMap(java.util.Collection<Long> employeeIds) {
|
if (CollUtil.isEmpty(employeeIds)) {
|
return Map.of();
|
}
|
return employeeService.getEmployeeList(employeeIds).stream()
|
.collect(java.util.stream.Collectors.toMap(HrmEmployeeDO::getId, e -> e, (a, b) -> a));
|
}
|
|
private int getRemindDays(String configKey) {
|
String value = configApi.getConfigValueByKey(configKey);
|
if (StrUtil.isBlank(value)) {
|
return DEFAULT_REMIND_DAYS;
|
}
|
try {
|
return Integer.parseInt(value.trim());
|
} catch (NumberFormatException ex) {
|
log.warn("[getRemindDays][配置 {} 的值 {} 非法,使用默认值 {}]", configKey, value, DEFAULT_REMIND_DAYS);
|
return DEFAULT_REMIND_DAYS;
|
}
|
}
|
|
private String convertCertTypeName(Integer certType) {
|
return switch (certType == null ? 0 : certType) {
|
case 1 -> "特种作业证书";
|
case 2 -> "从业资质";
|
case 3 -> "技能等级证书";
|
case 4 -> "安全培训合格证";
|
default -> "证书";
|
};
|
}
|
|
}
|