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

使用案例

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

     
  • 需要转换的实体

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.wsitm.web.basic.util.Constant;
import org.wsitm.web.basic.util.check.Check;
import org.wsitm.web.basic.util.export.Header;

import java.io.Serializable;

/**
 * 用户表参数
 * 

* * @author lzy on 2021/4/20 9:12 */ @Data @SuperBuilder @AllArgsConstructor @NoArgsConstructor public class QUserTemplet implements Serializable { /** * 用户(唯一) */ @Check(regex = "^[a-z0-9_-]{3,16}$", message = "用户名不符合要求") private String account; /** * 用户名称 */ @Check(notEmpty = true, message = "用户名称不能为空") private String fullname; /** * 角色描述 */ @Check(regex = "^(((\\d+,)+\\d+)|\\d)$", message = "角色请输入数字且以逗号分隔") private String roles; /** * 用户密码 */ @Check(regex = "^(?![a-zA-Z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[\\w!@#$%^&*]{6,18}$", message = "密码不符合要求") private String password; /** * 性别 */ @Check(regex = "[01]", message = "性别请输入1或0") private Integer sex; /** * 城市id */ @Check(regex = "\\d+", message = "地市请输入数字") private Long cityId; /** * 电话 */ @Check(regex = "^[1][0-9]{10}$", message = "电话号码格式不正确") private String telephone; /** * 邮箱 */ @Check(regex = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", message = "邮箱号码格式不正确") private String email; /** * 部门组织ID */ @Check(regex = "\\d+", message = "部门ID请输入数字") private Long departmentId; /** * 描述 */ @Check() private String remark; private static final long serialVersionUID = 1L; }

源码

package org.wsitm.web.basic.util.check;

import java.lang.annotation.*;

/**
 * 检查校验数据注解
 *
 * @author lzy
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Check {

    /**
     * 校验,不能为空
     *
     * @return true、不能为空,true:表示不能、false:可以为空
     */
    boolean notEmpty() default false;

    /**
     * 校验,正则
     *
     * @return 正则
     */
    String regex() default "";

    /**
     * 校验,时间的格式
     *
     * @return 时间的格式
     */
    String dateFormat() default "";

    /**
     * 异常的描述
     *
     * @return 异常的描述
     */
    String message() default "Incorrect field";
}

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

package org.wsitm.web.basic.util.check;

import java.io.Serializable;
import java.util.List;

/**
 * 业务异常
 *
 * @author lzy
 */
@SuppressWarnings({"unused", "rawtypes"})
public class CheckException extends RuntimeException implements Serializable {
    private static final long serialVersionUID = 1L;

    private int status = 500;
    private String detailedError;
    private List tempList;

    public CheckException(String message) {
        super(message);
    }

    public CheckException(String message, int status) {
        super(message);
        this.status = status;
    }

    public CheckException(String message, String detailedError) {
        super(message);
        this.detailedError = detailedError;
    }

    public CheckException(String message, String detailedError, int status) {
        super(message);
        this.detailedError = detailedError;
        this.status = status;
    }

    public CheckException(String message, String detailedError, List tempList) {
        super(message);
        this.detailedError = detailedError;
        this.tempList = tempList;
    }

    public CheckException(String message, String detailedError, List tempList, int status) {
        super(message);
        this.detailedError = detailedError;
        this.status = status;
        this.tempList = tempList;
    }

    public CheckException(String message, String detailedError, List tempList, int status, Throwable e) {
        super(message, e);
        this.detailedError = detailedError;
        this.tempList = tempList;
        this.status = status;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getDetailedError() {
        return detailedError;
    }

    public void setDetailedError(String detailedError) {
        this.detailedError = detailedError;
    }

    public List getTempList() {
        return tempList;
    }

    public void setTempList(List tempList) {
        this.tempList = tempList;
    }

}

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

package org.wsitm.web.basic.util.check;

import cn.hutool.core.annotation.Alias;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 数据检测并转换工具
 *
 * @author lzy
 */
public class CheckUtil {

    /**
     * 批量检测数据是否合理,并返回指定类型数
     *
     * @param list  原始数据
     * @param clazz 返回类型
     * @param    指定类型
     * @return 校验并转换后的数据
     */
    public static  List check(List> list, Class clazz) throws CheckException {
        return check(list, clazz, 1, true, null);
    }

    /**
     * 批量检测数据是否合理,并返回指定类型数
     *
     * @param list     原始数据
     * @param clazz    返回类型
     * @param checkRow 函数,自定义校验,参数:对象,行数
     * @param       指定类型
     * @return 校验并转换后的数据
     */
    public static  List check(List> list, Class clazz, CheckConsumer checkRow) throws CheckException {
        return check(list, clazz, 1, true, checkRow);
    }

    /**
     * 批量检测数据是否合理,并返回指定类型数
     *
     * @param list     原始数据
     * @param clazz    返回类型
     * @param skipRow  跳过的头部的行数
     * @param checkAll 是否校验全部,是:表示全部校验完成才返回结果集。否:表示一旦发生异常直接返回
     * @param checkRow 函数,自定义校验,参数:对象,行数
     * @param       指定类型
     * @return 校验并转换后的数据
     */
    public static  List check(List> list, Class clazz,
                                    int skipRow, boolean checkAll, CheckConsumer checkRow) throws CheckException {

        Field[] fields = ReflectUtil.getFields(clazz, field -> field.getAnnotation(Check.class) != null);

        Map patternMap = new HashMap<>();
        List msgList = new ArrayList<>();
        List result = new ArrayList<>();

        for (int i = 0, size = list.size(); i < size; i++) {
            int row = i + 1;
            if (skipRow >= row) {
                continue;
            }
            List line = list.get(i);

            if (checkAll) {
                try {
                    checkAllField(fields, line, patternMap, row, result, clazz, checkRow);
                } catch (Exception exception) {
                    msgList.add(exception.getMessage());
                }
            } else {
                checkAllField(fields, line, patternMap, row, result, clazz, checkRow);
            }
        }

        if (CollUtil.isNotEmpty(msgList)) {
            String msg = String.format("校验通过个数:%s,校验失败个数:%s", (list.size() - skipRow - msgList.size()), msgList.size());
            throw new CheckException(msg, String.join("\n", msgList), result);
        }

        return result;
    }

    /**
     * 校验n行的所有字段
     *
     * @param fields     字段数组
     * @param line       行的原数据
     * @param patternMap 枚举MAP
     * @param row        行数
     * @param result     结果集
     * @param clazz      类型
     * @param checkRow   函数,自定义校验,参数:对象,行数
     * @param         类型
     */
    private static  void checkAllField(Field[] fields, List line,
                                          Map patternMap, int row,
                                          List result, Class clazz, CheckConsumer checkRow) throws CheckException {
        Map item = MapUtil.newHashMap();
        for (int i = 0, size = fields.length; i < size; i++) {
            if (CollUtil.isNotEmpty(line) && line.size() > i) {
                Field field = fields[i];
                Check check = field.getAnnotation(Check.class);
                String key = field.getName();
                String tranKey = null;

                Alias alias = field.getAnnotation(Alias.class);
                if (alias != null) {
                    tranKey = alias.value();
                }

                Object value = line.get(i);

                if (check.notEmpty() && StrUtil.isEmpty(String.valueOf(value))) {
                    throw new CheckException("第" + row + "行," + check.message());
                }

                if (StrUtil.isNotEmpty(check.regex())) {
                    Pattern pattern = patternMap.computeIfAbsent(key, k -> Pattern.compile(check.regex(), Pattern.CASE_INSENSITIVE));
                    if (!pattern.matcher(String.valueOf(value)).find()) {
                        throw new CheckException("第" + row + "行," + check.message());
                    }
                }

                if (StrUtil.isNotEmpty(check.dateFormat())) {
                    value = checkDateTime(line, check, field.getType(), row);
                }

                item.put(StrUtil.emptyToDefault(tranKey, key), value);
            }
        }
        T t = BeanUtil.toBean(item, clazz);

        if (checkRow != null) {
            // 个性化校验回调
            checkRow.accept(t, row);
        }

        result.add(t);
    }


    private static Object checkDateTime(Object value, Check check, Class type, int row) throws CheckException {
        try {
            if (type.isAssignableFrom(CharSequence.class) || type.isAssignableFrom(Number.class)) {
                // bean中的类型是 字符串/整形(yyyyMMdd) 时,校验是否符合格式
                if (value instanceof DateTime) {
                    return ((DateTime) value).toString(check.dateFormat());
                }
                if (value instanceof LocalDate) {
                    return ((LocalDate) value).format(DateTimeFormatter.ofPattern(check.dateFormat()));
                }
                if (value instanceof LocalDateTime) {
                    return ((LocalDateTime) value).format(DateTimeFormatter.ofPattern(check.dateFormat()));
                }
            }
            return value;
        } catch (Exception e) {
            throw new CheckException("第" + row + "行," + check.message());
        }
    }

    @FunctionalInterface
    public interface CheckConsumer {
        void accept(T t, U u) throws CheckException;
    }

}
 
  

你可能感兴趣的:(hutool,java,java,hutool,批量导入校验,转换校验,1024程序员节)