FEATURED · 精选文章

【寻迹校园 HarmonyOS NEXT 实战 10】表单校验应该放在哪里:ReportService 的隐私友好校验策略

发布时间 / 2026/8/19 8:10:45
来源 / 创域科博编辑部
栏目 / 资讯中心
【寻迹校园 HarmonyOS NEXT 实战 10】表单校验应该放在哪里:ReportService 的隐私友好校验策略 【寻迹校园 HarmonyOS NEXT 实战 10】表单校验应该放在哪里ReportService 的隐私友好校验策略这是“寻迹校园 HarmonyOS NEXT 实战”系列第 10 篇。本文结合PublishValidation与ReportService.validateDraft()说明页面即时反馈、Service 最终门禁、隐私拦截、日期真实性和图片数量校验如何形成同一套可测试规则。上图为本文原创生成的校验门禁概念图不是项目截图。用户输入先经过页面提示真正写入前仍必须由 Service 统一验证。一、输入框 maxLength 为什么不是业务校验maxLength只能限制字符数量无法回答这些问题标题只有空格是否有效分类是否来自允许词表事件日期是否真的存在日期是否晚于今天公开描述是否包含手机号拾得物是否填写私密核验特征图片数量是否超过系统约束编辑时是否偷偷改变了记录类型。按钮禁用也不能成为唯一防线。调用方可能来自另一个页面、自动化测试或未来的深链入口。只要 Service 接受ReportDraft它就必须自己完成最终校验。二、页面校验与 Service 校验各负责什么页面层负责即时体验输入变化后清理错误、在字段附近显示提示、控制步骤切换和按钮状态。Service 层负责业务安全任何创建或更新动作都重新校验完整草稿失败时不进入 Repository。两层可以使用同一个PublishValidation结果但不能只保留页面层。正确顺序是用户输入 - 页面给出即时提示 - 组装 ReportDraft - Service.validateDraft() - 失败返回字段错误不写入 - 成功归一化、复制图片、Repository 持久化三、用 PublishValidation 承载字段级错误项目没有只返回一个布尔值而是定义字段级错误对象exportclassPublishValidation{titleError:string;categoryError:string;areaError:string;eventDateError:string;descriptionError:string;privateFeatureError:string;photoError:string;isValid():boolean{returnthis.titleError.length0this.categoryError.length0this.areaError.length0this.eventDateError.length0this.descriptionError.length0this.privateFeatureError.length0this.photoError.length0;}}页面能够把错误放到正确字段旁边Service 也可以在createReport()和updateReport()中复用同一规则。四、当前项目的校验规则validateDraft()覆盖以下字段字段规则用户提示标题去空格后至少 2 个字物品名称至少填写 2 个字分类去空格后至少 2 个字请填写物品分类区域去空格后至少 2 个字请填写具体校园区域日期YYYY-MM-DD、真实日期、不晚于今天请选择不晚于今天的事件日期公开描述至少 5 个字公开描述至少填写 5 个字隐私内容拦截明显手机号、微信号和证件号公开描述中不要填写敏感信息私密特征拾得记录至少 4 个字请填写私密核验特征照片最多 3 张最多选择 3 张照片真实实现保持顺序清晰validateDraft(draft:ReportDraft):PublishValidation{constvalidationnewPublishValidation();if(draft.title.trim().length2)validation.titleError物品名称至少填写 2 个字;if(draft.category.trim().length2)validation.categoryError请填写物品分类;if(draft.area.trim().length2)validation.areaError请填写具体校园区域;if(!this.isValidEventDate(draft.eventDate)){validation.eventDateError请选择不晚于今天的事件日期;}if(draft.description.trim().length5){validation.descriptionError公开描述至少填写 5 个字;}if(this.containsSensitiveContent(draft.description)){validation.descriptionError公开描述中不要填写手机号、微信号或证件号码;}if(draft.reportTypeReportType.FOUNDdraft.privateFeature.trim().length4){validation.privateFeatureError拾得物品需填写至少 4 个字的私密核验特征;}if(draft.imageUris.length3)validation.photoError最多选择 3 张照片;returnvalidation;}五、校验顺序也会影响用户体验公开描述先检查长度再检查敏感内容。后一个错误可以覆盖前一个错误使用户优先看到更重要的隐私风险而不是只提示“字数不足”。如果未来需要同时展示多个问题可以把单字符串升级为错误数组或错误码集合。但当前单字段显示一个最关键错误能够控制页面复杂度。六、日期校验不能只依赖正则2026-02-31能匹配YYYY-MM-DD正则却不是有效日期。项目的日期校验分四步正则检查格式构造Date并检查是否为NaN把解析后的年、月、日与原字符串逐项比较把今天设为 23:59:59拒绝未来日期。privateisValidEventDate(value:string):boolean{constnormalized:stringvalue.trim();if(!/^\d{4}-\d{2}-\d{2}$/.test(normalized))returnfalse;constselected:Datethis.parseDate(normalized);if(Number.isNaN(selected.getTime()))returnfalse;constyearNumber(normalized.substring(0,4));constmonthNumber(normalized.substring(5,7));constdayNumber(normalized.substring(8,10));if(selected.getFullYear()!year||selected.getMonth()!month-1||selected.getDate()!day)returnfalse;consttodaynewDate();today.setHours(23,59,59,999);returnselected.getTime()today.getTime();}日期选择器改善输入体验Service 校验保证任何调用路径都无法绕过真实性检查。上图展示两层校验的职责页面提供快速反馈Service 决定是否允许写入Repository 只接收已经通过业务门禁的数据。七、隐私拦截应该诚实描述能力边界当前项目使用轻量规则拦截明显内容关键词“微信”“手机号”“身份证”以及中国大陆手机号格式。privatecontainsSensitiveContent(value:string):boolean{consttext:stringvalue.trim();returntext.includes(微信)||text.includes(手机号)||text.includes(身份证)||/1[3-9][0-9]{9}/.test(text);}这是一道演示级防线不是完整敏感信息识别系统。它可能漏掉使用空格或符号分隔的号码拼音、谐音和图片中的联系方式QQ、邮箱、宿舍门牌和精确交接地点非中国大陆手机号经过编码或刻意规避的文本。因此文章和产品都不能宣称“已识别所有敏感信息”。更完整的生产方案需要规范化、规则库、服务端内容安全、人工审核和申诉链路。八、私密字段不是公开描述的替代垃圾桶拾得物的私密核验特征用于认领审核例如夹层内特定物品。它应该满足最小化原则只记录能验证归属的必要特征不记录无关身份信息。它还需要明确访问边界不出现在首页和详情公开区域不参与 Agent 输入不作为公开匹配理由只在认领审核场景展示删除记录时同步清理日志不得打印原文。校验“至少 4 个字”只是完整保护的一小部分数据流和访问控制同样重要。九、Service 最终门禁如何阻止无效写入创建和更新都先调用validateDraft()asynccreateReport(draft:ReportDraft):PromiseOperationResultItemReport{constvalidation:PublishValidationthis.validateDraft(draft);if(!validation.isValid()){returnnewOperationResultItemReport(false,请检查必填信息,undefined,ErrorCategory.VALIDATION);}// 通过后才复制图片并写入 Repository}这保证无论来自表单提交、编辑页还是未来其他入口非法草稿都不会进入持久化层。Repository 不需要重复理解标题长度和隐私关键词。十、按钮禁用仍然有价值但不是安全边界页面可以根据当前输入禁用“下一步”或“发布”减少无效点击并在 loading、图片选择或草稿保存期间防止冲突。但按钮禁用只负责体验状态可能过时未来调用方也可能不经过这个按钮。最终是否写入必须以 Service 校验结果为准。两者关系不是二选一而是“页面早反馈 Service 强门禁”。十一、如何测试校验规则建议使用表驱动测试覆盖标题空、空格、1 字、2 字、长文本分类与区域空值、未知旧值和权威词表值日期空、格式错、闰日、2 月 31 日、今天和明天描述4 字、5 字、手机号、关键词和普通文本类型丢失与拾得对私密特征的不同要求图片0、1、3、4 张组合错误多个字段同时无效更新动作类型变化、非用户创建记录和非开放状态。测试应该直接调用ReportService.validateDraft()和创建/更新方法而不是只模拟按钮点击。这样规则可以在没有 ArkUI 页面的环境中快速回归。十二、错误文案也是契约错误文案要告诉用户如何修复而不是泄露内部异常。存储异常应该映射成“保存失败请稍后重试”而不是把数据库路径、SQL 或堆栈直接显示在页面。同一错误在页面、测试和日志中应有稳定分类例如VALIDATION、STORAGE和CAPABILITY。用户看到可理解文案开发者通过受控日志定位原因。十三、生产级扩展方向当项目接入后端后校验需要分层扩展客户端即时反馈、基础格式、减少无效请求Service跨页面统一业务规则服务端鉴权、幂等、最终数据约束内容安全文本与图片检测人工治理复杂边界和申诉数据库非空、长度、枚举和唯一性约束。任何一层都不能单独替代其他层。特别是客户端校验永远不能当成服务端安全边界。十四、本文小结表单校验的正确位置不是“页面或 Service 二选一”。页面负责即时反馈ReportService负责最终门禁Repository 只处理已经通过业务规则的数据。PublishValidation提供字段级错误日期检查验证真实日历值隐私规则拦截明显联系方式并诚实保留能力边界。下一篇将进入本地数据层介绍ReportDraftRepository如何让三步表单在退出后恢复同时安全管理图片 URI。系列导航第 10 篇 / 共 50 篇。上一篇《三步结构化发布表单》下一篇《本地草稿恢复与图片 URI 生命周期》。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻