Browse Source

导入考勤信息

zhaopeiqing 7 months ago
parent
commit
a5a85a04ef
12 changed files with 246 additions and 19 deletions
  1. 1 0
      yudao-module-personnel/yudao-module-attendance-api/src/main/java/cn/iocoder/yudao/module/attendance/enums/ErrorCodeConstants.java
  2. 6 0
      yudao-module-personnel/yudao-module-attendance-biz/pom.xml
  3. 14 3
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/AttendanceInfoController.java
  4. 50 0
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoImportExcelVO.java
  5. 24 0
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoImportRespVO.java
  6. 13 4
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoPageReqVO.java
  7. 12 4
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoRespVO.java
  8. 10 3
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoSaveReqVO.java
  9. 12 3
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/dal/dataobject/info/AttendanceInfoDO.java
  10. 4 2
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/dal/mysql/info/AttendanceInfoMapper.java
  11. 11 0
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/service/info/AttendanceInfoService.java
  12. 89 0
      yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/service/info/AttendanceInfoServiceImpl.java

+ 1 - 0
yudao-module-personnel/yudao-module-attendance-api/src/main/java/cn/iocoder/yudao/module/attendance/enums/ErrorCodeConstants.java

@@ -14,4 +14,5 @@ public interface ErrorCodeConstants {
     ErrorCode ATTENDANCE_SCHEDULING_MANAGE_NOT_EXISTS = new ErrorCode(1_013_000_000, "考勤排班管理不存在");
     ErrorCode ATTENDANCE_INFO_NOT_EXISTS = new ErrorCode(1_013_000_001, "考勤信息不存在");
     ErrorCode ATTENDANCE_EMPLOYEE_SETTING_NOT_EXISTS = new ErrorCode(1_013_000_002, "考勤员工设置不存在");
+    ErrorCode ATTENDANCE_INFO_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_013_000_003, "导入考勤信息数据不能为空!");
 }

+ 6 - 0
yudao-module-personnel/yudao-module-attendance-biz/pom.xml

@@ -88,6 +88,12 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-mail</artifactId>
         </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-employee-biz</artifactId>
+            <version>2.1.0-jdk8-snapshot</version>
+            <scope>compile</scope>
+        </dependency>
 
         <!-- 三方云服务相关 -->
 

+ 14 - 3
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/AttendanceInfoController.java

@@ -1,10 +1,9 @@
 package cn.iocoder.yudao.module.attendance.controller.admin.info;
 
-import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoPageReqVO;
-import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoRespVO;
-import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoSaveReqVO;
+import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.*;
 import cn.iocoder.yudao.module.attendance.dal.dataobject.info.AttendanceInfoDO;
 import cn.iocoder.yudao.module.attendance.service.info.AttendanceInfoService;
+import io.swagger.v3.oas.annotations.Parameters;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,6 +23,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 
 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
@@ -94,4 +94,15 @@ public class AttendanceInfoController {
                         BeanUtils.toBean(list, AttendanceInfoRespVO.class));
     }
 
+    @PostMapping("/import-excel")
+    @Operation(summary = "导入考勤信息 Excel")
+    @Parameters({
+            @Parameter(name = "file", description = "Excel 文件", required = true)
+    })
+    @PreAuthorize("@ss.hasPermission('attendance:info:import')")
+    public CommonResult<AttendanceInfoImportRespVO> importExcel(@RequestParam("file") MultipartFile file) throws Exception {
+        List<AttendanceInfoImportExcelVO> list = ExcelUtils.read(file, AttendanceInfoImportExcelVO.class);
+        return success(infoService.importAttendanceInfoList(list));
+    }
+
 }

+ 50 - 0
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoImportExcelVO.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.attendance.controller.admin.info.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 考勤信息 Excel 导入 VO
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
+public class AttendanceInfoImportExcelVO {
+
+    @ExcelProperty("员工姓名")
+    private String employeeName;
+
+    @ExcelProperty("员工手机号")
+    private String employeeMobile;
+
+    @ExcelProperty("部门名称")
+    private String deptName;
+
+    @ExcelProperty("职位名称")
+    private String position;
+
+    @ExcelProperty("考勤日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDate[] attendanceDate;
+
+    @ExcelProperty("上班时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalTime[] workStartTime;
+
+    @ExcelProperty("下班时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalTime[] workEndTime;
+
+}

+ 24 - 0
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoImportRespVO.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.attendance.controller.admin.info.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Schema(description = "管理后台 - 考勤信息导入 Response VO")
+@Data
+@Builder
+public class AttendanceInfoImportRespVO {
+
+    @Schema(description = "创建成功的员工姓名数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private List<String> createEmployeeNames;
+
+    @Schema(description = "删除成功的员工姓名数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private List<String> deleteEmployeeNames;
+
+    @Schema(description = "导入失败的员工集合,key 为员工姓名,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Map<String, String> failureEmployeeNames;
+
+}

+ 13 - 4
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoPageReqVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.attendance.controller.admin.info.vo;
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import lombok.*;
 
 import java.time.LocalDate;
@@ -9,6 +10,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import java.math.BigDecimal;
 import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotEmpty;
 import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -22,11 +25,17 @@ public class AttendanceInfoPageReqVO extends PageParam {
     @Schema(description = "员工ID", example = "6960")
     private Long employeeId;
 
-    @Schema(description = "员工姓名", example = "王五")
-    private String name;
+    @Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    private String employeeName;
+
+    @Schema(description = "员工手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13812345678")
+    private String employeeMobile;
+
+    @Schema(description = "部门名称", example = "13463")
+    private String deptName;
 
-    @Schema(description = "部门ID", example = "13463")
-    private Long deptId;
+    @Schema(description = "职位名称", example = "13463")
+    private String position;
 
     @Schema(description = "考勤日期")
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)

+ 12 - 4
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoRespVO.java

@@ -12,6 +12,8 @@ import org.springframework.format.annotation.DateTimeFormat;
 import java.time.LocalDateTime;
 import com.alibaba.excel.annotation.*;
 
+import javax.validation.constraints.NotEmpty;
+
 @Schema(description = "管理后台 - 考勤信息 Response VO")
 @Data
 @ExcelIgnoreUnannotated
@@ -27,11 +29,17 @@ public class AttendanceInfoRespVO {
 
     @Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
     @ExcelProperty("员工姓名")
-    private String name;
+    private String employeeName;
+
+    @Schema(description = "员工手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13812345678")
+    @NotEmpty(message = "员工手机号不能为空")
+    private String employeeMobile;
+
+    @Schema(description = "部门名称", example = "13463")
+    private String deptName;
 
-    @Schema(description = "部门ID", example = "13463")
-    @ExcelProperty("部门ID")
-    private Long deptId;
+    @Schema(description = "职位名称", example = "13463")
+    private String position;
 
     @Schema(description = "考勤日期", requiredMode = Schema.RequiredMode.REQUIRED)
     @ExcelProperty("考勤日期")

+ 10 - 3
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/controller/admin/info/vo/AttendanceInfoSaveReqVO.java

@@ -24,10 +24,17 @@ public class AttendanceInfoSaveReqVO {
 
     @Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
     @NotEmpty(message = "员工姓名不能为空")
-    private String name;
+    private String employeeName;
 
-    @Schema(description = "部门ID", example = "13463")
-    private Long deptId;
+    @Schema(description = "员工手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13812345678")
+    @NotEmpty(message = "员工手机号不能为空")
+    private String employeeMobile;
+
+    @Schema(description = "部门名称", example = "13463")
+    private String deptName;
+
+    @Schema(description = "职位名称", example = "13463")
+    private String position;
 
     @Schema(description = "考勤日期", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotNull(message = "考勤日期不能为空")

+ 12 - 3
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/dal/dataobject/info/AttendanceInfoDO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.attendance.dal.dataobject.info;
 
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.*;
 
 import java.time.LocalDate;
@@ -39,11 +40,19 @@ public class AttendanceInfoDO extends BaseDO {
     /**
      * 员工姓名
      */
-    private String name;
+    private String employeeName;
     /**
-     * 部门ID
+     * 员工手机号
      */
-    private Long deptId;
+    private String employeeMobile;
+    /**
+     * 部门名称
+     */
+     String deptName;
+    /**
+     * 职位名称
+     */
+    private String position;
     /**
      * 考勤日期
      */

+ 4 - 2
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/dal/mysql/info/AttendanceInfoMapper.java

@@ -20,8 +20,10 @@ public interface AttendanceInfoMapper extends BaseMapperX<AttendanceInfoDO> {
     default PageResult<AttendanceInfoDO> selectPage(AttendanceInfoPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<AttendanceInfoDO>()
                 .eqIfPresent(AttendanceInfoDO::getEmployeeId, reqVO.getEmployeeId())
-                .likeIfPresent(AttendanceInfoDO::getName, reqVO.getName())
-                .eqIfPresent(AttendanceInfoDO::getDeptId, reqVO.getDeptId())
+                .likeIfPresent(AttendanceInfoDO::getEmployeeName, reqVO.getEmployeeName())
+                .likeIfPresent(AttendanceInfoDO::getEmployeeMobile, reqVO.getEmployeeMobile())
+                .likeIfPresent(AttendanceInfoDO::getDeptName, reqVO.getDeptName())
+                .likeIfPresent(AttendanceInfoDO::getPosition, reqVO.getPosition())
                 .betweenIfPresent(AttendanceInfoDO::getAttendanceDate, reqVO.getAttendanceDate())
                 .betweenIfPresent(AttendanceInfoDO::getWorkStartTime, reqVO.getWorkStartTime())
                 .betweenIfPresent(AttendanceInfoDO::getWorkEndTime, reqVO.getWorkEndTime())

+ 11 - 0
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/service/info/AttendanceInfoService.java

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.attendance.service.info;
 import java.util.*;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoImportExcelVO;
+import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoImportRespVO;
 import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoPageReqVO;
 import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoSaveReqVO;
 import cn.iocoder.yudao.module.attendance.dal.dataobject.info.AttendanceInfoDO;
@@ -54,4 +56,13 @@ public interface AttendanceInfoService {
      */
     PageResult<AttendanceInfoDO> getInfoPage(AttendanceInfoPageReqVO pageReqVO);
 
+    /**
+     * 批量导入考勤信息
+     *
+     * @param importAttendanceInfos 导入考勤信息列表
+     * @return 导入结果
+     */
+    AttendanceInfoImportRespVO importAttendanceInfoList(List<AttendanceInfoImportExcelVO> importAttendanceInfos);
+
+
 }

+ 89 - 0
yudao-module-personnel/yudao-module-attendance-biz/src/main/java/cn/iocoder/yudao/module/attendance/service/info/AttendanceInfoServiceImpl.java

@@ -1,15 +1,29 @@
 package cn.iocoder.yudao.module.attendance.service.info;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoImportExcelVO;
+import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoImportRespVO;
 import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoPageReqVO;
 import cn.iocoder.yudao.module.attendance.controller.admin.info.vo.AttendanceInfoSaveReqVO;
 import cn.iocoder.yudao.module.attendance.dal.dataobject.info.AttendanceInfoDO;
 import cn.iocoder.yudao.module.attendance.dal.mysql.info.AttendanceInfoMapper;
+import cn.iocoder.yudao.module.employee.controller.admin.info.vo.EmployeeInfoPageReqVO;
+import cn.iocoder.yudao.module.employee.dal.dataobject.info.EmployeeInfoDO;
+import cn.iocoder.yudao.module.employee.service.info.EmployeeInfoService;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
+import java.util.stream.Collectors;
+
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -18,6 +32,8 @@ import javax.annotation.Resource;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.attendance.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.employee.enums.ErrorCodeConstants.EMPLOYEE_INFO_NOT_EXISTS;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 
 /**
  * 考勤信息 Service 实现类
@@ -30,6 +46,8 @@ public class AttendanceInfoServiceImpl implements AttendanceInfoService {
 
     @Resource
     private AttendanceInfoMapper infoMapper;
+    @Resource
+    private EmployeeInfoService employeeInfoService;
 
     @Override
     @TenantIgnore
@@ -79,4 +97,75 @@ public class AttendanceInfoServiceImpl implements AttendanceInfoService {
         return infoMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    @TenantIgnore
+    @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
+    public AttendanceInfoImportRespVO importAttendanceInfoList(List<AttendanceInfoImportExcelVO> importAttendanceInfos) {
+        if (CollUtil.isEmpty(importAttendanceInfos)) {
+            throw exception(ATTENDANCE_INFO_IMPORT_LIST_IS_EMPTY);
+        }
+
+        // 获取用户信息和租户ID
+        LoginUser user = SecurityFrameworkUtils.getLoginUser();
+        Long tenantId = user != null && user.getTenantId() != null ? user.getTenantId() : 0L;
+
+        // 初始化响应对象
+        AttendanceInfoImportRespVO respVO = AttendanceInfoImportRespVO.builder()
+                .createEmployeeNames(new ArrayList<>())
+                .deleteEmployeeNames(new ArrayList<>())
+                .failureEmployeeNames(new LinkedHashMap<>())
+                .build();
+
+        // 收集要删除和创建的数据
+        List<AttendanceInfoDO> toInsert = new ArrayList<>();
+        List<Long> toDeleteIds = new ArrayList<>();
+        List<String> failedEmployeeNames = new ArrayList<>();
+
+        for (AttendanceInfoImportExcelVO importAttendance : importAttendanceInfos) {
+            try {
+                // 验证员工是否存在
+                validateEmployeeExists(importAttendance.getEmployeeName(), importAttendance.getEmployeeMobile(), tenantId);
+
+                // 查找并收集要删除的数据
+                List<AttendanceInfoDO> existingInfos = infoMapper.selectPage(
+                        new AttendanceInfoPageReqVO().setAttendanceDate(importAttendance.getAttendanceDate()).setEmployeeMobile(importAttendance.getEmployeeMobile())
+                ).getList();
+                toDeleteIds.addAll(existingInfos.stream().map(AttendanceInfoDO::getId).collect(Collectors.toList()));
+
+                // 准备要插入的数据
+                AttendanceInfoDO attendanceInfoDO = BeanUtils.toBean(importAttendance, AttendanceInfoDO.class);
+                toInsert.add(attendanceInfoDO);
+
+            } catch (ServiceException ex) {
+                failedEmployeeNames.add(importAttendance.getEmployeeName());
+                respVO.getFailureEmployeeNames().put(importAttendance.getEmployeeName(), ex.getMessage());
+            }
+        }
+
+        // 批量删除
+        if (!toDeleteIds.isEmpty()) {
+            infoMapper.deleteBatchIds(toDeleteIds);
+            toDeleteIds.forEach(id -> respVO.getDeleteEmployeeNames().add(this.getInfo(id).getEmployeeName())); // 假设有一个findEmployeeNameById方法来获取员工名
+        }
+
+        // 批量插入
+        if (!toInsert.isEmpty()) {
+            infoMapper.insertBatch(toInsert);
+            toInsert.forEach(info -> respVO.getCreateEmployeeNames().add(info.getEmployeeName()));
+        }
+        return respVO;
+    }
+
+    private boolean validateEmployeeExists(String employeeName, String employeeMobile, Long tenantId) {
+        // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
+        return DataPermissionUtils.executeIgnore(() -> {
+            List<EmployeeInfoDO> list = employeeInfoService.getInfoPage(new EmployeeInfoPageReqVO().setName(employeeName).setPhone(employeeMobile).setTenantId(tenantId)).getList();
+           if (CollUtil.isEmpty(list)) {
+               throw exception(EMPLOYEE_INFO_NOT_EXISTS);
+           }
+            return true;
+        });
+    }
+
+
 }