Files
Scoreboard/docs/部署指南.md
T
AskaEth 6483acf9ad feat: 人员年龄/号码布、图片上传、多显示器菜单、Console 两栏、exe 改名等一批改动
人员与报名:
- 人员唯一标识改为「证件号 或 号码布」二选一(0013/0014 迁移 + 导入匹配/回填/冲突)
- 人员新增「年龄」(0015 迁移):表单/列表/导入模板表头 11 列;冲突比较只在两边
  都有值且不同时才算差异,旧数据不误报
- 冲突决策键改为「证件号优先 → 号码布」(前端原先只用 idCard,号码布行会互相覆盖)
- 报名选择器支持无证件号但有号码布的运动员

大屏与 Console:
- 大屏控制新增「显示器」菜单:可给指定显示器重建全屏窗口;单屏/主显示器强制窗口化,
  限制在 Console 侧判定,不信任页面传参
- Console 服务管理窗口改左右两栏,运行日志独占右栏满高;窗口默认尺寸 1280×860
- 大屏右上角时钟精确到秒(同时把刷新间隔 15s 改为 1s)

图片与配置:
- 新增图片上传 POST /api/uploads(recorder+,png/jpg/webp/gif,≤5MiB)与匿名静态
  访问 GET /uploads/{name}(防目录穿越);文件落赛事项目目录 uploads/,随赛事包
  一起导出/导入
- 「赛事信息」新增赛事会标 / 大屏会标 / 大屏背景图三个上传项
- 参赛单位 logo 输入框右侧加上传按钮

关于页与构建:
- 构建时间打通:version.BuildTime + /api/system/about 返回 + build.bat 注入
  (用 PowerShell 取时间戳,避免本地化 %DATE% 的非 ASCII 星期)

安装包:
- 主程序改名 Scoreboard_Console.exe(win.executableName;productName/显示名不变)
- 记住上次安装目录:自定义 NSIS 宏优先沿用自存路径(卸载不清除),并在 customInit
  里补写回 electron-builder 的键,避免安装模式页把 $INSTDIR 打回默认

自检:服务端 12 包全绿;前端 vue-tsc + build 通过、node --test 58/58;console 12 文件通过。
2026-09-10 09:05:06 +08:00

428 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署指南
> 面向现场操作人员与管理员。本系统完全离线运行,不需要联网,不需要安装任何运行时。
---
## 1. 运行环境要求
| 项目 | 要求 |
| --- | --- |
| 服务端主机 | Windows 10 / 11,64 位(amd64) |
| 大屏 | 通过 HDMI 接入主机;显示模式设为**扩展**;推荐 16:9、1920×1080 或 3840×2160 |
| 网络 | 完全离线即可运行。若需要手机/平板/电视访问,用独立交换机或路由器组成局域网 |
| 磁盘 | 建议预留 ≥ 10GB |
| 运行时 | 无需安装 Go / Node / 数据库等任何运行时,前端已内嵌进可执行文件 |
| 权限 | 首次运行会在部署目录下创建 `data\`、`logs\`,请确保该目录可写 |
> 服务端只构建 Windows amd64 一个版本。Linux / 其他平台不在交付范围内。
---
## 2. 获取安装包
在**构建机**(具备 Go、Node.js、pnpm、git 环境,并能访问依赖源)上执行:
```bat
deploy\build.bat
```
脚本依次完成:拉取代码 → 构建前端 → 构建服务端 → 打包桌面宿主。产物落在仓库的 `dist\` 目录:
| 产物 | 类型 | 说明 |
| --- | --- | --- |
| `dist\scoreboard.exe` | 服务端 | 前端已通过内嵌方式打进可执行文件,单独运行也能提供全部页面 |
| `dist\console\Scoreboard Console-0.1.0-x64.exe` | 安装包 | NSIS 安装程序,双击按向导安装 |
| `dist\console\Scoreboard Console-0.1.0-x64.zip` | 免安装包 | 解压即用 |
| `dist\console\win-unpacked\` | 目录 | 未压缩的运行目录,便于现场调试 |
> 文件名中的 `0.1.0` 来自 `console\package.json` 的版本号,会随版本升级变化;以 `dist\console\` 下的实际文件名为准。
>
> 目标机器**不需要** Go / Node / pnpm,也不联网;上述工具只用于构建机。
---
## 3. 安装与首次启动
### 3.1 使用安装包
1. 双击 `Scoreboard Console-0.1.0-x64.exe`。
2. 按向导选择安装目录(可自定义)。
3. 安装完成后,桌面会出现名为**「赛事积分榜」**的快捷方式。
4. 双击「赛事积分榜」启动。
> 程序会在自己的安装目录下创建 `data\` 与 `logs\`,请安装到当前用户有写权限的目录;若装到受保护的系统目录,可能因无写入权限而无法建库。
### 3.2 使用免安装包
1. 把 `Scoreboard Console-0.1.0-x64.zip` 解压到任意目录(建议使用纯英文路径)。
2. 双击目录里的 `Scoreboard_Console.exe` 启动。
### 3.3 首次启动会自动完成
- 拉起并守护服务端 `scoreboard.exe`;
- 在 `data\` 下自动建库、建目录;
- 打开服务端管理面板窗口;
- 把大屏页面投到扩展屏;
- 启用防熄屏;
- 注册开机自启登录项但**不启用**(任务管理器「启动」里可见、状态为「已禁用」);需要随系统启动时,在服务管理窗口的「开机自启」开关里打开(仅打包后的 Windows 版本生效)。
部署目录结构(安装或解压后):
```
安装目录\
├── Scoreboard_Console.exe # 桌面宿主,双击启动
├── resources\app.asar # Console 自身资源
├── config.json # Console 配置(可编辑)
├── scoreboard.exe # Go 服务端
├── config.yaml # 服务端配置
├── scoreboard.png # 应用图标
├── data\ # 首次运行生成:数据库、备份
└── logs\console.log # 首次运行生成:运行日志
```
> `data\` 与二进制分离,升级时替换可执行文件即可保留数据。
---
## 4. 服务管理窗口(Scoreboard Console)
启动后首先看到的是「Scoreboard Console · 启动管理」窗口。
### 4.1 窗口按钮
| 位置 | 按钮 / 项 | 作用 |
| --- | --- | --- |
| 服务端状态 | **启动服务** | 手动拉起服务端 |
| 服务端状态 | **停止服务** | 优雅停止服务端(会先请求服务端关闭,超时后强制结束) |
| 服务端状态 | **重启服务** | 停止后重新启动 |
| 服务端状态 | 状态行 | 已停止 / 启动中 / 运行中 / 停止中 / 异常退出 |
| 服务端状态 | 服务地址、进程 PID、自动重启、防熄屏 | 实时运行信息 |
| 服务端状态 | 服务端程序、配置文件 | 实际使用的可执行文件与配置路径 |
| 大屏窗口 | **重新排布** | 重新枚举显示器并重开大屏窗口 |
| 大屏窗口 | **打开服务端管理面板** | 用内置窗口打开 `http://127.0.0.1:<端口>/admin`,无需再开浏览器 |
| 运行日志 | **自动滚动** | 日志是否跟随最新一行 |
| 运行日志 | **清空** | 清空窗口内显示(不影响日志文件) |
| 已连接的大屏实例 | — | 显示在线大屏窗口的数量与各自页面,5 秒刷新;需先在管理面板登录过才能读到 |
| 标题栏 | 最小化 / 最大化 / 关闭 | 关闭只是**隐藏到托盘**,服务端继续运行 |
### 4.2 托盘用法
- 托盘菜单显示当前服务端状态(如「服务端:运行中 · 就绪」);
- 菜单项:**启动服务**、**停止服务**、**重启服务**、**打开管理端**、**打开服务端管理面板**、**退出**;
- 双击托盘图标 = 打开启动管理窗口;
- **要真正退出程序,必须用托盘菜单的「退出」**;直接关窗口不会停服务端。
> 同一台机器只允许运行一个 Console 实例,重复启动会自动退出,避免抢占端口和大屏。
---
## 5. 首次登录
### 5.1 访问地址
| 用途 | 地址 |
| --- | --- |
| 本机管理面板 | `http://127.0.0.1:19540/admin` |
| 局域网管理面板 | `http://<主机IP>:19540/admin` |
| 大屏 | `http://<主机IP>:19540/display` |
| 导航页 | `http://<主机IP>:19540/` |
> 端口默认 **19540**,以 `config.yaml` 为准。
### 5.2 初始账号
- 用户名:`admin`
- 初始密码:**首次启动时随机生成,只打印一次**,数据库里只保存加密后的哈希,无法反查。
### 5.3 从日志里找初始密码
用 Console 启动时,服务端输出会同时显示在「运行日志」面板并写入 `logs\console.log`。初始密码所在的一段输出形如:
```
[server] 初始密码: xxxxxxxxxxxxxxxx
[server] 首次登录后请立即修改密码
[server] === INITIAL ADMIN CREATED (password above) ===
```
在部署目录下执行以下命令即可找到(搜索纯 ASCII 标记行,中文 `findstr` 在 chcp 936/65001 下不可靠):
```bat
findstr /C:"INITIAL ADMIN CREATED" logs\console.log
```
搜到这一行后,初始密码就在它上面几行(形如 `初始密码: xxxxxxxx`)。也可以直接打开 Console 的「运行日志」面板往上翻。
> 如果不用 Console、直接双击 `scoreboard.exe`,密码只会打印在终端窗口里,不会落文件。建议始终通过 Console 启动。
### 5.4 修改密码
- 首次登录后会自动跳到**「修改密码」**页,必须改密才能继续使用;
- 新密码至少 8 位,需输入两次确认;
- 修改成功后所有会话作废,需用新密码重新登录;
- 管理员也可以在「系统设置 → 用户管理 → 重置密码」为其他用户重置。
### 5.5 忘记密码 / 没记下初始密码
在 Console 里先**停止服务**,然后在部署目录(`config.yaml` 所在目录)执行:
```bat
scoreboard.exe passwd -config config.yaml
```
该命令会为用户 `admin` 生成一个新的随机密码并打印出来,同时要求下次登录强制改密。可选参数:
| 参数 | 说明 |
| --- | --- |
| `-user <用户名>` | 指定要重置的用户,默认 `admin` |
| `-password <新密码>` | 直接指定新密码;留空则随机生成并打印 |
| `-new-username <新用户名>` | 同时修改用户名 |
---
## 6. 端口与配置修改
### 6.1 服务端 `config.yaml`
| 项 | 当前默认值 | 说明 |
| --- | --- | --- |
| `server.host` | `0.0.0.0` | 监听地址;`0.0.0.0` 表示局域网内其他设备也能访问 |
| `server.port` | `19540` | HTTP 端口 |
| `dataDir` | `data` | 数据目录,相对服务端工作目录解析 |
| `log.level` | `info` | 日志级别:`debug` / `info` / `warn` / `error` |
| `backup.intervalMinutes` | `5` | 自动备份间隔(分钟),`<=0` 关闭自动备份 |
| `backup.keep` | `20` | 每个库滚动保留的备份份数 |
| `server.allowedOrigins` | `[]` | 额外放行的 WebSocket Origin 白名单;默认只放行同源与不带 Origin 的客户端 |
### 6.2 Console `console\config.json`
| 项 | 当前默认值 | 说明 |
| --- | --- | --- |
| `server.exe` | `scoreboard.exe` | 服务端可执行文件;相对路径会在配置目录、当前目录、上级目录、`dist\` 依次查找 |
| `server.args` | `["-config","config.yaml"]` | 传给服务端的启动参数 |
| `server.host` / `server.port` | `127.0.0.1` / `19540` | 健康检查与打开管理面板用的地址;配成 `0.0.0.0` 时本机探测自动改走回环地址 |
| `server.autoStart` | `true` | Console 启动后是否自动拉起服务端 |
| `server.healthIntervalMs` | `5000` | 健康检查间隔(毫秒) |
| `server.restartDelayMs` | `3000` | 崩溃重启的初始退避,之后逐次翻倍 |
| `server.maxRestarts` | `0` | `0` = 无限重启 |
| `server.shutdownTimeoutMs` | `5000` | 等待服务端优雅退出的上限 |
| `server.healthyResetMs` | `60000` | 连续健康多久后清零崩溃计数 |
| `display.selection` | `all-secondary` | 选屏策略,见第 7 节 |
| `display.displays` | `[]` | 仅 `specified` 策略生效,填显示器 id 或 0 起序号 |
| `display.pages` | `["standings","medals","individual","results"]` | 多屏时按屏序轮转锁定的页面 |
| `display.pageParam` | `page` | 页面 URL 参数名 |
| `display.kiosk` | `true` | 多屏时是否 kiosk 全屏(单屏恒为窗口化) |
| `display.ignoreMouse` | `true` | 多屏窗口是否忽略鼠标 |
| `display.reloadEveryHours` | `24` | 定时重载兜底,`0` = 关闭 |
| `display.waitForServerMs` | `20000` | 服务端未就绪时,最多等这么久也先把大屏开出来 |
| `display.backgroundColor` | `#0b0f1a` | 大屏窗口底色,减少加载白闪 |
| `startup.openAtLogin` | `false` | 默认注册但不启用(任务管理器可见、已禁用);在服务管理窗口「开机自启」开关里控制 |
| `admin.width` / `admin.height` | `1280` / `860` | 启动管理窗口初始尺寸(服务管理窗口与服务端管理面板共用) |
| `log.maxLines` | `2000` | 内存中保留的日志行数 |
| `log.file` | `logs/console.log` | 日志文件,相对 `config.json` 所在目录 |
| `log.maxFileBytes` | `4194304` | 单个日志文件上限,超出后轮转为 `.1` |
> 配置缺项会自动使用内置默认值;JSON 语法错误时回落到全默认值,并在窗口顶部提示。
### 6.3 修改端口
端口必须**两处一致**:
1. 关闭 Console(托盘 → 退出);
2. 修改 `config.yaml` 的 `server.port`;
3. 修改 `console\config.json` 的 `server.port`(保持一致,否则 Console 健康检查和管理面板地址会失效);
4. 重新启动 Console;
5. 之后管理面板地址变为 `http://<主机IP>:<新端口>/admin`。
---
## 7. 大屏接线
### 7.1 主方案:Console 驱动 HDMI
1. 用 HDMI 线连接主机与大屏;
2. 在 Windows「显示设置」里把显示模式设为**扩展**(不要「复制」);
3. 确认分辨率与缩放(推荐 1920×1080 或 3840×2160,缩放按现场效果调整);
4. 启动 Console,大屏窗口会自动打开。
### 7.2 选屏策略(`display.selection`)
| 取值 | 行为 |
| --- | --- |
| `all-secondary`(默认) | 除内置屏外的每块屏各开一个大屏窗口;若所有屏都报告为非内置屏(纯台式机),退回「排除主屏」 |
| `all` | 所有显示器都开大屏窗口 |
| `specified` | 只对 `display.displays` 里列出的显示器开窗;可填显示器 id 或 0 起序号 |
### 7.3 单屏:窗口化 + F11
- 只有一块屏时,大屏以窗口化方式打开(屏幕 80% 大小、居中),并显示自绘标题栏;
- 在窗口内按 **F11** 切换全屏 / 窗口;
- 单屏模式不跟随显示器变化重排。
### 7.4 多屏:每屏锁一页
- 除内置屏外的每块屏各开一个 kiosk 全屏窗口;
- 每块屏**锁定一个页面**,按 `display.pages` 的顺序轮转分配;
- 多屏 kiosk 不响应 F11,也不接收鼠标(`ignoreMouse`);
- 可用的页面值:`standings`(总积分榜)、`medals`(团体奖牌榜)、`individual`(个人积分奖牌榜)、`results`(分项成绩)、`announcement`(公告)。
### 7.5 大屏窗口的自动行为
| 场景 | 行为 |
| --- | --- |
| 防熄屏 | Console 启动即生效,前后台都算 |
| 服务端就绪 | 首次健康检查通过后开大屏 |
| 服务端迟迟不就绪 | 等待超过 `waitForServerMs`(默认 20 秒)也先开,避免只见桌面 |
| 服务端崩溃 | 按退避策略自动重启,恢复后大屏自动重载 |
| 长时间运行 | 每 `reloadEveryHours`(默认 24 小时)自动重载一次 |
### 7.6 备选方案:安卓 TV / 设备自带浏览器
如果不用 Console 驱动 HDMI,也可以让大屏设备用自己的浏览器访问:
```
http://<主机IP>:19540/display
```
- 需要主机设置固定 IP,且设备与主机在同一局域网;
- 安卓电视一般常亮,无需额外防熄屏;
- 浏览器方案**不受** Console 的 kiosk、防熄屏、选屏控制,字体与动画表现以实际设备为准;
- 现场以 Console + HDMI 为主方案,浏览器访问作为备用。
---
## 8. 备份与恢复
### 8.1 备份目录
所有备份都在 `data\backups\` 下,文件名为 `.db` 快照。该目录不进版本库。
### 8.2 如何备份
| 方式 | 操作 | 产物 |
| --- | --- | --- |
| 自动 | 服务端启动后按 `backup.intervalMinutes`(默认 5 分钟) | `system-auto-<时间戳>.db` 与 `event-auto-<时间戳>.db` |
| 管理端 | 「系统设置 → 备份 → 立即备份」 | `system-manual-<时间戳>.db` 与 `event-manual-<时间戳>.db` |
| 命令行 | 在部署目录执行 `scoreboard.exe backup -config config.yaml` | `system-manual-<时间戳>.db` 与 `event-manual-<时间戳>.db` |
| 快捷脚本 | `deploy\backup.bat`(原因记 `manual`)或 `deploy\backup.bat publish`(原因记 `publish`) | 同上(`publish` 时原因为 `publish`) |
| 发布前 | 每次发布成绩时自动 | `event-publish-<时间戳>.db` |
- 备份使用 SQLite 的 `VACUUM INTO`,服务运行中也能安全执行,不需要停服务,也不依赖外部 `sqlite3.exe`;
- 自动、手动、命令行三种方式都按 `backup.keep`(默认 20 份)滚动保留,`system` 库与赛事库各自计算,超出的旧备份自动删除;
- 同一时刻只允许一个备份在进行,自动备份与手动、发布前备份不会互相踩;
- 现场建议:开幕前、每次发布高峰前、每天结束时各手动备份一次(自动备份只是兜底)。
### 8.3 如何恢复
服务端目前**没有一键恢复接口**,恢复需停机手工执行:
1. 在 Console 里**停止服务**,避免恢复过程中继续写入;
2. 把当前的 `data\event_projs\<项目名>\event.db` 改名留档,确认恢复失败还能退回;
3. 选一份目标备份复制为 `event.db`(覆盖原文件);
4. 同时删除同目录下的 `event.db-wal` 与 `event.db-shm`;
5. 重新启动服务,核对积分榜与最近一次发布记录。
> `data\system.db` 保存账号与会话信息,一般不需要回退;如需一并回退,替换它的方法与 `event.db` 相同(同时删除 `system.db-wal` / `system.db-shm`)。
>
> 恢复会丢弃备份时间点之后的所有改动。现场若还有备机,优先切换备机比原地恢复更快。
### 8.4 整机迁移
把整个部署目录(含 `data\`)拷贝到另一台同版本 Windows 机器,启动 Console 即可,无需重新配置。
### 8.5 赛事项目包导出 / 导入
| 操作 | 位置 | 说明 |
| --- | --- | --- |
| 导出 | 「导出 → 赛事项目包 → 导出 zip」 | 包内含 `meta.yaml`、`event.db`、`uploads/`、`rules.yaml`;整包拷走即可在另一台机器复现 |
| 导入 | 「导出 → 赛事项目包 → 选择 .zip → 导入」 | **仅管理员**可操作;可填「重命名」,勾选「覆盖同名项目」会先二次确认 |
- 导入后项目落在 `data\event_projs\<项目名>\`;
- 当前运行的服务端默认使用 `default` 项目。要让新导入的赛事生效,可在 Console 的 `config.json` 里把 `server.args` 改为 `["-config","config.yaml","-project","<项目名>"]`,然后重启 Console;也可以直接用 `scoreboard.exe -config config.yaml -project <项目名>` 启动;
- 导入期间请勿停止服务端进程。
---
## 9. 故障排查
### 9.1 端口被占用,服务端起不来
1. 查看占用进程(Windows 命令提示符):
```bat
netstat -ano | findstr :19540
```
2. 根据最后一列的 PID 结束进程:
```bat
taskkill /PID <PID> /F
```
3. 或按第 6.3 节把端口改到其他值(两处同时改)。
> 同一台机器不要启动两个 Console,否则会互相抢占端口和大屏。
### 9.2 大屏不亮 / 只有桌面
按顺序检查:
1. HDMI 线是否插好,大屏电源是否打开;
2. Windows 显示设置是否为**扩展**模式;
3. Console「大屏窗口」卡片是否显示已打开;点「重新排布」;
4. 确认 `display.selection` 选屏策略是否漏掉了目标屏;
5. 服务端未就绪时大屏最多等 20 秒才打开,稍等即可;
6. 查看 Console 运行日志里的「大屏排布完成」「大屏加载」记录;
7. 单屏窗口化时按 **F11** 可切全屏。
### 9.3 管理面板打不开
1. 确认 Console 里服务端状态为「运行中」;
2. 本机访问 `http://127.0.0.1:19540/admin`,其他设备访问 `http://<主机IP>:19540/admin`;
3. 确认 `config.yaml` 与 `console\config.json` 的端口一致;
4. 检查 Windows 防火墙是否拦截了该端口;
5. 用托盘菜单的「打开服务端管理面板」直接打开。
### 9.4 服务反复重启
- 打开 `logs\console.log`,搜索 `[server!]`,这是服务端错误输出;
- 检查 `data\` 是否可写、磁盘是否已满;
- 检查 `config.yaml` 语法与端口是否合法。
### 9.5 页面显示「示例数据」
- 表示前端连不上后端或接口未就绪,此时录入、发布等操作**不会写入真实赛事**;
- 确认服务端状态为「运行中」、实时连接显示「实时在线」;
- 恢复连接后刷新页面即可切换到真实数据。
### 9.6 大屏时间不对
- 系统没有外网 NTP,**以主机时间为准**;
- 修改 Windows 系统时间与时区后,大屏顶部时钟会在下一次刷新时跟随(约 15 秒一次);
- 建议在开幕前校准主机时间。
### 9.7 安卓 TV 备选方案打不开
1. 确认电视与主机在同一局域网,主机 IP 为固定 IP;
2. 电视浏览器地址填 `http://<主机IP>:19540/display`;
3. 确认 `config.yaml` 的 `server.host` 为 `0.0.0.0`;
4. 部分电视浏览器对 CSS 动画、字体支持有限,显示效果以实际设备为准。
### 9.8 忘记管理员密码
见第 5.5 节,使用 `scoreboard.exe passwd -config config.yaml` 重置。
---
## 10. 上线检查清单
- [ ] 服务端开机自启、崩溃自动拉起已验证
- [ ] `data\backups\` 可写,已生成至少一份备份
- [ ] 大屏 HDMI 输出定位正确、分辨率正确、字体正常
- [ ] 管理端各角色账号可登录,权限正确
- [ ] 主机 IP 固定,管理端 / 大屏地址已打印或做成二维码贴在操作台
- [ ] 防熄屏、屏保、休眠、系统通知已关闭
- [ ] 主机时间已校准
- [ ] 磁盘剩余空间充足
- [ ] 安卓 TV / 浏览器备选通道实测可打开页面