feat: 优化用户导入失败反馈与模板说明
- 支持按字段返回用户导入失败明细 - 补充导入模板填写说明与名称匹配规则 - 优化管理端导入失败弹窗与错误展示
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package tech.easyflow.system.entity.vo;
|
||||
|
||||
/**
|
||||
* 用户导入失败明细。
|
||||
*/
|
||||
public class SysAccountImportErrorDetailVo {
|
||||
|
||||
private String fieldName;
|
||||
|
||||
private String fieldValue;
|
||||
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* 获取问题字段名。
|
||||
*
|
||||
* @return 字段名
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置问题字段名。
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
*/
|
||||
public void setFieldName(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户填写值。
|
||||
*
|
||||
* @return 用户填写值
|
||||
*/
|
||||
public String getFieldValue() {
|
||||
return fieldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户填写值。
|
||||
*
|
||||
* @param fieldValue 用户填写值
|
||||
*/
|
||||
public void setFieldValue(String fieldValue) {
|
||||
this.fieldValue = fieldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取失败原因。
|
||||
*
|
||||
* @return 失败原因
|
||||
*/
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置失败原因。
|
||||
*
|
||||
* @param reason 失败原因
|
||||
*/
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package tech.easyflow.system.entity.vo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户导入失败行。
|
||||
*/
|
||||
@@ -7,41 +10,141 @@ public class SysAccountImportErrorRowVo {
|
||||
|
||||
private Integer rowNumber;
|
||||
|
||||
private String deptCode;
|
||||
private String deptName;
|
||||
|
||||
private String loginName;
|
||||
|
||||
private String reason;
|
||||
private String nickname;
|
||||
|
||||
private String roleNames;
|
||||
|
||||
private String positionNames;
|
||||
|
||||
private List<SysAccountImportErrorDetailVo> details = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 获取行号。
|
||||
*
|
||||
* @return 行号
|
||||
*/
|
||||
public Integer getRowNumber() {
|
||||
return rowNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置行号。
|
||||
*
|
||||
* @param rowNumber 行号
|
||||
*/
|
||||
public void setRowNumber(Integer rowNumber) {
|
||||
this.rowNumber = rowNumber;
|
||||
}
|
||||
|
||||
public String getDeptCode() {
|
||||
return deptCode;
|
||||
/**
|
||||
* 获取部门名称。
|
||||
*
|
||||
* @return 部门名称
|
||||
*/
|
||||
public String getDeptName() {
|
||||
return deptName;
|
||||
}
|
||||
|
||||
public void setDeptCode(String deptCode) {
|
||||
this.deptCode = deptCode;
|
||||
/**
|
||||
* 设置部门名称。
|
||||
*
|
||||
* @param deptName 部门名称
|
||||
*/
|
||||
public void setDeptName(String deptName) {
|
||||
this.deptName = deptName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录账号。
|
||||
*
|
||||
* @return 登录账号
|
||||
*/
|
||||
public String getLoginName() {
|
||||
return loginName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置登录账号。
|
||||
*
|
||||
* @param loginName 登录账号
|
||||
*/
|
||||
public void setLoginName(String loginName) {
|
||||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
/**
|
||||
* 获取昵称。
|
||||
*
|
||||
* @return 昵称
|
||||
*/
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
/**
|
||||
* 设置昵称。
|
||||
*
|
||||
* @param nickname 昵称
|
||||
*/
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色名称集合文本。
|
||||
*
|
||||
* @return 角色名称集合文本
|
||||
*/
|
||||
public String getRoleNames() {
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置角色名称集合文本。
|
||||
*
|
||||
* @param roleNames 角色名称集合文本
|
||||
*/
|
||||
public void setRoleNames(String roleNames) {
|
||||
this.roleNames = roleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取岗位名称集合文本。
|
||||
*
|
||||
* @return 岗位名称集合文本
|
||||
*/
|
||||
public String getPositionNames() {
|
||||
return positionNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置岗位名称集合文本。
|
||||
*
|
||||
* @param positionNames 岗位名称集合文本
|
||||
*/
|
||||
public void setPositionNames(String positionNames) {
|
||||
this.positionNames = positionNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取失败明细。
|
||||
*
|
||||
* @return 失败明细
|
||||
*/
|
||||
public List<SysAccountImportErrorDetailVo> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置失败明细。
|
||||
*
|
||||
* @param details 失败明细
|
||||
*/
|
||||
public void setDetails(List<SysAccountImportErrorDetailVo> details) {
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package tech.easyflow.system.service.impl;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import cn.idev.excel.EasyExcel;
|
||||
import cn.idev.excel.ExcelWriter;
|
||||
import cn.idev.excel.FastExcel;
|
||||
import cn.idev.excel.context.AnalysisContext;
|
||||
import cn.idev.excel.metadata.data.ReadCellData;
|
||||
import cn.idev.excel.read.listener.ReadListener;
|
||||
import cn.idev.excel.write.metadata.WriteSheet;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -28,6 +30,7 @@ import tech.easyflow.system.entity.SysPosition;
|
||||
import tech.easyflow.system.entity.SysRole;
|
||||
import tech.easyflow.system.entity.vo.SysAccountBatchActionErrorItemVo;
|
||||
import tech.easyflow.system.entity.vo.SysAccountBatchActionResultVo;
|
||||
import tech.easyflow.system.entity.vo.SysAccountImportErrorDetailVo;
|
||||
import tech.easyflow.system.entity.vo.SysAccountImportErrorRowVo;
|
||||
import tech.easyflow.system.entity.vo.SysAccountImportResultVo;
|
||||
import tech.easyflow.system.mapper.SysAccountMapper;
|
||||
@@ -53,6 +56,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 用户表 服务层实现。
|
||||
@@ -69,15 +73,27 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
private static final String DEFAULT_RESET_PASSWORD = "123456";
|
||||
private static final long MAX_IMPORT_FILE_SIZE_BYTES = 10L * 1024 * 1024;
|
||||
private static final int MAX_IMPORT_ROWS = 5000;
|
||||
private static final String IMPORT_HEAD_DEPT_CODE = "部门编码";
|
||||
private static final String IMPORT_HEAD_LOGIN_NAME = "登录账号";
|
||||
private static final String IMPORT_HEAD_NICKNAME = "昵称";
|
||||
private static final String IMPORT_HEAD_DEPT_NAME = "部门名称*";
|
||||
private static final String IMPORT_HEAD_LOGIN_NAME = "登录账号*";
|
||||
private static final String IMPORT_HEAD_NICKNAME = "昵称*";
|
||||
private static final String IMPORT_HEAD_MOBILE = "手机号";
|
||||
private static final String IMPORT_HEAD_EMAIL = "邮箱";
|
||||
private static final String IMPORT_HEAD_STATUS = "状态";
|
||||
private static final String IMPORT_HEAD_ROLE_KEYS = "角色编码";
|
||||
private static final String IMPORT_HEAD_POSITION_CODES = "岗位编码";
|
||||
private static final String IMPORT_HEAD_ROLE_NAMES = "角色名称";
|
||||
private static final String IMPORT_HEAD_POSITION_NAMES = "岗位名称";
|
||||
private static final String IMPORT_HEAD_REMARK = "备注";
|
||||
private static final String IMPORT_GUIDE_HEAD_ITEM = "说明项";
|
||||
private static final String IMPORT_GUIDE_HEAD_CONTENT = "内容";
|
||||
private static final String IMPORT_FIELD_DEPT_NAME = "部门名称";
|
||||
private static final String IMPORT_FIELD_LOGIN_NAME = "登录账号";
|
||||
private static final String IMPORT_FIELD_NICKNAME = "昵称";
|
||||
private static final String IMPORT_FIELD_MOBILE = "手机号";
|
||||
private static final String IMPORT_FIELD_EMAIL = "邮箱";
|
||||
private static final String IMPORT_FIELD_STATUS = "状态";
|
||||
private static final String IMPORT_FIELD_ROLE_NAME = "角色名称";
|
||||
private static final String IMPORT_FIELD_POSITION_NAME = "岗位名称";
|
||||
private static final String IMPORT_FIELD_SYSTEM = "系统";
|
||||
private static final String IMPORT_ERROR_DUPLICATED_RESOURCE = "存在重名,请先在管理端处理";
|
||||
|
||||
@Resource
|
||||
private SysAccountRoleMapper sysAccountRoleMapper;
|
||||
@@ -244,16 +260,25 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
return result;
|
||||
}
|
||||
|
||||
Map<String, SysDept> deptMap = buildDeptCodeMap();
|
||||
Map<String, SysRole> roleMap = buildRoleKeyMap();
|
||||
Map<String, SysPosition> positionMap = buildPositionCodeMap();
|
||||
ImportNameLookup<SysDept> deptLookup = buildImportNameLookup(
|
||||
sysDeptMapper.selectListByQuery(QueryWrapper.create()),
|
||||
SysDept::getDeptName
|
||||
);
|
||||
ImportNameLookup<SysRole> roleLookup = buildImportNameLookup(
|
||||
sysRoleMapper.selectListByQuery(QueryWrapper.create()),
|
||||
SysRole::getRoleName
|
||||
);
|
||||
ImportNameLookup<SysPosition> positionLookup = buildImportNameLookup(
|
||||
sysPositionMapper.selectListByQuery(QueryWrapper.create()),
|
||||
SysPosition::getPositionName
|
||||
);
|
||||
for (SysAccountImportRow row : rows) {
|
||||
try {
|
||||
executeInRowTransaction(() -> importSingleRow(row, loginAccount, deptMap, roleMap, positionMap));
|
||||
executeInRowTransaction(() -> importSingleRow(row, loginAccount, deptLookup, roleLookup, positionLookup));
|
||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||
} catch (Exception e) {
|
||||
result.setErrorCount(result.getErrorCount() + 1);
|
||||
appendImportError(result, row, extractImportErrorMessage(e));
|
||||
appendImportError(result, row, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -261,10 +286,20 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
|
||||
@Override
|
||||
public void writeImportTemplate(OutputStream outputStream) {
|
||||
EasyExcel.write(outputStream)
|
||||
.head(buildImportHeadList())
|
||||
.sheet("模板")
|
||||
.doWrite(new ArrayList<>());
|
||||
ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
|
||||
try {
|
||||
WriteSheet templateSheet = EasyExcel.writerSheet("模板")
|
||||
.head(buildImportHeadList())
|
||||
.build();
|
||||
excelWriter.write(new ArrayList<>(), templateSheet);
|
||||
|
||||
WriteSheet guideSheet = EasyExcel.writerSheet("填写说明")
|
||||
.head(buildImportGuideHeadList())
|
||||
.build();
|
||||
excelWriter.write(buildImportGuideRows(), guideSheet);
|
||||
} finally {
|
||||
excelWriter.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeInRowTransaction(Runnable action) {
|
||||
@@ -281,29 +316,54 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
|
||||
private void importSingleRow(SysAccountImportRow row,
|
||||
LoginAccount loginAccount,
|
||||
Map<String, SysDept> deptMap,
|
||||
Map<String, SysRole> roleMap,
|
||||
Map<String, SysPosition> positionMap) {
|
||||
String deptCode = trimToNull(row.getDeptCode());
|
||||
ImportNameLookup<SysDept> deptLookup,
|
||||
ImportNameLookup<SysRole> roleLookup,
|
||||
ImportNameLookup<SysPosition> positionLookup) {
|
||||
List<SysAccountImportErrorDetailVo> details = new ArrayList<>();
|
||||
String deptName = trimToNull(row.getDeptName());
|
||||
String loginName = trimToNull(row.getLoginName());
|
||||
String nickname = trimToNull(row.getNickname());
|
||||
if (deptCode == null) {
|
||||
throw new BusinessException("部门编码不能为空");
|
||||
String mobile = trimToNull(row.getMobile());
|
||||
String email = trimToNull(row.getEmail());
|
||||
|
||||
if (deptName == null) {
|
||||
addImportDetail(details, IMPORT_FIELD_DEPT_NAME, row.getDeptName(), "部门名称不能为空");
|
||||
}
|
||||
if (loginName == null) {
|
||||
throw new BusinessException("登录账号不能为空");
|
||||
addImportDetail(details, IMPORT_FIELD_LOGIN_NAME, row.getLoginName(), "登录账号不能为空");
|
||||
} else if (isLoginNameExists(loginName)) {
|
||||
addImportDetail(details, IMPORT_FIELD_LOGIN_NAME, row.getLoginName(), "登录账号已存在");
|
||||
}
|
||||
if (nickname == null) {
|
||||
throw new BusinessException("昵称不能为空");
|
||||
addImportDetail(details, IMPORT_FIELD_NICKNAME, row.getNickname(), "昵称不能为空");
|
||||
}
|
||||
SysDept dept = deptMap.get(deptCode);
|
||||
if (dept == null) {
|
||||
throw new BusinessException("部门编码不存在: " + deptCode);
|
||||
if (mobile != null && !StringUtil.isMobileNumber(mobile)) {
|
||||
addImportDetail(details, IMPORT_FIELD_MOBILE, row.getMobile(), "手机号格式不正确");
|
||||
}
|
||||
if (email != null && !StringUtil.isEmail(email)) {
|
||||
addImportDetail(details, IMPORT_FIELD_EMAIL, row.getEmail(), "邮箱格式不正确");
|
||||
}
|
||||
ensureLoginNameNotExists(loginName);
|
||||
|
||||
List<BigInteger> roleIds = resolveRoleIds(row.getRoleKeys(), roleMap);
|
||||
List<BigInteger> positionIds = resolvePositionIds(row.getPositionCodes(), positionMap);
|
||||
SysDept dept = resolveSingleResource(deptName, deptLookup, IMPORT_FIELD_DEPT_NAME, details);
|
||||
Integer status = parseStatus(row.getStatus(), details);
|
||||
List<BigInteger> roleIds = resolveResourceIds(
|
||||
row.getRoleNames(),
|
||||
roleLookup,
|
||||
SysRole::getId,
|
||||
IMPORT_FIELD_ROLE_NAME,
|
||||
details
|
||||
);
|
||||
List<BigInteger> positionIds = resolveResourceIds(
|
||||
row.getPositionNames(),
|
||||
positionLookup,
|
||||
SysPosition::getId,
|
||||
IMPORT_FIELD_POSITION_NAME,
|
||||
details
|
||||
);
|
||||
|
||||
if (!details.isEmpty()) {
|
||||
throw new ImportRowValidationException(details);
|
||||
}
|
||||
|
||||
SysAccount entity = new SysAccount();
|
||||
entity.setDeptId(dept.getId());
|
||||
@@ -313,9 +373,9 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
entity.setPasswordResetRequired(true);
|
||||
entity.setAccountType(EnumAccountType.NORMAL.getCode());
|
||||
entity.setNickname(nickname);
|
||||
entity.setMobile(trimToNull(row.getMobile()));
|
||||
entity.setEmail(trimToNull(row.getEmail()));
|
||||
entity.setStatus(parseStatus(row.getStatus()));
|
||||
entity.setMobile(mobile);
|
||||
entity.setEmail(email);
|
||||
entity.setStatus(status);
|
||||
entity.setRemark(trimToNull(row.getRemark()));
|
||||
entity.setCreated(new Date());
|
||||
entity.setCreatedBy(loginAccount.getId());
|
||||
@@ -327,15 +387,26 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
syncRelations(entity);
|
||||
}
|
||||
|
||||
private void ensureLoginNameNotExists(String loginName) {
|
||||
/**
|
||||
* 校验登录账号是否已存在。
|
||||
*
|
||||
* @param loginName 登录账号
|
||||
* @return 是否已存在
|
||||
*/
|
||||
private boolean isLoginNameExists(String loginName) {
|
||||
QueryWrapper wrapper = QueryWrapper.create();
|
||||
wrapper.eq(SysAccount::getLoginName, loginName);
|
||||
if (count(wrapper) > 0) {
|
||||
throw new BusinessException("登录账号已存在: " + loginName);
|
||||
}
|
||||
return count(wrapper) > 0;
|
||||
}
|
||||
|
||||
private Integer parseStatus(String rawStatus) {
|
||||
/**
|
||||
* 解析导入状态列。
|
||||
*
|
||||
* @param rawStatus 原始状态文本
|
||||
* @param details 错误明细收集器
|
||||
* @return 解析后的状态编码
|
||||
*/
|
||||
private Integer parseStatus(String rawStatus, List<SysAccountImportErrorDetailVo> details) {
|
||||
String status = trimToNull(rawStatus);
|
||||
if (status == null) {
|
||||
return EnumDataStatus.AVAILABLE.getCode();
|
||||
@@ -346,39 +417,38 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
if ("0".equals(status) || "未启用".equals(status) || "停用".equals(status) || "禁用".equals(status)) {
|
||||
return EnumDataStatus.UNAVAILABLE.getCode();
|
||||
}
|
||||
throw new BusinessException("状态不合法,仅支持 1/0 或 已启用/未启用");
|
||||
addImportDetail(details, IMPORT_FIELD_STATUS, rawStatus, "状态不合法,仅支持 1/0/已启用/启用/未启用/停用/禁用");
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<BigInteger> resolveRoleIds(String roleKeysText, Map<String, SysRole> roleMap) {
|
||||
List<String> roleKeys = splitCodes(roleKeysText);
|
||||
if (roleKeys.isEmpty()) {
|
||||
/**
|
||||
* 解析名称列表并映射为主键集合。
|
||||
*
|
||||
* @param rawNames 原始名称文本
|
||||
* @param lookup 名称查找表
|
||||
* @param fieldName 字段名称
|
||||
* @param details 错误明细收集器
|
||||
* @param <T> 资源类型
|
||||
* @return 匹配到的主键集合
|
||||
*/
|
||||
private <T> List<BigInteger> resolveResourceIds(
|
||||
String rawNames,
|
||||
ImportNameLookup<T> lookup,
|
||||
Function<T, BigInteger> idExtractor,
|
||||
String fieldName,
|
||||
List<SysAccountImportErrorDetailVo> details) {
|
||||
List<String> names = splitCodes(rawNames);
|
||||
if (names.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<BigInteger> roleIds = new ArrayList<>(roleKeys.size());
|
||||
for (String roleKey : roleKeys) {
|
||||
SysRole role = roleMap.get(roleKey);
|
||||
if (role == null) {
|
||||
throw new BusinessException("角色编码不存在: " + roleKey);
|
||||
List<BigInteger> ids = new ArrayList<>(names.size());
|
||||
for (String name : names) {
|
||||
T resource = resolveSingleResource(name, lookup, fieldName, details);
|
||||
if (resource != null) {
|
||||
ids.add(idExtractor.apply(resource));
|
||||
}
|
||||
roleIds.add(role.getId());
|
||||
}
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
private List<BigInteger> resolvePositionIds(String positionCodesText, Map<String, SysPosition> positionMap) {
|
||||
List<String> positionCodes = splitCodes(positionCodesText);
|
||||
if (positionCodes.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<BigInteger> positionIds = new ArrayList<>(positionCodes.size());
|
||||
for (String positionCode : positionCodes) {
|
||||
SysPosition position = positionMap.get(positionCode);
|
||||
if (position == null) {
|
||||
throw new BusinessException("岗位编码不存在: " + positionCode);
|
||||
}
|
||||
positionIds.add(position.getId());
|
||||
}
|
||||
return positionIds;
|
||||
return ids;
|
||||
}
|
||||
|
||||
private List<String> splitCodes(String rawCodes) {
|
||||
@@ -398,40 +468,32 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, SysDept> buildDeptCodeMap() {
|
||||
List<SysDept> deptList = sysDeptMapper.selectListByQuery(QueryWrapper.create());
|
||||
Map<String, SysDept> deptMap = new HashMap<>();
|
||||
for (SysDept dept : deptList) {
|
||||
String deptCode = trimToNull(dept.getDeptCode());
|
||||
if (deptCode != null) {
|
||||
deptMap.putIfAbsent(deptCode, dept);
|
||||
/**
|
||||
* 构建导入名称查找表,并标记重名数据。
|
||||
*
|
||||
* @param resources 资源集合
|
||||
* @param nameExtractor 名称提取器
|
||||
* @param <T> 资源类型
|
||||
* @return 名称查找表
|
||||
*/
|
||||
private <T> ImportNameLookup<T> buildImportNameLookup(List<T> resources, Function<T, String> nameExtractor) {
|
||||
Map<String, T> uniqueMap = new HashMap<>();
|
||||
Set<String> duplicateNames = new LinkedHashSet<>();
|
||||
for (T resource : resources) {
|
||||
String name = trimToNull(nameExtractor.apply(resource));
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
if (uniqueMap.containsKey(name)) {
|
||||
duplicateNames.add(name);
|
||||
uniqueMap.remove(name);
|
||||
continue;
|
||||
}
|
||||
if (!duplicateNames.contains(name)) {
|
||||
uniqueMap.put(name, resource);
|
||||
}
|
||||
}
|
||||
return deptMap;
|
||||
}
|
||||
|
||||
private Map<String, SysRole> buildRoleKeyMap() {
|
||||
List<SysRole> roleList = sysRoleMapper.selectListByQuery(QueryWrapper.create());
|
||||
Map<String, SysRole> roleMap = new HashMap<>();
|
||||
for (SysRole role : roleList) {
|
||||
String roleKey = trimToNull(role.getRoleKey());
|
||||
if (roleKey != null) {
|
||||
roleMap.putIfAbsent(roleKey, role);
|
||||
}
|
||||
}
|
||||
return roleMap;
|
||||
}
|
||||
|
||||
private Map<String, SysPosition> buildPositionCodeMap() {
|
||||
List<SysPosition> positionList = sysPositionMapper.selectListByQuery(QueryWrapper.create());
|
||||
Map<String, SysPosition> positionMap = new HashMap<>();
|
||||
for (SysPosition position : positionList) {
|
||||
String positionCode = trimToNull(position.getPositionCode());
|
||||
if (positionCode != null) {
|
||||
positionMap.putIfAbsent(positionCode, position);
|
||||
}
|
||||
}
|
||||
return positionMap;
|
||||
return new ImportNameLookup<>(uniqueMap, duplicateNames);
|
||||
}
|
||||
|
||||
private List<SysAccountImportRow> parseImportRows(MultipartFile file) {
|
||||
@@ -462,12 +524,21 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
}
|
||||
}
|
||||
|
||||
private void appendImportError(SysAccountImportResultVo result, SysAccountImportRow row, String reason) {
|
||||
private void appendImportError(SysAccountImportResultVo result, SysAccountImportRow row, Exception exception) {
|
||||
SysAccountImportErrorRowVo errorRow = new SysAccountImportErrorRowVo();
|
||||
errorRow.setRowNumber(row.getRowNumber());
|
||||
errorRow.setDeptCode(row.getDeptCode());
|
||||
errorRow.setDeptName(row.getDeptName());
|
||||
errorRow.setLoginName(row.getLoginName());
|
||||
errorRow.setReason(reason);
|
||||
errorRow.setNickname(row.getNickname());
|
||||
errorRow.setRoleNames(row.getRoleNames());
|
||||
errorRow.setPositionNames(row.getPositionNames());
|
||||
if (exception instanceof ImportRowValidationException validationException) {
|
||||
errorRow.setDetails(validationException.getDetails());
|
||||
} else {
|
||||
List<SysAccountImportErrorDetailVo> details = new ArrayList<>(1);
|
||||
addImportDetail(details, IMPORT_FIELD_SYSTEM, null, extractImportErrorMessage(exception));
|
||||
errorRow.setDetails(details);
|
||||
}
|
||||
result.getErrorRows().add(errorRow);
|
||||
}
|
||||
|
||||
@@ -537,18 +608,69 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
|
||||
private List<List<String>> buildImportHeadList() {
|
||||
List<List<String>> headList = new ArrayList<>(9);
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_DEPT_CODE));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_DEPT_NAME));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_LOGIN_NAME));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_NICKNAME));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_MOBILE));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_EMAIL));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_STATUS));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_ROLE_KEYS));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_POSITION_CODES));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_ROLE_NAMES));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_POSITION_NAMES));
|
||||
headList.add(Collections.singletonList(IMPORT_HEAD_REMARK));
|
||||
return headList;
|
||||
}
|
||||
|
||||
private List<List<String>> buildImportGuideHeadList() {
|
||||
List<List<String>> headList = new ArrayList<>(2);
|
||||
headList.add(Collections.singletonList(IMPORT_GUIDE_HEAD_ITEM));
|
||||
headList.add(Collections.singletonList(IMPORT_GUIDE_HEAD_CONTENT));
|
||||
return headList;
|
||||
}
|
||||
|
||||
private List<List<String>> buildImportGuideRows() {
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
rows.add(List.of("填写规则", "请按名称填写部门、角色、岗位。"));
|
||||
rows.add(List.of("必填字段", "部门名称*、登录账号*、昵称*"));
|
||||
rows.add(List.of("可选字段", "手机号、邮箱、状态、角色名称、岗位名称、备注"));
|
||||
rows.add(List.of("状态可选值", "可留空,或填写 1/0/已启用/启用/未启用/停用/禁用"));
|
||||
rows.add(List.of("多值分隔", "角色名称、岗位名称支持使用英文逗号,或中文逗号,分隔多个名称"));
|
||||
rows.add(List.of("导入后初始密码", "导入成功的账号默认密码为 123456,首次登录需要修改密码"));
|
||||
rows.add(List.of("示例行", "市场部 | zhangsan | 张三 | 13800138000 | zhangsan@example.com | 已启用 | 普通员工,审批专员 | 产品经理 | 示例导入"));
|
||||
return rows;
|
||||
}
|
||||
|
||||
private void addImportDetail(List<SysAccountImportErrorDetailVo> details,
|
||||
String fieldName,
|
||||
String fieldValue,
|
||||
String reason) {
|
||||
SysAccountImportErrorDetailVo detail = new SysAccountImportErrorDetailVo();
|
||||
detail.setFieldName(fieldName);
|
||||
detail.setFieldValue(trimToNull(fieldValue));
|
||||
detail.setReason(reason);
|
||||
details.add(detail);
|
||||
}
|
||||
|
||||
private <T> T resolveSingleResource(
|
||||
String rawName,
|
||||
ImportNameLookup<T> lookup,
|
||||
String fieldName,
|
||||
List<SysAccountImportErrorDetailVo> details) {
|
||||
String name = trimToNull(rawName);
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
if (lookup.getDuplicateNames().contains(name)) {
|
||||
addImportDetail(details, fieldName, rawName, fieldName + IMPORT_ERROR_DUPLICATED_RESOURCE);
|
||||
return null;
|
||||
}
|
||||
T resource = lookup.getUniqueMap().get(name);
|
||||
if (resource == null) {
|
||||
addImportDetail(details, fieldName, rawName, fieldName + "不存在");
|
||||
return null;
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (!StringUtil.hasText(value)) {
|
||||
return null;
|
||||
@@ -558,14 +680,14 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
|
||||
private static class SysAccountImportRow {
|
||||
private Integer rowNumber;
|
||||
private String deptCode;
|
||||
private String deptName;
|
||||
private String loginName;
|
||||
private String nickname;
|
||||
private String mobile;
|
||||
private String email;
|
||||
private String status;
|
||||
private String roleKeys;
|
||||
private String positionCodes;
|
||||
private String roleNames;
|
||||
private String positionNames;
|
||||
private String remark;
|
||||
|
||||
public Integer getRowNumber() {
|
||||
@@ -576,12 +698,12 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
this.rowNumber = rowNumber;
|
||||
}
|
||||
|
||||
public String getDeptCode() {
|
||||
return deptCode;
|
||||
public String getDeptName() {
|
||||
return deptName;
|
||||
}
|
||||
|
||||
public void setDeptCode(String deptCode) {
|
||||
this.deptCode = deptCode;
|
||||
public void setDeptName(String deptName) {
|
||||
this.deptName = deptName;
|
||||
}
|
||||
|
||||
public String getLoginName() {
|
||||
@@ -624,20 +746,20 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getRoleKeys() {
|
||||
return roleKeys;
|
||||
public String getRoleNames() {
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
public void setRoleKeys(String roleKeys) {
|
||||
this.roleKeys = roleKeys;
|
||||
public void setRoleNames(String roleNames) {
|
||||
this.roleNames = roleNames;
|
||||
}
|
||||
|
||||
public String getPositionCodes() {
|
||||
return positionCodes;
|
||||
public String getPositionNames() {
|
||||
return positionNames;
|
||||
}
|
||||
|
||||
public void setPositionCodes(String positionCodes) {
|
||||
this.positionCodes = positionCodes;
|
||||
public void setPositionNames(String positionNames) {
|
||||
this.positionNames = positionNames;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
@@ -658,23 +780,23 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
@Override
|
||||
public void invoke(LinkedHashMap<Integer, Object> data, AnalysisContext context) {
|
||||
sheetRowNo++;
|
||||
String deptCode = getCellValue(data, IMPORT_HEAD_DEPT_CODE);
|
||||
String deptName = getCellValue(data, IMPORT_HEAD_DEPT_NAME);
|
||||
String loginName = getCellValue(data, IMPORT_HEAD_LOGIN_NAME);
|
||||
String nickname = getCellValue(data, IMPORT_HEAD_NICKNAME);
|
||||
String mobile = getCellValue(data, IMPORT_HEAD_MOBILE);
|
||||
String email = getCellValue(data, IMPORT_HEAD_EMAIL);
|
||||
String status = getCellValue(data, IMPORT_HEAD_STATUS);
|
||||
String roleKeys = getCellValue(data, IMPORT_HEAD_ROLE_KEYS);
|
||||
String positionCodes = getCellValue(data, IMPORT_HEAD_POSITION_CODES);
|
||||
String roleNames = getCellValue(data, IMPORT_HEAD_ROLE_NAMES);
|
||||
String positionNames = getCellValue(data, IMPORT_HEAD_POSITION_NAMES);
|
||||
String remark = getCellValue(data, IMPORT_HEAD_REMARK);
|
||||
if (!StringUtil.hasText(deptCode)
|
||||
if (!StringUtil.hasText(deptName)
|
||||
&& !StringUtil.hasText(loginName)
|
||||
&& !StringUtil.hasText(nickname)
|
||||
&& !StringUtil.hasText(mobile)
|
||||
&& !StringUtil.hasText(email)
|
||||
&& !StringUtil.hasText(status)
|
||||
&& !StringUtil.hasText(roleKeys)
|
||||
&& !StringUtil.hasText(positionCodes)
|
||||
&& !StringUtil.hasText(roleNames)
|
||||
&& !StringUtil.hasText(positionNames)
|
||||
&& !StringUtil.hasText(remark)) {
|
||||
return;
|
||||
}
|
||||
@@ -683,14 +805,14 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
}
|
||||
SysAccountImportRow row = new SysAccountImportRow();
|
||||
row.setRowNumber(sheetRowNo + 1);
|
||||
row.setDeptCode(deptCode);
|
||||
row.setDeptName(deptName);
|
||||
row.setLoginName(loginName);
|
||||
row.setNickname(nickname);
|
||||
row.setMobile(mobile);
|
||||
row.setEmail(email);
|
||||
row.setStatus(status);
|
||||
row.setRoleKeys(roleKeys);
|
||||
row.setPositionCodes(positionCodes);
|
||||
row.setRoleNames(roleNames);
|
||||
row.setPositionNames(positionNames);
|
||||
row.setRemark(remark);
|
||||
rows.add(row);
|
||||
}
|
||||
@@ -705,15 +827,9 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
}
|
||||
}
|
||||
List<String> requiredHeads = List.of(
|
||||
IMPORT_HEAD_DEPT_CODE,
|
||||
IMPORT_HEAD_DEPT_NAME,
|
||||
IMPORT_HEAD_LOGIN_NAME,
|
||||
IMPORT_HEAD_NICKNAME,
|
||||
IMPORT_HEAD_MOBILE,
|
||||
IMPORT_HEAD_EMAIL,
|
||||
IMPORT_HEAD_STATUS,
|
||||
IMPORT_HEAD_ROLE_KEYS,
|
||||
IMPORT_HEAD_POSITION_CODES,
|
||||
IMPORT_HEAD_REMARK
|
||||
IMPORT_HEAD_NICKNAME
|
||||
);
|
||||
for (String requiredHead : requiredHeads) {
|
||||
if (!headIndex.containsKey(requiredHead)) {
|
||||
@@ -740,4 +856,35 @@ public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAcco
|
||||
return value == null ? null : String.valueOf(value).trim();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImportNameLookup<T> {
|
||||
private final Map<String, T> uniqueMap;
|
||||
private final Set<String> duplicateNames;
|
||||
|
||||
private ImportNameLookup(Map<String, T> uniqueMap, Set<String> duplicateNames) {
|
||||
this.uniqueMap = uniqueMap;
|
||||
this.duplicateNames = duplicateNames;
|
||||
}
|
||||
|
||||
public Map<String, T> getUniqueMap() {
|
||||
return uniqueMap;
|
||||
}
|
||||
|
||||
public Set<String> getDuplicateNames() {
|
||||
return duplicateNames;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImportRowValidationException extends RuntimeException {
|
||||
private final List<SysAccountImportErrorDetailVo> details;
|
||||
|
||||
private ImportRowValidationException(List<SysAccountImportErrorDetailVo> details) {
|
||||
super("导入数据校验失败");
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public List<SysAccountImportErrorDetailVo> getDetails() {
|
||||
return details;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user