# equessass **Repository Path**: baeeqregist/equessass ## Basic Information - **Project Name**: equessass - **Description**: 马术比赛 计时记分系统 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-10 - **Last Updated**: 2026-07-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 马术场地障碍竞赛服务系统 基于 [国际马联(FEI)场地障碍规则 27 版(2022)](docs/场地障碍规则27版.pdf) 设计的赛事数字化服务平台。需求以 [`docs/dev.md`](docs/dev.md) 为原始需求说明,本文档在其基础上补充系统设计、规则细节与开发计划。 > 规则依据:`docs/场地障碍规则27版.pdf`(2022-01-01 生效,部分条款 2023-01-01 更新) > 原始需求:`docs/dev.md` --- ## 目录 - [项目背景与目标](#项目背景与目标) - [系统角色](#系统角色) - [系统组成](#系统组成) - [核心数据模型](#核心数据模型) - [功能需求](#功能需求) - [核心业务规则摘要](#核心业务规则摘要) - [系统架构](#系统架构) - [技术方案](#技术方案) - [MQTT 与数据发布](#mqtt-与数据发布) - [与 FEI 规则的关系](#与-fei-规则的关系) - [开发计划](#开发计划) - [里程碑](#里程碑) - [风险与依赖](#风险与依赖) - [参考文档](#参考文档) --- ## 项目背景与目标 场地障碍赛规则体系复杂,涵盖多种赛制(单轮、两轮、两段、附加赛、团体赛等),各赛段的时间、判罚、排名规则各不相同。本系统聚焦**赛事数据管理与成绩发布**,作为成绩汇聚、规则计算与信息分发的中枢,不替代现场执裁系统。 **建设目标:** 1. **数据中枢**:统一管理赛事、项目、场次、赛段、人马组合及成绩全链路数据 2. **规则计算**:支持按 FEI 规则自动计算排名、时间罚分、晋级(可选自由编辑模式) 3. **现场联动**:数据库变更后导出 `/res` JSON 快照,经 MQTT 通知大屏与 H5 拉取更新 4. **公众触达**:H5 移动端发布赛程、成绩、奖牌信息 5. **赛段精确**:允许时间等时间参数精确到赛段级别,适配两段赛等复杂赛制 --- ## 系统角色 > 来源:[`docs/dev.md`](docs/dev.md) 第 1 节 | 角色 | 职责 | |------|------| | 系统管理员 | 系统配置、用户权限、数据字典、赛事基础数据维护 | | 计时记分操作员 | 现场计时记分、大屏控制指令发送、成绩采集与录入 | | 观众 / 媒体 | 赛程、成绩、大屏公示(只读) | 场外裁判、路线设计师、兽医等角色使用其他执裁/管理系统完成现场工作,本系统通过成绩录入接口接收或同步其产出数据。 --- ## 系统组成 > 来源:[`docs/dev.md`](docs/dev.md) 第 2 节。原文称「三大子系统」,实际包含管理后台、计时记分与大屏控制、大屏显示、移动发布四个部分,下文按四个子系统展开。 ``` ┌─────────────────────────────────────────────────────────────┐ │ 管理后台子系统 │ │ 基础数据 │ 报名报项 │ 成绩处理(出场编排 / 录入 / 晋级) │ └──────────────────────────┬──────────────────────────────────┘ │ REST API ┌──────────────────────────▼──────────────────────────────────┐ │ 计时记分与大屏控制子系统 │ │ 现场成绩采集 │ 计时控制 │ 大屏指令编排 │ MQTT 消息发布 │ └──────────────────────────┬──────────────────────────────────┘ │ MQTT ┌────────────────┴────────────────┐ ▼ ▼ ┌─────────────────────┐ ┌─────────────────────┐ │ 大屏显示子系统 │ │ 移动发布子系统 │ │ 订阅 MQTT 更新大屏 │ │ H5 赛程/成绩/奖牌 │ └─────────────────────┘ └─────────────────────┘ ``` ### 一、管理后台子系统 #### 1. 基础数据 负责管理数据字典、赛事信息、比赛项目、比赛场次、参赛队伍、人马组合。 - [ ] 数据字典:赛制类型、判罚表、马匹品种、国家/地区等 - [ ] 赛事信息:名称、日期、场地、室内/室外、星级 - [ ] 比赛项目:如 1.20m 特殊两段赛、1.40m 个人赛、国家杯团体赛 - [ ] 比赛场次:第一轮、第二轮、附加赛等 - [ ] **赛段**:各场次下的第一段、第二段等(见[核心数据模型](#核心数据模型)) - [ ] 参赛队伍、人马组合(运动员 + 马匹) #### 2. 报名报项 负责管理比赛项目的参赛队伍和参赛人员,确立各比赛项目**首轮、首场**出场名单。 - [ ] 管理各比赛项目的参赛队伍与参赛人员 - [ ] 报名审核、替换、退赛 - [ ] 生成首轮、首场出场名单 #### 3. 成绩处理 负责管理各项目、各场次、各赛段、各人马组合的比赛成绩及排名。 **第 1 步:编排出场顺序** - [ ] 方便地修改人马组合在各个比赛场次和赛段的出场顺序 - [ ] 根据比赛规则,从上一轮比赛成绩自动计算下一轮出场顺序(倒序、抽签等) - [ ] 特殊调整:掉蹄铁延后、同骑手两马间隔等 **第 2 步:录入成绩** > `dev.md` 描述了两种录入方式。实现上统一为:**默认规则计算**,计算后界面支持手工改值;对改过的记录可打开**锁定开关**,阻止后续自动重算覆盖。详见 [`design/d01/10-score-entry-lock.md`](design/d01/10-score-entry-lock.md)。 | 能力 | 说明 | |------|------| | 规则计算(默认) | 录入基础成绩后,系统按 FEI 规则计算罚分、时间罚分、排名 | | 手工覆盖 | 计算结果可在界面直接修改并保存 | | 锁定开关 | `calc_locked=1` 时,批量/触发式自动重算**跳过**该条,保留人工值 | | 外部导入 | 支持从外部执裁系统导入基础成绩后再走规则计算 | - [ ] 赛段/场次成绩表 + 锁定列(Switch) - [ ] 重新计算(尊重锁定) - [ ] 支持从外部执裁系统导入成绩 **第 3 步:晋级** - [ ] 根据规则自动晋级(两轮赛、两段赛、附加赛资格等) - [ ] 操作人员手工调整晋级名单 --- ### 二、计时记分与大屏控制子系统 负责完成比赛现场的大屏控制与成绩采集,与大屏显示等系统之间通过 **MQTT** 发送消息报文进行互操作。 - [ ] 当前场次/赛段选择与切换 - [ ] 成绩实时采集并**直接写入数据库** - [ ] DB 变更后触发 JSON 快照与 MQTT 数据变更通知 - [ ] 45 秒倒计时控制与状态同步 - [ ] 当前选手信息维护(马号、骑手、队伍) - [ ] 大屏显示指令编排(成绩榜、当前选手、倒计时、全屏切换) - [ ] MQTT 消息发布 - [ ] 离线缓存,网络恢复后同步至管理后台 --- ### 三、大屏显示子系统 通过 MQTT 消息命令,更新显示大屏。采用独立 `display` 构建入口(见[技术方案 · 大屏子系统独立构建的原因](#大屏子系统独立构建的原因))。 - [ ] 订阅 MQTT **数据变更**主题,按需从 `/res` 拉取 JSON 更新页面 - [ ] 订阅 MQTT **显示指令**(可选,计时端控屏) - [ ] 成绩榜实时滚动更新 - [ ] 当前选手信息展示 - [ ] 45 秒倒计时显示 - [ ] 全屏/分屏模式切换 - [ ] 与管理后台、控制端解耦,可部署多套大屏 --- ### 四、移动发布子系统 通过适配手机浏览器的 H5 页面向观众发布赛事信息、竞赛计划、比赛成绩、奖牌排名等信息。 - [ ] 手机浏览器 H5 适配 - [ ] 赛事概览与竞赛计划 - [ ] 各项目实时成绩与排名 - [ ] 奖牌榜 / 团体排名 - [ ] 只读;订阅 MQTT 数据变更,从 `/res` 拉取 JSON(赛程、成绩、奖牌) --- ## 核心数据模型 ### 层级关系 在 `dev.md` 所述「赛事 → 比赛项目 → 比赛场次」基础上,补充**赛段**层级。允许时间等时间参数挂在赛段上,而非项目或场次。 ``` 赛事 (Event) └── 比赛项目 (Competition) 例:1.20m 特殊两段赛 ├── 赛制类型、团体/个人 └── 比赛场次 (Session) 例:第一轮、附加赛 └── 赛段 (Phase) 例:第一段、第二段 ├── 路线长度(米) ├── 规定速度(米/分钟) ├── 判罚表(表 A / 表 C) ├── 允许时间(秒) ← 赛段级属性 ├── 限制时间(秒) ← 赛段级属性 └── 障碍配置(可选) ``` ### 允许时间(重要修正) **允许时间是每个比赛项目中每个赛段的独立属性**,不是项目级或场次级的单一值。例如 1.20m 特殊两段赛中,两个赛段的允许时间各不相同。 | 概念 | 说明 | |------|------| | 计算方式 | 允许时间 = 该赛段路线长度 ÷ 该赛段规定速度(规则 227、附录二) | | 限制时间 | 该赛段允许时间 × 2(规则 228) | | 独立性 | 同一场比赛项目下,不同赛段可有不同的路线长度、速度和允许时间 | **示例:1.20m 特殊两段赛(规则 274.2)** | 赛段 | 障碍 | 速度 | 允许时间 | |------|------|------|----------| | 第一段 | 5–7 道,表 A 不争时 | 独立设定 | 按第一段路线单独计算 | | 第二段 | 余下障碍至 11–13 道,表 A 争时 | 可与第一段不同 | 按第二段路线单独计算 | 第一段超出允许时间将打铃停止(规则 274.1.3–1.4),选手不进入第二段排名;第二段使用自己的允许时间独立判罚超时。 **示例:单轮个人赛** 一个场次包含一个赛段,该赛段的允许时间即本场有效时间。 **示例:两轮赛** 第一轮场次、第二轮场次各自配置赛段与允许时间,两轮路线长度不同则允许时间不同。 ### 团体赛与个人赛联动(同高度) 同一障碍高度级别下,**通常同时设立团体赛与个人赛**,二者成绩与出场存在联动关系(国内赛事常见安排,区别于国际马联国家杯独立两轮团体骑行规则)。 ``` 同高度级别(如 1.40m) ├── 个人赛(2 轮) │ ├── 第 1 轮:正常比赛、产生个人成绩 │ └── 第 2 轮:按第 1 轮成绩倒序出场 └── 团体赛 └── 成绩来源:个人赛第 1 轮(按队伍规则汇总,与个人赛第 1 轮为同一批成绩数据) ``` | 规则 | 说明 | |------|------| | 成绩同源 | 团体赛排名所依据的队员成绩 = 个人赛**第 1 轮**成绩,不另行比赛 | | 团体汇总 | 按队伍取最佳 N 名队员(如 3 人)第 1 轮罚分/时间累加,得到团体名次 | | 个人第 2 轮出场 | 个人赛第 2 轮出场顺序 = 个人赛第 1 轮成绩**倒序**(成绩越好越靠后出场) | | 数据一致性 | 个人赛第 1 轮成绩变更时,应同步重算团体赛成绩与个人赛第 2 轮出场顺序 | 系统通过 `competition_link` 关联同高度的个人赛与团体赛项目,并在场次上配置成绩来源与出场规则引用(见 [`docs/db.md`](docs/db.md))。 ### 成绩数据 ``` 成绩 (Result) ├── 关联:比赛项目 + 场次 + 赛段 + 人马组合 ├── 罚分(障碍罚分) ├── 时间(秒,含校正时间) ├── 时间罚分(表 A 超时每秒 1 分) ├── 状态:完成 / 淘汰 / 退赛 / 未开始 ├── 赛段排名、场次排名、项目排名 └── 是否晋级下一赛段/场次 ``` --- ## 功能需求 ### 规则计算引擎(管理后台内置) 在「规则计算模式」下,根据 FEI 规则 27 版自动处理: #### 表 A(规则 236–238) | 失误 | 处罚 | |------|------| | 第一次不服从 | 4 分 | | 碰落障碍 | 4 分 | | 水障失误 | 4 分 | | 落马/马匹跌倒 | 淘汰 | | 第二次不服从 | 淘汰 | | 超限制时间 | 淘汰 | | 超允许时间 | 每超 1 秒 1 分(**按当前赛段允许时间计算**) | #### 表 C(规则 239) | 失误 | 处罚 | |------|------| | 碰落障碍/水障 | 室外 +4 秒 / 室内 +3 秒 | | 不服从 + 碰落 | +6 秒校正 | | 第二次不服从 | 淘汰 | | 超限制时间 | 淘汰(2 或 3 分钟,按赛段路线长度) | #### 赛制支持优先级 | 优先级 | 赛制 | 赛段特征 | |--------|------|----------| | P0 | 单轮个人赛 / 大奖赛 | 单赛段,单允许时间 | | P0 | 附加赛 | 独立场次,独立允许时间 | | P1 | 常规两段赛 | 两赛段,各自允许时间,第一段淘汰机制 | | P1 | 特殊两段赛 | 两赛段,不同判罚表组合 | | P1 | 两轮个人赛 | 两场次,第 2 轮倒序出场 | | P1 | 团体+个人联动赛 | 个人 2 轮;团体成绩取自个人第 1 轮汇总 | | P1 | 国家杯团体赛 | 独立两轮团体骑行 + 团体罚分累加(FEI 规则 264) | | P2 | 争胜赛 / 分组赛 | 多场次晋级 | | P3 | 打杆夺时 / 计分赛等 | 固定时间或特殊计分 | ### 出场顺序规则(可配置) - [ ] 首轮抽签(规则 252) - [ ] 成绩倒序(规则 245.5、273) - [ ] **联动倒序**:个人赛第 2 轮按关联个人赛第 1 轮成绩倒序(团体赛同批成绩) - [ ] 团体赛按队员序号分批(规则 264.6,国家杯等独立团体骑行时) - [ ] 同马/同骑手轮换(规则 252.5) ### 排名计算 - [ ] 赛段排名:按当前赛段判罚表与成绩 - [ ] 场次排名:合并场次内各赛段成绩(按赛制规则) - [ ] 项目排名:合并各场次成绩 - [ ] 团体排名(联动模式):由关联个人赛第 1 轮 `session_result` 按队伍汇总 - [ ] 团体排名(国家杯模式):最佳 N 人罚分累加(规则 264.9) - [ ] 并列处理:时间决胜、附加赛资格标记 --- ## 核心业务规则摘要 ### 计时 - 赛段时间:从过起点到该赛段终点(规则 226) - 校正时间:不服从导致障碍移动 +6 秒(规则 232) - 录像不得用于建立比赛时间(规则 229.5) ### 速度(规则 234) - 国际赛:325–400 m/min,按赛段独立配置 - 国家杯:5* 室外 400 m/min,1* 室外 350 m/min ### 两段赛要点(规则 274) - 第一段终点 = 第二段起点,期间不中断 - 第一段有罚分或超时 → 打铃停止,不参与第二段排名 - 第一段无罚分 → 继续第二段 - **两赛段允许时间分别计算、分别判罚** ### 附加赛(规则 245–246) - 零罚分并列第一进入附加赛 - 附加赛为独立场次,配置独立赛段与允许时间 ### 团体赛与个人赛联动(业务规则) 1. 创建同高度 `competition_link`,绑定个人赛项目与团体赛项目。 2. 个人赛第 1 轮正常录入 `phase_result` → `session_result`。 3. 个人赛第 1 轮结束后: - 自动汇总生成团体赛 `team_session_result`(来源:个人赛第 1 轮 `session_id`)。 - 自动生成个人赛第 2 轮 `start_order`(规则:`reverse_ref_session`,引用个人赛第 1 轮)。 4. 个人赛第 2 轮结束后,汇总个人项目最终成绩;团体成绩不因第 2 轮变化(除非规程另有规定)。 --- ## 系统架构 ``` ┌──────────────────────────────────────────────────────────────┐ │ 管理后台(Web) │ │ 基础数据 │ 报名报项 │ 成绩处理 │ 规则引擎 │ └────────────────────────────┬─────────────────────────────────┘ │ REST API ┌────────────────────────────▼─────────────────────────────────┐ │ 计时记分与大屏控制(现场 Web / 桌面) │ │ 成绩采集 │ 计时控制 │ MQTT Publisher │ └────────────────────────────┬─────────────────────────────────┘ │ MQTT Broker ┌──────────────┴──────────────┐ ▼ ▼ ┌─────────────────────────┐ ┌─────────────────────────┐ │ 大屏显示子系统 │ │ 移动发布子系统(H5) │ │ MQTT Subscriber │ │ REST API(只读) │ └─────────────────────────┘ └─────────────────────────┘ 数据层:MySQL 8.4 │ MQTT Broker(EMQX 等) ``` --- ## 技术方案 > 来源:[`docs/dev.md`](docs/dev.md) 技术方案章节 ### 技术栈 | 层次 | 选型 | |------|------| | 后端 | Python 3.8 + FastAPI + JWT | | 前端 | Vue + Vite + Vue Router + Axios | | 数据库 | MySQL 8.4 | | 配置中心 | Nacos(全部运行参数) | | 消息 | MQTT(计时记分与大屏控制子系统) | | UI | ui-ux-pro-max,主色 `#2563eb` | ### 部署模式 - **前后端分离**开发,**共用同一服务端口**发布 - 后端 FastAPI 同时承载 API 与前端静态资源,避免跨域 ``` 部署前 启动时 │ │ ▼ ▼ 用户在 Nacos 建立参数节点 ① 连接 Nacos,拉取全部配置 │ │ │ ▼ │ ② 初始化数据库、MQTT 等 │ │ │ ▼ │ ③ 启动 FastAPI,对外服务 │ │ └──────────────────────────────┘ ┌─────────────────────────────┐ │ FastAPI 单端口服务 │ ├─────────────────────────────┤ 浏览器 ──────────►│ /tsapp/* → 前端 dist 静态页 │ │ /api/* → 业务 API(JWT) │ │ /login → 登录 │ │ /regist → 注册 │ │ /res/* → 系统生成数据文件 │ └──────────────┬──────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ MySQL 8.4 Nacos MQTT Broker (配置中心) ``` ### 配置中心(Nacos) > 来源:[`docs/dev.md`](docs/dev.md) 技术方案第 6 条 **所有运行参数均从 Nacos 读取**,不在代码或本地配置文件中硬编码业务参数。 #### 部署前准备 系统部署前,运维人员在 Nacos 上**预先建立参数节点**(Data ID / Group),写入本环境所需的全部配置。 **只需修改两个顶层部署参数**,其余路径使用相对配置,由后端启动时拼接。**HTTPS/SSL 不在 Nacos 配置**,由 Nginx 反向代理终结;前端 MQTT 根据 `window.location` 自动选择 `ws` / `wss`。 | 顶层参数 | 说明 | |----------|------| | `deploy.domain` | 对外域名(后端生成绝对链接等;页面与 API 经 Nginx 同域访问) | | `deploy.root_dir` | 运行时数据根目录(`/res`、日志、引导配置;**推荐 `D:/data/equsass` 或 `/data/equsass`**,见下) | **部署数据目录**(来源:[`docs/dev.md`](docs/dev.md) §9):Nacos 最小引导配置(`nacos-bootstrap.json`)与 `/res` 物理目录统一部署在仓库外的 `data/equsass` 下,与 Git 源码分离。系统生成代码放入仓库内 [`sys/`](sys/) 目录(默认不提交)。 | 平台 | `deploy.root_dir` | |------|-------------------| | Windows | 当前磁盘根下 `data/equsass`,如 `D:/data/equsass` | | Linux | `/data/equsass` | 详细目录结构与 `sys/` 说明见 [`docs/nacos-config.md`](docs/nacos-config.md)。 详细字段、Nginx 示例与 MQTT 拼接规则见 [`docs/nacos-config.md`](docs/nacos-config.md)、模板 [`docs/nacos-config.example.json`](docs/nacos-config.example.json)。 | 配置项 | 示例 | |--------|------| | 部署根参数 | `deploy.domain`、`deploy.root_dir` | | 后端监听 | `server.http_port`(仅 HTTP,Nginx 反代) | | API 路径 | `urls.api_path_prefix`(前端相对路径 `/api`) | | MySQL 连接 | `mysql.host`、`mysql.port`、`mysql.database`、`mysql.user`、`mysql.password` | | JWT | `jwt.secret`、`jwt.expire_hours` | | MQTT 服务端 | `mqtt.server.host`、`mqtt.server.port`(TCP,后端发布) | | MQTT Web 路径 | `mqtt.web.path`(默认 `/mqtt`,前端按页面协议拼 ws/wss) | | NTP 对时 | `ntp.ntp_address`、`ntp.sync_interval_sec`、`ntp.sync_sample_count`(Web 端多次采样 + 往返补偿,见 [`docs/nacos-config.md`](docs/nacos-config.md)) | | 资源目录 | `static.res.path`(绝对路径,默认 `/data/res` 或 Windows `D:/data/res`) | | 前端静态目录 | `static.tsapp.admin.rel_path`、`static.tsapp.display.rel_path` | #### 启动顺序 后端启动时**必须先连接 Nacos、读取并加载参数**,完成后再执行后续初始化: ``` 1. 读取本地引导配置(仅 Nacos 连接信息:地址、命名空间、Data ID、Group) 2. 连接 Nacos,拉取参数节点全部配置 3. 用 Nacos 配置初始化:数据库连接池、JWT、MQTT 客户端、静态目录路径等 4. 注册路由、挂载 /tsapp 与 /res 5. 开始对外提供服务 ``` 若 Nacos 不可用或参数节点缺失,**拒绝启动**并输出明确错误,避免以错误配置运行。 #### 设计原则 - 环境差异(开发 / 测试 / 生产)通过 Nacos **不同命名空间或不同 Data ID** 区分,同一制品多环境部署 - 敏感信息(数据库密码、JWT 密钥)仅存于 Nacos,不提交至代码仓库 - 运行期参数变更:修改 Nacos 后重启服务生效(后续可扩展热更新) ### API 路由规划 | 路由 | 说明 | 鉴权 | |------|------|------| | `/regist` | 用户注册 | 否 | | `/login` | 用户登录,签发 JWT Token | 否 | | `/api/...` | 全部业务接口,对前端提供服务 | **是**,须携带有效 JWT Token | 前端 Axios 请求统一走 `/api/...` 前缀,登录后将 Token 写入请求头,由后端中间件校验。 ### 全局资源虚拟目录 | 路径 | 用途 | |------|------| | `/res` | 系统生成的 **JSON 数据快照**(成绩榜、出场序、排名等),供大屏/H5 拉取 | | `/tsapp` | 映射前端编译产物 `dist/` 目录,作为 Web 应用入口 | 访问示例: - 前端页面:`http://host:port/tsapp/` - 业务接口:`http://host:port/api/events` - 成绩快照:`http://host:port/res/evt2026/scoreboard/phase_12/v000045.json` ### 数据更新策略 > 来源:[`dev.md`](docs/dev.md) 技术方案第 7 条 · 详见 [`docs/data-sync.md`](docs/data-sync.md) | 步骤 | 说明 | |------|------| | 1. 写库 | 管理后台、计时记分控制端通过 `/api` **直接更新 MySQL** | | 2. 导出 | 变更后按编码规则在 `/res` 下写入 JSON 快照 | | 3. 通知 | 通过 MQTT 发布数据变更消息(含 `res_url`、`data_type`、`scope_key`) | | 4. 读端 | 大屏、移动 H5 订阅 MQTT,若与当前页相关则 `GET /res/...json` 刷新 | ``` 管理后台/计时端 ──写──► MySQL ──► /res/*.json + MQTT(data/change) │ 大屏 / 移动 H5 ◄─┘ 订阅并按需拉取 JSON ``` --- - 使用 **ui-ux-pro-max** 作为 UI 组件/设计规范 - 主题主色:**#2563eb** - 推荐采用 **一个前端工程、两个构建入口(entry)**: - `admin`:管理后台、计时记分控制端(走 `/tsapp`,JWT 鉴权,调用 `/api/...`) - `display`:大屏显示端(全屏、无登录,仅订阅 MQTT 更新画面) - 两个入口可共用组件库与工具函数,但产出**两份独立构建产物**,分别部署、互不影响 - 管理端与移动 H5 可在 `admin` 入口内按路由区分;大屏必须使用 `display` 入口,不与操作端混用 ### 大屏子系统独立构建的原因 | 维度 | 说明 | |------|------| | 部署场景 | 控制端在裁判台操作,大屏在赛场边全屏常亮,交互与权限需求完全不同 | | 多屏并行 | 成绩榜、当前选手、倒计时等可各部署一个 `display` 实例,订阅不同 MQTT Topic | | 故障隔离 | 大屏浏览器异常不影响计时记分控制端继续采集与发布消息 | | 性能 | 大屏页面极简、无 API 轮询;控制端承担完整业务交互 | | 与 dev.md 一致 | 计时记分控制端通过 MQTT 驱动大屏,显示端只负责渲染 | 工程目录示例: ``` frontend/ src/ admin/ # 管理后台 + 计时记分控制 + H5 display/ # 大屏(成绩榜 / 当前选手 / 倒计时) shared/ # 共用组件、工具、MQTT 客户端 vite.config.js # 多入口:build.admin / build.display ``` 部署时可将 `display` 产物挂载至 `/tsapp/display/`,或由赛场边设备本地打开,无需 JWT。 --- ## MQTT 与数据发布 > 数据发布策略详见 [`docs/data-sync.md`](docs/data-sync.md) ### 两类 MQTT 消息 | 类型 | Topic 示例 | 发布方 | 订阅方 | 用途 | |------|-----------|--------|--------|------| | **数据变更** | `equsass/{eventCode}/data/change` | 后端(写库后) | 大屏、H5 | 通知某类 `/res` JSON 已更新 | | **显示指令** | `equsass/{eventCode}/display/command` | 计时控制端 | 大屏 | 切屏、清屏等控屏指令 | ### 数据变更消息(核心) 写库成功后发布,读端**不解析业务字段**,只根据 `res_url` 拉取 JSON: ```json { "action": "update", "event_code": "evt2026", "data_type": "scoreboard", "scope_key": "phase_12", "version": "v000045", "res_url": "/res/evt2026/scoreboard/phase_12/v000045.json", "changed_at_ms": 1735689600123 } ``` ### `/res` 路径编码(摘要) ``` /res/{event_code}/{data_type}/{scope_key}/{version}.json ``` 常见 `data_type`:`schedule`、`start_order`、`scoreboard`、`team_scoreboard`、`current`、`countdown`、`ranking`、`medal`。 ### 显示指令(可选) | Topic | 用途 | |-------|------| | `equsass/{eventCode}/display/command` | 全屏切换/清屏 | | `equsass/{eventCode}/display/countdown` | 倒计时显隐(亦可走 data/change + `/res`) | --- ## 与 FEI 规则的关系 本系统以 [场地障碍规则 27 版](docs/场地障碍规则27版.pdf) 为规则计算模式的依据,但**不替代**现场执裁系统: - **规则计算(默认)**:录入基础成绩后,由系统按表 A / 表 C 等规则计算排名、时间罚分、晋级;结果可手工修改 - **锁定防覆盖**:手工改值后可打开锁定,阻止自动重算覆盖(非场次级「自由模式」切换) - 验马、裁判铃、水障判罚、路线设计等场外流程不在本子系统范围内,可作为外部数据源接入 --- ## 开发计划 ### 总体周期:约 8–10 个月(4–5 人团队) --- ### 第一阶段:管理后台基础(第 1–2 月) | 任务 | 说明 | |------|------| | 项目脚手架 | FastAPI + Vue/Vite 工程、MySQL 8.4、单端口部署 | | Nacos 配置 | 引导配置 + 参数节点模板;启动时先连 Nacos 再初始化 | | 认证模块 | `/regist`、`/login`、JWT 中间件、`/api` 鉴权 | | 静态资源 | `/tsapp` 挂载 dist、`/res` 资源目录 | | 用户权限 | 管理员、计时记分操作员 | | 数据字典 | 赛制、判罚表、状态枚举 | | 基础数据 CRUD | 赛事、项目、场次、**赛段**、队伍、人马组合 | | 赛段配置 | 路线长度、速度、允许时间/限制时间计算 | **交付物**:可创建赛事并配置到赛段级别的管理后台。 --- ### 第二阶段:报名报项与出场编排(第 3 月) | 任务 | 说明 | |------|------| | 报名报项 | 项目报名、审核、首轮出场名单 | | 出场顺序编排 | 手工调整、抽签、轮换 | | 成绩处理框架 | 成绩录入表单、状态管理 | **交付物**:完成报名到首轮出场编排的赛前流程。 --- ### 第三阶段:规则引擎与成绩处理(第 4–5 月)⭐ 核心 | 任务 | 说明 | |------|------| | 表 A 规则引擎 | 罚分、时间罚分、淘汰、**赛段级超时计算** | | 单轮赛 + 附加赛 | 完整成绩处理闭环 | | 规则/手工覆盖 | 默认规则计算 + 手工改值 + `calc_locked` 锁定 | | 排名计算 | 赛段 → 场次 → 项目排名 | | 晋级逻辑 | 附加赛资格、第二轮资格(自动 + 手工) | **交付物**:单轮个人赛 + 附加赛可完整跑通。 --- ### 第四阶段:赛制扩展(第 6–7 月) | 任务 | 说明 | |------|------| | 常规两段赛 | 两赛段独立允许时间、第一段停止逻辑 | | 特殊两段赛 | 不同判罚表、两赛段排名合并 | | 两轮赛 | 两场次、晋级比例、排名合并 | | 表 C 计分 | 罚秒、赛段限制时间 | | 国家杯团体赛 | 团体罚分、两轮排名 | **交付物**:覆盖主流国内/国际赛制。 --- ### 第五阶段:现场子系统(第 8 月) | 任务 | 说明 | |------|------| | 计时记分控制端 | 成绩采集、场次切换 | | MQTT 消息协议 | 数据变更 Topic + `/res` JSON 编码规则 | | 数据发布服务 | 写库后自动生成快照并 MQTT 通知 | | 大屏显示子系统 | 成绩榜、当前选手、倒计时 | | 离线缓存与同步 | 现场断网容错 | **交付物**:现场计时 + 大屏 MQTT 联动可用。 --- ### 第六阶段:移动发布与上线(第 9–10 月) | 任务 | 说明 | |------|------| | 移动 H5 | 赛程、成绩、奖牌榜 | | 成绩导出 | Excel、PDF | | 规则回归测试 | 赛段级时间、两段赛等关键用例 | | 部署文档 | 私有化部署、MQTT Broker 配置 | | 操作培训材料 | 管理员、计时记分操作员手册 | **交付物**:v1.0 四子系统完整发布。 --- ## 里程碑 | 里程碑 | 时间 | 标志 | |--------|------|------| | M1 数据骨架 | 第 2 月末 | 赛段级数据模型可用 | | M2 赛前流程 | 第 3 月末 | 报名 + 出场编排 | | M3 计分 MVP | 第 5 月末 | 单轮赛 + 附加赛规则计算 | | M4 赛制完整 | 第 7 月末 | 两段赛/两轮赛/团体赛 | | M5 现场联动 | 第 8 月末 | MQTT 大屏联调通过 | | M6 v1.0 | 第 10 月末 | 四子系统上线 | --- ## 风险与依赖 | 风险 | 缓解 | |------|------| | 赛段级时间理解偏差 | 以两段赛为基准用例;允许时间挂在赛段而非项目/场次 | | 外部执裁系统数据格式不一 | 定义通用成绩导入接口;支持手工补录 | | MQTT 现场网络 | 大屏本地缓存最近状态;控制端离线队列 | | Nacos 不可用 | 启动失败快速报错;部署文档明确参数节点清单 | | 规则计算与手工锁定 | 默认规则引擎;`calc_locked` 防止重算覆盖人工值;审计日志 | **外部依赖:** - Nacos 配置中心(部署前须预先建立参数节点) - MQTT Broker(现场部署或云端) - 可选:外部执裁/计时系统成绩导出接口 --- ## 参考文档 - [场地障碍规则 27 版(中文)](docs/场地障碍规则27版.pdf) - [原始需求说明](docs/dev.md) - [Nacos 配置说明](docs/nacos-config.md) - [Nacos 配置模板](docs/nacos-config.example.json) - [数据更新与发布策略](docs/data-sync.md) - [数据库设计](docs/db.md) - FEI 附录二:允许时间计算公式 --- ## 许可证 待定。