fix: 自評去識別化

This commit is contained in:
skytek_xinliang
2026-09-29 09:42:47 +08:00
parent 7eb37a37fb
commit 86ffaf54be
5 changed files with 39 additions and 343 deletions
@@ -1,29 +1,13 @@
import type {
ChronicDisease,
ExerciseFrequency,
Gender,
PainLevel,
RiskLevel,
SelfAssessmentRequest,
WantedImprovement,
} from "@/api/types";
export interface GuestSelfAssessmentProfile {
name: string;
phone: string;
birthday: string;
gender: Gender;
heightCm: number;
weightKg: number;
email: string | null;
emergencyContact: string | null;
emergencyPhone: string | null;
consent: boolean;
consentVersion: "v1";
}
export interface GuestSelfAssessmentDraft {
profile: GuestSelfAssessmentProfile;
assessment: SelfAssessmentRequest;
provisionalRiskLevel: RiskLevel;
/** 同一份草稿重送正式自評時沿用同一個 key。 */
@@ -32,7 +16,6 @@ export interface GuestSelfAssessmentDraft {
export const GUEST_SELF_ASSESSMENT_DRAFT_KEY = "guest-self-assessment-draft";
const GENDER_VALUES: Gender[] = ["male", "female"];
const EXERCISE_FREQUENCY_VALUES: ExerciseFrequency[] = [
"none",
"oneToTwo",
@@ -79,30 +62,6 @@ function isStringArray<T extends string>(
);
}
function isNullableString(value: unknown): value is string | null {
return value === null || typeof value === "string";
}
function isValidProfile(value: unknown): value is GuestSelfAssessmentProfile {
if (!isRecord(value)) {
return false;
}
return (
typeof value.name === "string" &&
typeof value.phone === "string" &&
typeof value.birthday === "string" &&
isOneOf(GENDER_VALUES, value.gender) &&
typeof value.heightCm === "number" &&
typeof value.weightKg === "number" &&
isNullableString(value.email) &&
isNullableString(value.emergencyContact) &&
isNullableString(value.emergencyPhone) &&
typeof value.consent === "boolean" &&
value.consentVersion === "v1"
);
}
function isValidAssessment(value: unknown): value is SelfAssessmentRequest {
if (!isRecord(value)) {
return false;
@@ -123,7 +82,6 @@ function isValidDraft(value: unknown): value is GuestSelfAssessmentDraft {
}
return (
isValidProfile(value.profile) &&
isValidAssessment(value.assessment) &&
isOneOf(RISK_LEVEL_VALUES, value.provisionalRiskLevel) &&
(value.idempotencyKey === undefined ||
@@ -150,7 +108,7 @@ export function calculateProvisionalRiskLevel(
return "low";
}
/** 僅在自評完成並確認送出後保存一份完整訪客草稿。 */
/** 僅在自評完成並確認送出後保存不含身分資料的訪客草稿。 */
export function saveGuestSelfAssessmentDraft(
draft: GuestSelfAssessmentDraft,
): void {
@@ -183,7 +141,23 @@ export function readGuestSelfAssessmentDraft(): GuestSelfAssessmentDraft | null
}
const parsedDraft: unknown = JSON.parse(rawDraft);
return isValidDraft(parsedDraft) ? parsedDraft : null;
if (!isValidDraft(parsedDraft)) {
return null;
}
const sanitizedDraft: GuestSelfAssessmentDraft = {
assessment: {
...parsedDraft.assessment,
wantedImprovements: [...parsedDraft.assessment.wantedImprovements],
chronicDiseases: [...parsedDraft.assessment.chronicDiseases],
},
provisionalRiskLevel: parsedDraft.provisionalRiskLevel,
...(parsedDraft.idempotencyKey
? { idempotencyKey: parsedDraft.idempotencyKey }
: {}),
};
saveGuestSelfAssessmentDraft(sanitizedDraft);
return sanitizedDraft;
} catch {
return null;
}