羽球搭子 HarmonyOS 实战(18):个人统计聚合与排行榜数据模型
一、排行榜首先要回答“哪些数据算数”一场活动可能同时存在未开始、计分中和已完成比赛。若统计服务消费所有场次未开始的 0:0 会被当成平局计分中的 18:16 会提前改变胜率。稳定规则是只聚合finishedAt 0的比赛参与者列表来自对局详情即使某人尚未出场也应该显示 0 场而不是从排行榜消失。个人统计不只包含胜负。胜场适合决定第一排序键胜率用于不同出场次数比较净胜分反映比分优势得失分用于解释净胜分搭档组合则揭示双打协作表现。页面不重复计算这些指标只消费统一的SessionStats。ArkUI 页面观察聚合结果的方式可参考ArkUI 状态管理概述。二、先建立可验证的统计模型PlayerStat应保存原始累计量和派生量出场、胜、负、得分、失分是原始累计净胜分和胜率可以在聚合末尾计算。把原始量保留下来页面才能解释“为什么排在这里”测试也能从逐场数据复算。指标计算方式空样本规则用途played参与的已完成场次数0样本量wins/losses胜负累计0/0主排名与战绩pointsFor所在队总得分0进攻表现pointsAgainst对手总得分0防守表现pointDiff得分 - 失分0同战绩比较winRatewins / played × 1000%不同出勤量比较SessionStats还应包含完成进度、排名后的选手列表、搭档组合、关键分洞察和摘要卡片。这样手机列表、平板侧栏和分享文本都使用同一结果避免多个页面各算一遍。三、从参与者初始化零值统计聚合开始时按参与者创建零值对象再遍历已完成比赛。不能只在比赛出现姓名时临时创建否则尚未出场的人无法显示姓名拼写差异也更难发现。比赛中的姓名若不在参与者池应跳过并记录数据一致性问题。function initPlayers(participants: string[]): PlayerStat[] { return participants.map((name: string): PlayerStat ({ name, played: 0, wins: 0, losses: 0, pointsFor: 0, pointsAgainst: 0, pointDiff: 0, winRate: 0 })) } function finishedMatches(detail: SessionDetail): MatchItem[] { return detail.matches.filter((match: MatchItem) match.finishedAt 0) }初始化后任何页面都能得到固定顺序和完整名单。最后再做排行榜排序不让“数据是否存在”和“排名如何”混在一起。四、一次比赛要同时更新双方对于双打比赛A 队两名选手都增加一次出场A 胜时两人各加一胜B 队两人各加一负得失分从各自视角写入。更新时生成新对象避免修改同一引用导致 UI 观察不到变化。function applyTeam( players: PlayerStat[], names: string[], pointsFor: number, pointsAgainst: number, won: boolean ): void { names.forEach((name: string) { const index players.findIndex((item: PlayerStat) item.name name) if (index 0) return const old players[index] players[index] { ...old, played: old.played 1, wins: old.wins (won ? 1 : 0), losses: old.losses (won ? 0 : 1), pointsFor: old.pointsFor pointsFor, pointsAgainst: old.pointsAgainst pointsAgainst } }) }每场都对 A、B 两队各调用一次。这里不处理平局因为羽毛球完赛数据不应是平局若服务端传入平局统计层应拒绝或把它标记为异常数据不能默认为 B 队获胜。五、派生指标在累计结束后统一计算胜率应以真实胜场除以出场次数保留一位小数。净胜分是累计得分减累计失分。所有场次累计后再计算可以减少浮点舍入次数也能确保页面拿到的各指标基于同一批数据。function normalizeStats(players: PlayerStat[]): PlayerStat[] { return players.map((item: PlayerStat): PlayerStat { const winRate item.played 0 ? 0 : Math.round(item.wins * 1000 / item.played) / 10 return { ...item, pointDiff: item.pointsFor - item.pointsAgainst, winRate } }) }若旧模型曾用正负数混合编码胜负迁移时应先恢复真实wins和losses不要把兼容公式继续扩散到新页面。统计公式越直接越容易与服务端结果对账。六、排行榜必须使用稳定排序键只按胜场排序时大量选手会同分JavaScript 排序在不同数据来源下还可能暴露原始顺序差异。明确排序链为胜场降序、胜率降序、净胜分降序、姓名升序。最后的姓名键不是为了业务公平而是保证完全相同战绩仍有稳定展示顺序。function rank(players: PlayerStat[]): PlayerStat[] { return players.slice().sort((left: PlayerStat, right: PlayerStat) { if (right.wins ! left.wins) return right.wins - left.wins if (right.winRate ! left.winRate) return right.winRate - left.winRate if (right.pointDiff ! left.pointDiff) return right.pointDiff - left.pointDiff return left.name.localeCompare(right.name) }) }选手战绩胜率净胜分排名理由小羽3 胜 1 负75%12胜场最高阿林2 胜 0 负100%8胜场少于小羽排其后子昂2 胜 1 负66.7%15与阿林同胜场胜率较低小周2 胜 1 负66.7%6同胜场胜率下比较净胜分排序规则需要在界面附近说明否则用户可能认为 100% 胜率理应第一。产品也可以改成先比较胜率但必须在所有端和服务端保持一致。七、双打还需要搭档维度个人榜不能回答“和谁搭档表现最好”。组合键应先对两个人名排序再用分隔符连接使小羽|阿林和阿林|小羽聚合为同一组合。组合累计出场、胜负和净胜分最后使用与个人榜类似的稳定排序。function pairKey(names: string[]): string { return names.slice().sort().join(|) } function applyPair(pairs: PairStat[], names: string[], won: boolean, diff: number): void { if (names.length 2) return const key pairKey(names) const found pairs.find((item: PairStat) item.key key) if (found undefined) { pairs.push({ key, names: names.slice().sort(), played: 1, wins: won ? 1 : 0, losses: won ? 0 : 1, pointDiff: diff, winRate: 0 }) return } found.played 1 found.wins won ? 1 : 0 found.losses won ? 0 : 1 found.pointDiff diff }姓名可能重复时生产模型应使用参与者 ID 作为组合键姓名只做展示。仅靠姓名聚合会把同名球友合并这也是账号关联后统计模型要升级的原因。八、用可手算样本验证聚合统计验收应选择能手工复算的小样本而不是导入几十场后只看榜单“像不像”。准备 4 人、3 场比赛确保每人至少出场一次包含相同胜场但不同胜率、相同胜率但不同净胜分的情况。1. 在完成 0 场时确认所有参与者显示 0 场、0%摘要提示样本不足。 2. 完成一场 21:18手算双方两人的得失分和净胜分。 3. 再完成两场使两人同胜场但胜率不同确认第二排序键生效。 4. 构造胜场、胜率都相同但净胜分不同的选手确认第三排序键生效。 5. 检查同一搭档交换左右顺序后仍聚合为一个组合。 6. 修改完赛比分后重新计算确认个人榜、搭档榜、趋势和分享文本同步变化。九、总结排行榜的可信度来自明确数据口径。只消费已完成比赛以参与者池初始化零值统计双方从各自视角累计得失分在聚合末尾计算胜率与净胜分再用稳定排序键生成个人榜双打组合则使用顺序无关的组合键。页面只渲染SessionStats分享和趋势也复用同一结果才能避免同一场比赛在不同入口出现不同排名。

相关新闻

做公司PPT最烦的不是写内容,是套模板

做公司PPT最烦的不是写内容,是套模板

近两年来,利用AI生成PPT这样的事情普及开来的速度还是比较快的。输入那么一小段需求,几十秒钟就能够弄出那么一份结构比较完整的PPT,内容方面的逻辑还算可以,排版也勉强说得过去 。 但问题出在拿到之后,许多公司皆拥有…

2026/10/11 22:36:45 阅读更多 →
Android应用换肤的终极解决方案:AndroidChangeSkin深度解析与实战指南

Android应用换肤的终极解决方案:AndroidChangeSkin深度解析与实战指南

Android应用换肤的终极解决方案:AndroidChangeSkin深度解析与实战指南 【免费下载链接】AndroidChangeSkin 一种完全无侵入的换肤方式,支持插件式和应用内,无需重启Activity. 项目地址: https://gitcode.com/gh_mirrors/an/AndroidChangeSk…

2026/10/8 19:22:25 阅读更多 →
解锁家庭音乐共享:用musikcube打造跨平台流媒体服务器

解锁家庭音乐共享:用musikcube打造跨平台流媒体服务器

解锁家庭音乐共享:用musikcube打造跨平台流媒体服务器 【免费下载链接】musikcube a cross-platform, terminal-based music player, audio engine, metadata indexer, and server in c 项目地址: https://gitcode.com/gh_mirrors/mu/musikcube 你是否厌倦了…

2026/10/8 4:51:13 阅读更多 →

最新新闻

emoji 图片与编码表维护指南:从豆腐块到精准匹配

emoji 图片与编码表维护指南:从豆腐块到精准匹配

简介:这份资源面向移动端开发者、前端工程师及文本处理研究者,系统整理了emoji表情的图形素材与编码对照数据,帮助解决跨平台表情显示异常、编码转换与数据库存储等实际问题。压缩包共468个文件,以467张png图片和1个sql数据库文件…

2026/10/15 13:03:27 阅读更多 →
context-mode详解:SQLite FTS5上下文感知搜索的核心开关

context-mode详解:SQLite FTS5上下文感知搜索的核心开关

1. “context-mode”到底是什么?别被术语唬住,它其实是开发者日常绕不开的“上下文感知开关”最近在好几个技术群和开源项目讨论区里,“context-mode”这个词高频出现,尤其和MCP、SQLite、FTS5这些词绑在一起。很多人第一反应是&a…

2026/10/15 13:03:27 阅读更多 →
AI Agent时代的能力单元:skills的本质是可验证的运行时契约

AI Agent时代的能力单元:skills的本质是可验证的运行时契约

1. “skills”不是功能菜单,而是AI Agent时代的底层能力契约最近在好几个技术群和开源项目讨论区里,看到大量开发者反复刷屏“skills”这个词——不是问“怎么学技能”,而是盯着终端里一行报错发呆:“npx claude/skills install报…

2026/10/15 13:03:27 阅读更多 →
RTD2660驱动板点屏实战:从原理图、固件到LVDS屏参调试

RTD2660驱动板点屏实战:从原理图、固件到LVDS屏参调试

简介:这份RTD2660资料大全面向嵌入式开发工程师、显示器驱动板维修人员及电子爱好者,整合了从本站多个RTD2660资料包中筛选出的程序原理图与源码资源,可解决芯片开发中原理理解、驱动编写与调试排错等实际问题。压缩包共746个文件、约30.08MB…

2026/10/15 13:03:27 阅读更多 →
CLI工具开发入门:从零构建可发布命令行应用

CLI工具开发入门:从零构建可发布命令行应用

我无法根据当前输入生成符合要求的博文。原因如下:项目标题 "impeccable" 本身是一个英文形容词(意为“无可挑剔的、完美的”),但未指明具体对象或领域。它既非技术名词、工具名、协议名,也非产品代号或项目…

2026/10/15 13:03:27 阅读更多 →
C4D插件Octane 4.0 RC7上手指南:安装、调参与避坑

C4D插件Octane 4.0 RC7上手指南:安装、调参与避坑

简介:面向C4D用户的OctaneRender 4.0 RC7渲染器资源包,覆盖C4D R15至R20等主流版本,专为需要GPU加速、实时预览和照片级输出的设计师打造。压缩包共605个文件,内含184张png贴图、164个res资源文件、80个str字符串、78个h头文件与6…

2026/10/15 13:02:27 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →