dev-tmp1
luotaiqian 2026-08-18 19:55:16 +08:00
parent a7b7764283
commit 3479407873
2 changed files with 441 additions and 236 deletions

View File

@ -1,6 +1,7 @@
package org.qinan.safetyeval.app.support;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
@ -11,11 +12,10 @@ import com.zcloud.gbscommon.utils.MD5;
import com.zcloud.gbscommon.utils.Sm2Util;
import org.qinan.safetyeval.client.co.OrgPersonnelImportErrorCO;
import org.qinan.safetyeval.client.co.OrgPersonnelImportResultCO;
import org.qinan.safetyeval.domain.constant.QualificationLevelEnum;
import org.qinan.safetyeval.domain.entity.OrgDepartmentEntity;
import org.qinan.safetyeval.domain.entity.OrgPersonnelCertEntity;
import org.qinan.safetyeval.domain.entity.OrgPersonnelEntity;
import org.qinan.safetyeval.domain.entity.OrgPositionEntity;
import org.qinan.safetyeval.domain.entity.TitleCodeCo;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.gateway.OrgDepartmentGateway;
@ -38,14 +38,15 @@ import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.*;
import java.util.stream.Collectors;
/**
* EasyExcel
* <p>
* Excel -> /ID -> ->
* sheetsheet1sheet2
*
* /ID -> ->
* </p>
*
* @author safety-eval
@ -55,10 +56,6 @@ public class OrgPersonnelExcelImporter {
private static final Logger LOGGER = LoggerFactory.getLogger(OrgPersonnelExcelImporter.class);
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter SLASH_DATE_FORMATTER =
new DateTimeFormatterBuilder().appendPattern("yyyy/M/d").toFormatter();
@Resource
private OrgDepartmentGateway orgDepartmentGateway;
@ -80,6 +77,8 @@ public class OrgPersonnelExcelImporter {
@Resource
private SystemRemote systemRemote;
public static final String YES = "是";
@Value("${def.password:a123456}")
private String defPassword;
@Value("${def.publicKey:0402df2195296d4062ac85ad766994d73e871b887e18efb9a9a06b4cebc72372869b7da6c347c129dee2b46a0f279ff066b01c76208c2a052af75977c722a2ccee}")
@ -137,29 +136,32 @@ public class OrgPersonnelExcelImporter {
// 5. 逐行转换、落库
int successCount = 0;
for (OrgPersonnelImportRow row : rows) {
String account = trim(row.getAccount());
String account = trim(row.getBasic().getAccount());
// 账号在库中已存在 -> 跳过该行,记录异常
if (existingAccounts.contains(account)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), account, trim(row.getUserName()),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), account, trim(row.getBasic().getUserName()),
"账号已存在,跳过该行"));
continue;
}
try {
OrgPersonnelEntity entity = buildEntity(row, orgId, errors);
if (entity == null) {
OrgPersonnelEntity personnelEntity = buildOrgPersonnel(row, orgId, errors);
if (personnelEntity == null) {
// 行内枚举/数据校验未通过,已记录异常
continue;
}
Long userId = addUserIfAbsentAndAppendRole(entity);
entity.setId(userId);
orgPersonnelDomainService.add(entity);
List<OrgPersonnelCertEntity> personnelCertEntities = buildOrgPersonnelCert(row, orgId, errors, personnelEntity);
Long userId = addUserIfAbsentAndAppendRole(personnelEntity);
personnelEntity.setId(userId);
orgPersonnelDomainService.add(personnelEntity);
successCount++;
} catch (Exception ex) {
LOGGER.warn("导入人员落库失败, rowIndex={}, account={}", row.getRowIndex(), account, ex);
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), account, trim(row.getUserName()),
LOGGER.warn("导入人员落库失败, rowIndex={}, account={}", row.getBasic().getRowIndex(), account, ex);
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), account, trim(row.getBasic().getUserName()),
"保存失败:" + ex.getMessage()));
}
}
@ -169,6 +171,19 @@ public class OrgPersonnelExcelImporter {
return result;
}
/**
*
* @param row row
* @param orgId orgId
* @param errors errors
* @param personnelEntity personnelEntity
* @return OrgPersonnelCertEntity
*/
private List<OrgPersonnelCertEntity> buildOrgPersonnelCert(OrgPersonnelImportRow row
, Long orgId, List<OrgPersonnelImportErrorCO> errors, OrgPersonnelEntity personnelEntity) {
return null;
}
/**
* addUserIfAbsentAndAppendRole
*
@ -192,40 +207,56 @@ public class OrgPersonnelExcelImporter {
}
/**
* Excel12
* Excel sheet1 sheet2
*
* 12
* <p>
* errors
* </p>
*/
private List<OrgPersonnelImportRow> parseRows(MultipartFile file, List<OrgPersonnelImportErrorCO> errors) {
List<OrgPersonnelImportRow> raw;
List<OrgPersonnelImportRow.BasicRow> basicRaw;
List<OrgPersonnelImportRow.CapabilityRow> capabilityRaw;
try {
raw = EasyExcel.read(file.getInputStream())
.head(OrgPersonnelImportRow.class)
.sheet()
// sheet1专职人员基础信息
basicRaw = EasyExcel.read(file.getInputStream())
.head(OrgPersonnelImportRow.BasicRow.class)
.sheet(0)
.headRowNumber(1)
.doReadSync();
// sheet2专职人员能力信息
capabilityRaw = EasyExcel.read(file.getInputStream())
.head(OrgPersonnelImportRow.CapabilityRow.class)
.sheet(1)
.headRowNumber(1)
.doReadSync();
} catch (Exception ex) {
LOGGER.error("解析人员导入Excel失败", ex);
throw new BizException(ErrorCode.ORG_PERSONNEL_IMPORT_PARSE_ERROR);
}
if (raw == null || raw.isEmpty()) {
if (basicRaw == null || basicRaw.isEmpty()) {
return new ArrayList<>();
}
List<OrgPersonnelImportRow> rows = new ArrayList<>(raw.size());
Map<String, OrgPersonnelImportRow.CapabilityRow> capabilityMap = indexCapabilityRows(capabilityRaw);
List<OrgPersonnelImportRow> rows = new ArrayList<>(basicRaw.size());
Set<String> seenAccounts = new HashSet<>();
for (int i = 0; i < raw.size(); i++) {
OrgPersonnelImportRow row = raw.get(i);
if (row == null) {
for (int i = 0; i < basicRaw.size(); i++) {
OrgPersonnelImportRow.BasicRow basic = basicRaw.get(i);
if (basic == null) {
continue;
}
// 空行:账号与姓名均为空
if (!StringUtils.hasText(trim(row.getAccount())) && !StringUtils.hasText(trim(row.getUserName()))) {
if (!StringUtils.hasText(trim(basic.getAccount())) && !StringUtils.hasText(trim(basic.getUserName()))) {
continue;
}
row.setRowIndex(i + 2);
OrgPersonnelImportRow row = new OrgPersonnelImportRow();
basic.setRowIndex(i + 2);
row.setBasic(basic);
row.setCapability(matchCapability(basic, capabilityMap));
// 字段校验JSR-303 注解(必填、长度、手机号格式)+ 身份证号 + 日期格式
// 字段校验JSR-303 注解(必填、长度、手机号格式)+ 身份证号
if (!validateRowFields(row, errors)) {
continue;
}
@ -238,13 +269,69 @@ public class OrgPersonnelExcelImporter {
return rows;
}
/**
* sheet2
*/
private Map<String, OrgPersonnelImportRow.CapabilityRow> indexCapabilityRows(
List<OrgPersonnelImportRow.CapabilityRow> capabilityRaw) {
Map<String, OrgPersonnelImportRow.CapabilityRow> map = new LinkedHashMap<>();
if (capabilityRaw == null || capabilityRaw.isEmpty()) {
return map;
}
for (int i = 0; i < capabilityRaw.size(); i++) {
OrgPersonnelImportRow.CapabilityRow capability = capabilityRaw.get(i);
if (capability == null) {
continue;
}
// 空行:姓名与身份证号均为空
if (!StringUtils.hasText(trim(capability.getIdCardNo()))
&& !StringUtils.hasText(trim(capability.getUserName()))) {
continue;
}
capability.setRowIndex(i + 2);
String key = capabilityKey(capability.getIdCardNo(), capability.getUserName());
if (map.putIfAbsent(key, capability) != null) {
LOGGER.warn("人员导入能力信息重复key={}sheet2行号={},仅保留首条", key, capability.getRowIndex());
}
}
return map;
}
/**
* null
*/
private OrgPersonnelImportRow.CapabilityRow matchCapability(OrgPersonnelImportRow.BasicRow basic,
Map<String, OrgPersonnelImportRow.CapabilityRow> capabilityMap) {
if (capabilityMap.isEmpty()) {
return null;
}
OrgPersonnelImportRow.CapabilityRow capability =
capabilityMap.get(capabilityKey(basic.getIdCardNo(), null));
if (capability != null) {
return capability;
}
return capabilityMap.get(capabilityKey(null, basic.getUserName()));
}
/**
* ID:使 NAME:
*/
private String capabilityKey(String idCardNo, String userName) {
String idCard = trim(idCardNo);
if (StringUtils.hasText(idCard)) {
return "ID:" + idCard;
}
return "NAME:" + trim(userName);
}
/**
*
* <ol>
* <li>JSR-303 {@link OrgPersonnelImportRow} @NotBlank/@Size/@Pattern
* <li>JSR-303 {@link OrgPersonnelImportRow.BasicRow} @NotBlank/@Size/@Pattern
* </li>
* <li>使 {@link IdcardUtil} </li>
* <li> yyyy-MM-dd / yyyy/M/d </li>
* <li>sheet2 {@link OrgPersonnelImportRow.CapabilityRow}JSR-303 +
* sheet2 </li>
* <li>使 {@link IdcardUtil} </li>
* </ol>
*
* @param row
@ -252,12 +339,12 @@ public class OrgPersonnelExcelImporter {
* @return true false
*/
private boolean validateRowFields(OrgPersonnelImportRow row, List<OrgPersonnelImportErrorCO> errors) {
Integer rowIndex = row.getRowIndex();
String account = trim(row.getAccount());
String userName = trim(row.getUserName());
Integer rowIndex = row.getBasic().getRowIndex();
String account = trim(row.getBasic().getAccount());
String userName = trim(row.getBasic().getUserName());
// 1. JSR-303 注解校验:每字段仅取第一条违规,避免 @NotBlank+@Pattern 重复报错
Set<ConstraintViolation<OrgPersonnelImportRow>> violations = validator.validate(row);
// 1. JSR-303 注解校验sheet1 基础信息):每字段仅取第一条违规,避免 @NotBlank+@Pattern 重复报错
Set<ConstraintViolation<OrgPersonnelImportRow.BasicRow>> violations = validator.validate(row.getBasic());
if (!violations.isEmpty()) {
violations.stream()
.collect(Collectors.toMap(
@ -270,22 +357,40 @@ public class OrgPersonnelExcelImporter {
return false;
}
// 2. 身份证号格式校验DB: id_card_no varchar(18)
String idCardNo = trim(row.getIdCardNo());
// 2. sheet2 能力信息校验(未匹配到能力信息时跳过),行号取 sheet2 行号,消息加前缀便于区分来源
OrgPersonnelImportRow.CapabilityRow capability = row.getCapability();
if (capability != null) {
Set<ConstraintViolation<OrgPersonnelImportRow.CapabilityRow>> capabilityViolations =
validator.validate(capability);
if (!capabilityViolations.isEmpty()) {
capabilityViolations.stream()
.collect(Collectors.toMap(
v -> v.getPropertyPath().toString(),
v -> v,
(a, b) -> a))
.values()
.forEach(v -> errors.add(new OrgPersonnelImportErrorCO(capability.getRowIndex(),
account, userName, "能力信息:" + v.getMessage())));
return false;
}
// sheet2 身份证号格式校验DB: id_card_no varchar(18)
String capabilityIdCardNo = trim(capability.getIdCardNo());
if (StringUtils.hasText(capabilityIdCardNo) && !IdcardUtil.isValidCard(capabilityIdCardNo)) {
errors.add(new OrgPersonnelImportErrorCO(capability.getRowIndex(), account, userName,
"能力信息身份证号格式不正确应为15或18位有效身份证号"));
return false;
}
}
// 3. 身份证号格式校验DB: id_card_no varchar(18)
String idCardNo = trim(row.getBasic().getIdCardNo());
if (StringUtils.hasText(idCardNo) && !IdcardUtil.isValidCard(idCardNo)) {
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
"身份证号格式不正确应为15或18位有效身份证号"));
return false;
}
// 3. 日期格式校验DB: join_work_date date出生日期由身份证号解析不做校验
String joinWorkDate = trim(row.getJoinWorkDate());
if (StringUtils.hasText(joinWorkDate) && parseDate(joinWorkDate) == null) {
errors.add(new OrgPersonnelImportErrorCO(rowIndex, account, userName,
"参加工作时间格式不正确应为yyyy-MM-dd或yyyy/M/d"));
return false;
}
return true;
}
@ -299,9 +404,9 @@ public class OrgPersonnelExcelImporter {
*/
private boolean checkAccountDuplicate(OrgPersonnelImportRow row, Set<String> seenAccounts,
List<OrgPersonnelImportErrorCO> errors) {
String account = trim(row.getAccount());
String account = trim(row.getBasic().getAccount());
if (StringUtils.hasText(account) && !seenAccounts.add(account)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), account, trim(row.getUserName()),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), account, trim(row.getBasic().getUserName()),
"账号在文件内重复,已剔除该行"));
return false;
}
@ -322,13 +427,14 @@ public class OrgPersonnelExcelImporter {
// 收集所有层级中不为空的部门名称
Set<String> deptNames = new LinkedHashSet<>();
for (OrgPersonnelImportRow row : rows) {
row.setDeptNameLevel3(StrUtil.trim(row.getDeptNameLevel3()));
row.setDeptNameLevel2(StrUtil.trim(row.getDeptNameLevel2()));
row.setDeptNameLevel1(StrUtil.trim(row.getDeptNameLevel1()));
OrgPersonnelImportRow.BasicRow basic = row.getBasic();
basic.setDeptNameLevel3(StrUtil.trim(basic.getDeptNameLevel3()));
basic.setDeptNameLevel2(StrUtil.trim(basic.getDeptNameLevel2()));
basic.setDeptNameLevel1(StrUtil.trim(basic.getDeptNameLevel1()));
collectDeptName(deptNames, row.getDeptNameLevel1());
collectDeptName(deptNames, row.getDeptNameLevel2());
collectDeptName(deptNames, row.getDeptNameLevel3());
collectDeptName(deptNames, basic.getDeptNameLevel1());
collectDeptName(deptNames, basic.getDeptNameLevel2());
collectDeptName(deptNames, basic.getDeptNameLevel3());
}
List<OrgDepartmentEntity> orgDepartmentList = orgDepartmentGateway.listByOrgIdAndNames(orgId, deptNames);
@ -360,7 +466,7 @@ public class OrgPersonnelExcelImporter {
*/
private Pair<Boolean, OrgDepartmentEntity> getL3(List<OrgPersonnelImportErrorCO> errors, OrgPersonnelImportRow row
, Map<String, List<OrgDepartmentEntity>> nameMapList, OrgDepartmentEntity l2Dept) {
String l3 = row.getDeptNameLevel3();
String l3 = row.getBasic().getDeptNameLevel3();
if (StrUtil.isBlank(l3)) {
return Pair.of(true, null);
}
@ -368,7 +474,7 @@ public class OrgPersonnelExcelImporter {
// l3 部门查询
List<OrgDepartmentEntity> departmentEntities = nameMapList.get(l3);
if (CollectionUtils.isEmpty(departmentEntities)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L3_NOT_EXIST.getMessage()));
row.setValidateFail(true);
return Pair.of(false, null);
@ -383,7 +489,7 @@ public class OrgPersonnelExcelImporter {
}
}
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
l2Dept.getDeptName() + "下"
+ ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L3_NOT_EXIST.getMessage()));
row.setValidateFail(true);
@ -401,7 +507,7 @@ public class OrgPersonnelExcelImporter {
*/
private Pair<Boolean, OrgDepartmentEntity> getL2(List<OrgPersonnelImportErrorCO> errors
, OrgPersonnelImportRow row, Map<String, List<OrgDepartmentEntity>> nameMapList, OrgDepartmentEntity l1Dept) {
String l2 = row.getDeptNameLevel2();
String l2 = row.getBasic().getDeptNameLevel2();
if (StrUtil.isBlank(l2)) {
return Pair.of(true, null);
}
@ -409,7 +515,7 @@ public class OrgPersonnelExcelImporter {
// l2 部门查询
List<OrgDepartmentEntity> departmentEntities = nameMapList.get(l2);
if (CollectionUtils.isEmpty(departmentEntities)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L2_NOT_EXIST.getMessage()));
row.setValidateFail(true);
return Pair.of(false, null);
@ -424,7 +530,7 @@ public class OrgPersonnelExcelImporter {
}
}
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
l1Dept.getDeptName() + "下" + ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L2_NOT_EXIST.getMessage()));
row.setValidateFail(true);
return Pair.of(false, null);
@ -439,9 +545,9 @@ public class OrgPersonnelExcelImporter {
*/
private static Pair<Boolean, OrgDepartmentEntity> getL1(List<OrgPersonnelImportErrorCO> errors, OrgPersonnelImportRow row
, Map<String, List<OrgDepartmentEntity>> nameMapList) {
String l1 = row.getDeptNameLevel1();
String l1 = row.getBasic().getDeptNameLevel1();
if (StrUtil.isBlank(l1)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L1_NOT_EXIST.getMessage()));
row.setValidateFail(true);
return Pair.of(false, null);
@ -450,7 +556,7 @@ public class OrgPersonnelExcelImporter {
// l1 部门查询
List<OrgDepartmentEntity> departmentEntities = nameMapList.get(l1);
if (CollectionUtils.isEmpty(departmentEntities)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L1_NOT_EXIST.getMessage()));
row.setValidateFail(true);
return Pair.of(false, null);
@ -458,7 +564,7 @@ public class OrgPersonnelExcelImporter {
// 一级部门重复
if (departmentEntities.size() > 1) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_DEPT_L1_REPEAT.getMessage()));
row.setValidateFail(true);
return Pair.of(false, null);
@ -484,7 +590,7 @@ public class OrgPersonnelExcelImporter {
if (row.getDeptId() != null) {
deptIds.add(row.getDeptId());
}
String posName = trim(row.getPositionName());
String posName = trim(row.getBasic().getPositionName());
if (StringUtils.hasText(posName)) {
positionNames.add(posName);
}
@ -501,9 +607,9 @@ public class OrgPersonnelExcelImporter {
// 逐行校验:岗位名称为空或部门下岗位不存在 -> 记录异常并标记校验通过则回填岗位ID
for (OrgPersonnelImportRow row : rows) {
String posName = trim(row.getPositionName());
String posName = trim(row.getBasic().getPositionName());
if (!StringUtils.hasText(posName)) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_POSITION_NOT_FOUND.getMessage()
+ "(部门:" + row.getDeptDisplayName() + ""));
row.setValidateFail(true);
@ -512,7 +618,7 @@ public class OrgPersonnelExcelImporter {
String key = row.getDeptId() + "|" + posName;
OrgPositionEntity pos = positionMap.get(key);
if (pos == null) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_POSITION_NOT_FOUND.getMessage()
+ "" + posName + "(部门:" + row.getDeptDisplayName() + ""));
row.setValidateFail(true);
@ -529,7 +635,7 @@ public class OrgPersonnelExcelImporter {
*/
private void validateAndFillMajor(List<OrgPersonnelImportRow> rows, List<OrgPersonnelImportErrorCO> errors) {
Set<String> majorNames = rows.stream()
.map(r -> trim(r.getMajor()))
.map(r -> r.getCapability() == null ? null : trim(r.getCapability().getMajor()))
.filter(StringUtils::hasText)
.collect(Collectors.toCollection(LinkedHashSet::new));
@ -544,13 +650,13 @@ public class OrgPersonnelExcelImporter {
// 逐行校验专业名称为空的行跳过对照表中不存在的行记录异常并标记校验通过则回填专业ID
for (OrgPersonnelImportRow row : rows) {
String name = trim(row.getMajor());
String name = row.getCapability() == null ? null : trim(row.getCapability().getMajor());
if (!StringUtils.hasText(name)) {
continue;
}
BasicDisciplineMajorE major = majorMap.get(name);
if (major == null) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), row.getAccount(), row.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(row.getBasic().getRowIndex(), row.getBasic().getAccount(), row.getBasic().getUserName(),
ErrorCode.ORG_PERSONNEL_IMPORT_MAJOR_NOT_FOUND.getMessage() + "" + name));
row.setValidateFail(true);
continue;
@ -561,7 +667,7 @@ public class OrgPersonnelExcelImporter {
private Set<String> batchQueryExistingAccounts(List<OrgPersonnelImportRow> rows, Long orgId) {
Set<String> accounts = rows.stream()
.map(r -> trim(r.getAccount()))
.map(r -> trim(r.getBasic().getAccount()))
.filter(StringUtils::hasText)
.collect(Collectors.toSet());
if (accounts.isEmpty()) {
@ -575,16 +681,20 @@ public class OrgPersonnelExcelImporter {
/**
* null
* <p> sheet1/// sheet2 </p>
*/
private OrgPersonnelEntity buildEntity(OrgPersonnelImportRow row, Long orgId, List<OrgPersonnelImportErrorCO> errors) {
private OrgPersonnelEntity buildOrgPersonnel(OrgPersonnelImportRow row, Long orgId, List<OrgPersonnelImportErrorCO> errors) {
OrgPersonnelImportRow.BasicRow basic = row.getBasic();
OrgPersonnelImportRow.CapabilityRow capability = row.getCapability();
OrgPersonnelEntity entity = new OrgPersonnelEntity();
entity.setId(IdUtil.getSnowflakeNextId());
entity.setOrgId(orgId);
entity.setDeptId(row.getDeptId());
entity.setPostId(row.getPostId());
entity.setBasicDisciplineMajorId(row.getBasicDisciplineMajorId());
entity.setUserName(trim(row.getUserName()));
entity.setAccount(trim(row.getAccount()));
String idCardNo = trim(row.getIdCardNo());
entity.setUserName(trim(basic.getUserName()));
entity.setAccount(trim(basic.getAccount()));
String idCardNo = trim(basic.getIdCardNo());
entity.setIdCardNo(idCardNo);
// 出生日期不再从Excel导入直接通过身份证号Hutool解析
if (StringUtils.hasText(idCardNo)) {
@ -593,21 +703,31 @@ public class OrgPersonnelExcelImporter {
entity.setBirthDate(LocalDate.parse(birth, DateTimeFormatter.BASIC_ISO_DATE));
}
}
entity.setJoinWorkDate(parseDate(trim(row.getJoinWorkDate())));
entity.setGraduateSchool(trim(row.getGraduateSchool()));
entity.setMajor(trim(row.getMajor()));
entity.setCurrentAddress(trim(row.getCurrentAddress()));
entity.setOfficeAddress(trim(row.getOfficeAddress()));
entity.setWorkExperience(trim(row.getWorkExperience()));
entity.setPublications(trim(row.getPublications()));
entity.setAbilityDeclaration(trim(row.getAbilityDeclaration()));
entity.setCurrentAddress(trim(basic.getCurrentAddress()));
entity.setOfficeAddress(trim(basic.getOfficeAddress()));
entity.setWorkExperience(trim(basic.getWorkExperience()));
entity.setPublications(trim(basic.getPublications()));
entity.setAbilityDeclaration(trim(basic.getAbilityDeclaration()));
// sheet2 能力信息(未匹配到能力信息的行为空)
if (capability != null) {
entity.setGraduateSchool(trim(capability.getGraduateSchool()));
entity.setMajor(trim(capability.getMajor()));
// 学历:名称取学历层次,类型/层次分别填入
entity.setEducationCode(trim(capability.getEducationLevel()));
entity.setEducationName(trim(capability.getEducationLevel()));
entity.setEducationTypeName(trim(capability.getEducationType()));
entity.setEducationTypeCode(trim(capability.getEducationType()));
entity.setEducationLevelName(trim(capability.getEducationLevel()));
entity.setEducationLevelCode(trim(capability.getEducationLevel()));
}
// 性别:严格转换
String genderText = trim(row.getGender());
String genderText = trim(basic.getGender());
if (StringUtils.hasText(genderText)) {
ImportGenderEnum gender = ImportGenderEnum.fromText(genderText);
if (gender == null) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), entity.getAccount(), entity.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(basic.getRowIndex(), entity.getAccount(), entity.getUserName(),
"性别[" + genderText + "]无法识别,跳过该行"));
row.setValidateFail(true);
return null;
@ -616,12 +736,12 @@ public class OrgPersonnelExcelImporter {
entity.setGenderName(gender.getName());
}
// 是否注册安全工程师:严格转换
String registerText = trim(row.getRegisterEngineer());
// 是否注册安全工程师:严格转换sheet2
String registerText = capability == null ? null : trim(capability.getRegisterEngineer());
if (StringUtils.hasText(registerText)) {
ImportRegisterEngineerEnum register = ImportRegisterEngineerEnum.fromText(registerText);
if (register == null) {
errors.add(new OrgPersonnelImportErrorCO(row.getRowIndex(), entity.getAccount(), entity.getUserName(),
errors.add(new OrgPersonnelImportErrorCO(basic.getRowIndex(), entity.getAccount(), entity.getUserName(),
"是否注册安全工程师[" + registerText + "]无法识别,跳过该行"));
row.setValidateFail(true);
return null;
@ -629,64 +749,13 @@ public class OrgPersonnelExcelImporter {
entity.setRegisterEngineerFlag(register.getCode());
}
// 学历:兜底字典映射,匹配不到则原样存名称
String educationText = trim(row.getEducation());
entity.setEducationName(educationText);
// 职称:无字典,按分隔符拆分后原样存入(保留信息,待后续字典完善)
entity.setTitleCodeArr(parseTitles(trim(row.getTitle())));
entity.setJoinWorkDate(LocalDate.now().minusYears(basic.getIndustryWorkYears()));
entity.setTechnicalDirectorFlag(YES.equals(basic.getTechnicalDirector()));
entity.setProcessControlLeaderFlag(YES.equals(basic.getProcessControlLeader()));
// 能力认证一/二、注安证书、评价师证书、高级工程师等
return entity;
}
private List<TitleCodeCo> parseTitles(String title) {
if (!StringUtils.hasText(title)) {
return null;
}
List<TitleCodeCo> list = Arrays.stream(title.split("[,;;、]"))
.map(String::trim)
.filter(StringUtils::hasText)
.map(t -> {
TitleCodeCo co = new TitleCodeCo();
co.setTitleCode(convertTitleToCode(t));
return co;
})
.collect(Collectors.toList());
return list.isEmpty() ? null : list;
}
/**
* code QualificationLevelEnum "高级工程师"SENIOR"中级工程师"MIDDLE"初级"JUNIOR
*
*/
private String convertTitleToCode(String titleText) {
for (QualificationLevelEnum level : QualificationLevelEnum.values()) {
if (titleText.contains(level.getName())) {
return level.getCode();
}
}
return titleText;
}
/**
* yyyy-MM-dd yyyy/M/d 1988-05-121988/5/12 Excel
*/
private LocalDate parseDate(String text) {
if (!StringUtils.hasText(text)) {
return null;
}
String trimmed = text.trim();
try {
return LocalDate.parse(trimmed, DATE_FORMATTER);
} catch (Exception ignore) {
// 继续尝试斜杠格式
}
try {
return LocalDate.parse(trimmed, SLASH_DATE_FORMATTER);
} catch (Exception ignore) {
return null;
}
}
private String trim(String text) {
return text == null ? null : text.trim();
}

View File

@ -3,21 +3,24 @@ package org.qinan.safetyeval.app.support;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import org.springframework.util.StringUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
* Excel
* Excel sheet2
* <p>
* / / / / () / / /
* / / / / / /
* / / / /
* /
* <p>Excel</p>
* </p>
* sheet
* <ul>
* <li>sheet1-> {@link BasicRow}</li>
* <li>sheet2-> {@link CapabilityRow}
* </li>
* </ul>
* <p>Excel</p>
* <p>
* parentId
* parentId deptId
@ -28,120 +31,253 @@ import javax.validation.constraints.Size;
@Data
public class OrgPersonnelImportRow {
@ExcelProperty(value = "一级部门", index = 0)
private String deptNameLevel1;
@ExcelProperty(value = "二级部门", index = 1)
private String deptNameLevel2;
@ExcelProperty(value = "三级部门", index = 2)
private String deptNameLevel3;
@ExcelProperty(value = "岗位", index = 3)
private String positionName;
@ExcelProperty(value = "账号", index = 4)
@NotBlank(message = "账号不能为空")
@Pattern(regexp = "^1\\d{10}$", message = "手机号格式不正确应为11位数字且以1开头")
@Size(max = 50, message = "账号长度不能超过50个字符")
private String account;
@ExcelProperty(value = "姓名", index = 5)
@NotBlank(message = "姓名不能为空")
@Size(max = 50, message = "姓名长度不能超过50个字符")
private String userName;
@ExcelProperty(value = "性别", index = 6)
@Size(max = 10, message = "性别长度不能超过10个字符")
private String gender;
@ExcelProperty(value = "身份证号", index = 7)
@Size(max = 18, message = "身份证号长度不能超过18个字符")
private String idCardNo;
@ExcelProperty(value = "学历", index = 8)
@Size(max = 50, message = "学历长度不能超过50个字符")
private String education;
@ExcelProperty(value = "毕业院校", index = 9)
@Size(max = 200, message = "毕业院校长度不能超过200个字符")
private String graduateSchool;
@ExcelProperty(value = "所学专业", index = 10)
@Size(max = 100, message = "所学专业长度不能超过100个字符")
private String major;
@ExcelProperty(value = "职称", index = 11)
private String title;
@ExcelProperty(value = "参加工作时间", index = 12)
private String joinWorkDate;
@ExcelProperty(value = "是否注册安全工程师", index = 13)
private String registerEngineer;
@ExcelProperty(value = "现住地址", index = 14)
@Size(max = 500, message = "现住地址长度不能超过500个字符")
private String currentAddress;
@ExcelProperty(value = "办公地址", index = 15)
@Size(max = 500, message = "办公地址长度不能超过500个字符")
private String officeAddress;
@ExcelProperty(value = "主要学习工作经历", index = 16)
private String workExperience;
@ExcelProperty(value = "出版学术专著、专利、获奖、发表学术论文等", index = 17)
@Size(max = 1000, message = "出版学术专著等信息长度不能超过1000个字符")
private String publications;
@ExcelProperty(value = "自我申报的专业能力及认定方式", index = 18)
@Size(max = 1000, message = "自我申报的专业能力信息长度不能超过1000个字符")
private String abilityDeclaration;
/**
* sheet1
*/
private BasicRow basic;
/**
* EasyExcel 1
* sheet2/
*/
@ExcelIgnore
private Integer rowIndex;
private CapabilityRow capability;
/**
* IDExcel
*/
@ExcelIgnore
private Long deptId;
/**
* IDExcel
*/
@ExcelIgnore
private Long postId;
/**
* IDExcel
*/
@ExcelIgnore
private Long basicDisciplineMajorId;
/**
* true false
*/
@ExcelIgnore
private boolean validateFail;
/**
*
*/
public String getDeptDisplayName() {
if (StringUtils.hasText(deptNameLevel3)) {
return deptNameLevel3.trim();
if (basic == null) {
return null;
}
if (StringUtils.hasText(deptNameLevel2)) {
return deptNameLevel2.trim();
if (StringUtils.hasText(basic.getDeptNameLevel3())) {
return basic.getDeptNameLevel3().trim();
}
if (StringUtils.hasText(deptNameLevel1)) {
return deptNameLevel1.trim();
if (StringUtils.hasText(basic.getDeptNameLevel2())) {
return basic.getDeptNameLevel2().trim();
}
if (StringUtils.hasText(basic.getDeptNameLevel1())) {
return basic.getDeptNameLevel1().trim();
}
return null;
}
/**
* Sheet1
* <p>
* / / / / / / /
* / / / / /
* / /
* /
* </p>
*/
@Data
public static class BasicRow {
@ExcelProperty(value = "一级部门", index = 0)
@NotBlank(message = "一级部门不能为空")
@Size(max = 128, message = "一级部门名称最多120个字符")
private String deptNameLevel1;
@ExcelProperty(value = "二级部门", index = 1)
@Size(max = 128, message = "二级部门名称最多120个字符")
private String deptNameLevel2;
@ExcelProperty(value = "三级部门", index = 2)
@Size(max = 128, message = "三级部门最多120个字符")
private String deptNameLevel3;
@ExcelProperty(value = "岗位", index = 3)
@Size(max = 128, message = "岗位最多120个字符")
private String positionName;
@ExcelProperty(value = "姓名", index = 4)
@NotBlank(message = "姓名不能为空")
@Size(max = 50, message = "姓名长度不能超过50个字符")
private String userName;
@ExcelProperty(value = "性别", index = 5)
@Size(max = 10, message = "性别长度不能超过10个字符")
@NotBlank(message = "性别不能为空")
private String gender;
@ExcelProperty(value = "本行业工作年限", index = 6)
@NotNull(message = "本行业工作年限不能为空")
@Range(min = 1, max = 200, message = "本行业工作年限1-200之间")
private Integer industryWorkYears;
@ExcelProperty(value = "账号(系统登录账号)", index = 7)
@NotBlank(message = "账号不能为空")
@Pattern(regexp = "^1\\d{10}$", message = "手机号格式不正确应为11位数字且以1开头")
@Size(max = 50, message = "账号长度不能超过50个字符")
private String account;
@ExcelProperty(value = "身份证号", index = 8)
@Size(max = 18, message = "身份证号长度不能超过18个字符")
@NotBlank(message = "身份证号不能为空")
private String idCardNo;
@ExcelProperty(value = "现住地址", index = 9)
@Size(max = 500, message = "现住地址长度不能超过500个字符")
private String currentAddress;
@ExcelProperty(value = "办公地址", index = 10)
@Size(max = 500, message = "办公地址长度不能超过500个字符")
private String officeAddress;
@ExcelProperty(value = "是否技术负责人", index = 11)
@Pattern(regexp = "^([是否])?$", message = "是否技术负责人仅允许:是、否")
private String technicalDirector;
@ExcelProperty(value = "是否过程控制负责人", index = 12)
@Pattern(regexp = "^([是否])?$", message = "是否过程控制负责人仅允许:是、否")
private String processControlLeader;
@ExcelProperty(value = "主要学习工作经历", index = 13)
@Size(max = 1024, message = "办公地址长度不能超过1024个字符")
private String workExperience;
@ExcelProperty(value = "出版学术专著、专利、获奖、发表学术论文等", index = 14)
@Size(max = 1000, message = "出版学术专著等信息长度不能超过1000个字符")
private String publications;
@ExcelProperty(value = "自我申报的专业能力及认定方式", index = 15)
@Size(max = 1000, message = "自我申报的专业能力信息长度不能超过1000个字符")
private String abilityDeclaration;
/**
* sheet1 1
*/
@ExcelIgnore
private Integer rowIndex;
}
/**
* Sheet2
* <p>
* / / /
* / / / / /
* / / / / /
* / / / / /
* </p>
*/
@Data
public static class CapabilityRow {
@ExcelProperty(value = "姓名", index = 0)
private String userName;
@ExcelProperty(value = "身份证号", index = 1)
@Size(max = 18, message = "身份证号长度不能超过18个字符")
@NotBlank(message = "身份证号不能为空")
private String idCardNo;
@ExcelProperty(value = "能力认证一-认证行业", index = 2)
@NotBlank(message = "能力认证一-认证行业不能为空")
@Size(max = 64, message = "能力认证一-认证行业最多64个字符")
private String cert1Industry;
@ExcelProperty(value = "能力认证一-专业能力", index = 3)
@NotBlank(message = "能力认证一-专业能力不能为空")
@Size(max = 64, message = "能力认证一-专业能力最多64个字符")
private String cert1Ability;
@ExcelProperty(value = "能力认证一-来源", index = 4)
@NotBlank(message = "能力认证一-来源不能为空")
@Size(max = 128, message = "能力认证一-来源最多64个字符")
private String cert1Source;
@ExcelProperty(value = "能力认证二-认证行业", index = 5)
@Size(max = 64, message = "能力认证一-认证行业最多64个字符")
private String cert2Industry;
@ExcelProperty(value = "能力认证二-专业能力", index = 6)
@Size(max = 64, message = "能力认证一-专业能力最多64个字符")
private String cert2Ability;
@ExcelProperty(value = "能力认证二-来源", index = 7)
@Size(max = 128, message = "能力认证二-来源最多128个字符")
private String cert2Source;
@ExcelProperty(value = "学历类型", index = 8)
@NotBlank(message = "学历类型不能为空")
@Size(max = 64, message ="学历类型最多64个字符")
private String educationType;
@ExcelProperty(value = "毕业院校", index = 9)
@Size(max = 200, message = "毕业院校长度不能超过200个字符")
@NotBlank(message = "毕业院校不能为空")
private String graduateSchool;
@ExcelProperty(value = "学历层次", index = 10)
@NotBlank(message = "学历层次不能为空")
private String educationLevel;
@ExcelProperty(value = "所学专业", index = 11)
@Size(max = 100, message = "所学专业长度不能超过100个字符")
@NotBlank(message = "所学专业不能为空")
private String major;
@ExcelProperty(value = "是否注册安全工程师", index = 12)
@Pattern(regexp = "^([是否])?$", message = "是否注册安全工程师仅允许:是、否")
@NotBlank(message = "是否注册安全工程师不能为空")
private String registerEngineer;
@ExcelProperty(value = "注安专业类别", index = 13)
@Size(max = 100, message = "注安专业类别长度不能超过100个字符")
private String registerEngineerCategory;
@ExcelProperty(value = "注安等级", index = 14)
@Size(max = 100, message = "注安等级长度不能超过100个字符")
private String registerEngineerLevel;
@ExcelProperty(value = "注安证书编号", index = 15)
@Size(max = 64, message = "注安证书编号长度不能超过100个字符")
private String registerEngineerCertNo;
@ExcelProperty(value = "执业证书编号", index = 16)
@Size(max = 64, message = "执业证书编号长度不能超过100个字符")
private String practiceCertNo;
@ExcelProperty(value = "是否评价师", index = 17)
@Pattern(regexp = "^([是否])?$", message = "是否评价师仅允许:是、否")
@NotBlank(message = "是否是否评价师不能为空")
private String evaluator;
@ExcelProperty(value = "评价师等级", index = 18)
@Size(max = 64, message = "评价师等级长度不能超过100个字符")
private String evaluatorLevel;
@ExcelProperty(value = "评价师证书编号", index = 19)
@Size(max = 64, message = "评价师证书编号长度不能超过100个字符")
private String evaluatorCertNo;
@ExcelProperty(value = "是否高级工程师", index = 20)
@Pattern(regexp = "^([是否])?$", message = "是否高级工程师仅允许:是、否")
private String seniorEngineer;
@ExcelProperty(value = "高级工程师系列/专业", index = 21)
private String seniorEngineerSeries;
@ExcelIgnore
private Integer rowIndex;
}
}