From 6d4ac9680c02148ee9e438e0ba44e370f67fd063 Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期四, 13 八月 2026 18:40:06 +0800
Subject: [PATCH] fix(family-card): 收紧身份证校验并调整关系箭头

---
 talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardPortalService.java      |    2 
 rencaika/pages/family-card-apply/family-card-apply.uvue                                                  |   19 +++++++--
 talent-card-server/src/main/java/com/talentcard/server/request/DependentCardApplyRequest.java            |    4 +-
 talent-card-server/src/test/java/com/talentcard/server/service/DependentCardApplyServiceTests.java       |    2 
 talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardApplicationRequest.java |    2 
 talent-card-server/src/test/java/com/talentcard/server/request/FamilyCardIdCardValidationTests.java      |   51 +++++++++++++++++++++++++
 6 files changed, 71 insertions(+), 9 deletions(-)

diff --git a/rencaika/pages/family-card-apply/family-card-apply.uvue b/rencaika/pages/family-card-apply/family-card-apply.uvue
index b02931f..ae872a8 100644
--- a/rencaika/pages/family-card-apply/family-card-apply.uvue
+++ b/rencaika/pages/family-card-apply/family-card-apply.uvue
@@ -15,14 +15,14 @@
 				<view class="input-box"><input v-model="form.name" maxlength="20" placeholder="请输入姓名" placeholder-class="input-placeholder" /></view>
 
 				<text class="field-label">身份证号</text>
-				<view class="input-box"><input v-model="form.idCard" maxlength="18" placeholder="请输入身份证号" placeholder-class="input-placeholder" /></view>
+				<view class="input-box"><input :value="form.idCard" maxlength="18" placeholder="请输入身份证号" placeholder-class="input-placeholder" @input="handleIdCardInput" /></view>
 
 				<text class="field-label">手机号</text>
 				<view class="input-box"><input v-model="form.phone" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="input-placeholder" /></view>
 
 				<text class="field-label">关系</text>
 				<picker class="relation-picker" :range="relations" :value="relationIndex" @change="changeRelation">
-					<view class="picker-box"><text>{{form.relation}}</text><text class="picker-arrow">⌄</text></view>
+					<view class="picker-box"><text>{{form.relation}}</text><view class="picker-arrow"></view></view>
 				</picker>
 
 				<view v-if="applicationId != '' && auditRemark != ''" class="reject-tip"><text>驳回原因:{{auditRemark}}</text></view>
@@ -97,13 +97,24 @@
 		next.relation = relations[selectedIndex]
 		form.value = next
 	}
+	const handleIdCardInput = (event: any) => {
+		const raw = (event.detail.value as string).toUpperCase()
+		let normalized = raw.replace(/[^0-9X]/g, '').slice(0, 18)
+		if (normalized.indexOf('X') >= 0 && (normalized.length != 18 || !normalized.endsWith('X'))) {
+			normalized = normalized.replace(/X/g, '')
+		}
+		const next = form.value
+		next.idCard = normalized
+		form.value = next
+		return normalized
+	}
 	const submitApplication = () => {
 		if (isSubmitting.value) return
 		if (form.value.name.trim().length == 0) {
 			uni.showToast({ title: '请填写姓名', icon: 'none' })
 			return
 		}
-		if (!(/^(?:[0-9]{15}|[0-9]{17}[0-9Xx])$/).test(form.value.idCard.trim())) {
+		if (!(/^[0-9]{17}[0-9X]$/).test(form.value.idCard.trim())) {
 			uni.showToast({ title: '请填写正确的身份证号', icon: 'none' })
 			return
 		}
@@ -162,7 +173,7 @@
 	.field-label:not(:first-child) { margin-top: 45rpx; }
 	.relation-picker { display: block; }
 	.picker-box { margin-top: 14rpx; color: #4E5867; font-size: 28rpx; line-height: 40rpx; justify-content: space-between; }
-	.picker-arrow { color: #9FA9B6; font-size: 40rpx; line-height: 38rpx; }
+	.picker-arrow { width: 12rpx; height: 12rpx; margin-right: 4rpx; border-right: 2rpx solid #AAB3BE; border-bottom: 2rpx solid #AAB3BE; transform: rotate(45deg); }
 	.submit-button { display: flex; height: 84rpx; margin-top: 43rpx; border-radius: 44rpx; background-image: linear-gradient(90deg, #FF4E35 0%, #FF8318 100%); justify-content: center; align-items: center; }
 	.submit-button text { color: #FFFFFF; font-size: 28rpx; line-height: 40rpx; font-weight: 700; }
 	.submit-button-active { opacity: .8; transform: scale(.99); }
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardApplicationRequest.java b/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardApplicationRequest.java
index ee8a7a3..7c7ec0b 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardApplicationRequest.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardApplicationRequest.java
@@ -7,7 +7,7 @@
 public record FamilyCardApplicationRequest(
 	@NotNull Long talentId,
 	@NotBlank String name,
-	@NotBlank @Pattern(regexp = "^(?:[0-9]{15}|[0-9]{17}[0-9Xx])$") String idCard,
+	@NotBlank @Pattern(regexp = "^[0-9]{17}[0-9X]$") String idCard,
 	@NotBlank @Pattern(regexp = "^1[3-9][0-9]{9}$") String mobile,
 	@NotBlank String relationship
 ) {}
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardPortalService.java b/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardPortalService.java
index 3644135..a5f345a 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardPortalService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/portal/FamilyCardPortalService.java
@@ -274,7 +274,7 @@
 	private String unbindReason(Map<String, Object> row) { if ("待审核".equals(string(row.get("applicationStatus")))) return "家属卡正在审核,暂不可解绑"; if (!"已通过".equals(string(row.get("applicationStatus")))) return "当前家属卡状态不可解绑"; if (number(row.get("cardStatus")) == null || number(row.get("cardStatus")) != 1) return "家属卡未处于有效状态"; return "可解除绑定"; }
 	private String statusText(String applicationStatus, Object cardStatus) { if ("待审核".equals(applicationStatus)) return "审核中"; if ("已驳回".equals(applicationStatus)) return "已驳回"; if ("已解绑".equals(applicationStatus)) return "已解除"; return number(cardStatus) != null && number(cardStatus) == 1 ? "已生效" : "已失效"; }
 	private Long number(Object value) { return value instanceof Number number ? number.longValue() : null; }
-	private boolean validIdCard(String value) { return value.matches("[0-9]{15}") || value.matches("[0-9]{17}[0-9Xx]"); }
+	private boolean validIdCard(String value) { return value.matches("[0-9]{17}[0-9X]"); }
 	private String string(Object value) { return value == null ? "" : String.valueOf(value); }
 	private String maskName(String value) { if (value.isBlank()) return ""; return value.length() == 1 ? "*" : value.substring(0, 1) + "*".repeat(value.length() - 1); }
 	private String maskMobile(String value) { return value.length() == 11 ? value.substring(0, 3) + "****" + value.substring(7) : ""; }
diff --git a/talent-card-server/src/main/java/com/talentcard/server/request/DependentCardApplyRequest.java b/talent-card-server/src/main/java/com/talentcard/server/request/DependentCardApplyRequest.java
index f78c041..2df3361 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/request/DependentCardApplyRequest.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/request/DependentCardApplyRequest.java
@@ -20,7 +20,7 @@
 		@Schema(description = "家属身份证号", example = "510105199001011234", maxLength = 32, requiredMode = Schema.RequiredMode.REQUIRED)
 		@NotBlank(message = "家属证件号不能为空")
 		@Size(max = 32, message = "家属证件号不能超过32个字符")
-		@Pattern(regexp = "^(?:[0-9]{15}|[0-9]{17}[0-9Xx])$", message = "家属证件号格式不正确")
+		@Pattern(regexp = "^[0-9]{17}[0-9X]$", message = "家属证件号格式不正确")
 		String dependentIdCardNo,
 
 		@Schema(description = "家属手机号", example = "13800138000", pattern = "^1[3-9][0-9]{9}$", requiredMode = Schema.RequiredMode.REQUIRED)
@@ -33,4 +33,4 @@
 		@Size(max = 32, message = "家属关系不能超过32个字符")
 		String relation
 ) {
-}
\ No newline at end of file
+}
diff --git a/talent-card-server/src/test/java/com/talentcard/server/request/FamilyCardIdCardValidationTests.java b/talent-card-server/src/test/java/com/talentcard/server/request/FamilyCardIdCardValidationTests.java
new file mode 100644
index 0000000..54a7cc0
--- /dev/null
+++ b/talent-card-server/src/test/java/com/talentcard/server/request/FamilyCardIdCardValidationTests.java
@@ -0,0 +1,51 @@
+package com.talentcard.server.request;
+
+import com.talentcard.server.business.portal.FamilyCardApplicationRequest;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class FamilyCardIdCardValidationTests {
+
+	private static jakarta.validation.ValidatorFactory validatorFactory;
+	private static Validator validator;
+
+	@BeforeAll
+	static void setUpValidator() {
+		validatorFactory = Validation.buildDefaultValidatorFactory();
+		validator = validatorFactory.getValidator();
+	}
+
+	@AfterAll
+	static void closeValidator() {
+		validatorFactory.close();
+	}
+
+	@Test
+	void acceptsEighteenDigitsOrUppercaseXAsCheckCode() {
+		assertThat(violationsForDependentRequest("510105199001011234")).isZero();
+		assertThat(violationsForDependentRequest("51010519900101123X")).isZero();
+		assertThat(violationsForPortalRequest("510105199001011234")).isZero();
+		assertThat(violationsForPortalRequest("51010519900101123X")).isZero();
+	}
+
+	@Test
+	void rejectsLegacyAndInvalidIdCardFormats() {
+		for (String idCard : new String[] { "510105900101123", "51010519900101123x", "51010519900101123A" }) {
+			assertThat(violationsForDependentRequest(idCard)).isPositive();
+			assertThat(violationsForPortalRequest(idCard)).isPositive();
+		}
+	}
+
+	private int violationsForDependentRequest(String idCard) {
+		return validator.validate(new DependentCardApplyRequest(1L, "家属", idCard, "13800138000", "配偶")).size();
+	}
+
+	private int violationsForPortalRequest(String idCard) {
+		return validator.validate(new FamilyCardApplicationRequest(1L, "家属", idCard, "13800138000", "配偶")).size();
+	}
+}
diff --git a/talent-card-server/src/test/java/com/talentcard/server/service/DependentCardApplyServiceTests.java b/talent-card-server/src/test/java/com/talentcard/server/service/DependentCardApplyServiceTests.java
index 43eba2d..495db5e 100644
--- a/talent-card-server/src/test/java/com/talentcard/server/service/DependentCardApplyServiceTests.java
+++ b/talent-card-server/src/test/java/com/talentcard/server/service/DependentCardApplyServiceTests.java
@@ -86,7 +86,7 @@
 				new DependentCardApplyRequest(
 						34L,
 						" 新家属 ",
-						"51010519900101123x",
+						"51010519900101123X",
 						"13700000000",
 						" 子女 "
 				)

--
Gitblit v1.7.1