01-EXCEL数据导入——数据校验及导入

1.目的

导入excel数据,对异常数据进行校验。

2.需求分析

  • 1.excel数据导入前校验
    • 数据格式校验
    • 数据类型校验
    • 数据是否符合业务进行校验
  • 2.excel中导入数据替换
    • 导入行业数据替换成数据库中code
    • 导入城市数据替换成对应code
    • 导入字典数据替换成对应的code

3.技术分析

  • 1.excel数据的读,使用alibaba提供的easyExcel

  • 2.数据格式校验,自定义注解

  • 3.业务校验,增加校验接口,根据具体业务扩展

4.代码实现过程

1.引入依赖

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>2.2.9</version>
</dependency>

2.定义校验注解类

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RegexCheck {

    RegexCheckEnum[] regexType() default {RegexCheckEnum.NULL} ;

    String dictCode() default ""; //配置字典数据




}

3.定义注解校验类型枚举

@Getter
@AllArgsConstructor
public enum RegexCheckEnum {

    NULL("", "可以为空"),//如果可以为空,必须使用这个类型,其他类型兼带非空的校验
    NOTNULL("\\S","不为空"),
    BIRTHDAY("[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}", "生日"),
    EMAIL("\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?", "邮件"),
    IDCARD("[1-9]\\d{13,16}[a-zA-Z0-9]{1}", "身份证"),
    DIGIT( "\\-?[1-9]\\d+","整数"),
    DECIMAL( "[-+]?[0-9]*\\.?[0-9]+","浮点数"),
    POSTCODE("[1-9]\\d{5}","邮编"),
    CHINESE("[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}","中文"),
    PHONE("^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$", "手机号码");

    private String regex;
    private String msg;

    public static List<String> regexCheck(RegexCheckEnum[] enums,String name, Object val) {
        List<String> msgs = new ArrayList<>();
        //空和非空校验
        if(StringUtils.isEmpty(val)){
            final RegexCheckEnum tempEnum = Arrays.stream(enums).filter(e -> e.name().equals(NULL.name())).findAny().orElse(null);
            if(StringUtils.isEmpty(tempEnum)){
                msgs.add("【"+name+"】不符合不能为空的规则");
            }else{
                return null;
            }
        }
        Arrays.stream(enums).forEach(e->{
            if(!e.name().equals(NULL.name()) && !e.name().equals(NOTNULL.name())){//过滤空和非空校验
                final boolean flag = Pattern.matches(e.getRegex(), String.valueOf(val));
                if(!flag){
                    msgs.add("【"+name+"】不符合"+e.getMsg()+"的规则");
                }
            }
        });
        return msgs;

    }

}

4.定义业务校验拓展接口和通用校验类

  • 业务扩展接口
public interface ImportInterface<T> {

    //每次校验后进行的操作
    void afterPerCheck(List<String> errMsgs, T t);

    //。。。。。其他业务接口在这里进行扩展
}
  • 通用校验处理类
@Component
public class ImportService<T,M extends ImportInterface> {
    //全局缓存数据
    @Autowired
    private TibesInitCache tibesInitCache;
    @Autowired
    private M m;

    /**
     * 导入数据校验操作
     * @param datas
     * @param t
     * @return
     * @throws JsonProcessingException
     */
    public Map<String,Object> checkData(List<T> datas,Class<T> t) throws JsonProcessingException {
        //校验字段
        final Map<String, RegexCheck> fileds =  getFieldCheck(t);
        final Map<String,String> properties = getFieldProperty(t);
        Map<String,List<String>> error = new HashMap<>();
        int count = 0 ;
        for(int i = 0 ; i < datas.size() ; i++) {
            final JSONObject d = (JSONObject) JSONObject.toJSON(datas.get(i));
            List<String> errMsgs = new ArrayList<>();
            fileds.forEach((k,v)->{
                String name=properties.get(k);
                //校验错误信息
                List<String> msgs = RegexCheckEnum.regexCheck(v.regexType(),name,  d.get(k));
                if(!StringUtils.isEmpty(msgs) && msgs.size() > 0){
                    errMsgs.addAll(msgs);
                 }

                 //统一替换字典数据,统一替换城市相关数据后续配置
                 


            });
            //实体类校验后操作,其他个性化校验在这里增加
            m.afterPerCheck(errMsgs,datas.get(i));

            if(errMsgs.size() > 0){
                ++ count ;
                error.put(String.valueOf(i + 1),errMsgs);
            }

        }
        if(count > 0){
            final Map<String, Object> params = new HashMap<>();
            params.put("normalNums",datas.size() - count);
            params.put("errorNums",count);
            params.put("error",error);
            return params ;
        }
        return null;
    }

5.具体使用

  • 实体类
@Data
public class Import {
    @ExcelProperty("姓名")
    @RegexCheck(regexType = {RegexCheckEnum.NOTNULL})
    private String name;

    @ExcelProperty("手机号码")
    @RegexCheck(regexType = {RegexCheckEnum.PHONE})
    private String phone;//手机号
}
  • controller层
    @PostMapping("/import")
    public ResponsePacket  importFile(MultipartFile file) throws Exception {
        final List<Import> datas = EasyExcel.read(file.getInputStream(), Import.class, null).sheet().headRowNumber(2).doReadSync();
        return service.importData(datas);
    }

  • service 层
public interface Service extends IService<CpCustomer>, ImportInterface<Import>{}

public class OperServiceImpl extends ServiceImpl<Mapper, Oper> implements Service{

   @Override
    public void afterPerCheck(List<String> errMsgs, Import import) {
        //业务判断
        boolean flag = 业务处理;
        if(业务处理){
            String msg = "【手机号】"+import.phone()+"已经存在";
            errMsgs.add(msg);
        }
    }
} 

posted @   PerfectLi  阅读(3941)  评论(1编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示