- 实现账户管理改进设计文档中的所有核心功能 - 三科目余额管理 (个人余额、劳动报酬、冻结余额) - 交易状态机 (created → pending → bank_submitted → success/failed/timeout → reversed) - 三键幂等体系 (JZTxId/BankTxId/SourceKey) - 优先级扣款规则 (先个人后劳动) - 在途资金管理 (可用→在途→结转/回退) - 三账对账闭环 (总账 = 银行账 + 在途净额) - 补偿服务域 (超时检测、重试、死信队列) - 虚拟银行模拟器用于业务测试 - 完整的集成测试套件 (133 个测试全部通过) - Docker 容器化部署配置 - 前端 Vue3 + TypeScript 项目结构
340 lines
15 KiB
Markdown
340 lines
15 KiB
Markdown
# 账户管理逻辑问题分析
|
||
|
||
## 一、业务逻辑汇总表
|
||
|
||
| 业务类型 | 操作步骤 | 账户变动 | 交易成功处理 | 交易失败处理 | 存在问题 |
|
||
|---------|---------|---------|------------|------------|---------|
|
||
| **订单** | 1. 实时计算劳动报酬和扣费金额<br>2. 预先扣减账户余额<br>3. 发起交易 | 预先扣减:个人余额↓ | 不做调整(余额已扣) | 恢复账户余额 | ⚠️ 先扣款后交易,存在数据不一致风险 |
|
||
| **代发** | 1. 发起交易 | 无预先变动 | 个人余额↑、劳动报酬↑、冻结余额↑ | 不做任何操作 | ⚠️ 失败不处理可能导致数据丢失 |
|
||
| **代扣** | 1. 发起交易 | 无预先变动 | 个人余额↓、劳动报酬可能↓ | 未说明 | ⚠️ 缺少失败处理逻辑 |
|
||
| **购药订单** | 1. 先发起冻结<br>2. 审批流程<br>3. 根据审批结果处理 | 冻结时:劳动报酬↓、个人余额↓、冻结余额↑ | 审批通过:恢复冻结金额→发起订单交易 | 审批不通过:恢复冻结金额 | ✅ 逻辑明确 |
|
||
| **罚金** | 1. 先发起冻结<br>2. 审批流程<br>3. 根据审批结果处理 | 冻结时:劳动报酬↓、个人余额↓、冻结余额↑ | 审批通过:恢复冻结金额→发起罚金交易 | 审批不通过:恢复冻结金额 | ✅ 逻辑完整 |
|
||
| **银行余额同步-增加** | 1. 直接增加个人余额 | 个人余额↑ = 银行余额↑ | 完成 | - | ✅ 逻辑简单清晰 |
|
||
| **银行余额同步-减少** | 1. 计算需减少的金额<br>2. 分配减少劳动报酬和冻结余额<br>3. 发起交易 | 预先计算:<br>劳动报酬↓ = 银行余额- - 个人余额- - 冻结余额-<br>冻结余额↓ = 银行余额- - 个人余额- - 劳动报酬- | 更新:个人余额↓、劳动报酬↓、冻结余额↓ | 恢复:个人余额、劳动报酬、冻结余额 | ⚠️ 计算公式可能存在逻辑问题 |
|
||
|
||
## 二、账户余额关系
|
||
|
||
**核心约束条件:**
|
||
```
|
||
个人余额 + 劳动报酬 + 冻结余额 = 银行余额
|
||
```
|
||
|
||
## 三、业务流程图
|
||
|
||
### 3.1 通用交易流程图
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[业务对象] --> B{需要预先扣款?}
|
||
B -->|是| C[计算扣款金额]
|
||
B -->|否| D[创建交易对象]
|
||
C --> E[扣减账户余额]
|
||
E --> D
|
||
D --> F[发起交易]
|
||
F --> G{交易结果}
|
||
G -->|成功| H{是否有预先扣款?}
|
||
G -->|失败| I{是否有预先扣款?}
|
||
H -->|是| J[余额不变化/增加]
|
||
H -->|否| K[更新账户余额]
|
||
I -->|是| L[恢复账户余额]
|
||
I -->|否| M[不做处理]
|
||
|
||
style A fill:#e1f5ff
|
||
style D fill:#fff4e1
|
||
style F fill:#ffe1f5
|
||
style G fill:#f0f0f0
|
||
```
|
||
|
||
### 3.2 订单业务流程图
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[订单请求] --> B[计算劳动报酬和扣费金额]
|
||
B --> C[预先扣减账户余额]
|
||
C --> D[发起订单交易]
|
||
D --> E{交易结果}
|
||
E -->|成功| F[余额不变化已扣减]
|
||
E -->|失败| G[恢复账户余额]
|
||
|
||
style C fill:#ffe1e1
|
||
style E fill:#f0f0f0
|
||
style G fill:#ffe1e1
|
||
```
|
||
|
||
### 3.3 购药订单业务流程图
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[购药订单请求] --> B[发起冻结]
|
||
B --> C[劳动报酬↓ 个人余额↓ 冻结余额↑]
|
||
C --> D[审批流程]
|
||
D --> E{审批结果}
|
||
E -->|通过| F[恢复冻结金额]
|
||
F --> G[发起订单交易]
|
||
G --> H{交易结果}
|
||
H -->|成功| I[更新账户]
|
||
H -->|失败| J[处理失败]
|
||
E -->|不通过| K[恢复冻结金额]
|
||
|
||
style K fill:#ff0000,color:#fff
|
||
style E fill:#f0f0f0
|
||
```
|
||
|
||
### 3.4 罚金业务流程图
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[罚金请求] --> B[发起冻结]
|
||
B --> C[劳动报酬↓ 个人余额↓ 冻结余额↑]
|
||
C --> D[审批流程]
|
||
D --> E{审批结果}
|
||
E -->|通过| F[恢复冻结金额]
|
||
F --> G[发起罚金交易]
|
||
G --> H{交易结果}
|
||
H -->|成功| I[更新账户]
|
||
H -->|失败| J[处理失败]
|
||
E -->|不通过| K[恢复冻结金额]
|
||
|
||
style E fill:#f0f0f0
|
||
```
|
||
|
||
### 3.5 银行余额同步-减少流程图
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[银行余额减少] --> B[直接减少个人余额]
|
||
B --> C[计算需减少的金额]
|
||
C --> D[劳动报酬减少 = 银行余额- - 个人余额- - 冻结余额-]
|
||
D --> E[冻结余额减少 = 银行余额- - 个人余额- - 劳动报酬-]
|
||
E --> F[发起交易]
|
||
F --> G{交易结果}
|
||
G -->|成功| H[更新: 个人余额↓ 劳动报酬↓ 冻结余额↓]
|
||
G -->|失败| I[恢复: 个人余额 劳动报酬 冻结余额]
|
||
|
||
style C fill:#ffe1e1
|
||
style D fill:#ffe1e1
|
||
style E fill:#ffe1e1
|
||
style G fill:#f0f0f0
|
||
```
|
||
|
||
## 四、系统交互时序图
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant BO as 业务对象
|
||
participant TO as 交易对象
|
||
participant AO as 账户对象
|
||
participant ExtSys as 外部交易系统
|
||
|
||
Note over BO: 业务开始
|
||
BO->>BO: 计算扣款金额(如需)
|
||
|
||
alt 需要预先扣款
|
||
BO->>AO: 预先扣减账户余额
|
||
AO-->>BO: 扣减成功
|
||
end
|
||
|
||
BO->>TO: 创建交易对象
|
||
TO-->>BO: 交易对象创建成功
|
||
|
||
BO->>TO: 发起交易
|
||
TO->>ExtSys: 调用外部交易接口
|
||
ExtSys-->>TO: 返回交易结果
|
||
|
||
alt 交易成功
|
||
TO->>AO: 进行动账操作
|
||
AO-->>TO: 动账完成
|
||
alt 有预先扣款
|
||
Note over BO,AO: 余额已在预先扣款时处理
|
||
else 无预先扣款
|
||
AO->>AO: 更新账户余额
|
||
end
|
||
else 交易失败
|
||
alt 有预先扣款
|
||
BO->>AO: 恢复账户余额
|
||
AO-->>BO: 恢复成功
|
||
else 无预先扣款
|
||
Note over BO,AO: 不做处理
|
||
end
|
||
end
|
||
|
||
TO-->>BO: 返回交易结果
|
||
BO->>BO: 业务处理完成
|
||
```
|
||
|
||
## 五、发现的问题分析
|
||
|
||
### 5.1 说明
|
||
|
||
1. **购药订单审批不通过的处理逻辑已澄清**
|
||
- **结论**:审批不通过时恢复冻结金额(与罚金一致),文档已更新
|
||
|
||
### 5.2 重要问题(⚠️ 建议修复)
|
||
|
||
2. **订单业务先扣款后交易的时序问题**
|
||
- **问题描述**:订单业务在交易发起前就扣减余额,如果交易长时间处理或系统异常,可能导致数据不一致
|
||
- **影响**:用户体验差(钱先扣了但订单可能失败)、数据一致性风险
|
||
- **建议**:考虑使用冻结机制替代直接扣款,或者优化事务处理
|
||
|
||
3. **代发和代扣业务缺少失败处理逻辑**
|
||
- **问题描述**:
|
||
- 代发失败不做任何操作,但可能已创建交易记录
|
||
- 代扣失败未说明如何处理
|
||
- **影响**:数据不一致、对账困难
|
||
- **建议**:明确失败场景下的回滚机制
|
||
|
||
4. **银行余额同步减少的计算逻辑可能存在错误**
|
||
- **问题描述**:计算公式中可能存在循环依赖或逻辑错误
|
||
```
|
||
劳动报酬- = 银行余额- - 个人余额- - 冻结余额-
|
||
冻结余额- = 银行余额- - 个人余额- - 劳动报酬-
|
||
```
|
||
- **分析**:第二个公式依赖第一个公式的结果,但两个公式的顺序执行可能导致结果不准确
|
||
- **建议**:重新设计计算公式,确保满足约束条件:`个人余额 + 劳动报酬 + 冻结余额 = 银行余额`
|
||
|
||
### 5.3 优化建议(💡 可优化)
|
||
|
||
5. **业务对象和账户对象的职责不清**
|
||
- **问题描述**:文档提到"动账也由业务对象进行处理",这可能导致业务逻辑和账户管理职责混乱
|
||
- **建议**:明确职责划分,账户对象的修改应该由账户对象自身管理,业务对象只负责业务逻辑协调
|
||
|
||
6. **缺少统一的异常处理机制**
|
||
- **问题描述**:各业务场景的异常处理方式不一致
|
||
- **建议**:制定统一的异常处理和回滚策略
|
||
|
||
7. **缺少事务管理说明**
|
||
- **问题描述**:多步骤操作(如银行余额同步减少)未说明是否使用事务
|
||
- **建议**:明确关键操作的事务边界,确保数据一致性
|
||
|
||
## 六、改进建议总结
|
||
|
||
1. ✅ **补全购药订单审批不通过的处理逻辑**(必须)
|
||
2. ✅ **优化订单业务的扣款时机**(建议使用冻结机制)
|
||
3. ✅ **统一代发和代扣的失败处理逻辑**
|
||
4. ✅ **修正银行余额同步的计算公式**
|
||
5. ✅ **明确业务对象和账户对象的职责边界**
|
||
6. ✅ **建立统一的异常处理和事务管理机制**
|
||
|
||
|
||
|
||
|
||
|
||
## 七、与《账户管理改进设计文档》的对齐评估
|
||
|
||
### 7.1 设计要点覆盖情况
|
||
- **三类账户与对照余额(个人余额/劳动报酬/冻结余额 与 银行余额)**:部分覆盖。
|
||
- 文档已声明约束关系,但缺少每次动账后的不变量校验与核对策略。
|
||
- **狱政交易对象(内部交易域)**:部分覆盖。
|
||
- 已有“交易对象”与“外部交易系统”,但未明确“狱政交易对象”的职责边界与与“在途余额”的关系。
|
||
- **扣款优先级(先个人余额,后劳动报酬,不足则失败)**:基本覆盖。
|
||
- 存量流程对订单类有预扣,但建议统一通过冻结实现以避免时序不一致。
|
||
- **在途余额机制**:未覆盖/不清晰。
|
||
- 当前仅有“预先扣减”与“发起交易”,缺乏在途余额账面隔离、失败/超时/退回的对账闭环。
|
||
- **交易结果处理(成功/失败/超时)**:部分覆盖。
|
||
- 成功/失败均有描述,超时场景仅有笼统说明,缺少重试、补偿与人工干预流程。
|
||
- **银行退回(对方银行冲退)**:未覆盖。
|
||
- 需要明确:原路退回与业务确认后冲正的分支,以及自动化/人工处理的触发条件。
|
||
- **外部入账作为来源(无我方唯一流水)**:未覆盖。
|
||
- 需定义来源幂等键(如银行对账单流水+金额+时间窗)与异常对账策略。
|
||
- **冻结/解冻不发起实际银行交易**:基本覆盖。
|
||
- 购药、罚金场景已有冻结/解冻,但需抽象成通用能力并统一失败回滚策略。
|
||
- **余额充足性预检查**:部分覆盖。
|
||
- 有检查思想,但未统一为账户域的原子校验与幂等接口。
|
||
|
||
### 7.2 结论
|
||
现有《账户管理逻辑问题分析》对“改进设计”的多数目标给出了雏形,但以下关键项仍需补齐:在途余额域模型、狱政交易对象职责、超时/退回/外部入账的闭环与幂等、统一失败回滚与事务边界、不变量校验与对账机制、银行余额同步减少的正确计算方法。
|
||
|
||
## 八、未解决问题与修复建议(落地项)
|
||
|
||
1. **在途余额与狱政交易对象的职责边界**(必须)
|
||
- 建议:在账户域引入`在途余额账本`,狱政交易创建即将金额从可用余额划转至在途;银行成功后从在途转出(或转回)。失败/取消则在途回退至可用。
|
||
|
||
2. **超时处理与补偿策略**(必须)
|
||
- 建议:定义状态机(created→pending→bank_submitted→success/failed/timeout→reversed)。超时进入`timeout`并触发重试/人工审核队列;所有操作需幂等键保护(交易号/对账单流水)。
|
||
|
||
3. **银行退回(冲退)闭环**(必须)
|
||
- 建议:新增“银行退回通知/对账识别”入口,定位原交易,若已记账成功则生成逆向狱政交易进行冲正;若仍在途则直接回退在途并关闭原交易。
|
||
|
||
4. **外部入账来源与幂等**(必须)
|
||
- 建议:允许“无我方唯一流水”的入账以“来源幂等键”(银行流水号+金额+记账日+对方户名等)落账;发现重复则幂等返回;与银行日终对账校验差异。
|
||
|
||
5. **统一失败处理与回滚**(必须)
|
||
- 建议:冻结/解冻/出入账/在途划转全部纳入事务;失败分两类:银行未受理(本地回滚),银行已受理(进入在途等待对账/退回)。
|
||
|
||
6. **银行余额同步减少公式修正**(必须)
|
||
- 问题:现有“劳动报酬- = 银行余额- - 个人余额- - 冻结余额-;冻结余额- = 银行余额- - 个人余额- - 劳动报酬-”存在循环依赖。
|
||
- 建议:以“银行差额Δ = 银行余额变化-(负值为减少)”为驱动,按既定优先级从可用部分(个人余额→劳动报酬)与冻结余额分桶扣减,确保每一步后不变量成立;严格禁止相互引用的代数解,改为确定性分配流程。
|
||
|
||
7. **账户不变量校验与对账**(必须)
|
||
- 建议:每次动账后校验`个人余额 + 劳动报酬 + 冻结余额 = 银行余额(账面)`;引入日终对账(银行账、在途账、总账三方核对)。
|
||
|
||
8. **幂等与唯一键设计**(必须)
|
||
- 建议:狱政交易号`JZTxId`、银行交易号`BankTxId`、来源幂等键`SourceKey`三键体系;所有写操作均需提供幂等键。
|
||
|
||
## 九、建议的状态机(文字版)
|
||
- 交易状态:`created → pending → bank_submitted → success | failed | timeout → reversed(可选)`
|
||
- 关键转移:
|
||
- created→pending:狱政交易创建,资金从可用划至在途
|
||
- pending→bank_submitted:调用银行接口
|
||
- bank_submitted→success:银行成功,记账从在途转出
|
||
- bank_submitted→failed:银行失败,在途回退
|
||
- bank_submitted→timeout:未回执/长时卡顿,进入补偿
|
||
- success→reversed:银行退回/业务冲正,生成逆向交易
|
||
|
||
## 十、执行优先级建议
|
||
1) 在途余额与状态机落地;2) 统一失败/超时/退回流程;3) 外部入账幂等与对账;4) 银行同步减少分配流程改造;5) 不变量与事务/幂等全链路加固。
|
||
|
||
|
||
## 十一、对账与异常处理落地方案
|
||
|
||
### 11.1 三账对齐与差异闭环
|
||
- 三账:银行账、在途账、总账。
|
||
- 目标:`总账 = 银行账 + 在途净额`;所有差异可解释并可关闭。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[日终获取银行对账单] --> B[计算银行账汇总]
|
||
B --> C[计算总账汇总]
|
||
C --> D[计算在途净额]
|
||
D --> E{等式是否成立?}
|
||
E -- 是 --> F[对账通过]
|
||
E -- 否 --> G[差异分类]
|
||
G --> H[自动处理队列]
|
||
G --> I[人工复核队列]
|
||
H --> J[自动冲正/回退/补记]
|
||
I --> K[人工结论执行]
|
||
```
|
||
|
||
### 11.2 差异分类与处理策略
|
||
- **短款**(银行少/本地多):多因银行未落账或我们过早确认。
|
||
- 处理:等待回执/重试;超时则回退在途或生成纠错交易。
|
||
- **长款**(银行多/本地少):多因外部入账未识别或重复入账。
|
||
- 处理:按来源幂等键补记来源型交易;重复则逆向冲正。
|
||
- **在途超时**:长时间无回执。
|
||
- 处理:转`timeout`补偿;达到阈值转人工。
|
||
|
||
### 11.3 异常处理分类
|
||
- 可重试:网络/5xx/通道抖动 → 指数退避+幂等。
|
||
- 不可重试:4xx/余额不足/幂等冲突 → 直接失败或回退在途。
|
||
- 半确定:银行已受理未知 → 在途等待对账确认。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[交易发起] --> B{返回}
|
||
B -- 成功 --> C[在途结转为成功]
|
||
B -- 4xx失败 --> D[回退在途/失败]
|
||
B -- 5xx失败 --> E[重试+幂等]
|
||
B -- 超时 --> F[标记timeout 进补偿]
|
||
F --> G[对账识别后转成功/失败]
|
||
```
|
||
|
||
### 11.4 退回/冲正闭环
|
||
```mermaid
|
||
flowchart TD
|
||
A[银行退回通知/对账发现] --> B{原交易状态}
|
||
B -- success --> C[生成逆向交易reversed]
|
||
C --> D[余额回补/恢复冻结]
|
||
B -- 在途 --> E[直接在途回退]
|
||
E --> F[关闭原交易]
|
||
```
|