对方在聊天里回了一句“转过去了”,消息显示已送达。你去看余额,没变。
做过 IM 内转账的工程师,多半处理过这种工单:聊天里是一个状态,账本里是另一个。这种错位可以叫它“幽灵状态”。
核心结论:IM 转账中,消息的 DELIVERED 只代表内容送达设备,转账是否完成以账本的 SETTLED 为准。两者应建成两套独立状态机,通过 transfer_id 关联,由账本事件驱动聊天卡片更新。
MPChat 的转账入口在会话里,资金动作由 MPChat Pay 处理。把消息的 DELIVERED 渲染成“对方已收款”,就是拿通信层的信号回答资金层的问题。
说明:下文为通用架构模型,用于技术讨论,不代表 MPChat 现网接口、字段或内部实现。
消息状态和转账状态有什么区别?
消息状态: CREATED → SENT → DELIVERED → READ 转账状态: CREATED → AUTH_PENDING → PROCESSING → SETTLED └──────→ FAILED| 消息状态机 | 转账状态机 | |
| 回答的问题 | 内容送到了没有 | 钱记到账上了没有 |
| 终态 | READ | SETTLED / FAILED |
| 依据 | 消息服务 | 账本 |
对方在聊天里打出“我已转账”,也只是一条消息。
数据模型怎么拆?
| 表 | 关键字段 | 作用 |
| chat_message | message_id, conversation_id, msg_status, transfer_id(可空) | 记录消息投递 |
| transfer | transfer_id, sender_account, recipient_account, amount, status | 记录转账进度 |
| ledger_entry | entry_id, transfer_id, account_id, direction, amount | 记录借贷分录 |
message_id 可以关联 transfer_id,但不能代替它。聊天窗口负责展示,账本是资金状态的 Source of Truth。
为什么不能先推“成功”卡片再记账?
先推卡片、再异步写账本,账本一旦写入失败或回滚,收款方看到了成功提示,交易记录里却没有这笔钱。
约束很简单:转账满足账本确认条件后,才生成“已完成”事件;聊天卡片只消费这个事件。
结算和通知怎么分离?
function settleTransfer(transferId): begin transaction transfer = lockTransfer(transferId) if transfer.status == SETTLED: return assert transfer.status == PROCESSING writeDebitEntry(transferId, transfer.senderAccount, transfer.amount) writeCreditEntry(transferId, transfer.recipientAccount, transfer.amount) updateTransferStatus(transferId, SETTLED) writeOutboxEvent(eventKey = "transfer.settled:" + transferId, transferId) commit function onTransferSettled(event): if alreadyConsumed(event.eventKey): return updateChatPaymentCard(event.transferId, displayStatus = "completed") markConsumed(event.eventKey)这里有两处幂等:
账本侧:锁单后检查状态,结算重试不会重复记账。
聊天侧:按 eventKey 去重,事件重投不会生成两张“已完成”卡片。
记账和写 Outbox 事件在同一事务里提交。通知失败只重试通知,不重做资金转移。
“对方说转了,我没收到”怎么排查?
按 transfer_id 查三层:
指令层:转账是否创建,收款账户和金额是否正确。
账本层:是否到达最终状态,借贷记录是否完整。
展示层:收款侧能否查到记录,聊天卡片和交易详情是否一致。
| 现象 | 问题出在哪 | 先查什么 |
| 账已记完,聊天仍显示“处理中” | 展示投影没跟上 | 修展示投影 |
| 聊天显示“已完成”,账本缺记录 | 状态发布早于记账 | 发布顺序、事务边界、补偿任务 |
状态不同步的单子,别靠重新发起一笔转账来补。再发一笔,账上多了一次真实转移,原来那条记录还在。
FAQ
消息显示 DELIVERED,能说明对方收到钱了吗?不能。DELIVERED 只说明消息到达接收端设备,资金状态要看转账记录是否为 SETTLED。
聊天里的转账卡片该由谁来更新?由结算完成后发出的事件驱动,聊天卡片只负责展示。
通知发送失败怎么办?重试通知即可。已结算的转账不需要重新转一次。
转账入口离对话越近,越需要在系统里把消息证据和资金证据分开。用户在意的是这笔交易查得到、对得上,聊天里看到的和账单写的是同一个结论。
你们团队的支付卡片,是订阅账本事件,还是由业务服务直接推送?评论区聊聊。