diff --git a/docs/自动化与资料收集规划.md b/docs/自动化与资料收集规划.md index d449f00..858a48a 100644 --- a/docs/自动化与资料收集规划.md +++ b/docs/自动化与资料收集规划.md @@ -215,6 +215,7 @@ | 8 | 资料模板字段固定为 **游戏编号 + 游戏昵称 + 主页截图**;**皮肤优先由订单号自动对应**;仅当订单命中多个斗鱼商品时(「3选1」「小店自选」类,占 12.4%),才让用户在链接里**选一件** | | 9 | 兑换**不涉及「加好友后赠送」**,状态机不需要"等待可赠送"等待态 | | 10 | 订单准入**分两层**:① 订单在 order_site 里**既无履约任务、也无接单平台工单**(`route=unhandled`,即「没人管」)才进入候选;② 候选订单的商品必须**命中皮肤兑换规则**才放行。其余一律提示「该订单暂不支持,请联系客服」,客服可在后台人工放行(留审计) | +| 11a | **重登失败按原因分型**(`relogin_failure_kind`),不是笼统「失败」:极验/凭证问题转人工,取码/中断/网络自动重试,配额满退避到次日(详见 §7.4.3) | | 11 | **准入白名单以斗鱼商品目录为准**:能映射到 `douyu_goods_snapshot` 的才放行;映射不到(占 C 桶皮肤订单 59.9%)直接拒绝 —— 因为斗鱼兑不出来。**不匹配就不建单**,后续按实际数据逐步放宽 | | 12 | **匹配优先按 SKU 精确定位**(`relSkuId` / `skuNick`),商品名文本只作兜底(§3.6)—— 接单平台已是这个做法 | @@ -453,16 +454,91 @@ lookup_order(order_no) -> { ### 7.4 P1b · 作业二:`ck_relogin`(自愈,默认关闭) -- **调度**:每 5 分钟 tick;取满足以下条件的账号,最多 `batch_size`(默认 20)个: - `ck_check_status = 'invalid'` 且 `relogin_next_at <= now`(或为空)且 `relogin_attempts < max_attempts_per_day`(默认 2)且 `auto_managed = true` 且 `attempt_status` 不在途; -- **执行**:复用 `LoginBatchRunner(mode="relogin")`,成功才替换 CK; +> **这一节 2026-10-02 按线上实测数据重写过。** 原来的设计只有「退避 + 熔断 + 开关」, +> 数据表明那样不够:**失败必须分型**,否则 64% 本可自动解决的失败会被当成人工活。 + +#### 7.4.1 为什么必须和 P1a 联动(数据) + +巡检只产出「谁失效了」的清单,不兑现修复,1,000+ 个账号还是要人工逐个选集→点重登→过滑块。 +线上实测(2026-10-02): + +| 项 | 数值 | 依据 | +| --- | --- | --- | +| 当前失效 | **1,356**(还在涨,巡检在排空积压) | `ck_check_status='invalid'` | +| 其中「还没在 attempt_* 时代重试过」 | 941 | 纯新机会 | +| 其中「最近一次重登失败过」 | 415 | 已有失败记录 | +| 历史重登**真实成功率** | **≈78%** | 成功 1,767 ÷ (1,767 + 真实失败 508),已排除「中断/停止」这类非真失败 | +| 预计可自动救回 | **≈1,050** | 1,356 × 78% | +| 预计需人工 | **≈300** | 主要是极验滑块 | + +**关键结论:失效是「会话级」而不是「凭证级」。** 斗鱼对这批账号回的是「用户未登录」, +不是「密码错误」;而这些行**本来都是登录成功的产物**(见下)。 + +> **「持有 CK 的行 = 真实登录成功过」的依据**:`LoginTask` 全项目**只在一处创建** +> —— `services/login_service.py`(登录成功写回 CK 处);**导入账号不建 LoginTask、 +> 不写 cookie**(已 grep 核实)。因此 `cookie` 非空且 `status='success'` 的行, +> 只可能是某次真实登录成功留下的。那批 `attempt_status=''` 不是「从未登录」, +> 而是「登录成功发生在 `attempt_*` 字段存在之前」(字段是迁移 `20261001_0035` 才加的)。 + +#### 7.4.2 候选筛选 + +- 每 5 分钟 tick,取满足以下条件的行,最多 `batch_size`(默认 20)个: + `ck_check_status = 'invalid'` 且 `relogin_next_at <= now`(或为空) + 且 `relogin_attempts < max_attempts_per_day`(默认 2) + 且 `auto_managed = true` 且 `attempt_status` 不在途; +- **不再按「有没有成功过」筛**:持 CK 的行按定义都成功过,这个条件没有区分度; + 真正有区分度的是「上一次为什么失败」(下一步的分型)。 + +#### 7.4.3 失败分型(本次新增的核心) + +重登失败不能笼统地「再试一次」或「丢给人工」。按原因落 **`login_tasks.relogin_failure_kind`** +(枚举字段,新增迁移;**不要靠 `attempt_message` 做字符串匹配** —— 文案一改就失效, +且页面要按类型筛选),不同分型走不同后续: + +| kind | 触发(线上实测条数) | 后续动作 | +| --- | --- | --- | +| `captcha` | 极验滑块失败(144 / 35%) | **转人工队列,不再自动重试**(反复撞只会加重风控) | +| `email_code` | 验证码错误 / 等待超时(144 / 35%) | **自动重试**(走退避表);这批多半正是 `20261001` 修掉的取码 bug 造成的 | +| `interrupted` | 服务重启 / 人工停止(121 / 29%) | **不是真失败** → 自动重试 | +| `email_quota` | 「发送次数已达上限」 | 退避到**次日零点 + 随机抖动 ±30 分钟**(决策 1:只影响该账号,批次继续) | +| `credential` | 密码错误 / 账号异常 | **转人工队列**(需核对密码或邮箱) | +| `network` | 请求失败 / 超时 / 连接异常 | 自动重试(退避) | + +> 64% 的线上「失败」(`email_code` + `interrupted`)其实是可自动解决的;不分型就会把它们 +> 一起推给人工,白费 265 个可救名额。 + +#### 7.4.4 退避与熔断 + - **退避**:`relogin_attempts` 1→1h、2→3h、3→12h、4+→24h(写 `relogin_next_at`); -- **邮箱配额(决策 1)**:命中 `EmailQuotaError` 时 - - 只把**该账号**标记为「邮件配额用尽」,`relogin_next_at` 设为次日零点 + 随机抖动(默认 ±30 分钟,避免整点齐发),`relogin_last_error` 记录原因; - - **批次继续跑其他账号**,不全局暂停; - - 说明:现有 `login()` 已经是这个语义(单账号返回失败、批次继续),自动化层只需额外记录退避时间,避免反复重试同一账号; -- **系统性故障刹车(与决策 1 不冲突)**:以下情况仍会自动暂停作业并告警 —— - 代理 API 不可用 / 连续 N 轮失败率超过阈值(默认 80%)/ 单轮异常数超过阈值。这些不是单账号问题,继续跑只会浪费资源。 +- **极验即停**:同一账号连续 2 次 `captcha` → 直接转人工,不再自动重试; +- **系统性故障刹车(与决策 1 不冲突)**:代理 API 不可用 / 单轮失败率超阈值(默认 80%) + / 单轮异常数超阈值 → **自动暂停作业并告警**。这些不是单账号问题,继续跑只会浪费资源。 + +#### 7.4.5 速率与风控 + +| 约束 | 实测/建议 | +| --- | --- | +| 邮箱配额 | **13,618 个账号对应 13,618 个独立邮箱(1:1,实测)** → 单账号配额满不影响他人,决策 1 成立;也不存在「打爆共享邮箱」的问题 | +| IP 才是瓶颈 | 1,356 次登录 + 1,356 封验证码,短时间集中做会触发风控 → **走现有代理池 + 限速每小时 30~60 个**(约 1~2 天跑完,后台慢慢来完全可以接受) | +| 单轮上限 | `batch_size` 默认 20(5 分钟一轮 = 上限 240/小时,实际由退避与每日上限压住) | + +#### 7.4.6 页面呈现(对应「失败单独标记提示」) + +`/automation` 页在作业卡片之外增加两块: + +- **自动重登中**:有退避倒计时、下次重试时间、当前 `relogin_attempts`; +- **待人工处理**:按 `relogin_failure_kind` 分组 —— + - 极验失败(要人工过滑块) + - 凭证问题(要核对密码/邮箱) + - 多次失败(要人工查账号可用性) + +#### 7.4.7 上线方式(三段,同 P1a 的谨慎程度) + +1. **小规模实测**:挑 10~20 个 invalid(覆盖「未重试过」与「取码 bug 时代失败过」两类), + 用现有手动重登链路跑一次 —— 把「78% 成功率」从历史推断变成实测,同时验证 + `20261001` 的取码修复在生产真的生效; +2. **限量开作业**:按实测成功率定 `batch_size` 与日上限,先只跑一个分组; +3. **全量**:观察若干轮,确认分型与退避符合预期后再放开。 ### 7.5 并发与互斥 @@ -972,13 +1048,14 @@ pending_material(待用户补资料) ← 模板有必填字段时的 | **P2** | 资料收集链接(公开页 + **OSS 图片直传** + 后端代理读图 + 队列 + 订单回显) | P0(订单回显)+ 运维建 OSS 桶;P0 未完成时先留手工填订单号 | 5~6 人日 | | **P3** | 皮肤台账(`skin_catalog` 以斗鱼商品为锚 + `skin_aliases` 映射 + 四路来源 + 365 天规则 + `/skins` 页) | P0(订单皮肤);映射分析**已完成**(见分析文档),人工只需过 60 个商品 / 76 条别名 | 4~5 人日 | | **P4** | 皮肤兑换工单(规则/模板 + 匹配层 + 工单/事件表 + 状态机 + 三处界面 + 前置校验作业) | P1a + P2 + P3(**不依赖 P1b**:CK 失效时先标记等人工重登,P1b 上线后自动接管) | 6~8 人日 | -| **P1b** | CK 自愈(`ck_relogin` 自动重登:退避 + 熔断 + 开关,**默认关闭**) | P1a 观察一到两周 | 1.5~2 人日 | +| **P1b** | CK 自愈(`ck_relogin` 自动重登:**失败分型** + 退避 + 熔断 + 开关,**默认关闭**) | P1a(检测已验证,不必再等观察期) | 2.5~3 人日 | -**建议顺序:P1a → P0 → P2 → P3 → P4 → P1b。** +**建议顺序:P1a → P1b → P0 → P2 → P3 → P4。** -- **P1a 先上**:只做「帮你把失效 CK 找出来」,零风险、立刻省掉大批量人工点检测;观察一到两周、确认检测结果与人工判断一致后,再打开 P1b; +- **P1a 已上线**(2026-10-02),只做「帮你把失效 CK 找出来」,零风险; +- **P1b 提到 P1a 之后**:原计划等一到两周观察期,但线上数据已把三个前提验证完了 —— 检测结果与人工/直连逐条一致、误判路径已封(三类判定 + 自动暂停)、失效性质查清(会话过期而非凭证问题)。不联动的话,巡检每天翻出失效清单却没人兑现修复,1,356 个账号只会越积越多; - **P0 随时可做**:只动本系统,order_site 侧代码改动量为 0; -- **P1b 代码可与 P1a 一起写完**,但开关默认关闭,等观察结论再开; +- **P1b 开关仍默认关闭**,按 §7.4.7 三段上线:小规模实测 → 限量开 → 全量; - **P1a 上线按三段走**: ① 打开 `AUTOMATION_ENABLED=true` 并重启(此时作业开关仍关着,调度线程空转,不会有任何检测); ② 在 `/automation` 页点「立即跑一次」并先把「每轮条数」设 **50** —— 手动触发不受开关限制,正好用来观察斗鱼风控与失败率; @@ -1115,7 +1192,8 @@ OSS_PREFIX=collect | 阶段 | 迁移内容 | | --- | --- | | P0 | `external_order_cache` | -| P1 | `automation_jobs`、`automation_runs`;`login_tasks` + `relogin_attempts`/`relogin_next_at`/`relogin_last_error`;`accounts` + `auto_managed` | +| P1a | `automation_jobs`、`automation_runs`;`login_tasks` + `relogin_attempts`/`relogin_next_at`/`relogin_last_error`;`accounts` + `auto_managed`(revision `20261002_0037`) | +| P1b | `login_tasks` + `relogin_failure_kind`(枚举;按落地顺序取号) | | P2 | `collect_links`、`collect_submissions`(截图只存 `object_key`,不存 URL/本地路径) | | P3 | `skin_catalog`、`skin_aliases`、`skin_douyu_items`、`account_exchange_records` | | P4 | `exchange_skin_rules`、`exchange_match_logs`、`exchange_orders`、`exchange_order_events`(**4 张**,映射层已取消) |