yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/FlexibleLocalDateConverter.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,113 @@ package cn.iocoder.yudao.framework.excel.core.convert; import cn.idev.excel.converters.Converter; import cn.idev.excel.enums.CellDataTypeEnum; import cn.idev.excel.metadata.GlobalConfiguration; import cn.idev.excel.metadata.data.ReadCellData; import cn.idev.excel.metadata.data.WriteCellData; import cn.idev.excel.metadata.property.ExcelContentProperty; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; /** * 宽æ¾ç LocalDate 转æ¢å¨ * * <p>FastExcel å ç½®ç {@code LocalDateStringConverter} åªè®¤ {@code yyyy-MM-dd}ï¼ * è客æ·å®é ç»´æ¤çè¡¨æ ¼éå¾å¸¸è§ {@code 2026.3.30}ã{@code 2026/3/30}ã{@code 2026å¹´3æ30æ¥} è¿ç±»åæ³ï¼ * 䏿¦è§£æä¸äºä¼æ {@code ExcelDataConvertException}ï¼å¯¼è´æ´ä¸ªå¯¼å ¥ç´æ¥å¤±è´¥ï¼500ï¼ï¼ * 䏿¡æ°æ®é½è¿ä¸å»ãè¿éå夿 ¼å¼å ¼å®¹ï¼è§£æä¸äºæ¶ç½®ç©ºï¼äº¤ç»ä¸å¡ä¾§æãå¿ å¡«ãè§åéæ¡æç¤ºã * * @author è¶ çº§ç®¡çå */ @Slf4j public class FlexibleLocalDateConverter implements Converter<LocalDate> { /** * Excel æ¥æåºåå·çåºåæ¥ï¼1899-12-30ï¼å ¼å®¹ Excel ç 1900 é°å¹´ bugï¼ */ private static final LocalDate EXCEL_EPOCH = LocalDate.of(1899, 12, 30); /** * çº¯æ¥ææ ¼å¼ï¼æé¡ºåºå°è¯ */ private static final List<DateTimeFormatter> DATE_FORMATTERS = Arrays.asList( DateTimeFormatter.ofPattern("yyyy-M-d"), DateTimeFormatter.ofPattern("yyyy.M.d"), DateTimeFormatter.ofPattern("yyyy/M/d"), DateTimeFormatter.ofPattern("yyyyå¹´Mædæ¥"), DateTimeFormatter.ofPattern("yyyyMMdd") ); /** * 带æ¶é´çæ ¼å¼ï¼æé¡ºåºå°è¯å忥æé¨å */ private static final List<DateTimeFormatter> DATETIME_FORMATTERS = Arrays.asList( DateTimeFormatter.ofPattern("yyyy-M-d H:m:s"), DateTimeFormatter.ofPattern("yyyy-M-d H:m"), DateTimeFormatter.ofPattern("yyyy.M.d H:m:s"), DateTimeFormatter.ofPattern("yyyy/M/d H:m:s"), DateTimeFormatter.ofPattern("yyyy/M/d H:m") ); @Override public Class<LocalDate> supportJavaTypeKey() { return LocalDate.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { // ä¸éå®åå æ ¼ç±»åï¼ææ¬åæ°åé½è¦æ¯æ return null; } @Override public LocalDate convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (cellData == null) { return null; } // 1. æ°åï¼æ Excel æ¥æåºåå·è§£æ if (CellDataTypeEnum.NUMBER.equals(cellData.getType())) { BigDecimal numberValue = cellData.getNumberValue(); return numberValue == null ? null : EXCEL_EPOCH.plusDays(numberValue.longValue()); } // 2. ææ¬ï¼å¤æ ¼å¼ä¾æ¬¡å°è¯ String text = cellData.getStringValue(); if (text == null || text.trim().isEmpty()) { return null; } text = text.trim(); for (DateTimeFormatter formatter : DATE_FORMATTERS) { try { return LocalDate.parse(text, formatter); } catch (Exception ignored) { // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ } } for (DateTimeFormatter formatter : DATETIME_FORMATTERS) { try { return LocalDateTime.parse(text, formatter).toLocalDate(); } catch (Exception ignored) { // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ } } log.warn("[convertToJavaData][æ¥æ({}) æ æ³è¯å«ï¼å·²ç½®ç©º]", text); return null; } @Override public WriteCellData<?> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (value == null) { return new WriteCellData<>(""); } return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/HrmEmployeeImportExcelVO.java
@@ -3,6 +3,7 @@ import cn.idev.excel.annotation.ExcelProperty; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.FlexibleLocalDateConverter; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -38,7 +39,7 @@ @ExcelProperty("ç¨æ·è§è²") private String roleNames; @ExcelProperty("å ¥èæ¥æ") @ExcelProperty(value = "å ¥èæ¥æ", converter = FlexibleLocalDateConverter.class) private LocalDate hireDate; @ExcelProperty(value = "åå·¥ç¶æ", converter = DictConvert.class) @@ -48,7 +49,7 @@ @ExcelProperty("身份è¯å·") private String idCard; @ExcelProperty("åºçæ¥æ") @ExcelProperty(value = "åºçæ¥æ", converter = FlexibleLocalDateConverter.class) private LocalDate birthday; @ExcelProperty("æ°æ") yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeServiceImpl.java
@@ -3,6 +3,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; 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; @@ -62,6 +63,7 @@ */ @Service @Validated @Slf4j public class HrmEmployeeServiceImpl implements HrmEmployeeService { @Resource @@ -470,11 +472,18 @@ if (StrUtil.isBlank(deptName)) { throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "é¨é¨åç§°"); } DeptRespDTO dept = deptMap.get(deptName.trim()); if (dept == null) { throw exception(EMPLOYEE_DEPT_NAME_NOT_EXISTS, deptName.trim()); String trimmed = deptName.trim(); DeptRespDTO dept = deptMap.get(trimmed); if (dept != null) { return dept.getId(); } return dept.getId(); // 客æ·è¡¨æ ¼éçé¨é¨åå¯è½å°æªå¨ç³»ç»ä¸å»ºç«ï¼å¦ã综å车é´ãï¼ï¼ // é¨é¨æ¯åå·¥çå¿ å¡«é¡¹ï¼ç´æ¥æ¥éä¼è®©æ´æ¹å¯¼å ¥å ¨é¨å¤±è´¥ï¼è¿éèªå¨è¡¥å»ºå°æ ¹é¨é¨ä¸ Long deptId = deptApi.getOrCreateDeptByName(trimmed); if (deptId == null) { throw exception(EMPLOYEE_DEPT_NAME_NOT_EXISTS, trimmed); } return deptId; } private Set<Long> parsePostIds(String postNames, Map<String, PostRespDTO> postMap) { @@ -488,10 +497,15 @@ continue; } PostRespDTO post = postMap.get(trimmed); if (post == null) { throw exception(EMPLOYEE_POST_NAME_NOT_EXISTS, trimmed); if (post != null) { postIds.add(post.getId()); continue; } postIds.add(post.getId()); // å²ä½æªå»ºç«æ¶èªå¨è¡¥å»ºï¼é¿å æ´æ¹å¯¼å ¥å¤±è´¥ Long postId = postApi.getOrCreatePostByName(trimmed); if (postId != null) { postIds.add(postId); } } if (CollUtil.isEmpty(postIds)) { throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "å²ä½åç§°"); @@ -511,7 +525,9 @@ } RoleRespDTO role = roleMap.get(trimmed); if (role == null) { throw exception(EMPLOYEE_ROLE_NAME_NOT_EXISTS, trimmed); // è§è²å¯¹åå·¥å¯¼å ¥æ¯å¯é项ï¼è¡¨æ ¼éå¡«äºç³»ç»æ²¡æçè§è²åï¼å¸¸è§æ¯æé¨é¨å误填å°è§è²åï¼æ¶è·³è¿ï¼ä¸é»å¡å¯¼å ¥ log.warn("[parseRoleIds][è§è²({}) ä¸åå¨ï¼å·²è·³è¿]", trimmed); continue; } roleIds.add(role.getId()); } yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java
@@ -65,4 +65,16 @@ */ List<DeptRespDTO> getChildDeptList(Long id); /** * æåç§°è·å¾é¨é¨ç¼å·ï¼ä¸å卿¶èªå¨åå»ºï¼æå¨æ ¹é¨é¨ä¸ï¼ * * <p>ç¨äº Excel å¯¼å ¥çãåªæå称没æç¼å·ãçåºæ¯ï¼åå·¥å¯¼å ¥æ¶é¨é¨æ¯å¿ å¡«é¡¹ï¼ * è¥å®¢æ·è¡¨æ ¼éçé¨é¨åå¨ç³»ç»ä¸å°æªå»ºç«ï¼ç´æ¥æ¥éä¼å¯¼è´æ´æ¹å¯¼å ¥å¤±è´¥ï¼ * è¿éæ¹ä¸ºèªå¨è¡¥å»ºï¼ä¿è¯å¯¼å ¥å¯ç¨ã * * @param name é¨é¨åç§° * @return é¨é¨ç¼å·ï¼å称为空æ¶è¿å {@code null} */ Long getOrCreateDeptByName(String name); } yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java
@@ -1,8 +1,11 @@ package cn.iocoder.yudao.module.system.api.dept; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.service.dept.DeptService; import org.springframework.stereotype.Service; @@ -51,4 +54,31 @@ return BeanUtils.toBean(childDeptList, DeptRespDTO.class); } @Override public Long getOrCreateDeptByName(String name) { String deptName = StrUtil.trim(name); if (StrUtil.isEmpty(deptName)) { return null; } List<DeptRespDTO> depts = getDeptList(); // 1. å·²åå¨ï¼ç´æ¥å¤ç¨ for (DeptRespDTO dept : depts) { if (deptName.equals(StrUtil.trim(dept.getName()))) { return dept.getId(); } } // 2. ä¸åå¨ï¼è¡¥å»ºå°æ ¹é¨é¨ä¸ Long parentId = depts.stream() .filter(dept -> dept.getParentId() == null || dept.getParentId() == 0L) .map(DeptRespDTO::getId) .findFirst() .orElse(0L); DeptSaveReqVO createReqVO = new DeptSaveReqVO(); createReqVO.setName(deptName); createReqVO.setParentId(parentId); createReqVO.setSort(0); createReqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); return deptService.createDept(createReqVO); } } yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java
@@ -34,6 +34,16 @@ */ List<PostRespDTO> getPostList(); /** * æåç§°è·å¾å²ä½ç¼å·ï¼ä¸å卿¶èªå¨å建 * * <p>ç¨äº Excel å¯¼å ¥çãåªæå称没æç¼å·ãçåºæ¯ï¼é¿å å å²ä½å°æªå»ºç«å¯¼è´æ´æ¹å¯¼å ¥å¤±è´¥ã * * @param name å²ä½åç§° * @return å²ä½ç¼å·ï¼å称为空æ¶è¿å {@code null} */ Long getOrCreatePostByName(String name); default Map<Long, PostRespDTO> getPostMap(Collection<Long> ids) { if (CollUtil.isEmpty(ids)) { return MapUtil.empty(); yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java
@@ -1,7 +1,10 @@ package cn.iocoder.yudao.module.system.api.dept; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; import cn.iocoder.yudao.module.system.service.dept.PostService; import org.springframework.stereotype.Service; @@ -38,4 +41,26 @@ return BeanUtils.toBean(list, PostRespDTO.class); } @Override public Long getOrCreatePostByName(String name) { String postName = StrUtil.trim(name); if (StrUtil.isEmpty(postName)) { return null; } List<PostRespDTO> posts = getPostList(); // 1. å·²åå¨ï¼ç´æ¥å¤ç¨ for (PostRespDTO post : posts) { if (postName.equals(StrUtil.trim(post.getName()))) { return post.getId(); } } // 2. ä¸åå¨ï¼è¡¥å»ºãcode å¿ å¡«ä¸éå¯ä¸ï¼ç¨æ¶é´æ³çæ PostSaveReqVO createReqVO = new PostSaveReqVO(); createReqVO.setName(postName); createReqVO.setCode("POST_" + System.currentTimeMillis()); createReqVO.setSort(0); createReqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); return postService.createPost(createReqVO); } }