功能开发追踪:无交易记录时按实时检测数据自动结算
功能编号: 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 目标逻辑
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 |
配置开关关闭 |
保持现有行为(不自动结算) |