fix(billing): 删任务/删项目级联吞 ACTIVE 预留致 reserved_balance 永久冻结(审计 I2)

- 根因: CreditReservation.task 是 OneToOneField(CASCADE), AITask.project 也是 CASCADE
  —— 删项目→级联删任务→ACTIVE 预留行被吞, 账户冻结份额永不归还(实测 4 账户漂移共 ¥42,
  且随删项目持续增长)
- 修法: billing/signals.py 挂 AITask pre_delete, 删除前先 release_credit 释放(级联同样
  触发, release 自带行锁+幂等); apps.py ready() 接线
- 回归测试 x3: 直删任务/删项目级联/已扣费不重复释放
- qa/repair_reserved_drift.py: 存量漂移回填脚本(dry-run 默认, --apply 落 RELEASE 流水),
  已对线上 4 账户执行并复验清零
- qa/function-audit/output: 本轮 18 页模拟点击审计报告(0 真死按钮 0 JS 报错)

验证: 后端测试 213/213 通过, django check 无警告
补齐 aab4d7e: 该提交带走了 signals.py 之外的测试轮改动, 信号挂载点与回归测试在本提交收口

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-07-02 13:07:09 +08:00
co-authored by Claude Fable 5
parent aab4d7ea4c
commit dcc5d7fe50
43 changed files with 4860 additions and 3143 deletions
+147 -74
View File
@@ -4,12 +4,12 @@
"url": "http://127.0.0.1:5173/messages",
"mode": "quick",
"tally": {
"total": 28,
"works": 20,
"total": 34,
"works": 23,
"dead": 0,
"error": 0,
"disabled": 0,
"skipped": 7,
"skipped": 10,
"noop": 1,
"blocked": 0
},
@@ -28,8 +28,8 @@
"url": true,
"overlay": false,
"self": false,
"mutations": 24,
"api": 0
"mutations": 31,
"api": 1
}
},
{
@@ -46,7 +46,7 @@
"url": false,
"overlay": false,
"self": false,
"mutations": 17,
"mutations": 19,
"api": 0
}
},
@@ -64,7 +64,7 @@
"url": false,
"overlay": true,
"self": false,
"mutations": 14,
"mutations": 17,
"api": 0
}
},
@@ -82,8 +82,8 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 24,
"api": 0
"mutations": 45,
"api": 1
}
},
{
@@ -100,7 +100,7 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 24,
"mutations": 33,
"api": 0
}
},
@@ -109,8 +109,8 @@
"tag": "a",
"role": "",
"cls": "",
"label": "视频项目",
"href": "/projects",
"label": "模特库",
"href": "/models",
"disabled": false,
"active": false,
"verdict": "works",
@@ -118,8 +118,8 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 19,
"api": 0
"mutations": 21,
"api": 1
}
},
{
@@ -127,8 +127,8 @@
"tag": "a",
"role": "",
"cls": "",
"label": "图片生成",
"href": "/asset-factory",
"label": "视频项目",
"href": "/projects",
"disabled": false,
"active": false,
"verdict": "works",
@@ -145,6 +145,24 @@
"tag": "a",
"role": "",
"cls": "",
"label": "图片生成",
"href": "/asset-factory",
"disabled": false,
"active": false,
"verdict": "works",
"signals": {
"url": true,
"overlay": false,
"self": true,
"mutations": 40,
"api": 4
}
},
{
"idx": 8,
"tag": "a",
"role": "",
"cls": "",
"label": "资产库",
"href": "/library",
"disabled": false,
@@ -154,12 +172,12 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 36,
"api": 0
"mutations": 37,
"api": 5
}
},
{
"idx": 8,
"idx": 9,
"tag": "a",
"role": "",
"cls": "",
@@ -172,12 +190,12 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 19,
"api": 0
"mutations": 36,
"api": 3
}
},
{
"idx": 9,
"idx": 10,
"tag": "a",
"role": "",
"cls": "",
@@ -190,12 +208,33 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 21,
"api": 1
"mutations": 104,
"api": 4
}
},
{
"idx": 10,
"idx": 11,
"tag": "a",
"role": "",
"cls": "",
"label": "垃圾桶",
"href": "/trash",
"disabled": false,
"active": false,
"verdict": "works",
"signals": {
"url": true,
"overlay": false,
"self": false,
"mutations": 23,
"api": 2
},
"consoleNote": [
"Failed to load resource: the server responded with a status of 404 (Not Found)"
]
},
{
"idx": 12,
"tag": "a",
"role": "",
"cls": "",
@@ -208,12 +247,30 @@
"url": true,
"overlay": false,
"self": true,
"mutations": 48,
"mutations": 53,
"api": 2
}
},
{
"idx": 11,
"idx": 13,
"tag": "div",
"role": "button",
"cls": "user",
"label": "账户",
"href": "",
"disabled": false,
"active": false,
"verdict": "works",
"signals": {
"url": false,
"overlay": false,
"self": true,
"mutations": 21,
"api": 0
}
},
{
"idx": 14,
"tag": "a",
"role": "",
"cls": "",
@@ -226,12 +283,12 @@
"url": true,
"overlay": false,
"self": false,
"mutations": 24,
"api": 0
"mutations": 45,
"api": 1
}
},
{
"idx": 12,
"idx": 15,
"tag": "button",
"role": "",
"cls": "icon-btn",
@@ -244,12 +301,12 @@
"url": false,
"overlay": false,
"self": false,
"mutations": 17,
"mutations": 20,
"api": 0
}
},
{
"idx": 13,
"idx": 16,
"tag": "button",
"role": "",
"cls": "btn",
@@ -263,11 +320,11 @@
"overlay": false,
"self": false,
"mutations": 0,
"api": 2
"api": 1
}
},
{
"idx": 14,
"idx": 17,
"tag": "button",
"role": "",
"cls": "btn",
@@ -280,16 +337,16 @@
"url": true,
"overlay": false,
"self": false,
"mutations": 58,
"api": 2
"mutations": 57,
"api": 3
}
},
{
"idx": 15,
"idx": 18,
"tag": "button",
"role": "",
"cls": "msg-filter.active",
"label": "全部111",
"label": "全部1191",
"href": "",
"disabled": false,
"active": true,
@@ -303,7 +360,7 @@
}
},
{
"idx": 16,
"idx": 19,
"tag": "button",
"role": "",
"cls": "msg-filter.",
@@ -316,16 +373,16 @@
"url": false,
"overlay": false,
"self": true,
"mutations": 28,
"mutations": 31,
"api": 1
}
},
{
"idx": 17,
"idx": 20,
"tag": "button",
"role": "",
"cls": "msg-filter.",
"label": "任务110",
"label": "任务560",
"href": "",
"disabled": false,
"active": false,
@@ -334,12 +391,12 @@
"url": false,
"overlay": false,
"self": true,
"mutations": 6,
"mutations": 8,
"api": 1
}
},
{
"idx": 18,
"idx": 21,
"tag": "button",
"role": "",
"cls": "msg-filter.",
@@ -352,16 +409,16 @@
"url": false,
"overlay": false,
"self": true,
"mutations": 6,
"mutations": 14,
"api": 1
}
},
{
"idx": 19,
"idx": 22,
"tag": "button",
"role": "",
"cls": "msg-filter.",
"label": "计费0",
"label": "计费630",
"href": "",
"disabled": false,
"active": false,
@@ -370,12 +427,12 @@
"url": false,
"overlay": false,
"self": true,
"mutations": 6,
"mutations": 10,
"api": 1
}
},
{
"idx": 20,
"idx": 23,
"tag": "button",
"role": "",
"cls": "msg-filter.",
@@ -388,28 +445,10 @@
"url": false,
"overlay": false,
"self": true,
"mutations": 6,
"mutations": 8,
"api": 1
}
},
{
"idx": 21,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 22,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 23,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 24,
"label": "",
@@ -433,17 +472,51 @@
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 28,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 29,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 30,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 31,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 32,
"label": "",
"tag": "",
"verdict": "stale"
},
{
"idx": 33,
"label": "",
"tag": "",
"verdict": "stale"
}
],
"missing": [
"搜索",
"E E2E 桥接测试团队 20260529",
"余额 ¥14,907.00",
"账户",
"资产「AI 生成 · image · 1」已加入资产库11mProduct Image · Im",
"资产「南卡 · 痛点种草 · v1-final.mp4」已加入资产库26mFinal Video",
"资产「南卡 · 痛点种草 · v1-final.mp4」已加入资产库1hFinal Video ",
"资产「南卡 · 痛点种草 · v1-BGM」已加入资产库1hUpload · Audio已完成系",
"余额 ¥14,072.00",
"商品图生成已扣费 ¥2.008mAI 任务扣费 · 扣费后余额 ¥14072.00更新系统",
"资产「AI 生成 · 商品三视图 · 1」已加入资产库8mProduct Image · Ima",
"商品图生成已扣费 ¥2.001dAI 任务扣费 · 扣费后余额 ¥14080.00更新系统",
"系统 →"
]
}