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

2 篇文章 1 订阅

使用案例

  • 使用方法
    // 读取数据,hutool读取数据成 List<List<Object>> 的形式
    List<List<Object>> list = excelReader.read();
    
    // 默认方式:对注解上的校验并转换
    List<QUserTemplet> qUserImportList = CheckUtil.check(list, QUserTemplet.class);
    
    
    
    // 携带自定义校验方式:或做其他动作
    List<Long> deptIds = departmentMapper.selectAll(); // 查询所有已知部门
    List<QUserTemplet> 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;

/**
 * 用户表参数
 * <p>
 *
 * @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 <T>   指定类型
     * @return 校验并转换后的数据
     */
    public static <T> List<T> check(List<List<Object>> list, Class<T> clazz) throws CheckException {
        return check(list, clazz, 1, true, null);
    }

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

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

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

        Map<String, Pattern> patternMap = new HashMap<>();
        List<String> msgList = new ArrayList<>();
        List<T> result = new ArrayList<>();

        for (int i = 0, size = list.size(); i < size; i++) {
            int row = i + 1;
            if (skipRow >= row) {
                continue;
            }
            List<Object> 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 <T>        类型
     */
    private static <T> void checkAllField(Field[] fields, List<Object> line,
                                          Map<String, Pattern> patternMap, int row,
                                          List<T> result, Class<T> clazz, CheckConsumer<T, Integer> checkRow) throws CheckException {
        Map<String, Object> 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<T, U> {
        void accept(T t, U u) throws CheckException;
    }

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值