Commit 5f6887bb7affa493e94d3e13387beaa52bce943b
1 parent
441f44f7
险情管理导出图片,用户管理导入调整
Showing
12 changed files
with
548 additions
and
20 deletions
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/BusinessLineConvert.java
0 → 100644
| 1 | +package com.zteits.urbanops.framework.excel.core.convert; | ||
| 2 | + | ||
| 3 | +import cn.idev.excel.converters.Converter; | ||
| 4 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 5 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 6 | +import cn.idev.excel.metadata.data.ReadCellData; | ||
| 7 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 8 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 9 | +import lombok.extern.slf4j.Slf4j; | ||
| 10 | + | ||
| 11 | +@Slf4j | ||
| 12 | +public class BusinessLineConvert implements Converter<Object> { | ||
| 13 | + | ||
| 14 | + @Override | ||
| 15 | + public Class<?> supportJavaTypeKey() { | ||
| 16 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public CellDataTypeEnum supportExcelTypeKey() { | ||
| 21 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 导入:业务线名称(园林,市政) → 编码(yl,sz) | ||
| 26 | + */ | ||
| 27 | + @Override | ||
| 28 | + public Object convertToJavaData(ReadCellData<?> readCellData, ExcelContentProperty contentProperty, | ||
| 29 | + GlobalConfiguration globalConfiguration) { | ||
| 30 | + String cellValue = readCellData.getStringValue(); | ||
| 31 | + if (cellValue == null || cellValue.isBlank()) { | ||
| 32 | + return null; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + try { | ||
| 36 | + // 按逗号切割 | ||
| 37 | + String[] lineArray = cellValue.split(","); | ||
| 38 | + StringBuilder codeSb = new StringBuilder(); | ||
| 39 | + | ||
| 40 | + for (String line : lineArray) { | ||
| 41 | + String code = convertLineToCode(line.trim()); | ||
| 42 | + if (codeSb.length() > 0) { | ||
| 43 | + codeSb.append(","); | ||
| 44 | + } | ||
| 45 | + codeSb.append(code); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + return codeSb.toString(); | ||
| 49 | + } catch (Exception e) { | ||
| 50 | + log.error("[convertToJavaData][业务线转换异常] {}", cellValue, e); | ||
| 51 | + return null; | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 导出:业务线编码(yl,sz) → 名称(园林,市政) | ||
| 57 | + */ | ||
| 58 | + @Override | ||
| 59 | + public WriteCellData<?> convertToExcelData(Object value, ExcelContentProperty contentProperty, | ||
| 60 | + GlobalConfiguration globalConfiguration) { | ||
| 61 | + if (value == null) { | ||
| 62 | + return new WriteCellData<>(""); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + try { | ||
| 66 | + String codeValue = String.valueOf(value); | ||
| 67 | + String[] codeArray = codeValue.split(","); | ||
| 68 | + StringBuilder nameSb = new StringBuilder(); | ||
| 69 | + | ||
| 70 | + for (String code : codeArray) { | ||
| 71 | + String name = convertCodeToLine(code.trim()); | ||
| 72 | + if (nameSb.length() > 0) { | ||
| 73 | + nameSb.append(","); | ||
| 74 | + } | ||
| 75 | + nameSb.append(name); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + return new WriteCellData<>(nameSb.toString()); | ||
| 79 | + } catch (Exception e) { | ||
| 80 | + log.error("[convertToExcelData][业务线转换异常] {}", value, e); | ||
| 81 | + return new WriteCellData<>(""); | ||
| 82 | + } | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + /** | ||
| 86 | + * 名称 → 编码 | ||
| 87 | + */ | ||
| 88 | + private String convertLineToCode(String line) { | ||
| 89 | + return switch (line) { | ||
| 90 | + case "园林" -> "yl"; | ||
| 91 | + case "市政" -> "sz"; | ||
| 92 | + case "物业" -> "wy"; | ||
| 93 | + case "秩序管理" -> "zx"; | ||
| 94 | + case "环境卫生" -> "hj"; | ||
| 95 | + default -> ""; | ||
| 96 | + }; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * 编码 → 名称 | ||
| 101 | + */ | ||
| 102 | + private String convertCodeToLine(String code) { | ||
| 103 | + return switch (code) { | ||
| 104 | + case "yl" -> "园林"; | ||
| 105 | + case "sz" -> "市政"; | ||
| 106 | + case "wy" -> "物业"; | ||
| 107 | + case "zx" -> "秩序管理"; | ||
| 108 | + case "hj" -> "环境卫生"; | ||
| 109 | + default -> ""; | ||
| 110 | + }; | ||
| 111 | + } | ||
| 112 | +} |
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/ImageListConverter.java
0 → 100644
| 1 | +package com.zteits.urbanops.framework.excel.core.convert; | ||
| 2 | + | ||
| 3 | +import cn.idev.excel.converters.Converter; | ||
| 4 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 5 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 6 | +import cn.idev.excel.metadata.data.ImageData; | ||
| 7 | +import cn.idev.excel.metadata.data.ReadCellData; | ||
| 8 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 9 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 10 | +import cn.idev.excel.util.FileUtils; | ||
| 11 | +import cn.idev.excel.util.ListUtils; | ||
| 12 | +import lombok.extern.slf4j.Slf4j; | ||
| 13 | +import org.springframework.util.StringUtils; | ||
| 14 | + | ||
| 15 | +import java.io.ByteArrayOutputStream; | ||
| 16 | +import java.io.File; | ||
| 17 | +import java.io.InputStream; | ||
| 18 | +import java.net.HttpURLConnection; | ||
| 19 | +import java.net.URL; | ||
| 20 | +import java.util.List; | ||
| 21 | + | ||
| 22 | +@Slf4j | ||
| 23 | +public class ImageListConverter implements Converter<List<String>> { | ||
| 24 | + | ||
| 25 | + // 横向排列:每张图片宽度 + 间距 | ||
| 26 | + private static final int IMAGE_WIDTH = 120; | ||
| 27 | + private static final int IMAGE_GAP = 15; | ||
| 28 | + private static final int MAX_IMAGE_SIZE = 10 * 1024 * 1024; | ||
| 29 | + private static final int CONNECT_TIMEOUT = 5000; | ||
| 30 | + private static final int READ_TIMEOUT = 10000; | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public Class<?> supportJavaTypeKey() { | ||
| 34 | + return List.class; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public CellDataTypeEnum supportExcelTypeKey() { | ||
| 39 | + return CellDataTypeEnum.EMPTY; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @Override | ||
| 43 | + public List<String> convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty, | ||
| 44 | + GlobalConfiguration globalConfiguration) { | ||
| 45 | + return null; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + public WriteCellData<?> convertToExcelData(List<String> imageList, ExcelContentProperty contentProperty, | ||
| 50 | + GlobalConfiguration globalConfiguration) { | ||
| 51 | + try { | ||
| 52 | + if (imageList == null || imageList.isEmpty()) { | ||
| 53 | + return new WriteCellData<>("无图片"); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + List<ImageData> imageDataList = ListUtils.newArrayList(); | ||
| 57 | + int leftOffset = 0; // 横向偏移量 | ||
| 58 | + | ||
| 59 | + for (String path : imageList) { | ||
| 60 | + if (!StringUtils.hasText(path)) continue; | ||
| 61 | + | ||
| 62 | + byte[] imageBytes = path.startsWith("http") ? loadNetworkImage(path) : loadLocalImage(path); | ||
| 63 | + if (imageBytes == null || imageBytes.length == 0 || imageBytes.length > MAX_IMAGE_SIZE) { | ||
| 64 | + log.warn("跳过无效图片:{}", path); | ||
| 65 | + continue; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + ImageData imageData = new ImageData(); | ||
| 69 | + imageData.setImage(imageBytes); | ||
| 70 | + imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG); | ||
| 71 | + | ||
| 72 | + // ====================== 核心:横向偏移 ====================== | ||
| 73 | + imageData.setLeft(leftOffset); | ||
| 74 | + // ============================================================= | ||
| 75 | + | ||
| 76 | + imageDataList.add(imageData); | ||
| 77 | + leftOffset += IMAGE_WIDTH + IMAGE_GAP; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + if (imageDataList.isEmpty()) { | ||
| 81 | + return new WriteCellData<>("无有效图片"); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + WriteCellData<?> writeCellData = new WriteCellData<>(); | ||
| 85 | + writeCellData.setType(CellDataTypeEnum.EMPTY); | ||
| 86 | + writeCellData.setImageDataList(imageDataList); | ||
| 87 | + | ||
| 88 | + return writeCellData; | ||
| 89 | + | ||
| 90 | + } catch (Exception e) { | ||
| 91 | + log.error("图片导出失败", e); | ||
| 92 | + return new WriteCellData<>("图片加载失败"); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + private byte[] loadLocalImage(String imagePath) throws Exception { | ||
| 97 | + File file = new File(imagePath); | ||
| 98 | + if (!file.exists() || !file.isFile()) { | ||
| 99 | + log.warn("本地图片不存在:{}", imagePath); | ||
| 100 | + return null; | ||
| 101 | + } | ||
| 102 | + return FileUtils.readFileToByteArray(file); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + private byte[] loadNetworkImage(String imageUrl) throws Exception { | ||
| 106 | + URL url = new URL(imageUrl); | ||
| 107 | + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | ||
| 108 | + conn.setConnectTimeout(CONNECT_TIMEOUT); | ||
| 109 | + conn.setReadTimeout(READ_TIMEOUT); | ||
| 110 | + try { | ||
| 111 | + if (conn.getResponseCode() != 200) return null; | ||
| 112 | + try (InputStream in = conn.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { | ||
| 113 | + byte[] buffer = new byte[4096]; | ||
| 114 | + int len; | ||
| 115 | + while ((len = in.read(buffer)) != -1) out.write(buffer, 0, len); | ||
| 116 | + return out.toByteArray(); | ||
| 117 | + } | ||
| 118 | + } finally { | ||
| 119 | + conn.disconnect(); | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | +} |
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/handler/ImageColumnWidthHandler.java
0 → 100644
| 1 | +package com.zteits.urbanops.framework.excel.core.handler; | ||
| 2 | + | ||
| 3 | +import cn.idev.excel.write.handler.SheetWriteHandler; | ||
| 4 | +import cn.idev.excel.write.handler.context.SheetWriteHandlerContext; | ||
| 5 | +import lombok.RequiredArgsConstructor; | ||
| 6 | +import org.apache.poi.ss.usermodel.Sheet; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 图片列自动加宽处理器 | ||
| 10 | + */ | ||
| 11 | +@RequiredArgsConstructor | ||
| 12 | +public class ImageColumnWidthHandler implements SheetWriteHandler { | ||
| 13 | + | ||
| 14 | + // 需要加宽的列索引(从0开始),比如你的图片列在第3列,就传2 | ||
| 15 | + private final int imageColumnIndex; | ||
| 16 | + // 加宽后的列宽(单位:字符宽度,Excel默认1个字符宽度≈256像素) | ||
| 17 | + private final int columnWidth; | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public void afterSheetCreate(SheetWriteHandlerContext context) { | ||
| 21 | + Sheet sheet = context.getWriteSheetHolder().getSheet(); | ||
| 22 | + // 设置指定列的宽度 | ||
| 23 | + sheet.setColumnWidth(imageColumnIndex, columnWidth); | ||
| 24 | + } | ||
| 25 | +} |
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/util/ExcelUtils.java
| @@ -44,9 +44,27 @@ public class ExcelUtils { | @@ -44,9 +44,27 @@ public class ExcelUtils { | ||
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException { | 46 | public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException { |
| 47 | - return FastExcelFactory.read(file.getInputStream(), head, null) | ||
| 48 | - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 | ||
| 49 | - .doReadAllSync(); | 47 | + List<T> list = FastExcelFactory.read(file.getInputStream(), head, null) |
| 48 | + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 | ||
| 49 | + .doReadAllSync(); | ||
| 50 | + | ||
| 51 | + // 过滤:对象不为 null + 不是全字段 null | ||
| 52 | + if (list != null) { | ||
| 53 | + list.removeIf(item -> { | ||
| 54 | + if (item == null) return true; | ||
| 55 | + // 判断所有字段是否都为 null | ||
| 56 | + for (java.lang.reflect.Field field : item.getClass().getDeclaredFields()) { | ||
| 57 | + field.setAccessible(true); | ||
| 58 | + try { | ||
| 59 | + if (field.get(item) != null) { | ||
| 60 | + return false; | ||
| 61 | + } | ||
| 62 | + } catch (Exception e) { } | ||
| 63 | + } | ||
| 64 | + return true; | ||
| 65 | + }); | ||
| 66 | + } | ||
| 67 | + return list; | ||
| 50 | } | 68 | } |
| 51 | 69 | ||
| 52 | public static <T> List<T> read(MultipartFile file, Class<T> head, Integer startIndex) throws IOException { | 70 | public static <T> List<T> read(MultipartFile file, Class<T> head, Integer startIndex) throws IOException { |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/vo/EmergencyTaskExcleRespVO.java
| @@ -2,8 +2,9 @@ package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo; | @@ -2,8 +2,9 @@ package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo; | ||
| 2 | 2 | ||
| 3 | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | 3 | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 4 | import cn.idev.excel.annotation.ExcelProperty; | 4 | import cn.idev.excel.annotation.ExcelProperty; |
| 5 | +import cn.idev.excel.annotation.write.style.ColumnWidth; | ||
| 6 | +import com.zteits.urbanops.framework.excel.core.convert.ImageListConverter; | ||
| 5 | import com.zteits.urbanops.framework.excel.core.convert.ListToStringConverter; | 7 | import com.zteits.urbanops.framework.excel.core.convert.ListToStringConverter; |
| 6 | -import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO; | ||
| 7 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| 8 | import lombok.Data; | 9 | import lombok.Data; |
| 9 | 10 | ||
| @@ -52,7 +53,9 @@ public class EmergencyTaskExcleRespVO { | @@ -52,7 +53,9 @@ public class EmergencyTaskExcleRespVO { | ||
| 52 | private LocalDateTime rescueStartTime; | 53 | private LocalDateTime rescueStartTime; |
| 53 | 54 | ||
| 54 | @Schema(description = "抢险中照片", requiredMode = Schema.RequiredMode.REQUIRED) | 55 | @Schema(description = "抢险中照片", requiredMode = Schema.RequiredMode.REQUIRED) |
| 55 | - @ExcelProperty(value="抢险中照片", converter = ListToStringConverter.class) | 56 | + //@ExcelProperty(value="抢险中照片", converter = ListToStringConverter.class) |
| 57 | + @ExcelProperty(value = "抢险中照片", converter = ImageListConverter.class) | ||
| 58 | + @ColumnWidth(70) | ||
| 56 | private List<String> emergencyImg; | 59 | private List<String> emergencyImg; |
| 57 | 60 | ||
| 58 | @Schema(description = "树种(如:国槐)") | 61 | @Schema(description = "树种(如:国槐)") |
| @@ -72,7 +75,9 @@ public class EmergencyTaskExcleRespVO { | @@ -72,7 +75,9 @@ public class EmergencyTaskExcleRespVO { | ||
| 72 | private Integer personnelCount; | 75 | private Integer personnelCount; |
| 73 | 76 | ||
| 74 | @Schema(description = "抢险结束照片", requiredMode = Schema.RequiredMode.REQUIRED) | 77 | @Schema(description = "抢险结束照片", requiredMode = Schema.RequiredMode.REQUIRED) |
| 75 | - @ExcelProperty(value="抢险结束照片", converter = ListToStringConverter.class) | 78 | + //@ExcelProperty(value="抢险结束照片", converter = ListToStringConverter.class) |
| 79 | + @ExcelProperty(value = "抢险结束照片", converter = ImageListConverter.class) | ||
| 80 | + @ColumnWidth(70) | ||
| 76 | private List<String> emergencyEndImg; | 81 | private List<String> emergencyEndImg; |
| 77 | 82 | ||
| 78 | @Schema(description = "备注") | 83 | @Schema(description = "备注") |
| @@ -111,4 +116,4 @@ public class EmergencyTaskExcleRespVO { | @@ -111,4 +116,4 @@ public class EmergencyTaskExcleRespVO { | ||
| 111 | @ExcelProperty("归属公司") | 116 | @ExcelProperty("归属公司") |
| 112 | private String companyName; | 117 | private String companyName; |
| 113 | 118 | ||
| 114 | -} | ||
| 115 | \ No newline at end of file | 119 | \ No newline at end of file |
| 120 | +} |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/user/UserController.java
| @@ -18,11 +18,13 @@ import com.zteits.urbanops.module.system.service.dept.DeptService; | @@ -18,11 +18,13 @@ import com.zteits.urbanops.module.system.service.dept.DeptService; | ||
| 18 | import com.zteits.urbanops.module.system.service.permission.PermissionService; | 18 | import com.zteits.urbanops.module.system.service.permission.PermissionService; |
| 19 | import com.zteits.urbanops.module.system.service.permission.RoleService; | 19 | import com.zteits.urbanops.module.system.service.permission.RoleService; |
| 20 | import com.zteits.urbanops.module.system.service.user.AdminUserService; | 20 | import com.zteits.urbanops.module.system.service.user.AdminUserService; |
| 21 | +import com.zteits.urbanops.module.system.util.dept.DeptCacheHelper; | ||
| 21 | import io.swagger.v3.oas.annotations.Operation; | 22 | import io.swagger.v3.oas.annotations.Operation; |
| 22 | import io.swagger.v3.oas.annotations.Parameter; | 23 | import io.swagger.v3.oas.annotations.Parameter; |
| 23 | import io.swagger.v3.oas.annotations.Parameters; | 24 | import io.swagger.v3.oas.annotations.Parameters; |
| 24 | import io.swagger.v3.oas.annotations.tags.Tag; | 25 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 25 | import jakarta.annotation.Resource; | 26 | import jakarta.annotation.Resource; |
| 27 | +import jakarta.annotation.security.PermitAll; | ||
| 26 | import jakarta.servlet.http.HttpServletResponse; | 28 | import jakarta.servlet.http.HttpServletResponse; |
| 27 | import jakarta.validation.Valid; | 29 | import jakarta.validation.Valid; |
| 28 | import org.springframework.security.access.prepost.PreAuthorize; | 30 | import org.springframework.security.access.prepost.PreAuthorize; |
| @@ -40,6 +42,7 @@ import java.util.stream.Collectors; | @@ -40,6 +42,7 @@ import java.util.stream.Collectors; | ||
| 40 | import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.EXPORT; | 42 | import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| 41 | import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | 43 | import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; |
| 42 | import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertList; | 44 | import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertList; |
| 45 | +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getDeptId; | ||
| 43 | 46 | ||
| 44 | @Tag(name = "管理后台 - 用户") | 47 | @Tag(name = "管理后台 - 用户") |
| 45 | @RestController | 48 | @RestController |
| @@ -213,11 +216,31 @@ public class UserController { | @@ -213,11 +216,31 @@ public class UserController { | ||
| 213 | public void importTemplate(HttpServletResponse response) throws IOException { | 216 | public void importTemplate(HttpServletResponse response) throws IOException { |
| 214 | // 手动创建导出 demo | 217 | // 手动创建导出 demo |
| 215 | List<UserImportExcelVO> list = Arrays.asList( | 218 | List<UserImportExcelVO> list = Arrays.asList( |
| 216 | - UserImportExcelVO.builder().username("yunai").deptId(1L).email("yunai@iocoder.cn").mobile("15601691300") | ||
| 217 | - .nickname("全域").status(CommonStatusEnum.ENABLE.getStatus()).sex(SexEnum.MALE.getSex()).build(), | ||
| 218 | - UserImportExcelVO.builder().username("yuanma").deptId(2L).email("yuanma@iocoder.cn").mobile("15601701300") | ||
| 219 | - .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SexEnum.FEMALE.getSex()).build() | 219 | + UserImportExcelVO.builder() |
| 220 | + .username("yunai") | ||
| 221 | + .deptId(255L) | ||
| 222 | + .email("yunai@iocoder.cn") | ||
| 223 | + .mobile("15601691300") | ||
| 224 | + .nickname("全域") | ||
| 225 | + // .status(CommonStatusEnum.ENABLE.getStatus()) | ||
| 226 | + .sex(SexEnum.MALE.getSex()) | ||
| 227 | + .busiLine("园林") // 简洁示例 | ||
| 228 | + .isInner(1) | ||
| 229 | + .build(), | ||
| 230 | + UserImportExcelVO.builder() | ||
| 231 | + .username("yuanma") | ||
| 232 | + .deptId(255L) | ||
| 233 | + .email("yuanma@iocoder.cn") | ||
| 234 | + .mobile("15601701300") | ||
| 235 | + .nickname("源码") | ||
| 236 | + //.status(CommonStatusEnum.DISABLE.getStatus()) | ||
| 237 | + .sex(SexEnum.FEMALE.getSex()) | ||
| 238 | + .busiLine("园林,物业") | ||
| 239 | + .isInner(1) | ||
| 240 | + .build() | ||
| 220 | ); | 241 | ); |
| 242 | + //初始化部门 | ||
| 243 | + DeptCacheHelper.init(deptService, getDeptId()); | ||
| 221 | // 输出 | 244 | // 输出 |
| 222 | ExcelUtils.write(response, "用户导入模板.xls", "用户列表", UserImportExcelVO.class, list); | 245 | ExcelUtils.write(response, "用户导入模板.xls", "用户列表", UserImportExcelVO.class, list); |
| 223 | } | 246 | } |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/user/vo/user/UserImportExcelVO.java
| @@ -2,8 +2,12 @@ package com.zteits.urbanops.module.system.controller.admin.user.vo.user; | @@ -2,8 +2,12 @@ package com.zteits.urbanops.module.system.controller.admin.user.vo.user; | ||
| 2 | 2 | ||
| 3 | import cn.idev.excel.annotation.ExcelProperty; | 3 | import cn.idev.excel.annotation.ExcelProperty; |
| 4 | import com.zteits.urbanops.framework.excel.core.annotations.DictFormat; | 4 | import com.zteits.urbanops.framework.excel.core.annotations.DictFormat; |
| 5 | +import com.zteits.urbanops.framework.excel.core.annotations.ExcelColumnSelect; | ||
| 5 | import com.zteits.urbanops.framework.excel.core.convert.DictConvert; | 6 | import com.zteits.urbanops.framework.excel.core.convert.DictConvert; |
| 6 | import com.zteits.urbanops.module.system.enums.DictTypeConstants; | 7 | import com.zteits.urbanops.module.system.enums.DictTypeConstants; |
| 8 | +import com.zteits.urbanops.framework.excel.core.convert.BusinessLineConvert; | ||
| 9 | +import com.zteits.urbanops.module.system.framework.excel.convert.DeptConvert; | ||
| 10 | +import com.zteits.urbanops.module.system.framework.excel.core.DeptExcelColumnSelectFunction; | ||
| 7 | import lombok.AllArgsConstructor; | 11 | import lombok.AllArgsConstructor; |
| 8 | import lombok.Builder; | 12 | import lombok.Builder; |
| 9 | import lombok.Data; | 13 | import lombok.Data; |
| @@ -18,13 +22,14 @@ import lombok.NoArgsConstructor; | @@ -18,13 +22,14 @@ import lombok.NoArgsConstructor; | ||
| 18 | @NoArgsConstructor | 22 | @NoArgsConstructor |
| 19 | public class UserImportExcelVO { | 23 | public class UserImportExcelVO { |
| 20 | 24 | ||
| 21 | - @ExcelProperty("登录名称") | 25 | + @ExcelProperty("用户账户") |
| 22 | private String username; | 26 | private String username; |
| 23 | 27 | ||
| 24 | @ExcelProperty("用户名称") | 28 | @ExcelProperty("用户名称") |
| 25 | private String nickname; | 29 | private String nickname; |
| 26 | 30 | ||
| 27 | - @ExcelProperty("部门编号") | 31 | + @ExcelProperty(value = "部门编号", converter = DeptConvert.class) |
| 32 | + @ExcelColumnSelect(functionName = DeptExcelColumnSelectFunction.NAME) | ||
| 28 | private Long deptId; | 33 | private Long deptId; |
| 29 | 34 | ||
| 30 | @ExcelProperty("用户邮箱") | 35 | @ExcelProperty("用户邮箱") |
| @@ -37,11 +42,14 @@ public class UserImportExcelVO { | @@ -37,11 +42,14 @@ public class UserImportExcelVO { | ||
| 37 | @DictFormat(DictTypeConstants.USER_SEX) | 42 | @DictFormat(DictTypeConstants.USER_SEX) |
| 38 | private Integer sex; | 43 | private Integer sex; |
| 39 | 44 | ||
| 40 | - @ExcelProperty(value = "账号状态", converter = DictConvert.class) | ||
| 41 | - @DictFormat(DictTypeConstants.COMMON_STATUS) | ||
| 42 | - private Integer status; | 45 | + //@ExcelProperty(value = "账号状态", converter = DictConvert.class) |
| 46 | + //@DictFormat(DictTypeConstants.COMMON_STATUS) | ||
| 47 | + //private Integer status; | ||
| 43 | 48 | ||
| 44 | - @ExcelProperty("业务线") | 49 | + @ExcelProperty(value = "业务线", converter = BusinessLineConvert.class) |
| 45 | private String busiLine; | 50 | private String busiLine; |
| 46 | 51 | ||
| 52 | + @ExcelProperty(value = "是否是内部员工", converter = DictConvert.class) | ||
| 53 | + @DictFormat(DictTypeConstants.SYSTEM_IS_INNER) | ||
| 54 | + private Integer isInner; | ||
| 47 | } | 55 | } |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/enums/DictTypeConstants.java
| @@ -30,6 +30,8 @@ public interface DictTypeConstants { | @@ -30,6 +30,8 @@ public interface DictTypeConstants { | ||
| 30 | 30 | ||
| 31 | String WORKORDER_TYPE = "work_order_type"; //工单类型 | 31 | String WORKORDER_TYPE = "work_order_type"; //工单类型 |
| 32 | 32 | ||
| 33 | + String SYSTEM_IS_INNER = "system_is_inner";//是否是内部员工 | ||
| 34 | + | ||
| 33 | 35 | ||
| 34 | 36 | ||
| 35 | 37 |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/framework/excel/convert/DeptConvert.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.framework.excel.convert; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.convert.Convert; | ||
| 4 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 5 | +import cn.idev.excel.converters.Converter; | ||
| 6 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 7 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 8 | +import cn.idev.excel.metadata.data.ReadCellData; | ||
| 9 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 10 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 11 | +import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; | ||
| 12 | +import com.zteits.urbanops.module.system.service.dept.DeptService; | ||
| 13 | +import lombok.extern.slf4j.Slf4j; | ||
| 14 | + | ||
| 15 | +import java.util.List; | ||
| 16 | +import java.util.Map; | ||
| 17 | +import java.util.stream.Collectors; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * 若依框架 部门Excel转换器(官方规范版) | ||
| 21 | + * 全程走缓存,绝不重复查询数据库 | ||
| 22 | + * | ||
| 23 | + * @author ruoyi | ||
| 24 | + */ | ||
| 25 | +@Slf4j | ||
| 26 | +public class DeptConvert implements Converter<Object> { | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 若依 部门Service(走系统缓存) | ||
| 30 | + */ | ||
| 31 | + private static final DeptService deptService = SpringUtil.getBean(DeptService.class); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 若依系统全局缓存:ID -> 部门名称 | ||
| 35 | + */ | ||
| 36 | + private static final Map<Long, String> DEPT_CACHE; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 若依系统全局缓存:名称 -> 部门ID | ||
| 40 | + */ | ||
| 41 | + private static final Map<String, Long> DEPT_NAME_CACHE; | ||
| 42 | + | ||
| 43 | + // ===================== 静态初始化:只加载一次 ===================== | ||
| 44 | + static { | ||
| 45 | + // 从若依缓存获取所有部门(只查一次库,后面全走Redis) | ||
| 46 | + List<DeptDO> deptList = deptService.getChildDeptList(0L); | ||
| 47 | + | ||
| 48 | + // ID -> 名称(导出用) | ||
| 49 | + DEPT_CACHE = deptList.stream() | ||
| 50 | + .collect(Collectors.toMap(DeptDO::getId, DeptDO::getName)); | ||
| 51 | + | ||
| 52 | + // 名称 -> ID(导入用) | ||
| 53 | + DEPT_NAME_CACHE = deptList.stream() | ||
| 54 | + .collect(Collectors.toMap(DeptDO::getName, DeptDO::getId)); | ||
| 55 | + | ||
| 56 | + log.info("【若依部门Excel转换器】初始化缓存成功,共 {} 个部门", deptList.size()); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + @Override | ||
| 60 | + public Class<?> supportJavaTypeKey() { | ||
| 61 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @Override | ||
| 65 | + public CellDataTypeEnum supportExcelTypeKey() { | ||
| 66 | + throw new UnsupportedOperationException("暂不支持,也不需要"); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + // ===================== 导入:Excel部门名称 → ID ===================== | ||
| 70 | + @Override | ||
| 71 | + public Object convertToJavaData(ReadCellData<?> readCellData, ExcelContentProperty contentProperty, | ||
| 72 | + GlobalConfiguration globalConfiguration) { | ||
| 73 | + String deptName = readCellData.getStringValue(); | ||
| 74 | + | ||
| 75 | + // 从缓存获取(0 查库) | ||
| 76 | + Long deptId = DEPT_NAME_CACHE.get(deptName); | ||
| 77 | + if (deptId == null) { | ||
| 78 | + log.error("[convertToJavaData][部门名称({}) 不存在]", deptName); | ||
| 79 | + return null; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + // 类型转换 | ||
| 83 | + Class<?> fieldClazz = contentProperty.getField().getType(); | ||
| 84 | + return Convert.convert(fieldClazz, deptId); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + // ===================== 导出:ID → 部门名称 ===================== | ||
| 88 | + @Override | ||
| 89 | + public WriteCellData<?> convertToExcelData(Object value, ExcelContentProperty contentProperty, | ||
| 90 | + GlobalConfiguration globalConfiguration) { | ||
| 91 | + if (value == null) { | ||
| 92 | + return new WriteCellData<>(""); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + Long deptId = Convert.toLong(value); | ||
| 96 | + // 从缓存获取名称(0 查库) | ||
| 97 | + String deptName = DEPT_CACHE.get(deptId); | ||
| 98 | + | ||
| 99 | + if (deptName == null) { | ||
| 100 | + log.error("[convertToExcelData][部门ID({}) 不存在]", deptId); | ||
| 101 | + return new WriteCellData<>(""); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + return new WriteCellData<>(deptName); | ||
| 105 | + } | ||
| 106 | +} |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/framework/excel/core/DeptExcelColumnSelectFunction.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.framework.excel.core; | ||
| 2 | + | ||
| 3 | +import com.zteits.urbanops.framework.excel.core.function.ExcelColumnSelectFunction; | ||
| 4 | +import com.zteits.urbanops.module.system.util.dept.DeptCacheHelper; | ||
| 5 | +import org.springframework.stereotype.Service; | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +@Service | ||
| 9 | +public class DeptExcelColumnSelectFunction implements ExcelColumnSelectFunction { | ||
| 10 | + | ||
| 11 | + public static final String NAME = "deptNameList"; | ||
| 12 | + | ||
| 13 | + @Override | ||
| 14 | + public String getName() { | ||
| 15 | + return NAME; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 从缓存获取部门名称列表,作为Excel下拉选项 | ||
| 20 | + */ | ||
| 21 | + @Override | ||
| 22 | + public List<String> getOptions() { | ||
| 23 | + return DeptCacheHelper.getNameList(); // 直接走缓存! | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | +} |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/util/dept/DeptCacheHelper.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.util.dept; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; | ||
| 5 | +import com.zteits.urbanops.module.system.service.dept.DeptService; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import java.util.*; | ||
| 8 | +import java.util.function.Function; | ||
| 9 | +import java.util.stream.Collectors; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 部门全局缓存工具 | ||
| 13 | + * 给 Excel 导入导出、下拉框、转换器统一使用 | ||
| 14 | + * 只加载一次,不重复查询 | ||
| 15 | + */ | ||
| 16 | +@Slf4j | ||
| 17 | +public class DeptCacheHelper { | ||
| 18 | + | ||
| 19 | + private static List<DeptDO> DEPT_LIST; | ||
| 20 | + private static Map<Long, DeptDO> DEPT_ID_MAP; | ||
| 21 | + private static Map<String, DeptDO> DEPT_NAME_MAP; | ||
| 22 | + private static List<String> DEPT_NAME_LIST; | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 初始化部门缓存(在导入模板接口中主动调用) | ||
| 26 | + */ | ||
| 27 | + public static void init(DeptService deptService, Long rootDeptId) { | ||
| 28 | + | ||
| 29 | + //每次调用都使用传入的 deptId,每次可刷新 | ||
| 30 | + if (DEPT_LIST != null) { | ||
| 31 | + // 清空旧缓存 | ||
| 32 | + DEPT_LIST = null; | ||
| 33 | + DEPT_ID_MAP = null; | ||
| 34 | + DEPT_NAME_MAP = null; | ||
| 35 | + DEPT_NAME_LIST = null; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + try { | ||
| 39 | + | ||
| 40 | + // 1. 批量查询部门(只查一次) | ||
| 41 | + DEPT_LIST = deptService.getLastDeptList(rootDeptId); | ||
| 42 | + | ||
| 43 | + if (CollUtil.isEmpty(DEPT_LIST)) { | ||
| 44 | + log.warn("[DeptCacheHelper] 未查询到部门数据"); | ||
| 45 | + return; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + // 3. 构建各种缓存 | ||
| 49 | + DEPT_ID_MAP = DEPT_LIST.stream() | ||
| 50 | + .collect(Collectors.toMap(DeptDO::getId, Function.identity())); | ||
| 51 | + | ||
| 52 | + DEPT_NAME_MAP = DEPT_LIST.stream() | ||
| 53 | + .collect(Collectors.toMap(DeptDO::getName, Function.identity())); | ||
| 54 | + | ||
| 55 | + DEPT_NAME_LIST = DEPT_LIST.stream() | ||
| 56 | + .map(DeptDO::getName) | ||
| 57 | + .collect(Collectors.toList()); | ||
| 58 | + | ||
| 59 | + log.info("[DeptCacheHelper] 部门缓存初始化成功,共 {} 个部门", DEPT_LIST.size()); | ||
| 60 | + } catch (Exception e) { | ||
| 61 | + log.error("[DeptCacheHelper] 部门缓存初始化失败", e); | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + // ===================== 提供给外部使用 ===================== | ||
| 66 | + public static DeptDO getById(Long id) { | ||
| 67 | + return DEPT_ID_MAP == null ? null : DEPT_ID_MAP.get(id); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + public static DeptDO getByName(String name) { | ||
| 71 | + return DEPT_NAME_MAP == null ? null : DEPT_NAME_MAP.get(name); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + public static List<String> getNameList() { | ||
| 75 | + return DEPT_NAME_LIST == null ? Collections.emptyList() : DEPT_NAME_LIST; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + public static boolean isEmpty() { | ||
| 79 | + return CollUtil.isEmpty(DEPT_LIST); | ||
| 80 | + } | ||
| 81 | +} |
urbanops-module-system/src/test/java/com/zteits/urbanops/module/system/service/user/AdminUserServiceImplTest.java
| @@ -482,7 +482,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { | @@ -482,7 +482,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { | ||
| 482 | public void testImportUserList_02() { | 482 | public void testImportUserList_02() { |
| 483 | // 准备参数 | 483 | // 准备参数 |
| 484 | UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | 484 | UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { |
| 485 | - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | 485 | + //o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 |
| 486 | o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | 486 | o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 |
| 487 | o.setEmail(randomEmail()); | 487 | o.setEmail(randomEmail()); |
| 488 | o.setMobile(randomMobile()); | 488 | o.setMobile(randomMobile()); |
| @@ -517,7 +517,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { | @@ -517,7 +517,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { | ||
| 517 | userMapper.insert(dbUser); | 517 | userMapper.insert(dbUser); |
| 518 | // 准备参数 | 518 | // 准备参数 |
| 519 | UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | 519 | UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { |
| 520 | - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | 520 | + //o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 |
| 521 | o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | 521 | o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 |
| 522 | o.setUsername(dbUser.getUsername()); | 522 | o.setUsername(dbUser.getUsername()); |
| 523 | o.setEmail(randomEmail()); | 523 | o.setEmail(randomEmail()); |
| @@ -549,7 +549,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { | @@ -549,7 +549,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { | ||
| 549 | userMapper.insert(dbUser); | 549 | userMapper.insert(dbUser); |
| 550 | // 准备参数 | 550 | // 准备参数 |
| 551 | UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { | 551 | UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { |
| 552 | - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | 552 | + //o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 |
| 553 | o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 | 553 | o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 |
| 554 | o.setUsername(dbUser.getUsername()); | 554 | o.setUsername(dbUser.getUsername()); |
| 555 | o.setEmail(randomEmail()); | 555 | o.setEmail(randomEmail()); |