Files
badminton-match-hub/README.md
T
JianMiauandClaude Fable 5 ae8dba99b7 V2 推送 LINE 改依環境決定目標,移除卡片底部說明文字
根本原因:
V2 原本把推送目標寫死在測試對話(target: local),部署到 NAS 後也只會
私訊測試對象,無法推到正式羽球群;另外卡片底部的「搭檔不重複・實力平衡
排點」說明文字對群組成員沒有意義。

影響:
- NAS 正式環境(LINE_TARGET_MODE=prod)推送到勝皇羽球團群組
- 本機開發(local)維持推到測試對話,按鈕會標示「推送到 LINE(測試)」
- LINE 卡片不再顯示底部說明文字

修法:
前端移除寫死的 target 參數,交由 server 依 LINE_TARGET_MODE 解析目標;
按鈕文字與推送成功訊息改為依環境顯示;buildLineFlexMessageV2 移除 footer。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 11:32:41 +08:00

7.4 KiB
Raw Blame History

羽毛球分組配對器

這是一個使用 React + Vite + TypeScript 製作的網頁應用程式,用來排羽毛球場地賽程與隊伍配對。

  • 首頁(/)是 V2:2、3 號場地各排 8 局雙打
  • /v1 保留舊版:A / B 區三輪配對

V2 功能說明(首頁)

  • 輸入或用 讀取出席名單 載入出席成員(來源同 V1 的 attendance API
  • 每局從全部成員中挑 8 人上場(2、3 號場地各 4 人打雙打),其他人休息
  • 成員不固定場地,每局重新分配;系統會讓每個人上場局數盡量平均(落差最多 1 局)
  • 搭檔組合在全部組合輪完一遍之前不會重複;對手組合也會盡量錯開
  • 頁面載入時會先透過 GET /api/skillstg_members 的實力表,載入完成前不能產生賽程
  • 依實力權重排點:在不重複的前提下,搭檔盡量強弱互補、每局兩隊實力總和盡量接近(查不到的人權重當 1);對戰表平常只顯示名字,滑鼠停在隊伍上會浮出實力泡泡(例如 阿志 1、蓉蓉 4(合計 5
  • 人數 4~7 人時只排 2 號場地 8 局;未滿 4 人無法排賽程
  • 讀取指定日期 可把之前上傳的 V2 賽程從資料庫回填到畫面;該日還沒賽程時會自動改載入出席名單(V1 格式的舊資料請到 /v1 讀取)
  • 上傳沿用原本的 badminton 表:personnel 存全部出席者、battlecombination"0" / "1" 分別存 2、3 號場地的 16 組搭檔(依序每 2 組為一局的對戰雙方)
  • 推送到 LINE 跟著 LINE_TARGET_MODE 走:NAS 正式環境(prod)推到羽球群、本機開發(local)推到測試對話;一樣要按兩次確認,local 模式時按鈕會標示「測試」

V1 功能說明(/v1

  • 分別輸入 A 區與 B 區名單
  • 可指定要寫入資料庫的目標日期
  • 每次固定產生 3 組完整配對結果
  • 每一隊都由 1 位 A 區 + 1 位 B 區 組成
  • 每一組都會把 A 區與 B 區名單完整分配完成
  • 兩區人數差 2 以上時,每輪會輪流讓人多的一區成員跨區支援
  • 跨區平衡後仍有缺口(總人數為奇數)時,才補入「那個」
  • 產生配對後可用按鈕手動上傳資料到資料庫
  • 若指定日期已經有資料,上傳前會先詢問是否覆蓋
  • 可讀取指定日期的資料庫內容並回填到畫面
  • 若指定日期還沒有配對資料,讀取指定日期 會自動改抓 Telegram bot 的出席名單
  • 也可直接按 讀取今日出席 帶入出席名單
  • 載入出席名單時會依 tg_members.skill 實力權重自動分區:實力強的一半放 A 區、弱的一半放 B 區(可再手動調整)
  • 兩邊都沒有資料時,畫面會顯示對應的錯誤訊息
  • 組隊結果產生後可一鍵主動推播到指定 LINE 對話(需按兩次確認)
  • 支援換行、半形逗號、全形逗號與頓號輸入
  • 會自動去除空白與重複名稱

V1 操作流程

  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 連線,不需要新的環境變數
  • APIGET /api/attendance/:timetimeYYYYMMDD),回傳 { ok: true, data: { time, names: [...], members: [{ name, skill }] } };沒資料回 404
  • 每個人的 skill 來自 tg_members.skill(優先用 user_id 對應,對不到再用 nickname 對應),查不到就當 1
  • 載入後依 skill 由高到低排序,前一半(無條件進位)放 A 區、後一半放 B 區;同分時維持報名順序
  • 載入時會沿用名單去重邏輯,並清空既有配對結果

attendance 表結構:

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;

實力權重

tg_members 表加了 skill 欄位(TINYINT UNSIGNED NOT NULL DEFAULT 1,範圍 1~10,10 最強),這個專案只會讀取、不會寫入:

UPDATE tg_members SET skill = 8 WHERE nickname = '柏威';

bot 另外還有 tg_poll(報名訊息 id)表,這個專案不會讀寫;tg_members 也只讀 skillnickname 對應,不會修改。

使用方式

npm install
npm run dev

前端開發網址預設為:

http://localhost:3500

推播到 LINE 時,會直接使用 Flex Message 主動送到指定對話,格式參考既有 line-bot-ts 羽球查詢結果樣式。 若指定日期尚未上傳資料,系統會先提示你上傳後再推播。

LINE 推播目標支援分成兩組:

  • LINE_TARGET_ID_LOCAL: 本地測試用對話
  • LINE_TARGET_ID_PROD: 正式環境用對話
  • LINE_TARGET_MODE: localprod

當系統偵測目前為 local 模式時,頁面右上角會顯示「測試環境」標籤。 正式環境 prod 不會顯示這個標籤。

資料庫欄位

  • time: 目標日期,格式為 YYYYMMDD
  • personnel: 人員清單,格式為 [[1,"A區成員"],[0,"B區成員"]]
  • battlecombination: 三組隊伍搭配,格式為 {"0":[["A","B"]],"1":[...],"2":[...]}

正式建置

npm run build

Docker 部署

這個專案已提供單容器部署方式,容器啟動後會同時提供:

  • 網頁前端
  • /api 後端
  • MariaDB 寫入功能

建立映像

docker build -t badminton-match-hub .

啟動容器

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 直接打包進映像

部署完成後可直接透過:

http://NAS_IP:3500

開啟系統。

docker-compose

專案也已提供 docker-compose.yml

docker compose up -d --build

Compose 版本預設會:

  • 對外使用 3500
  • 容器內部使用 3500
  • 讀取 .env 內的資料庫設定
  • 預設使用 LINE_TARGET_MODE=prod
  • 推播到 LINE_TARGET_ID_PROD

也就是說,在 NAS 上直接執行:

sudo docker compose up -d --build

就會以正式環境方式啟動。

請先確認 .env 至少有以下設定:

LINE_CHANNEL_ACCESS_TOKEN=你的token
LINE_TARGET_ID_LOCAL=你的測試對話ID
LINE_TARGET_ID_PROD=你的正式對話ID

部署後可透過:

http://NAS_IP:3500

開啟系統。