• Excel导入且进行数据校验


    目录

    ​​​​​​一、产品需求

    二、解决方法

    方案一:大量if-else判断校验

    方案二:请求体加入注解进行校验

    三、测试结果


     

    一、产品需求

    1.下载指定的excel数据模板

    2.excel模板写入数据并导入

    3.导入的时候根据校验规则进行筛选,导入成功的返回成功列表,数据存在问题的返回失败列表,失败列表支持数据编辑修正

    看到需求的第一眼,可能就觉得第三点有点难度,我们知道,传统的数据校验可以通过在传输对象dto上面加注解实现。

    1. //第一种
    2. public Result test1(@RequestBody @Validated TestDTO dto) {...}
    3. //第二种
    4. public Result test2(@RequestBody @Valid TestDTO dto{...}
    5. //第三种
    6. public Result test3(@RequestBody @Validated(value = {SaveGroup.class}) TestDTO dto) {...}

    TestDTO里面会有一些类似 @NotNull@NotBlank@Size等校验注解,这里就不列了。

    然后在全局异常拦截那里进行统一封装,使其返回的数据结构尽量保持统一,所以一般还得有一个RestExceptionHandler类。

    1. @ControllerAdvice
    2. public class RestExceptionHandler {
    3. /**
    4. * 处理参数验证失败异常
    5. * @param e
    6. * @return
    7. */
    8. @ExceptionHandler(MethodArgumentNotValidException.class)
    9. @ResponseBody
    10. @ResponseStatus(HttpStatus.OK)
    11. private Response methodArgumentNotValidException(MethodArgumentNotValidException e) {
    12. log.warn("MethodArgumentNotValidException", e);
    13. FieldError fieldError = e.getBindingResult().getFieldError();
    14. return ResponseUtils.create(CommonCodeEnum.VALIDATE_ERROR.getCode(), CommonCodeEnum.VALIDATE_ERROR.getMessage(), fieldError.getDefaultMessage());
    15. }
    16. }

    讲到常见的数据校验,那么我们画风一转,再回来看需求,可见以上是不满足需求的,首先,我们的入参是一个文件流(指定的Excel模板文件),我们得先解析文件再进行数据校验,合法的放一个集合,不合法的放另一个集合;再者,即使入参是一个数组,这种校验一旦不满足立马进异常处理了,无法返回给前端正确的数据结构,所以今天就分享解决这类需求的解决方案。 

    二、解决方法

    基础数据

    UserExcelVO

    1. import lombok.Data;
    2. import java.util.List;
    3. /**
    4. *
    5. */
    6. @Data
    7. public class UserExcelVO {
    8. /**
    9. * 成功列表
    10. */
    11. private List success;
    12. /**
    13. * 失败列表
    14. */
    15. private List fail;
    16. }

    UserExcel

    1. import com.alibaba.excel.annotation.ExcelProperty;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import javax.validation.constraints.NotBlank;
    6. import javax.validation.constraints.Pattern;
    7. import javax.validation.constraints.Size;
    8. import java.io.Serializable;
    9. /**
    10. *
    11. */
    12. @Data
    13. @AllArgsConstructor
    14. @NoArgsConstructor
    15. public class UserExcel implements Serializable {
    16. @NotBlank(message = "手机号不能为空")
    17. @Size(max = 4)
    18. @ExcelProperty(value = "用户名", index = 0)
    19. private String name;
    20. @ExcelProperty(value = "年龄", index = 1)
    21. private Integer age;
    22. @Pattern(regexp = "^[1][3,4,5,7,8][0-9]{9}$$", message = "手机号不合法")
    23. @NotBlank(message = "手机号不能为空")
    24. @ExcelProperty(value = "手机号", index = 2)
    25. private String mobile;
    26. @ExcelProperty(value = "性别", index = 3)
    27. private String sex;
    28. }

    excel模板数据

     

    方案一:大量if-else判断校验

    1. import com.alibaba.excel.EasyExcel;
    2. import org.apache.commons.lang3.StringUtils;
    3. import org.springframework.web.bind.annotation.PostMapping;
    4. import org.springframework.web.bind.annotation.RequestParam;
    5. import org.springframework.web.bind.annotation.RestController;
    6. import org.springframework.web.multipart.MultipartFile;
    7. import java.io.IOException;
    8. import java.util.ArrayList;
    9. import java.util.List;
    10. /**
    11. *
    12. */
    13. @RestController
    14. @RequestMapping("/excel")
    15. public class ExcelController {
    16. @PostMapping("/importExcel1")
    17. public UserExcelVO importExcel(@RequestParam("file") MultipartFile file) {
    18. List list;
    19. List fail = new ArrayList<>();
    20. UserExcelVO userExcelVO = new UserExcelVO();
    21. String mobileReg = "^[1][3,4,5,7,8][0-9]{9}$";
    22. try {
    23. list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync();
    24. list.forEach(data -> {
    25. // 处理姓名的校验
    26. if (StringUtils.isEmpty(data.getName()) || data.getName().length() > 4) {
    27. fail.add(data);
    28. return;
    29. }
    30. // 处理手机号的校验
    31. if (StringUtils.isEmpty(data.getMobile()) || !data.getMobile().matches(mobileReg)) {
    32. fail.add(data);
    33. return;
    34. }
    35. // 以下根据字段多少可能有n个if...
    36. });
    37. userExcelVO.setFail(fail);
    38. list.removeAll(fail);
    39. userExcelVO.setSuccess(list);
    40. } catch (IOException e) {
    41. e.printStackTrace();
    42. }
    43. return userExcelVO;
    44. }
    45. }

    方案二:请求体加入注解进行校验

    实际的业务场景,一个excel里面假如是订单数据,最少是几十个字段起步的,难道要写几十个if else吗?方案一明显是不合理的,因此使用注解的方式帮我们解决。

    ValidationUtils

    1. import javax.validation.Validation;
    2. import javax.validation.Validator;
    3. import javax.validation.ValidatorFactory;
    4. /**
    5. *
    6. */
    7. public class ValidationUtils {
    8. public static Validator getValidator() {
    9. return validator;
    10. }
    11. static Validator validator;
    12. static {
    13. ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    14. validator = validatorFactory.getValidator();
    15. }
    16. }

    ModelExcelListener

    1. import com.alibaba.excel.context.AnalysisContext;
    2. import com.alibaba.excel.event.AnalysisEventListener;
    3. import lombok.extern.slf4j.Slf4j;
    4. import java.util.ArrayList;
    5. import java.util.List;
    6. /**
    7. *
    8. */
    9. @Slf4j
    10. public class ModelExcelListener extends AnalysisEventListener {
    11. private List datas = new ArrayList<>();
    12. /**
    13. * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
    14. */
    15. @Override
    16. public void invoke(UserExcel data, AnalysisContext context) {
    17. //数据存储到list,供批量处理,或后续自己业务逻辑处理。
    18. log.info("读取到数据{}",data);
    19. datas.add(data);
    20. //根据业务自行处理,可以写入数据库等等
    21. }
    22. //所有的数据解析完了调用
    23. @Override
    24. public void doAfterAllAnalysed(AnalysisContext context) {
    25. log.info("所有数据解析完成");
    26. }
    27. }

     请求

    1. import com.alibaba.excel.EasyExcel;
    2. import org.springframework.web.bind.annotation.PostMapping;
    3. import org.springframework.web.bind.annotation.RequestParam;
    4. import org.springframework.web.bind.annotation.RestController;
    5. import org.springframework.web.multipart.MultipartFile;
    6. import javax.validation.ConstraintViolation;
    7. import java.io.IOException;
    8. import java.util.ArrayList;
    9. import java.util.List;
    10. import java.util.Set;
    11. /**
    12. *
    13. */
    14. @RestController
    15. @RequestMapping("/excel")
    16. public class ExcelController {
    17. @PostMapping("/importExcel2")
    18. public UserExcelVO importExcelV2(@RequestParam("file") MultipartFile file) {
    19. List list;
    20. List fail = new ArrayList<>();
    21. UserExcelVO userExcelVO = new UserExcelVO();
    22. try {
    23. list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync();
    24. list.forEach(data -> {
    25. Set> violations = ValidationUtils.getValidator().validate(data);
    26. if (violations.size() > 0) {
    27. fail.add(data);
    28. }
    29. });
    30. userExcelVO.setFail(fail);
    31. list.removeAll(fail);
    32. userExcelVO.setSuccess(list);
    33. } catch (IOException e) {
    34. e.printStackTrace();
    35. }
    36. return userExcelVO;
    37. }
    38. }

    三、测试结果

    方案一的结果:

    1. {
    2. "success": [
    3. {
    4. "name": "张2",
    5. "age": 19,
    6. "mobile": "13056781235",
    7. "sex": "女"
    8. },
    9. {
    10. "name": "张3",
    11. "age": 20,
    12. "mobile": "13056781236",
    13. "sex": "男"
    14. },
    15. {
    16. "name": "张4",
    17. "age": 21,
    18. "mobile": "13056781237",
    19. "sex": "女"
    20. },
    21. {
    22. "name": "张5",
    23. "age": 22,
    24. "mobile": "13056781238",
    25. "sex": "男"
    26. },
    27. {
    28. "name": "张6",
    29. "age": 23,
    30. "mobile": "13056781239",
    31. "sex": "男"
    32. },
    33. {
    34. "name": "张7",
    35. "age": 24,
    36. "mobile": "13056781240",
    37. "sex": "男"
    38. },
    39. {
    40. "name": "张8",
    41. "age": 25,
    42. "mobile": "13056781241",
    43. "sex": "男"
    44. },
    45. {
    46. "name": "张9",
    47. "age": 26,
    48. "mobile": "13056781242",
    49. "sex": "男"
    50. }
    51. ],
    52. "fail": [
    53. {
    54. "name": "张1",
    55. "age": 18,
    56. "mobile": "3056781234",
    57. "sex": "男"
    58. },
    59. {
    60. "name": "张10",
    61. "age": 27,
    62. "mobile": "130567812436",
    63. "sex": "男"
    64. }
    65. ]
    66. }

    方案二的结果:

    1. {
    2. "success": [
    3. {
    4. "name": "张2",
    5. "age": 19,
    6. "mobile": "13056781235",
    7. "sex": "女"
    8. },
    9. {
    10. "name": "张3",
    11. "age": 20,
    12. "mobile": "13056781236",
    13. "sex": "男"
    14. },
    15. {
    16. "name": "张4",
    17. "age": 21,
    18. "mobile": "13056781237",
    19. "sex": "女"
    20. },
    21. {
    22. "name": "张5",
    23. "age": 22,
    24. "mobile": "13056781238",
    25. "sex": "男"
    26. },
    27. {
    28. "name": "张6",
    29. "age": 23,
    30. "mobile": "13056781239",
    31. "sex": "男"
    32. },
    33. {
    34. "name": "张7",
    35. "age": 24,
    36. "mobile": "13056781240",
    37. "sex": "男"
    38. },
    39. {
    40. "name": "张8",
    41. "age": 25,
    42. "mobile": "13056781241",
    43. "sex": "男"
    44. },
    45. {
    46. "name": "张9",
    47. "age": 26,
    48. "mobile": "13056781242",
    49. "sex": "男"
    50. }
    51. ],
    52. "fail": [
    53. {
    54. "name": "张1",
    55. "age": 18,
    56. "mobile": "3056781234",
    57. "sex": "男"
    58. },
    59. {
    60. "name": "张10",
    61. "age": 27,
    62. "mobile": "130567812436",
    63. "sex": "男"
    64. }
    65. ]
    66. }

    发现两种方案的测试结果虽然是一样的,但是很明显,方案二更优秀。我们后续写代码的时候,除了做功能,也要考虑代码的扩展性,不然产品说加个功能,我们又得吭哧吭哧写代码了。

    如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。 

  • 相关阅读:
    C++行为型模式-职责链模式
    如何将OAK集成到你的系统中?
    P3613 【深基15.例2】寄包柜
    『忘了再学』Shell基础 — 6、Bash基本功能(输入输出重定向)
    JavaScript 全局污染 回调函数
    pytorch笔记:TRIPLETMARGINLOSS
    完全背包代码模板
    Mac安装Mysql,并启动
    EasyNetQ-用于使用 RabbitMQ 的 .NET API开源的工具库
    【Harmony OS】【ArkUI】ets开发 简易视频播放器
  • 原文地址:https://blog.csdn.net/weixin_42555014/article/details/133858328