Files
badminton-match-hub/README.md
T
JianMiauandClaude Fable 5 121a5fee4c 新增讀取 Telegram 出席名單功能,讀取指定日期改為自動 fallback
根本原因:
Node-RED 的 Telegram bot「建喵打羽球」已把報名名單寫進同庫的 attendance 表,但配對器沒有任何入口讀取,每次仍需手動把出席者一個個打進名單。

影響:
- 新增 GET /api/attendance/:time,用既有 pool 讀 attendance 表,沒資料回 404
- 「讀取指定日期」改為兩段:先讀 badminton 配對資料,該日還沒配對時自動改載入出席名單
- 新增「讀取今日出席」按鈕可直接載入出席名單
- attendance 表沒有分區資訊,出席者一律先放 A 區、清空 B 區與既有配對,由使用者自行分配
- badminton 表結構與上傳流程不變

修法:
server.mjs 新增 attendance 端點;App.tsx 新增 findAttendance / loadAttendance / applyAttendance,loadResults 改用 findMatchResults 判斷後 fallback,移除不再使用的 loadMatchResults;README 補上讀取今日出席章節與 attendance 表結構。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 11:46:13 +08:00

179 lines
5.3 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.
# 羽毛球分組配對器
這是一個使用 `React + Vite + TypeScript` 製作的網頁應用程式,用來將 A 區與 B 區成員配成羽毛球隊伍。
## 功能說明
- 分別輸入 A 區與 B 區名單
- 可指定要寫入資料庫的目標日期
- 每次固定產生 3 組完整配對結果
- 每一隊都由 `1 位 A 區 + 1 位 B 區` 組成
- 每一組都會把 A 區與 B 區名單完整分配完成
- 兩區人數差 2 以上時,每輪會輪流讓人多的一區成員跨區支援
- 跨區平衡後仍有缺口(總人數為奇數)時,才補入「那個」
- 產生配對後可用按鈕手動上傳資料到資料庫
- 若指定日期已經有資料,上傳前會先詢問是否覆蓋
- 可讀取指定日期的資料庫內容並回填到畫面
- 若指定日期還沒有配對資料,`讀取指定日期` 會自動改抓 Telegram bot 的出席名單帶入 A 區
- 也可直接按 `讀取今日出席`,把出席名單帶進 A 區後再自行分配到 B 區
- 兩邊都沒有資料時,畫面會顯示對應的錯誤訊息
- 組隊結果產生後可一鍵主動推播到指定 LINE 對話(需按兩次確認)
- 支援換行、半形逗號、全形逗號與頓號輸入
- 會自動去除空白與重複名稱
## 操作流程
1. 選擇 `目標日期`
2.`讀取今日出席` 帶入 Telegram 報名名單(或手動輸入 A 區與 B 區名單)
3. 把 A 區的人自行搬一部分到 B 區
4. 按下 `產生三組配對`
5. 按下 `上傳資料` 將資料寫入 DB
6. 若需要,可按 `讀取指定日期` 載回既有資料;該日還沒配對時會自動改載入出席名單
7. 組隊結果顯示後,可按 `推送到 LINE`(按兩次確認才會送出)
## 讀取今日出席
出席名單來自 Node-RED 的 Telegram bot「建喵打羽球」:群組成員按「參加」後會寫進同一個資料庫的 `attendance` 表,按「不參加」則直接刪除,所以表裡的就是當天出席名單。
- 使用現有的 `DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_DATABASE` 連線,不需要新的環境變數
- API`GET /api/attendance/:time``time``YYYYMMDD`),回傳 `{ ok: true, data: { time, names: [...] } }`;沒資料回 404
- `attendance` 表沒有 A / B 區資訊,載入後所有人會先放在 A 區,請自行分配到 B 區
- 載入時會沿用名單去重邏輯,並清空 B 區與既有配對結果
`attendance` 表結構:
```sql
CREATE TABLE attendance (
poll_date INT(11) NOT NULL COMMENT '場次日期 YYYYMMDD',
user_id BIGINT NOT NULL COMMENT 'Telegram user id',
nickname VARCHAR(64) NOT NULL COMMENT '顯示名稱(就是要放進 personnel 的名字)',
tg_name VARCHAR(128) NULL COMMENT 'Telegram 顯示名',
joined_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '報名時間',
PRIMARY KEY (poll_date, user_id)
) CHARSET utf8mb4;
```
bot 另外還有 `tg_poll`(報名訊息 id)與 `tg_members`(暱稱記憶)兩張表,這個專案不會讀寫。
## 使用方式
```bash
npm install
npm run dev
```
前端開發網址預設為:
```text
http://localhost:3500
```
推播到 LINE 時,會直接使用 Flex Message 主動送到指定對話,格式參考既有 `line-bot-ts` 羽球查詢結果樣式。
若指定日期尚未上傳資料,系統會先提示你上傳後再推播。
LINE 推播目標支援分成兩組:
- `LINE_TARGET_ID_LOCAL`: 本地測試用對話
- `LINE_TARGET_ID_PROD`: 正式環境用對話
- `LINE_TARGET_MODE`: `local``prod`
當系統偵測目前為 `local` 模式時,頁面右上角會顯示「測試環境」標籤。
正式環境 `prod` 不會顯示這個標籤。
## 資料庫欄位
- `time`: 目標日期,格式為 `YYYYMMDD`
- `personnel`: 人員清單,格式為 `[[1,"A區成員"],[0,"B區成員"]]`
- `battlecombination`: 三組隊伍搭配,格式為 `{"0":[["A","B"]],"1":[...],"2":[...]}`
## 正式建置
```bash
npm run build
```
## Docker 部署
這個專案已提供單容器部署方式,容器啟動後會同時提供:
- 網頁前端
- `/api` 後端
- MariaDB 寫入功能
### 建立映像
```bash
docker build -t badminton-match-hub .
```
### 啟動容器
```bash
docker run -d \
--name badminton-match-hub \
-p 3500:3500 \
-e PORT=3500 \
-e DB_HOST=192.168.0.15 \
-e DB_PORT=3307 \
-e DB_USER=jianmiau \
-e DB_PASSWORD=你的密碼 \
-e DB_DATABASE=badminton \
-e DB_TABLE=badminton \
badminton-match-hub
```
### NAS 上建議設定
- 容器埠使用 `3500`
- 對外埠可自訂,例如 `3500:3500`
- 環境變數請在 NAS 的 Docker 介面中填入
- 不要把本機 `.env` 直接打包進映像
部署完成後可直接透過:
```text
http://NAS_IP:3500
```
開啟系統。
### docker-compose
專案也已提供 `docker-compose.yml`
```bash
docker compose up -d --build
```
Compose 版本預設會:
- 對外使用 `3500`
- 容器內部使用 `3500`
- 讀取 `.env` 內的資料庫設定
- 預設使用 `LINE_TARGET_MODE=prod`
- 推播到 `LINE_TARGET_ID_PROD`
也就是說,在 NAS 上直接執行:
```bash
sudo docker compose up -d --build
```
就會以正式環境方式啟動。
請先確認 `.env` 至少有以下設定:
```env
LINE_CHANNEL_ACCESS_TOKEN=你的token
LINE_TARGET_ID_LOCAL=你的測試對話ID
LINE_TARGET_ID_PROD=你的正式對話ID
```
部署後可透過:
```text
http://NAS_IP:3500
```
開啟系統。