From ce5f8a87bb5d411bae67c39b269d879677bb98da Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 08 十一月 2026 19:29:25 +0800
Subject: [PATCH] feat(hrm): 请假导入、加班代申请、批量排班与考勤应出勤重算
---
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 331 insertions(+), 23 deletions(-)
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java
index cec35b5..14b5075 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java
@@ -2,19 +2,27 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmLeaveApplicationImportExcelVO;
+import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmLeaveApplicationImportFailureVO;
+import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmLeaveApplicationImportRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmLeaveApplicationPageReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmLeaveApplicationRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmLeaveApplicationSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO;
+import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmLeaveApplicationMapper;
+import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper;
import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO;
import cn.iocoder.yudao.module.hrm.enums.HrmAuditStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.HrmLeaveTypeEnum;
@@ -30,6 +38,12 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -37,6 +51,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -53,8 +68,12 @@
@Slf4j
public class HrmLeaveApplicationServiceImpl implements HrmLeaveApplicationService {
+ private static final DateTimeFormatter LEAVE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+
@Resource
private HrmLeaveApplicationMapper leaveApplicationMapper;
+ @Resource
+ private HrmEmployeeMapper employeeMapper;
@Resource
private HrmNoRedisDAO noRedisDAO;
@Resource
@@ -73,22 +92,33 @@
@Override
@Transactional(rollbackFor = Exception.class)
public Long createLeaveApplication(HrmLeaveApplicationSaveReqVO createReqVO) {
- // 1. 鐢熸垚鐢宠鍗曞彿
- String no = generateUniqueNo();
- // 2. 璁剧疆鐢宠浜轰俊鎭�
+ // 1. 纭鐢宠浜哄凡鍏宠仈鍛樺伐妗f
Long userId = createReqVO.getUserId();
if (userId == null) {
userId = getCurrentUserId();
}
AdminUserRespDTO user = adminUserApi.getUser(userId);
if (user == null) {
- userId = getCurrentUserId();
+ throw exception(LEAVE_APPLICATION_APPLICANT_NOT_EXISTS);
}
+ HrmEmployeeDO employee = employeeMapper.selectByUserId(userId);
+ if (employee == null) {
+ throw exception(Objects.equals(userId, getCurrentUserId())
+ ? LEAVE_APPLICATION_EMPLOYEE_NOT_EXISTS : LEAVE_APPLICATION_SELECTED_EMPLOYEE_NOT_EXISTS);
+ }
+ if (employee.getDeptId() == null) {
+ throw exception(LEAVE_APPLICATION_EMPLOYEE_DEPT_NOT_FOUND);
+ }
+ if (createReqVO.getDeptId() != null && !Objects.equals(createReqVO.getDeptId(), employee.getDeptId())) {
+ throw exception(LEAVE_APPLICATION_DEPT_MISMATCH);
+ }
+ // 2. 鐢熸垚鐢宠鍗曞彿
+ String no = generateUniqueNo();
// 3. 鎻掑叆
HrmLeaveApplicationDO application = BeanUtils.toBean(createReqVO, HrmLeaveApplicationDO.class);
application.setNo(no);
application.setUserId(userId);
- application.setDeptId(createReqVO.getDeptId() != null ? createReqVO.getDeptId() : user.getDeptId());
+ application.setDeptId(employee.getDeptId());
application.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
leaveApplicationMapper.insert(application);
// 缁戝畾闄勪欢
@@ -96,6 +126,242 @@
application.getId(), createReqVO.getBlobIds());
return application.getId();
}
+
+ @Override
+ public HrmLeaveApplicationImportRespVO importLeaveApplications(List<HrmLeaveApplicationImportExcelVO> list) {
+ if (CollUtil.isEmpty(list)) {
+ throw exception(LEAVE_APPLICATION_IMPORT_FILE_EMPTY);
+ }
+ int totalCount = 0;
+ int successCount = 0;
+ List<HrmLeaveApplicationImportFailureVO> failures = new ArrayList<>();
+ for (int index = 0; index < list.size(); index++) {
+ HrmLeaveApplicationImportExcelVO row = list.get(index);
+ // Excel 绗� 1 琛屾槸琛ㄥご锛岀 index 鏉℃暟鎹搴旂 index + 2 琛�
+ int rowNum = index + 2;
+ if (isBlankRow(row)) {
+ continue;
+ }
+ totalCount++;
+ try {
+ leaveApplicationMapper.insert(buildImportedDraft(row));
+ successCount++;
+ } catch (LeaveImportRowException e) {
+ failures.add(new HrmLeaveApplicationImportFailureVO(rowNum, trimToNull(row.getPhone()),
+ trimToNull(row.getEmployeeName()), e.getMessage()));
+ } catch (Exception e) {
+ log.error("[importLeaveApplications][绗� {} 琛屽鍏ュけ璐", rowNum, e);
+ failures.add(new HrmLeaveApplicationImportFailureVO(rowNum, trimToNull(row.getPhone()),
+ trimToNull(row.getEmployeeName()),
+ "鍐欏叆鏁版嵁搴撳け璐ワ細" + StrUtil.blankToDefault(e.getMessage(), e.getClass().getSimpleName())
+ + "锛涜妫�鏌ヨ鍋囧師鍥�/澶囨敞闀垮害鍚庨噸鏂板鍏ヨ琛岋紝浠嶅け璐ヨ鑱旂郴绠$悊鍛�"));
+ }
+ }
+ if (totalCount == 0) {
+ throw exception(LEAVE_APPLICATION_IMPORT_FILE_EMPTY);
+ }
+ return HrmLeaveApplicationImportRespVO.builder()
+ .totalCount(totalCount)
+ .successCount(successCount)
+ .failureCount(failures.size())
+ .failures(failures)
+ .build();
+ }
+
+ private boolean isBlankRow(HrmLeaveApplicationImportExcelVO row) {
+ return row == null || (StrUtil.isBlank(row.getPhone()) && StrUtil.isBlank(row.getEmployeeName())
+ && StrUtil.isBlank(row.getLeaveTypeName()) && StrUtil.isBlank(row.getStartTime())
+ && StrUtil.isBlank(row.getEndTime()) && StrUtil.isBlank(row.getDuration())
+ && StrUtil.isBlank(row.getReason()));
+ }
+
+ /**
+ * 灏嗕竴琛� Excel 鏁版嵁杞崲涓鸿鍋囩敵璇疯崏绋匡紱浠讳竴鏍¢獙涓嶉�氳繃鏃舵姏鍑哄甫绮剧‘鍘熷洜鐨� {@link LeaveImportRowException}
+ */
+ private HrmLeaveApplicationDO buildImportedDraft(HrmLeaveApplicationImportExcelVO row) {
+ // 1. 鎵嬫満鍙峰尮閰嶅憳宸ユ。妗�
+ String phone = normalizeCell(row.getPhone());
+ if (StrUtil.isBlank(phone)) {
+ throw new LeaveImportRowException("鎵嬫満鍙蜂负绌猴紝璇峰~鍐欏憳宸ユ。妗堜腑鐧昏鐨� 11 浣嶆墜鏈哄彿");
+ }
+ if (!PHONE_PATTERN.matcher(phone).matches()) {
+ throw new LeaveImportRowException("鎵嬫満鍙穂" + phone + "]涓嶆槸鏈夋晥鐨� 11 浣嶆墜鏈哄彿锛岃妫�鏌ユ槸鍚﹀濉簡绌烘牸銆佺鍙锋垨灏忔暟浣�");
+ }
+ HrmEmployeeDO employee = employeeMapper.selectByPhone(phone);
+ if (employee == null) {
+ throw new LeaveImportRowException("鎵嬫満鍙穂" + phone + "]鏈尮閰嶅埌鍛樺伐妗f锛岃鍏堝湪銆屽憳宸ユ。妗堛�嶄腑缁存姢璇ユ墜鏈哄彿鍚庨噸鏂板鍏�");
+ }
+ String inputName = trimToNull(row.getEmployeeName());
+ if (inputName != null && !Objects.equals(inputName, StrUtil.trim(employee.getName()))) {
+ throw new LeaveImportRowException("鎵嬫満鍙穂" + phone + "]瀵瑰簲鍛樺伐涓篬" + employee.getName()
+ + "]锛屼笌濉啓鐨勫憳宸ュ鍚峓" + inputName + "]涓嶄竴鑷达紝璇锋牳瀵规墜鏈哄彿鍚庨噸鏂板鍏�");
+ }
+ if (employee.getUserId() == null) {
+ throw new LeaveImportRowException("鍛樺伐妗f[" + employee.getName() + "]锛堟墜鏈哄彿 " + phone
+ + "锛夋湭鍏宠仈绯荤粺鐧诲綍璐﹀彿锛岃鍏堝湪鍛樺伐妗f涓粦瀹氳处鍙峰悗閲嶆柊瀵煎叆");
+ }
+ if (employee.getDeptId() == null) {
+ throw new LeaveImportRowException("鍛樺伐妗f[" + employee.getName()
+ + "]鏈缃儴闂紝璇峰厛鍦ㄥ憳宸ユ。妗堜腑琛ュ叏閮ㄩ棬鍚庨噸鏂板鍏�");
+ }
+ // 2. 璇峰亣绫诲瀷
+ Integer leaveType = parseLeaveType(row.getLeaveTypeName());
+ // 3. 璧锋鏃堕棿涓庢椂闀�
+ LocalDateTime startTime = parseDateTime("寮�濮嬫椂闂�", row.getStartTime());
+ LocalDateTime endTime = parseDateTime("缁撴潫鏃堕棿", row.getEndTime());
+ if (!endTime.isAfter(startTime)) {
+ throw new LeaveImportRowException("缁撴潫鏃堕棿[" + formatDateTime(endTime) + "]蹇呴』鏅氫簬寮�濮嬫椂闂碵"
+ + formatDateTime(startTime) + "]锛岃璋冩暣鍚庨噸鏂板鍏�");
+ }
+ BigDecimal duration = parseDuration(row.getDuration(), startTime, endTime);
+ // 4. 璇峰亣鍘熷洜
+ String reason = trimToNull(row.getReason());
+ if (reason == null) {
+ throw new LeaveImportRowException("璇峰亣鍘熷洜涓虹┖锛岃濉啓璇峰亣浜嬬敱锛堝皢灞曠ず鍦ㄥ鎵瑰崟涓婏級");
+ }
+ if (reason.length() > 500) {
+ throw new LeaveImportRowException("璇峰亣鍘熷洜闀垮害涓� " + reason.length()
+ + " 涓瓧绗︼紝瓒呰繃 500 瀛椾笂闄愶紝璇风簿绠�鍚庨噸鏂板鍏�");
+ }
+ String remark = trimToNull(row.getRemark());
+ if (remark != null && remark.length() > 500) {
+ throw new LeaveImportRowException("澶囨敞闀垮害涓� " + remark.length()
+ + " 涓瓧绗︼紝瓒呰繃 500 瀛椾笂闄愶紝璇风簿绠�鍚庨噸鏂板鍏�");
+ }
+ // 5. 缁勮鑽夌锛堜笉鎻愪氦瀹℃壒锛�
+ HrmLeaveApplicationDO application = new HrmLeaveApplicationDO();
+ application.setNo(generateUniqueNo());
+ application.setUserId(employee.getUserId());
+ application.setDeptId(employee.getDeptId());
+ application.setLeaveType(leaveType);
+ application.setStartTime(startTime);
+ application.setEndTime(endTime);
+ application.setDuration(duration);
+ application.setReason(reason);
+ application.setRemark(remark);
+ application.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
+ return application;
+ }
+
+ private Integer parseLeaveType(String raw) {
+ String value = normalizeCell(raw);
+ if (StrUtil.isBlank(value)) {
+ throw new LeaveImportRowException("璇峰亣绫诲瀷涓虹┖锛岃濉啓锛氫簨鍋囥�佺梾鍋囥�佸勾鍋囥�佸鍋囥�佷骇鍋囥�佷抚鍋囷紙鎴栨暟瀛� 1-6锛�");
+ }
+ for (HrmLeaveTypeEnum type : HrmLeaveTypeEnum.values()) {
+ if (StrUtil.equals(value, type.getName()) || StrUtil.equals(value, String.valueOf(type.getType()))) {
+ return type.getType();
+ }
+ }
+ throw new LeaveImportRowException("璇峰亣绫诲瀷[" + value
+ + "]涓嶅湪鍙�夎寖鍥村唴锛岃濉啓锛氫簨鍋囥�佺梾鍋囥�佸勾鍋囥�佸鍋囥�佷骇鍋囥�佷抚鍋囷紙鎴栨暟瀛� 1-6锛�");
+ }
+
+ private LocalDateTime parseDateTime(String field, String raw) {
+ String value = normalizeCell(raw);
+ if (StrUtil.isBlank(value)) {
+ throw new LeaveImportRowException(field + "涓虹┖锛岃濉啓鏍煎紡濡� 2026-11-10 09:00 鐨勬椂闂�");
+ }
+ // 鍏煎 Excel 鎶婃棩鏈熷瓨鎴愭暟瀛楀簭鍒楀彿锛堝 46000.375锛�
+ if (EXCEL_SERIAL_PATTERN.matcher(value).matches()) {
+ double serial = Double.parseDouble(value);
+ if (serial > 20000 && serial < 80000) {
+ return LocalDateTime.ofInstant(
+ org.apache.poi.ss.usermodel.DateUtil.getJavaDate(serial).toInstant(), ZoneId.systemDefault());
+ }
+ }
+ for (DateTimeFormatter formatter : DATE_TIME_FORMATTERS) {
+ try {
+ return LocalDateTime.parse(value, formatter);
+ } catch (DateTimeParseException ignored) {
+ // 灏濊瘯涓嬩竴绉嶆牸寮�
+ }
+ }
+ // 鍙~鏃ユ湡鏃舵寜褰撳ぉ 00:00 澶勭悊
+ for (DateTimeFormatter formatter : DATE_ONLY_FORMATTERS) {
+ try {
+ return java.time.LocalDate.parse(value, formatter).atStartOfDay();
+ } catch (DateTimeParseException ignored) {
+ // 灏濊瘯涓嬩竴绉嶆牸寮�
+ }
+ }
+ throw new LeaveImportRowException(field + "[" + value
+ + "]鏍煎紡涓嶆纭紝璇蜂娇鐢� yyyy-MM-dd HH:mm 鎴� yyyy-MM-dd HH:mm:ss锛堜緥濡� 2026-11-10 09:00锛�");
+ }
+
+ private BigDecimal parseDuration(String raw, LocalDateTime startTime, LocalDateTime endTime) {
+ String value = normalizeCell(raw);
+ if (StrUtil.isBlank(value)) {
+ throw new LeaveImportRowException("璇峰亣鏃堕暱涓虹┖锛岃濉啓澶т簬 0 鐨勫ぉ鏁帮紙鏀寔 0.5 澶╋級");
+ }
+ value = StrUtil.removeSuffix(value, "澶�").trim();
+ BigDecimal duration;
+ try {
+ duration = new BigDecimal(value);
+ } catch (NumberFormatException e) {
+ throw new LeaveImportRowException("璇峰亣鏃堕暱[" + value + "]涓嶆槸鏈夋晥鏁板瓧锛岃濉啓澶т簬 0 鐨勫ぉ鏁帮紙渚嬪 0.5銆�1銆�2.5锛�");
+ }
+ if (duration.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new LeaveImportRowException("璇峰亣鏃堕暱[" + duration.stripTrailingZeros().toPlainString()
+ + "]蹇呴』澶т簬 0 澶╋紝璇锋寜瀹為檯璇峰亣澶╂暟濉啓");
+ }
+ // 浠呭仛鎻愮ず锛屼笉闃绘柇锛氭椂闀夸笌璧锋鏃堕棿鏄庢樉涓嶇鏃剁粰鍑哄彲鏍稿淇℃伅
+ long minutes = java.time.Duration.between(startTime, endTime).toMinutes();
+ BigDecimal expectedMax = BigDecimal.valueOf(minutes)
+ .divide(BigDecimal.valueOf(60 * 24), 2, RoundingMode.HALF_UP);
+ if (duration.compareTo(expectedMax) > 0) {
+ throw new LeaveImportRowException("璇峰亣鏃堕暱[" + duration.stripTrailingZeros().toPlainString()
+ + " 澶瓒呰繃璧锋鏃堕棿璺ㄥ害[" + expectedMax.stripTrailingZeros().toPlainString()
+ + " 澶锛岃鏍稿寮�濮�/缁撴潫鏃堕棿涓庢椂闀垮悗閲嶆柊瀵煎叆");
+ }
+ return duration;
+ }
+
+ private static String normalizeCell(String raw) {
+ if (StrUtil.isBlank(raw)) {
+ return null;
+ }
+ String value = raw.trim();
+ // Excel 鏁板�煎崟鍏冩牸鍙兘琚鎴愮瀛﹁鏁版硶鎴栧甫 .0 鐨勫皬鏁帮紝缁熶竴杩樺師涓烘櫘閫氭暟瀛�
+ if (SCIENTIFIC_PATTERN.matcher(value).matches() || TRAILING_ZERO_PATTERN.matcher(value).matches()) {
+ try {
+ value = new BigDecimal(value).stripTrailingZeros().toPlainString();
+ } catch (NumberFormatException ignored) {
+ // 淇濇寔鍘熷��
+ }
+ }
+ return value;
+ }
+
+ private static String trimToNull(String value) {
+ return StrUtil.isBlank(value) ? null : value.trim();
+ }
+
+ private static String formatDateTime(LocalDateTime value) {
+ return value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
+ }
+
+ /** 鍗曡瀵煎叆鏍¢獙寮傚父锛歮essage 鐩存帴浣滀负璇ヨ鐨勫け璐ュ師鍥犺繑鍥炵粰鐢ㄦ埛 */
+ private static class LeaveImportRowException extends RuntimeException {
+ LeaveImportRowException(String message) {
+ super(message);
+ }
+ }
+
+ private static final Pattern PHONE_PATTERN = Pattern.compile("^1\\d{10}$");
+ private static final Pattern EXCEL_SERIAL_PATTERN = Pattern.compile("^\\d{5}(\\.\\d+)?$");
+ private static final Pattern SCIENTIFIC_PATTERN = Pattern.compile("^-?\\d+(\\.\\d+)?[eE][+-]?\\d+$");
+ private static final Pattern TRAILING_ZERO_PATTERN = Pattern.compile("^-?\\d+\\.0+$");
+ private static final DateTimeFormatter[] DATE_TIME_FORMATTERS = new DateTimeFormatter[]{
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm")
+ };
+ private static final DateTimeFormatter[] DATE_ONLY_FORMATTERS = new DateTimeFormatter[]{
+ DateTimeFormatter.ofPattern("yyyy-MM-dd"),
+ DateTimeFormatter.ofPattern("yyyy/MM/dd")
+ };
private Long getCurrentUserId() {
return SecurityFrameworkUtils.getLoginUserId();
@@ -113,13 +379,20 @@
public void updateLeaveApplication(HrmLeaveApplicationSaveReqVO updateReqVO) {
// 鏍¢獙瀛樺湪
HrmLeaveApplicationDO application = validateLeaveApplicationExists(updateReqVO.getId());
- // 鏍¢獙鐘舵�佷负鑽夌鎴栧鎵逛腑
+ validateOperator(application, getCurrentUserId());
+ // 椹冲洖鍚庡厑璁镐慨鏀瑰苟閲嶆柊鎻愪氦瀹℃壒銆�
if (!Objects.equals(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())
- && !Objects.equals(application.getStatus(), HrmAuditStatusEnum.PROCESS.getStatus())) {
- throw exception(LEAVE_APPLICATION_UPDATE_FAIL_NOT_DRAFT);
+ && !Objects.equals(application.getStatus(), HrmAuditStatusEnum.REJECT.getStatus())) {
+ throw exception(LEAVE_APPLICATION_UPDATE_FAIL_NOT_EDITABLE);
}
+ if (updateReqVO.getUserId() != null && !Objects.equals(updateReqVO.getUserId(), application.getUserId())) {
+ throw exception(LEAVE_APPLICATION_APPLICANT_IMMUTABLE);
+ }
+ validateApplicantDept(application.getUserId(),
+ updateReqVO.getDeptId() != null ? updateReqVO.getDeptId() : application.getDeptId());
// 鏇存柊
HrmLeaveApplicationDO updateObj = BeanUtils.toBean(updateReqVO, HrmLeaveApplicationDO.class);
+ updateObj.setUserId(null);
leaveApplicationMapper.updateById(updateObj);
storageAttachmentApi.updateAttachments("file", "hrm_leave_application", updateReqVO.getId(), updateReqVO.getBlobIds());
}
@@ -128,6 +401,7 @@
public void deleteLeaveApplication(Long id) {
// 鏍¢獙瀛樺湪
HrmLeaveApplicationDO application = validateLeaveApplicationExists(id);
+ validateOperator(application, getCurrentUserId());
// 鏍¢獙鐘舵�佷负鑽夌
if (!Objects.equals(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) {
throw exception(LEAVE_APPLICATION_UPDATE_FAIL_NOT_DRAFT);
@@ -218,9 +492,12 @@
public void submitLeaveApplication(Long id, String processDefinitionKey, Long userId) {
// 1. 鏍¢獙璇峰亣鐢宠鏄惁瀛樺湪
HrmLeaveApplicationDO application = validateLeaveApplicationExists(id);
- if (ObjUtil.notEqual(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) {
+ validateOperator(application, userId);
+ if (ObjUtil.notEqual(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())
+ && ObjUtil.notEqual(application.getStatus(), HrmAuditStatusEnum.REJECT.getStatus())) {
throw exception(LEAVE_APPLICATION_SUBMIT_FAIL_NOT_DRAFT);
}
+ validateApplicantDept(application.getUserId(), application.getDeptId());
// 2. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
// 鍏ㄥ眬鍏嶅鎵癸細涓氬姟鏍¢獙閫氳繃鍚庣洿鎺ユ墽琛屽鎵归�氳繃閫昏緫锛屾棤闇�娴佺▼妯″瀷銆�
@@ -237,21 +514,51 @@
validateApproveCategoryAndProcessDefinition();
// 3. 鏍¢獙浼犲叆鐨勬祦绋嬪畾涔� Key 鏄惁灞炰簬璇ュ垎绫讳笖澶勪簬婵�娲荤姸鎬�
+ if (processDefinitionKey == null || processDefinitionKey.isBlank()) {
+ throw exception(LEAVE_APPLICATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+ }
ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
- if (processDefinition == null) {
+ if (processDefinition == null || bpmProcessDefinitionService
+ .getProcessDefinitionInfoListByCategory(APPROVE_CATEGORY_CODE).stream()
+ .noneMatch(info -> Objects.equals(info.getProcessDefinitionId(), processDefinition.getId()))) {
throw exception(LEAVE_APPLICATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
}
// 4. 鍒涘缓瀹℃壒娴佺▼瀹炰緥
- String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
- .setProcessDefinitionKey(processDefinitionKey).setBusinessKey(String.valueOf(id)));
+ Map<String, Object> variables = new HashMap<>();
+ variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_BUSINESS_NO, application.getNo());
+ AdminUserRespDTO applicant = adminUserApi.getUser(application.getUserId());
+ variables.put("Fl55mt725q5yabc", applicant != null ? applicant.getNickname() : String.valueOf(application.getUserId()));
+ variables.put("F5eqmt725u3naec", LEAVE_TIME_FORMAT.format(application.getStartTime())
+ + " 鑷� " + LEAVE_TIME_FORMAT.format(application.getEndTime()));
+ variables.put("F8wtmt726c3zahc", application.getDuration().toPlainString());
+ String processInstanceId = bpmProcessInstanceApi.createProcessInstance(application.getUserId(), new BpmProcessInstanceCreateReqDTO()
+ .setProcessDefinitionKey(processDefinitionKey).setBusinessKey(String.valueOf(id))
+ .setVariables(variables));
// 5. 鏇存柊璇峰亣鐢宠宸ヤ綔娴佺紪鍙�
- leaveApplicationMapper.updateById(HrmLeaveApplicationDO.builder()
- .id(id)
- .processInstanceId(processInstanceId)
- .status(HrmAuditStatusEnum.PROCESS.getStatus())
- .build());
+ leaveApplicationMapper.update(null, new LambdaUpdateWrapper<HrmLeaveApplicationDO>()
+ .eq(HrmLeaveApplicationDO::getId, id)
+ .set(HrmLeaveApplicationDO::getProcessInstanceId, processInstanceId)
+ .set(HrmLeaveApplicationDO::getStatus, HrmAuditStatusEnum.PROCESS.getStatus())
+ .set(HrmLeaveApplicationDO::getApproveTime, null));
+ }
+
+ private void validateOperator(HrmLeaveApplicationDO application, Long operatorId) {
+ if (!Objects.equals(application.getUserId(), operatorId)
+ && !Objects.equals(application.getCreator(), String.valueOf(operatorId))) {
+ throw exception(LEAVE_APPLICATION_OPERATOR_NOT_ALLOWED);
+ }
+ }
+
+ private void validateApplicantDept(Long userId, Long deptId) {
+ HrmEmployeeDO employee = employeeMapper.selectByUserId(userId);
+ if (employee == null || employee.getDeptId() == null) {
+ throw exception(LEAVE_APPLICATION_EMPLOYEE_DEPT_NOT_FOUND);
+ }
+ if (!Objects.equals(employee.getDeptId(), deptId)) {
+ throw exception(LEAVE_APPLICATION_DEPT_MISMATCH);
+ }
}
/**
@@ -331,11 +638,12 @@
throw exception(LEAVE_APPLICATION_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
}
// 鏇存柊鐘舵��
- leaveApplicationMapper.updateById(HrmLeaveApplicationDO.builder()
- .id(id)
- .status(status)
- .approveTime(java.time.LocalDateTime.now())
- .build());
+ leaveApplicationMapper.update(null, new LambdaUpdateWrapper<HrmLeaveApplicationDO>()
+ .eq(HrmLeaveApplicationDO::getId, id)
+ .set(HrmLeaveApplicationDO::getStatus, status)
+ .set(HrmLeaveApplicationDO::getApproveTime,
+ Objects.equals(status, HrmAuditStatusEnum.APPROVE.getStatus())
+ ? java.time.LocalDateTime.now() : null));
}
@Override
@@ -343,4 +651,4 @@
return leaveApplicationMapper.selectByProcessInstanceId(processInstanceId);
}
-}
\ No newline at end of file
+}
--
Gitblit v1.9.3