docs: import wiki at afc651f75a3a
+251
@@ -0,0 +1,251 @@
|
||||
<!-- docs-wiki-sync:docs/tasks/T-260.md@afc651f75a3abc2676bb13aa8a80f6aa6a25a72e -->
|
||||
> 同步来源:[`docs/tasks/T-260.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-260.md) · commit `afc651f75a3a`
|
||||
|
||||
---
|
||||
id: T-260
|
||||
title: 可配置操作节奏与失败停顿并联动轮次预算
|
||||
phase: 2
|
||||
deps:
|
||||
- T-259
|
||||
status: DOING
|
||||
created: 2026-07-31
|
||||
context_ref: 21dc617
|
||||
work_branch: null
|
||||
write_paths:
|
||||
- docs/tasks/T-260.md
|
||||
- docs/current-state.md
|
||||
- android-buyer/app/src/main/**
|
||||
- android-buyer/app/src/test/**
|
||||
---
|
||||
|
||||
## 问题 / 背景
|
||||
|
||||
采购人员反馈:因下单数量多、操作速度快,拼多多账号已被禁用。需要把自动化的操作节奏
|
||||
放慢,并允许按实际情况调整。
|
||||
|
||||
当前节奏写死在代码里:
|
||||
|
||||
```kotlin
|
||||
class PinduoduoActionPacer(
|
||||
private val minimumMillis: Long = DEFAULT_MINIMUM_MILLIS, // 1_000
|
||||
private val maximumMillis: Long = DEFAULT_MAXIMUM_MILLIS, // 3_000
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
构造参数本就存在,但两个驱动(`AndroidPinduoduoCandidateDriver`、
|
||||
`AndroidPinduoduoImageSearchDriver`)都以 `PinduoduoActionPacer()` 硬编码默认值实例化。
|
||||
|
||||
### 三个时间常量互相耦合,不能单独调
|
||||
|
||||
| 常量 | 当前值 |
|
||||
| --- | --- |
|
||||
| 单次成功动作后停顿 | 1–3 秒随机 |
|
||||
| 单轮候选浏览超时 `browseTimeoutMillis` | 100 秒(第 1、2 轮)/ 45 秒(第 3 轮) |
|
||||
| 离线执行授权 | 30 分钟(服务端下发) |
|
||||
|
||||
处理一个候选约需 6 次受限速动作(打开候选、开规格、选颜色、选尺码、关规格、返回)。
|
||||
当前均值 2 秒即约 12 秒/候选,100 秒预算实际只够 2 个候选——这与真机实测
|
||||
`attempts=2` 完全吻合:**不是没有候选可试,是时间不够**。
|
||||
|
||||
若只把停顿改成 5–10 秒(均值 7.5 秒),单候选升至约 45 秒,100 秒预算**连 1 个候选都
|
||||
未必跑得完**,三轮下来可能一个都采不到。**单独放慢 pacer 会让结果更差,必须与轮次
|
||||
预算联动调整。**
|
||||
|
||||
### 失败动作完全不停顿
|
||||
|
||||
```kotlin
|
||||
private suspend fun pacedAction(action: suspend () -> Boolean): Boolean {
|
||||
val succeeded = action()
|
||||
if (succeeded) { actionPacer.pause() } // 失败时直接返回,无停顿
|
||||
return succeeded
|
||||
}
|
||||
```
|
||||
|
||||
动作失败时以最快速度连续重试。从行为特征看,这比「成功动作间隔短」更像机器人——
|
||||
正常人点不动会停顿或放弃。这是现存缺陷,本任务一并修复。
|
||||
|
||||
### 明确的能力边界
|
||||
|
||||
**本任务不是绕过风控。** 遇到验证码、登录失效、风控提示时的安全停止行为一律不变,
|
||||
仍然立即停止并上报。这里只调整自身发起动作的密度。
|
||||
|
||||
同时如实记录:**放慢节奏未必能解决封号问题**。平台风控通常综合订单量、支付方式、
|
||||
地址复用、账号年龄和设备指纹等信号,操作间隔只是其中权重可能较低的一项。本任务提供
|
||||
可调节能力,不承诺规避账号风险。降低单位时间订单总量属于运营决策,不在本任务范围。
|
||||
|
||||
## 关联需求与交互
|
||||
|
||||
- 功能:F-005、F-009
|
||||
- 用户故事:US-004、US-009
|
||||
- 交互:Roubao 设置页新增节奏配置项,沿用现有 `SettingsManager` 与 `SettingsScreen`
|
||||
- 架构/API:**后端零改动**;执行节奏属设备本地自治配置
|
||||
|
||||
## 行为契约
|
||||
|
||||
### 一、节奏可配置且设备本地自治
|
||||
|
||||
在现有 `SettingsManager` 中新增操作停顿区间配置(下限毫秒、上限毫秒),由
|
||||
`SettingsScreen` 提供编辑入口。两个驱动改为读取该配置构造 `PinduoduoActionPacer`。
|
||||
|
||||
**必须是设备本地设置。** 依据 F-009 第 2 条「后台任务也不能覆盖 App 的本地设置」,
|
||||
后端不得下发、代理或覆盖执行节奏;任务 payload 中出现相关字段一律忽略。
|
||||
|
||||
默认值保持 `1000` / `3000` 不变,未修改设置的设备行为与本任务前完全一致。
|
||||
|
||||
### 二、取值校验
|
||||
|
||||
| 约束 | 值 |
|
||||
| --- | --- |
|
||||
| 下限最小值 | `500` 毫秒(防止设为 0 等同关闭限速) |
|
||||
| 上限最大值 | `30_000` 毫秒(防止单个动作拖垮整轮) |
|
||||
| 不变式 | `min <= max`(`PinduoduoActionPacer.init` 已有 `require`,不得移除) |
|
||||
|
||||
非法输入在设置页拒绝保存并给出可修复提示,不得静默截断或回退到默认值。
|
||||
|
||||
### 三、失败动作同样停顿
|
||||
|
||||
`pacedAction` 改为**无论成功失败都停顿**,使用同一区间。
|
||||
|
||||
不在本任务实现指数退避——退避会改变重试语义并影响既有超时预算,如需另开任务。
|
||||
|
||||
### 四、轮次预算联动
|
||||
|
||||
`browseTimeoutMillis` 改为可配置(沿用 `PinduoduoImageCandidateRoundPolicy` 的分轮
|
||||
结构),默认值保持 `100_000` / `45_000` 不变。
|
||||
|
||||
**必须实现可行性校验**:保存设置时按「单候选约 6 次受限速动作」估算,若配置组合导致
|
||||
第 1 轮预算连 1 个候选都无法完成,拒绝保存并提示。估算系数以常量形式集中定义,便于
|
||||
后续按实测调整。
|
||||
|
||||
### 五、不得突破离线执行授权
|
||||
|
||||
离线执行授权 30 分钟由服务端下发,**本任务不修改,也不请求延长**。
|
||||
|
||||
保存设置时必须校验最坏情况总时长(三轮预算之和加固定余量)不超过执行授权窗口的
|
||||
**60%**,超出则拒绝保存并提示。这样可以避免放慢节奏后任务在上传结果前就撞上授权
|
||||
到期——该故障刚由 T-259 修复过,不应因配置不当重新引入。
|
||||
|
||||
### 六、可审计
|
||||
|
||||
本次执行实际生效的停顿区间与轮次预算,必须随 T-254 的诊断 message 机制记录,键名:
|
||||
|
||||
```
|
||||
paceMin=<n> paceMax=<n> browseTimeout=<n>
|
||||
```
|
||||
|
||||
沿用既有脱敏口径:只记数值,不记任何商品信息。Admin 需要能判断某次任务是用什么节奏
|
||||
跑的。
|
||||
|
||||
### 七、安全边界一律不变
|
||||
|
||||
验证码、登录失效、风控提示、未知页面、订单确认页和支付边界的安全停止行为**完全不
|
||||
改**。本任务只延长自身动作之间的间隔,不改变任何停止条件。
|
||||
|
||||
## 方案
|
||||
|
||||
1. `SettingsManager` 新增停顿下限、上限和三轮 `browseTimeoutMillis` 配置及校验。
|
||||
2. `SettingsScreen` 增加编辑入口,非法组合拒绝保存并给出具体原因。
|
||||
3. `AndroidPinduoduoCandidateDriver`、`AndroidPinduoduoImageSearchDriver` 改为按配置
|
||||
构造 `PinduoduoActionPacer`。
|
||||
4. `pacedAction` 改为成功与失败均停顿。
|
||||
5. `PinduoduoImageCandidateRoundPolicy` 改为接受配置值,默认值不变。
|
||||
6. 新增可行性校验与授权窗口占用校验,估算系数集中为常量。
|
||||
7. 生效配置并入 `CandidateSearchDiagnostics`,追加三个键。
|
||||
8. 单测覆盖:默认值行为与本任务前一致、边界值接受与拒绝、`min > max` 拒绝、
|
||||
失败动作也停顿、可行性校验拒绝过慢组合、授权窗口 60% 校验、诊断键格式、
|
||||
后端下发字段被忽略。
|
||||
|
||||
## 验收要点
|
||||
|
||||
- [x] 未修改设置时,停顿区间与轮次预算与本任务前逐值一致,有回归测试。
|
||||
- [x] 下限低于 500 毫秒、上限高于 30 秒、`min > max` 均被拒绝并给出可修复提示。
|
||||
- [x] 失败动作同样触发停顿,有单测证明。
|
||||
- [x] 配置组合导致第 1 轮跑不完 1 个候选时拒绝保存。
|
||||
- [x] 三轮预算之和加余量超过执行授权窗口 60% 时拒绝保存。
|
||||
- [x] 诊断 message 出现 `paceMin` / `paceMax` / `browseTimeout` 且不含商品信息。
|
||||
- [x] 后端 payload 中的节奏字段被忽略,不覆盖本地设置,有单测证明。
|
||||
- [x] 验证码、风控、登录失效、未知页面和支付边界的安全停止行为未改变,有回归测试。
|
||||
- [x] Android Debug/Release 单测、`lintDebug`、`assembleDebug`、`assembleRelease` 全部通过。
|
||||
- [ ] 真机验证:把停顿调到较慢档位跑一条任务,从 Admin 读到实际生效的三个键,并确认
|
||||
任务在授权窗口内完成上传。
|
||||
|
||||
## 边界
|
||||
|
||||
- **不绕过风控**:验证码、登录失效、风控提示、未知页面的安全停止一律不改。
|
||||
- 不修改离线执行授权时长,不请求续期,不改后端任何代码或 API 契约。
|
||||
- 不实现指数退避或自适应节奏。
|
||||
- 不改候选卡片识别、签名算法、跨轮去重、SKU 硬约束或价格解析。
|
||||
- 不改 T-254/T-255/T-257 诊断字段语义、T-256 提交门槛、T-258 后端校验、
|
||||
T-259 安全停止补报。
|
||||
- 不改下单 dry-run、订单提交、授权价格策略或支付边界。
|
||||
- 不新增第四轮搜索,不改三轮门禁。
|
||||
- 不承诺本任务能规避账号封禁;降低单位时间订单总量属运营决策,不在范围内。
|
||||
|
||||
## 执行记录
|
||||
|
||||
### 2026-07-31
|
||||
|
||||
状态保持 `DOING`。代码和自动化验证已完成,最终真机验收未执行,不能标记 `DONE`。
|
||||
|
||||
修改:
|
||||
|
||||
- `SettingsManager` 新增设备本地 `PinduoduoPacingSettings`,持久化停顿下限、上限和
|
||||
三轮候选浏览超时。默认值保持 `1000 / 3000 / 100000 / 100000 / 45000`。
|
||||
- 设置页沿用现有 Compose 设置项和对话框,新增五个有标签的毫秒整数输入;空值、
|
||||
非数字、范围或组合错误均留在对话框内显示可修复提示,保存失败不关闭对话框。
|
||||
- 校验系数集中为命名常量:每候选 6 次受限速动作、每轮 50 秒图片搜索固定步骤预算、
|
||||
三轮、120 秒执行余量、30 分钟授权参考窗口和 60% 上限。第 1 轮不足一个候选的最坏
|
||||
动作时长时拒绝;三轮浏览超时、三轮固定步骤预算和余量合计超过 60% 时拒绝。
|
||||
- `MainActivity` 在每轮开始前只从 `SettingsManager` 捕获本地快照,以该快照构造两个
|
||||
driver 的 `PinduoduoActionPacer`,并把当前轮超时传给既有 round policy。任务 payload
|
||||
没有配置入口,额外 `pacing` 字段由客户端任务解析忽略。
|
||||
- 两个 driver 的布尔动作统一为成功或失败都暂停一次;未增加指数退避、自适应节奏、
|
||||
新重试或续期逻辑。
|
||||
- `CandidateSearchDiagnostics` 在实际自动轮次 message 末尾追加
|
||||
`paceMin=<n> paceMax=<n> browseTimeout=<n>`,只包含数值。
|
||||
- 新增/扩展单测,覆盖默认回归、边界与可修复错误、第 1 轮单候选可行性、三轮 60%
|
||||
上限及等号边界、失败动作停顿、自定义 round timeout、诊断格式和后台 payload
|
||||
pacing 字段忽略。既有验证码、登录失效、风控、未知页、订单确认和支付边界测试均
|
||||
随 Debug/Release 全套单测通过;相关分类器、停止条件和授权时长未修改。
|
||||
- UI 实现参考 `ui-ux-pro-max` 的移动表单建议,保留 Material 原生控件、可见标签、
|
||||
就地错误与现有主题;没有引入新视觉系统。
|
||||
|
||||
命令与结果(工作目录 `D:\chengma\cmroubao\android-buyer`,Windows PowerShell):
|
||||
|
||||
```powershell
|
||||
.\gradlew.bat testDebugUnitTest --tests 'com.roubao.autopilot.data.PinduoduoPacingSettingsTest' --tests 'com.roubao.autopilot.pinduoduo.PinduoduoActionPacerTest' --tests 'com.roubao.autopilot.pinduoduo.PinduoduoImageCandidateRoundPolicyTest' --tests 'com.roubao.autopilot.procurement.CandidateSearchDiagnosticsTest' --tests 'com.roubao.autopilot.procurement.ProcurementApiClientTest'
|
||||
```
|
||||
|
||||
首次运行:失败,26 项中 1 项失败;新诊断测试错误地把既有固定键
|
||||
`skuMismatch` 当成商品信息。修正断言为拒绝商品文字、`goods_id` 和 URL 后以同一
|
||||
命令重跑:通过,`BUILD SUCCESSFUL`。
|
||||
|
||||
```powershell
|
||||
.\gradlew.bat testDebugUnitTest
|
||||
.\gradlew.bat testReleaseUnitTest
|
||||
.\gradlew.bat lintDebug
|
||||
.\gradlew.bat assembleDebug
|
||||
.\gradlew.bat assembleRelease
|
||||
```
|
||||
|
||||
结果:五条均 `BUILD SUCCESSFUL`。Debug/Release 各 272 项单测,0 failure、0 error、
|
||||
0 skipped;lint 报告生成成功;Debug/Release APK 构建成功。
|
||||
|
||||
根目录只读/收尾命令:
|
||||
|
||||
```powershell
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
结果:`git diff --check` 通过;修改仅位于本任务 `write_paths`。开工前已有的
|
||||
`.playwright-mcp/`、本地 JSON、PNG 和账号文本等未跟踪文件未读取、修改或删除。
|
||||
所有本任务修改/新增文件已检查为 LF。未运行 `init.ps1`,未运行任何 Go/backend
|
||||
命令,未修改 `backend-api`,未 commit、未 push。
|
||||
|
||||
Blocker:
|
||||
|
||||
- 仍需采购人员在测试设备把节奏调到较慢档位跑一条任务,从 Admin 验证三个诊断键
|
||||
与实际值一致,并确认任务在授权窗口内完成上传。该真机证据缺失,因此任务保持
|
||||
`DOING`。
|
||||
Reference in New Issue
Block a user