Commit 5f6887bb7affa493e94d3e13387beaa52bce943b

Authored by wangqian
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 44 }
45 45  
46 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 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 2  
3 3 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
4 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 7 import com.zteits.urbanops.framework.excel.core.convert.ListToStringConverter;
6   -import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO;
7 8 import io.swagger.v3.oas.annotations.media.Schema;
8 9 import lombok.Data;
9 10  
... ... @@ -52,7 +53,9 @@ public class EmergencyTaskExcleRespVO {
52 53 private LocalDateTime rescueStartTime;
53 54  
54 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 59 private List<String> emergencyImg;
57 60  
58 61 @Schema(description = "树种(如:国槐)")
... ... @@ -72,7 +75,9 @@ public class EmergencyTaskExcleRespVO {
72 75 private Integer personnelCount;
73 76  
74 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 81 private List<String> emergencyEndImg;
77 82  
78 83 @Schema(description = "备注")
... ... @@ -111,4 +116,4 @@ public class EmergencyTaskExcleRespVO {
111 116 @ExcelProperty("归属公司")
112 117 private String companyName;
113 118  
114   -}
115 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 18 import com.zteits.urbanops.module.system.service.permission.PermissionService;
19 19 import com.zteits.urbanops.module.system.service.permission.RoleService;
20 20 import com.zteits.urbanops.module.system.service.user.AdminUserService;
  21 +import com.zteits.urbanops.module.system.util.dept.DeptCacheHelper;
21 22 import io.swagger.v3.oas.annotations.Operation;
22 23 import io.swagger.v3.oas.annotations.Parameter;
23 24 import io.swagger.v3.oas.annotations.Parameters;
24 25 import io.swagger.v3.oas.annotations.tags.Tag;
25 26 import jakarta.annotation.Resource;
  27 +import jakarta.annotation.security.PermitAll;
26 28 import jakarta.servlet.http.HttpServletResponse;
27 29 import jakarta.validation.Valid;
28 30 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -40,6 +42,7 @@ import java.util.stream.Collectors;
40 42 import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
41 43 import static com.zteits.urbanops.framework.common.pojo.CommonResult.success;
42 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 47 @Tag(name = "管理后台 - 用户")
45 48 @RestController
... ... @@ -213,11 +216,31 @@ public class UserController {
213 216 public void importTemplate(HttpServletResponse response) throws IOException {
214 217 // 手动创建导出 demo
215 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 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 2  
3 3 import cn.idev.excel.annotation.ExcelProperty;
4 4 import com.zteits.urbanops.framework.excel.core.annotations.DictFormat;
  5 +import com.zteits.urbanops.framework.excel.core.annotations.ExcelColumnSelect;
5 6 import com.zteits.urbanops.framework.excel.core.convert.DictConvert;
6 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 11 import lombok.AllArgsConstructor;
8 12 import lombok.Builder;
9 13 import lombok.Data;
... ... @@ -18,13 +22,14 @@ import lombok.NoArgsConstructor;
18 22 @NoArgsConstructor
19 23 public class UserImportExcelVO {
20 24  
21   - @ExcelProperty("登录名称")
  25 + @ExcelProperty("用户账户")
22 26 private String username;
23 27  
24 28 @ExcelProperty("用户名称")
25 29 private String nickname;
26 30  
27   - @ExcelProperty("部门编号")
  31 + @ExcelProperty(value = "部门编号", converter = DeptConvert.class)
  32 + @ExcelColumnSelect(functionName = DeptExcelColumnSelectFunction.NAME)
28 33 private Long deptId;
29 34  
30 35 @ExcelProperty("用户邮箱")
... ... @@ -37,11 +42,14 @@ public class UserImportExcelVO {
37 42 @DictFormat(DictTypeConstants.USER_SEX)
38 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 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 30  
31 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 482 public void testImportUserList_02() {
483 483 // 准备参数
484 484 UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
485   - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
  485 + //o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
486 486 o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
487 487 o.setEmail(randomEmail());
488 488 o.setMobile(randomMobile());
... ... @@ -517,7 +517,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
517 517 userMapper.insert(dbUser);
518 518 // 准备参数
519 519 UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
520   - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
  520 + //o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
521 521 o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
522 522 o.setUsername(dbUser.getUsername());
523 523 o.setEmail(randomEmail());
... ... @@ -549,7 +549,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
549 549 userMapper.insert(dbUser);
550 550 // 准备参数
551 551 UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
552   - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
  552 + //o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
553 553 o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
554 554 o.setUsername(dbUser.getUsername());
555 555 o.setEmail(randomEmail());
... ...