『博客开发日记-后台』之导入用户数据接口的实现

本文最后更新于 2026年5月21日 晚上

导入用户数据接口的实现


导入用户数据接口的需求

判断导入的文件是否为空

验证文件格式

验证文件大小不能超过1m

读取每一行表格数据并校验每条用户数据

校验导入数据的规格和内容

保存用户信息


代码实现

AdminUserController

1
2
3
4
5
6
7
8
9
@PostMapping(value = "/import")
@PreAuthorize("@ps.hasPermission('sys:user:import')")
@SystemLog(businessName = "用户导入")
@ApiOperation(value = "用户导入接口", notes = "用户导入", response = ResponseResult.class)
@ApiImplicitParam(name = "file", value = "Excel导入文件", dataType = "file", paramType = "form")
public ResponseResult importUsers(@RequestPart("file") MultipartFile file)
{
return adminUserService.importUsers(file);
}

在 AdminUserServiceImpl 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//导入用户数据
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseResult importUsers(MultipartFile file)
{
//判断导入的文件是否为空
if (file == null || file.isEmpty()) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "请选择要导入的Excel文件");
}

String originalFilename = file.getOriginalFilename();
if (!StringUtils.hasText(originalFilename)) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "文件名不能为空");
}

//验证文件格式
String lowerFilename = originalFilename.toLowerCase(Locale.ROOT);
if (!(lowerFilename.endsWith(".xls") || lowerFilename.endsWith(".xlsx"))) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "只允许导入 .xls 或 .xlsx 格式的文件");
}

//验证文件大小不能超过1m
long maxSize = 1024L * 1024L;
if (file.getSize() > maxSize) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "文件大小不能超过 1MB");
}

List<String> messageList = new ArrayList<>();
AtomicInteger validCount = new AtomicInteger();
AtomicInteger invalidCount = new AtomicInteger();

//读取每一行表格数据并校验每条用户数据
try (InputStream inputStream = file.getInputStream())
{
EasyExcel.read(inputStream, UserImportTemplateVo.class, new AnalysisEventListener<UserImportTemplateVo>() {
//在读取 Excel 表头时触发
@Override
public void invokeHead(Map<Integer, com.alibaba.excel.metadata.data.ReadCellData<?>> headMap, AnalysisContext context) {
//只触发但不执行操作
}
@Override
public void invoke(UserImportTemplateVo data, AnalysisContext context)
{
//校验导入数据的规格和内容
int rowNum = context.readRowHolder().getRowIndex() + 1;
String validationError = validateImportRow(data);
if (StringUtils.hasText(validationError)) {
invalidCount.incrementAndGet();
messageList.add("第" + rowNum + "行:" + validationError);
return;
}

//保存用户信息
ResponseResult importResult = saveImportedUser(data);
if (importResult.getCode() == 200) {
validCount.incrementAndGet();
} else {
invalidCount.incrementAndGet();
messageList.add("第" + rowNum + "行:" + importResult.getMsg());
}
}

//在整份 Excel 全部读取完成后触发
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//只触发但不执行操作
}
}).sheet().doRead();
} catch (IOException e) {
throw new RuntimeException("导入文件读取失败", e);
}

Map<String, Object> result = new HashMap<>();
result.put("validCount", validCount.get());
result.put("invalidCount", invalidCount.get());
result.put("messageList", messageList);
return ResponseResult.okResult(result);
}

//判断导入的规格和内容
private String validateImportRow(UserImportTemplateVo data)
{
if (data == null) {
return "导入数据为空";
}
if (!StringUtils.hasText(data.getUsername())) {
return "用户名不能为空";
}
if (!StringUtils.hasText(data.getNickname())) {
return "昵称不能为空";
}
if (StringUtils.hasText(data.getPhone()) && !data.getPhone().matches("^1[3-9]\\d{9}$")) {
return "手机号格式不正确";
}
if (StringUtils.hasText(data.getEmail()) && !data.getEmail().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) {
return "邮箱格式不正确";
}
return null;
}

//保存导入的角色并设置默认密码
private ResponseResult saveImportedUser(UserImportTemplateVo data)
{
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUsername, data.getUsername());
if (adminUserService.count(queryWrapper) > 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.USERNAME_EXIST, "用户名已存在");
}

if (StringUtils.hasText(data.getPhone())) {
queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getPhone, data.getPhone());
if (adminUserService.count(queryWrapper) > 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.PHONENUMBER_EXIST, "手机号已存在");
}
}

if (StringUtils.hasText(data.getEmail())) {
queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getEmail, data.getEmail());
if (adminUserService.count(queryWrapper) > 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.EMAIL_EXIST, "邮箱已存在");
}
}

SysUser user = new SysUser();
user.setUsername(data.getUsername());
user.setNickname(data.getNickname());
user.setSex(StringUtils.hasText(data.getSex()) ? data.getSex() : SystemConstants.SEX_HIDE);
user.setEmail(data.getEmail());
user.setPhone(data.getPhone());
user.setPassword(new BCryptPasswordEncoder().encode("user123456"));
user.setStatus(SystemConstants.USER_STATUS_NORMAL);
boolean save = adminUserService.save(user);
if (!save) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "保存用户失败");
}

return ResponseResult.okResult();
}




PS:该系列只做为作者学习开发项目做的笔记用

不一定符合读者来学习,仅供参考


预告

后续会记录博客的开发过程

每次学习会做一份笔记来进行发表

“一花一世界,一叶一菩提”


版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/


『博客开发日记-后台』之导入用户数据接口的实现
http://example.com/2026/05/21/『博客开发日记-后台』之导入用户数据接口的实现/
作者
云梦泽
发布于
2026年5月21日
更新于
2026年5月21日
许可协议