互换重收盘误删手动互换资金记录: 分析文档+TDD红灯测试

问题:重新收盘后用户手动互换产生的资金记录(ClientCashInCashOut,
Action=系统操作-互换)被误删。根因 ClearSwapPositions:400 按 Action
字符串模糊删除,而该 Action 手动/自动共用,表无来源字段区分。
引入点 commit 44e89726(2026-05-14)。

- 新增决策文档(根因/引入时间/三种修复方案/自测方法)
- 新增 SwapReEodDeleteManualCashRecordTest 4个测试(探查/诊断/录制红灯/离线校验)
- 录制 golden 坐实 bug: trade 1903 手动互换资金记录 id=13032 会被误删
This commit is contained in:
hjhan
2026-07-01 08:35:58 +08:00
parent b4ff5f9f10
commit 6f9aa5fb45
3 changed files with 1229 additions and 0 deletions
@@ -0,0 +1,313 @@
{
"SwapTradeId": 1903,
"SwapTradeNo": "GLMS-20260624-0005",
"RecordedAt": "2026-07-01T08:33:12",
"SourceDb": "test",
"Purpose": "重收盘误删手动互换资金记录 - TDD红灯证据",
"InputEvents": [
{
"ValueDate": "2026-06-24T00:00:00",
"SwapTradeId": 1903,
"EventType": 9,
"EventTypeName": "确认交易",
"EventReason": "确认交易",
"EventData": "",
"Invalid": false,
"ClientCashId": 0,
"BackId": 0,
"extenstionData": null,
"SwapTradeNumber": null,
"unwindData": null,
"id": 20625,
"EncryptId": "l4jIqgrZepVZBJ8gLfbu_w",
"OptId": 1,
"OptName": "初始用户",
"OptTime": "2026-06-30T17:38:40"
},
{
"ValueDate": "2026-06-24T00:00:00",
"SwapTradeId": 1903,
"EventType": 5,
"EventTypeName": "回退",
"EventReason": "交易回退至2026年06月24日",
"EventData": "",
"Invalid": false,
"ClientCashId": 0,
"BackId": 0,
"extenstionData": null,
"SwapTradeNumber": null,
"unwindData": null,
"id": 20631,
"EncryptId": "lNwpsaP9qzZyFuecPppR4Q",
"OptId": 1,
"OptName": "初始用户",
"OptTime": "2026-06-30T17:58:12"
},
{
"ValueDate": "2026-06-29T00:00:00",
"SwapTradeId": 1903,
"EventType": 4,
"EventTypeName": "自动互换",
"EventReason": "系统操作-自动互换",
"EventData": "{\"SwapTradeId\":1903,\"CloseType\":0,\"CloseQty\":0.0,\"ClosePercent\":0.0,\"CloseNotionalValue\":0.0,\"StartDate\":\"2026-06-25T00:00:00\",\"ValueDate\":\"2026-06-29T00:00:00\",\"UnwindDate\":null,\"NotionalValue\":19990.0,\"NotionalQty\":0.0,\"PosiNotionalValue\":19990.000000,\"PositionQty\":0.0,\"AnnualDays\":0,\"CloseMethod\":0,\"SwapRealizedPnL\":1.86160315679,\"SwapMarginRebatePnl\":-1.36986460274,\"SwapMarginAmount\":0.0,\"SwapCloseAmount\":1.86160315679,\"SwapDividendPnl\":0.0,\"FlowEvents\":[],\"StructureType\":null,\"PayDate\":\"2026-06-29T00:00:00\",\"TradeStartDate\":null}",
"Invalid": false,
"ClientCashId": 13031,
"BackId": 0,
"extenstionData": null,
"SwapTradeNumber": null,
"unwindData": null,
"id": 20659,
"EncryptId": "xgIja4GsGIq0qiL3Q-IdbQ",
"OptId": 1,
"OptName": "初始用户",
"OptTime": "2026-06-30T18:08:51"
},
{
"ValueDate": "2026-06-30T00:00:00",
"SwapTradeId": 1903,
"EventType": 3,
"EventTypeName": "互换",
"EventReason": "系统操作-互换",
"EventData": "{\"SwapTradeId\":1903,\"CloseType\":0,\"CloseQty\":0.0,\"ClosePercent\":1.0,\"CloseNotionalValue\":19990.0,\"StartDate\":\"2026-06-24T00:00:00\",\"ValueDate\":\"2026-06-30T00:00:00\",\"UnwindDate\":\"2026-06-30T00:00:00\",\"NotionalValue\":19990.00,\"NotionalQty\":1000.00,\"PosiNotionalValue\":19990.00,\"PositionQty\":1000.0,\"AnnualDays\":365,\"CloseMethod\":0,\"SwapRealizedPnL\":3.23,\"SwapMarginRebatePnl\":0.00,\"SwapMarginAmount\":0.0,\"SwapCloseAmount\":3.23,\"SwapDividendPnl\":0.0,\"FlowEvents\":[],\"StructureType\":\"普通债券类收益互换\",\"PayDate\":\"2026-06-30T00:00:00\",\"TradeStartDate\":\"2026-06-25T00:00:00\"}",
"Invalid": false,
"ClientCashId": 12996,
"BackId": 0,
"extenstionData": null,
"SwapTradeNumber": null,
"unwindData": null,
"id": 20632,
"EncryptId": "GLfe6-0kN1kS1j8fwI35Jw",
"OptId": 1,
"OptName": "初始用户",
"OptTime": "2026-06-30T18:00:12"
},
{
"ValueDate": "2026-06-30T00:00:00",
"SwapTradeId": 1903,
"EventType": 3,
"EventTypeName": "互换",
"EventReason": "系统操作-互换",
"EventData": "{\"SwapTradeId\":1903,\"CloseType\":0,\"CloseQty\":0.0,\"ClosePercent\":1.0,\"CloseNotionalValue\":19990.0,\"StartDate\":\"2026-06-30T00:00:00\",\"ValueDate\":\"2026-06-30T00:00:00\",\"UnwindDate\":\"2026-06-30T00:00:00\",\"NotionalValue\":19990.00,\"NotionalQty\":1000.00,\"PosiNotionalValue\":19990.00,\"PositionQty\":1000.0,\"AnnualDays\":365,\"CloseMethod\":0,\"SwapRealizedPnL\":5.00,\"SwapMarginRebatePnl\":0.00,\"SwapMarginAmount\":0.0,\"SwapCloseAmount\":5.00,\"SwapDividendPnl\":0.0,\"FlowEvents\":[],\"StructureType\":\"普通债券类收益互换\",\"PayDate\":\"2026-06-30T00:00:00\",\"TradeStartDate\":\"2026-06-25T00:00:00\"}",
"Invalid": false,
"ClientCashId": 13029,
"BackId": 0,
"extenstionData": null,
"SwapTradeNumber": null,
"unwindData": null,
"id": 20658,
"EncryptId": "CGvRD9lBlpTNg44rkLnzvA",
"OptId": 1,
"OptName": "初始用户",
"OptTime": "2026-06-30T18:08:07"
},
{
"ValueDate": "2026-06-30T00:00:00",
"SwapTradeId": 1903,
"EventType": 3,
"EventTypeName": "互换",
"EventReason": "系统操作-互换",
"EventData": "{\"SwapTradeId\":1903,\"CloseType\":0,\"CloseQty\":0.0,\"ClosePercent\":1.0,\"CloseNotionalValue\":19990.0,\"StartDate\":\"2026-06-30T00:00:00\",\"ValueDate\":\"2026-06-30T00:00:00\",\"UnwindDate\":\"2026-06-30T00:00:00\",\"NotionalValue\":19990.00,\"NotionalQty\":1000.00,\"PosiNotionalValue\":19990.00,\"PositionQty\":1000.0,\"AnnualDays\":365,\"CloseMethod\":0,\"SwapRealizedPnL\":5.00,\"SwapMarginRebatePnl\":0.00,\"SwapMarginAmount\":0.0,\"SwapCloseAmount\":5.00,\"SwapDividendPnl\":0.0,\"ClientCashIds\":[],\"FlowEvents\":[],\"StructureType\":\"普通债券类收益互换\",\"PayDate\":\"2026-06-30T00:00:00\",\"TradeStartDate\":\"2026-06-25T00:00:00\"}",
"Invalid": false,
"ClientCashId": 13032,
"BackId": 0,
"extenstionData": null,
"SwapTradeNumber": null,
"unwindData": null,
"id": 20660,
"EncryptId": "Q5Dzi9hzcXG6b8gXbawLHw",
"OptId": 1,
"OptName": "初始用户",
"OptTime": "2026-06-30T18:10:29"
}
],
"InputCashRecords": [
{
"DirectionType": "互换支出",
"TradeDirectionType": "互换收入",
"CanGenerateExit": false,
"Explain": null,
"ValidState": "Valid",
"TradeId": 1903,
"Action": "系统操作-互换",
"Direction": "应收",
"Number": "20260630180850883",
"ClientId": 38,
"ClientNumber": "26033ZMRCS",
"ClientName": "张名锐测试",
"Money": -3.23,
"HappenDate": "2026-06-29T00:00:00",
"State": "已结算",
"OpenBank": null,
"OpenBankId": null,
"OpenBankComments": null,
"OpenBankCard": "",
"MoneyStr": "-3.23",
"SerialNumber": null,
"BankFlowId": null,
"AccountBalance": null,
"Uses": null,
"SettleDate": "2026-06-29T00:00:00",
"Comments": null,
"TradeNumber": "GLMS-20260624-0005",
"TradeType": null,
"CashFlag": 0,
"TradeCashId": 0,
"IsGroup": 0,
"CurrencyCode": "",
"ParentTradeNumber": null,
"Deal": 0,
"ApprovalProcess": 0,
"CreatorId": 1,
"CreatorName": "初始用户",
"CreateDate": "2026-06-30T18:08:51",
"ApprovalDate": null,
"cash_type": null,
"TransferOutAccount": null,
"TransferInAccount": null,
"OptId": 1,
"OptName": "初始用户",
"OptDate": "2026-06-30T18:08:51",
"id": 13030,
"EncryptId": "PmbbhBl-gOhCtjzjFAL3MQ"
},
{
"DirectionType": "预付金返息收入",
"TradeDirectionType": "预付金返息支出",
"CanGenerateExit": false,
"Explain": null,
"ValidState": "Valid",
"TradeId": 1903,
"Action": "系统操作-预付金返息",
"Direction": "应收",
"Number": "20260630180850888",
"ClientId": 38,
"ClientNumber": "26033ZMRCS",
"ClientName": "张名锐测试",
"Money": 1.37,
"HappenDate": "2026-06-29T00:00:00",
"State": "已结算",
"OpenBank": null,
"OpenBankId": null,
"OpenBankComments": null,
"OpenBankCard": "",
"MoneyStr": "1.37",
"SerialNumber": null,
"BankFlowId": null,
"AccountBalance": null,
"Uses": null,
"SettleDate": "2026-06-29T00:00:00",
"Comments": null,
"TradeNumber": "GLMS-20260624-0005",
"TradeType": null,
"CashFlag": 0,
"TradeCashId": 0,
"IsGroup": 0,
"CurrencyCode": "",
"ParentTradeNumber": null,
"Deal": 0,
"ApprovalProcess": 0,
"CreatorId": 1,
"CreatorName": "初始用户",
"CreateDate": "2026-06-30T18:08:51",
"ApprovalDate": null,
"cash_type": null,
"TransferOutAccount": null,
"TransferInAccount": null,
"OptId": 1,
"OptName": "初始用户",
"OptDate": "2026-06-30T18:08:51",
"id": 13031,
"EncryptId": "cR8xg27rKifLQECWC61RFg"
},
{
"DirectionType": "互换支出",
"TradeDirectionType": "互换收入",
"CanGenerateExit": false,
"Explain": null,
"ValidState": "Valid",
"TradeId": 1903,
"Action": "系统操作-互换",
"Direction": "应收",
"Number": "20260630181033576",
"ClientId": 38,
"ClientNumber": "26033ZMRCS",
"ClientName": "张名锐测试",
"Money": -5.0,
"HappenDate": "2026-06-30T00:00:00",
"State": "已结算",
"OpenBank": null,
"OpenBankId": null,
"OpenBankComments": null,
"OpenBankCard": "",
"MoneyStr": "-5.00",
"SerialNumber": null,
"BankFlowId": null,
"AccountBalance": null,
"Uses": null,
"SettleDate": "2026-06-30T00:00:00",
"Comments": null,
"TradeNumber": "GLMS-20260624-0005",
"TradeType": null,
"CashFlag": 0,
"TradeCashId": 0,
"IsGroup": 0,
"CurrencyCode": "",
"ParentTradeNumber": null,
"Deal": 0,
"ApprovalProcess": 0,
"CreatorId": 1,
"CreatorName": "初始用户",
"CreateDate": "2026-06-30T18:10:34",
"ApprovalDate": null,
"cash_type": null,
"TransferOutAccount": null,
"TransferInAccount": null,
"OptId": 1,
"OptName": "初始用户",
"OptDate": "2026-06-30T18:10:34",
"id": 13032,
"EncryptId": "NSMb8Sa0Q0Xx-5oOCr_UAw"
}
],
"Diagnosis": {
"SwapTradeId": 1903,
"自动互换事件数": 1,
"删除起点ValueDate": "2026-06-29T00:00:00",
"删除命中总数": 3,
"手动互换误删记录数": 1,
"自动互换命中记录数": 1,
"孤儿命中记录数": 1,
"手动互换误删CashIds": [
13032
],
"命中明细": [
{
"CashId": 13030,
"Action": "系统操作-互换",
"Money": -3.23,
"HappenDate": "2026-06-29T00:00:00",
"来源": "未知(孤儿)",
"会被误删": false
},
{
"CashId": 13031,
"Action": "系统操作-预付金返息",
"Money": 1.37,
"HappenDate": "2026-06-29T00:00:00",
"来源": "自动互换",
"会被误删": false
},
{
"CashId": 13032,
"Action": "系统操作-互换",
"Money": -5.0,
"HappenDate": "2026-06-30T00:00:00",
"来源": "手动互换",
"会被误删": true
}
],
"BUG成立": true,
"结论": "坐实BUG:误删 1 条手动互换资金记录",
"Summary": "--- 重收盘误删诊断 SwapTradeId=1903 ---\n\n[swap_event] 手动互换 vs 自动互换:\n event id=20659 EventType=自动互换 ValueDate=2026-06-29 ClientCashId=13031\n event id=20632 EventType=互换 ValueDate=2026-06-30 ClientCashId=12996\n event id=20658 EventType=互换 ValueDate=2026-06-30 ClientCashId=13029\n event id=20660 EventType=互换 ValueDate=2026-06-30 ClientCashId=13032\n\n[删除条件模拟] valueDate=2026-06-29 Action IN (系统操作-互换, 系统操作-预付金返息)\n id Action MoneyHappenDate 来源 误删? \n 13030 系统操作-互换 -3.232026-06-29 未知(孤儿) \n 13031 系统操作-预付金返息 1.372026-06-29 自动互换 \n 13032 系统操作-互换 -5.002026-06-30 手动互换 ✓BUG \n\n[结论]\n 删除命中 3 条 = 手动互换 1 + 自动互换 1 + 孤儿 1\n ⚠⚠⚠ 坐实 BUG:重收盘会误删 1 条手动互换资金记录 (ids=[13032])\n 根因:ClearSwapPositions:400 按 Action=系统操作-互换 删除,该 Action 手动/自动共用,无来源字段区分。"
}
}