Files
jsowell-charger-web/docs/feature-tracker-无交易记录自动结算-实施总结.md
jsowell 864d04ccfb docs: 补充无交易记录自动结算功能文档
新增文档:
- feature-tracker-无交易记录自动结算-实施总结.md - 完整实施总结,包含部署指南、监控方案、风险应对
- feature-tracker-无交易记录自动结算-附录.md - 功能设计附录

文档内容:
- 开发进度总结(8个任务全部完成)
- 核心实现详解(配置、数据转换、业务逻辑、定时任务、幂等保护、数据校验)
- 文件清单(3个新增文件、7个修改文件)
- 部署指南(代码合并、配置更新、定时任务添加、4阶段灰度发布流程)
- 监控方案(日志监控、数据库监控、Redis监控)
- 风险点与应对(6类风险及解决方案)
- 后续优化建议(5个方向)
- 验收标准(功能、性能、稳定性)
2026-08-11 16:48:41 +08:00

534 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 无交易记录自动结算功能 - 实施总结
## 版本信息
- **功能版本**: v1.0
- **开发分支**: feature/auto-settle-no-transaction
- **提交哈希**: 121b357a2
- **完成日期**: 2026-08-11
- **开发者**: jsowell
---
## 实施概览
本次开发完成了"无交易记录自动结算"功能的完整实现,包括配置管理、定时任务、核心业务逻辑、幂等保护、数据校验、告警日志和单元测试。
### 开发进度
| 任务 | 状态 | 完成时间 |
|------|------|---------|
| ✅ 配置开关11个配置项 | 已完成 | 2026-08-11 |
| ✅ 创建配置属性类 | 已完成 | 2026-08-11 |
| ✅ 实时数据→结算数据构造方法 | 已完成 | 2026-08-11 |
| ✅ 待结算订单查询 SQL | 已完成 | 2026-08-11 |
| ✅ 定时任务实现 | 已完成 | 2026-08-11 |
| ✅ 幂等保护redis锁+乐观锁) | 已完成 | 2026-08-11 |
| ✅ 数据校验与告警日志 | 已完成 | 2026-08-11 |
| ✅ 单元测试 / 集成测试 | 已完成 | 2026-08-11 |
---
## 核心实现
### 1. 配置管理
**文件**: `jsowell-pile/src/main/java/com/jsowell/pile/config/AutoSettleConfig.java`
```java
@ConfigurationProperties(prefix = "auto-settle")
public class AutoSettleConfig {
private Boolean enabled = false; // 功能总开关
private Integer timeoutMinutes = 10; // 停止超时阈值(分钟)
private String interval = "0 */10 * * * ?"; // 定时任务间隔
private List<Long> grayscaleStationIds; // 灰度站点ID列表
private Integer batchSize = 100; // 单次扫描订单数量上限
private Double amountThresholdRatio = 1.5; // 异常金额阈值比例
private Integer dataFreshnessMinutes = 30; // 实时数据新鲜度阈值(分钟)
private Boolean alertEnabled = true; // 告警开关
private Long lockTimeout = 300L; // Redis锁超时时间
}
```
**配置示例** (application.yml):
```yaml
auto-settle:
enabled: false # 默认关闭,生产环境需手动开启
timeout-minutes: 10
interval: "0 */10 * * * ?"
grayscale-station-ids: [] # 空数组表示全量
batch-size: 100
amount-threshold-ratio: 1.5
data-freshness-minutes: 30
alert-enabled: true
lock-timeout: 300
```
### 2. 数据转换工具
**文件**: `jsowell-pile/src/main/java/com/jsowell/pile/util/SettlementDataConverter.java`
核心功能:
-`RealTimeMonitorData` 提取充电数据
- 构造 `TransactionRecordsData` 结算数据
- 时间格式转换Date ↔ String
- 默认值设置(所有电量归为平段)
### 3. 数据库层
**Mapper 接口**: `OrderBasicInfoMapper.java`
```java
// 查询待结算订单
List<OrderBasicInfo> selectPendingAutoSettleOrders(
@Param("cutoffTime") LocalDateTime cutoffTime,
@Param("stationIds") List<Long> stationIds,
@Param("limit") int limit);
// 乐观锁更新订单
int updateOrderWithOptimisticLock(
@Param("order") OrderBasicInfo order,
@Param("orderId") Integer orderId,
@Param("expectedStatus") String expectedStatus,
@Param("expectedSettlementTime") java.util.Date expectedSettlementTime);
```
**SQL 实现**: `OrderBasicInfoMapper.xml`
- 查询条件del_flag='0', order_status='3', pay_status='1', transaction_code IS NULL/空字符串, charge_end_time < 截止时间
- 灰度过滤支持按站点ID列表过滤
- 分页限制LIMIT 限制单次查询数量
- 乐观锁WHERE order_status='3' AND settlement_time IS NULL
### 4. 业务逻辑
**核心方法**: `OrderBasicInfoServiceImpl.autoSettleOrdersWithoutTransactionRecord()`
**执行流程**
1. **功能开关检查** - 如果未启用直接返回
2. **计算截止时间** - 当前时间 - 超时阈值
3. **查询待结算订单** - 支持灰度站点过滤
4. **逐个处理订单**
- 获取 Redis 分布式锁key: `settle_order_{orderId}`
- 重新查询订单最新状态(防止并发)
- 检查灰度范围
- 获取充电桩连接器状态Redis优先
- 检查充电桩在线状态
- 获取最后一条实时数据
- 检查数据新鲜度(< 30分钟
- 数据校验:电量和金额格式、非空、非零
- 构造结算数据
- 金额异常检测chargingAmount > payAmount × 1.5倍)
- 调用结算方法(`returnUpdateOrderBasicInfo` + `returnUpdateOrderDetail`
- 乐观锁更新订单状态
- 处理退款(如果结算金额 < 已支付金额)
- 释放 Redis 锁
**统计信息**
- successCount - 成功结算数量
- skipCount - 跳过数量
- failCount - 失败数量
### 5. 定时任务
**文件**: `jsowell-quartz/src/main/java/com/jsowell/quartz/task/JsowellTask.java`
```java
public void autoSettleOrdersWithoutTransactionRecord() {
if (skipInPre("无交易记录自动结算")) {
return; // 预发布环境跳过
}
log.info("【无交易记录自动结算】定时任务开始执行");
try {
orderBasicInfoService.autoSettleOrdersWithoutTransactionRecord();
log.info("【无交易记录自动结算】定时任务执行完成");
} catch (Exception e) {
log.error("【无交易记录自动结算】定时任务执行失败", e);
}
}
```
**调用字符串**: `jsowellTask.autoSettleOrdersWithoutTransactionRecord()`
**推荐配置**:
- 执行周期每10分钟执行一次
- Cron 表达式:`0 */10 * * * ?`
### 6. 幂等保护
#### Redis 分布式锁
- **锁键**: `settle_order_{orderId}`
- **锁值**: "1"
- **超时时间**: 300秒可配置
- **获取方式**: `redisCache.setCacheObject()` + 检查返回值
#### 数据库乐观锁
- **WHERE 条件**: `order_status = '3' AND settlement_time IS NULL`
- **更新失败处理**: 记录警告日志,跳过该订单
- **防止并发**: 重新查询订单最新状态
### 7. 数据校验与告警
#### 充电桩在线检测
```java
// 从 Redis 获取充电枪状态
String connectorKey = "CONNECTOR_STATUS:" + connectorId;
Integer status = redisCache.getCacheObject(connectorKey);
// 状态 3=充电中4=已连接未充电 为在线状态
if (status == null || (status != 3 && status != 4)) {
logger.warn("充电桩可能已离线,跳过");
continue;
}
```
#### 数据新鲜度检测
```java
long minutesSinceUpdate = Duration.between(
lastUpdateTime, LocalDateTime.now()
).toMinutes();
if (minutesSinceUpdate > dataFreshnessMinutes) {
logger.warn("实时数据过期({}分钟前),跳过", minutesSinceUpdate);
continue;
}
```
#### 金额异常告警
```java
BigDecimal threshold = payAmount.multiply(amountThresholdRatio);
if (chargingAmount.compareTo(threshold) > 0) {
String alertMsg = String.format(
"【金额异常】订单号:%s站点ID%s充电桩%s" +
"实时充电金额:%.2f 元,已支付金额:%.2f 元,阈值倍数:%.1f",
orderCode, stationId, pileSn,
chargingAmount, payAmount, amountThresholdRatio
);
logger.error(alertMsg);
// TODO: 发送告警通知(邮件/短信/钉钉/企业微信)
}
```
#### 数据格式校验
- 充电电量非空、格式正确、大于0
- 充电金额非空、格式正确
- 异常时记录错误日志,跳过该订单
### 8. 单元测试
**文件**: `jsowell-admin/src/test/java/com/jsowell/AutoSettleOrdersWithoutTransactionTest.java`
**测试用例**
1. **testSelectPendingAutoSettleOrders** - 查询待结算订单
2. **testGrayscaleConfiguration** - 灰度配置测试
3. **testAmountThresholdValidation** - 金额阈值检测
4. **testFeatureToggle** - 配置开关测试
5. **testAutoSettleMainFlow** - 完整流程测试
6. **testDataFreshness** - 数据新鲜度判断
---
## 文件清单
### 新增文件3个
```
jsowell-pile/src/main/java/com/jsowell/pile/config/AutoSettleConfig.java
jsowell-pile/src/main/java/com/jsowell/pile/util/SettlementDataConverter.java
jsowell-admin/src/test/java/com/jsowell/AutoSettleOrdersWithoutTransactionTest.java
```
### 修改文件7个
```
docs/feature-tracker-无交易记录自动结算.md
jsowell-admin/src/main/resources/application.yml
jsowell-pile/src/main/java/com/jsowell/pile/service/OrderBasicInfoService.java
jsowell-pile/src/main/java/com/jsowell/pile/service/impl/OrderBasicInfoServiceImpl.java
jsowell-pile/src/main/java/com/jsowell/pile/mapper/OrderBasicInfoMapper.java
jsowell-pile/src/main/resources/mapper/pile/OrderBasicInfoMapper.xml
jsowell-quartz/src/main/java/com/jsowell/quartz/task/JsowellTask.java
```
### 代码统计
- 新增代码:约 500 行(核心业务逻辑)
- 配置类95 行
- 工具类180 行
- 单元测试180 行
- SQL 语句2 个
---
## 部署指南
### 1. 代码合并
```bash
# 切换到目标分支(通常是 dev 或 master
git checkout dev
# 合并功能分支
git merge feature/auto-settle-no-transaction
# 推送到远程仓库
git push origin dev
```
### 2. 配置更新
`application-{env}.yml` 中添加配置:
```yaml
# 无交易记录自动结算配置
auto-settle:
# 功能总开关(默认关闭,需手动开启)
enabled: false
# 停止超时阈值(分钟)- 订单停止超过该时间才触发自动结算
timeout-minutes: 10
# 定时任务执行间隔cron表达式
interval: "0 */10 * * * ?"
# 灰度站点ID列表为空表示全量上线
# 示例:[1001, 1002, 1003]
grayscale-station-ids: []
# 单次扫描订单数量上限
batch-size: 100
# 异常金额阈值比例chargingAmount > payAmount * 该比例时告警跳过)
amount-threshold-ratio: 1.5
# 实时数据新鲜度阈值(分钟)
data-freshness-minutes: 30
# 告警开关
alert-enabled: true
# Redis锁超时时间
lock-timeout: 300
```
### 3. 添加定时任务
在管理后台的"定时任务"菜单中添加任务:
| 字段 | 值 |
|------|-----|
| 任务名称 | 无交易记录自动结算 |
| 任务组名 | DEFAULT |
| 调用目标字符串 | jsowellTask.autoSettleOrdersWithoutTransactionRecord() |
| cron表达式 | 0 */10 * * * ? |
| 执行策略 | 立即执行 |
| 是否并发 | 否 |
| 状态 | 暂停(灰度期间) |
### 4. 灰度发布流程
#### 阶段1单站点灰度第1-3天
```yaml
auto-settle:
enabled: true
grayscale-station-ids: [1001] # 选择1个业务量适中的站点
```
**观察指标**
- 成功结算数量
- 跳过数量(及原因)
- 失败数量(及异常)
- 金额异常告警
- Redis锁竞争情况
- 数据库慢查询
#### 阶段2小范围灰度第4-7天
```yaml
auto-settle:
enabled: true
grayscale-station-ids: [1001, 1002, 1003, 1004, 1005] # 扩展到5个站点
```
#### 阶段3大范围灰度第8-14天
```yaml
auto-settle:
enabled: true
grayscale-station-ids: [...] # 扩展到30%的站点
```
#### 阶段4全量上线第15天+
```yaml
auto-settle:
enabled: true
grayscale-station-ids: [] # 空数组 = 全量
```
### 5. 监控指标
#### 日志监控
```bash
# 查看执行日志
tail -f logs/jsowell-admin.log | grep "无交易记录自动结算"
# 统计成功数量
grep "【无交易记录自动结算】执行完成,成功" logs/jsowell-admin.log | tail -20
# 查看告警日志
grep "【无交易记录自动结算-金额异常】" logs/jsowell-admin.log
grep "【无交易记录自动结算-处理异常】" logs/jsowell-admin.log
```
#### 数据库监控
```sql
-- 查看待结算订单数量
SELECT COUNT(*)
FROM order_basic_info
WHERE del_flag = '0'
AND order_status = '3'
AND pay_status = '1'
AND (transaction_code IS NULL OR transaction_code = '')
AND charge_end_time < DATE_SUB(NOW(), INTERVAL 10 MINUTE);
-- 查看最近自动结算的订单
SELECT order_code, station_id, pile_sn,
charge_end_time, settlement_time,
order_amount, refund_amount
FROM order_basic_info
WHERE reason = '无交易记录自动结算'
AND settlement_time > DATE_SUB(NOW(), INTERVAL 1 DAY)
ORDER BY settlement_time DESC
LIMIT 100;
```
#### Redis监控
```bash
# 查看当前锁数量
redis-cli KEYS "settle_order_*" | wc -l
# 检查是否有死锁TTL < 0
redis-cli KEYS "settle_order_*" | xargs -I {} redis-cli TTL {}
```
---
## 风险点与应对
### 1. 并发冲突
**风险**: 多个定时任务实例同时处理同一订单
**应对**:
- Redis 分布式锁 + 乐观锁双重保护
- 锁超时时间设置为5分钟防止死锁
- 乐观锁更新失败时记录日志,不抛出异常
### 2. 金额异常
**风险**: 实时数据的充电金额远大于预付金额
**应对**:
- 设置金额阈值默认1.5倍)
- 超过阈值告警跳过,人工介入
- 详细记录订单信息、站点ID、充电桩SN
### 3. 数据不新鲜
**风险**: 充电桩"假在线"(最后一条实时数据很久之前)
**应对**:
- 检查实时数据更新时间
- 超过30分钟的数据视为陈旧跳过结算
- 记录警告日志
### 4. 充电桩离线
**风险**: 充电桩实际已离线,但订单未正常结束
**应对**:
- 从 Redis 检查充电枪状态
- 状态非"充电中"或"已连接"时跳过
- 依赖设备心跳机制更新状态
### 5. 退款失败
**风险**: 结算成功但退款失败,用户损失
**应对**:
- 退款失败记录错误日志
- 告警通知运营人员
- 支持后台手动发起退款
### 6. 性能问题
**风险**: 单次扫描订单过多,影响数据库性能
**应对**:
- 限制单次查询数量默认100
- 使用索引优化查询charge_end_time, order_status, pay_status
- 定时任务间隔可调整默认10分钟
---
## 后续优化建议
### 1. 告警系统集成
- 对接现有告警系统(邮件/短信/钉钉/企业微信)
- 金额异常、处理失败立即通知
- 每日汇总报告(成功数、失败数、异常数)
### 2. 监控大盘
- Grafana 可视化监控
- 实时展示待结算订单数量
- 自动结算成功率、平均处理时长
- 金额异常趋势图
### 3. 智能调度
- 根据待结算订单数量动态调整执行频率
- 业务高峰期降低频率,低谷期提高频率
- 避免影响正常业务
### 4. 数据分析
- 统计哪些站点、哪些充电桩频繁出现无交易记录
- 分析根本原因(设备故障、网络问题、协议异常)
- 针对性优化设备通信逻辑
### 5. 手动补结算
- 后台管理页面添加"手动自动结算"按钮
- 支持批量选择订单执行结算
- 提供详细的执行日志和结果反馈
---
## 验收标准
### 功能验收
- [x] 配置开关正常工作
- [x] 定时任务正常调度执行
- [x] 查询待结算订单准确
- [x] 结算逻辑正确(金额、电量、时间)
- [x] 退款逻辑正确
- [x] 幂等保护有效
- [x] 数据校验完整
- [x] 告警日志记录详细
- [x] 单元测试通过
### 性能验收
- [ ] 单次处理100个订单耗时 < 30秒
- [ ] Redis 锁无死锁现象
- [ ] 数据库慢查询 < 5%
### 稳定性验收
- [ ] 灰度期间7天无P0/P1故障
- [ ] 并发场景下无数据不一致
- [ ] 异常场景正常降级(记录日志、跳过订单)
---
## 附录
### A. 相关文档
- [功能设计文档](./feature-tracker-无交易记录自动结算.md)
- [功能设计附录](./feature-tracker-无交易记录自动结算-附录.md)
- [项目架构文档](../CLAUDE.md)
- [jsowell-pile 模块文档](../jsowell-pile/CLAUDE.md)
- [jsowell-quartz 模块文档](../jsowell-quartz/CLAUDE.md)
### B. 联系方式
- 开发负责人: jsowell
- Git 分支: feature/auto-settle-no-transaction
- 提交哈希: 121b357a2
---
**文档版本**: v1.0
**最后更新**: 2026-08-11
**状态**: ✅ 开发完成,待灰度发布