【听见课堂 HarmonyOS NEXT 实战系列 47】完成与撤销完成:可逆任务状态为何比一个 Toggle 更复杂
【听见课堂 HarmonyOS NEXT 实战系列 47】完成与撤销完成:可逆任务状态为何比一个 Toggle 更复杂
任务卡上的“完成”看起来只是布尔值切换,但可靠实现至少要回答四个问题:候选任务能否完成、写入失败怎么办、统计何时刷新、用户误触后如何撤销。听见课堂把这些约束分散在页面动作、Service 接口和 Repository 门禁中。

一、completed 不是任何任务都能修改的字段
项目规则要求只有 confirmed=true 的正式任务才能进入完成流程。候选任务仍在人工复核阶段,不能绕过确认直接变成“已完成”。
页面隐藏按钮只是第一层体验保护,Repository 再次检查任务是否存在、是否已确认,才是最终数据边界。
二、页面为什么使用显式 setTaskCompleted
Repository 同时保留 toggleTask() 与 setTaskCompleted(taskId, completed)。P09 页面选择后者,先根据当前快照算出 nextCompleted,再明确写入目标状态。
显式赋值更适合撤销和重试,因为调用者知道自己希望得到什么,而不是只发出“翻转一次”这种依赖旧值的命令。
三、Repository 的状态门禁
RelationalClassroomRepository 在更新前先查询任务。记录不存在或尚未确认时直接返回 false;只有正式任务才更新 completed 字段。
const task: TaskItem | undefined = await this.findTask(taskId);
if (task === undefined || !task.confirmed) {
return false;
}
return this.updateById(TABLE_TASKS, taskId, {
completed: completed ? 1 : 0
});
这使错误调用不会污染候选数据。
四、成功后为什么必须重新读取快照
页面没有手工修改任务卡、完成数和待办数,而是在写入返回成功后调用 refreshData()。任务中心、复盘指标和历史视图重新从 canonical data 聚合。
写后刷新避免多个页面各自维护计数,也能让 RelationalStore 的真实结果成为唯一事实来源。
五、完成与恢复待办共享一条动作链
toggleConfirmedTask() 读取当前 task.completed,目标值取反。写入成功后,提示文案分别为“已标记完成”或“已恢复待办”。
这不是视觉 Toggle 的本地动画,而是一次异步持久化操作;只有 Repository 确认成功才改变用户看到的状态。
六、立即撤销需要保存旧值
成功修改后,页面记录任务 ID、修改前的 completed 和提示文案。用户点击撤销时,调用 setTaskCompleted(taskId, previousCompleted) 写回旧值。
撤销不是再盲目 toggle 一次,而是恢复明确的历史状态,语义更稳定。
七、当前撤销是单槽位,不是操作历史
页面只保存最近一次任务变更。用户连续修改两个任务后,只能撤销最后一个;刷新页面或重启应用后,这个短生命周期撤销信息也不会保留。
文章不能把它描述成完整撤销栈。若产品需要跨页面或跨重启撤销,应设计操作日志、有效期和冲突处理。
八、统计口径如何随状态变化
任务中心从当前快照重新计算 completedCount、overdueCount 和待办数量。完成一条逾期任务后,它不再计入逾期;恢复待办后,若截止时间已过,又会重新成为逾期。
复盘页的已确认任务完成度也会在刷新后重新聚合,而不是由 P09 单独加减百分比。
九、重启回读验证了什么
如果使用 RelationalStore,完成状态写入数据库后应在应用重启、页面重建和再次查询时保持。验证不能只看按钮颜色变化,还要检查重新获取的 TaskItem、统计数和复盘指标。
若存储降级到内存 Repository,则重启会丢失;界面必须继续暴露临时存储模式,不能伪装成持久化成功。

十、写入失败时当前页面有什么表现
当前方法只在 Service 返回 true 时刷新和提示;返回 false 时没有专门的失败文案。数据不会被误改,但用户可能不知道点击为何没有生效。
这是可用性缺口:后续应增加失败提示、保留原状态,并区分记录不存在、尚未确认和数据库写入异常。
十一、快速重复点击为什么危险
页面当前没有显式的“写入中”锁。若用户快速连续点击,同一张旧快照可能发出多个异步请求,返回顺序也可能与点击顺序不同。
更稳妥的方案是按任务 ID 设置 pending 状态、临时禁用操作,并在请求结束后从 Repository 回读最终值。
十二、为什么不默认做乐观 UI
乐观 UI 可以让按钮立即变化,但需要失败回滚、并发序列号和跨页面同步。当前项目选择“写成功后刷新”,交互稍慢,却减少了虚假完成状态。
对课堂待办这种非高频动作,确认式更新是合理取舍;如果以后需要离线队列,再单独引入可审计的乐观策略。
十三、toggleTask 还能用于哪里
toggleTask() 在 Repository 内部也检查 confirmed,适合调用者只关心“切换”的简单入口。但当动作需要撤销旧值、重试幂等或处理并发时,显式 setTaskCompleted 更清晰。
两者并存不是错误,关键是页面不要混用造成语义不一致。
十四、建议补充旧值条件更新
当前实现先查询再更新,中间仍可能被另一操作改写。可进一步用旧值作为更新条件:只有数据库中的 completed 等于预期旧值时才写入,并要求影响行数恰好为 1。
这类乐观并发控制能让快速点击和未来多设备同步更可诊断。
十五、验收矩阵应该覆盖什么
至少验证:候选任务拒绝完成、正式任务完成、恢复待办、完成逾期任务后统计变化、立即撤销、写入失败不改 UI、快速连点保护、重启回读,以及内存降级模式的明确提示。
每一项都应同时观察 TaskItem、任务中心统计和复盘指标,而不是只截一张按钮图片。
十六、总结
可靠的完成状态由 Repository 门禁、显式目标值、写后刷新、旧值撤销和失败反馈共同组成。听见课堂已经守住“候选不能完成”和“成功后回读”的核心边界,但单槽撤销、失败提示和快速连点保护仍有补强空间。
下一篇将审计复盘页的完成度、重点标记率、板书数与待处理数究竟从哪里来。
更多推荐
所有评论(0)