mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-08-14 02:03:54 +08:00
新增文档: - feature-tracker-无交易记录自动结算-实施总结.md - 完整实施总结,包含部署指南、监控方案、风险应对 - feature-tracker-无交易记录自动结算-附录.md - 功能设计附录 文档内容: - 开发进度总结(8个任务全部完成) - 核心实现详解(配置、数据转换、业务逻辑、定时任务、幂等保护、数据校验) - 文件清单(3个新增文件、7个修改文件) - 部署指南(代码合并、配置更新、定时任务添加、4阶段灰度发布流程) - 监控方案(日志监控、数据库监控、Redis监控) - 风险点与应对(6类风险及解决方案) - 后续优化建议(5个方向) - 验收标准(功能、性能、稳定性)
274 lines
9.8 KiB
Markdown
274 lines
9.8 KiB
Markdown
# 附录:无交易记录自动结算功能
|
||
|
||
本文档为 `feature-tracker-无交易记录自动结算.md` 的附录部分
|
||
|
||
---
|
||
|
||
## 八、附录
|
||
|
||
### 8.1 关键代码位置索引
|
||
|
||
| 代码位置 | 说明 | 行号 |
|
||
|---------|------|------|
|
||
| `TransactionRecordsRequestHandler.processOrder()` | 交易记录触发结算入口 | jsowell-netty/.../TransactionRecordsRequestHandler.java:683 |
|
||
| `OrderService.manualSettlementOrder()` | 人工结算逻辑(可复用实时数据构造) | jsowell-admin/.../OrderService.java:1008-1027 |
|
||
| `YKCBusinessServiceImpl` | 桩离线标记异常订单 | jsowell-netty/.../YKCBusinessServiceImpl.java:166 |
|
||
| `ChargeEndHandler` | 0x19充电结束帧处理 | jsowell-netty/.../ChargeEndHandler.java |
|
||
| `RealTimeMonitorData` | 实时检测数据实体类 | jsowell-common/.../domain/RealTimeMonitorData.java |
|
||
| `order_monitor_data` 表 | 实时数据落库 | MySQL |
|
||
| Redis `PILE_REAL_TIME_MONITOR_DATA` | 实时数据缓存 | Redis |
|
||
| `settle_order_` 锁前缀 | 结算分布式锁 | Redis |
|
||
|
||
### 8.2 数据流程图
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Timer as 定时任务
|
||
participant DB as 数据库
|
||
participant Redis as Redis
|
||
participant OrderLogic as 结算逻辑
|
||
participant Alert as 告警系统
|
||
|
||
Timer->>Timer: 检查配置开关
|
||
alt 开关关闭
|
||
Timer->>Timer: 退出
|
||
end
|
||
|
||
Timer->>DB: 查询待结算订单<br/>(状态=待结算 + 桩在线 + 停止>10min)
|
||
DB-->>Timer: 订单列表(限batch-size条)
|
||
|
||
loop 遍历每个订单
|
||
Timer->>Redis: 尝试获取锁<br/>settle_order_{orderId}
|
||
alt 获取锁失败
|
||
Timer->>Timer: 跳过该订单
|
||
end
|
||
|
||
Timer->>DB: 查询最后一条实时数据
|
||
alt 数据异常检查
|
||
Timer->>Timer: 检查 chargingDegree > 0
|
||
Timer->>Timer: 检查 chargingAmount > 0
|
||
Timer->>Timer: 检查金额阈值
|
||
alt 任一检查不通过
|
||
Timer->>Alert: 发送告警
|
||
Timer->>Timer: 跳过该订单
|
||
end
|
||
end
|
||
|
||
Timer->>Timer: 构造 TransactionRecordsData
|
||
Timer->>OrderLogic: settleOrder(data, orderInfo)
|
||
OrderLogic-->>Timer: 结算结果
|
||
|
||
alt 结算成功
|
||
Timer->>Timer: 记录成功日志
|
||
else 结算失败
|
||
Timer->>Alert: 发送告警
|
||
Timer->>Timer: 记录失败日志
|
||
end
|
||
|
||
Timer->>Redis: 释放锁
|
||
end
|
||
```
|
||
|
||
### 8.3 状态流转图
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> 充电中: 开始充电
|
||
充电中 --> 待结算: 收到0x19结束帧<br/>或桩停止上报
|
||
|
||
待结算 --> 已结算: 收到交易记录帧<br/>(现有逻辑)
|
||
待结算 --> 已结算: 定时任务自动结算<br/>(新增逻辑)
|
||
待结算 --> 已结算: 人工结算
|
||
|
||
待结算 --> 异常订单: 桩离线<br/>(现有逻辑)
|
||
|
||
已结算 --> [*]
|
||
异常订单 --> [*]
|
||
|
||
note right of 待结算
|
||
定时任务触发条件:
|
||
1. 桩在线
|
||
2. 停止>10分钟
|
||
3. 有实时数据
|
||
4. 在灰度站点范围内
|
||
end note
|
||
```
|
||
|
||
### 8.4 配置项完整清单
|
||
|
||
```yaml
|
||
# application.yml 或 application-{env}.yml
|
||
auto-settle:
|
||
# ========== 基础配置 ==========
|
||
enabled: false # 总开关,默认关闭
|
||
timeout-minutes: 10 # 停止超时阈值(分钟)
|
||
interval: '0 */10 * * * ?' # 扫描周期(Cron表达式,每10分钟)
|
||
grayscale-station-ids: # 灰度站点白名单
|
||
- 1001
|
||
- 1002
|
||
|
||
# ========== 金额与数据校验 ==========
|
||
amount-threshold-ratio: 1.5 # 金额异常阈值倍数(chargingAmount > payAmount * 1.5 时告警)
|
||
batch-size: 100 # 单次扫描订单数量上限
|
||
data-freshness-minutes: 30 # 实时数据新鲜度阈值(分钟)
|
||
|
||
# ========== 并发与容错 ==========
|
||
alert-enabled: true # 告警开关
|
||
lock-timeout-seconds: 60 # redis分布式锁超时时间(秒)
|
||
retry-on-failure: false # 失败是否重试(建议false,等下一轮)
|
||
|
||
# ========== 监控指标 ==========
|
||
metrics:
|
||
enabled: true # 是否启用Prometheus指标
|
||
prefix: "auto_settle" # 指标前缀
|
||
```
|
||
|
||
### 8.5 数据库查询SQL示例
|
||
|
||
#### 查询待结算订单(方案D:直接JOIN)
|
||
|
||
```sql
|
||
SELECT
|
||
obi.order_id,
|
||
obi.order_no,
|
||
obi.pile_connector_id,
|
||
obi.pay_amount,
|
||
obi.charge_end_time,
|
||
obi.station_id,
|
||
pci.connector_status,
|
||
omd.charging_degree,
|
||
omd.charging_amount,
|
||
omd.date_time as last_monitor_time
|
||
FROM order_basic_info obi
|
||
INNER JOIN pile_connector_info pci
|
||
ON obi.pile_connector_id = pci.connector_id
|
||
LEFT JOIN (
|
||
SELECT
|
||
order_id,
|
||
charging_degree,
|
||
charging_amount,
|
||
date_time,
|
||
ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY date_time DESC, id DESC) as rn
|
||
FROM order_monitor_data
|
||
) omd ON obi.order_id = omd.order_id AND omd.rn = 1
|
||
WHERE obi.order_status = 'STAY_SETTLEMENT' -- 待结算
|
||
AND pci.connector_status != '0' -- 桩在线
|
||
AND obi.station_id IN (?, ?, ...) -- 灰度站点
|
||
AND (
|
||
-- 依据1:有充电结束时间且超过10分钟
|
||
(obi.charge_end_time IS NOT NULL
|
||
AND obi.charge_end_time < DATE_SUB(NOW(), INTERVAL 10 MINUTE))
|
||
OR
|
||
-- 依据2:最后实时数据超过10分钟
|
||
(omd.date_time IS NOT NULL
|
||
AND omd.date_time < DATE_SUB(NOW(), INTERVAL 10 MINUTE))
|
||
)
|
||
AND omd.charging_degree > 0 -- 有充电度数
|
||
AND omd.charging_amount > 0 -- 有充电金额
|
||
AND omd.date_time > DATE_SUB(NOW(), INTERVAL 30 MINUTE) -- 数据新鲜度
|
||
LIMIT 100;
|
||
```
|
||
|
||
#### 查询最后一条实时数据
|
||
|
||
```sql
|
||
SELECT *
|
||
FROM order_monitor_data
|
||
WHERE order_id = ?
|
||
ORDER BY date_time DESC, id DESC
|
||
LIMIT 1;
|
||
```
|
||
|
||
### 8.6 Redis Key 设计
|
||
|
||
| Key 模式 | 说明 | 过期时间 | 示例值 |
|
||
|---------|------|---------|--------|
|
||
| `settle_order_{orderId}` | 结算分布式锁 | 60秒 | `settle_order_123456` |
|
||
| `PILE_REAL_TIME_MONITOR_DATA:{connectorId}` | 实时数据缓存 | 持久化 | `PILE_REAL_TIME_MONITOR_DATA:ABC001-1` |
|
||
| `auto_settle_alert:{orderId}` | 告警去重(可选) | 3600秒 | `auto_settle_alert_123456` |
|
||
|
||
### 8.7 日志规范
|
||
|
||
#### INFO 级别日志
|
||
|
||
```java
|
||
log.info("[自动结算] 开始扫描待结算订单,灰度站点数量:{}", stationIds.size());
|
||
log.info("[自动结算] 扫描到{}个待结算订单", orders.size());
|
||
log.info("[自动结算] 订单{}自动结算成功,金额:{}元,耗时:{}ms",
|
||
orderId, chargingAmount, duration);
|
||
```
|
||
|
||
#### WARN 级别日志
|
||
|
||
```java
|
||
log.warn("[自动结算] 订单{}充电度数异常:{},跳过结算", orderId, chargingDegree);
|
||
log.warn("[自动结算] 订单{}充电金额异常:{},跳过结算", orderId, chargingAmount);
|
||
log.warn("[自动结算] 订单{}状态已变更,跳过结算", orderId);
|
||
log.warn("[自动结算] 订单{}获取锁失败,跳过", orderId);
|
||
```
|
||
|
||
#### ERROR 级别日志(需发送告警)
|
||
|
||
```java
|
||
log.error("[自动结算] 订单{}金额异常:实际{}元 > 预付{}元 * {},跳过结算",
|
||
orderId, chargingAmount, payAmount, threshold);
|
||
log.error("[自动结算] 订单{}结算失败:{}", orderId, e.getMessage(), e);
|
||
log.error("[自动结算] 定时任务执行失败:{}", e.getMessage(), e);
|
||
```
|
||
|
||
### 8.8 Prometheus 监控指标
|
||
|
||
```java
|
||
// 建议采集的指标
|
||
auto_settle_scan_count // 扫描次数
|
||
auto_settle_order_found_count // 发现待结算订单数
|
||
auto_settle_success_count // 结算成功数
|
||
auto_settle_skip_count{reason="lock_failed"} // 跳过数(锁失败)
|
||
auto_settle_skip_count{reason="data_invalid"} // 跳过数(数据异常)
|
||
auto_settle_skip_count{reason="amount_exceed"} // 跳过数(金额超限)
|
||
auto_settle_failure_count // 结算失败数
|
||
auto_settle_duration_seconds // 扫描耗时
|
||
auto_settle_order_duration_seconds // 单个订单结算耗时
|
||
```
|
||
|
||
---
|
||
|
||
## 九、审查意见总结
|
||
|
||
### ✅ 文档优点
|
||
|
||
1. **风险识别全面**:6个风险点覆盖了金额准确性、并发、影响面等核心问题
|
||
2. **决策清晰**:对R1(金额策略)、R4(停止判定)都有明确决策并记录
|
||
3. **实现思路稳健**:不改核心链路,新增入口,灰度上线
|
||
4. **业务条件合理**:桩在线、停止超10分钟、有实时数据,三个条件都是必要的
|
||
|
||
### ⚠️ 需补充内容(已在第六章列出)
|
||
|
||
| 类别 | 问题编号 | 状态 | 优先级 |
|
||
|------|---------|------|--------|
|
||
| 并发幂等 | 6.1, 6.5 | ⬜ 待回答 | P0 |
|
||
| 数据校验 | 6.2, 6.4, 6.7 | ⬜ 待回答 | P0 |
|
||
| 性能优化 | 6.3 | ⬜ 待回答 | P1 |
|
||
| 业务逻辑 | 6.6 | ⬜ 待回答 | P0 |
|
||
| 配置设计 | 6.8 | ⬜ 待回答 | P1 |
|
||
|
||
**建议行动路径**:
|
||
1. ✅ 步骤1:需求评审确认(已完成)
|
||
2. 🔴 步骤2:回答第六章的8个待明确问题(**当前阻塞**)
|
||
3. ⬜ 步骤3:更新文档版本号为 v0.4
|
||
4. ⬜ 步骤4:进入开发阶段(第五章进度追踪)
|
||
|
||
---
|
||
|
||
## 十、版本历史
|
||
|
||
| 版本 | 日期 | 变更内容 | 作者 |
|
||
|------|------|---------|------|
|
||
| v0.1 | 2026-08-11 | 初稿:需求分析、风险点、实现方案、开发进度追踪框架 | - |
|
||
| v0.2 | 2026-08-11 | 确认决策:金额以桩端 chargingAmount 为准;双依据判定都启用;特定站点灰度 | - |
|
||
| v0.3 | 2026-08-11 | 新增8个待明确问题(第六章);扩充测试计划至29个场景(第七章);新增附录 | Claude |
|
||
| v0.4 | 2026-08-11 | ✅ 完成8个问题的决策回答;更新已决策问题列表(11个决策);状态更新为"方案已确认,待开发" | Claude |
|
||
|
||
---
|
||
|
||
**文档结束** |