若依管理系统前后端分离版基于ElementUI和SpringBoot怎样实现Excel导入和导出:
若依管理系统前后端分离版基于ElementUI和SpringBoot怎样实现Excel导入和导出_霸道流氓气质的博客-CSDN博客
SpringBoot+Vue实现excel导入带格式化的时间参数(moment格式化明天日期并设置el-date-picker默认值):
SpringBoot+Vue实现excel导入带格式化的时间参数(moment格式化明天日期并设置el-date-picker默认值)_霸道流氓气质的博客-CSDN博客
在上面搭建SpringBoot+Vue并实现Excel导入的基础上,为避免导入的excel中数据格式不规范
导致产生大量脏数据,所以需要对excel的数据进行校验,比如不能为空、类型是否为数字、数字范围等等规则。
若依官方对这块有专门说明

说明比较简单,具体实现流程如下,也可参考其用户导入的实现代码和流程。

这里对用户账号做了非空校验,需要在实体类上对应属性添加
- @Xss(message = "用户账号不能包含脚本字符")
- @NotBlank(message = "用户账号不能为空")
- @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
- public String getUserName()
- {
- return userName;
- }
查看@NotBlank注解的实现

发现其来自javax.validation。
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
Hibernate Validator 是 Bean Validation 的参考实现 。Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,
除此之外还有一些附加的 constraint 在日常开发中,Hibernate Validator经常用来验证bean的字段,基于注解,方便快捷高效。
在SpringBoot中可以使用@Validated,注解Hibernate Validator加强版,也可以使用@Valid原来Bean Validation java版本
添加pom依赖
- <!-- 自定义验证注解 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
- </dependency>
若依框架添加在common模块中

使用 Validation API 进行参数效验步骤整个过程如下图所示,用户访问接口,然后进行参数效验 ,
如果效验通过,则进入业务逻辑,否则抛出异常,交由全局异常处理器进行处理

自定义全局捕获异常
- /**
- * 全局异常处理器
- *
- * @author ruoyi
- */
- @RestControllerAdvice
- public class GlobalExceptionHandler
- {
- private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
-
- /**
- * 自定义验证异常
- */
- @ExceptionHandler(BindException.class)
- public AjaxResult handleBindException(BindException e)
- {
- log.error(e.getMessage(), e);
- String message = e.getAllErrors().get(0).getDefaultMessage();
- return AjaxResult.error(message);
- }
-
- /**
- * 自定义验证异常
- */
- @ExceptionHandler(MethodArgumentNotValidException.class)
- public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
- {
- log.error(e.getMessage(), e);
- String message = e.getBindingResult().getFieldError().getDefaultMessage();
- return AjaxResult.error(message);
- }
- }
若依框架中将其定义在framework模块下

@ExceptionHandler用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。
在需要校验的实体类属性上或者get方法上添加校验规则注解 ,比如下面
- @Xss(message = "用户昵称不能包含脚本字符")
- @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
- public String getNickName()
- {
- return nickName;
- }
-
- public void setNickName(String nickName)
- {
- this.nickName = nickName;
- }
-
- @Xss(message = "用户账号不能包含脚本字符")
- @NotBlank(message = "用户账号不能为空")
- @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
- public String getUserName()
- {
- return userName;
- }
-
- public void setUserName(String userName)
- {
- this.userName = userName;
- }
-
- @Email(message = "邮箱格式不正确")
- @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
- public String getEmail()
- {
- return email;
- }
-
- public void setEmail(String email)
- {
- this.email = email;
- }
-
- @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
- public String getPhonenumber()
- {
- return phonenumber;
- }
| 注解名称 | 功能 |
|---|---|
| @Xss | 检查该字段是否存在跨站脚本工具 |
| @Null | 检查该字段为空 |
| @NotNull | 不能为null |
| @NotBlank | 不能为空,常用于检查空字符串 |
| @NotEmpty | 不能为空,多用于检测list是否size是0 |
| @Max | 该字段的值只能小于或等于该值 |
| @Min | 该字段的值只能大于或等于该值 |
| @Past | 检查该字段的日期是在过去 |
| @Future | 检查该字段的日期是否是属于将来的日期 |
| 检查是否是一个有效的email地址 | |
| @Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
| @Range(min=,max=,message=) | 被注释的元素必须在合适的范围内 |
| @Size(min=, max=) | 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等 |
| @Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
| @AssertTrue | 用于boolean字段,该字段只能为true |
| @AssertFalse | 该字段的值只能为false |
其它格式校验的注解可自行搜索,用法较多。
如果是在Controller中传参时进行校验,可以直接添加注解@Validated
比如添加用户的Controller
- @PreAuthorize("@ss.hasPermi('system:user:add')")
- @Log(title = "用户管理", businessType = BusinessType.INSERT)
- @PostMapping
- public AjaxResult add(@Validated @RequestBody SysUser user)
- {
-
- }
但是形如Excel导入时,Controller接收的是MultipartFile数据,无法在Controller添加校验
- @PostMapping("/importData")
- public AjaxResult importData(MultipartFile file, String planDateString) throws Exception {
- ExcelUtil<LimitQuotaStatistics> util = new ExcelUtil<LimitQuotaStatistics>(LimitQuotaStatistics.class);
- List<LimitQuotaStatistics> limitQuotaStatisticsList = util.importExcel(file.getInputStream());
- String message =limitQuotaStatisticsService.insertLimitQuotaStatisticsBatch(limitQuotaStatisticsList,planDateString);
- return success(message);
- }
就需要在serviceImpl中通过如下方式进行注入和使用
先通过
- @Autowired
- protected Validator validator;
注入依赖
再通过
BeanValidators.validateWithException(validator, limitQuotaStatistics);
进行参数校验
- @Service
- public class LimitQuotaStatisticsServiceImpl implements ILimitQuotaStatisticsService {
-
- private static final Logger log = LoggerFactory.getLogger(LimitQuotaStatisticsServiceImpl.class);
-
- @Autowired
- private LimitQuotaStatisticsMapper limitQuotaStatisticsMapper;
-
- @Autowired
- protected Validator validator;
-
-
- @Override
- public String insertLimitQuotaStatisticsBatch(List<LimitQuotaStatistics> limitQuotaStatisticsList, String planDateString) throws ParseException {
-
- if (StringUtils.isNull(limitQuotaStatisticsList) || limitQuotaStatisticsList.size() == 0) {
- throw new ServiceException("导入数据不能为空!");
- }
- int successNum = 0;
- int failureNum = 0;
- StringBuilder successMsg = new StringBuilder();
- StringBuilder failureMsg = new StringBuilder();
-
- Date plaeDate = new SimpleDateFormat("yyyy-MM-dd").parse(planDateString);
-
- for (LimitQuotaStatistics limitQuotaStatistics : limitQuotaStatisticsList) {
- try {
- BeanValidators.validateWithException(validator, limitQuotaStatistics);
- limitQuotaStatistics.setPlanDate(plaeDate);
- this.insertLimitQuotaStatistics(limitQuotaStatistics);
- successNum++;
- successMsg.append("
" + successNum + "、" + limitQuotaStatistics.getDeptName() + " 导入成功"); -
- } catch (Exception e) {
- failureNum++;
- String msg = "
" + failureNum + "、" + limitQuotaStatistics.getDeptName() + " 导入失败:"; - failureMsg.append(msg + e.getMessage());
- log.error(msg, e);
- }
- }
- if (failureNum > 0) {
- failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
- throw new ServiceException(failureMsg.toString());
- } else {
- successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
- }
- return successMsg.toString();
- }
- }
其中BeanValidators的实现如下
- package com.bdtd.limit.common.utils.bean;
-
- import java.util.Set;
- import javax.validation.ConstraintViolation;
- import javax.validation.ConstraintViolationException;
- import javax.validation.Validator;
-
- /**
- * bean对象属性验证
- *
- * @author ruoyi
- */
- public class BeanValidators
- {
- public static void validateWithException(Validator validator, Object object, Class<?>... groups)
- throws ConstraintViolationException
- {
- Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
- if (!constraintViolations.isEmpty())
- {
- throw new ConstraintViolationException(constraintViolations);
- }
- }
- }
这里要校验的实体类添加规则为
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- @Builder
- public class LimitQuotaStatistics extends BaseEntity
- {
- private static final long serialVersionUID = 1L;
-
- /** id */
- private Long id;
-
- /** 部门id */
- private Long deptId;
-
- /** 部门名称 */
- @Excel(name = "部门名称")
- @NotNull(message = "部门名称为空")
- private String deptName;
-
- /** 夜班人数 */
- @Excel(name = "夜班人数")
- @NotNull(message = "夜班人数为空或格式不对")
- @Range(max = 1000, min = 1, message = "夜班人数需在1-1000之间")
- private Long nightShiftNum;
- }
部分字段,仅供参考。
组件实现代码参考若依官方文档中示例
- <template>
- <!-- 用户导入对话框 -->
- <el-dialog :title="title" :visible.sync="open" width="400px" append-to-body>
- <div class="block">
- <span class="demonstration">计划日期: </span>
- <el-date-picker
- v-model="planDate"
- type="date"
- placeholder="选择计划日期"
- size="small"
- value-format="yyyy-MM-dd"
- >
- </el-date-picker>
- </div>
- <br />
- <el-upload
- ref="upload"
- :limit="1"
- accept=".xlsx, .xls"
- :headers="headers"
- :action="upLoadUrl + '?planDateString=' + this.planDate"
- :disabled="isUploading"
- :on-progress="handleFileUploadProgress"
- :on-success="handleFileSuccess"
- :auto-upload="false"
- drag
- >
- <i class="el-icon-upload"></i>
- <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
- <div class="el-upload__tip text-center" slot="tip">
- <span>仅允许导入xls、xlsx格式文件。</span>
- </div>
- </el-upload>
- <div slot="footer" class="dialog-footer">
- <el-button type="primary" @click="submitFileForm">确 定</el-button>
- <el-button @click="open = false">取 消</el-button>
- </div>
- </el-dialog>
- </template>
-
- <script>
- import { getToken } from "@/utils/auth";
- import moment from "moment";
-
- export default {
- data() {
- return {
- // 是否显示弹出层(用户导入)
- open: false,
- // 弹出层标题(用户导入)
- title: "",
- // 是否禁用上传
- isUploading: false,
- //计划日期
- planDate: new Date(),
- // 设置上传的请求头部
- headers: { Authorization: "Bearer " + getToken() },
- // 上传的地址
- upLoadUrl: "",
- };
- },
- mounted() {
- //默认计划日期为明天
- this.planDate = moment().subtract(-1, "days").format("YYYY-MM-DD");
- },
- methods: {
- /** 导入按钮操作 */
- handleImport(data) {
- this.title = data.title;
- this.upLoadUrl = process.env.VUE_APP_BASE_API + data.upLoadUrl;
- this.open = true;
- },
- // 提交上传文件
- submitFileForm() {
- this.$refs.upload.submit();
- },
- // 文件上传中处理
- handleFileUploadProgress() {
- this.isUploading = true;
- },
- // 文件上传成功处理
- handleFileSuccess(response) {
- this.open = false;
- this.isUploading = false;
- this.$refs.upload.clearFiles();
- this.$alert(
- "
- response.msg +
- "",
- "导入结果",
- { dangerouslyUseHTMLString: true }
- );
- //上传数据成功后重新请求数据
- this.$emit("getList");
- },
- },
- };
- </script>
-
- <style>
- </style>
导入测试效果
