bug fix
parent
88172e0868
commit
1e48fb8cf3
|
|
@ -3,10 +3,8 @@ package org.qinan.safetyeval.app.support;
|
||||||
import cn.hutool.core.lang.Pair;
|
import cn.hutool.core.lang.Pair;
|
||||||
import cn.hutool.core.util.IdcardUtil;
|
import cn.hutool.core.util.IdcardUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.validation.ValidationUtil;
|
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.jjb.saas.framework.auth.utils.AuthContext;
|
import com.jjb.saas.framework.auth.utils.AuthContext;
|
||||||
import com.jjb.saas.framework.core.utils.ValidatorUtils;
|
|
||||||
import com.jjb.saas.system.client.user.request.RoleDeptAddCmd;
|
import com.jjb.saas.system.client.user.request.RoleDeptAddCmd;
|
||||||
import com.jjb.saas.system.client.user.request.UserAddCmd;
|
import com.jjb.saas.system.client.user.request.UserAddCmd;
|
||||||
import com.zcloud.gbscommon.utils.MD5;
|
import com.zcloud.gbscommon.utils.MD5;
|
||||||
|
|
@ -34,10 +32,10 @@ import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.ValidationUtils;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
|
|
@ -61,12 +59,12 @@ public class OrgPersonnelExcelImporter {
|
||||||
private static final DateTimeFormatter SLASH_DATE_FORMATTER =
|
private static final DateTimeFormatter SLASH_DATE_FORMATTER =
|
||||||
new DateTimeFormatterBuilder().appendPattern("yyyy/M/d").toFormatter();
|
new DateTimeFormatterBuilder().appendPattern("yyyy/M/d").toFormatter();
|
||||||
|
|
||||||
/** 手机号格式:11位纯数字、1开头 */
|
|
||||||
private static final String MOBILE_PHONE_REGEX = "^1\\d{10}$";
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OrgDepartmentGateway orgDepartmentGateway;
|
private OrgDepartmentGateway orgDepartmentGateway;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private javax.validation.Validator validator;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OrgPositionGateway orgPositionGateway;
|
private OrgPositionGateway orgPositionGateway;
|
||||||
|
|
||||||
|
|
@ -227,12 +225,12 @@ public class OrgPersonnelExcelImporter {
|
||||||
}
|
}
|
||||||
row.setRowIndex(i + 2);
|
row.setRowIndex(i + 2);
|
||||||
|
|
||||||
// 账号(手机号)校验:必填、格式、文件内去重
|
// 字段校验:JSR-303 注解(必填、长度、手机号格式)+ 身份证号 + 日期格式
|
||||||
if (!validateAccount(row, seenAccounts, errors)) {
|
if (!validateRowFields(row, errors)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 字段级校验:必填、长度、格式
|
// 账号文件内去重
|
||||||
if (!validateRowFields(row, errors)) {
|
if (!checkAccountDuplicate(row, seenAccounts, errors)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rows.add(row);
|
rows.add(row);
|
||||||
|
|
@ -241,48 +239,13 @@ public class OrgPersonnelExcelImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验账号(手机号):必填、格式、文件内去重。
|
* 字段级校验:
|
||||||
*
|
* <ol>
|
||||||
* @param row 当前行
|
* <li>JSR-303 注解校验({@link OrgPersonnelImportRow} 字段上的 @NotBlank/@Size/@Pattern),
|
||||||
* @param seenAccounts 已出现的账号集合(用于文件内去重)
|
* 覆盖必填、长度、手机号格式;</li>
|
||||||
* @param errors 异常收集列表
|
* <li>身份证号:使用 {@link IdcardUtil} 校验校验位与出生日期合法性;</li>
|
||||||
* @return true 通过,false 失败(已记录异常)
|
* <li>日期字段:兼容 yyyy-MM-dd / yyyy/M/d 格式校验。</li>
|
||||||
*/
|
* </ol>
|
||||||
private boolean validateAccount(OrgPersonnelImportRow row, Set<String> seenAccounts,
|
|
||||||
List<OrgPersonnelImportErrorCO> errors) {
|
|
||||||
Integer rowIndex = row.getRowIndex();
|
|
||||||
String account = trim(row.getAccount());
|
|
||||||
String userName = trim(row.getUserName());
|
|
||||||
|
|
||||||
// 账号必填(DB: account NOT NULL)
|
|
||||||
if (!StringUtils.hasText(account)) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "账号不能为空"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 手机号格式校验:11位纯数字、1开头
|
|
||||||
if (!account.matches(MOBILE_PHONE_REGEX)) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
|
||||||
"手机号格式不正确,应为11位数字且以1开头"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 文件内去重
|
|
||||||
if (!seenAccounts.add(account)) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
|
||||||
"账号在文件内重复,已剔除该行"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验行数据的各字段:必填、长度、格式/类型。
|
|
||||||
* <p>
|
|
||||||
* 覆盖直接存入数据库的字段,校验规则依据 {@code org_personnel} 表结构:
|
|
||||||
* <ul>
|
|
||||||
* <li>必填:NOT NULL 字段(user_name、account);</li>
|
|
||||||
* <li>长度:varchar 字段不超过定义长度;</li>
|
|
||||||
* <li>类型/格式:身份证号、日期等。</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
*
|
||||||
* @param row 当前行
|
* @param row 当前行
|
||||||
* @param errors 异常收集列表
|
* @param errors 异常收集列表
|
||||||
|
|
@ -293,17 +256,21 @@ public class OrgPersonnelExcelImporter {
|
||||||
String account = trim(row.getAccount());
|
String account = trim(row.getAccount());
|
||||||
String userName = trim(row.getUserName());
|
String userName = trim(row.getUserName());
|
||||||
|
|
||||||
// 姓名:必填(DB: user_name varchar(50) NOT NULL)
|
// 1. JSR-303 注解校验:每字段仅取第一条违规,避免 @NotBlank+@Pattern 重复报错
|
||||||
if (!StringUtils.hasText(userName)) {
|
Set<ConstraintViolation<OrgPersonnelImportRow>> violations = validator.validate(row);
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "姓名不能为空"));
|
if (!violations.isEmpty()) {
|
||||||
return false;
|
violations.stream()
|
||||||
}
|
.collect(Collectors.toMap(
|
||||||
if (userName.length() > 50) {
|
v -> v.getPropertyPath().toString(),
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "姓名长度不能超过50个字符"));
|
v -> v,
|
||||||
|
(a, b) -> a))
|
||||||
|
.values()
|
||||||
|
.forEach(v -> errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
||||||
|
v.getMessage())));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 身份证号:可选,格式校验(DB: id_card_no varchar(18))
|
// 2. 身份证号格式校验(DB: id_card_no varchar(18))
|
||||||
String idCardNo = trim(row.getIdCardNo());
|
String idCardNo = trim(row.getIdCardNo());
|
||||||
if (StringUtils.hasText(idCardNo) && !IdcardUtil.isValidCard(idCardNo)) {
|
if (StringUtils.hasText(idCardNo) && !IdcardUtil.isValidCard(idCardNo)) {
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
||||||
|
|
@ -311,22 +278,13 @@ public class OrgPersonnelExcelImporter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 性别名称:最大10(DB: gender_name varchar(10))
|
// 3. 日期格式校验(DB: birth_date / join_work_date date)
|
||||||
String gender = trim(row.getGender());
|
|
||||||
if (StringUtils.hasText(gender) && gender.length() > 10) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "性别长度不能超过10个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 出生日期:日期格式校验(DB: birth_date date)
|
|
||||||
String birthDate = trim(row.getBirthDate());
|
String birthDate = trim(row.getBirthDate());
|
||||||
if (StringUtils.hasText(birthDate) && parseDate(birthDate) == null) {
|
if (StringUtils.hasText(birthDate) && parseDate(birthDate) == null) {
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
||||||
"出生日期格式不正确,应为yyyy-MM-dd或yyyy/M/d"));
|
"出生日期格式不正确,应为yyyy-MM-dd或yyyy/M/d"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 参加工作时间:日期格式校验(DB: join_work_date date)
|
|
||||||
String joinWorkDate = trim(row.getJoinWorkDate());
|
String joinWorkDate = trim(row.getJoinWorkDate());
|
||||||
if (StringUtils.hasText(joinWorkDate) && parseDate(joinWorkDate) == null) {
|
if (StringUtils.hasText(joinWorkDate) && parseDate(joinWorkDate) == null) {
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
||||||
|
|
@ -334,57 +292,25 @@ public class OrgPersonnelExcelImporter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 学历名称:最大50(DB: education_name varchar(50))
|
return true;
|
||||||
String education = trim(row.getEducation());
|
}
|
||||||
if (StringUtils.hasText(education) && education.length() > 50) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "学历长度不能超过50个字符"));
|
/**
|
||||||
|
* 账号文件内去重(空账号不参与去重)。
|
||||||
|
*
|
||||||
|
* @param row 当前行
|
||||||
|
* @param seenAccounts 已出现的账号集合
|
||||||
|
* @param errors 异常收集列表
|
||||||
|
* @return true 通过,false 重复(已记录异常)
|
||||||
|
*/
|
||||||
|
private boolean checkAccountDuplicate(OrgPersonnelImportRow row, Set<String> seenAccounts,
|
||||||
|
List<OrgPersonnelImportErrorCO> errors) {
|
||||||
|
String account = trim(row.getAccount());
|
||||||
|
if (StringUtils.hasText(account) && !seenAccounts.add(account)) {
|
||||||
|
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), account, trim(row.getUserName()),
|
||||||
|
"账号在文件内重复,已剔除该行"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 毕业院校:最大200(DB: graduate_school varchar(200))
|
|
||||||
String graduateSchool = trim(row.getGraduateSchool());
|
|
||||||
if (StringUtils.hasText(graduateSchool) && graduateSchool.length() > 200) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "毕业院校长度不能超过200个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 所学专业:最大100(DB: major varchar(100))
|
|
||||||
String major = trim(row.getMajor());
|
|
||||||
if (StringUtils.hasText(major) && major.length() > 100) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "所学专业长度不能超过100个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 现住地址:最大500(DB: current_address varchar(500))
|
|
||||||
String currentAddress = trim(row.getCurrentAddress());
|
|
||||||
if (StringUtils.hasText(currentAddress) && currentAddress.length() > 500) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "现住地址长度不能超过500个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 办公地址:最大500(DB: office_address varchar(500))
|
|
||||||
String officeAddress = trim(row.getOfficeAddress());
|
|
||||||
if (StringUtils.hasText(officeAddress) && officeAddress.length() > 500) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName, "办公地址长度不能超过500个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 出版学术专著等:最大1000(DB: publications varchar(1000))
|
|
||||||
String publications = trim(row.getPublications());
|
|
||||||
if (StringUtils.hasText(publications) && publications.length() > 1000) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
|
||||||
"出版学术专著等信息长度不能超过1000个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自我申报专业能力:最大1000(DB: ability_declaration varchar(1000))
|
|
||||||
String abilityDeclaration = trim(row.getAbilityDeclaration());
|
|
||||||
if (StringUtils.hasText(abilityDeclaration) && abilityDeclaration.length() > 1000) {
|
|
||||||
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
|
|
||||||
"自我申报的专业能力信息长度不能超过1000个字符"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 人员导入 Excel 行模型(按列下标映射,避免表头存在重名列)。
|
* 人员导入 Excel 行模型(按列下标映射,避免表头存在重名列)。
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -36,27 +40,37 @@ public class OrgPersonnelImportRow {
|
||||||
private String positionName;
|
private String positionName;
|
||||||
|
|
||||||
@ExcelProperty(value = "账号", index = 4)
|
@ExcelProperty(value = "账号", index = 4)
|
||||||
|
@NotBlank(message = "账号不能为空")
|
||||||
|
@Pattern(regexp = "^1\\d{10}$", message = "手机号格式不正确,应为11位数字且以1开头")
|
||||||
|
@Size(max = 50, message = "账号长度不能超过50个字符")
|
||||||
private String account;
|
private String account;
|
||||||
|
|
||||||
@ExcelProperty(value = "姓名", index = 5)
|
@ExcelProperty(value = "姓名", index = 5)
|
||||||
|
@NotBlank(message = "姓名不能为空")
|
||||||
|
@Size(max = 50, message = "姓名长度不能超过50个字符")
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
@ExcelProperty(value = "性别", index = 6)
|
@ExcelProperty(value = "性别", index = 6)
|
||||||
|
@Size(max = 10, message = "性别长度不能超过10个字符")
|
||||||
private String gender;
|
private String gender;
|
||||||
|
|
||||||
@ExcelProperty(value = "出生日期", index = 7)
|
@ExcelProperty(value = "出生日期", index = 7)
|
||||||
private String birthDate;
|
private String birthDate;
|
||||||
|
|
||||||
@ExcelProperty(value = "身份证号", index = 8)
|
@ExcelProperty(value = "身份证号", index = 8)
|
||||||
|
@Size(max = 18, message = "身份证号长度不能超过18个字符")
|
||||||
private String idCardNo;
|
private String idCardNo;
|
||||||
|
|
||||||
@ExcelProperty(value = "学历", index = 9)
|
@ExcelProperty(value = "学历", index = 9)
|
||||||
|
@Size(max = 50, message = "学历长度不能超过50个字符")
|
||||||
private String education;
|
private String education;
|
||||||
|
|
||||||
@ExcelProperty(value = "毕业院校", index = 10)
|
@ExcelProperty(value = "毕业院校", index = 10)
|
||||||
|
@Size(max = 200, message = "毕业院校长度不能超过200个字符")
|
||||||
private String graduateSchool;
|
private String graduateSchool;
|
||||||
|
|
||||||
@ExcelProperty(value = "所学专业", index = 11)
|
@ExcelProperty(value = "所学专业", index = 11)
|
||||||
|
@Size(max = 100, message = "所学专业长度不能超过100个字符")
|
||||||
private String major;
|
private String major;
|
||||||
|
|
||||||
@ExcelProperty(value = "职称", index = 12)
|
@ExcelProperty(value = "职称", index = 12)
|
||||||
|
|
@ -69,18 +83,22 @@ public class OrgPersonnelImportRow {
|
||||||
private String registerEngineer;
|
private String registerEngineer;
|
||||||
|
|
||||||
@ExcelProperty(value = "现住地址", index = 15)
|
@ExcelProperty(value = "现住地址", index = 15)
|
||||||
|
@Size(max = 500, message = "现住地址长度不能超过500个字符")
|
||||||
private String currentAddress;
|
private String currentAddress;
|
||||||
|
|
||||||
@ExcelProperty(value = "办公地址", index = 16)
|
@ExcelProperty(value = "办公地址", index = 16)
|
||||||
|
@Size(max = 500, message = "办公地址长度不能超过500个字符")
|
||||||
private String officeAddress;
|
private String officeAddress;
|
||||||
|
|
||||||
@ExcelProperty(value = "主要学习工作经历", index = 17)
|
@ExcelProperty(value = "主要学习工作经历", index = 17)
|
||||||
private String workExperience;
|
private String workExperience;
|
||||||
|
|
||||||
@ExcelProperty(value = "出版学术专著、专利、获奖、发表学术论文等", index = 18)
|
@ExcelProperty(value = "出版学术专著、专利、获奖、发表学术论文等", index = 18)
|
||||||
|
@Size(max = 1000, message = "出版学术专著等信息长度不能超过1000个字符")
|
||||||
private String publications;
|
private String publications;
|
||||||
|
|
||||||
@ExcelProperty(value = "自我申报的专业能力及认定方式", index = 19)
|
@ExcelProperty(value = "自我申报的专业能力及认定方式", index = 19)
|
||||||
|
@Size(max = 1000, message = "自我申报的专业能力信息长度不能超过1000个字符")
|
||||||
private String abilityDeclaration;
|
private String abilityDeclaration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue