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);}@Overridepublic 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);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {if (excelImportContext.getInvalid()) {// 验证不成功,进度:50excelResponseService.importFailure(excelResponseDTO, dataList, excelImportContext.getImportClass(),excelImportContext.getParamMap());} else {// 否则成功excelResponseService.importSuccess(excelResponseDTO);}dataBeanMapList.clear();dataList.clear();}@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {log.error("验证异常", exception);String msg = "该行导入出错,请联系管理员;";// 这里捕获异常,包含 ImportObjectMapEventListener 抛出的异常if (exception instanceof ServiceException) {// 处理 ServiceExceptionmsg = ((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);}@Overridepublic 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);}}@Overridepublic boolean skip(BeanMap dataBeanMap, List<BeanMap> dataBeanMapList, Map<String, Object> objectMap) {return false;}
}
其他的都是一样的实现方法,我觉得这个框架真的很有趣,但是我真的没有办法拿出来,想要抽离真的代价太大了
相关文章:

excel导入 Easy Excel
依旧是框架感觉有东西,但是确实是模拟不出来,各种零零散散的件太多了 controller层 ApiOperation(value "导入Excel", notes "导入Excel", httpMethod "POST", response ExcelResponseDTO.class)ApiImplicitParams({…...

html实现图片裁剪处理(附源码)
文章目录 1.设计来源1.1 主界面1.2 裁剪界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/134455169 html实现图片裁剪处理(附源码),支持图片放大缩小&#…...
前端语言报错
1. 语法错误(Syntax Errors) 这是由于代码不符合语法规则而引起的错误,通常在代码编译阶段发生。示例: javascriptCopy code if (x 10 { // 缺少了右括号 // 代码逻辑 } 2. 类型错误(Type Errors) 这…...
详细讲解什么是观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。 该模…...

镭速,克服UDP传输缺点的百倍提速传输软件工具
在网络传输中,我们经常会面临这样的困难:文件太大,传输速度太慢,浪费时间和流量;文件太小,传输速度太快,容易出现丢包和乱序,损害数据的完整性和正确性。这些困难的根本在于传输层协…...

Semi-Supervised Multi-Modal Learning with Balanced Spectral Decomposition
Y是所有模态的表征矩阵, ∑ i 1 d h ( λ i ) \sum_{i1}^dh(\lambda_i) ∑i1dh(λi) is the proposed eigenvalue-based objective function,the final similarity matrix W for the multimodal data as a block matrix 辅助信息 作者未提供代码...
3296:【例50.2】 计算书费《信息学奥赛一本通编程启蒙(C++版)》
3296:【例50.2】 计算书费《信息学奥赛一本通编程启蒙(C版)》 【题目描述】 下面是一个图书的单价表: 1、计算概论 28.9 元/本 2、数据结构与算法 32.7 元/本 3、数字逻辑 45.6 元/本 4、C程序设计教程 78 元/本 5、人工智能…...

统一身份认证平台之SSO建设
前言 上篇说道Passwordless无密码技术,也提到了数字时代密码管理的难度,其实在日常的生活中,很多用户也会因为忘记某些网站的登录密码而烦恼。为了方便记忆,很多人都在不同的站点使用相同的用户名和密码,虽然也可以减少…...
【开题报告】基于SpringBoot的膳食营养健康网站的设计与实现
1.选题背景与意义 基于SpringBoot的膳食营养健康网站的设计与实现是一个具有重要意义的选题。背景和意义主要包括以下几点: (1)社会健康意识的提升:随着人们健康意识的提高,越来越多的人开始关注自己的饮食营养问题。…...
超五类网线和六类网线的相同点和区别
本文对超五类网线和六类网线的相同点和区别进行了简单介绍,帮助大家区分和建立相应的概念。 相同点: (1)都是网络跳线,用于连接网络设备。 (2)网线内部由8根不同颜色的线组成。 区别…...

Linux--初识和基本的指令(1)
目录 前言 0.什么是操作系统 0.1 搭建 Linux 环境 0.2搭建 Linux 环境小结 1.使用 XShell 远程登录 Linux 1.1关于 Linux 桌面 1.2下载安装 XShell 1.3查看 Linux 主机 ip 1.4XShell 下的复制粘贴 2.Linux下基本指令 2.1 pwd命令 2.2 ls命令 2.3 mkdir指令 2.4 cd…...

万宾科技智能井盖传感器,提升市政井盖健康
市政井盖就是城市里不可或缺的基础设施之一,关于它的监测工作可马虎不得。它承载着保护市民的交通安全以及城市正常运转的重要使命。虽然现在城市化的速度很快,但是传统的市政井盖管理方式变得有些力不从心了。井盖的覆盖范围很广,如果单单依…...

transformer学习资料
一、NLP 自然语言处理 NLP 是机器学习在语言学领域的研究,专注于理解与人类语言相关的一切。NLP 的目标不仅是要理解每个单独的单词含义,而且也要理解这些单词与之相关联的上下文之间的意思。 常见的NLP 任务列表: 对整句的分类࿱…...

一起学docker系列之四docker的常用命令--系统操作docker命令及镜像命令
目录 前言1 操作 Docker 的命令1.1 启动 Docker1.2 停止 Docker1.3 重启 Docker1.4 查看 Docker 状态1.5 查看 Docker 所有命令的信息1.6 查看某个命令的帮助信息 2 操作镜像的命令2.1 查看所有镜像2.2 搜索某个镜像2.3 下载某个镜像2.4 查看镜像所占空间2.5 删除镜像2.6 强制删…...

MySQL 的执行原理(三)
5.4. InnoDB 中的统计数据 我们前边唠叨查询成本的时候经常用到一些统计数据,比如通过 SHOW TABLE STATUS 可以看到关于表的统计数据,通过 SHOW INDEX 可以看到关于索引 的统计数据,那么这些统计数据是怎么来的呢?它们是以什么方…...
一道好题——分治
一道好题应该有一个简洁的题面。 有一个长度为 n,初始全为 0 的序列 a,另有一个长度为 n 的序列 b,你希望将 a 变成 b,你可以执行如下两种操作: 1 x:将 a 中所有值为 x 的数 11。 2 x:将 a 中下…...

庖丁解牛:NIO核心概念与机制详解 02 _ 缓冲区的细节实现
文章目录 PreOverview状态变量概述PositionLimitCapacity演示: 观察变量 访问方法get() 方法put()方法类型化的 get() 和 put() 方法 缓冲区的使用:一个内部循环 Pre 庖丁解牛:NIO核心概念与机制详解 01 接下来我们来看下缓冲区内部细节 Ov…...
Python itertools模块中的combinations() 函数用法
Python itertools模块中的combinations 函数用法 调用方法示例1示例2 调用方法 itertools.combinations(iterable, r)各个参数意义: iterable:输入数据,数据应该是可迭代的。 r:子序列的长度 返回值:从输入的可迭代数…...

在线预览excel,luckysheet在vue项目中的使用
一. 需求 需要在内网项目中在线预览excel文档,并可以下载 二.在项目中下载并引入luckysheet 1.打开项目根目录,npm i luckyexcel 安装 npm i luckyexcel2.在项目的index.html文件中引入依赖 外网项目中的引入(CDN引入)&#…...

【python】OpenCV—Image Pyramid(8)
文章目录 1 图像金字塔2 拉普拉斯金字塔 1 图像金字塔 高斯金字塔 在 OpenCV 中使用函数 cv2.pyrDown(),实现图像高斯金字塔操作中的向下采样,使用函数 cv2.pyrUp() 实现图像金字塔操作中的向上采样 import cv2img cv2.imread(C://Users/Administrat…...

如何使用Jmeter进行压力测试?
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、什么是压力测试 软件测试中:压力测试(Stress Test),也称为强度测试、负载测试。压力测试是模拟实际应用的软硬…...

超大规模芯片验证:基于AMD VP1902的S8-100原型验证系统实测性能翻倍
引言: 随着AI、HPC及超大规模芯片设计需求呈指数级增长原型验证平台已成为芯片设计流程中验证复杂架构、缩短迭代周期的核心工具。然而,传统原型验证系统受限于单芯片容量(通常<5000万门)、多芯片分割效率及系统级联能力&#…...
【CATIA的二次开发22】关于抽象对象Document概念详细总结
在CATIA VBA开发中,Document对象是最核心、最基础的对象之一。它代表了当前在CATIA会话中打开的一个文档(文件)。 几乎所有与文件操作、模型访问相关的操作都始于获取一个Document对象。 一、Document对象概述 1、获取Document对象: 当前活动文档: 最常见的方式是获取用户…...
Android LinearLayout、FrameLayout、RelativeLayout、ConstraintLayout大混战
一、为什么布局性能如此重要? 在Android应用中,布局渲染耗时直接决定了界面的流畅度。根据Google官方数据,超过60%的卡顿问题源于布局性能不佳。本文将彻底解析三大传统布局的性能奥秘,并提供可直接落地的优化方案。 二、三大布局…...
Langchain学习笔记(十一):Chain构建与组合技巧
注:本文是Langchain框架的学习笔记;不是教程!不是教程!内容可能有所疏漏,欢迎交流指正。后续将持续更新学习笔记,分享我的学习心得和实践经验。 前言 在LangChain的发展过程中,API设计经历了重…...
数学建模期末速成 聚类分析与判别分析
聚类分析是在不知道有多少类别的前提下,建立某种规则对样本或变量进行分类。判别分析是已知类别,在已知训练样本的前提下,利用训练样本得到判别函数,然后对未知类别的测试样本判别其类别。 聚类分析 根据样本自身的属性…...
python版若依框架开发:前端开发规范
python版若依框架开发 从0起步,扬帆起航。 python版若依部署代码生成指南,迅速落地CURD!项目结构解析前端开发规范文章目录 python版若依框架开发新增 view新增 api新增组件新增样式引⼊依赖新增 view 在 @/views文件下 创建对应的文件夹,一般性一个路由对应⼀个文件, 该…...

《Brief Bioinform》: 鼠脑单细胞与Stereo-seq数据整合算法评估
一、写在前面 基因捕获效率、分辨率一直是空间转录组细胞类型识别的拦路虎,许多算法能够整合单细胞(single-cell, sc)或单细胞核(single-nuclear, sn)数据与空间转录组数据,从而帮助空转数据的细胞类型注释。此前我们介绍过近年新出炉的Stereo-seq平台&…...

Prompt提示工程指南#Kontext图像到图像
重要提示:单个prompt的最大token数为512 # 核心能力 Kontext图像编辑系统能够: 理解图像上下文语义实现精准的局部修改保持原始图像风格一致性支持复杂的多步迭代编辑 # 基础对象修改 示例场景:改变汽车颜色 Prompt设计: Change …...
SQL 基础入门
SQL 基础入门 SQL(全称 Structured Query Language,结构化查询语言)是用于操作关系型数据库的标准语言,主要用于数据的查询、新增、修改和删除。本文面向初学者,介绍 SQL 的基础概念和核心操作。 1. 常见的 SQL 数据…...