【寻迹校园 HarmonyOS NEXT 实战 17】编辑、撤回、删除与结案:如何保持报告状态和图片资源一致
发布时间:2026/8/21 22:48:19 作者:尧图编辑部 阅读量:1,286

【寻迹校园 HarmonyOS NEXT 实战 17】编辑、撤回、删除与结案如何保持报告状态和图片资源一致这是“寻迹校园 HarmonyOS NEXT 实战”系列第 17 篇。本文结合ReportService、ReportPhotoRepository与“我的发布”页面拆解一条校园失物记录从开放、认领中到结案、撤回或隐藏的生命周期并重点分析编辑图片、永久删除和跨页面刷新时的数据一致性边界。上图为原创生成的技术插画不是项目截图。一条报告并不是永远可编辑的普通表单状态变化会同时影响按钮、首页公开展示、匹配候选、图片引用和后续认领交接流程。一、CRUD 为什么不足以描述失物报告如果只按数据库动作理解编辑是UPDATE撤回是改状态删除是DELETE结案也是改状态。但从用户视角看它们完全不同编辑修正当前开放记录仍继续参与展示与匹配撤回停止公开和匹配但发布者仍能在本机查看与删除删除从当前设备永久移除记录与私密字段同时清理受管图片结案双方完成安全交接后保留业务历史但不再作为新匹配候选隐藏由内容治理流程阻止公开展示不等于发布者主动删除。这些语义必须先进入状态机再落到数据库操作。否则页面很容易出现“按钮还能点但 Service 已经拒绝”或者“列表消失了图片文件仍残留”的不一致。二、六个状态分别承担什么职责项目使用以下枚举exportenumReportStatus{DRAFTDRAFT,OPENOPEN,CLAIMINGCLAIMING,RESOLVEDRESOLVED,WITHDRAWNWITHDRAWN,HIDDENHIDDEN}状态—动作矩阵如下当前状态编辑撤回删除首页展示新候选匹配DRAFT走草稿流程不适用清草稿否否OPEN允许允许允许是是CLAIMING禁止禁止禁止是仅按流程受控RESOLVED禁止禁止允许本机删除结果页可查否WITHDRAWN禁止已撤回允许否否HIDDEN禁止不适用允许自有记录删除否否真正的门禁必须放在 Service。页面可以提前隐藏按钮改善体验但不能成为唯一保护层。三、编辑只允许发生在 OPENupdateReport()先验证三件事记录存在且由当前设备创建、状态仍为OPEN、丢失/拾得类型没有被修改。if(!current||!current.isUserCreated){returnnewOperationResultItemReport(false,只能编辑当前设备创建的记录,undefined,ErrorCategory.VALIDATION);}if(current.status!ReportStatus.OPEN){returnnewOperationResultItemReport(false,只有进行中的开放记录可以编辑,current,ErrorCategory.VALIDATION);}if(current.reportType!draft.reportType){returnnewOperationResultItemReport(false,编辑时不能更改丢失/拾得类型,current,ErrorCategory.VALIDATION);}限制 ReportType 很重要。失主发布和拾得者发布的私密核验规则不同直接在编辑时翻转类型会让已有匹配、认领申请和私密字段语义同时失效。编辑页面加载旧值时还要临时保留历史分类和地点。第 20 篇会继续讨论词表升级后的回显问题。四、编辑图片的正确顺序编辑不是先删除旧图片再复制新图片。若新图片复制或数据库写入失败先删旧图会让原记录也无法展示。项目采用的顺序是读取原记录与原图片 URI将新选择的 URI 物化到应用沙箱用新 URI 更新业务记录数据库成功后清理不再被新记录引用的旧图片任一步失败清理本轮新建图片并保留原引用。核心代码保留了preparedUris和originalUris两组快照originalUriscurrent.imageUris;preparedUristhis.photoRepository.materialize(draft.imageUris,this.context);constsavedawaitthis.repository.update(updated,privateFeature,this.context);this.photoRepository.cleanup(current.imageUris,preparedUris,this.context);这属于补偿式一致性不是数据库与文件系统之间的原子事务。应用在进程被强制终止时仍可能留下孤儿文件正式产品应增加启动期扫描或引用计数清理。五、撤回为什么不是删除用户可能暂时不想继续公开一条信息但仍希望保留记录。撤回只把OPEN改成WITHDRAWNawaitthis.repository.updateStatus(current.id,ReportStatus.WITHDRAWN,this.context);current.statusReportStatus.WITHDRAWN;returnnewOperationResult(true,记录已撤回不再参与公开展示和匹配,current);筛选规则会排除WITHDRAWN匹配链路也不再把它当作候选。“我的发布”页面仍可显示并给出“已停止公开展示和候选匹配”的解释。撤回保留记录与图片删除才释放资源。把两者拆开可以减少误操作也为未来的“重新发布”留出产品演进空间但当前实现没有提供撤回后恢复开放的入口文章不能把未来能力写成已完成。六、删除为什么必须二次确认“我的发布”页面把撤回和删除分别显示并使用不同确认文案撤回不再公开展示或参与匹配记录仍可查看删除记录与私密核验特征从当前设备移除无法恢复。这不是文案细节而是把数据后果提前告诉用户。删除 Service 还会阻止CLAIMING状态if(current.statusReportStatus.CLAIMING){returnnewOperationResult(false,认领处理中不能删除请先处理认领申请,current,ErrorCategory.VALIDATION);}awaitthis.repository.deleteById(current.id,this.context);this.photoRepository.cleanup(current.imageUris,[],this.context);先删数据库、后清图片确保业务记录不会继续引用已删除文件。当前cleanup()会吞掉单个文件删除异常因此数据库成功不等于磁盘一定没有残留这需要被记录为维护风险而不是被隐藏。上图展示两条关键路径编辑成功后只删除不再引用的旧图片永久删除先移除业务记录再尝试清理所有受管文件。任何失败提示都应由 Service 映射页面不直接操作路径。七、结案由双方确认驱动报告不能因为一方点击“已拿到”就立刻结束。项目的安全交接流程要求认领申请先通过再约定固定交接点和时间段最后双方分别确认完成。只有HandoffService看到双方确认均成立才将交接标记为完成、认领标记为完成并把关联报告更新为RESOLVED。这种设计避免页面直接调用markResolved()形成旁路。状态变化应由拥有业务规则的 Service 驱动而不是由按钮文字决定。当前流程是单机角色模拟用于验证状态顺序和页面闭环它不代表已经具备真实账号身份、服务端鉴权或两台设备之间的同步确认。八、RESOLVED 为什么保留而不是直接删除结案记录仍有价值用户可以回顾处理结果消息和交接页需要解释完成状态产品可以统计找回率和平均处理时间发生争议时需要保留状态轨迹后续可以防止同一报告再次进入认领流程。因此RESOLVED是业务完成不是数据销毁。首页是否展示完成记录可以按产品策略决定但新候选召回必须排除已经结案的记录。九、HIDDEN 与 WITHDRAWN 的来源不同WITHDRAWN由发布者主动触发HIDDEN来自内容举报和本地治理流程。二者都不参与公开筛选但审计语义不同。如果未来接入远端运营后台隐藏动作需要保存原因、处理人、时间和申诉结果发布者撤回则属于个人操作。将两者合并为单一“不可见”布尔值会丢失业务来源也无法正确恢复。十、跨页面刷新不能手工补计数编辑、撤回、删除或结案成功后“我的发布”、首页、消息和匹配页都可能需要更新。项目通过回调递增dataRevision各页面重新从 Service 获取权威数据。正确顺序是Service 操作完成并返回成功当前页展示用户可见回执触发onDataChanged()页面重新查询列表返回上一级时其他页面根据版本信号刷新。不要在删除后手工执行“首页数量减一、我的发布数量减一、候选数量减一”。这类派生状态很快会与 Repository 脱节。十一、候选为什么会自然失效首页筛选直接排除DRAFT、HIDDEN与WITHDRAWN匹配候选只接收对向类型且状态为OPEN或CLAIMING的报告。因此撤回、隐藏和结案不需要维护额外的“候选表删除标志”。候选是从当前权威记录动态计算的派生结果。只要所有入口都调用同一套 Service/Policy状态变化就会自然反映在下一次查询中。若未来把候选缓存到数据库或云端就必须新增失效策略不能继续假设它会自动同步。十二、当前实现仍有哪些一致性缺口从工程交付角度至少有四点需要诚实记录文件删除失败被忽略可能留下孤儿文件数据库记录与文件系统之间没有真正事务永久删除后历史认领/交接记录的关联清理策略还不完整单机角色模拟无法证明多人并发操作与远端权限正确。正式产品可以考虑软删除、外键约束、审计表、后台清理任务和服务端状态机。当前文章只描述已存在的本地实现不把建议写成现状。十三、如何验证这条生命周期自动化可以固定验证认领提交把报告改为CLAIMING拒绝或取消恢复为OPEN双方完成交接后两条关联报告变成RESOLVED治理完成后目标报告变成HIDDEN。powershell-ExecutionPolicy Bypass-File.\scripts\test-local-state-machines.ps1但本地 Node 测试使用无UIAbilityContext的 Repository 回退路径不能证明文件实际删除、RelationalStore 事务和冷启动持久化。设备层还应手工验证编辑替换图片后旧图是否清理撤回后首页和匹配页是否同时消失删除后重启是否仍不可见交接双方确认后是否结案数据库或文件失败时页面是否保留可恢复状态。验证结果要分别记录为passed、failed或not run不要用一次页面点击替代所有层级。十四、维护时最容易犯的三个错误第一个错误是只改页面按钮不改 Service 门禁。结果是深链、旧页面实例或其他调用方仍能执行非法动作。第二个错误是更新状态后不重新查权威数据页面继续拿着旧对象修改导致内存回退和数据库行为不一致。第三个错误是只处理数据库不处理图片引用。短期看记录删除成功长期会积累沙箱垃圾或产生断图。每新增一种状态或动作都要同时检查状态枚举、Service 转移规则、Repository 更新、页面按钮、筛选候选、图片生命周期、自动化用例和用户可见文案。十五、修改状态机前先做影响分析增加一个新状态并不只是扩展枚举。它会同时影响首页可见性、匹配候选、编辑权限、撤回与删除按钮、交接条件、举报治理、统计口径和历史数据恢复。若只修改其中一个入口系统会出现页面显示允许、Service 实际拒绝或旧客户端继续写入非法状态的分叉。因此变更前应建立状态—动作—数据后果矩阵逐项标出由哪个 Service 发起、哪个 Repository 持久化、失败后恢复到什么状态、哪些页面需要重新查询。矩阵既是实现清单也是测试清单能把“按钮能点”提升为“状态转移可证明”。十六、失败补偿与幂等处理编辑、撤回、删除和结案都可能被重复触发。页面禁用按钮只能降低重复点击不能成为唯一防线。Service 仍应根据当前权威状态判断请求是否已经完成、是否可以安全重试以及重复调用应返回成功、拒绝还是保持原值。编辑写库失败时原记录和原图片引用必须保持可用撤回重复执行时不应继续生成新的状态副作用删除操作找不到记录时要区分已删除与数据源异常双方确认重复提交时只能完成一次结案图片清理失败时业务记录删除成功与磁盘回收失败要分别记录。这些规则让重试有确定结果也为未来远端接口增加幂等键、版本号和审计日志留下边界。当前单机项目可以验证顺序和门禁但不能把它表述为多端并发已经解决。十七、如何形成可追踪的验收证据自动化应覆盖每条合法转移和关键非法转移并保存初始状态、执行动作、返回结果与最终权威状态。设备测试还要加入重启复核撤回、删除或结案后结束进程再次启动确认首页、个人页和匹配页都从 Repository 得到一致结果。对于图片资源应记录替换前后的 URI 集合而不是只看页面有没有显示图片。对于结案流程应分别保存双方确认前、单方确认后和双方完成后的状态。只有证据能区分 UI 呈现、业务状态与文件资源才能在回归时定位问题落在哪一层。十八、本文小结编辑、撤回、删除与结案不是四个孤立按钮而是一条受状态机约束的报告生命周期。“寻迹校园”把门禁放在ReportService把记录读写放在 Repository把受管图片交给ReportPhotoRepository并通过重新查询权威数据刷新页面。当前实现已经区分撤回与删除、阻止认领中永久删除、在编辑和失败路径清理图片也明确存在文件事务与历史关联清理的后续空间。系列导航第 17 篇 / 共 50 篇。上一篇《Preferences 还是 RelationalStore》下一篇《六维 AND 组合筛选》。