Files
Scoreboard/docs/部署指南.md
T
AskaEth 407abec3b0 fix: 大屏背景图被兜底渐变盖住 + 配置/规则也搬到用户数据目录
1) 大屏背景图不生效:App.vue 里 .bg 与 .bg-fallback 都是 absolute inset:0,
   而兜底渐变是不透明的且在图片之后绘制 → 配了背景图也只看得到渐变。
   改成 v-if/v-else 互斥(用无头 Chromium 真渲染对照验证:修复后取到图片像素,
   回退版本只剩渐变)。新增 background-layer.test.ts 锁住层次关系。

2) 规则/config 仍在安装目录,覆盖安装/卸载会连配置一起删:现在三样都落在
   %LOCALAPPDATA%\ScoreboardConsole\(与数据目录同一基座):
   - console\config.json(Console 读写都用这份)
   - config.yaml(Console 用绝对路径 -config 传给服务端与 passwd)
   - rules\(首次从安装目录复制过去,之后只认用户目录那份)
   首次运行复制迁移、已存在不覆盖、不可写则回落安装目录并记日志(不影响启动);
   开发态行为不变。备份脚本与文档同步。
   注意 resolveExecutable 必须显式传部署包根目录,否则会去用户目录找 scoreboard.exe。

自检:服务端 11 包全绿;console 语法 16 文件 + node:test 20/20;
前端 vue-tsc + build、node --test 69/69。
2026-09-10 10:06:21 +08:00

541 lines
33 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 / 数据库等任何运行时,前端已内嵌进可执行文件 |
| 权限 | 首次运行会在 `%LOCALAPPDATA%\ScoreboardConsole\` 下建数据目录并写入配置/规则(见 3.5),在部署目录的 `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. 双击「赛事积分榜」启动。
> 赛事数据、配置与规则都存放在 `%LOCALAPPDATA%\ScoreboardConsole\`(不随安装目录走,见 3.4、3.5),日志写在安装目录的 `logs\` 下;安装目录本身只要当前用户可写即可。
### 3.2 使用免安装包
1. 把 `Scoreboard Console-0.1.0-x64.zip` 解压到任意目录(建议使用纯英文路径)。
2. 双击目录里的 `Scoreboard_Console.exe` 启动。
### 3.3 首次启动会自动完成
- 拉起并守护服务端 `scoreboard.exe`;
- 在数据目录(默认 `%LOCALAPPDATA%\ScoreboardConsole\data`)下自动建库、建目录;
- 打开服务端管理面板窗口;
- 把大屏页面投到扩展屏;
- 启用防熄屏;
- 注册开机自启登录项但**不启用**(任务管理器「启动」里可见、状态为「已禁用」);需要随系统启动时,在服务管理窗口的「开机自启」开关里打开(仅打包后的 Windows 版本生效)。
部署目录结构(安装或解压后):
```
安装目录\
├── Scoreboard_Console.exe # 桌面宿主,双击启动
├── resources\app.asar # Console 自身资源
├── scoreboard.exe # Go 服务端
├── scoreboard.png # 应用图标
├── config.json # Console 配置模板(首次启动复制到用户配置目录)
├── config.yaml # 服务端配置模板(同上)
├── rules\ # 规则包模板(同上)
└── logs\console.log # 首次运行生成:运行日志
```
配置、规则与数据**都不在安装目录里**,各是独立的一处(详见 3.4、3.5):
```
%LOCALAPPDATA%\ScoreboardConsole\ # 用户数据目录(与安装目录无关)
├── data\ # 数据库、备份、上传的图片
├── console\config.json # Console 自己的配置(实际生效的那份)
├── config.yaml # 服务端配置(实际生效的那份)
└── rules\ # 规则包(实际生效的那份)
```
### 3.4 数据放在哪里(升级/卸载都不会删)
覆盖安装时安装程序会**先静默运行旧版卸载器**,而卸载器会把整个安装目录删掉,所以数据库、规则、用户、上传的图片都不能放在安装目录里。现在的规则:
| 场景 | 数据目录 |
| --- | --- |
| 默认(`console\config.json` 的 `server.dataDir` 留空,该文件见 3.5) | `%LOCALAPPDATA%\ScoreboardConsole\data`,即 `C:\Users\<用户名>\AppData\Local\ScoreboardConsole\data` |
| 想放到别处,例如 D 盘 | 在 `%LOCALAPPDATA%\ScoreboardConsole\console\config.json` 里把 `server.dataDir` 改成 `"D:\\scoreboard-data"`(绝对路径),或相对安装目录的路径 |
| 直接双击 `scoreboard.exe`(不经 Console) | 服务端默认行为不变:用工作目录下的 `data\`,**这种用法在覆盖安装时仍会丢数据** |
- Console 启动时会把这个目录用 `-data` 参数交给服务端,界面上方「运行日志」里能看到 `数据目录:...` 这一行;
- **一次性迁移**:如果新数据目录还是空的,而旧的 `<安装目录>\data` 里有数据,Console 会在启动时把旧目录**整体改名**过去(同盘很快),日志里会写「已把旧数据从 X 迁移到 Y」。任何失败(跨盘、权限不足等)都只记日志,旧数据原地保留,不会被删;
- 如果两个目录里**都有数据**,Console 不做任何动作,只写一条警告日志,由现场人工决定保留哪一份;
- 迁移只在安装包版本(打包后)执行一次,`pnpm start` 之类的开发运行不会碰仓库里的 `data\`;
- 迁移的**旧目录只认 `<安装目录>\data`**。如果你已经把数据迁到默认位置、之后又想改成 D 盘等其他目录,Console **不会**自动搬运:请先停服务,把 `%LOCALAPPDATA%\ScoreboardConsole\data` 整个目录复制到新路径,再改 `server.dataDir` 并重启 Console。
> 升级到本版本后第一次启动,请打开 Console 的「运行日志」确认是否有迁移记录,并核对管理端里的赛事/账号数据是否完整。
### 3.5 配置与规则放在哪里(升级/卸载也不会丢)
`config.json`、`config.yaml`、`rules\` 以前跟着安装包落在安装目录里,而**安装目录会被卸载器整个删掉**(覆盖安装会先静默跑一遍旧卸载器),所以用户改过的配置和规则包会一起没。本版本起它们也搬到**用户数据目录**(与数据目录同一处,安装目录里那三份只当模板):
| 内容 | 实际生效的位置 | 管什么 |
| --- | --- | --- |
| Console 配置 | `%LOCALAPPDATA%\ScoreboardConsole\console\config.json` | 端口、选屏策略、开机自启等(见 6.2) |
| 服务端配置 | `%LOCALAPPDATA%\ScoreboardConsole\config.yaml` | 监听地址/端口、备份间隔、日志级别(见 6.1) |
| 规则目录 | `%LOCALAPPDATA%\ScoreboardConsole\rules\` | 规则包 YAML;管理面板「导入规则包」时从这里选 |
**怎么改**:直接编辑上表里的文件。`config.json` 与 `config.yaml` 都是 Console 启动时读一次,改完要**退出并重启 Console**(托盘 → 退出);服务管理窗口的「配置文件」一行显示的就是 `console\config.json` 的实际路径,可用来确认。`rules\` 只是规则包存放目录,往里放文件不需要重启,但规则要在管理面板「系统设置 → 规则包 → 导入」里选文件导入后才生效(服务端不会自动加载)。安装目录里那三份是模板,改了不生效。
升级/覆盖安装时:
- **首次运行会复制**:用户目录里还没有、安装目录里有 → 原样复制一份过去,安装目录那份留着当模板;
- **已存在就绝不覆盖**:用户目录里已经有一份时一律用它,Console 既不覆盖也不删除,之后升级只换程序不换配置;
- Console 拉起服务端时传**绝对路径** `-config <用户目录>\config.yaml`,`passwd` 子命令用同一份(见 5.5),不会两边各读各的;
- 规则目录只在首次复制一次,之后你往里面放的规则包不会被模板顶掉;
- 某一处建不出来或不可写(权限、杀毒软件拦截等)时**只记日志并回落到安装目录里那份**,绝不因此启动失败;代价是那份改动仍会在覆盖安装时丢失,日志里会明确写出来;
- 开发运行(`pnpm start`)不接管这三项,仍用仓库里的 `console\config.json`、`config.yaml`、`rules\`;
- 不用 Console、直接双击 `scoreboard.exe` 时,服务端仍按工作目录下的 `config.yaml` 读配置,**不受这套保护**。
> 从老版本覆盖升级上来的第一次启动:安装目录里那三份在覆盖安装时**就已经被旧卸载器删过一遍**(新装进去的是新版模板),Console 只能把模板复制到用户目录,升级前改在安装目录里的配置需要重做一遍。**从本版本起**(也就是改动落在上表位置之后),配置与规则才会跨升级保留。
> 日志是例外:它留在安装目录的 `logs\console.log`(每次运行都会重写的排查材料,不值得搬),所以卸载后日志会消失,配置与规则不会。
首次启动后看 Console「运行日志」里这几行核对:
| 日志 | 含义 |
| --- | --- |
| `已把 Console 配置文件 复制到 …` | 三项各自的首选复制记录,各一条,属正常 |
| `用户数据目录:<路径>` | 本次实际使用的用户数据目录 |
| `警告:准备 … 失败…` | 该项回落到安装目录里那份,按日志里的路径手工复制过去再重启 Console |
| `警告:拿不到 %LOCALAPPDATA%…` | 系统环境异常,三项都还在安装目录里,覆盖安装会丢改动 |
---
## 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**,以 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml` 为准(该文件在哪见 3.5)。
### 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 里先**停止服务**,然后在安装目录(`scoreboard.exe` 所在目录)执行:
```bat
scoreboard.exe passwd -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -data "%LOCALAPPDATA%\ScoreboardConsole\data"
```
> **`-config` 与 `-data` 都不能省**:`-config` 指到 Console 实际使用的用户配置(安装目录里那份只是模板,见 3.5);`-data` 不加的话服务端会去用工作目录下的 `data\`,改的是另一个库里的账号(那个库是空的,看起来像「密码重置成功但登录不上」)。数据目录改过配置的,把这里换成用户配置里 `server.dataDir` 实际生效的那个路径;实在不确定,可以先看 `logs\console.log` 里 `数据目录:` 那一行。
该命令会为用户 `admin` 生成一个新的随机密码并打印出来,同时要求下次登录强制改密。可选参数:
| 参数 | 说明 |
| --- | --- |
| `-user <用户名>` | 指定要重置的用户,默认 `admin` |
| `-password <新密码>` | 直接指定新密码;留空则随机生成并打印 |
| `-new-username <新用户名>` | 同时修改用户名 |
| `-data <数据目录>` | 数据目录;通过 Console 启动时必须带上同一个值(见第 3.4 节) |
---
## 6. 端口与配置修改
### 6.1 服务端配置 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml`
> 路径说明见 3.5;安装目录里的 `config.yaml` 只是模板,改它不生效。
| 项 | 当前默认值 | 说明 |
| --- | --- | --- |
| `server.host` | `0.0.0.0` | 监听地址;`0.0.0.0` 表示局域网内其他设备也能访问 |
| `server.port` | `19540` | HTTP 端口 |
| `dataDir` | `data` | 数据目录,相对服务端工作目录解析;**Console 启动时会用 `-data <绝对路径>` 覆盖它**,配置项本身只对直接运行 `scoreboard.exe` 时生效 |
| `log.level` | `info` | 日志级别:`debug` / `info` / `warn` / `error` |
| `backup.intervalMinutes` | `5` | 自动备份间隔(分钟),`<=0` 关闭自动备份 |
| `backup.keep` | `20` | 每个库滚动保留的备份份数 |
| `server.allowedOrigins` | `[]` | 额外放行的 WebSocket Origin 白名单;默认只放行同源与不带 Origin 的客户端 |
### 6.2 Console 配置 `%LOCALAPPDATA%\ScoreboardConsole\console\config.json`
> 路径说明见 3.5;安装目录里的 `config.json` 只是模板,改它不生效。服务管理窗口的「配置文件」一行显示的就是这份实际路径。
| 项 | 当前默认值 | 说明 |
| --- | --- | --- |
| `server.exe` | `scoreboard.exe` | 服务端可执行文件;相对路径会在部署目录(安装目录)、当前目录、上级目录、`dist\` 依次查找 |
| `server.args` | `["-config","config.yaml"]` | 传给服务端的启动参数;其中 `-config` 的**值**会被换成 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml` 的绝对路径,其余参数(如 `-project`)按原样传(见 3.5) |
| `server.dataDir` | `""` | 数据目录;留空 = 用默认位置 `%LOCALAPPDATA%\ScoreboardConsole\data`。填了就按它来:绝对路径直接用,相对路径以安装目录为基准。Console 会把它作为 `-data <绝对路径>` 追加到启动参数后(见第 3.4 节) |
| `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` | 日志文件,相对部署目录(安装目录)解析 —— 日志不随配置搬到用户目录 |
| `log.maxFileBytes` | `4194304` | 单个日志文件上限,超出后轮转为 `.1` |
> 配置缺项会自动使用内置默认值;JSON 语法错误时回落到全默认值,并在窗口顶部提示。
### 6.3 修改端口
端口必须**两处一致**:
1. 关闭 Console(托盘 → 退出);
2. 修改 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml` 的 `server.port`;
3. 修改 `%LOCALAPPDATA%\ScoreboardConsole\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 备份目录
所有备份都在**数据目录**下的 `backups\` 里,文件名为 `.db` 快照。默认数据目录是 `%LOCALAPPDATA%\ScoreboardConsole\data`,所以备份在:
```
%LOCALAPPDATA%\ScoreboardConsole\data\backups\
```
数据目录改过 `server.dataDir` 的,按改后的路径找(同目录下的 `backups\`)。该目录不进版本库。
### 8.2 如何备份
| 方式 | 操作 | 产物 |
| --- | --- | --- |
| 自动 | 服务端启动后按 `backup.intervalMinutes`(默认 5 分钟) | `system-auto-<时间戳>.db` 与 `event-auto-<时间戳>.db` |
| 管理端 | 「系统设置 → 备份 → 立即备份」 | `system-manual-<时间戳>.db` 与 `event-manual-<时间戳>.db` |
| 命令行 | 在安装目录执行 `scoreboard.exe backup -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -data "%LOCALAPPDATA%\ScoreboardConsole\data"` | `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` 库与赛事库各自计算,超出的旧备份自动删除;
- 同一时刻只允许一个备份在进行,自动备份与手动、发布前备份不会互相踩;
- `deploy\backup.bat` 会自动用和 Console 相同的配置与数据目录(配置优先 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml`、数据优先 `%LOCALAPPDATA%\ScoreboardConsole\data`,即用户数据目录里那两份;都不存在时回退到脚本上级目录 `..\config.yaml` 与 `..\data`),并分别作为 `-config` / `-data` 传给服务端,所以从哪里运行都不会备份到别的库;
- 现场建议:开幕前、每次发布高峰前、每天结束时各手动备份一次(自动备份只是兜底)。
### 8.3 如何恢复
服务端目前**没有一键恢复接口**,恢复需停机手工执行:
1. 在 Console 里**停止服务**,避免恢复过程中继续写入;
2. 把当前的 `<数据目录>\event_projs\<项目名>\event.db` 改名留档,确认恢复失败还能退回;
3. 选一份目标备份复制为 `event.db`(覆盖原文件);
4. 同时删除同目录下的 `event.db-wal` 与 `event.db-shm`;
5. 重新启动服务,核对积分榜与最近一次发布记录。
> `<数据目录>\system.db` 保存账号与会话信息,一般不需要回退;如需一并回退,替换它的方法与 `event.db` 相同(同时删除 `system.db-wal` / `system.db-shm`)。
>
> 恢复会丢弃备份时间点之后的所有改动。现场若还有备机,优先切换备机比原地恢复更快。
### 8.4 整机迁移
用户数据目录(`%LOCALAPPDATA%\ScoreboardConsole\`,里面是数据 + 配置 + 规则)与**安装目录**(`Scoreboard_Console.exe`、`scoreboard.exe` 等)一起拷到另一台同版本 Windows 机器:
- 用户数据目录:整个 `%LOCALAPPDATA%\ScoreboardConsole\` 拷过去即可(含 `data\`、`console\config.json`、`config.yaml`、`rules\`;同一用户名下路径一致,最省事)——这样配置、规则和赛事数据原样接管,无需重新配置;
- 安装目录:按同样路径放好,或直接用新机器上的安装包装一遍(安装目录里那三份只是模板,拷不拷都行);
- 启动 Console 即可;若数据目录放到了别处,记得同步改 `console\config.json` 的 `server.dataDir`(在用户数据目录里那份)。
### 8.5 赛事项目包导出 / 导入
| 操作 | 位置 | 说明 |
| --- | --- | --- |
| 导出 | 「导出 → 赛事项目包 → 导出 zip」 | 包内含 `meta.yaml`、`event.db`、`uploads/`、`rules.yaml`;整包拷走即可在另一台机器复现 |
| 导入 | 「导出 → 赛事项目包 → 选择 .zip → 导入」 | **仅管理员**可操作;可填「重命名」,勾选「覆盖同名项目」会先二次确认 |
- 导入后项目落在 `<数据目录>\event_projs\<项目名>\`;
- 当前运行的服务端默认使用 `default` 项目。要让新导入的赛事生效,可在 Console 的用户配置 `config.json` 里给 `server.args` 加 `"-project","<项目名>"`,然后重启 Console;也可以直接用 `scoreboard.exe -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -project <项目名> -data "%LOCALAPPDATA%\ScoreboardConsole\data"` 启动;
- 导入期间请勿停止服务端进程。
### 8.6 手工备份 / 迁移整个数据目录
快照备份之外,数据库文件、上传的图片、备份都可以整体拷走:
1. 在 Console 里**停止服务**(托盘 → 停止服务),避免拷贝过程中继续写入;
2. 把整个数据目录 `%LOCALAPPDATA%\ScoreboardConsole\data` 复制到 U 盘或其他盘(里面含 `system.db`、`event_projs\`(数据库 + `uploads\` 图片)、`backups\`);
3. 恢复时把整个目录拷回去,然后:
- 拷回**原路径**:直接启动 Console 即可;
- 拷到**别的路径**:先建好目标目录并放入数据,再在用户配置 `console\config.json` 里把 `server.dataDir` 指向它,重启 Console;
4. 恢复完成后在管理端核对赛事、账号、上传图片是否齐全。
> 只拷 `backups\` 里的 `.db` 快照也能救回成绩数据,但上传的背景图/会标在 `event_projs\<项目名>\uploads\` 里,不拷这个目录会丢图片。
---
## 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` 的端口一致(都在 `%LOCALAPPDATA%\ScoreboardConsole\` 下,见 3.5);
4. 检查 Windows 防火墙是否拦截了该端口;
5. 用托盘菜单的「打开服务端管理面板」直接打开。
### 9.4 服务反复重启
- 打开 `logs\console.log`,搜索 `[server!]`,这是服务端错误输出;
- 检查数据目录 `%LOCALAPPDATA%\ScoreboardConsole\data` 是否可写、磁盘是否已满;
- 检查用户配置 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml` 的语法与端口是否合法。
### 9.5 页面显示「示例数据」
- 表示前端连不上后端或接口未就绪,此时录入、发布等操作**不会写入真实赛事**;
- 确认服务端状态为「运行中」、实时连接显示「实时在线」;
- 恢复连接后刷新页面即可切换到真实数据。
### 9.6 大屏时间不对
- 系统没有外网 NTP,**以主机时间为准**;
- 修改 Windows 系统时间与时区后,大屏顶部时钟会在下一次刷新时跟随(约 15 秒一次);
- 建议在开幕前校准主机时间。
### 9.7 安卓 TV 备选方案打不开
1. 确认电视与主机在同一局域网,主机 IP 为固定 IP;
2. 电视浏览器地址填 `http://<主机IP>:19540/display`;
3. 确认用户配置 `%LOCALAPPDATA%\ScoreboardConsole\config.yaml` 的 `server.host` 为 `0.0.0.0`;
4. 部分电视浏览器对 CSS 动画、字体支持有限,显示效果以实际设备为准。
### 9.8 忘记管理员密码
见第 5.5 节,使用 `scoreboard.exe passwd -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -data "%LOCALAPPDATA%\ScoreboardConsole\data"` 重置。
### 9.9 升级后数据/配置不见了 / 日志里有迁移警告
覆盖安装时安装程序会先跑旧卸载器删掉安装目录,所以本版本起数据放在 `%LOCALAPPDATA%\ScoreboardConsole\data`、配置与规则放在 `%LOCALAPPDATA%\ScoreboardConsole\` 下的另外三处(见 3.4、3.5)。首次启动后先看 Console「运行日志」或 `logs\console.log`:
| 日志 | 含义 |
| --- | --- |
| `数据目录:<路径>` | 本次实际使用的数据目录,先确认它是不是你期望那个 |
| `用户数据目录:<路径>` | 本次实际使用的用户数据目录(配置与规则都在它下面) |
| `已把 <X> 复制到 <Y>` | 首次运行把安装目录里的配置/规则复制到用户目录,属正常 |
| `已把旧数据从 X 迁移到 Y` | 旧数据已整体搬过去,属正常 |
| `警告:...都有数据...` | 两个目录都有数据,Console 没有动任何一边;请人工把要保留的那份拷到 `数据目录:` 指向的位置 |
| `警告:迁移旧数据失败...` | 跨盘或权限问题;旧数据仍在日志里写的 `X`,手工拷过去再重启即可 |
| `警告:准备 <X> 失败...` | 配置/规则那一项回落到安装目录里那份,改动仍会在覆盖安装时丢;按日志里的路径手工复制到用户目录再重启 |
| `警告:数据目录仍在安装目录内` | `server.dataDir` 配置成了安装目录里的路径,覆盖安装会丢数据,请改到安装目录之外 |
若安装目录里的 `data\` 已被删掉,优先用 `backups\` 里最新的 `.db` 快照按第 8.3 节恢复;配置与规则从老版本升上来时只能由 Console 复制新版模板过去(原因见 3.5 的说明),升级后确认一遍端口等设置即可。
---
## 10. 上线检查清单
- [ ] 服务端开机自启、崩溃自动拉起已验证
- [ ] 「运行日志」里的 `数据目录:` 指向 `%LOCALAPPDATA%\ScoreboardConsole\data`(不是安装目录),且没有迁移警告
- [ ] 「运行日志」里的 `用户数据目录:` 指向 `%LOCALAPPDATA%\ScoreboardConsole`,且没有「准备 … 失败」警告(配置与规则也在用户目录里,见 3.5)
- [ ] 数据目录的 `backups\` 可写,已生成至少一份备份
- [ ] 已把数据目录整个拷一份到 U 盘(含 `event_projs\<项目名>\uploads\` 里的图片)
- [ ] 大屏 HDMI 输出定位正确、分辨率正确、字体正常
- [ ] 管理端各角色账号可登录,权限正确
- [ ] 主机 IP 固定,管理端 / 大屏地址已打印或做成二维码贴在操作台
- [ ] 防熄屏、屏保、休眠、系统通知已关闭
- [ ] 主机时间已校准
- [ ] 磁盘剩余空间充足
- [ ] 安卓 TV / 浏览器备选通道实测可打开页面