excel导入 Easy Excel

依旧是框架感觉有东西,但是确实是模拟不出来,各种零零散散的件太多了

controller层

  @ApiOperation(value = "导入Excel", notes = "导入Excel", httpMethod = "POST", response = ExcelResponseDTO.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "X-Person-Id", value = "登录人ID", paramType = "header", dataType = "long", required = true),
            @ApiImplicitParam(name = "X-Person-Name", value = "登录人姓名", paramType = "header", dataType = "string", required = true),
            @ApiImplicitParam(name = "X-Data-Permission", value = "数据安全性", paramType = "header", dataType = "String", required = true),
            @ApiImplicitParam(name = "X-Business-Group-Id", value = "用户所属业务组编号", paramType = "header", dataType = "long", required = true, defaultValue = "1001"),
            @ApiImplicitParam(name = "multipartFile", value = "附件", dataType = "__file", required = true),
            @ApiImplicitParam(name = "modelType", value = "导入模块标示,严格大小写,例如:BaseInfo", dataType = "string", required = true)
    })
    @ApiResponses({
            @ApiResponse(code = 204, message = "导入失败"),
            @ApiResponse(code = 200, message = "导入成功")
    })
    @PostMapping("/excel/{modelType}")
    public ResponseEntity<Object> excel(
            @RequestHeader("X-Person-Id") Long xPersonId,
            @RequestHeader("X-Person-Name") String xPersonName,
            @RequestHeader("X-Data-Permission") String dataPermission,
            @RequestHeader("X-Business-Group-Id") Long xBusinessGroupId,
            @RequestPart("multipartFile") MultipartFile multipartFile,
            @PathVariable("modelType") String modelType,
            @RequestParam(value = IMPORT_PARAM_MAP_K1, required = false) String param1,
            @RequestParam(value = IMPORT_PARAM_MAP_K2, required = false) String param2,
            @RequestParam(value = IMPORT_PARAM_MAP_K3, required = false) String param3,
            @RequestParam(value = IMPORT_PARAM_MAP_K4, required = false) String param4,
            @RequestParam(value = IMPORT_PARAM_MAP_K5, required = false) String param5) {
        if (!multipartFile.getOriginalFilename().endsWith(EXCEL_SUFFIX_XLSX) &&
                !multipartFile.getOriginalFilename().endsWith(EXCEL_SUFFIX_XLS)) {
            return new ResponseEntity<>(new MessageResponse("导入暂时只支持[" + EXCEL_SUFFIX_XLSX + "]或[" + EXCEL_SUFFIX_XLS + "]格式的文件!"), HttpStatus.NOT_FOUND);
        }
        Map<String, String> paramMap = fillParamMap(dataPermission,param1, param2, param3, param4, param5);
        /**
         * 导入逻辑
         * 1.上传文件到aws
         * 2.存储上传记录到redis
         * 3.异步请求获取导入记录
         * 4.从记录中获取需要执行的记录
         * 5.执行记录并把进度放入redis
         */
        try {
            Class<?> iClass = this.foundClass(modelType);
            ExcelResponseDTO excelResponseDTO = excelResponseService.buildExcelResponse(xPersonId, xPersonName, xBusinessGroupId, multipartFile,this.getRequestHeaderContext());
            importService.importExcel(excelResponseDTO, iClass, paramMap);
            return ResponseEntity.ok(excelResponseDTO);
        } catch (ClassNotFoundException e) {
            log.error("com.chinaunicom.ihr.coreperson.web.ImportController.excel", e);
            return new ResponseEntity<>(new MessageResponse("导入模块未找到!"), HttpStatus.NOT_FOUND);
        } catch (Exception e) {
            log.error("com.chinaunicom.ihr.coreperson.web.ImportController.excel", e);
            return new ResponseEntity<>(new MessageResponse("系统暂时不支持上传,请联系管理员!"), HttpStatus.NOT_FOUND);
        }
    }

我们一块一块的分析。
首先开始时对于excel不同的版本的一个处理

        if (!multipartFile.getOriginalFilename().endsWith(EXCEL_SUFFIX_XLSX) &&
                !multipartFile.getOriginalFilename().endsWith(EXCEL_SUFFIX_XLS)) {
            return new ResponseEntity<>(new MessageResponse("导入暂时只支持[" + EXCEL_SUFFIX_XLSX + "]或[" + EXCEL_SUFFIX_XLS + "]格式的文件!"), HttpStatus.NOT_FOUND);
        }

然后是将接口传入的参数储存进map中便于后续调用

Map<String, String> paramMap = fillParamMap(dataPermission,param1, param2, param3, param4, param5);

因为这个导入是一个通用方法,传入不同的实体类名称,去读取制定的类

Class<?> iClass = this.foundClass(modelType);
    private Class foundClass(String modelType) throws ClassNotFoundException {
        // 获取模块 加载 com.chinaunicom.ihr.coreperson.dao.excel.domain. 下的导入类
        return Class.forName(IMPORT_DOMAIN_PACKAGE_NAME + "." + modelType + "Import");
    }

在这里插入图片描述
这是所有的导入的类,

@Getter
@Setter
@ToString
@ExcelImportServiceTag(PersonBaseInfoImportService.class)
@CloseImportOperateStatus(closeDelete = true, closeUpdate = true, closeRenew = false, closeModify = false)
public class PersonBaseInfoNewImport {
    @IdVerify(PersonBaseInfoService.class)
    @ExcelProperty(value = "数据唯一标识", index = 0)
    protected String id;

    @OperateStatusVerify
    @ExcelProperty(value = "操作状态[创建,更正,更新]", index = 1)
    protected String operateStatus;

    /**
     * 使用操作日期作为生效日期
     * 生效日期就是加入本企业日期
     */
    @NotEmptyVerify
    @ExcelProperty(value = "生效日期(*)(日期格式:1990/1/1)" + YELLOW_CELL_SUFFIX, index = 2)
    protected String operateDate;

    /**
     * 证件类型
     */
    @GlobalLookupCodeInfoVerify(value = "证件类型有误;", lookupType = LookupType.PERSON_ID_CARD_TYPE)
    @ExcelProperty(value = "证件类型(*)" + YELLOW_CELL_SUFFIX, index = 3)
    private String idCardType;
    /**
     * 证件编号
     */
    @IdCardNumberVerify
    @ExcelProperty(value = "证件编号(*)" + YELLOW_CELL_SUFFIX, index = 4)
    private String idCardNumber;

    /**
     * 是否残疾人
     */
    @BooleanVerify("是否残疾人有误;")
    @ExcelProperty(value = "是否残疾人(*)(是/否)" + YELLOW_CELL_SUFFIX, index = 5)
    private String handicapped;

    /**
     * 是否外籍人员
     */
    @BooleanVerify("是否外籍人员有误;")
    @ExcelProperty(value = "是否外籍人员(*)(是/否)" + YELLOW_CELL_SUFFIX, index = 6)
    private String foreigner;

    /**
     * 员工编号
     */
    @CreateCanEmptyVerify("员工编号不能为空;")
    @PersonNoUniqueVerify
    @ExcelProperty(value = "员工编号(*)" + YELLOW_CELL_SUFFIX, index = 7)
    private String employeeNumber;

    /**
     * 姓名
     */
    @NotEmptyVerify
    @ExcelProperty(value = "姓名(*)" + YELLOW_CELL_SUFFIX, index = 8)
    private String chineseName;

    /**
     * 英文名
     */
    @ExcelProperty(value = "英文名", index = 9)
    private String englishName;

    /**
     * 用工性质
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "用工性质有误;", lookupType = LookupType.PERSON_EMPLOYMENT_NATURE)
    @EmploymentNatureVerify
    @ExcelProperty(value = "用工性质(*)" + YELLOW_CELL_SUFFIX, index = 10)
    private String employmentNature;

    /**
     * 性别:1 男 0 女
     */
    @SexVerify("性别有误;")
    @ExcelProperty(value = "性别(*)(1/男 0/女)" + YELLOW_CELL_SUFFIX, index = 11)
    private String sex;

    /**
     * 婚姻状况
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "婚姻状况有误;", lookupType = LookupType.PERSON_MARITAL_STATUS)
    @ExcelProperty(value = "婚姻状况(*)" + YELLOW_CELL_SUFFIX, index = 12)
    private String maritalStatus;

    /**
     * 出生日期
     */
    @NotEmptyVerify
    @DateVerify("出生日期有误;")
    @ExcelProperty(value = "出生日期(*)(日期格式:1990/1/1)" + YELLOW_CELL_SUFFIX, index = 13)
    private String dateOfBirth;

    /**
     * 国籍
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "国籍有误;", lookupType = LookupType.PERSON_NATIONALITY)
    @ExcelProperty(value = "国籍(*)" + YELLOW_CELL_SUFFIX, index = 14)
    private String nationality;

    /**
     * 民族
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "民族有误;", lookupType = LookupType.PERSON_ETHNICITY)
    @ExcelProperty(value = "民族(*)" + YELLOW_CELL_SUFFIX, index = 15)
    private String ethnicity;

    /**
     * 籍贯
     */
    @NotEmptyVerify
    @ExcelProperty(value = "籍贯(*)" + YELLOW_CELL_SUFFIX, index = 16)
    private String nativePlace;

    /**
     * 出生地
     */
    @ExcelProperty(value = "出生地", index = 17)
    private String townOfBirth;

    /**
     * 户籍所在地
     */
    @ExcelProperty(value = "户籍所在地", index = 18)
    private String domicile;

    /**
     * 政治面貌
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "政治面貌;", lookupType = LookupType.PERSON_POLITICAL)
    @ExcelProperty(value = "政治面貌(*)" + YELLOW_CELL_SUFFIX, index = 19)
    private String political;

    /**
     * 公司邮箱
     */
    @ExcelProperty(value = "公司邮箱", index = 20)
    private String officeEmail;

    /**
     * 手机号码
     */
    @NotEmptyVerify
    @MobilePhoneNoVerify
    @ExcelProperty(value = "手机号码(*)" + YELLOW_CELL_SUFFIX, index = 21)
    private String phoneNumber;

    /**
     * 个人邮箱
     */
    @ExcelProperty(value = "个人邮箱", index = 22)
    private String personEmail;

    /**
     * 最高学历
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "最高学历有误;", lookupType = LookupType.PERSON_HIGHEST_EDUCATION)
    @ExcelProperty(value = "最高学历(*)" + YELLOW_CELL_SUFFIX, index = 23)
    private String highestEducation;

    /**
     * 是否职业经理人 (1 是 0 否)
     */
    @NotEmptyVerify
    @ProfessionalManageVerify("是否职业经理人有误;")
    @ExcelProperty(value = "是否职业经理人(*)" + YELLOW_CELL_SUFFIX, index = 24)
    private String professionalManager;

    /**
     * 学历类型
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "学历类型有误;", lookupType = LookupType.EDUCATION_TYPE)
    @ExcelProperty(value = "学历类型(*)" + YELLOW_CELL_SUFFIX, index = 25)
    private String educationType;

    /**
     * 最高学位
     */
    @GlobalLookupCodeInfoVerify(value = "最高学位有误;", lookupType = LookupType.PERSON_HIGHEST_DEGREE)
    @ExcelProperty(value = "最高学位", index = 26)
    private String highestDegree;

    /**
     * 参加工作日期
     */
    @NotEmptyVerify
    @DateVerify("参加工作日期有误;")
    @ExcelProperty(value = "参加工作日期(日期格式:1990/1/1)(*)" + YELLOW_CELL_SUFFIX, index = 27)
    private String dateOfWork;

    /**
     * 社会工龄调整值(月)
     */
    @IntegerVerify("社会工龄调整值有误;")
    @ExcelProperty(value = "社会工龄调整值(月)", index = 28)
    private String dateOfWorkAdj;

    /**
     * 企业工龄调整值(月)
     */
    @IntegerVerify("企业工龄调整值有误;")
    @ExcelProperty(value = "企业工龄调整值(月)", index = 29)
    private String dateOfJoinAdj;

    /**
     * 加入本企业途径
     */
    @GlobalLookupCodeInfoVerify(value = "加入本企业途径有误;", lookupType = LookupType.PERSON_WAY_TO_JOIN)
    @ExcelProperty(value = "加入本企业途径", index = 30)
    private String wayToJoin;

    /**
     * 劳务派遣入本企业日期
     */
    @DateVerify("劳务派遣入本企业日期有误;")
    @ExcelProperty(value = "劳务派遣入本企业日期(日期格式:1990/1/1)", index = 31)
    private String dateOfLaborDispatch;

    /**
     * 劳务派遣工龄调整值(月)
     */
    @IntegerVerify("劳务派遣工龄调整值有误;")
    @ExcelProperty(value = "劳务派遣工龄调整值(月)", index = 32)
    private String dateOfLaborDispatchAdj;

    /**
     * 部门
     */
    @NotEmptyVerify
    @OrgCodeVerify
    @ExcelProperty(value = "部门编码(*)" + YELLOW_CELL_SUFFIX, index = 33)
    private String orgCode;
    @ExcelProperty(value = "部门名称", index = 34)
    private String orgName;

    /**
     * 职位
     */
    @NotEmptyVerify
    @PositionCodeVerify
    @ExcelProperty(value = "职位编码(*)" + YELLOW_CELL_SUFFIX, index = 35)
    private String positionCode;
    @ExcelProperty(value = "职位名称", index = 36)
    private String positionName;
    /**
     * 职务
     */
    @JobCodeVerify
    @ExcelProperty(value = "职务编码", index = 37)
    private String jobCode;
    @ExcelProperty(value = "职务名称", index = 38)
    private String jobName;

    /**
     * 在岗类别
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "在岗类别有误;", lookupType = LookupType.PERSON_ON_DUTY_CATEGORY)
    @ExcelProperty(value = "在岗类别(*)" + YELLOW_CELL_SUFFIX, index = 39)
    private String onDutyCategory;

    /**
     * 人员类别
     */
    @GlobalLookupCodeInfoVerify(value = "人员类别有误;", lookupType = LookupType.PERSON_EMPLOYEE_CATEGORY)
    @ExcelProperty(value = "人员类别", index = 40)
    private String employeeCategory;

    /**
     * 上级主管
     */
    @SeniorRoleVerify
    @ExcelProperty(value = "上级主管员工编号", index = 41)
    private String seniorRoleNo;
    @ExcelProperty(value = "上级主管姓名", index = 42)
    private String seniorRoleName;

    /**
     * 岗级
     */
    @NotEmptyVerify
    @GlobalLookupCodeInfoVerify(value = "职位级别有误;", lookupType = LookupType.PERSON_GRADE)
    @ExcelProperty(value = "职位级别(*)" + YELLOW_CELL_SUFFIX, index = 43)
    private String grade;





    /**
     * 退伍时间
     */
    @DateVerify("退伍时间有误;")
    @ExcelProperty(value = "退伍时间", index = 44)
    private String dateOfDisbandment;

    /**
     * 党内职务
     */
    @GlobalLookupCodeInfoVerify(value = "党内职务有误;", lookupType = LookupType.PARTY_JOB)
    @ExcelProperty(value = "党内职务", index = 45)
    private String jobOnParty;

    /**
     * 工龄
     */
    @ExcelProperty(value = "工龄", index = 46)
    private String workYear;

    /**
     * 职位备注
     */
    @ExcelProperty(value = "职位备注", index = 47)
    private String positionRemark;

    @ExcelProperty(value = "报错提示", index = 48)
    protected String message;

}

这个实体类大有文章,我们先按照顺序看代码后续再回来看。

            ExcelResponseDTO excelResponseDTO = excelResponseService.buildExcelResponse(xPersonId, xPersonName, xBusinessGroupId, multipartFile,this.getRequestHeaderContext());

    /**
     * 构建一个 ExcelResponse
     *
     * @param xPersonId
     * @param xPersonName
     * @param xBusinessGroupId
     * @param multipartFile
     * @return
     */
    public ExcelResponseDTO buildExcelResponse(Long xPersonId, String xPersonName, Long xBusinessGroupId, MultipartFile multipartFile, RequestHeaderContext requestHeaderContext) {
        String url = uploadService.uploadFile(xPersonId, multipartFile);
        ExcelResponseDTO excelResponseDTO = new ExcelResponseDTO(xPersonId, xPersonName, xBusinessGroupId,
                url, multipartFile.getOriginalFilename(),requestHeaderContext);
        manageable.putExcelResponse(excelResponseDTO);
        return excelResponseDTO;
    }

实话说从开始一直没有看懂为啥要把导入的文件存起来,会占用空间吧,我能够理解的想法就是,这样可以不用将文件存储在内容中,然后再讲对象读取出来用于后续的导入以及校验,

importService.importExcel(excelResponseDTO, iClass, paramMap);
    public void importExcel(ExcelResponseDTO excelResponseDTO, Class<?> iClass, Map<String, String> paramMap) {
        // 导入上下文
        ExcelImportContext excelImportContext = new ExcelImportContext(iClass, excelResponseDTO.getPersonId(),
                excelResponseDTO.getPersonName(), excelResponseDTO.getBusinessGroupId(), paramMap);
        String url = uploadService.getRealPath(excelResponseDTO.getAddress());
        try (InputStream inputStream = new BufferedInputStream(new URL(url).openConnection().getInputStream())) {
            boolean bo = false;
            List<Class<?>> queue = Collections.singletonList(iClass);
            // 如果是多个,获取多个的导入类
            ExcelMultipleImportClassTag multipleImportClassTag = iClass.getAnnotation(ExcelMultipleImportClassTag.class);
            if (Objects.nonNull(multipleImportClassTag)) {
                queue = Arrays.asList(multipleImportClassTag.value());
                // 是否多sheet页对应同一个导入模板
                bo = multipleImportClassTag.mulSheet();
            }
            // 队列执行
            for (Class<?> ic : queue) {
                EasyExcel
                        .read(inputStream)
                        .head(ic)
                        .password(EasyExcelUtils.getPassword(ic))
                        .headRowNumber(EasyExcelUtils.getHeadRowNumber(ic))
                        .registerReadListener(new ImportVerifyEventListener(excelImportContext, excelResponseDTO))
                        .registerReadListener(new ImportObjectMapEventListener(excelImportContext))
                        .sheet(EasyExcelUtils.getSheetIndex(ic))
                        .doRead();
            }
            // 如果多sheet页对应同一导入类
            if (bo) {
                // 获取sheet页数量
                Workbook workbook = Workbook.getWorkbook(inputStream);
                Sheet[] sheets = workbook.getSheets();

                //
                for (int i = multipleImportClassTag.index(); i < sheets.length; i++) {
                    EasyExcel
                            .read(inputStream)
                            .head(multipleImportClassTag.mulSheetToTemplate())
                            .password(EasyExcelUtils.getPassword(multipleImportClassTag.mulSheetToTemplate()))
                            .headRowNumber(EasyExcelUtils.getHeadRowNumber(multipleImportClassTag.mulSheetToTemplate()))
                            .registerReadListener(new ImportVerifyEventListener(excelImportContext, excelResponseDTO))
                            .registerReadListener(new ImportObjectMapEventListener(excelImportContext))
                            .sheet(i-1)
                            .doRead();
                }
            }
        } catch (Exception e) {
            excelResponseDTO.setResult("导入出错,请检查与标准模板的差异或联系管理员");
            excelResponseService.importFailure(excelResponseDTO);
            log.error("com.chinaunicom.ihr.coreperson.service.BaseImportService.importExcel Exception:", e);
        }
    }

其实这一块才是主要去实现导入以及导入的字段的校验的地方,我也是用过easyexcel但是我感觉这个项目中是用的高级用法,包括easyexcel在官网的demo真的好简单
在这里插入图片描述
主要是这一块的两个类,去实现的具体的校验以及具体的导入

public class ImportVerifyEventListener extends AnalysisEventListener {

    /**
     * 处理 信息dto
     */
    private final ExcelResponseService excelResponseService;

    /**
     * 类验证字段之前处理器
     */
    private final List<VerifyProcess> classVerifyFieldBeforeList = new ArrayList<>();

    /**
     * 类验证字段之后处理器
     */
    private final List<VerifyProcess> classVerifyFieldAfterList = new ArrayList<>();

    /**
     * 字段验证处理器
     */
    private final Map<Integer, List<VerifyProcess>> fieldVerifyMap = new TreeMap<>();

    /**
     * 导入类 index 与 字段集合
     */
    private Map<Integer, Field> fieldMap = new TreeMap<>();

    /**
     * 导入上下文
     */
    private ExcelImportContext excelImportContext;
    /**
     * 导入 信息dto
     */
    private ExcelResponseDTO excelResponseDTO;
    /**
     * 当前的数据Map
     */
    private BeanMap currentDataBeanMap;
    /**
     * 所有已经读取出来的数据BeanMap(包含当前)
     */
    private List<BeanMap> dataBeanMapList = new ArrayList<>();
    /**
     * 所有的数据 Object 如果最后验证完无效,那么导出的就是这个List
     */
    private List<Object> dataList = new ArrayList<>();


    public ImportVerifyEventListener(ExcelImportContext excelImportContext, ExcelResponseDTO excelResponseDTO) {
        this.excelImportContext = excelImportContext;
        this.excelResponseDTO = excelResponseDTO;
        this.excelResponseService = SpringContextUtils.getApplicationContext().getBean(ExcelResponseService.class);
        this.initClassVerifyMap();
        this.initFieldMap();
        this.initFieldVerifyMap();
    }

    /**
     * 初始化类验证处理器
     */
    private void initClassVerifyMap() {
        for (Annotation annotation : excelImportContext.getImportClass().getAnnotations()) {
            VerifyProcessTag processTag = annotation.annotationType().getAnnotation(VerifyProcessTag.class);
            if (Objects.nonNull(processTag)) {
                if (processTag.fieldBefore()) {
                    classVerifyFieldBeforeList.add(buildClassVerifyProcess(processTag.processClass(), annotation, processTag.order()));
                }
                if (processTag.fieldAfter()) {
                    classVerifyFieldAfterList.add(buildClassVerifyProcess(processTag.processClass(), annotation, processTag.order()));
                }
            }
        }
        classVerifyFieldBeforeList.sort(Comparator.comparingInt(VerifyProcess::getOrder));
        classVerifyFieldAfterList.sort(Comparator.comparingInt(VerifyProcess::getOrder));
    }

    /**
     * 初始化字段Map
     *
     * @return
     */
    public void initFieldMap() {
        List<Field> fieldList = new ArrayList<>();
        // 当为空时即还没有初始化
        Class<?> tempClass = excelImportContext.getImportClass();
        while (tempClass != null) {
            Collections.addAll(fieldList, tempClass.getDeclaredFields());
            tempClass = tempClass.getSuperclass();
        }
        for (Field field : fieldList) {
            ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
            if (excelIgnore != null) {
                continue;
            }
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null && excelProperty.index() >= 0) {
                fieldMap.put(excelProperty.index(), field);
            }
        }
    }

    /**
     * 初始化字段验证处理器
     */
    private void initFieldVerifyMap() {
        for (Map.Entry<Integer, Field> fieldEntry : fieldMap.entrySet()) {
            Field field = fieldEntry.getValue();
            List<VerifyProcess> processList = new ArrayList<>();
            for (Annotation annotation : field.getAnnotations()) {
                FieldVerifyProcessTag processTag = annotation.annotationType().getAnnotation(FieldVerifyProcessTag.class);
                if (Objects.nonNull(processTag)) {
                    processList.add(buildFieldVerifyProcess(processTag.processClass(), annotation, processTag.order(), field));
                }
            }
            processList.sort(Comparator.comparingInt(VerifyProcess::getOrder));
            fieldVerifyMap.put(fieldEntry.getKey(), processList);
        }
    }


    /**
     * 构造处理器
     *
     * @param processClass
     * @return
     */
    private AbstractVerifyProcess buildClassVerifyProcess(Class<? extends AbstractVerifyProcess> processClass, Annotation annotation, int order) {
        return ReflectUtil.newInstance(processClass, excelImportContext, annotation, order);
    }

    /**
     * 构造处理器
     *
     * @param processClass
     * @return
     */
    private AbstractFieldVerifyProcess buildFieldVerifyProcess(Class<? extends AbstractFieldVerifyProcess> processClass, Annotation annotation, int order, Field field) {
        return ReflectUtil.newInstance(processClass, excelImportContext, annotation, order, field);
    }

    @Override
    public void invoke(Object data, AnalysisContext context) {
        dataList.add(data);
        currentDataBeanMap = BeanMap.create(data);
        // 把代入的 message 去掉
        currentDataBeanMap.put(IMPORT_MESSAGE_FIELD_NAME, null);
        dataBeanMapList.add(currentDataBeanMap);
        // 把 data 转换为 Map 并且去掉 null 值
        Map<String, Object> objectMap = BeanUtil.beanToMap(data, false, true);

        // 字段之前验证的类验证
        eachVerifyMap(objectMap, classVerifyFieldBeforeList);
        // 字段验证
        // 根据字段顺序处理
        for (Map.Entry<Integer, List<VerifyProcess>> verifyEntry : fieldVerifyMap.entrySet()) {
            // 根据处理器顺序处理
            eachVerifyMap(objectMap, verifyEntry.getValue());
        }

        // 字段之后验证的类验证
        eachVerifyMap(objectMap, classVerifyFieldAfterList);

        Object message = objectMap.get(IMPORT_MESSAGE_FIELD_NAME);
        if (Objects.nonNull(message) && CollectionUtils.isNotEmpty((List) message)) {
            currentDataBeanMap.put(IMPORT_MESSAGE_FIELD_NAME, String.join("", (List) message));
            excelImportContext.setInvalid(Boolean.TRUE);
        }
        context.readRowHolder().setCurrentRowAnalysisResult(objectMap);
    }

    /**
     * 循环验证
     *
     * @param objectMap
     * @param verifyList
     */
    private void eachVerifyMap(Map<String, Object> objectMap, List<VerifyProcess> verifyList) {
        // 字段之后验证的类验证
        for (VerifyProcess verifyProcess : verifyList) {
            // 只要有一个跳过那么就都跳过
            if (verifyProcess.skip(currentDataBeanMap, dataBeanMapList, objectMap)) {
                return;
            }
            verifyProcess.execute(currentDataBeanMap, dataBeanMapList, objectMap);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (excelImportContext.getInvalid()) {
            // 验证不成功,进度:50
            excelResponseService.importFailure(excelResponseDTO, dataList, excelImportContext.getImportClass(),excelImportContext.getParamMap());
        } else {
            // 否则成功
            excelResponseService.importSuccess(excelResponseDTO);
        }
        dataBeanMapList.clear();
        dataList.clear();
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("验证异常", exception);
        String msg = "该行导入出错,请联系管理员;";
        // 这里捕获异常,包含 ImportObjectMapEventListener 抛出的异常
        if (exception instanceof ServiceException) {
            // 处理 ServiceException
            msg = ((ServiceException) exception).getMessageResponse().getMsg();
        }
        String message = (String) currentDataBeanMap.get(IMPORT_MESSAGE_FIELD_NAME);
        currentDataBeanMap.put(IMPORT_MESSAGE_FIELD_NAME,
                StringUtils.isEmpty(message) ? msg : (message + ";" + msg));
        excelImportContext.setInvalid(Boolean.TRUE);
    }
}

具体的操作在invoke里面
在这里插入图片描述
这一块我真的踩了好多坑,其实这一块是去读实体类上面的注解,
我们以id为例将几种类型

    @IdVerify(PersonBaseInfoService.class)
    @ExcelProperty(value = "数据唯一标识", index = 0)
    protected String id;

ExcelProperty这是生成excel中的字段名,IdVerify专门写的id的注解,用来验证id字段的

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@FieldVerifyProcessTag(processClass = IdVerifyProcess.class)
public @interface IdVerify {
    /**
     * 调用查询的 service 类型
     *
     * @return
     */
    Class<? extends ServiceImpl> value();
}

FieldVerifyProcessTag这个注解就是专门用来去实现的类

public class IdVerifyProcess extends AbstractFieldVerifyProcess {


    public IdVerifyProcess(ExcelImportContext context, Annotation annotation, int order, Field field) {
        super(context, annotation, order, field);
    }

    @Override
    public void execute(BeanMap dataBeanMap, List<BeanMap> dataBeanMapList, Map<String, Object> objectMap) {
        if (this.isUpdateOperateStatus(dataBeanMap)
                || this.isDeleteOperateStatus(dataBeanMap)) {
            String fieldValue = this.getFieldValue(dataBeanMap);
            if (StringUtils.isEmpty(fieldValue)) {
                this.setMessage(objectMap, "未找到要操作的数据,操作状态为【更新或更正或更改】时,唯一标识不能为空;");
            } else {
                try {
                    fieldValue = ExcelImportUtils.decode(fieldValue, StringUtils.UTF8);
                } catch (UnsupportedEncodingException e) {
                    this.setMessage(objectMap, "ID解码失败;");
                }
                IdVerify annotation = (IdVerify) this.annotation;
                // 如果是带时间戳的类
                if (HistoryServiceImpl.class.isAssignableFrom(annotation.value())) {
                    String operateDate = this.getFieldValue(dataBeanMap, String.class, IMPORT_OPERATE_DATE_FIELD_NAME);
                    if (StringUtils.isNotEmpty(operateDate)) {
                        LocalDate localDate = ExcelImportUtils.convertDate(operateDate);
                        if (Objects.nonNull(localDate)) {
                            objectMap.put(IMPORT_OPERATE_DATE_FIELD_NAME, localDate);
                            dataBeanMap.put(IMPORT_OPERATE_DATE_FIELD_NAME, localDate.toString());
                            // 把日期传入
                            idVerify(objectMap, fieldValue,
                                    ((HistoryServiceImpl) this.getBean(annotation.value())).selectById(fieldValue, localDate));
                        } else {
                            this.setMessage(objectMap, "操作日期有误;");
                        }
                    } else {
                        this.setMessage(objectMap, "操作日期不能为空;");
                    }
                } else {
                    idVerify(objectMap, fieldValue, (this.getBean(annotation.value())).selectById(fieldValue));
                }
            }
        }
    }

    /**
     * 验证 Id
     *
     * @param objectMap
     * @param fieldValue
     * @param selectById
     */
    private void idVerify(Map<String, Object> objectMap, String fieldValue, Object selectById) {
        if (Objects.isNull(selectById)) {
            this.setMessage(objectMap, "当前行数据唯一标识的数据在系统中未找到;");
        } else {
            objectMap.put(field.getName(), fieldValue);
        }
    }

    @Override
    public boolean skip(BeanMap dataBeanMap, List<BeanMap> dataBeanMapList, Map<String, Object> objectMap) {
        return false;
    }
}

其他的都是一样的实现方法,我觉得这个框架真的很有趣,但是我真的没有办法拿出来,想要抽离真的代价太大了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/155929.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

回调方法Callbak方法的理解——Java中回调的实现方式 从系统调用角度理解回调

目录 回调方法实现用反射实现直接调用callback进化&#xff1a;接口实现分离 匿名内部类 到 函数式编程 从系统调用角度理解回调同步调用异步调用不需要对方结果需要对方的结果 菜鸡问大佬问题案例同步回调异步回调基于Future的半同步 回调方法就是一个参数,将一个A方法作为参数…

Power Automate-创建自定义连接器

点击左侧导航栏&#xff0c;更多&#xff0c;点击全部发现 点击下方的自定义连接器 点击从空白创建 注意命名要用英文 常规信息中可以上传连接器icon、写一些说明 方案是观察接口地址前面的文本&#xff0c;主机是下方接口地址中蓝色框中的内容 点击下一步&#xff0c;根据API自…

CTF-PWN-堆-【前置知识】

CTF-PWN-堆 堆申请堆块main_areanabrk&sbrk函数mallocfreefree后top chunk 堆 由malloc alloc realloc 函数分配 chunk的前指的是地址低的&#xff0c;chunk的高指的是地址高的 申请堆块 ptmalloc2堆管理器&#xff1a; 通俗的讲就是相当于一个”中间商”&#xff0c;在…

Python编程技巧 – 使用列表(list)

Python编程技巧 – 使用列表(list) Python Programming Skills – Using a List 在Python编程语言中&#xff0c;我们会用到许多列表&#xff08;List&#xff09;。 一门强大的编程语言会包含列表&#xff08;或者数组&#xff09;的数据结构。列表&#xff08;或数组&#…

cpolar+LightPicture,将个人电脑改造成公网图床服务器

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

C++ Qt 学习(九):模型视图代理

1. Qt 模型视图代理 Qt 模型视图代理&#xff0c;也可以称为 MVD 模式 模型(model)、视图(view)、代理(delegate)主要用来显示编辑数据 1.1 模型 模型 (Model) 是视图与原始数据之间的接口 原始数据可以是&#xff1a;数据库的一个数据表、内存中的一个 StringList&#xff…

什么是持续集成的自动化测试?

持续集成的自动化测试 如今互联网软件的开发、测试和发布&#xff0c;已经形成了一套非常标准的流程&#xff0c;最重要的组成部分就是持续集成&#xff08;Continuous integration&#xff0c;简称CI&#xff0c;目前主要的持续集成系统是Jenkins&#xff09;。 那么什么是持…

Python爬虫教程:从入门到实战

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是涛哥&#xff0c;今天为大家分享 Python爬虫教程&#xff1a;从入门到实战&#xff0c;文章3800字&#xff0c;阅读大约15分钟&#xff0c;大家enjoy~~ 网络上的信息浩如烟海&#xff0c;而爬虫&#xff08;…

4G工业路由器智慧电梯联网应用方案

随着电梯老旧增多及日常管理上缺失&#xff0c;电梯安全运行上存在一定的问题&#xff0c;从全国电梯统计数据中可以发现&#xff0c;主要的电梯困人、故障事件发生在住宅小区&#xff0c;当前&#xff0c;住宅小区的电梯绝大多数是通过物业公司负责管理&#xff0c;物业公司安…

【尘缘赠书活动第四期】推荐几本架构师成长和软件架构技术相关的好书,助你度过这个不太景气的寒冬!

目录 1 《高并发架构实战&#xff1a;从需求分析到系统设计》2《架构师的自我修炼&#xff1a;技术、架构和未来》3《中台架构与实现&#xff1a;基于DDD和微服务》4《分布式系统架构&#xff1a;架构策略与难题求解》5《流程自动化实战&#xff1a;系统架构和软件开发视角 》6…

验证码识别之OCR识别

验证码识别&#xff1a; 背景&#xff1a; ​ 决定开一个专题给大家讲一下验证码识别&#xff0c;不要多想&#xff0c;我们不搞深度学习&#xff0c;知识用于攻破模拟登录的时候弹出的验证码&#xff0c;后续会给大家讲讲滑块等等&#xff0c;反爬虫策略如何应对。 好了&am…

大会技术“硬核”资讯来啦!点击预约 IoTDB 现场一对一调优咨询服务

承诺大家的&#xff0c;2023 IoTDB 用户大会现场“快讯”第一弹来啦&#xff01; 我们将在大会现场设置 IoTDB Workshop&#xff0c;IoTDB 原厂团队将免费提供一对一技术咨询、使用调优、疑问解答等服务。 现场提供的一对一咨询、调优时长为 30 分钟&#xff0c;在您预约的时间…

Linux_VMware 软件安装与虚拟机

从这里开始&#xff0c;我们就踏上了学习 Linux 的路程。长路漫漫亦灿灿&#xff0c;祝我也祝所有看到这篇文章的朋友&#xff01;&#xff01;&#xff01; 参考书目&#xff1a;【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81 / ARM 裸机与嵌入式 Linux 驱动…

Stable Diffusion1.5网络结构-超详细原创

目录 1 Unet 1.1详细整体结构 1.2 缩小版整体结构 1.3 时间步编码 1.4 CrossAttnDownBlock2D 1.4.1 ResnetBlock2D 1.4.2 Transformer2DModel 1.4.2.1 BasicTransformerBlock 1.4.2.1.1 SelfAttention 1.4.2.1.2 CrossAttention 2 VAE 3 CLIP 绘制软件&#xff1a;…

Linux操作系统基础 – 正则表达式快速入门

Linux操作系统基础 – 正则表达式快速入门 Linux Operating System Essentials - Introduction to Regular Expressions 通常在计算机科学领域&#xff0c;正则表达式被解释为对字符串操作的一种逻辑公式&#xff0c;即用事先定义好的特定字符及其组合组成所谓的“规则字符串”…

深度学习之基于YoloV5血红细胞检测识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习已经在许多领域中得到了广泛的应用&#xff0c;包括医疗健康领域。其中&#xff0c;YOLO&#xff08;You O…

UE基础篇四:地形基础概念

导语: 视频文档在文末 一、从引擎中导入高度图 高度图是灰度图,并且每个像素有16位的深度,支持这种格式的是.png 和.raw格式 高度图可以自己绘制,或者通过许多网站下载那些复制现实世界地点的图片,许多网站也有免费资源下载 二、草地和石头混合图层 创建一个混合材质,…

龙迅LT8912B 单通道MIPIDSI桥接LVDS+HDMI(1.4)同显点屏LVDS,加环出一路HDMI

龙迅LT8912B 描述: Lontium LT8912B MIPIDSI到LVDS和HDMI桥采用单通道MIPID-PHY接收器前端配置&#xff0c;每个通道有4个数据通道&#xff0c;每个数据通道运行1.5Gbps&#xff0c;最大输入带宽可达6Gbps。对于屏幕应用&#xff0c;该桥解码MIPIDSI 18bpp RGB666和24bpp RGB…

系列四、JVM的内存结构【本地接口(Native Interface)】

一、组成 本地接口由本地方法栈&#xff08;Native Method Stack&#xff09;、本地方法接口&#xff08;Native Interface&#xff09;、本地方法库组成。 二、本地接口的作用 本地接口的作用是融合不同的编程语言为Java所用&#xff0c;它的初衷是融合C/C程序&#xff0c;Jav…

【ARM Trace32(劳特巴赫) 使用介绍 5 -- Trace32 scan dump 详细介绍】

文章目录 1.1 JTAG 测试逻辑架构1.2 D型扫描触发器1.2.1 全扫描介绍1.3 IR 寄存器1.4 TDR(Test data registers)1.4.1 TDR 的实现1.4.1.1 Bypass Register1.4.1.2 Boundary-scan register1.5 Scan Dump1.5.1 soft fusion1.1 JTAG 测试逻辑架构 图 1-1 片上测试逻辑概念图 如前面…