# Thrypton 全案規格書 v1.6 **最後更新**: 2026-05-09 **適用對象**: 開發者、AI 輔助工具 **目標**: 任何人閱讀本文後,不需查看原始碼即可理解完整商業邏輯並重構專案 > ★ v1.6 基準變更:本版將獨立部署(independent)模式提升為完整的封閉式集群匹配系統,增加 `clusters.deploy_mode` / `is_locked` 欄位與 `cluster_operation_log` 審計表。所有數據參數、邏輯流程、校驗順序均嚴格對齊 `config/database.php`、`api/*.php`、`tasks/check-expirations.php` 中的實際實現。 --- ## 一、專案總覽 ### 1.1 產品定位 Thrypton 是一個**節點算力投資分紅平台**。用戶購買雲端算力節點(OC 機構節點 / HM 個人節點),節點按月產出收益。系統透過**推薦網絡**和**平衡組配對機制**提供額外的收益加成。 ### 1.2 目標用戶 - 地區:馬來西亞 - 語言:繁體中文(zh-Hant) - 貨幣:RM(馬來西亞令吉) - 裝置:移動端優先(max-width: 430px) ### 1.3 技術棧 | 層級 | 技術 | 備註 | |------|------|------| | 後端 | PHP 8.2+ | 無框架,純原生 | | 資料庫 | MySQL 5.7+ | utf8mb4_unicode_ci | | 前端 | HTML + CSS + 原生 JS | 無框架、無 Tailwind | | 字體 | 系統默認 | PingFang TC / Microsoft JhengHei | | 會話 | PHP Session | 無 JWT | ### 1.4 目錄結構 ``` my/ ├── config/ │ └── database.php # 資料庫連接 + 公共函數庫(核心引擎) │ ├── api/ # RESTful API │ ├── register.php # 代報單註冊 │ ├── withdraw.php # 提現 │ ├── user-info.php # 用戶資訊(含自動到期檢查觸發) │ ├── topology.php # 拓撲資料API │ ├── auth/ │ │ ├── login.php # 登入 │ │ └── change-password.php # 修改密碼 │ ├── dashboard.php # 看板聚合資料 │ ├── activate-self.php # 為自己激活 │ ├── team.php # 集群資料(含時間倒序排序) │ ├── transactions.php # 收支明細 │ ├── node-detail.php # 節點詳情 │ ├── cron/ │ │ └── settlement-trigger.php # 外部 cron 結算觸發端點(第二道防線) │ └── wallet/ │ ├── convert-key.php # 餘額轉激活碼 │ ├── my-keys.php # 我的激活碼列表 │ └── pin.php # PIN 設置/驗證 │ ├── h5/ # ★ 前端(單檔 SPA) │ ├── index.php # SPA 外殼(含所有頁面 + 組件) │ ├── login.php # 登入頁 │ ├── css/ │ │ └── style.css # 全站樣式 │ └── js/ │ ├── app.js # SPA 路由 + 全域狀態 + 頁面邏輯 │ └── api.js # API 請求封裝 │ ├── user/ # 舊版前端 ├── admin/ # 管理後台(★ 已擴充) │ ├── index.php # 會員管理 │ ├── activation.php # 激活碼管理 │ ├── dashboard.php # 管理後台看板 │ ├── finance.php # ★ 財務管理(套餐開通/提現管理/獎勵管理) │ ├── withdrawals.php # 提現審核(保留,側邊欄入口移至 finance.php) │ ├── clusters.php # 平衡組管理(全局總覽) │ ├── settlement.php # 結算任務 │ ├── settlement_console.php # 結算中控台(含拓撲三Tab視圖) │ ├── settlement_detail.php # 結算詳情 │ ├── login.php / logout.php # 管理員認證 │ ├── sidebar.php # ★ 共享側邊欄組件(v1.6) │ └── style.css # 後台公用樣式 │ ├── tasks/ # 定時任務 │ ├── check-expirations.php # ★ 到期檢查(獨立腳本備用) │ ├── monthly-settlement.php # 月度結算(獨立腳本備用) │ └── setup-cron.sh # Cron 配置腳本 │ ├── src/services/ # 服務層(★ 已擴充) │ └── settlement/ │ └── settlement.service.php # 結算引擎(含 getTopologyData 增強) │ ├── thrypton.sql # VPS 資料庫匯出 ├── pusatix_h5_v5.html # UI 設計藍本(純靜態原型) └── Thrypton-全案規格書-v2.md # 本文件 ``` --- ## 二、商業規則 ### 2.1 節點類型 | 屬性 | OC 機構節點 (OpenClaw) | HM 個人節點 (Hermes) | |------|----------------------|----------------------| | 有效期 | 1 年 | 1 年 | | 節點顏色 | 紫色 #a855f7 | 青色 #00d4ff | | **購買價格 (purchase_price)** | **RM 5,000** | **RM 2,500** | | 激活碼價值 | RM 5,000 | RM 2,500 | | 終身收益上限 (QUOTA) | RM 50,000 | RM 25,000 | | 靜態基準值 (max_output) | 667 | 333.75 | **月收益率配置**(`node_rate_config` 表,欄位 `monthly_rate`): | 年期 | OC 月收益 | HM 月收益 | |------|----------|----------| | 第 1 年 | RM 417.00 | RM 208.50 | | 第 2 年 | RM 167.00 | RM 83.50 | | 第 3 年 | RM 83.00 | RM 41.75 | **節點產出上限 (max_output)**: 系統存在兩種 max_output 概念: **① 靜態基準值(資料庫 `nodes.max_output`)**: ``` 寫入時機:節點創建時一次性寫入,之後永不再更新 OC 節點 = 三年月收益總和 = 417 + 167 + 83 = 667 HM 節點 = 三年月收益總和 = 208.5 + 83.5 + 41.75 = 333.75 不含加速度,純粹為節點終身產能的靜態快照 ★ 重要:max_output 僅用於節點產出進度展示,不再用於計算 withdrawal_quota。 withdrawal_quota 改為基於 purchase_price(購買價格)× 10,詳見 §2.5。 ``` **② 動態剩餘上限(運行時計算,不持久化)**: ``` 動態 max_output = total_produced + 剩餘產出能力 剩餘產出能力 = 當前年剩餘月數 × 當前年基準月收益 × (1 + 當前加速度) + YearN+1 全年 × 該年基準月收益 × (1 + 當前加速度) + YearN+2 全年 × 該年基準月收益 × (1 + 當前加速度) (最多算到 Year 3) 超過 3 年:已無生命週期上限,動態 max_output = total_produced ``` > ★ 重要說明:舊公式 `667 × (1+accel)` 隱含假設加速度從第一天即為當前值且三年不變,導致計算偏離實際。新公式中 `total_produced` 天然包含各階段的歷史加速度,剩餘部分以當前加速度預估,更精確反映真實剩餘產能。 > > 例:OC 節點已激活 5 個月(前 5 月加速度 0%),當前加速度變為 10% > total_produced = 5 × 417 × 1.0 = 2085(真實累計) > 剩餘 = 7 × 417 × 1.1 + 12 × 167 × 1.1 + 12 × 83 × 1.1 = 6510.9 > 動態 max_output = 2085 + 6510.9 = 8595.9 ### 2.2 收益公式 ``` 預期月收益 = 月基數 × (1 + 總加速度) 月基數 = SUM(每個活躍節點的當年月收益) 總加速度 = 個人特權 + 直推獎勵 + 網路協同 ``` ### 2.3 加成體系 #### 2.3.1 個人特權 | 條件 | 加成 | |------|------| | 每擁有 1 個全自有完整 active 平衡組 | +5%(按組數累加,不封頂) | | 無全自有平衡組 | 0% | **全自有平衡組**定義:一個 active 平衡組中的 3 個節點(1 OC + 2 HM)的 `user_id` 全部等於當前用戶。 > 注意:用戶僅持有足夠數量的 OC/HM 節點但分散在不同平衡組(每組均包含他人節點)時,**不計入**個人特權。必須在同一個平衡組中三台機器全為自己所有才算。 > > 例:2 個全自有平衡組 → +10%;3 個 → +15% #### 2.3.2 直推獎勵 | 直推類型 | 每人加成 | |----------|---------| | 直推 OC | +8% | | 直推 HM | +6% | 直推 = depth=1 的推薦關係中,有活躍節點的使用者。 #### 2.3.3 網路協同(核心) 以平衡組(Cluster)為單位計算,但費率由用戶自身是否持有 OC 節點決定。詳見 §2.4。 > ★ v1.4 修正:網路協同費率採「用戶級統一費率」模式 — 若用戶自身持有 OC 節點則每個符合條件的 cluster 均貢獻 +5%,否則貢獻 +4%。不再按單個 cluster 中哪個成員在推薦鏈上來決定費率高低。 ### 2.4 平衡組系統 #### 2.4.1 基本定義 ``` 一個平衡組 (Cluster) = 1 OC + 2 HM (固定比例,不可變) ``` #### 2.4.2 節點激活時的排隊邏輯(★ v1.1 改為團隊優先 → ★ v1.6 獨立部署重寫) ``` auto 模式(自動排列): 1. 搜尋節點所有者自己的 building 集群(自己缺位優先補) 2. 搜尋推薦樹內所有 building 集群(團隊內匹配) 3. 搜尋推薦樹內所有保護期(protection)集群(grace 節點替換) 4. 都沒找到 → 創建新集群(該節點佔位) 匹配規則精細: - OC 類型:匹配 oc_node_id IS NULL 的集群(缺 OC) - HM 類型:匹配 position=1 或 position=2 空缺的集群(缺 HM) - 保護期集群:匹配 grace 節點所在位置,直接替換(grace→expired) - 匹配優先級:步驟1→2→3→4,嚴格按順序 independent 模式(獨立部署)★ v1.6 完全重寫: 設計原則:獨立部署節點永遠不參與公共池匹配,僅與自己投資的節點成組。 系統通過三層防線確保此原則:deploy_mode 標記、user_id 限定、is_locked 鎖定。 1. 搜尋節點所有者自己創建的 independent + building 集群 → 若找到且空缺位置類型匹配 → 填入該集群 → 若找到但類型不匹配 → 不填入,轉步驟2 2. 未找到匹配的獨立集群 → 創建新 independent 集群並鎖定 → 寫入 deploy_mode = 'independent', is_locked = 1 → 集群對所有 auto 節點不可見,對其他用戶的 independent 節點不可見 鎖定規則(三層防線): ┌─────────────────────────────────────────────────────────────┐ │ 層級 │ 機制 │ 擋住誰 │ │───────┼─────────────────────────────────────────┼───────────────│ │ 1 │ clusters.deploy_mode = 'independent' │ auto 搜索函數 │ │ 2 │ n.user_id = {當前用戶} (嚴格限定) │ 其他用戶 │ │ 3 │ clusters.is_locked = 1 (auto搜索過濾) │ auto 節點 │ └─────────────────────────────────────────────────────────────┘ ``` > ★ v1.1 重大變更:v1.0 的「全局搜尋」已廢棄。集群匹配限定於推薦樹內,不再跨團隊匹配。這確保團隊利益不洩露。 > > ★ v1.6 重大變更:獨立部署從「不搜尋直接建」重構為「搜尋自有鎖定集群 → 填充 → 滿員解鎖」的封閉式流程。新增 `clusters.deploy_mode`、`is_locked` 欄位與 `cluster_operation_log` 審計表。 #### 2.4.3 Cluster 狀態機(★ v1.6 擴充:is_locked 鎖定生命週期) ``` auto 集群: 創建 cluster(building, is_locked=0) │ ├─ 只有 OC(缺 2 HM) → 等待中 ├─ OC + 1 HM(缺 1 HM) → 等待中 └─ OC + 2 HM 全到齊 → active(完整) independent 集群(★ v1.6 新增 is_locked 狀態機): 創建 cluster(building, is_locked=1, deploy_mode='independent') │ │ 同用戶 independent 節點激活,位置類型匹配 → 填入(仍鎖定) │ ├─ 未滿 3 人 → building, is_locked=1(繼續鎖定,auto 不可見) └─ 滿 3 人 → active, is_locked=0(解鎖,完整成組) │ │ 某節點到期 → grace → protection_until=45天 │ 狀態保持 active, is_locked=0(保護期內不重鎖) │ └─ 45 天後 grace→expired,從 cluster_members 移除 → 集群人數 < 3 → status = 'building' → is_locked = 1 ★ 降級重鎖(防止 auto 節點趁虛而入) → 返回等待狀態,僅同用戶 independent 節點可填入 ``` #### 2.4.4 網路協同增益計算(結算層,不影響匹配) ``` 1. 判斷掃描深度: 用戶自身有全自有 active cluster → 掃描深度 = 20 層 用戶自身無全自有 active cluster → 掃描深度 = 10 層 ★ 全自有 active cluster 定義:一個 active 集群中的 3 個節點 (1 OC + 2 HM) 的 user_id 全部等於當前用戶 2. 確定用戶級網路費率(★ v1.4 關鍵變更): 若用戶自身持有任一 active OC 節點 → 費率 = +5% 若用戶自身無 active OC 節點 → 費率 = +4% (費率由用戶自身節點類型決定,而非按 cluster 中誰在鏈上決定) 3. 從 relationships 表展平推薦樹至指定深度 收集範圍內所有 user_id 4. 遍歷範圍內所有 status='active' 的 cluster 對每個 cluster: a. 取 cluster 中 3 個節點的擁有者 (OC owner + HM1 owner + HM2 owner) b. 追溯每個 owner 是否在用戶的推薦鏈上(不限層級,沿 users.parent_id 向上遞歸) c. 判定增益: - OC owner 或 HM1 owner 或 HM2 owner 任一在推薦鏈上 → 該組 +(步驟2確定的用戶費率) - 三者都不在推薦鏈上 → 該組 +0% d. 每組只計一次 5. 所有 cluster 增益加總 = 網路協同總幣 ★ v1.4 設計理念變更(與 v1.3 的關鍵差異): - v1.3:逐 cluster 判斷費率(該 cluster 的 OC 在鏈上→5%,僅 HM 在鏈上→4%) - v1.4:用戶級統一費率(用戶有 OC→所有符合條件 cluster 統一 5%,無 OC→統一 4%) - 此變更簡化計算邏輯,強化 OC 持有者的收益優勢 ``` #### 2.4.5 節點到期與保護期機制(★ v1.1 新增) ``` 節點到期處理流程: 1. 節點 expired_at 到期 └─ 節點 status 變為 'grace'(非 active,非 expired) └─ 節點基礎收益暫停 └─ 所在集群 protection_until = NOW() + 45 天 └─ 集群保持 active,成員協同收益照發 └─ 若節點無集群(independent),直接變 'expired' 2. 保護期內(45天): ├─ 到期者續費 → grace 節點恢復 active,集群恢復正常 ├─ 新同類型節點激活 → 自動替換 grace 節點位置 │ └─ grace 節點變 expired,從集群移除 │ └─ 新節點佔位,集群 protection_until 清空 └─ 集群協同收益在保護期內照常發放 3. 保護期到期(45天後無人續費無人補位): └─ grace 節點正式變 'expired' └─ 從 cluster_members 移除 └─ 集群 protection_until 清空 └─ 集群人數 < 3 → status 變 'building' └─ ★ v1.6 獨立集群降級時自動重鎖 `is_locked = 1` └─ 剩餘成員進入優先匹配池(auto)或等待同用戶 independent 節點 節點狀態機: active ──(到期)──→ grace ──(45天到期/被替換)──→ expired │ │ └──(續費恢復)←───────┘ 設計理念: - 剩餘成員不受連坐:45天內協同收益照發 - 刺激續費:不續費就丟位,45天窗口有緊迫感 - 新節點接盤:到期不續費,系統自動找新節點補位 - 公司最多承擔45天協同收益支出 ``` #### 2.4.6 增益範例 ``` 前提:用戶 Alice 持有 1 個 OC 節點 → 用戶級網路費率 = +5% 用戶 Bob 持有 0 個 OC 節點 → 用戶級網路費率 = +4% 範例 1: 用戶自己的全自有 cluster OC(Alice) + HM1(Alice) + HM2(Alice) = active cluster → 網路協同 +0%(全自有 cluster 不計入網路協同,收益歸「個人特權」+5%) 範例 2: Alice(有 OC, 費率 5%)的傘下 cluster OC(下級A) + HM1(下級B) + HM2(外人) → 下級A 和下級B 的 owner 均在推薦鏈上 → 該組 +5%(Alice 持有 OC) 範例 3: Bob(無 OC, 費率 4%)的傘下 cluster OC(外人) + HM1(下級C) + HM2(外人) → 下級C 的 owner 在推薦鏈上 → 該組 +4%(Bob 無 OC) → 雖 OC 不在鏈上,但因 Bob 無 OC,費率統一為 +4% 範例 4: 傘下團隊(無推薦關係) OC(外人1) + HM1(外人2) + HM2(外人3) → 三人都不在推薦鏈上 → +0% ★ v1.4 關鍵變化:費率由用戶是否持有 OC 決定,而非 cluster 中哪個成員在鏈上 ``` ### 2.5 提現規則 #### 2.5.1 基本提現規則 | 規則 | 值 | |------|----| | 最低提現 | RM 500 | | 金額倍數 | 500 的整數倍 | | 處理時間 | 1-3 工作日 | | 結算鎖定期 | 每月 28 日至次月 1 日(含 1 日,即 28、29、30、31、1 日)禁止提現 | | 驗證方式 | 6 位 PIN 碼 | #### 2.5.2 終身收益上限 (QUOTA) — 1:10 內生循環 ``` 設計目的:內建 1:10 額度激活邏輯,獲利者必須通過二次採購 Token Plan 來維持算力產出,為系統提供持續的內生性現金流支撐。 初始化:new user 註冊 activation_key → withdrawal_quota 初始值 = 0 增量公式:每次激活節點 OC → withdrawal_quota += 50,000 (purchase_price 5,000 × 10) HM → withdrawal_quota += 25,000 (purchase_price 2,500 × 10) 扣減時機:每次成功提現 withdrawal_quota -= 提現金額 節點到期:不扣減 withdrawal_quota(配額為累計歷史總額度,到期不影響已發放配額) 封頂邏輯: 用戶終身可提現總額 ≤ Σ(所有已激活節點的 purchase_price × 10) 例:激活 1 OC + 2 HM → 總配額 = 50,000 + 25,000 + 25,000 = 100,000 達到上限後需再激活新節點才能解鎖更多額度 ``` #### 2.5.3 月提現流閥 (DAM) — 投資額 × 3 ``` 設計目的:每月提現總量不能超過用戶投資額的 3 倍, 防止短時間內大量資金流出,保護系統穩定性。 公式: 當月 DAM 上限 = (OC 活躍數 × 5,000 + HM 活躍數 × 2,500) × 3 當月已提現 = SUM(amount) FROM withdrawal_journal WHERE user_id=? AND status='completed' AND MONTH(created_at)=本月 AND YEAR(created_at)=本年 校驗邏輯: if 當月已提現 + 本次提現金額 > 當月 DAM 上限: → 攔截: "超過本月提現上限" ★ 每月重置,不累積到次月 ``` #### 2.5.4 提現校驗順序 ``` ① 結算鎖定期檢查: 是否在 28-1 日? → "系統結算中,暫停提現" ② 金額格式檢查: 非 500 整數倍? → "提現金額必須是 RM500 的整數倍" ③ QUOTA 檢查: withdrawal_quota < 金額? → "超出可用配額" ④ 餘額檢查: wallet_balance < 金額? → "餘額不足" ⑤ 銀行賬戶檢查: bank_account 或 account_name 為空? → 攔截 ⑥ PIN 驗證: 尚未設置 / 未輸入 / PIN 碼錯誤? → 攔截 ⑦ DAM 檢查: 當月已提現+本次 > 當月 DAM 上限? → "超過本月提現上限" ⑧ 通過 → 扣減 balance + quota,寫入 withdrawal_journal(事務保護) ★ v1.4: 帳戶凍結檢查在登入階段完成(login.php),提現 API 不再重複檢查。 若帳戶已被凍結,用戶無法登入,因此無法發起提現請求。 ``` #### 2.5.5 兩層燒傷機制對比 | | QUOTA(終身收益上限) | DAM(月提現流閥) | |------|:---:|:---:| | 計算基準 | purchase_price × 10 | purchase_price × 3 | | OC 基值 | 5,000 × 10 = 50,000 | 5,000 × 3 = 15,000 | | HM 基值 | 2,500 × 10 = 25,000 | 2,500 × 3 = 7,500 | | 時間維度 | 終身累計 | 每月獨立(次月重置) | | 扣減方式 | 提現逐次扣減 | 僅比對不扣減 | | 節點到期 | 不扣減 | 活躍節點數減少,上限自然降低 | | 設計目的 | 內生循環:逼用戶二次採購 | 風控流閥:保護系統流動性 | ### 2.6 結算規則 | 規則 | 值 | |------|----| | 結算週期 | 月結 | | 鎖定開始 | 每月 28 日 | | 鎖定結束 | 次月 1 日(含 1 日) | | 鎖定日期 | 28、29、30、31、1 日 | | 鎖定期禁止 | 報單、提現、轉激活碼 | ### 2.7 結算保障機制(三道防線) 系統透過三道防線確保月度結算必然執行: | 防線 | 觸發方式 | 說明 | |------|---------|------| | 第一道 | VPS 本地 cron | `tasks/monthly-settlement.php`,每月 1 日 01:00 執行 | | 第二道 | 外部 cron-job.org | `api/cron/settlement-trigger.php`,獨立於 VPS,防止 VPS 宕機漏結算 | | 第三道 | 用戶訪問巡檢 (patrol) | `config/database.php` 每次請求自動檢查當月是否已結算,未結算則自動補執行 | **心跳標記**(存於 `system_config` 表): - `cron_heartbeat` — VPS cron 最後執行時間 - `cron_external_heartbeat` — 外部 cron 最後執行時間 - `last_patrol_check` — 巡檢最後觸發時間 **force_lock(強制鎖定)**: - 管理員可透過後台手動啟用/關閉強制鎖定 - 強制鎖定獨立於結算鎖定期,優先級最高(三層鎖定:force_lock > 結算進行中 > 週期鎖定) - Lock-status API 返回三層鎖定狀態:`forceLock`、`settling`、`periodLock` ### 2.8 餘額轉激活碼 - 用戶可用錢包餘額生成新的激活碼(等同購買節點) - OC 激活碼價值 RM 5000,HM 激活碼價值 RM 2500 - 激活碼生成後不可撤銷 - 激活碼可轉發他人使用 ### 2.9 推薦關係不可變更 - 推薦關係一經建立(用戶註冊時綁定 parent_id),**永久不可更改** - 即使管理員透過後台編輯用戶,也禁止修改 parent_id 欄位 - 管理後台的用戶編輯介面僅顯示推薦人資訊,不提供修改入口 - 所有 API 和後台接口在 UPDATE users 時必須排除 parent_id 欄位 --- ## 三、資料庫設計 ### 3.1 ER 關係圖(文字版) ``` users ────┬──────────── nodes │ │ └── deploy_mode (ENUM: auto/independent) │ │ │ ├──────────── relationships (推薦樹, 自引用) │ │ │ ├── 1:1 ───── user_pin (新) │ │ │ ├── 1:N ───── user_bank_accounts (新) │ │ │ ├── 1:N ───── user_generated_keys (新) │ │ │ └── 1:N ───── withdrawal_journal │ └── bank_account, account_name (新欄位) nodes ──────┬─────────── clusters (OC node 關聯) │ └─────────── cluster_members (HM node 關聯, 新) activation_keys ───────── user_generated_keys (來源追蹤) clusters ─── 1:N ──────── cluster_members └── deploy_mode (ENUM: auto/independent) ★ v1.6 └── is_locked (TINYINT(1)) ★ v1.6 clusters ─── 1:N ──────── cluster_operation_log ★ v1.6 新增 settlement_tasks ── 1:N ─ settlement_journal settlement_tasks ── 1:N ─ settlement_audit_log > ★ `settlement_tasks` 和 `settlement_audit_log` 含 `triggered_by VARCHAR(20)` 欄位,值為:`cron_vps` / `cron_external` / `patrol` / `manual`,記錄結算由哪道防線觸發。 ``` ### 3.2 現有表(摘要) #### users | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | username | VARCHAR(50) UNIQUE | | | password | VARCHAR(255) | BCRYPT | | parent_id | INT | 推薦人 | | wallet_balance | FLOAT | 錢包餘額 | | withdrawal_quota | FLOAT | 提現配額(終身收益上限)。Σ(該用戶所有已激活節點的 purchase_price × 10)。節點激活時自動增加、提現時扣減,節點到期不扣減。詳見 §2.5.2 | | status | ENUM('active','frozen') | | | frozen_at | DATETIME | 帳戶被凍結的時間(NULL = 未凍結) | | frozen_reason | VARCHAR(255) | 凍結原因 | | created_at | DATETIME | | | updated_at | DATETIME | 最後更新時間 | > ★ **凍結帳戶檢查機制**:帳戶凍結檢查在登入階段完成。 > > 登入時(`api/auth/login.php`),若 `users.status = 'frozen'`,返回 403 錯誤 "賬戶已被凍結,請聯繫管理員",阻止用戶登入。 > 由於凍結用戶無法登入取得 session,因此無法調用任何需認證的 API(包括提現、激活、轉換等)。 > > 管理員可通過後台(`admin/index.php`)手動凍結/解凍用戶帳戶。提現 API(`api/withdraw.php`)不重複檢查 frozen 狀態,因為凍結用戶根本無法通過登入關卡。 #### nodes | 欄位 | 類型 | 說明 | |------|------|------| | id | VARCHAR(50) PK | | | user_id | INT FK | | | type | ENUM('OC','HM') | | | status | ENUM('active','expired','completed','grace') | ★ v1.1 新增 grace 狀態 | | **deploy_mode** | ENUM('auto','independent') DEFAULT 'auto' | ★ 新欄位 | | activated_at | DATETIME | | | expired_at | DATETIME | | | total_produced | FLOAT | | | max_output | FLOAT | 靜態基準值(OC=667, HM=333.75),三年月收益總和,不含加速度。節點創建時一次性寫入,永不更新。動態上限需運行時計算 | | purchase_price | DECIMAL(10,2) | 節點購買價格(OC=5000, HM=2500)。★ v1.4:提現配額和 DAM 均以此為計算基準 | | created_at | DATETIME | | #### relationships | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | parent_id | INT | 上級 | | child_id | INT | 下級 | | depth | INT | 層級深度 | #### activation_keys | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | activation_key | VARCHAR(50) UNIQUE | | | status | ENUM('unused','used') | | | node_type | ENUM('OC','HM') | | | used_by | INT | | | used_at | DATETIME | | #### clusters(★ v1.6 擴充) | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | display_code | VARCHAR(6) | 6位 base36 編碼 | | oc_node_id | VARCHAR(50) | OC 節點 ID | | status | ENUM('building','active') | | | deploy_mode | ENUM('auto','independent') | ★ v1.6 部署模式,繼承自首個入駐節點 | | is_locked | TINYINT(1) | ★ v1.6 鎖定標記(independent 集群 building 期間=1) | | protection_until | DATETIME | 保護期截止時間 | | created_at | DATETIME | | #### cluster_members | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | cluster_id | INT FK | | | node_id | VARCHAR(50) | | | position | TINYINT | 0=OC, 1=HM1, 2=HM2 | | joined_at | DATETIME | | #### withdrawal_journal | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | user_id | INT FK | | | amount | DECIMAL | | | **bank_account** | VARCHAR(50) | ★ 新欄位 | | **account_name** | VARCHAR(100) | ★ 新欄位 | | status | ENUM('pending','completed','failed') | | | request_id | VARCHAR(100) | 冪等性 | | created_at | DATETIME | | #### admins | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | username | VARCHAR(50) UNIQUE | | | password | VARCHAR(255) | BCRYPT | | role | ENUM('SA') | 目前僅超級管理員 | | created_at | DATETIME | | #### idempotency_keys | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | request_id | VARCHAR(100) UNIQUE | X-Request-ID 標頭值 | | response | TEXT | 儲存的回應 JSON | | request_type | VARCHAR(50) | 請求類型(如 withdraw) | | status | VARCHAR(20) | | | executed_at | DATETIME | | | created_at | DATETIME | | #### system_config | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | config_key | VARCHAR(100) UNIQUE | 如 force_lock、last_cron_run | | config_value | TEXT | | | description | VARCHAR(255) | | | created_at | DATETIME | | | updated_at | DATETIME | | #### node_rate_config | 欄位 | 類型 | 說明 | |------|------|------| | id | INT PK | | | node_type | ENUM('OC','HM') | | | tier | TINYINT | 年期(1/2/3) | | monthly_rate | DECIMAL(10,2) | 月收益基數 | | created_at | DATETIME | | | updated_at | DATETIME | | ### 3.3 新表完整 DDL 在 phpMyAdmin 中選 `thrypton` 資料庫後執行: ```sql -- ============================================ -- Thrypton Cluster Ecosystem DDL -- 在 phpMyAdmin → thrypton 資料庫 → SQL 標籤頁執行 -- ============================================ -- 1. nodes 增加部署模式 ALTER TABLE nodes ADD COLUMN deploy_mode ENUM('auto','independent') DEFAULT 'auto' AFTER status; -- 2. 使用者 PIN 碼 CREATE TABLE user_pin ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL UNIQUE, pin_hash VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3. 使用者銀行帳戶 CREATE TABLE user_bank_accounts ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, account_number VARCHAR(50) NOT NULL, account_name VARCHAR(100) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 4. 使用者自生成激活碼 CREATE TABLE user_generated_keys ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, activation_key VARCHAR(50) NOT NULL UNIQUE, node_type ENUM('OC','HM') NOT NULL, cost DECIMAL(10,2) NOT NULL, status ENUM('unused','used') DEFAULT 'unused', used_by INT DEFAULT NULL, used_at DATETIME DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5. 平衡組(★ v1.6 擴充:deploy_mode + is_locked) CREATE TABLE clusters ( id INT AUTO_INCREMENT PRIMARY KEY, display_code VARCHAR(6) NOT NULL DEFAULT '' COMMENT '6位base36編碼,隱藏真實數量', oc_node_id VARCHAR(50) DEFAULT NULL, status ENUM('building','active') DEFAULT 'building', deploy_mode ENUM('auto','independent') NOT NULL DEFAULT 'auto' COMMENT '★ v1.6 部署模式,繼承自首個入駐節點', is_locked TINYINT(1) NOT NULL DEFAULT 0 COMMENT '★ v1.6 鎖定標記:independent集群building期間=1,滿員解鎖=0,降級重鎖=1', protection_until DATETIME DEFAULT NULL COMMENT '保護期截止時間(45天)', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 6. 平衡組成員(含 OC/HM 佔位) CREATE TABLE cluster_members ( id INT AUTO_INCREMENT PRIMARY KEY, cluster_id INT NOT NULL, node_id VARCHAR(50) NOT NULL, position TINYINT NOT NULL COMMENT '0=OC, 1=HM位置1, 2=HM位置2', joined_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE, UNIQUE KEY uk_cluster_pos (cluster_id, position) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 7. withdrawal_journal 加銀行欄位 ALTER TABLE withdrawal_journal ADD COLUMN bank_account VARCHAR(50) DEFAULT NULL AFTER amount, ADD COLUMN account_name VARCHAR(100) DEFAULT NULL AFTER bank_account; -- 8. 獎勵記錄表(★ v1.1 新增) CREATE TABLE bonus_records ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL COMMENT '獲得獎勵的用戶ID', bonus_type ENUM('direct_referral','network_synergy','personal_privilege') NOT NULL, bonus_rate DECIMAL(5,4) NOT NULL COMMENT '獎勵比例 如0.0600', source_node_id VARCHAR(50) DEFAULT NULL COMMENT '來源節點ID(直推獎勵用)', source_user_id INT DEFAULT NULL COMMENT '來源節點所屬用戶ID', source_cluster_id INT DEFAULT NULL COMMENT '來源平衡組ID(網路協同用)', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='獎勵記錄表'; -- 9. 集群操作審計日誌(★ v1.6 新增) CREATE TABLE cluster_operation_log ( id INT AUTO_INCREMENT PRIMARY KEY, cluster_id INT NOT NULL, node_id VARCHAR(50) DEFAULT NULL COMMENT '節點ID(集群級操作時為NULL)', operation ENUM('create','join','leave','replace','complete','unlock','relock','expire') NOT NULL, deploy_mode ENUM('auto','independent') NOT NULL COMMENT '操作時的部署模式', operator_user_id INT NOT NULL COMMENT '發起操作的用戶ID', detail JSON DEFAULT NULL COMMENT '額外上下文(匹配步驟、替換原因等)', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_cluster (cluster_id), INDEX idx_node (node_id), INDEX idx_created (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='集群操作審計日誌'; ``` --- ## 四、核心演算法偽代碼 ### 4.1 節點激活排隊 `assignNodeToCluster()`(★ v1.1 團隊優先 → ★ v1.6 獨立分支重寫) ``` function assignNodeToCluster(nodeType, nodeId, deployMode, nodeOwnerId): // ========== ★ v1.6 INDEPENDENT 分支(完整重寫) ========== if deployMode === 'independent': // 步驟 1: 搜尋自己未滿的 independent 集群 myCluster = findMyIndependentCluster(nodeOwnerId, nodeType) if myCluster != null: // 類型匹配 → 填入空位 addToCluster(myCluster.id, nodeId, nodeType) logClusterOperation(clusterId, nodeId, 'join', 'independent', nodeOwnerId) completeClusterIfReady(myCluster.id) return myCluster.id // 步驟 2: 無匹配 → 新建 + 鎖定 clusterId = createNewCluster(nodeType, nodeId, 'independent') logClusterOperation(clusterId, nodeId, 'create', 'independent', nodeOwnerId) return clusterId // ========== AUTO 分支(★ v1.6 搜索函數加 is_locked=0 過濾) ========== waitingCluster = null // 步驟 1: 自己的 building 集群(is_locked=0) waitingCluster = findMyWaitingCluster(nodeOwnerId, nodeType) // 步驟 2: 推薦樹內的 building 集群(is_locked=0) if waitingCluster == null: waitingCluster = findTeamWaitingCluster(nodeOwnerId, nodeType) // 步驟 3: 推薦樹內的 protection 集群(替換 grace 節點, is_locked=0) if waitingCluster == null: protectionCluster = findTeamProtectionCluster(nodeOwnerId, nodeType) if protectionCluster != null: graceNodeId = getGraceNodeAtPosition(protectionCluster, gracePosition) UPDATE nodes SET status='expired' WHERE id=graceNodeId DELETE FROM cluster_members WHERE node_id=graceNodeId INSERT INTO cluster_members (cluster_id, node_id, position) VALUES (...) UPDATE clusters SET protection_until=NULL completeClusterIfReady(clusterId) return clusterId // 步驟 4: 新建 auto 集群 if waitingCluster == null: return createNewCluster(nodeType, nodeId) // 填入步驟 1 或 2 找到的集群 addToCluster(waitingCluster.id, nodeId, nodeType) completeClusterIfReady(waitingCluster.id) return waitingCluster.id ``` ### 4.2 叢集搜尋函數群(★ v1.1 擴充 → ★ v1.6 新増獨立函數 + 鎖定過濾) #### `findMyIndependentCluster(userId, nodeType)` ★ v1.6 新增 ``` // 搜尋用戶自己創建的 independent + building 集群(不要求 is_locked=1,因降級重鎖後也會匹配) SELECT c.* FROM clusters c JOIN cluster_members cm ON c.id = cm.cluster_id JOIN nodes n ON cm.node_id = n.id WHERE c.deploy_mode = 'independent' AND c.status = 'building' AND n.user_id = userId GROUP BY c.id ORDER BY c.created_at ASC // 篩選缺該類型的集群(與 findMyWaitingCluster 邏輯相同) OC: oc_node_id IS NULL → return HM: filled HM positions < 2 → return ``` #### `findMyWaitingCluster(userId, nodeType)` ``` // 搜尋用戶自己的 building 集群(缺位的優先, ★ v1.6 加 is_locked=0 過濾) SELECT c.*, COUNT(cm.id) as member_count, GROUP_CONCAT(cm.position) FROM clusters c JOIN cluster_members cm ON c.id = cm.cluster_id JOIN nodes n ON cm.node_id = n.id WHERE c.status = 'building' AND c.is_locked = 0 AND n.user_id = userId GROUP BY c.id ORDER BY c.created_at ASC // 篩選缺該類型的集群 OC: oc_node_id IS NULL → return HM: filled HM positions < 2 → return ``` #### `findTeamWaitingCluster(userId, nodeType)` ``` // 追溯推薦根節點 rootId = traceRoot(userId) // 沿 parent_id 向上到 null // 展平整個推薦樹 teamUserIds = flattenReferralTree(rootId, 20) // 搜尋推薦樹內所有 building 集群(★ v1.6 加 is_locked=0 過濾) SELECT c.*, ... WHERE c.status='building' AND c.is_locked = 0 AND n.user_id IN (teamUserIds) GROUP BY c.id ORDER BY c.created_at ASC // 篩選邏輯同上 ``` #### `findTeamProtectionCluster(userId, nodeType)` ``` // 搜尋推薦樹內所有保護期集群 SELECT c.*, GROUP_CONCAT(cm.position), GROUP_CONCAT(CASE WHEN n.status='grace' THEN cm.position END) as grace_positions FROM clusters c JOIN cluster_members cm ON c.id = cm.cluster_id JOIN nodes n ON cm.node_id = n.id WHERE c.protection_until IS NOT NULL AND c.protection_until > NOW() AND c.is_locked = 0 AND n.user_id IN (teamUserIds) GROUP BY c.id ORDER BY c.created_at ASC // 匹配規則 OC: 0 ∈ grace_positions → return HM: 1∈grace_positions or 2∈grace_positions → return ``` ### 4.3 創建新 Cluster `createNewCluster()`(★ v1.6 簽名擴充) ``` function createNewCluster(nodeType, nodeId, deployMode = 'auto'): displayCode = LPAD(CONV(UNIX_TIMESTAMP(), 10, 36), 6, '0') isLocked = (deployMode === 'independent') ? 1 : 0 INSERT INTO clusters (status, display_code, deploy_mode, is_locked) VALUES ('building', displayCode, deployMode, isLocked) clusterId = lastInsertId if nodeType == 'OC': UPDATE clusters SET oc_node_id = nodeId WHERE id = clusterId INSERT INTO cluster_members (cluster_id, node_id, position) VALUES (clusterId, nodeId, 0) if nodeType == 'HM': INSERT INTO cluster_members (cluster_id, node_id, position) VALUES (clusterId, nodeId, 1) // 佔用 HM 位置 1 // position 2 空缺,等待另一個 HM return clusterId ``` > `display_code` 為 6 位 base36 編碼,隱藏真實集群數量。 ### 4.4 檢查滿員 `completeClusterIfReady()`(★ v1.6 擴充:獨立集群解鎖) ``` function completeClusterIfReady(clusterId): SELECT COUNT(*) as cnt FROM cluster_members WHERE cluster_id = clusterId if cnt >= 3: // 3 個位置全滿 = 1 OC + 2 HM UPDATE clusters SET status = 'active', protection_until = NULL WHERE id = clusterId // ★ v1.6 獨立集群解鎖 if clusters.deploy_mode === 'independent': UPDATE clusters SET is_locked = 0 WHERE id = clusterId logClusterOperation(clusterId, null, 'unlock', 'independent', 0) ``` ### 4.6 集群操作審計 `logClusterOperation()` ★ v1.6 新增 ``` function logClusterOperation(clusterId, nodeId, operation, deployMode, operatorUserId, detail): detailJson = json_encode(detail) INSERT INTO cluster_operation_log (cluster_id, node_id, operation, deploy_mode, operator_user_id, detail) VALUES (clusterId, nodeId, operation, deployMode, operatorUserId, detailJson) ``` | operation 取值 | 觸發時機 | |:---|------| | `create` | `createNewCluster()` 新建集群 | | `join` | `assignNodeToCluster()` 節點填入已有集群 | | `replace` | `assignNodeToCluster()` auto 步驟③ grace 節點替換 | | `complete` | `completeClusterIfReady()` 3人滿員 | | `unlock` | `completeClusterIfReady()` 獨立集群滿員解鎖 | | `relock` | `check-expirations.php` 獨立集群降級重鎖 | | `leave` / `expire` | `check-expirations.php` 節點到期離開 | ### 4.7 獨立集群降級重鎖(★ v1.6 新增,位於 `check-expirations.php`) ``` // 保護期到期後,grace 節點變 expired、從 cluster_members 移除 // 集群人數 < 3 → status 降為 'building' if remainingMemberCount < 3: UPDATE clusters SET status = 'building' WHERE id = clusterId if clusters.deploy_mode === 'independent': UPDATE clusters SET is_locked = 1 WHERE id = clusterId logClusterOperation(clusterId, null, 'relock', 'independent', 0) ``` ### 4.8 推薦鏈追溯 `isUserInReferralChain()` ``` function isUserInReferralChain(ownerUserId, targetUserId): // 判斷 ownerUserId 是否在 targetUserId 的推薦鏈上 // 從 ownerUserId 沿 parent_id 向上追溯,直到 null 或找到 targetUserId // ★ 自己不計入推薦鏈(自己的 cluster 收益歸「個人特權」) currentId = ownerUserId while currentId is not null: SELECT parent_id FROM users WHERE id = currentId if parent_id == targetUserId: return true if parent_id is null: return false currentId = parent_id return false ``` ### 4.9 網路協同計算 `calculateNetworkBoost()` ``` function calculateNetworkBoost(userId): result = { boost: 0, clusterDetails: [] } // 判斷掃描深度 hasActive = hasActiveCluster(userId) scanDepth = hasActive ? 20 : 10 // 展平推薦樹 downlineUserIds = flattenReferralTree(userId, scanDepth) // 加入自身 allUserIds = [userId] + downlineUserIds // 查詢範圍內所有 active cluster activeClusters = findActiveClustersInUserScope(allUserIds) for each cluster in activeClusters: // 獲取 3 個節點的 owner ocOwnerId = getNodeOwner(cluster.ocNodeId) hm1OwnerId = getNodeOwner(cluster.hm1NodeId) hm2OwnerId = getNodeOwner(cluster.hm2NodeId) boost = 0 // OC owner 在推薦鏈上? if isUserInReferralChain(ocOwnerId, userId): boost = 5% // OC 不在,任一 HM 在鏈上? else if isUserInReferralChain(hm1OwnerId, userId) or isUserInReferralChain(hm2OwnerId, userId): boost = 4% result.boost += boost result.clusterDetails.push({ clusterId: cluster.id, ocOwnerId, hm1OwnerId, hm2OwnerId, boost: boost }) return result ``` ### 4.7 總收益計算 ``` function calculateTotalYield(userId): // 月基數 monthlyBase = 0 nodes = getUserActiveNodes(userId) for each node: yearNumber = getNodeYear(node.activatedAt) monthlyRate = getMonthlyRate(node.type, yearNumber) monthlyBase += monthlyRate // 個人特權(按全自有平衡組數量累加) personalPrivilege = countFullyOwnedClusters(userId) * 0.05 // 直推獎勵 directReferralBonus = 0 directReferrals = getDirectReferrals(userId) for each ref: if ref.hasActiveNode: if ref.nodeType == 'OC': directReferralBonus += 0.08 if ref.nodeType == 'HM': directReferralBonus += 0.06 // 網路協同 networkBoost = calculateNetworkBoost(userId).boost // 總加速度 totalBoost = personalPrivilege + directReferralBonus + networkBoost // 預期月收益 expectedMonthly = monthlyBase * (1 + totalBoost) return { monthlyBase, personalPrivilege, directReferralBonus, networkBoost, totalBoost, expectedMonthly } ``` ### 4.8 到期檢查與保護期處理(★ v1.1 新增) ``` 觸發方式:config/database.php 被任何請求 include 時自動檢查 頻率:每小時最多觸發一次(透過 system_config.last_cron_run 記錄) function autoCronCheck(): // 1. 節流檢查 lastRun = SELECT config_value FROM system_config WHERE config_key='last_cron_run' if time() - lastRun < 3600: return UPDATE system_config SET config_value=NOW() WHERE config_key='last_cron_run' // 2. 到期節點 → grace SELECT * FROM nodes WHERE status='active' AND expired_at <= NOW() for each expiredNode: UPDATE nodes SET status='grace', expired_at=DATE_ADD(NOW(), INTERVAL 45 DAY) cluster = SELECT cluster_id FROM cluster_members WHERE node_id=expiredNode.id if cluster: UPDATE clusters SET protection_until=DATE_ADD(NOW(), INTERVAL 45 DAY) else: UPDATE nodes SET status='expired' // 3. 保護期到期 → 正式踢出 SELECT * FROM clusters WHERE protection_until IS NOT NULL AND protection_until <= NOW() for each cluster: graceNodes = SELECT cm.* FROM cluster_members cm JOIN nodes n ON cm.node_id=n.id WHERE cm.cluster_id=cluster.id AND n.status='grace' for each gn: UPDATE nodes SET status='expired' WHERE id=gn.node_id DELETE FROM cluster_members WHERE id=gn.member_id if gn.position==0: UPDATE clusters SET oc_node_id=NULL WHERE id=cluster.id UPDATE clusters SET protection_until=NULL WHERE id=cluster.id if COUNT(cluster_members) < 3: UPDATE clusters SET status='building' ``` --- ## 五、API 文件 ### 5.1 通用規範 ``` Content-Type: application/json; charset=utf-8 X-Request-ID: <唯一請求ID> (可選,用於冪等性) Cookie: PHPSESSID=xxx (登入後) 成功回應: { "success": true, "message": "...", "data": { ... } } 錯誤回應: { "error": "錯誤訊息" } ``` ### 5.2 端點清單 #### POST /api/auth/login.php (新) ``` Request: { "username": "string", "password": "string" } Response 200: { "success": true, "data": { "user_id": 123, "username": "test_user_1", "parent_username": "admin", "is_activated": true } } Response 401: { "error": "用戶名或密碼錯誤" } Response 403: { "error": "賬戶已被凍結" } ``` #### POST /api/auth/change-password.php (新) ``` Request: { "old_password": "string", "new_password": "string" } Response 200: { "success": true, "message": "密碼已更新" } ``` #### GET /api/dashboard.php (新) ``` Response 200: { "success": true, "data": { "user": { "user_id": 123, "username": "test_user_1", "parent_username": "admin", "wallet_balance": 10000, "withdrawal_quota": 100000, "status": "active" }, "nodes": [ { "id": "NODE_HM_001", "type": "HM", "status": "active", "deploy_mode": "auto", "activated_at": "2026-05-02 17:47:58", "expired_at": "2026-05-02 17:47:58", "total_produced": 0, "max_output": 5004, "year_number": 1, "monthly_income": 417, "remaining_months": 12, "progress_percent": 0 } ], "node_summary": { "total": 1, "active": 1, "total_max_output": 5004 }, "yield": { "monthly_base": 104.25, "personal_privilege": 0.05, "personal_privilege_pct": "5%", "direct_referral_bonus": 0.06, "direct_referral_bonus_pct": "6%", "network_boost": 0.05, "network_boost_pct": "5%", "network_boost_details": [ { "cluster_id": 1, "oc_owner_id": 123, "hm1_owner_id": 123, "hm2_owner_id": 456, "boost": 0.05, "boost_pct": "5%" } ], "total_boost": 0.16, "total_boost_pct": "16%", "expected_monthly": 120.93, "scan_depth": 20, "has_active_cluster": true, "my_active_clusters": 1, "my_building_clusters": 1, "downline_active_clusters": 2, "last_settlement_date": "2026-04-28", "last_settlement_amount": 26324 }, "clusters": { "my_total": 2, "my_active": 1, "my_building": 1 }, "direct_referrals": { "count": 1, "list": [ { "id": 2, "username": "test_user_2", "node_type": "HM" } ] }, "settlement": { "is_locked": false, "message": "系統正常" }, "dam": { "used": 0, "limit": 60000, "percent": 0 } // dam.used = 當月已提現總額(completed 狀態) // dam.limit = (OC活躍數 × 10000 + HM活躍數 × 5000) × 3 // dam.percent = used / limit × 100% } } ``` #### POST /api/register.php (擴充:加 deploy_mode) ``` Request: { "username": "string", "password": "string", "plan_type": "OC|HM", "activation_key": "XXXXXXXX-XXXXXXXX", "deploy_mode": "auto|independent" ★ 新參數 } Response 201: { "success": true, "data": { "user_id": 123, "username": "new_user_1", "node_id": "NODE_XXXXXXXXXXXX", "node_type": "OC", "max_output": 667, "deploy_mode": "auto", "cluster_id": 1 } } ``` #### POST /api/activate-self.php (新) ``` Request: { "plan_type": "OC|HM", "activation_key": "XXXXXXXX-XXXXXXXX", "deploy_mode": "auto|independent" } Response 201: { "success": true, "data": { "node_id": "NODE_XXXXXXXXXXXX", "node_type": "OC", "max_output": 667, "deploy_mode": "auto", "cluster_id": 1 } } ``` #### POST /api/withdraw.php (擴充:加銀行/PIN + DAM驗證) ``` Request: { "amount": 1000, "bank_account": "1234567890", ★ 新參數 "account_name": "TAN AH KAU", ★ 新參數 "pin": "123456" ★ 新參數 } 校驗順序(★ v1.4): ① 帳戶狀態 → ② 結算鎖定期 → ③ 金額格式 → ④ QUOTA 配額 → ⑤ 餘額 → ⑥ QUOTA 充足 → ⑦ DAM 本月上限 → ⑧ PIN 碼 → 執行提現 錯誤碼: QUOTA_EXHAUSTED: "提現配額已用完" INSUFFICIENT_BALANCE: "餘額不足" INSUFFICIENT_QUOTA: "配額不足" DAM_THRESHOLD_EXCEEDED: "超過本月提現上限(本月已提現: xxx,限額: xxx)" Response 201: { "success": true, "data": { "withdrawal_id": 456, "transaction_id": "TXN_XXXX", "amount": 1000, "status": "pending" } } ``` #### POST /api/wallet/convert-key.php (新) ``` Request: { "node_type": "OC|HM", "quantity": 1 } Response 201: { "success": true, "data": { "keys": [ { "activation_key": "A1B2C3D4-E5F6G7H8", "node_type": "OC", "cost": 10000 } ], "total_cost": 10000, "remaining_balance": 0 } } ``` #### GET /api/wallet/my-keys.php (新) ``` Response 200: { "success": true, "data": [ { "id": 1, "activation_key": "A1B2C3D4-E5F6G7H8", "node_type": "OC", "cost": 10000, "status": "unused", "used_by": null, "used_at": null, "created_at": "2026-03-15" } ] } ``` #### POST /api/wallet/pin.php (新) ``` // 設置 PIN { "action": "set", "pin": "123456", "confirm_pin": "123456" } // 驗證 PIN { "action": "verify", "pin": "123456" } // 查詢 PIN 狀態 { "action": "status" } Response (status): { "success": true, "data": { "has_pin": true } } ``` #### GET /api/transactions.php (新) ``` Query: ?type=all|in|out&page=1&per_page=20 Response 200: { "success": true, "data": [ { "tx_type": "settlement", "category": "in", "description": "月度結算入賬", "amount": 26324, "status": "completed", "created_at": "2026-04-28" }, { "tx_type": "withdrawal", "category": "out", "description": "提現支出 · 銀行轉賬", "amount": 500, "status": "pending", "created_at": "2026-05-04 16:13:38", "completed_at": null } ], "pagination": { "page": 1, "per_page": 20, "total": 50 } } ``` #### GET /api/team.php (新) ``` Response 200: { "success": true, "data": { "protocol_tier": "OPENCLAW LEVEL", "network_capture_depth": 20, "has_active_cluster": true, "my_active_count": 1, "my_clusters": [ { "id": 1, "display_code": "TEGCZZ", "status": "active", "members": [ { "node_id": "NODE_OC_001", "position": 0, "type": "OC", "user_id": 1, "username": "test_user_1" }, { "node_id": "NODE_HM_001", "position": 1, "type": "HM", "user_id": 1, "username": "test_user_1" }, { "node_id": "NODE_HM_002", "position": 2, "type": "HM", "user_id": 2, "username": "test_user_2" } ] } ], "downline_clusters": [ ... ], "network_boost": { "boost": 0.05, "boost_pct": "5%", "clusters": [ ... ] } } } ``` #### GET /api/node-detail.php?id=NODE_XXX (新) > ★ 返回的 `max_output` 為動態值,公式為 `total_produced + 剩餘產出能力`,隨節點已激活月數和當前加速度變化。 ``` Response 200: { "success": true, "data": { "id": "NODE_HM_001", "type": "HM", "type_name": "Hermes 個人節點", "status": "active", "deploy_mode": "auto", "owner_username": "test_user_1", "owner_id": 1, "activated_at": "2026-05-02", "expired_at": "2027-05-02", "total_produced": 0, "max_output": 5004, "monthly_income": 417, "year_number": 1, "cluster_id": 1, "cluster_status": "active", "cluster_display_code": "TEGCZZ" } } ``` #### GET /api/topology.php?uid=123(★ v1.1 新增) ``` Response 200: { "rootId": 1, "userId": 1, "rootUsername": "test_user_1", "totalDownlines": 10, "balancedNodes": 3, "redundantNodes": 5, "unmatchedNodes": 2, "ocCount": 4, "hmCount": 8, "activeClusterCount": 2, "buildingClusterCount": 1, "totalOcNeeded": 1, "totalHmNeeded": 2, "layers": [ { "layer": 1, "nodes": [ { "uid": 2, "username": "test_user_2", "nodeCount": 1, "status": "balanced", "nodes": [{"type": "OC", "nodeId": "NODE_OC_001"}], "clusters": ["TEGCZZ"] } ] } ], "clusters": [ { "id": 1, "display_code": "TEGCZZ", "status": "active", "members": [...], "missing": [] } ] } ``` --- ## 六、前端規格 ### 6.1 架構模式 ``` SPA (Single Page Application) 不使用前端框架,純 PHP + 原生 JS 7 個內頁在同一個 index.php 中切換(class="page active") 登入頁獨立(login.php) ``` ### 6.2 SPA 路由 ``` 底部導航 4 個 Tab: 看板 (dashboard) → /h5/index.php#dashboard 財務 (finance) → /h5/index.php#finance ├── 子頁: withdraw (申請提現) └── 子頁: convert (轉激活碼) 集群 (team) → /h5/index.php#team 激活 (recruit) → /h5/index.php#recruit Header 點擊用戶名 → /h5/index.php#account ``` ### 6.3 頁面列表 | 頁面 ID | 名稱 | 說明 | |---------|------|------| | dashboard | 看板 | 首頁,節點狀態、收益構成、快速入口 | | finance | 財務中心 | 錢包、餘額、收支明細 | | withdraw | 申請提現 | 提現表單 + 記錄 | | convert | 轉激活碼 | 用餘額生成激活碼 | | team | 集群 | 平衡組狀態、傘下結構 | | recruit | 節點激活 | 為成員激活 / 為自己激活 | | account | 帳戶設定 | 身份卡、修改密碼、PIN碼、登出 | ### 6.4 設計系統 #### 色碼 | 名稱 | 色碼 | 用途 | |------|------|------| | 背景色 | #0f0f1c | body | | 卡片色 | #1a1a2e | .card, .stat-box | | 深色背景 | #141428 | 列表項目 hover | | 邊框色 | #2a2a42 | border | | 分隔線 | #222238 | divider | | 霓虹青 | #00d4ff | HM、主色調 | | 霓虹紫 | #a855f7 | OC | | 金色 | #f59e0b | 金額、強調 | | 次文字 | #8888a8 | 描述文字 | | 暗文字 | #5a5a78 | 輔助文字 | | 紅色 | #ef4444 | 危險操作、缺失 | | 綠色 | #10b981 | 成功 | #### 字體 ``` 系統默認棧: -apple-system, 'PingFang TC', 'Microsoft JhengHei', 'Helvetica Neue', sans-serif 等寬: monospace (用於節點ID、數據顯示) ``` #### 斷點 ``` max-width: 430px (移動端容器) ``` ### 6.5 每個頁面的 UI 元素 → API 映射 | 頁面 | UI 元素 | 資料來源 API | |------|---------|-------------| | **看板** | 節點卡片清單 | GET /api/dashboard.php | | | 收益構成 (基數/加速度/預期) | GET /api/dashboard.php | | | 全網協同增益 (右上角) | GET /api/dashboard.php | | | 底部 3 格 (DAM/配額/保護) | GET /api/dashboard.php | | | 收益明細抽屜 (基數) | GET /api/dashboard.php (nodes 陣列) | | | 加速明細抽屜 (加成) | GET /api/dashboard.php (boost details) | | **財務** | 錢包餘額區塊 | GET /api/dashboard.php (user 物件) | | | 收支明細 | GET /api/transactions.php | | **提現** | 可用餘額/剩餘配額 | GET /api/dashboard.php | | | 提現表單 | POST /api/withdraw.php | | | 提現記錄 | GET /api/transactions.php?type=withdrawal | | **轉碼** | 可用餘額 | GET /api/dashboard.php | | | 生成表單 | POST /api/wallet/convert-key.php | | | 已生成列表 | GET /api/wallet/my-keys.php | | **集群** | 協議等級/深度/平衡組數 | GET /api/team.php | | | 我的節點組卡片 | GET /api/team.php | | | 傘下平衡組卡片 | GET /api/team.php | | | 節點詳情抽屜 | GET /api/node-detail.php | | **激活** | 代報單表單 | POST /api/register.php | | | 自己激活表單 | POST /api/activate-self.php | | **帳戶** | 身份卡 | GET /api/dashboard.php (user+nodes) | | | 修改密碼 | POST /api/auth/change-password.php | | | PIN 碼設置 | POST /api/wallet/pin.php | | | 退出登錄 | GET /user/logout.php | --- ### 6.6 後台管理 — 會員管理頁(admin/index.php) #### 頁面結構 | 統計卡片 | 顯示 | |----------|------| | 總用戶 | users 表 COUNT | | 活躍用戶 | users WHERE status='active' | | 活躍節點 | nodes WHERE status='active' | | 總餘額 | SUM(users.wallet_balance) | | 待審提現 | withdrawal_journal WHERE status='pending' | #### 會員列表表格 | 列名 | 資料來源 | |------|---------| | ID | users.id | | 用戶名 | users.username | | 上級 | users.parent_id → LEFT JOIN users p | | 錢包餘額 | users.wallet_balance | | 提現配額 | users.withdrawal_quota (★ 自動計算:Σ(該用戶所有已激活節點的 purchase_price × 10)) | | 狀態 | users.status (active/frozen) | | **活躍主機** | **★ 新增**:批量查詢 nodes WHERE status='active',顯示每個節點的類型(OC=紫/HM=青)和激活日期 | | 註冊時間 | users.created_at | | 操作 | 編輯、凍結/解凍、刪除 | > ★ 活躍主機列為 v1.3 新增:每行以數量大字顯示,下方小字列出每個節點的類型(OC=紫色 / HM=青色)+ 激活日期。 #### 操作功能 | 操作 | 說明 | |------|------| | 添加會員 | 手動建立用戶(可設父級、錢包、配額) | | 編輯會員 | 修改用戶名、密碼(可選)、錢包、配額;可新增節點(後台激活) | | 凍結/解凍 | 切換 users.status,記錄 frozen_at + frozen_reason | | 刪除會員 | 級聯刪除 relationships → cluster_members → nodes | > ★ 後台為會員新增節點時,自動增加 `withdrawal_quota += purchase_price × 10`(OC +50,000 / HM +25,000),與 API 層行為一致。 --- ## 七、實作順序 ### 修改邊界(v1.1 現狀:以下皆已修改) ``` 已完成: admin/ ★ 已擴充(新增 finance.php,6個側邊欄更新) tasks/ ★ 已擴充(新增 check-expirations.php) src/services/ ★ 已修改(settlement.service.php 增強 getTopologyData) config/database.php ★ 已擴充(團隊優先匹配 + 保護期 + display_code + 自動到期檢查) api/register.php ★ 已修改(傳 newUserId 給 assignNodeToCluster) api/activate-self.php ★ 已修改(傳 userId 給 assignNodeToCluster) api/topology.php ★ 已修改(路徑修正) api/team.php ★ 已修改(時間倒序排序) h5/js/app.js ★ 已修改(display_code 顯示 + 緩存版本) h5/index.php ★ 已修改(JS 緩存版本 v=7) ``` ### 步驟清單(v1.1 新增項目) ``` 步驟 7: v1.1 更新(2026-05-04 已完成) 7a. 集群編號系統: display_code 字段 + base36 編碼 + 前端/後台顯示更新 7b. 團隊優先匹配: findMyWaitingCluster / findTeamWaitingCluster / findTeamProtectionCluster 7c. assignNodeToCluster 改為四層優先級(自己→團隊building→團隊保護期→新建) 7d. 45天保護期: grace 狀態 / protection_until / 自動觸發器 7e. 財務管理模塊: admin/finance.php + bonus_records 表(套餐開通/提現管理/獎勵管理) 7f. 拓撲三Tab視圖: 推薦樹 / 集群池 / 統計(含增強 getTopologyData API) 7g. 集群頁排序: 按 created_at DESC 7h. 到期自動檢查: config/database.php 內聯,每次請求時自動觸發 ``` ``` 步驟 1: 資料庫 DDL 交付: 在 phpMyAdmin 執行 §3.3 的 SQL → 確認所有表建立成功 步驟 2: 核心引擎 交付: 在 config/database.php 新增以下函數: - assignNodeToCluster() - countFullyOwnedClusters() - createNewCluster() - completeClusterIfReady() - hasActiveCluster() - isUserInReferralChain() - flattenReferralTree() - findActiveClustersInUserScope() - calculateNetworkBoost() ← 完全重寫 - calculateTotalYield() 步驟 3: API 層 交付: 建立 §5.2 中所有新端點 + 擴充既有端點 順序: 3a. api/auth/login.php 3b. api/dashboard.php 3c. api/activate-self.php 3d. api/register.php (擴充) 3e. api/withdraw.php (擴充) 3f. api/wallet/convert-key.php 3g. api/wallet/my-keys.php 3h. api/wallet/pin.php 3i. api/auth/change-password.php 3j. api/transactions.php 3k. api/team.php 3l. api/node-detail.php 步驟 4: H5 前端 交付: 4a. 從 pusatix_h5_v5.html 萃取 CSS → h5/css/style.css 4b. 從 pusatix_h5_v5.html 萃取 JS 框架 → h5/js/app.js (SPA 路由 + 全頁面邏輯) 4c. 建立 h5/js/api.js (API 請求封裝) 4d. 建立 h5/index.php (SPA 外殼,含所有頁面和組件) 4e. 建立 h5/login.php 步驟 5: H5 頁面對接 API(逐頁實現於 index.php 內) 5a. dashboard 看板頁 5b. finance 財務中心頁 5c. withdraw 申請提現頁 5d. convert 轉激活碼頁 5e. team 集群頁 5f. recruit 節點激活頁 5g. account 帳戶設定頁 步驟 6: 端到端測試 - 登入 → 看板 → 集群 → 激活 → 財務 → 提現 → 轉碼 → 帳戶 - 結算鎖定期測試 - 冪等性測試 ``` --- ## 八、附錄 ### A. 資料來源說明 | 檔案 | 角色 | 可信度 | |------|------|--------| | `thrypton.sql` | 資料庫結構與測試資料 | ✅ 權威來源 | | `pusatix_h5_v5.html` | UI 設計藍本 | ⚠️ 僅 UI/CSS 可用,數值不可信 | | `config/database.php` | 現有業務邏輯 | ✅ 權威來源(以現有程式碼為準) | ### C. glossary | 繁體中文 | 英文 | 說明 | |----------|------|------| | 節點 | Node | 算力節點,OC 或 HM | | 平衡組 | Cluster | 1 OC + 2 HM 的固定組合 | | 看板 | Dashboard | 首頁 | | 集群 | Team/Network | 傘下組織結構 | | 激活 | Activate | 使用激活碼開通節點 | | 提現 | Withdraw | 從錢包提款。受 QUOTA(終身配額)和 DAM(月流閥)雙重限制 | | 加速/增益 | Boost | 收益加成百分比 | | 推薦鏈 | Referral Chain | 沿 parent_id 向上的關係鏈 | | 傘下 | Downline | 推薦網絡中的所有下級 | | 結算鎖定 | Settlement Lock | 每月 28 日至次月 1 日 | | 冪等性 | Idempotency | X-Request-ID 防止重複請求 | | 顯示編碼 | display_code | 6位 base36 集群編碼,隱藏真實數量 | | 保護期 | Grace Period | 節點到期後 45 天緩衝期 | | 緩衝狀態 | grace | 節點到期但尚未正式踢出 | | 拓撲視圖 | Topology | 推薦樹 + 集群池可視化 | | 獎勵記錄 | Bonus Record | bonus_records 表中的加速度明細 | | 團隊優先 | Team-Priority | 集群匹配限定推薦樹內,不跨團隊 | | 觸發器 | Trigger | 透過 Web 請求自動觸發定時任務 | | 提現配額 | QUOTA | 終身收益上限 = purchase_price × 10,激活時增加,提現時扣減 | | 月提現流閥 | DAM | 每月提現上限 = 投資額 × 3,次月重置 | | 購買價格 | purchase_price | OC=5,000,HM=2,500,系統內部為節點賦予的金額標籤 | --- **文件版本**: v1.4 **生成日期**: 2026-05-06 **基於**: 現有程式碼為準(config/database.php + yield-calculator + API 層)。 v1.4 修正(2026-05-06): - **withdrawal_quota 1:10 內生循環**:提現配額改為 purchase_price × 10(OC=50,000, HM=25,000),不再基於 max_output 靜態基準值。節點到期不扣減配額 - **DAM 月提現流閥**:新增月提現流閥機制,每月提現 ≤ 投資額 × 3。DAM_MULTIPLIER 由 5 改為 3,時間維度由終身累計改為每月獨立(次月重置) - **dashboard 看板 DAM 展示修正**:dam.used 由錢包餘額改為當月已提現總額;dam.limit 由 Σ(max_output) × 5 改為 (OC數×5000+HM數×2500) × 3 - **main withdraw API 新增 DAM 校驗**:api/withdraw.php 新增當月 DAM 上限檢查 - **recalculateWithdrawalQuota() 修正**:重算公式改為 SUM(CASE type) × purchase_price × 10 - **刪除節點到期扣減配額邏輯**:config/database.php 移除節點 final expire 時的 withdrawal_quota 扣減 v1.5 修正(2026-05-08): - **全案數據參數以程序代碼為基準**:所有金額參數(月收益率、purchase_price、QUOTA、DAM、max_output、激活碼價值)統一對齊程序代碼實際執行值 - **網路協同邏輯修正**:從 v1.3 的「逐 cluster 判定費率」改為「用戶級統一費率」(用戶持有 OC→所有符合條件 cluster +5%,否則 +4%),與 database.php 及 settlement.service.php 實現一致 - **提現校驗順序修正**:按 withdraw.php 實際執行順序重排;帳戶凍結檢查移至登入階段(login.php),提現 API 不再重複檢查 - **新增凍結帳戶檢查機制說明**:文檔化 login.php 中的 frozen 狀態攔截邏輯 v1.3 修正: - **max_output 雙軌定義**:拆分靜態基準值(DB 欄位,三年月收益總和,不含加速度)與動態剩餘上限(運行時計算,`total_produced + 剩餘產出能力`),修正舊公式 `667×(1+accel)` 因忽略歷史加速度變化導致的計算偏離 - **withdrawal_quota 自動化管理**:提現配額隨節點激活/過期自動增減,等於 Σ(active 節點的 max_output 靜態基準值)(★ v1.4 已重構,改為 purchase_price × 10) - **新增 `recalculateWithdrawalQuota()` 工具函數**:可一鍵重算用戶配額,修補歷史數據(★ v1.4 公式已更新) - **admin/index.php 活躍主機列**:後台會員列表新增「活躍主機」,批量展示每個用戶的 active 節點類型(OC=紫/HM=青)和激活日期 - **API 示例更新**:node-detail.php max_output 值更新為新動態公式計算結果;dashboard.php 的 max_output / total_max_output / monthly_income 同步更新 v1.2 修正:個人特權按全自有平衡組累加、直推 HM 按人頭累加、max_output 三年總和公式、結算鎖定期精確化、補充三道防線/force_lock/admins表/system_config表/idempotency_keys表/node_rate_config表定義、users表補全欄位、API 範例對齊實際程式碼。