羽球搭子 HarmonyOS 实战(17):比分撤销与边界校验
一、撤销不是把较大的一方减一分比分从 8:7 变成 8:8 后发现误触正确撤销结果应该回到 8:7仅比较当前大小会把 A 队减成 7:8。要恢复最近一次动作必须记录“哪支队伍获得了这一分”而不是根据最终比分猜测。多场比赛同时计分时历史还要按比赛标识隔离否则在第二场点击撤销可能改动第一场。一个轻量方案是MapmatchId, Team[]每次有效1将队伍压栈撤销时弹出栈顶并把对应比分减一。空栈、比赛不存在、比赛已结束都直接拒绝。ArkUI 的状态更新仍使用不可变数组替换具体状态管理原则可参考ArkUI 状态管理概述。二、先定义撤销的语义边界用户通常把“撤销”理解为回退最近一次按钮加分而不是恢复任意历史快照。直接录入 15:12、重置为 0:0、从云端覆盖比分都会建立新的基线旧的逐分历史已经无法解释应立即清空。比赛结束后也不继续撤销若产品需要修改完赛比分应走独立的“编辑结果”流程并记录审计事件。操作是否写入动作栈是否清空动作栈是否允许撤销有效 1是记录 A 或 B否是在 0 分执行 -1否否不改变状态直接录入比分否是从新基线重新记录重置比分否是重置前历史作废比赛结束否是否云端快照覆盖否是以服务端快照为基线这种定义简单且可向用户解释。如果要支持多步重做还需要双栈和更多动作类型但普通球场快记没有必要一开始就引入完整命令系统。三、只记录真正生效的加分动作栈必须在比分确认变化后写入。比赛已结束、标识无效或变化量为零时不能记录减分也不能伪装成可撤销的加分。记录函数同时接收旧比分和新比分只有目标队伍确实增加才压栈。private recordHistory( matchId: string, team: Team, delta: number, before: Score, after: Score ): void { if (delta 0) return const changed team A ? after.a before.a : after.b before.b if (!changed) return const stack this.scoreHistory.get(matchId) ?? [] stack.push(team) this.scoreHistory.set(matchId, stack) }如果未来支持“一次加 2 分”动作记录最好升级成{ team, delta, before }否则一次撤销只能减一。当前所有快捷按钮都以单分为单位队伍栈已经足够但要把这个前提写进模型约束。四、撤销按栈顶动作回退撤销先检查栈是否存在再检查比赛仍处于playing。顺序很重要不能先pop再发现比赛已结束否则历史被无声丢弃。比分回退使用非负收敛完成替换后才弹栈如果 UI 更新过程中抛错历史仍保留可再次尝试。undoLastScore(matchId: string): UndoResult { const stack this.scoreHistory.get(matchId) if (stack undefined || stack.length 0) { return { ok: false, message: 暂无可撤销的得分 } } const index this.matches.findIndex((item: ScoreMatch) item.id matchId) if (index 0 || this.matches[index].status ! playing) { return { ok: false, message: 当前比赛不可修改 } } const team stack[stack.length - 1] const current this.matches[index] const a team A ? Math.max(0, current.playerA.score - 1) : current.playerA.score const b team B ? Math.max(0, current.playerB.score - 1) : current.playerB.score this.replaceScore(index, a, b) stack.pop() return { ok: true, message: 已撤销 ${team} 队上一分 } }返回结构化结果比在领域函数里直接弹 Toast 更容易测试。页面根据ok决定反馈样式领域层只描述发生了什么。五、比分边界必须在所有入口一致除加减按钮外快捷比分、文本输入、语音指令和云端事件都可能改变比分。每个入口各写一套校验会逐渐分叉。可以把整数解析、范围收敛和状态检查封装成共同函数任何来源先得到ScoreValidation通过后再更新。function validateScore(rawA: number, rawB: number, maxScore: number): ScoreValidation { if (!Number.isFinite(rawA) || !Number.isFinite(rawB)) { return { valid: false, reason: 比分必须是数字 } } if (!Number.isInteger(rawA) || !Number.isInteger(rawB)) { return { valid: false, reason: 比分必须是整数 } } if (rawA 0 || rawB 0) { return { valid: false, reason: 比分不能为负数 } } if (rawA maxScore || rawB maxScore) { return { valid: false, reason: 比分不能超过 ${maxScore} } } return { valid: true, scoreA: rawA, scoreB: rawB } }边界输入处理原因8.5:7拒绝逐分计分只接受整数NaN:10拒绝文本解析失败51:20普通制拒绝或明确收敛超出产品保护上限22:20普通 21 分进入结束态满足到点且领先 2 分21:20抢 21进入结束态到点且不平分六、重置与直接录入要切断旧历史重置比分后若保留[A, B, A]下一次撤销会试图从 0:0 减分直接录入 18:16 后保留旧栈则撤销得到的“上一分”未必对应 18:16 的真实形成过程。两种操作都应先确认比赛可修改再替换比分并删除该场历史。resetScore(matchId: string): boolean { const match this.findPlayingMatch(matchId) if (match undefined) return false if (match.playerA.score 0 match.playerB.score 0) return false this.updateMatch(matchId, this.copyWithScore(match, 0, 0)) this.scoreHistory.delete(matchId) return true } applyDirectScore(matchId: string, a: number, b: number): boolean { const checked validateScore(a, b, this.maxScoreOf(matchId)) if (!checked.valid) return false this.updateScoreFromBaseline(matchId, checked.scoreA!, checked.scoreB!) this.scoreHistory.delete(matchId) return true }若需要撤销“重置”本身应把重置建模成完整快照命令而不是继续混用队伍栈。两种撤销语义不要同时隐藏在同一个按钮里。七、自动结束后的纠错要走独立流程普通 21 分制在 22:20 自动结束系统会清空动作栈并保存结果。如果用户此时发现最后一分误触直接撤销会让云端和统计页不知道比赛已从完成退回进行中。更安全的交互是进入“修正结果”弹窗展示原比分和新比分提交后产生score.updated事件并重新计算胜方、结束时间和统计。interface ScoreCorrection { matchId: string previousScoreA: number previousScoreB: number scoreA: number scoreB: number reason: string } function createCorrection(match: MatchItem, a: number, b: number): ScoreCorrection { return { matchId: match.id, previousScoreA: match.scoreA, previousScoreB: match.scoreB, scoreA: a, scoreB: b, reason: manual_correction } }这条路径比“让结束态重新可点击”多一步但能保留审计信息也便于云端按版本处理冲突。八、用动作序列而不是单点比分验收撤销测试应输入完整动作序列。只检查某个最终比分无法证明栈顺序正确也无法覆盖多场隔离和基线切换。1. A、B、B 依次得分确认比分 1:2连续撤销得到 1:1、1:0、0:0。 2. 在空栈继续撤销确认仅提示且比分不变化。 3. 同时创建两场比赛在两场分别加分确认撤销只影响目标场次。 4. 形成 6:4 后直接录入 15:12再撤销确认提示空栈而不是回到 14:12。 5. 重置后再次加分确认新动作栈从空开始。 6. 比赛结束后点击撤销确认被拒绝通过结果修正流程更新时产生独立记录。九、总结比分撤销的可靠性来自清晰语义它回退最近一次真实加分不猜测较大比分不跨比赛共享历史不越过直接录入和重置形成的新基线也不修改已经结束的比赛。按场次隔离的动作栈配合统一边界校验足以覆盖球场快记的大多数纠错场景需要修改完赛结果时再使用带审计信息的独立流程。

相关新闻

羽球搭子 HarmonyOS 实战(16):实时计分页的状态机设计

羽球搭子 HarmonyOS 实战(16):实时计分页的状态机设计

一、计分页最怕状态由按钮文字暗示 实时计分看起来只是两个“1”按钮,实际同时约束比赛是否进行、采用哪种赛制、比分能否修改、是否达到结束条件、是否已经落盘以及结束后是否允许重复提交。如果这些规则散落在多个点击回调中,21:20、30:29、抢 21 和普…

2026/7/22 23:26:24阅读更多 →
多步骤智能体任务断层?Kimi-K3 前沿推理专用模型,DMXAPI对接300多款API,AIAgent 开发必备

多步骤智能体任务断层?Kimi-K3 前沿推理专用模型,DMXAPI对接300多款API,AIAgent 开发必备

AI 智能体开发过程中,多步骤链式任务极易出现逻辑断层、步骤缺失、任务中断问题,严重影响智能体落地效果。Kimi-K3 是前沿推理专用模型,长程链式任务推理连贯稳定,彻底解决智能体任务断层难题。DMXAPI 平台入账快速、流程高效&…

2026/7/22 23:26:24阅读更多 →
计算机毕业设计之智慧篮球馆预约

计算机毕业设计之智慧篮球馆预约

近些年来,随着科技的飞速发展,互联网的普及逐渐延伸到各行各业中,给人们生活带来了十分的便利,智慧篮球馆预约利用计算机网络实现信息化管理,使整个智慧篮球馆预约的发展和服务水平有显著提升。本文拟采用Eclipse开发工…

2026/7/22 23:26:24阅读更多 →
深入解析MCU I/O引脚复用与Flash SECDED ECC机制:原理、配置与安全实践

深入解析MCU I/O引脚复用与Flash SECDED ECC机制:原理、配置与安全实践

1. 项目概述与核心价值在嵌入式微控制器(MCU)的世界里,尤其是面对汽车电子、工业控制这类对可靠性和资源利用率要求极高的领域,有两项底层技术是每一位资深工程师都必须吃透的:I/O引脚复用(Pin Multiplexin…

2026/7/23 0:26:35阅读更多 →
【Bug已解决】Support NexusQuant KV cache compression for memory reduction 解决方案

【Bug已解决】Support NexusQuant KV cache compression for memory reduction 解决方案

【Bug已解决】Support NexusQuant KV cache compression for memory reduction 解决方案 一、现象长什么样 在 DeepSpeed 做长上下文推理/训练时,KV cache(注意力键值缓存)是显存大户:序列越长、batch 越大,KV cache 占…

2026/7/23 0:24:35阅读更多 →
【Bug已解决】[REQUEST] Add Trackio as a New Backend for Experiment Monitoring 解决方案

【Bug已解决】[REQUEST] Add Trackio as a New Backend for Experiment Monitoring 解决方案

【Bug已解决】[REQUEST] Add Trackio as a New Backend for Experiment Monitoring 解决方案 一、现象长什么样 这是一个功能请求(feature request):用户希望 DeepSpeed 的实验监控(experiment monitoring)支持一个新的…

2026/7/23 0:24:35阅读更多 →
栈的应用(表达式求值)

栈的应用(表达式求值)

文章目录三种表达式的形式中缀表达式 转 后缀表达式手算方法机算中缀表达式 转 前缀表达式手算方法机算总结算数表达式由三部分组成:操作数、运算符、界限符(界限符是必不可少的,反映了计算的先后顺序)三种表达式的形式 首先要分…

2026/7/23 0:24:35阅读更多 →
开源大模型生态盘点:从Llama到Qwen的选型指南

开源大模型生态盘点:从Llama到Qwen的选型指南

打开 Hugging Face 的模型榜单,前排几乎每周都在换。Llama、Qwen、DeepSeek、Mistral、GLM……对想在自己的项目里跑一个开源模型的开发者来说,问题早已不是"有没有得选",而是"选错了要多花多少冤枉钱"。这篇文章按实际落…

2026/7/23 0:24:35阅读更多 →
AI媒体生产:从机器写作到智能编审流程

AI媒体生产:从机器写作到智能编审流程

媒体行业用机器写稿比大多数人想象得早。财经快讯里"某公司今日发布财报,营收同比增长 X%"这类句子,十年前就是模板生成的。变化发生在最近两三年:大模型让机器从"填数字"进化到"写整篇",同时把编审…

2026/7/23 0:24:35阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:00:28阅读更多 →
从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:28阅读更多 →
油泥处理设备哪里能买到

油泥处理设备哪里能买到

油泥处理设备哪里有?这是许多从事油田、炼化、清罐业务的从业者最关心的问题。根据河南三丰环保设备有限公司的行业经验,选购油泥处理设备的核心在于设备能否适配当地环保法规与原料特性,而非单纯看价格。该公司总经理王钦田先生指出&#xf…

2026/7/23 0:00:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/22 22:56:18阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/22 18:55:50阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/22 18:55:50阅读更多 →