场景
若依管理系统前后端分离版基于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(
"<br/>" + successNum +
"、" + limitQuotaStatistics.getDeptName() +
" 导入成功");
-
-
}
catch (Exception e) {
-
failureNum++;
-
String
msg
=
"<br/>" + 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(
-
"<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
-
response.
msg +
-
"</div>",
-
"导入结果",
-
{
dangerouslyUseHTMLString:
true }
-
);
-
//上传数据成功后重新请求数据
-
this.$emit(
"getList");
-
},
-
},
-
};
-
</script>
-
-
<style>
-
</style>
导入测试效果
转载:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127871424