fix(excel): Excel 导入报错统一转为可读中文提示,不再出现「系统异常」500

问题:119 个导入接口中有 52 个直接调用 ExcelUtils.read 且无 try-catch,任何坏文件 / 空文件 / 模板不匹配都会抛底层异常,前端只看到「系统异常」。

修复思路:不逐个改 52 个 Controller,而是在唯一的入口 ExcelUtils.read 统一收敛。

1. 新增 ExcelErrorCodeConstants(1_099_xxx 区间,框架级):文件为空 / 读取失败 / 内容为空 / 表头不匹配 / 模板不匹配 / 单元格格式错误

2. ExcelUtils.read 逐类识别异常并转成可读提示:

- 空文件 -> 请选择要导入的 Excel 文件

- IOException -> Excel 文件读取失败,请确认文件未损坏且为 .xls/.xlsx 格式

- ExcelDataConvertException -> 第 N 行第 M 列数据格式不正确:xxx

- ExcelAnalysisException -> 表头/模板不匹配,请下载最新模板重新填写

- 其它异常兜底 -> Excel 文件读取失败

- 只有表头没有数据 -> Excel 内容为空,请按照模板填写数据后再导入

3. GlobalExceptionHandler 补齐客户端问题不报 500:

- MultipartException(导入接口漏传文件)-> 400

- 其它反序列化失败 -> 400,不再落到 defaultExceptionHandler

- buildExceptionLog 对 NOT NULL 列做空值兜底,避免异常日志本身插入失败

4. UserController 移除与 ExcelUtils 重复的 try-catch(否则会吞掉更精确的提示)

5. 同步更新 AdminUserServiceImplTest 四个导入用例以适配 deptName
已添加1个文件
已修改4个文件
190 ■■■■■ 文件已修改
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelErrorCodeConstants.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelErrorCodeConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.framework.excel.core.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
 * Excel ç›¸å…³çš„错误码
 *
 * <p>统一在 {@link cn.iocoder.yudao.framework.excel.core.util.ExcelUtils} ä¸­æŠ›å‡ºï¼Œ
 * è®©æ‰€æœ‰å¯¼å…¥æŽ¥å£åœ¨é‡åˆ°åæ–‡ä»¶ã€æ ¼å¼ä¸å¯¹ã€æ¨¡æ¿ä¸åŒ¹é…æ—¶éƒ½è¿”回可读的中文提示,
 * è€Œä¸æ˜¯å«ç³Šçš„「系统异常」500。</p>
 *
 * <p>错误码区间:1_099_000_000 ~ 1_099_999_999(框架级,避免与业务模块冲突)</p>
 *
 * @author è¶…级管理员
 */
public interface ExcelErrorCodeConstants {
    // ========== Excel æ–‡ä»¶æœ¬èº«çš„问题 ==========
    ErrorCode EXCEL_FILE_EMPTY = new ErrorCode(1_099_000_001, "请选择要导入的 Excel æ–‡ä»¶");
    ErrorCode EXCEL_FILE_READ_FAILED = new ErrorCode(1_099_000_002, "Excel æ–‡ä»¶è¯»å–失败,请确认文件未损坏且为 .xls/.xlsx æ ¼å¼");
    ErrorCode EXCEL_FILE_EMPTY_CONTENT = new ErrorCode(1_099_000_003, "Excel å†…容为空,请按照模板填写数据后再导入");
    // ========== æ¨¡æ¿ä¸åŒ¹é… ==========
    ErrorCode EXCEL_HEAD_NOT_MATCH = new ErrorCode(1_099_000_010, "Excel è¡¨å¤´ä¸Žå¯¼å…¥æ¨¡æ¿ä¸ä¸€è‡´ï¼Œè¯·ä¸‹è½½æœ€æ–°æ¨¡æ¿é‡æ–°å¡«å†™");
    ErrorCode EXCEL_TEMPLATE_NOT_MATCH = new ErrorCode(1_099_000_011, "Excel æ¨¡æ¿ç‰ˆæœ¬è¿‡æ—§æˆ–不匹配,请下载最新模板重新填写");
    // ========== å•元格数据问题 ==========
    ErrorCode EXCEL_DATA_CONVERT_FAILED = new ErrorCode(1_099_000_020, "第 %d è¡Œç¬¬ %d åˆ—数据格式不正确:%s");
    ErrorCode EXCEL_DATA_ILLEGAL = new ErrorCode(1_099_000_021, "Excel æ•°æ®æ ¼å¼ä¸æ­£ç¡®ï¼Œè¯·æ£€æŸ¥å¡«å†™å†…容后重试");
}
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
@@ -1,9 +1,16 @@
package cn.iocoder.yudao.framework.excel.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcelFactory;
import cn.idev.excel.converters.longconverter.LongStringConverter;
import cn.idev.excel.exception.ExcelAnalysisException;
import cn.idev.excel.exception.ExcelCommonException;
import cn.idev.excel.exception.ExcelDataConvertException;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
import cn.iocoder.yudao.framework.excel.core.handler.DateAsTextCellWriteHandler;
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
@@ -14,11 +21,15 @@
import java.io.InputStream;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.excel.core.enums.ExcelErrorCodeConstants.*;
/**
 * Excel å·¥å…·ç±»
 *
 * @author è¶…级管理员
 */
@Slf4j
public class ExcelUtils {
    /**
@@ -82,13 +93,61 @@
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    }
    public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
    /**
     * è¯»å– Excel æ–‡ä»¶
     *
     * <p>所有导入接口都经过这里,因此在这里统一把「坏文件 / ç©ºæ–‡ä»¶ / æ¨¡æ¿ä¸åŒ¹é… / å•元格格式错」
     * ç­‰å„种底层异常,转换成可读的中文提示(ServiceException)。
     * è¿™æ ·ä¸šåŠ¡æŽ¥å£ä¸å¿…å„è‡ªå†™ try-catch,也不会再出现含糊的「系统异常」500。</p>
     *
     * @param file Excel æ–‡ä»¶
     * @param head è¡¨å¤´å¯¹åº”çš„ VO ç±»
     * @return è§£æžç»“æžœ
     */
    public static <T> List<T> read(MultipartFile file, Class<T> head) {
        // 1. æ–‡ä»¶æœ¬èº«ä¸ºç©º
        if (file == null || file.isEmpty()) {
            throw exception(EXCEL_FILE_EMPTY);
        }
        // 2. è§£æžï¼ˆé€ç±»è¯†åˆ«å¼‚常,给出可读提示)
        List<T> list;
        // å‚考 https://t.zsxq.com/zM77F å¸–子,增加 try å¤„理,兼容 windows åœºæ™¯
        try (InputStream inputStream = file.getInputStream()) {
            return FastExcelFactory.read(inputStream, head, null)
            list = FastExcelFactory.read(inputStream, head, null)
                    .autoCloseStream(false) // ä¸è¦è‡ªåŠ¨å…³é—­ï¼Œäº¤ç»™ Servlet è‡ªå·±å¤„理
                    .doReadAllSync();
        } catch (IOException ex) {
            log.warn("[read][Excel æ–‡ä»¶è¯»å–失败,文件名({})]", file.getOriginalFilename(), ex);
            throw exception(EXCEL_FILE_READ_FAILED);
        } catch (ExcelDataConvertException ex) {
            // å•元格数据格式不对,例如文本列填了数字、日期列填了乱码
            String detail = StrUtil.blankToDefault(ex.getMessage(), "内容与列类型不匹配");
            log.warn("[read][Excel å•元格数据格式错误,行({}) åˆ—({})]", ex.getRowIndex(), ex.getColumnIndex(), ex);
            throw exception(EXCEL_DATA_CONVERT_FAILED,
                    (ex.getRowIndex() == null ? 0 : ex.getRowIndex()) + 1,
                    (ex.getColumnIndex() == null ? 0 : ex.getColumnIndex()) + 1,
                    StrUtil.maxLength(detail, 60));
        } catch (ExcelAnalysisException ex) {
            // æ–‡ä»¶æŸåã€åŠ å¯†ã€ä¸æ˜¯çœŸæ­£çš„ Excel、表头与模板对不上等
            String rootMsg = StrUtil.blankToDefault(ExceptionUtil.getRootCauseMessage(ex), "");
            log.warn("[read][Excel è§£æžå¤±è´¥ï¼Œæ–‡ä»¶å({}),原因({})]", file.getOriginalFilename(), rootMsg, ex);
            if (StrUtil.containsAnyIgnoreCase(rootMsg, "head", "Can not find", "autoTrim", "Convert")) {
                throw exception(EXCEL_HEAD_NOT_MATCH);
            }
            throw exception(EXCEL_TEMPLATE_NOT_MATCH);
        } catch (ExcelCommonException ex) {
            log.warn("[read][Excel é€šç”¨å¼‚常,文件名({})]", file.getOriginalFilename(), ex);
            throw exception(EXCEL_FILE_READ_FAILED);
        } catch (Exception ex) {
            // å…œåº•:任何未预料的解析异常都转成可读提示,避免直接 500「系统异常」
            log.warn("[read][Excel è§£æžå‡ºçŽ°æœªé¢„æœŸå¼‚å¸¸ï¼Œæ–‡ä»¶å({})]", file.getOriginalFilename(), ex);
            throw exception(EXCEL_FILE_READ_FAILED);
        }
        // 3. è§£æžå‡ºæ¥æ˜¯ç©ºè¡¨ï¼ˆåªæœ‰è¡¨å¤´ã€æ²¡æœ‰æ•°æ®è¡Œï¼‰
        if (CollUtil.isEmpty(list)) {
            throw exception(EXCEL_FILE_EMPTY_CONTENT);
        }
        return list;
    }
}
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -36,6 +36,7 @@
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import tools.jackson.databind.exc.InvalidFormatException;
@@ -96,6 +97,10 @@
        }
        if (ex instanceof MaxUploadSizeExceededException) {
            return maxUploadSizeExceededExceptionHandler((MaxUploadSizeExceededException) ex);
        }
        // ä¸Šä¼ ç±»æŽ¥å£æ”¶åˆ°éž multipart è¯·æ±‚(前端漏传文件 / ç”¨ JSON è°ƒç”¨äº†å¯¼å…¥æŽ¥å£ï¼‰å±žäºŽå®¢æˆ·ç«¯é—®é¢˜ï¼Œä¸åº”按 500 å¤„理
        if (ex instanceof MultipartException) {
            return multipartExceptionHandler((MultipartException) ex);
        }
        if (ex instanceof NoHandlerFoundException) {
            return noHandlerFoundExceptionHandler((NoHandlerFoundException) ex);
@@ -181,6 +186,18 @@
     *
     * ä¾‹å¦‚说,接口上设置了 @RequestBody å®žä½“中 xx å±žæ€§ç±»åž‹ä¸º Integer,结果传递 xx å‚数类型为 String
     */
    /**
     * å¤„理文件上传接口收到非 multipart è¯·æ±‚
     *
     * <p>例如说,Excel å¯¼å…¥æŽ¥å£æ¼ä¼  file å­—段、或前端用 application/json è°ƒç”¨äº†å¯¼å…¥æŽ¥å£ã€‚
     * è¿™ç±»éƒ½æ˜¯å®¢æˆ·ç«¯ç”¨æ³•问题,返回 400 æ˜Žç¡®æç¤ºï¼Œè€Œä¸æ˜¯ 500「系统异常」。</p>
     */
    @ExceptionHandler(MultipartException.class)
    public CommonResult<?> multipartExceptionHandler(MultipartException ex) {
        log.warn("[multipartExceptionHandler]", ex);
        return CommonResult.error(BAD_REQUEST.getCode(), "请求参数不正确:请使用 multipart/form-data ä¸Šä¼ æ–‡ä»¶");
    }
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @SuppressWarnings("PatternVariableCanBeUsed")
    public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) {
@@ -192,7 +209,9 @@
        if (StrUtil.startWith(ex.getMessage(), "Required request body is missing")) {
            return CommonResult.error(BAD_REQUEST.getCode(), "请求参数类型错误: request body ç¼ºå¤±");
        }
        return defaultExceptionHandler(ServletUtils.getRequest(), ex);
        // å…¶å®ƒååºåˆ—化失败(如把对象传成数组、字段类型不匹配)同样是客户端报文问题,不应按 500 å¤„理
        return CommonResult.error(BAD_REQUEST.getCode(),
                String.format("请求参数类型错误:%s", StrUtil.maxLength(ex.getMessage(), 120)));
    }
    /**
@@ -358,27 +377,31 @@
        errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request));
        // è®¾ç½®å¼‚常字段
        errorLog.setExceptionName(e.getClass().getName());
        errorLog.setExceptionMessage(ExceptionUtil.getMessage(e));
        errorLog.setExceptionRootCauseMessage(ExceptionUtil.getRootCauseMessage(e));
        errorLog.setExceptionStackTrace(ExceptionUtil.stacktraceToString(e));
        // infra_api_error_log çš„这些列都是 NOT NULL;而 MyBatis-Plus æ’入时会跳过 null å­—段,
        // äºŽæ˜¯ä»»ä¸€å­—段为 null éƒ½ä¼šå˜æˆ "Field 'xxx' doesn't have a default value",异常日志反而记不下来
        errorLog.setExceptionMessage(StrUtil.nullToEmpty(ExceptionUtil.getMessage(e)));
        errorLog.setExceptionRootCauseMessage(StrUtil.nullToEmpty(ExceptionUtil.getRootCauseMessage(e)));
        errorLog.setExceptionStackTrace(StrUtil.nullToEmpty(ExceptionUtil.stacktraceToString(e)));
        StackTraceElement[] stackTraceElements = e.getStackTrace();
        Assert.notEmpty(stackTraceElements, "异常 stackTraceElements ä¸èƒ½ä¸ºç©º");
        StackTraceElement stackTraceElement = stackTraceElements[0];
        errorLog.setExceptionClassName(stackTraceElement.getClassName());
        errorLog.setExceptionFileName(stackTraceElement.getFileName());
        errorLog.setExceptionMethodName(stackTraceElement.getMethodName());
        // æ ˆé¡¶å…ƒç´ å¯èƒ½æ˜¯ native æ–¹æ³•或 Lambda,getFileName() ä¸º null;
        // infra_api_error_log çš„这些列是 NOT NULL,为 null ä¼šå¯¼è‡´å¼‚常日志插入失败
        errorLog.setExceptionFileName(StrUtil.nullToEmpty(stackTraceElement.getFileName()));
        errorLog.setExceptionMethodName(StrUtil.nullToEmpty(stackTraceElement.getMethodName()));
        errorLog.setExceptionLineNumber(stackTraceElement.getLineNumber());
        // è®¾ç½®å…¶å®ƒå­—段
        errorLog.setTraceId(TracerUtils.getTraceId());
        errorLog.setApplicationName(applicationName);
        errorLog.setRequestUrl(request.getRequestURI());
        errorLog.setTraceId(StrUtil.nullToEmpty(TracerUtils.getTraceId()));
        errorLog.setApplicationName(StrUtil.nullToEmpty(applicationName));
        errorLog.setRequestUrl(StrUtil.nullToEmpty(request.getRequestURI()));
        Map<String, Object> requestParams = MapUtil.<String, Object>builder()
                .put("query", ServletUtils.getParamMap(request))
                .put("body", ServletUtils.getBody(request)).build();
        errorLog.setRequestParams(JsonUtils.toJsonString(requestParams));
        errorLog.setRequestMethod(request.getMethod());
        errorLog.setUserAgent(ServletUtils.getUserAgent(request));
        errorLog.setUserIp(ServletUtils.getClientIP(request));
        errorLog.setRequestParams(StrUtil.nullToEmpty(JsonUtils.toJsonString(requestParams)));
        errorLog.setRequestMethod(StrUtil.nullToEmpty(request.getMethod()));
        errorLog.setUserAgent(StrUtil.nullToEmpty(ServletUtils.getUserAgent(request)));
        errorLog.setUserIp(StrUtil.nullToEmpty(ServletUtils.getClientIP(request)));
        errorLog.setExceptionTime(LocalDateTime.now());
    }
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java
@@ -44,9 +44,6 @@
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_IMPORT_FILE_EMPTY;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_IMPORT_FILE_INVALID;
@Tag(name = "管理后台 - ç”¨æˆ·")
@RestController
@@ -242,16 +239,8 @@
    @PreAuthorize("@ss.hasPermission('system:user:import')")
    public CommonResult<UserImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
                                                      @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
        // ç©ºæ–‡ä»¶ / éžæ³•文件给出明确提示,避免底层异常变成「系统异常」
        if (file == null || file.isEmpty()) {
            throw exception(USER_IMPORT_FILE_EMPTY);
        }
        List<UserImportExcelVO> list;
        try {
            list = ExcelUtils.read(file, UserImportExcelVO.class);
        } catch (Exception ex) {
            throw exception(USER_IMPORT_FILE_INVALID);
        }
        // ç©ºæ–‡ä»¶ / åæ–‡ä»¶ / æ¨¡æ¿ä¸åŒ¹é…ç­‰ï¼ŒExcelUtils.read å·²ç»Ÿä¸€è½¬æ¢ä¸ºå¯è¯»çš„中文提示
        List<UserImportExcelVO> list = ExcelUtils.read(file, UserImportExcelVO.class);
        return success(userService.importUserList(list, updateSupport));
    }
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
@@ -15,6 +15,7 @@
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
@@ -416,21 +417,22 @@
     */
    @Test
    public void testImportUserList_01() {
        // å‡†å¤‡å‚æ•°
        // å‡†å¤‡å‚数:部门名称在系统中不存在
        UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
            o.setDeptName("不存在的部门-" + System.currentTimeMillis());
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        // mock æ–¹æ³•,模拟失败
        doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any());
        // mock:系统中没有匹配的部门
        when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(newArrayList());
        // è°ƒç”¨
        UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
        // æ–­è¨€
        // æ–­è¨€ï¼šè¯¥è¡Œå› éƒ¨é—¨ä¸å­˜åœ¨è€Œå¤±è´¥
        assertEquals(0, respVO.getCreateUsernames().size());
        assertEquals(0, respVO.getUpdateUsernames().size());
        assertEquals(1, respVO.getFailureUsernames().size());
        assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
        assertTrue(respVO.getFailureUsernames().get(importUser.getUsername()).contains("不存在"));
    }
    /**
@@ -446,10 +448,13 @@
            o.setMobile(randomMobile());
        });
        // mock deptService çš„æ–¹æ³•
        // éƒ¨é—¨å·²æ”¹ä¸ºæŒ‰åç§°å¯¼å…¥ï¼šmock getDeptList è¿”回一个可匹配的部门
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setId(importUser.getDeptId());
            o.setId(1L);
            o.setName(importUser.getDeptName());
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(newArrayList(dept));
        when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
        // mock passwordEncoder çš„æ–¹æ³•
        when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java");
@@ -481,11 +486,13 @@
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        // mock deptService çš„æ–¹æ³•
        // éƒ¨é—¨å·²æ”¹ä¸ºæŒ‰åç§°å¯¼å…¥ï¼šmock getDeptList è¿”回一个可匹配的部门
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setId(importUser.getDeptId());
            o.setId(1L);
            o.setName(importUser.getDeptName());
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(newArrayList(dept));
        when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
        // è°ƒç”¨
@@ -513,11 +520,13 @@
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        // mock deptService çš„æ–¹æ³•
        // éƒ¨é—¨å·²æ”¹ä¸ºæŒ‰åç§°å¯¼å…¥ï¼šmock getDeptList è¿”回一个可匹配的部门
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setId(importUser.getDeptId());
            o.setId(1L);
            o.setName(importUser.getDeptName());
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(newArrayList(dept));
        when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
        // è°ƒç”¨