shinnosuke
2026-08-03 98d1455efaa4cd3d5d3213e047ccc0ac187ba05c
talent-card-server/src/main/java/com/talentcard/server/business/talent/TalentService.java
@@ -31,10 +31,13 @@
@Service
public class TalentService {
   private static final Set<String> TALENT_LEVELS = Set.of("卓越人才卡", "杰出人才卡", "菁英人才卡", "普通游客");
   private static final String[] IMPORT_HEADERS = {
         "姓名", "手机号", "身份证号", "性别", "卡片等级", "人才卡号", "外地/本地", "工作单位", "工作地址", "联系地址"
         "姓名(必填)", "手机号码(必填)", "身份证号(必填)", "性别(必填)", "卡片等级(必填)", "人才卡号(必填)",
         "外地/本地(必填)", "工作单位(必填)", "工作地址(必填)", "联系地址(必填)", "服务专员(必填)"
   };
   private static final Set<String> TALENT_LEVELS = Set.of("卓越人才卡", "杰出人才卡", "普通人才卡", "普通游客");
   private record ImportTalentRow(TalentCreateRequest request, Long advisorId) {}
   private final JdbcTemplate jdbcTemplate;
@@ -327,7 +330,7 @@
            headerRow.createCell(index).setCellValue(IMPORT_HEADERS[index]);
            headerRow.getCell(index).setCellStyle(headerStyle);
         }
         int[] widths = { 16, 18, 24, 10, 18, 20, 14, 24, 28, 28 };
         int[] widths = { 16, 18, 24, 10, 18, 20, 14, 24, 28, 28, 18 };
         for (int index = 0; index < widths.length; index++) {
            sheet.setColumnWidth(index, widths[index] * 256);
         }
@@ -343,6 +346,7 @@
   public Map<String, Object> importExcel(InputStream inputStream) {
      List<Map<String, Object>> failures = new ArrayList<>();
      List<TalentCreateRequest> validRows = new ArrayList<>();
      List<Long> advisorIds = new ArrayList<>();
      Set<String> fileMobiles = new HashSet<>();
      Set<String> fileIdCards = new HashSet<>();
      Set<String> fileCardNos = new HashSet<>();
@@ -373,15 +377,22 @@
            String workUnit = cellText(row, 7, formatter);
            String workAddress = cellText(row, 8, formatter);
            String contactAddress = cellText(row, 9, formatter);
            String advisorName = cellText(row, 10, formatter);
            String error = validateImportRow(name, mobile, idCardNo, gender, level, talentCardNo, area, workUnit, workAddress,
                  contactAddress, fileMobiles, fileIdCards, fileCardNos);
            if (error != null) {
               failures.add(importFailure(rowIndex + 1, name, error));
               continue;
            }
            Long advisorId = advisorIdByName(advisorName);
            if (advisorId == null) {
               failures.add(importFailure(rowIndex + 1, name, advisorName.isBlank() ? "服务专员不能为空" : "服务专员不存在、已停用或名称不唯一"));
               continue;
            }
            fileMobiles.add(mobile);
            fileIdCards.add(idCardNo);
            fileCardNos.add(talentCardNo);
            advisorIds.add(advisorId);
            validRows.add(new TalentCreateRequest(null, name, mobile, idCardNo, genderValue(gender), workUnit, level, talentCardNo,
                  "外地".equals(area) ? "OUTSIDE" : "LOCAL", workAddress, contactAddress));
         }
@@ -394,8 +405,9 @@
         throw new BusinessException(ErrorCode.BAD_REQUEST, "Excel 文件无法解析,请使用下载的导入模板");
      }
      for (TalentCreateRequest request : validRows) {
         create(request);
      for (int index = 0; index < validRows.size(); index++) {
         Map<String, Object> created = create(validRows.get(index));
         updateAdvisor(((Number) created.get("id")).longValue(), advisorIds.get(index));
      }
      Map<String, Object> result = new LinkedHashMap<>();
      result.put("successCount", validRows.size());
@@ -473,11 +485,21 @@
         String talentCardNo, String area, String workUnit, String workAddress, String contactAddress,
         Set<String> fileMobiles, Set<String> fileIdCards, Set<String> fileCardNos) {
      if (name.isBlank()) return "姓名不能为空";
      if (mobile.isBlank()) return "手机号不能为空";
      if (idCardNo.isBlank()) return "身份证号不能为空";
      if (gender.isBlank()) return "性别不能为空";
      if (level.isBlank()) return "卡片等级不能为空";
      if (talentCardNo.isBlank()) return "人才卡号不能为空";
      if (area.isBlank()) return "外地/本地不能为空";
      if (workUnit.isBlank()) return "工作单位不能为空";
      if (workAddress.isBlank()) return "工作地址不能为空";
      if (contactAddress.isBlank()) return "联系地址不能为空";
      if (name.isBlank()) return "姓名不能为空";
      if (name.length() > 64) return "姓名不能超过64个字符";
      if (!mobile.matches("^1\\d{10}$")) return "手机号须为11位数字";
      if (!idCardNo.matches("^[0-9A-Z]{18}$")) return "身份证号须为18位数字或大写英文字母";
      if (!gender.isBlank() && !Set.of("男", "女").contains(gender)) return "性别只能填写男或女";
      if (!TALENT_LEVELS.contains(level)) return "卡片等级须填写卓越人才卡、杰出人才卡、普通人才卡或普通游客";
      if (!TALENT_LEVELS.contains(level)) return "卡片等级须填写卓越人才卡、杰出人才卡、菁英人才卡或普通游客";
      if (!talentCardNo.matches("^[0-9A-Z-]{1,64}$")) return "人才卡号只能包含大写字母、数字和短横线";
      if (!area.isBlank() && !Set.of("本地", "外地").contains(area)) return "外地/本地只能填写本地或外地";
      if (workUnit.length() > 128) return "工作单位不能超过128个字符";
@@ -495,6 +517,14 @@
      return null;
   }
   private Long advisorIdByName(String advisorName) {
      if (advisorName == null || advisorName.isBlank()) return null;
      List<Long> ids = jdbcTemplate.queryForList(
            "select id from talent_card_advisor where status = 1 and advisor_name = ?",
            Long.class, advisorName.trim());
      return ids.size() == 1 ? ids.get(0) : null;
   }
   private Byte genderValue(String gender) {
      if ("男".equals(gender)) return 1;
      if ("女".equals(gender)) return 2;