【SpringBoot+Mybatis-plus+poi】实现校验导入excel(二)(附源码)

【SpringBoot+Mybatis-plus+poi】实现校验导入excel(二)(附源码)

实现校验导入excel,我打算用两章实现:
第一章根据工厂模式(三个工厂:导入工厂、校验工厂和实现类导入方法工厂)和简单的非空校验实现user.xlsx的导入校验;
第二章根据工厂模式自定义注解+反射实现user.xlsx的导入校验。
首先看一下excel文档:
在这里插入图片描述
这里我删掉了部分单元格数据,作为非空校验的导入模板;
校验成功则根据id查询数据库的数据,如果结果为空则新增,否则更新
校验失败则给对应单元格标红,并在最后一列打印错误信息
上代码:

1. 抽象

1.1. 非空校验注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 非空校验
 * @author wp
 * @date 2022年1月4日
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckIsNull {

    /**
     * 错误信息
     * @return
     */
    String message() default "";

    /**
     * 字段名称
     * @return
     */
    String name() default "";

    /**
     * 单元格索引
     * @return
     */
    int index();

    /**
     * 是否为表头数据
     * @return
     */
    boolean btData() default false;
}

1.2. 非空校验注解配置文件
/**
 * CheckAnnotationConfig
 * @author wp
 * @date 2022年1月4日
 */
public class CheckIsNullAnnotationConfig {

    private final String name;
    private final int index;
    // 表头数据
    private final boolean btData;

    public CheckIsNullAnnotationConfig(CheckIsNull checkIsNull) {
        this.name = checkIsNull.name();
        this.index = checkIsNull.index();
        this.btData = checkIsNull.btData();
    }

    public String getName() {
        return name;
    }

    public int getIndex() {
        return index;
    }

    public boolean getBtData() {
        return btData;
    }
}

1.3. 抽象校验方式
import com.example.import1.vo.CheckVo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 抽象校验方式
 * @author wp
 * @date 2022年1月4日
 */
public abstract class CheckMethod<V> {

    public void annotationMethod(V v, CheckVo checkVo) throws IllegalAccessException {
        Class<?> aClass = v.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            validation(declaredField, v, checkVo);
        }
    }

    private void validation(Field field, V v, CheckVo checkVo) throws IllegalAccessException {
        // 获取每个字段上的所有注解
        Annotation[] annotations = field.getAnnotations();
        field.setAccessible(true);
        for (Annotation annotation : annotations) {
            AnnotationType<V> annotationType = new AnnotationType<>();
            annotationType.choiceType(annotation, v, field, checkVo);
        }
    }
}
1.4. 实现抽象校验方式
/**
 * CheckMethodOutput
 *
 * @author wp
 * @date 2022年1月4日
 */
public class CheckMethodOutput<V> extends CheckMethod<V>{
}
1.5. 抽象校验类型工厂
import com.example.import1.vo.CheckVo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 抽象校验类型工厂
 * @author wp
 * @date 2022年1月4日
 */
public interface ICheckAnnotationFactory<V> {

    void checkType(Annotation annotation, Field field, V v, CheckVo checkVo) throws IllegalAccessException;
}

1.6. 校验类型

import com.example.import1.vo.CheckVo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 校验类型
 * @author wp
 * @date 2022年1月4日
 */
public class AnnotationType<V>{

    public void choiceType(Annotation annotation, V v, Field field, CheckVo checkVo) throws IllegalAccessException {

        // 非空校验
        ICheckAnnotationFactory<V> isNullCheckType = new IsNullCheckType<>();
        if (annotation instanceof CheckIsNull) {
            isNullCheckType.checkType(annotation, field, v, checkVo);
        }
    }
}
1.7. 非空校验
import com.example.import1.util.CheckoutUtils;
import com.example.import1.vo.CheckVo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 非空校验
 * @author wp
 * @date 2022年1月4日
 * absCheck 预留,私有校验调用
 */
public class IsNullCheckType<V> implements ICheckAnnotationFactory<V> {

    @Override
    public void checkType(Annotation annotation, Field field, V v, CheckVo checkVo) throws IllegalAccessException {
        CheckIsNull checkIsNull = (CheckIsNull) annotation;
        CheckIsNullAnnotationConfig checkIsNullAnnotationConfig = new CheckIsNullAnnotationConfig(checkIsNull);
        Object o = field.get(v);
        String s = null != o ? o.toString() : "";
        CheckoutUtils.isNull(checkIsNullAnnotationConfig.getName(), checkVo.getErrmsg(),
                checkVo.getRow().getCell(checkIsNullAnnotationConfig.getIndex()),
                s, checkVo.getCellStyle(), checkVo.getRow(), checkIsNullAnnotationConfig.getIndex());
    }
}

2. 实体(添加注解)

import com.baomidou.mybatisplus.annotation.TableName;
import com.example.import1.dispose.annotation.CheckIsNull;
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
@TableName("t_user")
public class TUser {

    @CheckIsNull(name = "id", index = 0)
    private Integer id;
    @CheckIsNull(name = "姓名", index = 1)
    private String name;
    @CheckIsNull(name = "年龄", index = 2)
    private String age;
    @CheckIsNull(name = "性别", index = 3)
    private String sex;
    @CheckIsNull(name = "地址", index = 4)
    private String address;
    @CheckIsNull(name = "联系方式", index = 5)
    private String contactInformation;
}

3. 抽象导入(内含注解反射调用)

import com.example.import1.domain.TUser;
import com.example.import1.vo.CheckVo;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * 抽象导入
 * @author wp
 */
public interface IImport<T> {

    /**
     * 校验
     * @param errorMsg
     * @param wb
     * @param s
     * @param allNums
     * @param inputStream
     * @throws IOException
     */
    Integer checkResult(StringBuilder errorMsg, XSSFWorkbook wb, Sheet s, int allNums, InputStream inputStream, List<TUser> users)throws Exception;

    /**
     * 导入数据
     * @param s
     * @param allNums
     */
    Integer checkData(Sheet s, int allNums, CheckVo checkVo, List<String> dataList,
                    StringBuilder errorString, int flag, List<TUser> users) throws IllegalAccessException;

    /**
     * 导入数据处理
     * @param s
     * @param i
     */
    Integer create(Sheet s, int i, CheckVo checkVo, List<String> dataList, StringBuilder errorString, int flag, List<TUser> users) throws IllegalAccessException;

    /**
     * 校验通过插入/更新数据
     * @param zhqJtsyjcs
     */
    void createData(List<T> zhqJtsyjcs);
}

3.1. 生产导入方式
import cn.hutool.core.bean.BeanUtil;
import com.example.import1.dispose.IImport;
import com.example.import1.dispose.annotation.CheckMethod;
import com.example.import1.dispose.annotation.CheckMethodOutput;
import com.example.import1.domain.TUser;
import com.example.import1.mapper.TUserMapper;
import com.example.import1.vo.CheckVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFDataFormat;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * user导入
 * @author wp
 */
public class UserImport implements IImport<TUser> {
    private final DataFormatter DATAFORMATTER = new DataFormatter();
    //excel起始校验行下标
    private static final int START_INDEX = 1;

    //excel错误信息下标
    private static final int ERROR_INDEX = 6;

    private final TUserMapper userMapper;

    public UserImport(TUserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public Integer checkResult(StringBuilder errorMsg, XSSFWorkbook wb, Sheet s, int allNums, InputStream inputStream, List<TUser> users) throws Exception {
        // 标识为0,文件校验没有出现错误,执行插入;否则导出错误信息
        int flag = 0;
        //获取默认单元格样式(在第一行创建一个空单元格获取默认样式)
        XSSFCellStyle orgSty = (XSSFCellStyle) s.getRow(0).createCell(100).getCellStyle();
        //设置单元格为文本格式
        XSSFDataFormat format = wb.createDataFormat();
        orgSty.setDataFormat(format.getFormat("@"));
        //错误标红单元格样式
        XSSFCellStyle cellStyle = wb.createCellStyle();
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
        cellStyle.setDataFormat(format.getFormat("@"));
        // 重复数据缓存
        List<String> dataList = new ArrayList<>();
        CheckVo checkVo = new CheckVo();
        checkVo.setCellStyle(cellStyle);
        return checkData(s, allNums, checkVo, dataList, errorMsg, flag, users);
    }

    @Override
    public Integer checkData(Sheet s, int allNums, CheckVo checkVo, List<String> dataList,
                              StringBuilder errorString, int flag, List<TUser> users) throws IllegalAccessException {
        for (int i = START_INDEX; i < allNums; i++) {
            Row r = s.getRow(i);
            if (r == null) {
                continue;
            }
            // 导入用户信息
            flag = create(s, i, checkVo, dataList, errorString, flag, users);
        }
        return flag;
    }

    @Transactional
    public Integer create(Sheet s, int i, CheckVo checkVo, List<String> dataList, StringBuilder errorString, int flag, List<TUser> users) throws IllegalAccessException {
        Row r = s.getRow(i);
        TUser user = new TUser();
        CheckVo checkVo1 = BeanUtil.toBean(checkVo, CheckVo.class);
        //定义错误信息msg
        StringBuilder errorMsg = new StringBuilder();
        checkVo1.setErrmsg(errorMsg);
        //获取错误信息清空
        Cell orgErrCell = r.getCell(ERROR_INDEX);
        if (orgErrCell != null) {
            orgErrCell.setCellValue("");
        }
        TUser userCondition = new TUser();
        checkVo1.setRow(r);
        // id
        String id = DATAFORMATTER.formatCellValue(r.getCell(0));
        if (StringUtils.isNotBlank(id)) {
            int idInt = Integer.parseInt(id);
            userCondition = userMapper.selectById(id);
            user.setId(idInt);
        }

        // 姓名
        String name = DATAFORMATTER.formatCellValue(r.getCell(1));
        user.setName(name);

        // 年龄
        String age = DATAFORMATTER.formatCellValue(r.getCell(2));
        user.setAge(age);

        // 性别
        String sex = DATAFORMATTER.formatCellValue(r.getCell(3));
        user.setSex(sex);

        // 地址
        String address = DATAFORMATTER.formatCellValue(r.getCell(4));
        user.setAddress(address);

        // 联系方式
        String contactInformation = DATAFORMATTER.formatCellValue(r.getCell(5));
        user.setContactInformation(contactInformation);

        CheckMethod<TUser> checkMethod = new CheckMethodOutput<>();
        checkMethod.annotationMethod(user, checkVo1);

        errorMsg = checkVo1.getErrmsg();
//        errorMsg.append(errMag);
        if(orgErrCell==null) {
            orgErrCell= r.createCell(ERROR_INDEX);
        }
        orgErrCell.setCellValue(errorMsg.toString());
        errorString.append(errorMsg);

        // 如果flag
        if (StringUtils.isNotBlank(errorString.toString())) {
            flag = 1;
        }
        if (flag == 0 && errorString.length() <= 0 || errorString.toString().equals("")) {

            users.add(user);
        }else {
            users.clear();
        }
        return flag;
    }

    @Override
    public void createData(List<TUser> users) {
        for (TUser user : users) {
            TUser userCondition = userMapper.selectById(user.getId());
            if (null != userCondition) {
                userMapper.updateById(user);
            }else {
                userMapper.insert(user);
            }
        }
    }
}

4. dao层

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.import1.domain.TUser;
import org.apache.ibatis.annotations.Mapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author wp
 * @since 2022-01-04
 */
@Mapper
public interface TUserMapper extends BaseMapper<TUser> {

}

5. service层(内含实现类工厂)

5.1. api
5.1.1. 实现类工厂
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;

/**
 * 禽流感环境病原学监测信息Service接口
 *
 * @author wp
 * @date 2021-12-17
 */
public interface ICombineService {

    /**
     * 通用导入
     */
    XSSFWorkbook importExcel(InputStream inputStream) throws Exception;
}
5.1.2. service
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.import1.domain.TUser;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author wp
 * @since 2022-01-04
 */
public interface IUserService extends IService<TUser> {

}
5.2. 实现类导入方法(实现service)
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.import1.dispose.IImport;
import com.example.import1.dispose.input.UserImport;
import com.example.import1.mapper.TUserMapper;
import com.example.import1.domain.TUser;
import com.example.import1.service.ICombineService;
import com.example.import1.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author wp
 * @since 2022-01-04
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements IUserService, ICombineService {

    @Autowired
    private TUserMapper userMapper;


    @Override
    @Transactional
    public XSSFWorkbook importExcel(InputStream inputStream) throws Exception {
        StringBuilder errorString = new StringBuilder();
        XSSFWorkbook wb = new XSSFWorkbook(inputStream);
        Sheet s = wb.getSheetAt(0);
        int allNums = s.getPhysicalNumberOfRows();
        IImport<TUser> check = new UserImport(userMapper);
        List<TUser> users = new ArrayList<>();
        //校验
        Integer flag = check.checkResult(errorString, wb, s, allNums, inputStream, users);
//            check.createData(wb, s, allNums);
        if (flag == 0) {
            check.createData(users);
            wb = null;
        }
        return wb;
    }
}

6. 工具类

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;

public class CheckoutUtils {
    /**
     * 非空校验
     * @param zhName
     * @param errorMsg
     * @param cell
     * @param cellStyle
     * @return
     */
    public static boolean isNull(String zhName, StringBuilder errorMsg, Cell cell, String cellString, XSSFCellStyle cellStyle, Row r, int i){
        //如果为空则增加错误信息
        if(StringUtils.isBlank(cellString)) {
            errorMsg.append(zhName).append("必填; ");
            setCellStyle(r, i, cell, cellStyle);
            return false;
        }
        CellStyle style = cell.getCellStyle();
        if (IndexedColors.RED.getIndex() == style.getFillBackgroundColor()) {
            cell.setCellStyle(null);
        }
        return true;
    }

    private static void setCellStyle(Row r, int i, Cell cell, XSSFCellStyle cellStyle) {
        if (null == cell) {
            cell = r.createCell(i);
        }
        cell.setCellStyle(cellStyle);
    }
}

7. 实体Vo

import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;

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

@Data
@Accessors(chain = true)
public class CheckVo implements Serializable {
    private static final long serialVersionUID = -1494924043115338235L;

    /** 错误信息 */
    private StringBuilder errmsg;

    /** 行数 */
    private Row row;

    /** 对应单元格 */
    private Cell cell;

    /** 对应单元格数据 */
    private String s;

    /** 单元格样式 */
    private XSSFCellStyle cellStyle;

}

8. Controller层

import com.example.import1.domain.TUser;
import com.example.import1.service.ICombineService;
import com.example.import1.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author wp
 * @since 2022-01-04
 */
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private IUserService userService;

    @Autowired
    private ICombineService combineService;

    /**
     * 用户查询
     * @return
     */
    @GetMapping("/query/user")
    public ResponseEntity<List<TUser>> queryUser(){
        return ResponseEntity.ok(userService.list());
    }

    /**
     * 用户导入
     */
    @PostMapping("/import/user")
    public ResponseEntity<Boolean> importExcel(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws Exception {
        XSSFWorkbook wb = combineService.importExcel(file.getInputStream());
        if (wb!=null){
            ServletOutputStream outputStream = response.getOutputStream();
            wb.write(outputStream);
            wb.close();
            outputStream.close();
        }
        return ResponseEntity.ok(true);
    }
}

9. 项目层级

在这里插入图片描述

其余和第二章一样,不变

10. 导入结果(校验失败)(注意postman导入失败时要改后缀zip->xlsx)

在这里插入图片描述

11. 导入结果(校验成功,数据库对应id数据已改正)

在这里插入图片描述

在这里插入图片描述
根据自定义注解导入校验,代码灵活度非常高,如果要进行不同维度的校验,只需要添加枚举类,内部添加校验方式;或者直接创建校验方式注解对象,不同校验方式实现不同的校验,进行判断就可以完成。
承接上一章多个类型的excel文件校验,只需要添加特定的实体,上面配置注解即可达到目的。
纯手打,技术有限,请各位大佬批评指正,谢谢!
项目已经提交在git上
导入校验项目git地址:
https://gitee.com/table-tennis-king/import_test.git

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值