From ef3cbc1c6882786e8218b08be3e52ea44587f03b Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 08 十一月 2026 09:45:42 +0800
Subject: [PATCH] feat(hrm): 请假批量导入、加班代申请人、批量排班与节假日区间联动

---
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java |   96 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 96 insertions(+), 0 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 0ee5726..a943b22 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,6 +2,8 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 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;
@@ -10,11 +12,15 @@
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 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.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;
@@ -31,8 +37,10 @@
 import org.springframework.validation.annotation.Validated;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -41,6 +49,7 @@
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
 import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*;
 
 /**
@@ -69,6 +78,8 @@
     private StorageAttachmentApi storageAttachmentApi;
     @Resource
     private BpmProcessDefinitionService bpmProcessDefinitionService;
+    @Resource
+    private HrmEmployeeMapper employeeMapper;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -332,4 +343,89 @@
         return leaveApplicationMapper.selectByProcessInstanceId(processInstanceId);
     }
 
+    @Override
+    public HrmLeaveApplicationImportRespVO importLeaveApplicationList(List<HrmLeaveApplicationImportExcelVO> importList) {
+        // 1. 杩囨护绌鸿锛堟墜鏈哄彿涓虹┖鐨� Excel 琛岃涓虹┖琛岋級
+        importList = filterList(importList, item -> StrUtil.isNotBlank(item.getPhone()));
+        if (CollUtil.isEmpty(importList)) {
+            throw exception(LEAVE_APPLICATION_IMPORT_LIST_IS_EMPTY);
+        }
+
+        // 2. 閫愯瀵煎叆锛屽崟琛屽け璐ヤ笉褰卞搷鍏朵粬琛�
+        HrmLeaveApplicationImportRespVO respVO = HrmLeaveApplicationImportRespVO.builder()
+                .createNames(new ArrayList<>())
+                .failureNames(new LinkedHashMap<>())
+                .build();
+        for (int i = 0; i < importList.size(); i++) {
+            HrmLeaveApplicationImportExcelVO row = importList.get(i);
+            // Excel 绗� 1 琛屾槸琛ㄥご锛屾暟鎹粠绗� 2 琛屽紑濮�
+            String rowKey = "绗�" + (i + 2) + "琛�";
+            try {
+                HrmLeaveApplicationSaveReqVO saveReqVO = buildImportSaveReqVO(row);
+                createLeaveApplication(saveReqVO);
+                respVO.getCreateNames().add(row.getPhone() + " " + row.getStartTime().toLocalDate());
+            } catch (ServiceException ex) {
+                respVO.getFailureNames().put(rowKey, ex.getMessage());
+            } catch (Exception ex) {
+                respVO.getFailureNames().put(rowKey, "瀵煎叆澶辫触锛�" + ex.getMessage());
+            }
+        }
+        return respVO;
+    }
+
+    private HrmLeaveApplicationSaveReqVO buildImportSaveReqVO(HrmLeaveApplicationImportExcelVO row) {
+        String phone = row.getPhone().trim();
+        HrmEmployeeDO employee = employeeMapper.selectByPhone(phone);
+        if (employee == null) {
+            throw exception(LEAVE_APPLICATION_IMPORT_EMPLOYEE_NOT_EXISTS, phone);
+        }
+        if (employee.getUserId() == null) {
+            throw exception(LEAVE_APPLICATION_IMPORT_EMPLOYEE_NO_USER, employee.getName());
+        }
+        // 璇峰亣绫诲瀷锛氭寜鍚嶇О鍖归厤锛堝吋瀹圭洿鎺ュ~瀛楀吀鍊硷級锛屽尮閰嶄笉鍒版椂鍒楀嚭鍙~鍊�
+        List<String> leaveTypeNames = Arrays.stream(HrmLeaveTypeEnum.values())
+                .map(HrmLeaveTypeEnum::getName).toList();
+        String leaveTypeText = StrUtil.trim(row.getLeaveType());
+        if (StrUtil.isBlank(leaveTypeText)) {
+            throw exception(LEAVE_APPLICATION_IMPORT_FIELD_REQUIRED,
+                    "璇峰亣绫诲瀷锛堝彲閫夛細" + String.join("銆�", leaveTypeNames) + "锛�");
+        }
+        Integer leaveType = Arrays.stream(HrmLeaveTypeEnum.values())
+                .filter(item -> item.getName().equals(leaveTypeText)
+                        || String.valueOf(item.getType()).equals(leaveTypeText))
+                .map(HrmLeaveTypeEnum::getType)
+                .findFirst().orElse(null);
+        if (leaveType == null) {
+            throw exception(LEAVE_APPLICATION_IMPORT_FIELD_INVALID,
+                    "璇峰亣绫诲瀷", leaveTypeText, String.join("銆�", leaveTypeNames));
+        }
+        if (row.getStartTime() == null) {
+            throw exception(LEAVE_APPLICATION_IMPORT_FIELD_REQUIRED, "寮�濮嬫椂闂�");
+        }
+        if (row.getEndTime() == null) {
+            throw exception(LEAVE_APPLICATION_IMPORT_FIELD_REQUIRED, "缁撴潫鏃堕棿");
+        }
+        if (!row.getStartTime().isBefore(row.getEndTime())) {
+            throw exception(LEAVE_APPLICATION_IMPORT_TIME_INVALID,
+                    row.getStartTime(), row.getEndTime());
+        }
+        if (row.getDuration() == null || row.getDuration().signum() <= 0) {
+            throw exception(LEAVE_APPLICATION_IMPORT_FIELD_REQUIRED, "璇峰亣鏃堕暱锛堝ぉ锛屽繀椤诲ぇ浜� 0锛�");
+        }
+        if (StrUtil.isBlank(row.getReason())) {
+            throw exception(LEAVE_APPLICATION_IMPORT_FIELD_REQUIRED, "璇峰亣鍘熷洜");
+        }
+
+        HrmLeaveApplicationSaveReqVO saveReqVO = new HrmLeaveApplicationSaveReqVO();
+        saveReqVO.setUserId(employee.getUserId());
+        saveReqVO.setDeptId(employee.getDeptId());
+        saveReqVO.setLeaveType(leaveType);
+        saveReqVO.setStartTime(row.getStartTime());
+        saveReqVO.setEndTime(row.getEndTime());
+        saveReqVO.setDuration(row.getDuration());
+        saveReqVO.setReason(row.getReason().trim());
+        saveReqVO.setRemark(row.getRemark());
+        return saveReqVO;
+    }
+
 }
\ No newline at end of file

--
Gitblit v1.9.3