根本原因: 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>
179 lines
5.3 KiB
Markdown
179 lines
5.3 KiB
Markdown
# 羽毛球分組配對器
|
||
|
||
這是一個使用 `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
|
||
```
|
||
|
||
開啟系統。
|