【寻迹校园 HarmonyOS NEXT 实战 26】“先查看、再勾选、再二次确认”:认领审核门禁如何降低误操作
这是“寻迹校园 HarmonyOS NEXT 实战”系列第 26 篇。本文结合
ClaimReviewPage、ClaimService.getReviewBundle()、ClaimService.accept()与本地状态机测试,拆解“展开私密信息—主动勾选—二次确认—Service 再校验”四层门禁,并说明布尔门禁、幂等处理和正式权限模型之间的边界。
上图为原创生成的审核门禁架构插画,不是应用截图。审核者必须沿着VIEW → CHECK → CONFIRM → ACCEPT顺序推进,不能从列表直接跨到“认领已同意”。
一、一个“同意认领”按钮为什么不够
认领审核不是普通的点赞或收藏。一次误点可能让错误申请进入线下交接,也可能让真正失主暂时无法继续申请。
如果页面打开时“同意认领”就处于可点击状态,系统无法区分审核者是否:
- 看过拾得者保留的私密特征;
- 看过申请者提交的核验回答;
- 理解“信息相似不等于所有权成立”;
- 意识到同意后会进入安全交接;
- 只是误触了页面底部按钮。
因此门禁的目标不是增加步骤,而是让高风险动作留下明确、可解释的用户意图。
二、审核页先获取专用 ReviewBundle
普通认领列表只返回ClaimRecord,不包含私密答案。进入审核页后,ClaimService.getReviewBundle()才调用专用 Repository 查询,并组合三部分数据:
| 字段 | 来源 | 用途 |
|---|---|---|
claim | 认领元数据 | 显示目标、状态与关联关系 |
ownerPrivateFeature | 拾得记录私密特征 | 作为核验基准 |
applicantProof | 申请者回答 | 与基准并列人工核对 |
Service 还会阻止缺少拾得者私密特征的申请被直接同意。因为没有基准时,展示一个“同意”按钮只是在制造虚假的审核感。
三、第一道门禁:必须主动展开两段私密信息
ClaimReviewPage默认把私密特征对照区折叠。审核者点击“展开”后,页面才显示“我的私密提示”和“申请者回答”。
这不是为了视觉简洁,而是把“读取私密数据”变成显式动作。收起对照区时,页面会把reviewed重新置为false:
.onClick(()=>{this.expanded=!this.expanded;if(!this.expanded)this.reviewed=false;})这样可以避免用户先勾选、再收起内容、最后在没有上下文的情况下继续提交。
四、第二道门禁:勾选表达的是审核承诺
展开内容后,页面提供“我已核对两段信息,并理解相似不代表物品归属”的复选框。
这句话同时表达两个事实:
- 审核者已经比较了两段信息;
- 相似只支持继续线下核验,不代表系统替用户判定所有权。
复选框还配置了无障碍语义。读屏用户听到的不只是“复选框”,还会得到“确认相似信息不代表物品归属”的描述。
五、第三道门禁:按钮 disabled 只是体验层保护
当reviewed === false时,“同意认领”按钮使用禁用颜色并设置.enabled(false)。这能减少普通点击误操作,也让页面状态一眼可见。
但页面禁用不是安全边界。深链、旧页面实例、未来新入口或自动化调用都可能绕过当前组件。因此业务层仍要校验:
asyncaccept(claimId:string,proofReviewed:boolean):Promise<OperationResult<ClaimRecord>>{if(!proofReviewed){returnnewOperationResult<ClaimRecord>(false,'请先完整核对私密特征并勾选已核对');}returnthis.transition(claimId,ClaimStatus.PENDING,ClaimStatus.ACCEPTED,'已同意认领申请');}ArkUI 页面负责表达状态,Service 才负责维护业务不变量。
六、第四道门禁:真正改变状态前再二次确认
审核者点击“同意认领”后,项目不会立刻写入ACCEPTED,而是进入确认区,显示:
- “确认同意本次认领?”
- “同意后将进入安全交接,联系方式仍不会公开。”
- “取消”和“确认”两个动作。
拒绝同样有独立确认文案,并提前说明“物品会重新开放认领”。二次确认把结果和副作用放在最后一次点击之前,能降低底部按钮误触带来的业务风险。
七、为什么确认区比普通 Toast 更合适
Toast 适合展示结果,不适合承载高风险决策。它持续时间短、不能阻止写入,也很难让用户在操作前理解后果。
确认区位于页面内容流中,长文案可换行,按钮状态可禁用,处理过程中还会显示“处理中…”。这种结构更适合 ArkUI 的小屏、字体放大和返回流程。
八、状态机再阻止“过时页面”提交
即使用户已经完成所有页面门禁,认领状态也可能在此期间发生变化。transition()会检查当前状态必须仍为PENDING。
如果状态已经是目标ACCEPTED,Service 返回成功,形成重复接受的幂等语义;如果状态变成其他值,则提示“当前认领状态已变化,请刷新后重试”。
这比页面缓存一个旧状态后直接覆盖数据库更安全。
上图展示页面层负责“看过、勾选、确认”,Service 层负责proofReviewed、当前状态和幂等检查,Repository 只执行受约束的状态写入。
九、拒绝路径为什么不能被同意门禁拖累
同意必须先核对,因为它会进入交接;拒绝不要求勾选reviewed,但仍需要二次确认。
这是风险差异化设计:不能因为用户未勾选核验框,就阻止发布者结束明显错误或恶意的申请。但拒绝后会把目标物品恢复为OPEN,因此仍要展示清楚副作用。
十、处理状态防止连续点击
页面在网络或存储操作期间把processing设置为true。确认按钮禁用,acceptClaim()和rejectClaim()入口也会再次检查。
这能减少同一页面实例上的重复点击,却不能代替 Repository 唯一约束、事务或服务端版本锁。正式多用户系统仍必须假设两个设备可能同时提交决策。
十一、本地测试验证了哪些门禁
当前自动化覆盖以下路径:
- 未勾选核验时
accept(..., false)返回失败; - 失败后 Claim 仍保持
PENDING; - 勾选后可以从
PENDING转为ACCEPTED; - 对已经
ACCEPTED的同一申请再次接受仍返回成功; - 目标报告在申请提交后进入
CLAIMING; - 拒绝路径恢复目标报告为
OPEN。
运行命令:
powershell-ExecutionPolicy Bypass-File.\scripts\test-local-state-machines.ps1这些测试证明当前业务规则在本地回退 Repository 中成立,不等于真实账号权限、加密数据库真机恢复或多设备并发已经验证。
十二、布尔值门禁还缺少什么
当前proofReviewed: boolean只表达“调用方说自己看过”。它没有记录何时展开、何时勾选、看的是哪个版本的 proof,也没有服务端权威时间。
正式版本可以升级为:
| 字段 | 作用 |
|---|---|
proofViewedAt | 记录受权审核者首次读取时间 |
proofConfirmedAt | 记录明确确认时间 |
proofVersion | 防止读取后内容被替换 |
reviewerId | 绑定真实审核身份 |
decisionVersion | 处理并发更新 |
这些是升级方向,不能倒推为当前项目已经具备审计系统。
十三、私密数据读取需要真实权限
当前页面明确提示“单机比赛演示:本页模拟切换到拾得者视角”。这是一条重要的产品诚实边界。
正式联网版中,getReviewBundle()应由服务端确认当前用户确实是目标拾得记录的创建者或授权治理人员。只靠页面路由参数claimId不能成为访问控制。
设备数据库加密也不能代替身份权限:加密保护落盘数据,授权决定谁能读取。
十四、长文本与无障碍不能破坏门禁顺序
私密特征和申请者回答可能很长。页面使用可滚动容器、正文行高和自适应卡片,避免按钮覆盖内容。
验收时应检查:
- 系统字体放大后两段内容仍可完整阅读;
- 复选框标签不会被截断;
- 读屏顺序先读基准、再读回答、再读承诺;
- 禁用按钮仍能传达不可操作原因;
- 返回再进入时不保留未经权威记录的
reviewed草稿态。
十五、门禁改动的工程影响
如果未来把三步改成滑动确认、验证码或管理员复核,修改范围不应只落在页面。
需要同时检查:
ClaimReviewPage的可见交互;ClaimService.accept()的业务参数;- Repository 是否保存审核证据;
- 消息页如何展示处理中状态;
- 已有 Claim 数据的迁移;
- 自动化测试中的幂等与过时状态案例;
- 正式服务端权限和审计合约。
把门禁视为跨层契约,才能避免“页面看起来更安全,实际接口仍可直接接受”。
工程复盘:门禁还要做到可观测、可复现
审核失败时,排查信息不能只剩一句“操作失败”。为了既定位问题又不泄露私密核验答案,可以把证据分成三个层级:页面只记录用户走到了“未展开、已展开、已勾选、待确认”中的哪一步;Service 记录调用发生时的 Claim 状态、目标迁移和结果类型;Repository 只记录受影响记录的 ID 与写入结果。私密特征正文、申请者回答和任何能反推物品归属的内容都不应进入普通日志。
回归用例也应围绕“初始条件—用户动作—预期状态—落盘结果”组织,而不是只断言按钮有没有变色。例如:初始状态为PENDING且未核对时,点击路径应停在页面门禁,Service 直接调用也必须失败,Repository 不得产生写入;完成核对后第一次接受应迁移为ACCEPTED,再次接受应返回幂等成功;如果审核期间状态已经变成CANCELLED,旧页面即使保留勾选态也不能覆盖新状态。
这套用例能同时发现三类常见回归:页面重构时遗漏禁用条件、调用方忘记传递核对结果、Service 放宽迁移来源却没有同步测试。它还给未来接入服务端审计留下了清晰边界:新增权威时间和审核身份时,只扩展证据模型与合约,不需要把私密答案复制到更多层。
交付验收时还应从真实用户视角复核返回与恢复流程。用户在确认区点击取消后必须回到可继续阅读的审核页;应用切到后台再恢复时,未经权威保存的勾选态不应被当成已经审核;处理失败后按钮要恢复可用,同时保留足够明确的错误文案,避免用户通过连续点击猜测系统是否成功。
十六、本文小结
认领审核的可靠性来自多层约束共同工作:先读取专用审核 bundle,再展开私密信息、主动勾选、二次确认,最后由 Service 检查proofReviewed和PENDING状态。
“寻迹校园”当前已经实现页面禁用、确认区、Service 门禁和重复接受幂等测试;但权威查看时间、真实审核身份、服务端事务与多设备并发仍是正式版本的升级项。
系列导航:第 26 篇 / 共 50 篇。上一篇:《匿名认领申请设计》;下一篇:《认领状态机与 OPEN 恢复》。