• 基于Hutool,文件导入读取,批量转换bean并且自定义校验


    使用案例

    • 使用方法
      1. // 读取数据,hutool读取数据成 List> 的形式
      2. List> list = excelReader.read();
      3. // 默认方式:对注解上的校验并转换
      4. List qUserImportList = CheckUtil.check(list, QUserTemplet.class);
      5. // 携带自定义校验方式:或做其他动作
      6. List deptIds = departmentMapper.selectAll(); // 查询所有已知部门
      7. List qUserImportList = CheckUtil.check(list, QUserTemplet.class, (qUserTemplet, row) -> {
      8. // 判断部门是否存在
      9. if (!CollUtil.contains(deptIds, qUserTemplet.getDepartmentId())) {
      10. throw new CheckException("第" + row + "行,部门不存在,请输入正确ID");
      11. }
      12. // 生成 加密密码 和 盐
      13. String salt = IdUtil.simpleUUID();
      14. String pass = EncryptKit.encryptSha1(qUserTemplet.getPassword(), salt);
      15. qUserTemplet.setPassword(pass);
      16. qUserTemplet.setSalt(salt);
      17. });

       
    • 需要转换的实体
    1. import lombok.AllArgsConstructor;
    2. import lombok.Data;
    3. import lombok.NoArgsConstructor;
    4. import lombok.experimental.SuperBuilder;
    5. import org.wsitm.web.basic.util.Constant;
    6. import org.wsitm.web.basic.util.check.Check;
    7. import org.wsitm.web.basic.util.export.Header;
    8. import java.io.Serializable;
    9. /**
    10. * 用户表参数
    11. *

    12. *
    13. * @author lzy on 2021/4/20 9:12
    14. */
    15. @Data
    16. @SuperBuilder
    17. @AllArgsConstructor
    18. @NoArgsConstructor
    19. public class QUserTemplet implements Serializable {
    20. /**
    21. * 用户(唯一)
    22. */
    23. @Check(regex = "^[a-z0-9_-]{3,16}$", message = "用户名不符合要求")
    24. private String account;
    25. /**
    26. * 用户名称
    27. */
    28. @Check(notEmpty = true, message = "用户名称不能为空")
    29. private String fullname;
    30. /**
    31. * 角色描述
    32. */
    33. @Check(regex = "^(((\\d+,)+\\d+)|\\d)$", message = "角色请输入数字且以逗号分隔")
    34. private String roles;
    35. /**
    36. * 用户密码
    37. */
    38. @Check(regex = "^(?![a-zA-Z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[\\w!@#$%^&*]{6,18}$", message = "密码不符合要求")
    39. private String password;
    40. /**
    41. * 性别
    42. */
    43. @Check(regex = "[01]", message = "性别请输入1或0")
    44. private Integer sex;
    45. /**
    46. * 城市id
    47. */
    48. @Check(regex = "\\d+", message = "地市请输入数字")
    49. private Long cityId;
    50. /**
    51. * 电话
    52. */
    53. @Check(regex = "^[1][0-9]{10}$", message = "电话号码格式不正确")
    54. private String telephone;
    55. /**
    56. * 邮箱
    57. */
    58. @Check(regex = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", message = "邮箱号码格式不正确")
    59. private String email;
    60. /**
    61. * 部门组织ID
    62. */
    63. @Check(regex = "\\d+", message = "部门ID请输入数字")
    64. private Long departmentId;
    65. /**
    66. * 描述
    67. */
    68. @Check()
    69. private String remark;
    70. private static final long serialVersionUID = 1L;
    71. }

    源码

    1. package org.wsitm.web.basic.util.check;
    2. import java.lang.annotation.*;
    3. /**
    4. * 检查校验数据注解
    5. *
    6. * @author lzy
    7. */
    8. @Target({ElementType.FIELD})
    9. @Retention(RetentionPolicy.RUNTIME)
    10. @Documented
    11. public @interface Check {
    12. /**
    13. * 校验,不能为空
    14. *
    15. * @return true、不能为空,true:表示不能、false:可以为空
    16. */
    17. boolean notEmpty() default false;
    18. /**
    19. * 校验,正则
    20. *
    21. * @return 正则
    22. */
    23. String regex() default "";
    24. /**
    25. * 校验,时间的格式
    26. *
    27. * @return 时间的格式
    28. */
    29. String dateFormat() default "";
    30. /**
    31. * 异常的描述
    32. *
    33. * @return 异常的描述
    34. */
    35. String message() default "Incorrect field";
    36. }

    注意:转换校验只转换携带@Check注解的字段,没有设置排序注解,所以排序默认是从上到下且携带@Check注解的字段

    1. package org.wsitm.web.basic.util.check;
    2. import java.io.Serializable;
    3. import java.util.List;
    4. /**
    5. * 业务异常
    6. *
    7. * @author lzy
    8. */
    9. @SuppressWarnings({"unused", "rawtypes"})
    10. public class CheckException extends RuntimeException implements Serializable {
    11. private static final long serialVersionUID = 1L;
    12. private int status = 500;
    13. private String detailedError;
    14. private List tempList;
    15. public CheckException(String message) {
    16. super(message);
    17. }
    18. public CheckException(String message, int status) {
    19. super(message);
    20. this.status = status;
    21. }
    22. public CheckException(String message, String detailedError) {
    23. super(message);
    24. this.detailedError = detailedError;
    25. }
    26. public CheckException(String message, String detailedError, int status) {
    27. super(message);
    28. this.detailedError = detailedError;
    29. this.status = status;
    30. }
    31. public CheckException(String message, String detailedError, List tempList) {
    32. super(message);
    33. this.detailedError = detailedError;
    34. this.tempList = tempList;
    35. }
    36. public CheckException(String message, String detailedError, List tempList, int status) {
    37. super(message);
    38. this.detailedError = detailedError;
    39. this.status = status;
    40. this.tempList = tempList;
    41. }
    42. public CheckException(String message, String detailedError, List tempList, int status, Throwable e) {
    43. super(message, e);
    44. this.detailedError = detailedError;
    45. this.tempList = tempList;
    46. this.status = status;
    47. }
    48. public int getStatus() {
    49. return status;
    50. }
    51. public void setStatus(int status) {
    52. this.status = status;
    53. }
    54. public String getDetailedError() {
    55. return detailedError;
    56. }
    57. public void setDetailedError(String detailedError) {
    58. this.detailedError = detailedError;
    59. }
    60. public List getTempList() {
    61. return tempList;
    62. }
    63. public void setTempList(List tempList) {
    64. this.tempList = tempList;
    65. }
    66. }

     注意:自定义异常,主要用于携带校验异常的数据,便于业务记录

    1. package org.wsitm.web.basic.util.check;
    2. import cn.hutool.core.annotation.Alias;
    3. import cn.hutool.core.bean.BeanUtil;
    4. import cn.hutool.core.collection.CollUtil;
    5. import cn.hutool.core.date.DateTime;
    6. import cn.hutool.core.map.MapUtil;
    7. import cn.hutool.core.util.ReflectUtil;
    8. import cn.hutool.core.util.StrUtil;
    9. import java.lang.reflect.Field;
    10. import java.time.LocalDate;
    11. import java.time.LocalDateTime;
    12. import java.time.format.DateTimeFormatter;
    13. import java.util.ArrayList;
    14. import java.util.HashMap;
    15. import java.util.List;
    16. import java.util.Map;
    17. import java.util.regex.Pattern;
    18. /**
    19. * 数据检测并转换工具
    20. *
    21. * @author lzy
    22. */
    23. public class CheckUtil {
    24. /**
    25. * 批量检测数据是否合理,并返回指定类型数
    26. *
    27. * @param list 原始数据
    28. * @param clazz 返回类型
    29. * @param 指定类型
    30. * @return 校验并转换后的数据
    31. */
    32. public static List check(List> list, Class clazz) throws CheckException {
    33. return check(list, clazz, 1, true, null);
    34. }
    35. /**
    36. * 批量检测数据是否合理,并返回指定类型数
    37. *
    38. * @param list 原始数据
    39. * @param clazz 返回类型
    40. * @param checkRow 函数,自定义校验,参数:对象,行数
    41. * @param 指定类型
    42. * @return 校验并转换后的数据
    43. */
    44. public static List check(List> list, Class clazz, CheckConsumer checkRow) throws CheckException {
    45. return check(list, clazz, 1, true, checkRow);
    46. }
    47. /**
    48. * 批量检测数据是否合理,并返回指定类型数
    49. *
    50. * @param list 原始数据
    51. * @param clazz 返回类型
    52. * @param skipRow 跳过的头部的行数
    53. * @param checkAll 是否校验全部,是:表示全部校验完成才返回结果集。否:表示一旦发生异常直接返回
    54. * @param checkRow 函数,自定义校验,参数:对象,行数
    55. * @param 指定类型
    56. * @return 校验并转换后的数据
    57. */
    58. public static List check(List> list, Class clazz,
    59. int skipRow, boolean checkAll, CheckConsumer checkRow) throws CheckException {
    60. Field[] fields = ReflectUtil.getFields(clazz, field -> field.getAnnotation(Check.class) != null);
    61. Map patternMap = new HashMap<>();
    62. List msgList = new ArrayList<>();
    63. List result = new ArrayList<>();
    64. for (int i = 0, size = list.size(); i < size; i++) {
    65. int row = i + 1;
    66. if (skipRow >= row) {
    67. continue;
    68. }
    69. List line = list.get(i);
    70. if (checkAll) {
    71. try {
    72. checkAllField(fields, line, patternMap, row, result, clazz, checkRow);
    73. } catch (Exception exception) {
    74. msgList.add(exception.getMessage());
    75. }
    76. } else {
    77. checkAllField(fields, line, patternMap, row, result, clazz, checkRow);
    78. }
    79. }
    80. if (CollUtil.isNotEmpty(msgList)) {
    81. String msg = String.format("校验通过个数:%s,校验失败个数:%s", (list.size() - skipRow - msgList.size()), msgList.size());
    82. throw new CheckException(msg, String.join("\n", msgList), result);
    83. }
    84. return result;
    85. }
    86. /**
    87. * 校验n行的所有字段
    88. *
    89. * @param fields 字段数组
    90. * @param line 行的原数据
    91. * @param patternMap 枚举MAP
    92. * @param row 行数
    93. * @param result 结果集
    94. * @param clazz 类型
    95. * @param checkRow 函数,自定义校验,参数:对象,行数
    96. * @param 类型
    97. */
    98. private static void checkAllField(Field[] fields, List line,
    99. Map patternMap, int row,
    100. List result, Class clazz, CheckConsumer checkRow) throws CheckException {
    101. Map item = MapUtil.newHashMap();
    102. for (int i = 0, size = fields.length; i < size; i++) {
    103. if (CollUtil.isNotEmpty(line) && line.size() > i) {
    104. Field field = fields[i];
    105. Check check = field.getAnnotation(Check.class);
    106. String key = field.getName();
    107. String tranKey = null;
    108. Alias alias = field.getAnnotation(Alias.class);
    109. if (alias != null) {
    110. tranKey = alias.value();
    111. }
    112. Object value = line.get(i);
    113. if (check.notEmpty() && StrUtil.isEmpty(String.valueOf(value))) {
    114. throw new CheckException("第" + row + "行," + check.message());
    115. }
    116. if (StrUtil.isNotEmpty(check.regex())) {
    117. Pattern pattern = patternMap.computeIfAbsent(key, k -> Pattern.compile(check.regex(), Pattern.CASE_INSENSITIVE));
    118. if (!pattern.matcher(String.valueOf(value)).find()) {
    119. throw new CheckException("第" + row + "行," + check.message());
    120. }
    121. }
    122. if (StrUtil.isNotEmpty(check.dateFormat())) {
    123. value = checkDateTime(line, check, field.getType(), row);
    124. }
    125. item.put(StrUtil.emptyToDefault(tranKey, key), value);
    126. }
    127. }
    128. T t = BeanUtil.toBean(item, clazz);
    129. if (checkRow != null) {
    130. // 个性化校验回调
    131. checkRow.accept(t, row);
    132. }
    133. result.add(t);
    134. }
    135. private static Object checkDateTime(Object value, Check check, Class type, int row) throws CheckException {
    136. try {
    137. if (type.isAssignableFrom(CharSequence.class) || type.isAssignableFrom(Number.class)) {
    138. // bean中的类型是 字符串/整形(yyyyMMdd) 时,校验是否符合格式
    139. if (value instanceof DateTime) {
    140. return ((DateTime) value).toString(check.dateFormat());
    141. }
    142. if (value instanceof LocalDate) {
    143. return ((LocalDate) value).format(DateTimeFormatter.ofPattern(check.dateFormat()));
    144. }
    145. if (value instanceof LocalDateTime) {
    146. return ((LocalDateTime) value).format(DateTimeFormatter.ofPattern(check.dateFormat()));
    147. }
    148. }
    149. return value;
    150. } catch (Exception e) {
    151. throw new CheckException("第" + row + "行," + check.message());
    152. }
    153. }
    154. @FunctionalInterface
    155. public interface CheckConsumer {
    156. void accept(T t, U u) throws CheckException;
    157. }
    158. }
    159. 相关阅读:
      Linux系统——LVS负载均衡群集
      数据结构笔记(王道考研) 第三章:栈和队列
      【编程不良人】Redis后端实战学习笔记01---NoSQL简介、分类、应用场景;Redis特点、安装、相关指令
      【实验】配置用户自动获取IPv6地址的案例
      LeetCode每日一题 分发糖果
      Kubernetes(k8s第二部分)
      java基础---RandomAccessFile
      第一章:为什么要并行计算
      如何在企业网站里做好网络安全
      Flink
    160. 原文地址:https://blog.csdn.net/LZY_1993/article/details/126913783