yml2213
|
973042288f
|
feat(douyu): 增加协作单礼包字典与权限控制
|
2026-11-06 15:09:50 +08:00 |
|
yml2213
|
c9fbbdb828
|
feat(douyu): 支持账号批量撤回并刷新未分配状态
新增按勾选或标签批量撤回账号归属,撤回只处理确实已分配的账号。账号管理和分配管理增加可见时自动刷新、窗口聚焦刷新及手动刷新,解决批量登录完成后未分配账号延迟出现。
|
2026-11-06 13:25:32 +08:00 |
|
yml2213
|
4c1aff47da
|
feat(douyu): 高价值套装改用官方 storehome 三态判定
高价值套装的准入判定改为只认官方精英宝典 storehome 里账号级的
status:0=可兑换、1=已达兑换上限,商品缺失/接口异常=unknown。
兑换记录和积分记录只作历史展示,不参与是否拥有的判定。
原来的 high_value_owned(JSON 名单)无法区分「确认可兑换」和
「官方接口没返回商品」,也无法表达部分失败。改为两套独立状态 +
检查时间(迁移 20261106_0065),旧快照保守回填:status 缺失的
一律 unknown,不猜。
CK 判定失效时在数据层作废结论(降为 unknown),不是展示层降级。
官方 status 是账号维度的,但 CK 失效那段时间账号可能被别人手工
登录并兑换(这些号共享给客服),缓存里的「可兑换」就不成立了;
只在展示层降级的话,CK 恢复后那条陈旧的「可换」会重新变绿。
error(查不到)不作废 —— 查不到不等于账号出问题了。
高价值结论有自己的时钟:资料刷新除了看 overview_queried_at,还看
high_value_checked_at。两者会脱钩(商品接口单独失败时资料时间被刷新、
高价值时间停在原地),少了这一条这类账号会永久停在旧结论。作废的
结论按 6 小时慢速重试,但「从来没查到过」不因此重选 —— 那是接口对
该账号稳定失败,每轮都重选只会反复撞墙。
商城商品列表改用精英宝典的 manualID 拉取,不再寄生在电竞手册分支里
(否则没配电竞手册的配置就永远拉不到商城,三态全部停在待确认)。
筛选拆成三类「查不到」:unqueried(未查资料,旧口径不变)、
high_value_never(从未读到商城)、high_value_unknown(查过但结论
未知/过期)。之前第二类无处可寻 —— 它既不满足 unqueried 也不满足
high_value_unknown。
|
2026-11-06 12:25:08 +08:00 |
|
yml2213
|
e7e187cf57
|
docs: 精简项目文档并清理 Ruff 问题
|
2026-11-06 11:09:53 +08:00 |
|
yml2213
|
6fc453017c
|
feat(auth): 后台登录改为滑动会话
- JWT 记录 iat,exp=min(now+24h, iat+7d)
- 已认证请求自动续签 httpOnly Cookie
- 新增 ACCESS_TOKEN_IDLE_HOURS / ACCESS_TOKEN_MAX_HOURS
|
2026-10-25 14:07:44 +08:00 |
|
yml2213
|
2de5d374df
|
feat(douyu): 拆分鱼翅积分与兑换记录查询
|
2026-10-23 10:42:42 +08:00 |
|
yml2213
|
84d7c8e0a4
|
feat(douyu): 精英宝典工作台支持 CK 检测与失效重登
|
2026-10-23 10:21:15 +08:00 |
|
yml2213
|
f074b75871
|
feat(douyu): 支持批量导出用户名
|
2026-10-23 09:52:47 +08:00 |
|
yml2213
|
bf2f120fdc
|
feat(douyu): 外部单协作支持复制 CK
|
2026-10-23 09:29:53 +08:00 |
|
yml2213
|
96c4f41674
|
fix(assignments): 未分配列表不再混入 CK 失效账号
分配池统一为:持 CK、CK 未判失效、未被停用/归档。新增 assignable_only 参数贯穿账号列表、批量分配与分配概览;前端列表与一键全部分配使用同一口径。
|
2026-10-22 11:01:55 +08:00 |
|
yml2213
|
2f5a63ae27
|
feat(auth): 新增手机号与 DCAuth 设备认证登录
- 登录链路改为用户名 + 密码 + 短信验证码 + DCAuth v2 设备签名
- 新增 login_sms_codes / user_devices / device_challenges 三张表,迁移 0063/0064
- 设备绑定只比对 deviceId + publicKey,每账号一个 active 设备,管理员可重置
- 保留未绑定手机号账号的密码登录兜底,绑定后强制安全登录
- 登录页与用户管理页支持验证码、DCAuth 轮询、手机号绑定与设备重置
|
2026-10-21 22:00:03 +08:00 |
|
yml2213
|
71574908ff
|
feat: 斗鱼外部单协作台账与店铺实时同步
|
2026-10-20 20:11:35 +08:00 |
|
yml2213
|
c778ae9a09
|
feat: 账号管理支持按账号名批量选号打标签,并支持复制账号名
|
2026-10-20 17:31:39 +08:00 |
|
yml2213
|
3a6649a054
|
feat: 重做协作台账状态机并支持充值核账
|
2026-10-19 16:06:02 +08:00 |
|
yml2213
|
781b27e63a
|
feat: 协作台账支持归档并精简状态列
|
2026-10-19 13:04:27 +08:00 |
|
yml2213
|
21f7ee3f3f
|
feat(huya): 礼包名默认空白,改用「常用礼包」备选按钮
新建协作单以前会把「上次选的礼包」自动填进礼包名(localStorage
huya.collab.lastGift)。连开几单同一个礼包是省事,但每开一单都顶着上一次的
选择,稍不留神就按错礼包建单;而且这个自动值会被草稿自动保存吃进去,
下次打开又「恢复」出来。
- 新建弹窗**不再预填礼包名**:删掉 lastGift 的读写,字段默认空白。
- 草稿恢复只带订单号 / 店铺 / CK / 截图 / 备注,礼包名留空;
hasDraftContent() 也不再认「只有礼包名」的草稿(否则被旧逻辑自动填过的
那份草稿会一直弹「已恢复上次未提交的草稿」)。
- 新增 GET /external-orders/gifts/frequent:按台账里的使用次数排序
(次数相同看最近一次使用),新建与补资料弹窗在字段上方渲染最多 6 个按钮,
只显示**当前礼包缓存里还有的**(活动换 SID 后老礼包会整批下架)。
- 新增 5 例测试,并做了 3 次反向验证(去掉次数排序 / 改成升序 / 去掉空名过滤)。
补资料弹窗的初值仍是「这一单自己的礼包」,详情抽屉编辑也不动 —— 那不是
「上次选的」。
|
2026-10-18 10:53:04 +08:00 |
|
yml2213
|
c441b6c663
|
feat(huya): 批量查询拆成「查积分」与「查兑换记录」两个按钮
前端工具栏原来一个按钮同时查两件事,拆开后每次只查一半,所以重点在
「没查的那一半不能被当成没查到」:
- 接口加 scope(both / score / records),默认 both 保持原行为;
只查积分时不产生判定(空串「未判定」,与 unknown「无法判定」分开),
也**不会**自动改业务状态(连判定都没有,凭什么改)。
- 列表的积分与判定各取「最近一条查到过它的」快照:点「查积分」不再把
整页判定徽标冲成未判定,点「查兑换记录」也不再清空积分;但新的
unknown 仍会盖掉旧的 success(抖动结果不许藏)。
- 结果弹窗 / 积分列 Tooltip / 生命周期时间线都按 scope 显示,
不再把「本次没查」显示成「积分 -」「兑换记录 0 条」。
- 新增 12 例测试(TestCollabActivityScopeSplit),并做了 4 次反向验证
(去掉判定分支 / 判定跟随最新 / 积分跟随最新 / 删掉响应字段),全部确认变红。
顺带修掉文档漂移:AGENTS.md 的迁移 head 早就过期(main 实际
20261016_0059、dev 20261008_0053),并补上「两侧已分叉成两个 head、
合并 dev 前必须先 alembic merge」这条;设计文档补 §11.9。
|
2026-10-17 23:32:51 +08:00 |
|
yml2213
|
f06869c4b1
|
feat(huya): 协作台账补资料与管理员复制 CK
|
2026-10-17 20:47:31 +08:00 |
|
yml2213
|
fe1f959648
|
fix(huya): 协作台账固定序号并支持表头排序
|
2026-10-17 16:05:11 +08:00 |
|
yml2213
|
fcc2fbdc09
|
feat(huya): 协作台账支持待补资料一键建单与表单暂存
|
2026-10-17 14:21:36 +08:00 |
|
yml2213
|
cad06ae789
|
feat(huya): 协作台账增加 WebSocket 实时同步
- 后端新增进程内事件总线与 /api/huya/external-orders/ws\n- 所有写接口成功后发布轻量失效事件\n- 前端自动重连、静默刷新、断线兜底轮询
|
2026-10-17 13:44:27 +08:00 |
|
yml2213
|
26b8d6b054
|
feat(huya): 协作台账支持皮肤筛选与主页截图拖拽/粘贴上传
- 外部单列表新增礼包/皮肤精确筛选,并提供去重下拉选项
- 主页截图支持拖拽、多选及 Ctrl/Cmd+V 剪贴板粘贴上传
|
2026-10-17 12:49:42 +08:00 |
|
yml2213
|
82c59bb4af
|
feat(huya): 协作台账勾选导出 CK 到剪贴板(单点权限 + 逐行审计)+ 工具区固定两行
|
2026-10-16 22:24:08 +08:00 |
|
yml2213
|
6ef74c8e45
|
refactor(huya): 协作台账 failed 状态展示名「兑换失败」→「抢购失败」(枚举值不变)
|
2026-10-16 22:09:21 +08:00 |
|
yml2213
|
fdc57752dc
|
refactor(huya): 协作台账 done 状态展示名「已完成」→「抢购成功」(枚举值不变)
|
2026-10-16 22:06:39 +08:00 |
|
yml2213
|
e8abd60734
|
feat(huya): 协作台账状态机新口径(跳步/已确认绑定/兑换失败)+ 账号全生命周期快照(迁移 0059)
|
2026-10-16 22:00:34 +08:00 |
|
yml2213
|
aac264b639
|
feat(huya): 协作台账店铺字典·新建强校验·同买家多单 + uid 放开(迁移 0057/0058)
- 店铺字典(迁移 0057):大按钮点选 + 「新店」自定义自动登记;重命名可级联、
停用/备注;列表行内改、详情、筛选条共用同一个 picker
- 新建强校验:订单编号/店铺/CK/礼包名/主页截图必填;贴 CK 即探针预览,
读不到绑定角色禁止提交;礼包用工作台配置的活动 SID 现拉(浏览器缓存 + 强制刷新)
- 图片:绑定图停用、「绑定斗鱼号」下线(绑定关系改由探针读 bind_game_*),
主页截图改为新建必填(弹窗内先传 OSS 再随表单落库)
- 同买家多单软识别:按「绑定账号 + 绑定角色」整批一次查询,列表「同买家」列 +
详情分组,点单号直接切单
- uid 放开唯一性(迁移 0058):同一份 CK 可服务多单,原 409 冲突改为提示 + 审计
- 列表体验:序号列、搜索扩到 11 个字段、CK 状态列截断、新建弹窗放大并去滚动条
- 验证:pytest 932 passed / 6 skipped;接口冒烟 56 项全绿;tsc+eslint+build 通过
|
2026-10-16 21:29:08 +08:00 |
|
yml2213
|
df8c63b829
|
feat(huya): 外部 CK 协作台账(S1–S6)+ 图片上传能力
协作台账:新表 huya_external_orders 替代 Excel 多人协同表(设计见
docs/设计-虎牙外部CK协作台账.md),一条 CK 一行,所有人共享、改动留痕。
- 前端 pages/HuyaCollabPage.tsx + 菜单「虎牙 → 外部单协作」/huya/collab:
内联编辑(带乐观锁 version)、图片缩略图、详情抽屉(字段表单 + 两个图片槽位 +
操作时间线)、粘贴导入弹窗(带 TSV 预览)、批量/单条验证 CK、页头统计条、筛选
- 两套状态机:业务 pending→bound→recharged→scripted→done(旁路 void)、
凭据 unverified/valid/invalid/disabled;唯一写入口在 services/huya_collab_service.py,
字段级审计 + 整数 version 乐观锁(不用 updated_at:DATETIME 只到秒)
- CK 只读探针 check_user_bind_game_account(真机判据:响应非 None 且 status == 200),
成功即覆盖写绑定快照 bind_game_account/bind_game_role/bind_game_channel(迁移 0056),
失败只改凭据状态、不动业务状态;人工只能停用/恢复(POST /{id}/ck/status)
- 贴/换/重建 CK、TSV 批量导入(SAVEPOINT 单行隔离,部分失败不整体失败)、
权限 huya:collab:view/edit/manage(删除只给运营)
- 孤儿图清理脚本 scripts/cleanup_orphan_file_assets.py(默认 --dry-run)
图片上传(协作台账截图用):OSS/本地双后端 + 缩略图变体 + /api/files 代理回读,
权限 file:upload / file:view。
验证:pytest 880 passed / 6 skipped;ruff format 全绿、ruff check 仅剩既有告警;
前端 tsc -b / eslint / vite build 通过;接口端到端冒烟 29 项全绿(TestClient,探针用假客户端);
探针真机冒烟 3 例(有效 CK=True、别人 uid=False、裸 cookie=False)。
|
2026-10-16 19:38:59 +08:00 |
|
yml2213
|
9a0d59825c
|
fix(huya): 开通商品只保留真正可购买的 spu,并清理历史死商品行
真机 2026-10-15(HAR 10.15-精英徽章两个-1 + 逐个 spu 探测):面板里的「精英徽章 hy-5872824」是积分中心「赠送指定礼物得积分」那排的礼物 spu —— getGoodsInfoV5 仍返回 200+价格,但 listPayChannelV5 返回 401「您的设备环境暂不支持购买该商品」,所以下单必报「当前支付渠道不可用」(与数量无关)。真能买的是 hy-8343549 精英徽章 ¥1 / hy-8343551 精英令 ¥10。
- 内置候选换成可购买的 4 个:hy-8328015 宝典 / hy-8327356 币 / hy-8343549 徽章 / hy-8343551 令
- 刷新商品增加「能买到」闸门:详情 200 后再校验 listPayChannelV5,非 200 或渠道为空即剔除并记录服务端原话
- 下单流程渠道失败时透出服务端消息,只有渠道真不在可用列表才说「当前支付渠道不可用(可用:…)」
- 商品列表只认「最近一次刷新」那一套:原先按账号并集返回,别的账号旧刷新留下的礼物 spu 会和最新一套叠成 7 个选项
- 刷新时清理历史死行:本账号旧行 + 全局默认行(account_id 为空)+ 其它账号里已不在候选中的 spu 行
- 测试:401 商品被剔除、渠道失败透传、列表只认最近一套、刷新清理别处死行
|
2026-10-15 21:01:23 +08:00 |
|
yml2213
|
0b9a01ab3f
|
feat(huya): 开通支付码复用未支付旧单 + 二维码改用支付宝收银台直达地址
- 限购商品(精英宝典 buy_limit=1)挂着未支付旧单时,真正先失败的是购买权限
校验 checkUserBuyAuth(真机 703「已超过该商品的购买上限,下单失败~」),
createOrderV5 根本不会执行;现在 checkUserBuyAuth 与 createOrderV5 两处失败
都会回退 find_reusable_order,拿同一商品的未支付旧单继续 payOrderSubmitV5。
- 新增支付宝收银台直达地址解析(resolve_pay_entry_url):跟随网关 302 后的
excashier.alipay.com/standard/auth.htm?payOrderId=…(93 字符,原始网关地址
1616 字符)写入结果 pay_cashier_url;解析失败回落本站短链
/api/huya/pay/<task_id>?k=<hmac>,后端跑在 localhost 时客户也扫得开。
- 前端 QRCode 显式 color="#000" bgColor="#fff":antd v6 暗色主题默认
color=token.colorText、bgColor=transparent,白码落在白底容器里根本看不见。
- 支付成功后列表「二维码」列不再展示二维码(qrDoneLabel → 已支付/已开通)。
测试:pytest 767 passed / 6 skipped;typecheck、eslint、ruff 通过。
|
2026-10-15 19:28:26 +08:00 |
|
yml2213
|
73536d62c8
|
feat(huya): 工作台补手机号/精英宝典列与一键查询
- 手机号列:外部 CK 的 `----手机号` 本来就落在 huya_accounts.game_phone,
列表一直没展示;现在单独成列,并纳入搜索
- 精英宝典列:显示「已开通/未开通/未查询」+ 开通时间 + 有效期
· 开通状态 = 积分中心 getRedeemNeedGoods 的 tag5(真机对照网页:1=已开通,
0=未开通,此时 tag7 会列出开通套餐),拿不准时为 NULL(显示「未查询」)
· 开通时间 = revenueWebUI.queryUserOrderList 里已支付的宝典订单 pay_time
(按 SPU 白名单 + 订单名含「精英宝典」识别,未支付订单不计,取最近一次)
· 有效期 = PlayMallUI.queryAllocation 页面配置的本赛季节点 tag10/tag11
(2026-10-15 抓包:精英宝典福利季丨Q4,2026-10-09 ~ 2026-12-31 23:59:59),
不写死索引,递归找带 tag10/tag11 的节点
- 新任务 query_all「一键查询」:勾选账号一次跑完角色绑定 + 宝典开通 + 积分,
按部分失败如实汇总(某个接口挂了不影响其它结论),并把宝典状态写回账号
- 迁移 20261015_0053:huya_accounts 新增 elite_opened / elite_open_time /
elite_expire_time(时间存 Unix 秒,与斗鱼 elite_expire_time 同约定)
- 开通商品按页面「赠送指定礼物得积分」那一排对齐成 6 款(去掉页面已不用的
旧版精英宝典 hy-5879340),列表接口只返回账号级快照并按 spu 去重
- 建 WSS 会话失败(积分中心端点静默不回包)时回退 HTTP cdnws,
不再让整批任务判失败
- 列表列宽改按比例分配,避免宽屏下被拉出奇怪的宽度;最近操作不再重复状态
- 新增 tests/test_huya_elite_query.py(10 条);同步迁移头版本断言
真机验证:#65 已开通(10-13 20:41:45,有效期 12-31)、#70 新开通
(10-15 16:37:37)判定与订单/网页一致
|
2026-10-15 16:53:30 +08:00 |
|
yml2213
|
7ca0f7ee28
|
fix(huya): 刷新开通商品不再依赖已下线的活动任务接口
真机(2026-10-15)实测 webActUI.getActTaskDetail(act 25135) 返回
601「活动已下线」,刷新开通商品第一步就失败,下拉框一直为空。
- 商品来源改为内置候选(积分中心页面「赠送指定礼物得积分」那一排,
全部经 getGoodsInfoV5 真机验证 code=200):精英宝典 hy-8328015 ¥30、
精英币 hy-8327356 ¥0.1、精英徽章 hy-5872824 ¥1、精英令 hy-5879346 ¥10、
精英令*10 hy-5874269 ¥100、精英令*100 hy-5874270 ¥1000;
旧版精英宝典 hy-5879340 页面已不再使用,从候选移除
- 活动任务详情降级为可选补充:返回非 200 只记 warning 并写入
result.task_detail_error,不再让整次刷新失败;失败原因以商品详情为准
- 刷新时清掉该账号全部旧商品行(不限 sid),避免同名商品重复
- 列表接口只返回账号级快照并过滤 2026-07-04 的全局种子行,
同一 spu_id 保留最新一行(原先 精英宝典 会出现两次)
- 前端:下拉标签带 spu 便于区分,按内置顺序排序,默认自动选中第一项
- 新增 tests/test_huya_recharge_goods_source.py(6 条):活动下线响应字节
回归、下线不阻断刷新、活动商品仍会并入、失败原因归属、跨 sid 清理、
列表去重与隐藏种子行(均已做反向验证)
真机验证:刷新成功 6 个商品,价格/库存与网页一致
|
2026-10-15 15:57:25 +08:00 |
|
yml2213
|
4ce30e5c26
|
feat(huya): 精英宝典工作台支持导入外部 CK
- 新增 POST /api/huya/workbench-accounts/import-cookies(huya:task + huya:import):
外部 CK 复用现有解析入库后直接加入当前用户的工作台集合,导入完即可在工作台执行
「生成开通支付码/绑定二维码/查询角色/刷新积分」等操作
- 新账号自动 assigned_to=导入者:客服的账号列表按归属过滤,不设归属会「导入成功但看不到」
- import_huya_cookies_detailed 返回账号对象供工作台复用,原 import_huya_cookies 行为不变
- 工作台账号卡片新增「导入外部 CK」入口(huya:import 权限控制)与导入弹窗
验证:新增 tests/test_huya_workbench_import_ck.py(入库、归属、工作台集合、重复导入幂等、
全脏数据不建记录);按 AGENTS.md 做了反向验证(关掉归属赋值后该用例变红,还原后恢复)。
前端 tsc -b 与全量 pytest 通过。
|
2026-10-15 14:56:58 +08:00 |
|
yml2213
|
ec07e4fc06
|
fix(config): 修复切换充值渠道报错及补齐 peace_enabled 字段声明
- 在 DouyuConfigOut 与 DouyuConfigUpdate 中补齐 peace_enabled 声明与序列化
- 在 update_config 中使用 getattr(req, field, None) 安全读取属性,避免 AttributeError 导致 500
- 在 _config_out 中加入 peace_enabled 输出映射
- 增加切换充值渠道与配置更新的单元测试
|
2026-10-08 11:49:41 +08:00 |
|
yml2213
|
4eef45efe4
|
fix(accounts): 完善手机号必绑与账号注销等登录异常分型识别
- 补充必须绑定手机号与账号注销/封禁/冻结等错误文案的识别规则
- 增加数据修正迁移 20261008_0052,回写历史账号和登录任务的失败分型
- 增加列表查询与导出时根据错误文案自动兜底细化 failure_kind 的逻辑
- 增加并完善相关分型单元测试
|
2026-10-08 11:26:19 +08:00 |
|
yml2213
|
f8ad6e1777
|
perf(assignments): 优化分配管理页面加载性能与角标数量展示
- 前端改造为服务端分页查询 (accountApi.listPaged),避免一次性拉取 1.6 万+ 账号导致页面卡死
- 优化后端 assignments_summary 聚合查询,消除大子查询临时表
- 修复 Badge 默认 99+ 限制,设置 overflowCount 支持展示完整账号数量
- 修复测试用例 test_business_events.py 当前时间跨天断言问题
|
2026-10-08 11:08:41 +08:00 |
|
yml2213
|
f101414f5e
|
feat(accounts): 新增登录异常分型统计、多选/全量导出、筛选记忆及页面组件模块化拆分
- 新增登录失败异常分型统计:支持「必须绑定手机号」、「极验滑块」、「凭证错误」、「邮箱取码失败」、「邮箱配额已满」、「网络异常」分型识别、生命周期联动(待人工)与一键筛选;
- 新增账号名单与明细导出功能:支持勾选多选或按当前筛选条件全选,可导出纯用户名名单 (TXT) 或 UTF-8 BOM 结构化明细报表 (CSV),并支持一键复制到剪贴板;
- 优化筛选条件记忆机制:支持页面切换/返回后自动保留所有筛选与排序状态,新增顶部「重置全部筛选」快捷按钮;
- 模块化拆分 AccountsPage:将 2100+ 行的大文件拆解至 pages/accounts/ 子目录(包含状态卡片、筛选面板、工具栏、操作弹窗与表格构建器),提升可维护性;
- 验证方式:新增 5 个后端自动化单测 (test_account_failure_and_export.py),全量 694 个测试用例通过,前端 TypeScript 编译与 pre-commit 代码规范检查全部通过。
|
2026-10-07 17:23:39 +08:00 |
|
yml2213
|
44b762ea8c
|
fix(overview): 锁定核心高价值套装并修复未查询空值误判
- 高价值套装精确匹配「套装-兔兔白日梦」与「套装-狂战萌兔」,排除头盔/背包/手榴弹散件干扰
- 增加 is_suit_queried_expr,未查询或 NULL 账号严禁误判为双套均可兑换
- 修复前后端序列化空值保持,完善列表与详情页准入状态和判定依据展示
- 新增头盔排除与 NULL 统计防错回归单测,全量通过 689 个用例
|
2026-10-07 15:27:30 +08:00 |
|
yml2213
|
d11fa1032d
|
feat(login-tasks): 按状态导出 CSV + 登录失败写进账号生命周期
一、失败入账号一生(此前完全缺失)
生产实测:`login_ok` 1,456 条、`relogin_failed` 73 条,但**没有 login_failed**,
而 login_tasks 里 attempt_status='failed' 有 247 条 —— 这 247 次失败在账号时间线上
一条痕迹都没有,「这账号为什么一直登录不上」在账号页上无从回答。
- 新增事件类型 login_failed,在 `_mark_attempt_failed` 里写入
- **被停止 / 被中断不写**:分型把「已停止/中断/服务重启」归到 interrupted,
那不是账号的问题 —— 否则运营按一次停止就能给几十个账号刷满假失败
- 事件带分型(captcha/credential/email_code/email_quota/network),
同时落进 detail.kind,以后可直接按「验证码失败」筛账号
- 幂等键 login_task:{id}:failed:{ts},与成功路径同构
二、失败分型下沉成共用 service
`classify_failure` 原在 automation/jobs/ck_relogin.py,手动登录也要用。
挪到 services/login_failure.py(job 改为 import,`ck_relogin.classify_failure`
这个名字保留,旧调用方与用例不受影响),并提供型别中文标签与
`is_account_fault` 判据 —— 与仓库既有的「service 不该依赖 job」分层一致。
三、按状态导出 CSV
- 新增 GET /api/login/tasks/export:不分页,导出当前筛选的**全部**匹配记录;
列:账号/状态/失败原因/失败分型/批次/最近尝试时间(UTC)/标签/归属客服
- 权限与筛选与列表**完全共用**,客服只导出自己名下账号
- 带 UTF-8 BOM(不加 Excel 打开中文表头是乱码),导出动作写审计
四、修一个口径不一致的真 bug
页面「失败 N」= failed + error 之和,但筛选只认 failed ——
所以截图里「失败 389」点进去只有 386 条(差的就是那几条 error);
「清空失败」同样漏删 error。现在把状态组收敛成一份 `_apply_status_filter`,
列表 / 导出 / 批量删除三处共用。
测试:684 passed, 6 skipped(本次新增 13 个);三条关键断言做了反向验证
(去掉 stopped 判断 / 失败组去掉 error / 去掉 BOM,对应用例均变红)。
|
2026-10-07 12:54:57 +08:00 |
|
yml2213
|
281769b440
|
feat(accounts): 账号列表按归属筛选,批量分配支持按筛选全选
原来只有 assigned_only(已分配),筛不出「未分配」,也看不到账号归谁。
新增 assign_status/assigned_user_id 可与标签、CK、搜索等叠加;
BatchAssign 支持 all_matching,「筛出未分配 → 一键分给某人」不必先勾选。
|
2026-10-07 09:13:54 +08:00 |
|
yml2213
|
5da12df16b
|
feat(account-check): 登录接口可选 wgapi / iframe
账号检测原来把 WgapiLoginAPI 写死在执行器里,收不到邮箱验证码时只能干等;
登录任务早就可选 iframe(邮箱验证走 iframe 接口)。默认仍 wgapi,旧行为不变。
|
2026-10-06 20:19:26 +08:00 |
|
 yml2213andCursor
|
f2157f67f7
|
feat(accounts): 账号列表按列排序,默认把还能换套装的排到前面
分页在服务端,点表头按全库排,不能只排当前这一页。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-05 18:50:43 +08:00 |
|
 yml2213andCursor
|
998998c390
|
feat(automation): 巡检和重登优先保住高价值账号的 CK
价值按套装和积分现算,不看标签。第 1、2 档 6 小时巡检一次,并先占重登名额。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-05 17:51:04 +08:00 |
|
yml2213
|
27e9107e38
|
fix(accounts): 修复鱼翅筛选参数类型为浮点数并优化错误提示
1. 后端将 min_gold 与 max_gold 参数及 Schema 类型从 int 改为 float | None,支持小数鱼翅(如 >0 快捷筛选的 0.01);
2. 修复前端 axios 拦截器在遇到 FastAPI 422 验证错误时未展开 detail 导致弹出 [object Object] 的问题;
3. 单元测试补充验证浮点数鱼翅过滤场景。
|
2026-10-05 11:36:46 +08:00 |
|
yml2213
|
512ea3db06
|
feat(accounts): 增加高价值资产一键筛选与高级自定义筛选功能
1. 后端新增高价值套装状态筛选(双套均可换/可换白日梦/可换萌兔/已拥有/高价值待救/未查资料),兼容 MySQL 原生 JSON 与 SQLite unicode 转义存储;
2. 后端新增多维资产与状态区间筛选(积分范围、鱼翅范围、宝典开通、电竞开通、角色绑定状态),并支持与批量打标签/删除联动;
3. 统计接口 accounts_summary 新增 high_value 聚合数据,毫秒级返回各类高价值资产数量;
4. 前端账号管理页面新增「高价值一键筛选」胶囊区(带数量徽标与激活状态,即点即滤);
5. 前端新增可折叠「高级自定义筛选」面板,支持套装状态下拉、积分快捷区间(≥300、≥600)、鱼翅快捷区间(>0、≥6)、宝典与角色绑定状态多条件组合;
6. 补充 6 个高价值筛选与资产区间单元测试,全量用例通过。
|
2026-10-05 11:23:48 +08:00 |
|
yml2213
|
55f899c615
|
feat(automation): 拆开「多久跑一次」与「同一账号多久重检」—— 调度频率终于可配
用户反馈:「单账号重检间隔(小时) 很别扭,直接设置多久跑一次、一次跑多少个更符合直觉」。
**对,而且比"别扭"更严重:调度频率以前根本改不了。**
## 问题:两个概念挤在一个字段里
`_run_one` 里原本写着:
job.next_run_at = finished + timedelta(seconds=JOB_INTERVAL_SECONDS) # 硬编码 300 秒
也就是不管界面上填什么,下一轮永远是「跑完 + 5 分钟」。
而界面那个「单账号重检间隔(小时)」对应的是 `interval_seconds` ——
它只被作业**自己的选行逻辑**使用("这行多久该重检"),**完全不参与调度**。
于是出现最坏的情况:**看起来能配调度,其实配的是一个不相干的参数**。
上一轮我还拿它算过吞吐("10 分钟一轮"),算错了 —— 已在调度器注释里纠正。
## 拆法
| 概念 | 字段 | 界面 |
| --- | --- | --- |
| **多久跑一次** | `run_interval_seconds`(迁移 0051,默认 300)| 「多久跑一次(分钟)」|
| 同一账号多久重检 | `interval_seconds`(不变)| 「同一账号多久重检(小时)」|
- `next_run_at = finished + run_interval_seconds`(有下限 30 秒,避免忙轮询)
- 接口:`GET` 返回新字段、`PATCH` 可改
- 前端:两个输入框分开,并把原来那段绕口的 tooltip
("它不决定调度频率 —— 调度周期见页面顶部说明")改掉 ——
那句话本身就是这个设计缺陷的自白
## 默认 300 秒
与改动前**实际行为一致** —— 不会因为这次迁移让已有作业突然变快或变慢。
想调快就自己填(比如「账号资料定时补全」填 5 分钟 + 每轮 400 条 ≈ 3 小时补完)。
## ⚠️ 顺带说明一个你截图里可能看到的现象
截图里「账号资料定时补全」显示的是 **0.5 小时 / 30 条** —— 那是我**最初的默认值**。
因为 `ensure_jobs` **只创建不更新**,我后面改成 10 分钟/400 **不会作用到已存在的行**。
这是刻意的(否则会覆盖运维的手工调整),但意味着**默认值改了要手动在界面上改**,
或者删掉那行让 `ensure_jobs` 重建。现在有了「多久跑一次」这个框,直接填就行。
## 测试(+3)
- 下一轮按**运行间隔**排,不是按「同一行重检间隔」(两个值刻意设成不同来钉住)
- 不填时默认 300 秒(保持旧行为)
- 过小的间隔被夹到下限
## 验证
TZ=Asia/Shanghai **639 passed / 6 skipped**;ruff 全绿;前端 build 通过、lint 0 errors。
|
2026-10-04 21:30:13 +08:00 |
|
yml2213
|
85a6186993
|
feat(account): 电竞手册到期时间落库与展示;迁移 0048 改名
## 背景(用户反馈)
「精英宝典和电竞手册已开通的话,应该增加时间展示,可以看到开通时间和过期时间」
- 精英宝典:开通时间来自事件;过期时间 `elite_expire_time` 本来就有
- 电竞手册:开通时间来自事件;**过期时间原来没有落库** ——
只在 `_refresh_esports_handbook` 的返回值里带着,界面上只能看到「已开通」
## 改动
- 新增 `accounts.esports_expire_time`(迁移 0049,与 `elite_expire_time` 对称),
刷新电竞手册时写回
- 详情页:**已开通才显示**「开通 x / 到期 x」两行小字;顺手删掉底部那段
重复的「开通时间」说明(已挪到每项旁边)
- 新增 `formatUnixSeconds()`:斗鱼的到期时间是**秒级时间戳**,
原 `formatTime()` 只收字符串;新函数对 `0`/空/负数一律显示 `-`
## 明确回答:历史开通记录**查不到**
实测官方兑换记录里**只有商品兑换、没有开通记录**(账号 61:精英 2 条都是套装)。
我们自己的 `elite_opened` / `esports_opened` 事件也只有上线后的。
所以历史上的开通时间拿不回来 —— 按用户说的「没有就算了」,
并把这点写进界面说明,别再让人去找。
## 测的时候抓出一个真 bug(又是 1970)
电竞到期时间的 `"0"`(斗鱼用它表示「没有到期时间」)被原样存成 `0`
→ 界面会显示 **1970/1/1**。已修(`0`/空 → `None`)并加回归用例 ——
这个坑在积分到期上踩过一次,这次差点重演。
## 迁移 0048 改名
`20261004_0048_overview_summary.py` → `20261004_0048_high_value_owned.py`:
上一轮把内容从「汇总」收敛成单一用途的 `high_value_owned`,文件名没跟着改。
revision id 不变、迁移状态不受影响;同时清了 `__pycache__`
(本仓库被过期 `.pyc` 咬过,症状是「改了没生效」)。
## 验证(本地)
`TZ=Asia/Shanghai` **608 passed / 6 skipped**;ruff 全绿;前端 build 通过、lint 0 errors。
|
2026-10-04 20:17:05 +08:00 |
|
yml2213
|
62d9053e8a
|
feat(account): 官方兑换记录补成账号事件(三作用域)+ 一键查询结果落到账号
## 官方兑换记录 → 真正的账号事件
关键发现:斗鱼官方兑换记录**带真实兑换时间**(`time` 字段),所以可以直接
补成正常的历史事件,时间线因此变完整(这就是「账号全生命周期」该有的样子),
列表的兑换聚合也随之准确 —— 上线前的兑换不再是一片空白。
- **三个作用域都拉**:精英宝典 / 电竞手册(共用 `exchange_records`,只有 manual_id
不同)/ 和平小店(`xpd_embed_query` → `xpd_purchase_records`)
- 幂等键含作用域:`official_exchange:{scope}:{account}:{commodity}:{time}`
- `detail.scope` 标作用域;`source=official_sync` 与"当场记的"分得开
- 任一作用域失败只记进 `errors`,不影响其余(小店未开通会报
`Livelink登录态校验失败`,属正常)
## 一键查询结果落到账号(不再只留在任务里)
- 新增 `accounts.high_value_owned`(迁移 0048):**已兑换过的高价值套装名**,
供列表展示。单开一列而不是让列表读 `overview_snapshot` —— 那份 JSON 有 1~2KB,
15000 个账号各带一份会把响应放大到几十 MB。
- 迁移**可安全重跑**(启动时会自动跑):加列前查 `information_schema`。
⚠️ 回填里**不要** `op.get_bind().commit()` —— MySQL 的 DDL 自动提交而 commit
会打乱版本记账,表现为"列加上了、版本号没更新",下次启动重复 ADD COLUMN
直接把服务起不来(生产上就是启动即挂)。
- 迁移里从旧快照**回填** `high_value_owned`,已查过的账号不必重查。
## 两个 bug
1. **「最近一次兑换」按插入 id 排**(应为事件时间):补录的历史事件 id 最大、
时间却最老,会把几个月前的老兑换显示成"最近一次"。改为 `occurred_at DESC, id DESC`。
2. **手写的 `@model_serializer` 没列新字段** → 属性有值、JSON 里没有。
这是本项目第三次踩 Pydantic 静默丢字段;已补,并把断言改为看 `model_dump()`。
## 列表「兑换记录」口径
只聚合**官方**记录(`source=official_sync`):用户要求列表看官方的权威口径,
我们当场记的那些留给详情页时间线。另加 `GET /accounts/{id}/exchange-records`
供列表点击预览(按需拉取,不进列表响应)。
|
2026-10-04 20:03:42 +08:00 |
|
yml2213
|
aea05952af
|
refactor(account): 一键查询不该重复展示账号字段 —— 改成刷新它们,只留账号表里没有的
用户反馈:「一键查询也太重复了,直接更新账号信息不行吗?为啥很多都要再展示一次?
那建立账号信息表有啥用」——**说得对,是我设计错了**。
## 问题
面板把鱼翅 / 积分 / 钱包余额 / 精英宝典 / 电竞手册又原样列了一遍,
而这些账号表(`accounts`)本来就有对应列,账号详情也在显示同一批值 ——
同一屏出现两份,既冗余又让人分不清哪份是准的。
## 改法:查询 = 刷新,不是另开副本
| 内容 | 处理 |
| --- | --- |
| 鱼翅 / 积分 / 交易余额 / 开通状态 | 写回**账号表**(`_refresh_*` 本来就在写),由账号详情已有界面展示 —— **面板不再重复** |
| **积分到期时间** | 有 `accounts.points_expire_at` 列,但**查询漏了没写、界面也没显示** → 现在写回,并加到账号信息区展示 |
| **高价值套装是否已获得** | 账号表没有 → **只有这个该单独展示** |
| **官方兑换记录** | 账号表没有 → 单独展示 |
面板因此瘦身成只含「🎯 高价值套装」+「兑换记录」(+ 错误提示 + 查询时间),
标题也改成「高价值套装与兑换记录(一键查询)」,不再叫"账号信息"。
## 修了一个连带 bug
积分到期时间一直没被查询写回账号(只在快照里躺着),界面也没展示 ——
现在 `_parse_expire_at` 后写入 `accounts.points_expire_at`,
且斗鱼返回 `0` 时写 `None` 而不是 `0`(否则又显示 1970/1/1)。
## 测试(+3,共 41)
`TestOverviewRefreshesAccountFields`:资产/开通状态确实写回账号列、
**积分到期时间写回**、`willExpireTime="0"` 时清成 None 而非 0。
## 验证
`TZ=Asia/Shanghai` **593 passed / 6 skipped**;ruff 全绿;前端 build 通过、lint 0 errors。
|
2026-10-04 19:22:34 +08:00 |
|
yml2213
|
c2520613ca
|
fix(account): 修一键查询「一直循环」—— 快照字段加到了列表接口而不是详情接口
用户报:账号详情点「一键查询」会一直循环(Network 里刷一片 `overview` 请求)。
## 根因
上一轮我把 `overview_snapshot` / `overview_queried_at` 加到了**列表**接口
(锚点 `**bind_payload(acc),` 第一次出现的位置在列表构造器里),详情接口没有。
两层后果:
1. **详情不返回 `overview_queried_at`** → 前端 `marker !== before` 永远相等
→ 轮询到 120 秒超时。看起来就是"点一下一直转圈刷请求"。
2. **列表每个账号都带一整份 JSON 快照** → 15000 个账号的响应体积爆炸
(这个是我自己引入的回归,比第一个更隐蔽)。
## 修法
- 字段从**列表**移除、加到**详情**(列表刻意不返回,注释写明原因)
- 前端轮询**加自证**:第一次拿到响应时若没有标记字段,**立刻报错退出**,
而不是傻等 120 秒 —— 这类"字段没返回"的 bug 不该再以死循环的形式暴露
## ⚠️ 过程中我又踩了一个,被既有用例抓住
删那 4 行时**把 `**bind_payload(acc),` 一起删掉了**,导致列表不再返回绑定列
(`role_bind_status` 显示的是 Pydantic 默认值 `unknown`)。
`TestListViewKeyColumns::test_item_carries_key_columns` **立刻变红** ——
既有用例替我把这个真回归挡下来了。已恢复,并确认 diff 只剩预期的 3 行。
## 新增回归用例(+2,共 38)
`TestSnapshotExposedOnDetailOnly`:
- 详情**必须**返回快照与查询时间(不返回前端就会轮询到超时)
- 列表**不得**返回完整快照(每个账号一份 JSON,体积爆炸)
并做了**反向验证**:把"详情不返回字段"的 bug 注入回去 → 用例立刻红。
另外把 `TestListViewKeyColumns::_item` 按用户名查找改成按 id ——
`make_account("key")` 每次建新账号而库里一直累积,按用户名可能命中很早以前那个
(这次就让用例假红过一次)。
## 验证
`TZ=Asia/Shanghai` **590 passed / 6 skipped**;`TZ=UTC` 80 passed;
ruff 全绿;前端 build 通过、lint 0 errors。
|
2026-10-04 19:19:53 +08:00 |
|