import { z } from "zod" /** * Onboarding 输入校验 schema。 * * 设计原则(参考 K12 教务铁律): * - 角色字段不在用户可提交的 schema 中,角色由管理员预分配,服务端从 usersToRoles 读取。 * - 班级代码仅作为"确认/补绑"用途,服务端调用 modules/classes data-access 做强校验。 * * v3 修复(对标 PowerSchool Access ID + Access Password): * - P0-2 家长绑定验证增强:从"邮箱+生日"(生日仅 365 种可能)升级为"邮箱+生日+手机号后4位"三因子, * 组合空间提升至 365 × 10000 = 3.65M 种,显著降低枚举攻击风险。 * - P1-4 家长多子女:children 数组替代单个 childEmail/childBindingCode,支持一次绑定多个子女。 */ const childSchema = z.object({ childEmail: z .string() .trim() .min(1, "请填写子女邮箱") .email("子女邮箱格式错误"), childBirthDate: z .string() .trim() .min(1, "请填写子女生日") .regex(/^\d{4}-\d{2}-\d{2}$/, "子女生日格式错误(YYYY-MM-DD)"), childPhoneSuffix: z .string() .trim() .min(1, "请填写子女手机号后 4 位") .regex(/^\d{4}$/, "子女手机号后 4 位格式错误"), childRelation: z .string() .trim() .max(50, "关系字段长度不能超过 50 个字符") .optional() .or(z.literal("")), }) export const OnboardingSchema = z.object({ name: z .string() .trim() .min(1, "请填写姓名") .max(50, "姓名长度不能超过 50 个字符"), phone: z .string() .trim() .min(1, "请填写电话") .regex(/^1\d{10}$/, "请输入有效的手机号(11 位,以 1 开头)"), address: z .string() .trim() .max(200, "住址长度不能超过 200 个字符") .optional() .or(z.literal("")), // 学生/教师补绑班级的邀请码列表(可选,每项为 6 位字母数字,v3 新格式) // v3:从 6 位数字升级为 6 位字母数字(剔除歧义字符 0/O/1/I/L),兼容旧 6 位数字码 classCodes: z .array( z .string() .trim() .regex( /^[A-Z2-9]{6}$|^\d{6}$/, "班级邀请码格式错误(6 位字母数字或 6 位数字)" ) ) .max(10, "单次最多绑定 10 个班级") .optional() .default([]), // 教师任课科目(可选,v3 修复 P0-3:服务端循环为每个科目绑定) teacherSubjects: z .array(z.string().trim().min(1)) .max(10) .optional() .default([]), // 家长绑定子女列表(P1-4 多子女支持) children: z .array(childSchema) .max(10, "单次最多绑定 10 个子女") .optional() .default([]), }) export type OnboardingInput = z.infer