diff --git a/docs/Frida绕过配方.md b/docs/虎牙/Frida绕过配方.md similarity index 100% rename from docs/Frida绕过配方.md rename to docs/虎牙/Frida绕过配方.md diff --git a/docs/HUYA_APP_OVERVIEW.md b/docs/虎牙/HUYA_APP_OVERVIEW.md similarity index 100% rename from docs/HUYA_APP_OVERVIEW.md rename to docs/虎牙/HUYA_APP_OVERVIEW.md diff --git a/docs/HUYA_BROWSER_EXCHANGE.md b/docs/虎牙/HUYA_BROWSER_EXCHANGE.md similarity index 100% rename from docs/HUYA_BROWSER_EXCHANGE.md rename to docs/虎牙/HUYA_BROWSER_EXCHANGE.md diff --git a/docs/HUYA_CHAT_缺失字段补齐方案.md b/docs/虎牙/HUYA_CHAT_缺失字段补齐方案.md similarity index 100% rename from docs/HUYA_CHAT_缺失字段补齐方案.md rename to docs/虎牙/HUYA_CHAT_缺失字段补齐方案.md diff --git a/docs/HUYA_COOKIE合并审计与来源矩阵.md b/docs/虎牙/HUYA_COOKIE合并审计与来源矩阵.md similarity index 100% rename from docs/HUYA_COOKIE合并审计与来源矩阵.md rename to docs/虎牙/HUYA_COOKIE合并审计与来源矩阵.md diff --git a/docs/HUYA_COOKIE路线状态.md b/docs/虎牙/HUYA_COOKIE路线状态.md similarity index 100% rename from docs/HUYA_COOKIE路线状态.md rename to docs/虎牙/HUYA_COOKIE路线状态.md diff --git a/docs/HUYA_HDID_ALGORITHM_GEN.md b/docs/虎牙/HUYA_HDID_ALGORITHM_GEN.md similarity index 100% rename from docs/HUYA_HDID_ALGORITHM_GEN.md rename to docs/虎牙/HUYA_HDID_ALGORITHM_GEN.md diff --git a/docs/HUYA_HDID_RESEARCH.md b/docs/虎牙/HUYA_HDID_RESEARCH.md similarity index 100% rename from docs/HUYA_HDID_RESEARCH.md rename to docs/虎牙/HUYA_HDID_RESEARCH.md diff --git a/docs/HUYA_ID_NOMENCLATURE.md b/docs/虎牙/HUYA_ID_NOMENCLATURE.md similarity index 100% rename from docs/HUYA_ID_NOMENCLATURE.md rename to docs/虎牙/HUYA_ID_NOMENCLATURE.md diff --git a/docs/HUYA_WEB_FIELD_TRACE.md b/docs/虎牙/HUYA_WEB_FIELD_TRACE.md similarity index 100% rename from docs/HUYA_WEB_FIELD_TRACE.md rename to docs/虎牙/HUYA_WEB_FIELD_TRACE.md diff --git a/docs/HUYA_精英宝典-8.31-PY重新分析.md b/docs/虎牙/HUYA_精英宝典-8.31-PY重新分析.md similarity index 100% rename from docs/HUYA_精英宝典-8.31-PY重新分析.md rename to docs/虎牙/HUYA_精英宝典-8.31-PY重新分析.md diff --git a/docs/HUYA_精英宝典-9.1-全流程分析.md b/docs/虎牙/HUYA_精英宝典-9.1-全流程分析.md similarity index 100% rename from docs/HUYA_精英宝典-9.1-全流程分析.md rename to docs/虎牙/HUYA_精英宝典-9.1-全流程分析.md diff --git a/docs/HUYA_精英宝典-后端与9.1抓包对照.md b/docs/虎牙/HUYA_精英宝典-后端与9.1抓包对照.md similarity index 100% rename from docs/HUYA_精英宝典-后端与9.1抓包对照.md rename to docs/虎牙/HUYA_精英宝典-后端与9.1抓包对照.md diff --git a/docs/HUYA_铸币机-终局总结.md b/docs/虎牙/HUYA_铸币机-终局总结.md similarity index 100% rename from docs/HUYA_铸币机-终局总结.md rename to docs/虎牙/HUYA_铸币机-终局总结.md diff --git a/docs/HUYA精英宝典联调指南.md b/docs/虎牙/HUYA精英宝典联调指南.md similarity index 100% rename from docs/HUYA精英宝典联调指南.md rename to docs/虎牙/HUYA精英宝典联调指南.md diff --git a/docs/Hook脚本运行与捕获约定.md b/docs/虎牙/Hook脚本运行与捕获约定.md similarity index 100% rename from docs/Hook脚本运行与捕获约定.md rename to docs/虎牙/Hook脚本运行与捕获约定.md diff --git a/docs/dfpReport生成流程.md b/docs/虎牙/dfpReport生成流程.md similarity index 100% rename from docs/dfpReport生成流程.md rename to docs/虎牙/dfpReport生成流程.md diff --git a/docs/dfpReport破解进度.md b/docs/虎牙/dfpReport破解进度.md similarity index 100% rename from docs/dfpReport破解进度.md rename to docs/虎牙/dfpReport破解进度.md diff --git a/docs/模拟器存活闪退诊断报告.md b/docs/虎牙/模拟器存活闪退诊断报告.md similarity index 100% rename from docs/模拟器存活闪退诊断报告.md rename to docs/虎牙/模拟器存活闪退诊断报告.md diff --git a/docs/虎牙App密码登录-WUP明文抓包.md b/docs/虎牙/虎牙App密码登录-WUP明文抓包.md similarity index 100% rename from docs/虎牙App密码登录-WUP明文抓包.md rename to docs/虎牙/虎牙App密码登录-WUP明文抓包.md diff --git a/docs/虎牙App密码登录协议-抓包实证.md b/docs/虎牙/虎牙App密码登录协议-抓包实证.md similarity index 100% rename from docs/虎牙App密码登录协议-抓包实证.md rename to docs/虎牙/虎牙App密码登录协议-抓包实证.md diff --git a/docs/虎牙App登录-APK静态分析.md b/docs/虎牙/虎牙App登录-APK静态分析.md similarity index 100% rename from docs/虎牙App登录-APK静态分析.md rename to docs/虎牙/虎牙App登录-APK静态分析.md diff --git a/docs/虎牙App登录-加密算法动态验证.md b/docs/虎牙/虎牙App登录-加密算法动态验证.md similarity index 100% rename from docs/虎牙App登录-加密算法动态验证.md rename to docs/虎牙/虎牙App登录-加密算法动态验证.md diff --git a/docs/虎牙App登录逆向笔记.md b/docs/虎牙/虎牙App登录逆向笔记.md similarity index 100% rename from docs/虎牙App登录逆向笔记.md rename to docs/虎牙/虎牙App登录逆向笔记.md diff --git a/docs/虎牙hyCred签发-侦察笔记.md b/docs/虎牙/虎牙hyCred签发-侦察笔记.md similarity index 100% rename from docs/虎牙hyCred签发-侦察笔记.md rename to docs/虎牙/虎牙hyCred签发-侦察笔记.md diff --git a/docs/虎牙扫码登录闭环逆向.md b/docs/虎牙/虎牙扫码登录闭环逆向.md similarity index 100% rename from docs/虎牙扫码登录闭环逆向.md rename to docs/虎牙/虎牙扫码登录闭环逆向.md diff --git a/docs/虎牙纯Python全自动登录-总结与交接.md b/docs/虎牙/虎牙纯Python全自动登录-总结与交接.md similarity index 100% rename from docs/虎牙纯Python全自动登录-总结与交接.md rename to docs/虎牙/虎牙纯Python全自动登录-总结与交接.md diff --git a/docs/虎牙证书算法闭环-差分实证.md b/docs/虎牙/虎牙证书算法闭环-差分实证.md similarity index 100% rename from docs/虎牙证书算法闭环-差分实证.md rename to docs/虎牙/虎牙证书算法闭环-差分实证.md diff --git a/docs/设计-登录任务与CK解耦.md b/docs/设计-登录任务与CK解耦.md index c9f156c..4d27813 100644 --- a/docs/设计-登录任务与CK解耦.md +++ b/docs/设计-登录任务与CK解耦.md @@ -3,6 +3,85 @@ > 状态:**待评审**(未动代码)|**方案已修订:不做双写,代码一次切换,旧列留到下次发版再 drop(§6)** > 起因:2026-10-03 误删事故([复盘](事故复盘-CK误删与恢复.md))。当前靠「删除时跳过持有 CK 的记录」 > 兜住,但那只是**临时护栏** —— 根因是两类数据挤在同一张表里。 +> +> **2026-10-04 已逐行核对代码**,结论见 §0:方向成立,但耦合面比原文列举的更大, +> 且有 4 处漏项与 1 处死代码 —— 动手前先看 §0。 + +## 0. 现状核对(2026-10-04 逐行核对代码) + +### 0.1 一句话结论 + +设计的**判断与方案都成立**(CK 确实只存在于 `login_tasks.cookie`,`accounts` 表没有 cookie 列), +原文 §5 的读写清单**方向正确、覆盖约 80%**。但有 4 处实际耦合点原文没列, +另有 1 处是死代码、若干行号已漂移 —— 而这些恰恰是「一次切换」最容易漏掉的地方(漏一处就是静默用错数据)。 + +### 0.2 精确耦合面(核对后) + +**读**(判定「谁有 CK / 取 CK」),共 **15 个真实调用点**: + +| 入口 | 调用点 | +| --- | --- | +| `cookie_holding_filter()` | `services/douyu_service.py:209,223,238,245`(内部)
`routers/douyu.py:188`、`routers/cookies.py:121,133,367`、`routers/accounts.py:295,338,391`、`routers/dashboard.py:90`
`services/login_service.py:605`、`jobs/ck_relogin.py:90,158` | +| `cookie_operable_filter()` | `routers/cookies.py:502,712,750`、`jobs/ck_health.py:77,179` | +| `latest_cookie_login_task()` | `services/douyu_runner.py:59` | +| `cookie_holding_account_ids_query()` | `services/account_service.py:31` | +| `visible_douyu_task_accounts()` | `services/douyu_service.py:283` | +| `login.py::_holds_ck_clause()`(**重复实现**) | `routers/login.py:259` | + +**写**,共 **4 个文件 10 个写入点**(约 25 行赋值): + +| 类别 | 位置 | +| --- | --- | +| `cookie`(CK 本体) | `login_service.py:155`(登录成功)、`login_service.py:405`(重登后复检失败清空)、`cookies.py:819`(批量清除)、`cookies.py:842`(单个清除) | +| `ck_check_*`(健康状态) | `cookies.py:535-542`(手动检测)、`login_service.py:421-430`(重登后复检)、`douyu_runner.py:70-75`(工作台登录检测)、`ck_health.py:141-149`(自动巡检) | +| `relogin_*`(重登调度) | `ck_relogin.py:304-307`(成功清零)、`ck_relogin.py:312-338`(失败退避 + 下次重试时间) | + +**合计:6 个入口 / 24 个读点 / 10 个写点,横跨 12 个文件。** + +涉及 **12 个文件**(6 service + 6 router):`douyu_service`、`account_service`、`login_service`、 +`douyu_runner`、`jobs/ck_health`、`jobs/ck_relogin`;`douyu`、`cookies`、`accounts`、`dashboard`、 +`login`、`automation`。 + +### 0.3 原文漏掉的 4 处 + 1 处死代码 + +1. **`services/account_service.py:31` `cookie_account_ids_query()`** —— 账号管理列表「只看持有 CK」 + 的筛选走这里。切了 `cookie_holding_filter` 却漏了它,账号列表的 CK 筛选会静默失效; +2. **`services/douyu_service.py:283` `visible_douyu_task_accounts()`** —— 业务任务页的账号可见性 + 也依赖「记录持有 CK」; +3. **`routers/accounts.py:629-643`(账号详情概览)** —— 这是 **2026-10-03 账号事件功能新增**的接口, + 用裸 SQL 读 `login_tasks.ck_checked_at` 作为「最近 CK 检测」。本文写作时还不存在, + 但它是「账号详情页」的数据源,**解耦后必须改成读 `accounts`**,否则详情页会永远显示旧值; +4. **`routers/login.py:259` `_holds_ck_clause()`** —— 与 `cookie_holding_filter()` **同口径的重复实现** + (只差一处注释口径)。解耦时要一起处理,并建议顺手合并成一个来源,避免第三次分叉; +5. `douyu_service.py:230 latest_cookie()` 是**死代码**(全仓库无调用)—— §5.1 把它列为改造目标, + 实际可以**直接删除**,不必迁移。 + +> 另:`jobs/ck_health.py` / `jobs/ck_relogin.py` 的选行查询都是 +> `db.query(LoginTask).join(Account, ...)` —— 解耦后要变成 `db.query(Account)`。 +> 这**不是简单的字段替换**:join 反过来写,`auto_managed` / `ck_checked_at` 的过滤与排序 +> 全要重新落到 `Account` 上(原文 §5.3 已给出写法,照做即可)。 + +### 0.4 行号漂移(原文 → 现在) + +| 原文 | 现在 | 原因 | +| --- | --- | --- | +| `routers/accounts.py:285/324/374` | `295/338/391` | 账号事件功能在文件头部加了导入与辅助函数 | +| `routers/automation.py:218` | ✅ 未变 | —— | +| `routers/douyu.py:188` | ✅ 未变 | —— | +| `routers/dashboard.py:90` | ✅ 未变 | —— | + +### 0.5 与「账号事件」功能的关系(2026-10-03 新增,在 `dev`) + +账号事件表([设计](设计-账号详情页与事件历史.md))**已经**把「账号发生过什么」独立出来了, +并且明确遵循同一个模型:`accounts` 存当前状态、`account_events` 存流水。 +本次解耦补的是**「当前状态」里最核心的那一块(凭证与健康)**,两者是同一件事的两半: + +- 解耦后 `accounts` 上才有完整的「当前状态」,事件表补「历史」,账号详情页把两者拼起来; +- 事件发射点(`ck_health` 判失效、`login_service` 登录成功、`ck_relogin` 重登结果) + **与本次要改的写入点是同一批** —— 建议 C2/C3 改动时**顺带一起改**,否则改两遍。 + +> ⚠️ 账号详情页的概览接口(`routers/accounts.py:586` 起)现在同时读 `accounts`(状态卡) +> 和 `login_tasks`(登录次数 / 最近 CK 检测,§0.3 第 3 条)。解耦后这段裸 SQL 必须一起改。 ## 1. 问题:一张表扛了四件事 @@ -180,11 +259,15 @@ SELECT (SELECT COUNT(*) FROM accounts WHERE cookie<>''), | --- | --- | --- | | `services/douyu_service.py::cookie_holding_filter()` | `LoginTask.cookie != ""` | `Account.cookie != ""` | | `services/douyu_service.py::cookie_operable_filter()` | 持有 CK + 非在途 | 持有 CK(账号级)+ 该账号无在途批次 | -| `services/douyu_service.py::latest_cookie(db, account_id)` | 查 login_tasks | `account.cookie` | +| `services/douyu_service.py::latest_cookie(db, account_id)` | **死代码**(全仓库无调用) | **直接删除**,不必迁移 | | `services/douyu_service.py::latest_cookie_login_task()` | 返回 LoginTask | **改为 `latest_cookie_account()`**,或保留但内部改查 accounts | | `routers/douyu.py:188` | `select(LoginTask.account_id).where(...)` | `select(Account.id).where(Account.cookie != "")` | -| `routers/accounts.py:285/324/374` | 同上 | 同上 | +| `routers/accounts.py:295/338/391` | 同上 | 同上 | | `routers/dashboard.py:90` | 同上 | 同上 | +| **`services/account_service.py:31`**(§0.3 漏项) | `cookie_account_ids_query()` → `cookie_holding_account_ids_query()` | 同上(账号列表的 CK 筛选) | +| **`services/douyu_service.py:283`**(§0.3 漏项) | `visible_douyu_task_accounts()` | 同上(业务任务页的账号可见性) | +| **`routers/login.py:259`**(§0.3 漏项) | `_holds_ck_clause()` 重复实现 | 与 `cookie_holding_filter` 合并成单一来源 | +| **`routers/accounts.py:629-643`**(§0.3 漏项) | 裸 SQL 读 `login_tasks.ck_checked_at` | 改读 `Account.ck_checked_at` | | `routers/automation.py:218` | `LoginTask.ck_check_status == "invalid"` | `Account.ck_check_status == "invalid"` | | `services/automation/jobs/ck_health.py:75-90` | 以 LoginTask 选行 | **以 Account 选行**(见 §5.3) | | `services/automation/jobs/ck_relogin.py:85-115/155` | 以 LoginTask 选行 | 同上 | @@ -455,3 +538,105 @@ C1(加列+回填,无行为变化) 这次改造本身是安全的(旧列保留、可反向回填),但**最后的 drop 列是不可逆的**。 叠加 2026-10-03 的事故(当时唯一退路是 binlog),建议把**每日逻辑备份**作为这个改造的 前置项一起做掉 —— 否则「不可逆的一步」仍然只靠 30 天 binlog 兜底。 + +--- + +## 13. 账号「独立」的目标形态:全生命周期信息模型 + +> 本节回答的是比「解耦」更大的问题:**一个斗鱼账号,应该自己承载哪些信息?** +> 解耦只是让第 ② 类信息回到账号上;要「账号管理单独出来」,还得把 ③④ 补上。 + +### 13.1 现状:账号的事实散在 5 张表、7 个页面 + +| # | 信息类别 | 今天存在哪 | 今天在哪看 | +| --- | --- | --- | --- | +| ① | 身份与凭据(用户名/密码/邮箱/邮箱密码) | `accounts` ✅ | 账号管理 | +| ② | **凭证与健康(CK / 巡检 / 重登)** | **`login_tasks`** ❌ | Cookie 管理、CK 检测与重登、自动巡检 | +| ③ | **生命周期状态(现在是「没有」)** | —— ❌ | —— | +| ④ | 归属与运营(分配/标签/备注/自动化开关) | `accounts` ✅ | 账号管理、分配管理 | +| ⑤ | 平台画像(uid/昵称/积分/鱼翅/游戏角色…) | `accounts` ✅ | 散在精英宝典/电竞手册/和平小店 | +| ⑥ | 业务流水(充值/兑换/积分/开通) | `douyu_tasks`+`account_events` ✅ | 各业务页、账号详情页 | +| ⑦ | 人工操作审计 | `audit_logs`(**关联不到账号**)⚠️ | 审计日志页 | + +**「账号管理没有单独出来」的三种具体表现:** + +1. **数据层**:`accounts` 表**没有 `cookie` 列**,账号的登录态寄存在某条登录记录的 `cookie` 字段上。 + 删除登录记录 = 删除账号的登录态(事故根因); +2. **接口层**:`AccountOut` 只暴露 `id/username/tag/remark/assigned_to/created_at`(+敏感字段), + **零运营状态** —— 账号列表接口拿不到 CK、健康、积分、开通状态; +3. **UI 层**:账号管理页只有「ID/用户名/标签/上传时间/密码/邮箱/分配/删除」8 列。 + 要知道这个账号能不能用,得跳去 Cookie 管理页;要看积分/开通,得跳去业务页。 + +### 13.2 参照物:虎牙账号已经是「独立」的 + +`huya_accounts` 把凭据与状态**全部挂在账号上**,因此虎牙账号页一页就能看清: +`has_cookie` / `cookie` / `cookie_preview` / `status` / `login_channel` / +`points` / `game_*` / `sms_url` / `assigned_to` / `updated_at`。 + +斗鱼与之相比:**平台画像比虎牙更全**(多了精英宝典、电竞手册、和平小店、鱼翅/兑换余额), +但**凭证与状态几乎为零**。对齐虎牙即可,不需要发明新模型。 + +### 13.3 目标:账号承载的四层信息 + +``` +┌ ① 身份凭据 ────────────────────────────────────────────┐ +│ username / password / email / email_password / tag / remark │ +└────────────────────────────────────────────────────────┘ +┌ ② 登录态(本次解耦)────────────────────────────────────┐ +│ cookie / cookie_updated_at │ +│ ck_check_status / ck_check_result / ck_checked_at │ +│ relogin_attempts / relogin_next_at / relogin_last_error / │ +│ relogin_failure_kind │ +└────────────────────────────────────────────────────────┘ +┌ ③ 生命周期状态(新增)──────────────────────────────────┐ +│ status: imported → active → ck_invalid → relogin → manual │ +│ → disabled / archived │ +│ 派生视图(不落库):能不能跑业务 = 有 CK 且健康且未被禁用 │ +└────────────────────────────────────────────────────────┘ +┌ ④ 平台画像 + 运营 ──────────────────────────────────────┐ +│ uid / nickname / points / gold / exchange / bind / │ +│ elite_* / esports_* / xpd_* / assigned_to / auto_managed │ +└────────────────────────────────────────────────────────┘ + ↓ 流水(append-only,已经做好) + account_events(业务+状态变更+人工操作)+ login_tasks(登录尝试历史) +``` + +### 13.4 生命周期阶段与缺口 + +| 阶段 | 现状 | 缺口 | +| --- | --- | --- | +| 导入 | `accounts.created_at` | 无导入批次、无导入人(只有一句审计文案「导入103个」) | +| 待登录 | **无状态**,靠「有没有持 CK 的记录」反推 | ③ `status` 缺失 | +| 登录成功 | CK 写进 `login_tasks.cookie` | ② 不在账号上 | +| 已分配 | `accounts.assigned_to` ✅ | —— | +| 业务执行 | 画像字段 ✅ + `douyu_tasks` | 画像字段在账号页看不到(接口未暴露) | +| CK 失效 | `login_tasks.ck_check_status` | ② 不在账号上 | +| 自动重登 | `login_tasks.relogin_*` | ② 不在账号上 | +| 转人工 | `login_tasks.relogin_failure_kind` | ② 不在账号上;人工队列页面以「记录」为单位 | +| **停用 / 归档** | **只有 `auto_managed`(自动化后门)** | ③ 没有停用/归档语义,删除是硬删(连带删任务与事件) | +| 删除 | 硬删 + 连带清 `login_tasks`/`douyu_tasks`/`account_events` | 无软删除,历史不可追溯 | + +### 13.5 建议的落地顺序(与 §10 的 C1~C7 对齐) + +解耦(C1~C5)是**前置**:凭证状态不回到账号上,后面的「状态机」「账号页」都是空中楼阁。 + +| 阶段 | 内容 | 依赖 | +| --- | --- | --- | +| **S1 = C1~C3** | CK / 健康 / 重登 状态搬到 `accounts`(含作业改按账号调度) | —— | +| **S2 = C4~C5** | Cookie 页与删除护栏改为账号级 | S1 | +| **S3** | `AccountOut` 补全运营状态字段;账号列表页加「CK / 健康 / 积分 / 开通」列 | S1、S2 | +| **S4** | 账号详情页补全生命周期(状态机、画像、开通时间、累计统计)—— 已有 `AccountDetailPage` 底座 | S3 | +| **S5** | 补 ③ 生命周期 `status` + 停用/归档 + 导入批次 | S1 | +| **S6** | 打通 `audit_logs.account_id`,让「分配/改标签/删除」进时间线 | S1 | + +> S3/S4 是用户感知最强的部分(「账号管理终于能看全」),但**必须等 S1**—— +> 否则账号页展示的 CK 状态仍然来自登录记录,删一条记录页面就变了。 + +### 13.6 待确认 + +| # | 问题 | 选项 | +| --- | --- | --- | +| **L1** | ③ 生命周期 `status` 落库还是派生? | ①显式列(好筛选/统计,需维护一致性)②纯派生(无冗余,但查询复杂) | +| **L2** | 停用/归档要不要做(软删除)? | ①做(账号是资产,硬删不可逆)②不做 | +| **L3** | 导入批次要不要做? | ①做(能回答「这批 103 个现在活了几个」)②不做 | +| **L4** | 账号列表页要不要直接显示 CK/健康/积分? | ①显示(信息密度换查询成本,需注意 N+1)②只显示状态徽标 |