【寻迹校园 HarmonyOS NEXT 实战 25】匿名认领申请设计:公开描述与私密核验答案必须分开
这是“寻迹校园 HarmonyOS NEXT 实战”系列第 25 篇。本文结合
ClaimRequestPage、ClaimService、ClaimRepository与ClaimReviewPage的真实实现,说明为什么公开描述不能承担所有权核验,申请者答案为什么不能出现在普通列表,以及当前单机角色模拟与正式账号权限之间仍有哪些边界。
上图为原创生成的隐私架构插画,不是应用截图。公开失物卡片、受限认领元数据和私密核验答案进入不同通道,只有审核动作需要临时组合两段私密信息。
一、公开描述越详细,冒领风险可能越高
失物招领需要足够信息帮助失主发现候选,但如果把包内物品、划痕位置、卡套颜色等全部公开,任何浏览者都能复述这些细节。
更合理的设计是把信息分为三类:
| 分类 | 示例 | 可见范围 |
|---|---|---|
| 公开信息 | 类别、模糊地点、日期、外观概述 | 候选列表和详情 |
| 受限业务信息 | 申请 ID、状态、关联报告 ID、提交时间 | 相关流程页面 |
| 私密核验信息 | 拾得者保留特征、申请者回答 | 审核时按权限读取 |
公开层负责召回,私密层负责增加区分度。二者目的不同,不能为了少建一个字段而混在一起。
二、拾得记录为什么要保留一段未公开特征
发布拾得记录时,项目要求填写私密特征,例如“包内夹层有特殊物品”。这段文字不进入普通ItemReport列表展示,也不交给小艺摘要。
真正失主提交申请时,需要描述自己知道的细节。审核页再把拾得者保留特征与申请者回答并列,供人工判断是否一致。
这是挑战—响应式的产品设计:公开页面只发出足够找到候选的线索,验证答案在受限流程中比较。
三、认领表单先做内容最小化
ClaimRequestPage只要求一段至少 4 个字的核验描述,并明确提示不要输入密码、支付信息、完整证件号和联系方式。
页面还要求用户确认处理说明后才能提交。它不会在认领前公开联系方式,也不会要求上传身份证照片。
减少采集比“采集后再保护”更安全。校园失物核验通常不需要支付账号、证件全号或家庭住址,这些字段不应因为“以后也许有用”而进入数据库。
四、Service 门禁必须覆盖页面之外的调用
ClaimService.submit()依次验证答案长度、处理同意、敏感内容、目标类型、目标状态、重复待处理申请和关联来源:
if(normalizedProof.length<4){returnnewOperationResult<ClaimRecord>(false,'核验特征至少填写 4 个字');}if(!processingConsent){returnnewOperationResult<ClaimRecord>(false,'请先确认核验信息处理说明');}if(this.containsSensitiveContent(normalizedProof)){returnnewOperationResult<ClaimRecord>(false,'请勿填写密码、支付信息、完整证件号或联系方式');}按钮禁用只改善体验,Service 校验才是业务保护。深链、旧页面实例、测试或未来其他入口都必须遵守同一规则。
五、为什么只有 FOUND 记录可以作为认领目标
认领申请的目标必须是拾得记录。用户可以从自己的丢失记录进入匹配结果,再把sourceReportId关联到某个FOUND候选。
Service 会检查目标确实为FOUND,可选来源确实为LOST。这样后续结案时可以同时把拾得和丢失两条关联报告标记为RESOLVED。
如果允许对丢失记录发起“认领”,角色语义会混乱,也无法判断谁保存了物品和私密特征。
六、重复申请需要权威状态门禁
提交前,目标报告必须处于OPEN,且 Repository 中不能已有待处理申请。成功写入后,Service 将报告标记为CLAIMING。
这形成双重门禁:状态阻止普通重复操作,待处理记录检查防止状态与数据偶发不同步。
当前本地流程仍存在跨两次写入的补偿风险:认领记录插入成功后,如果报告状态更新失败,可能出现申请存在但报告仍开放。正式产品需要事务或服务端原子操作;当前代码的try/catch不能被描述成完整事务。
七、公开 ClaimRecord 不包含 proof
ClaimRepository.list()返回ClaimRecord[],其中只有 ID、报告信息、状态、时间和关联来源;核验答案只通过findWithProof()在审核路径读取。
exportclassClaimRecord{id:string='';reportId:string='';reportTitle:string='';status:ClaimStatus=ClaimStatus.PENDING;createdAt:number=0;sourceReportId:string='';}这种模型拆分能降低普通列表意外展示 proof 的概率。即使页面开发者拿到ClaimRecord,也没有可直接渲染的私密答案字段。
上图展示普通消息列表只读取认领元数据,审核页通过专用查询临时组合拾得者私密特征与申请者答案。私密内容不会进入候选列表或小艺摘要。
八、数据库加密配置不能替代访问控制
设备上下文下,ClaimRepository使用加密 RelationalStore 保存claim_record.proof。这可以降低静态存储暴露风险,但不能证明谁有权限读取。
加密解决“数据如何落盘”,权限解决“哪个身份可以解密或请求”。当前项目是单机比赛演示,审核页通过页面流程模拟切换到拾得者视角,没有真实登录身份和服务端授权。
因此准确结论是:设备数据库配置了加密存储,普通列表模型不返回 proof;正式多用户权限仍未实现。
九、审核页为什么先展开,再勾选
ClaimReviewPage默认折叠两段私密信息。只有展开后,审核者才能勾选“已核对”;收起时勾选状态会被清除。
同意按钮还要求reviewed === true,Service 的accept()再次检查proofReviewed。这避免用户在未查看内容时直接同意。
页面同时提醒“相似不代表物品归属”。人工核对仍可能出错,因此同意后进入安全交接,而不是立刻把所有记录永久结案。
十、拒绝、取消和过期为什么要恢复 OPEN
如果申请被拒绝、申请被取消或交接过期,目标拾得记录应恢复为OPEN,让其他真实失主继续申请。
状态恢复属于 Service 责任。页面只展示结果并触发重新查询,不应自己修改列表项。
若状态恢复失败,Service 必须返回“申请状态已变化,但报告恢复失败”的部分失败提示,而不是假装整个动作都成功。认领记录和报告是两个权威实体,故障结果需要分别追踪。
十一、为什么不能在审核通过前公开联系方式
匿名申请阶段公开手机号会带来骚扰、钓鱼和绕过核验。项目成功提示明确说明:核验通过前不会公开联系方式。
后续交接也优先选择校内固定地点与时间段,而不是要求双方立即交换私人地址。即使正式版需要消息通知,也应通过受控通道逐步披露,而不是把联系方式写进公开描述。
当前项目没有真实消息服务器和账号系统,不能声称已经实现端到端匿名通信。
十二、小艺为什么永远看不到私密答案
小艺适配器的输入来自ReportMatchBundle的公开字段,不读取ClaimRepository.findWithProof(),也不读取拾得者的privateFeature。
这是数据流层面的隔离,不只是提示词里写“不要泄露”。如果模型根本拿不到私密答案,越权风险比“拿到后要求它别说”更低。
未来接入云端 AI 时也应使用专门 DTO,只包含完成当前比较所需的最小公开信息。
十三、敏感内容检测只是第一道网
当前 Service 通过关键词和手机号正则阻止密码、支付宝、微信号、身份证与手机号。这能覆盖典型误填,但不是完整 DLP 系统。
用户仍可能用空格、谐音或其他格式输入敏感内容。正式产品可以增加更完善的校验和人工提示,但应优先从表单文案、字段目的和最小采集上降低风险。
不能把一次正则匹配表述为“已经杜绝隐私泄露”。
十四、自动化应该验证哪些隐私契约
本地测试至少要覆盖:
- 少于 4 个字被拒绝;
- 未同意处理说明被拒绝;
- 手机号和敏感关键词被拒绝;
- 目标不是
FOUND被拒绝; - 目标不是
OPEN被拒绝; - 同一报告已有待处理申请时被拒绝;
- 普通
listClaims()不返回 proof; - 未勾选已核对时不能同意;
- 拒绝、取消和过期恢复目标报告;
- 双方完成交接后关联报告结案。
powershell-ExecutionPolicy Bypass-File.\scripts\test-local-state-machines.ps1Node 回退测试可以证明规则和状态机,不能证明真机数据库加密、进程重启恢复、真实身份隔离或多设备并发。
十五、正式版的权限模型应该怎样升级
正式多用户版本至少需要:
- 登录身份与设备绑定;
- 服务端确认谁创建了拾得记录;
- 只有目标记录创建者或授权管理员可读取核验答案;
- 读取和决策写入审计日志;
- API 返回最小字段,列表接口永不包含 proof;
- 状态更新带版本号或事务,阻止并发重复;
- 申诉、举报和数据删除机制;
- 明确保留期限,到期删除不再需要的私密答案。
这些是后续架构要求,不是当前单机版本已完成能力。
十六、隐私验收要看数据路径而不是只看页面
页面上不显示 proof,只能证明视觉层没有展示。完整验收还要检查模型类型、Repository 查询、日志、AI DTO、异常信息、备份和数据库文件。
设备测试应使用专门的非真实测试数据,验证重启后审核页可读、普通列表不可见、拒绝后状态恢复,并检查 hilog 不打印私密答案。证据中也应遮罩身份和联系方式。
当前文章引用的是项目代码和已有状态机测试,不包含真实用户私密数据。
十七、本文小结
匿名认领的核心不是把姓名隐藏,而是按用途拆分数据:公开描述负责召回,受限元数据负责流程,私密核验答案只在审核路径读取。页面、Service、Repository 和 AI 适配器都要遵守同一边界。
“寻迹校园”当前已经做到普通ClaimRecord不携带 proof、专用审核查询组合两段私密信息、提交前过滤典型敏感内容,并在设备上下文使用加密 RelationalStore。但真实账号权限、服务端事务、多设备同步和审计仍未实现,不能用单机页面模拟替代。
系列导航:第 25 篇 / 共 50 篇。上一篇:《AI 不可用时的确定性降级》;下一篇:《认领状态机与并发门禁》。