mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-08-12 17:23:50 +08:00
提交功能设计文档
This commit is contained in:
162
docs/feature-tracker-无交易记录自动结算.md
Normal file
162
docs/feature-tracker-无交易记录自动结算.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 功能开发追踪:无交易记录时按实时检测数据自动结算
|
||||
|
||||
**功能编号**: TBD
|
||||
**版本**: v0.2
|
||||
**日期**: 2026-08-11
|
||||
**项目**: 万车充运营管理平台
|
||||
**状态**: 方案已确认,开发中
|
||||
**负责模块**: jsowell-pile / jsowell-netty / jsowell-quartz
|
||||
|
||||
---
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|------|------|---------|
|
||||
| v0.1 | 2026-08-11 | 初稿:需求分析、风险点、实现方案、开发进度追踪框架 |
|
||||
| v0.2 | 2026-08-11 | 确认决策:金额以桩端 chargingAmount 为准;双依据判定都启用;特定站点灰度 |
|
||||
|
||||
---
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
### 背景
|
||||
|
||||
当前自动结算**唯一入口**是收到交易记录帧。充电桩在线、充电已停止,但**若未收到交易记录**(桩端漏发、网络丢帧、交易记录帧异常等),订单将永远停留在「待结算」状态,无法自动结算,导致:
|
||||
- 用户预付资金长期冻结,体验差
|
||||
- 后台需要人工介入结算,运维成本高
|
||||
|
||||
### 目标
|
||||
|
||||
充电桩**在线**且**停止充电超过 10 分钟**仍未收到交易记录时,平台**按最后一条实时检测记录中的耗电量(充电度数)自动结算**,替代人工结算,缩短资金冻结周期。
|
||||
|
||||
### 非目标(本期不做)
|
||||
|
||||
- 桩离线场景的自动结算(离线时数据不可信,需人工确认)
|
||||
- 尖峰平谷分时明细的补全(实时数据本身不含分时信息)
|
||||
|
||||
---
|
||||
|
||||
## 二、需求分析
|
||||
|
||||
### 2.1 当前逻辑(现状)
|
||||
|
||||
| 环节 | 现状 | 代码位置 |
|
||||
|------|------|---------|
|
||||
| 结算触发 | 仅收到交易记录帧时触发 `settleOrder` | `TransactionRecordsRequestHandler.processOrder()` → jsowell-netty/.../TransactionRecordsRequestHandler.java:683 |
|
||||
| 结算数据来源 | 交易记录 `TransactionRecordsData`(含尖峰平谷分时电量、单价、金额) | 同上 |
|
||||
| 无交易记录时 | 订单停在「待结算」,不自动结算 | — |
|
||||
| 桩离线 | 订单置为异常 `updateOrderStatusAsAbnormal` | YKCBusinessServiceImpl.java:166 |
|
||||
| 人工结算 | 已支持「无交易记录 → 用最后一条实时数据构造结算数据」 | `OrderService.manualSettlementOrder()` → jsowell-admin/.../OrderService.java:1008-1027 |
|
||||
|
||||
### 2.2 目标逻辑
|
||||
|
||||
```
|
||||
定时任务(周期可配,默认如每 10 分钟)扫描待结算订单:
|
||||
├─ 条件 1:订单状态 = 待结算 (STAY_SETTLEMENT)
|
||||
├─ 条件 2:充电桩在线(connector status ≠ "0" 离线)
|
||||
├─ 条件 3:充电已停止超过 10 分钟
|
||||
│ ├─ 优先依据:chargeEndTime 非空 且 距当前 > 10 分钟(桩上报过 0x19 充电结束)
|
||||
│ └─ 兜底依据:最后一条实时检测数据 dateTime 距当前 > 10 分钟(桩未上报结束/交易记录)
|
||||
└─ 条件 4:存在实时检测数据(chargingDegree > 0)
|
||||
|
||||
命中后:
|
||||
1. 复用人工结算逻辑,从最后一条实时检测数据构造 TransactionRecordsData
|
||||
2. 走 orderLogic.settleOrder(data, orderBasicInfo)
|
||||
```
|
||||
|
||||
### 2.3 关键数据来源
|
||||
|
||||
| 数据 | 字段 | 说明 |
|
||||
|------|------|------|
|
||||
| 累计充电度数 | `RealTimeMonitorData.chargingDegree` | 精确到 4 位小数,待机置零 |
|
||||
| 已充金额 | `RealTimeMonitorData.chargingAmount` | 桩端计算(电费+服务费)*计损度数 |
|
||||
| 实时数据落库 | `order_monitor_data` 表 / redis `PILE_REAL_TIME_MONITOR_DATA` | 充电中每 10s 上报一次,按分钟保留最后一条 |
|
||||
| 桩在线状态 | `PileConnectorDataBaseStatusEnum` | `"0"`=离线,`1/2/3/4`=在线 |
|
||||
| 充电结束信号 | `ChargeEndHandler` (0x19) | 更新 `chargeEndTime`、`endSoc` |
|
||||
|
||||
---
|
||||
|
||||
## 三、风险点与应对
|
||||
|
||||
| # | 风险 | 等级 | 说明 | 应对 |
|
||||
|---|------|------|------|------|
|
||||
| R1 | **金额准确性依赖桩端上报** | 高 | 实时数据无尖峰平谷分时明细,结算走「以交易记录金额为准」分支,即完全信任桩端 `chargingAmount`。若桩端该值有误(充电中途拔枪、数据异常),直接导致扣费/退款不准确 | **已决策:以桩端 `chargingAmount` 为准**(与人工结算一致)。降低影响:① 异常金额阈值(chargingAmount 远超 payAmount)时跳过并告警;② 特定站点灰度上线,逐步观察 |
|
||||
| R2 | **退款联动** | 中 | `chargingAmount` < 预付款 `payAmount` 时走退款流程,金额不准会连带退款不准 | 结算金额以 `chargingAmount` 为准,与人工结算一致;金额异常(远超 payAmount)时跳过并告警 |
|
||||
| R3 | **幂等 / 并发** | 中 | 定时扫描与「交易记录恰好到达」可能并发重复结算 | 复用现有 `settle_order_`+transactionCode redis 锁(TransactionRecordsRequestHandler.java:602);结算前校验订单状态与结算时间 |
|
||||
| R4 | **判「停止」时间点模糊** | 中 | 桩只拔枪不上报 0x19 时,`chargeEndTime` 为空,只能依赖实时数据 `dateTime` 判定;若桩同时离线则数据停更 | **已决策:双依据都启用**(chargeEndTime 优先,最后实时数据 dateTime 兜底)。条件 2(在线)为安全底线,离线订单不结算 |
|
||||
| R5 | **影响面扩散** | 中 | 该逻辑影响所有待结算订单,改动结算主链路风险高 | 不修改结算核心 `settleOrder`,仅新增定时扫描入口,复用人工结算构造逻辑 |
|
||||
| R6 | **灰度与回滚** | 中 | 新逻辑上线初期存在未覆盖场景 | **已决策:特定站点灰度**(`auto-settle.grayscale-station-ids`)。开关关闭即回退到现有行为 |
|
||||
|
||||
---
|
||||
|
||||
## 四、实现方案
|
||||
|
||||
### 4.1 总体思路
|
||||
|
||||
不修改现有结算核心链路,新增**定时扫描任务**,命中条件后复用人工结算的「实时数据 → TransactionRecordsData」构造逻辑,再走既有的 `settleOrder`。
|
||||
|
||||
### 4.2 实现步骤
|
||||
|
||||
| # | 步骤 | 说明 | 模块 |
|
||||
|---|------|------|------|
|
||||
| 1 | 配置开关 | 新增自动结算开关 + 站点灰度白名单:`auto-settle.enabled`、`auto-settle.timeout-minutes=10`、`auto-settle.interval`、`auto-settle.grayscale-station-ids`(特定站点灰度)。默认关闭或仅灰度站点开启 | jsowell-pile / application.yml |
|
||||
| 2 | 构造结算数据方法 | 在 `OrderService`(或抽到 pile 的公共 Service)新增「从最后一条实时数据构造 TransactionRecordsData」方法,与人工结算复用同一逻辑 | jsowell-admin / jsowell-pile |
|
||||
| 3 | 查询待结算订单 | 新增 mapper 查询:状态=待结算 + 桩在线 + 满足停止条件(chargeEndTime/最后实时数据时间 < 当前-10min)的订单列表 | jsowell-pile |
|
||||
| 4 | 定时任务 | 新增 Quartz Job,周期执行扫描+结算,含 redis 分布式锁防并发 | jsowell-quartz |
|
||||
| 5 | 结算执行 | 循环命中订单,构造数据 → `orderLogic.settleOrder`,异常单独捕获不影响整体 | jsowell-pile |
|
||||
| 6 | 幂等保护 | 结算前校验订单状态未变更、`settle_order_` 锁未占用 | jsowell-pile |
|
||||
| 7 | 告警日志 | 结算异常、金额异常(chargingAmount 远大于 payAmount)时输出 ERROR 日志/告警 | 各模块 |
|
||||
|
||||
### 4.3 涉及文件清单(预估)
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `jsowell-pile/.../OrderBasicInfoService.java` | 新增查询待结算订单方法 |
|
||||
| `jsowell-pile/.../OrderBasicInfoServiceImpl.java` | 实现查询逻辑 |
|
||||
| `jsowell-pile/.../OrderBasicInfoMapper.xml` | 新增 SQL |
|
||||
| `jsowell-admin/.../OrderService.java` | 抽取「实时数据→结算数据」构造方法(或下沉到 pile) |
|
||||
| `jsowell-quartz/.../RyTask.java` 或新增 Task | 新增自动结算定时任务 |
|
||||
| `jsowell-admin/src/main/resources/application*.yml` | 新增配置项 |
|
||||
|
||||
---
|
||||
|
||||
## 五、开发进度追踪
|
||||
|
||||
> 状态:`⬜ 未开始` / `🟡 进行中` / `✅ 已完成` / `🔴 阻塞`
|
||||
|
||||
| # | 任务 | 模块 | 负责人 | 状态 | 备注 |
|
||||
|---|------|------|--------|------|------|
|
||||
| 1 | 需求评审确认(含 R1/R4 决策) | - | | ✅ 已完成 | 2026-08-11 确认:金额以桩端 chargingAmount 为准;双依据判定;特定站点灰度 |
|
||||
| 2 | 配置开关 | jsowell-pile | | ⬜ 未开始 | |
|
||||
| 3 | 实时数据→结算数据构造方法 | jsowell-admin/pile | | ⬜ 未开始 | |
|
||||
| 4 | 待结算订单查询 SQL | jsowell-pile | | ⬜ 未开始 | |
|
||||
| 5 | 定时任务 | jsowell-quartz | | ⬜ 未开始 | |
|
||||
| 6 | 幂等保护 | jsowell-pile | | ⬜ 未开始 | |
|
||||
| 7 | 告警日志 | 各模块 | | ⬜ 未开始 | |
|
||||
| 8 | 单元测试 / 集成测试 | jsowell-admin | | ⬜ 未开始 | |
|
||||
| 9 | 灰度上线(特定站点) | - | | ⬜ 未开始 | 先对灰度站点开启,观察稳定后逐步放开 |
|
||||
| 10 | 上线后监控与复盘 | - | | ⬜ 未开始 | |
|
||||
|
||||
### 已决策问题
|
||||
|
||||
| # | 问题 | 决策 | 决策日期 |
|
||||
|---|------|------|---------|
|
||||
| 1 | 金额策略 | **以桩端 `chargingAmount` 为准**(与人工结算一致) | 2026-08-11 |
|
||||
| 2 | 停止判定依据 | **双依据都启用**:chargeEndTime 优先,最后实时数据 dateTime 兜底 | 2026-08-11 |
|
||||
| 3 | 灰度范围 | **特定站点灰度**,先对指定站点开启,观察稳定后再逐步放开 | 2026-08-11 |
|
||||
|
||||
---
|
||||
|
||||
## 六、测试计划
|
||||
|
||||
| # | 场景 | 预期 |
|
||||
|---|------|------|
|
||||
| 1 | 桩在线 + 有 chargeEndTime + 停止>10min + 有实时数据 | 自动结算成功,金额=实时数据 chargingAmount |
|
||||
| 2 | 桩在线 + 无 chargeEndTime + 最后实时数据>10min + 有实时数据 | 兜底依据触发,自动结算 |
|
||||
| 3 | 桩离线 | 不触发(条件 2 拦截) |
|
||||
| 4 | 停止 < 10min | 不触发 |
|
||||
| 5 | 无实时检测数据(chargingDegree=0) | 不触发 |
|
||||
| 6 | 扫描期间交易记录到达 | 不重复结算(幂等) |
|
||||
| 7 | chargingAmount > payAmount 异常值 | 跳过并告警 |
|
||||
| 8 | 配置开关关闭 | 保持现有行为(不自动结算) |
|
||||
Reference in New Issue
Block a user