From 864d04ccfb8540462983fcc2270115af92c9de52 Mon Sep 17 00:00:00 2001 From: jsowell <123@jsowell.com> Date: Tue, 11 Aug 2026 16:48:41 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85=E6=97=A0=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E8=AE=B0=E5=BD=95=E8=87=AA=E5=8A=A8=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增文档: - feature-tracker-无交易记录自动结算-实施总结.md - 完整实施总结,包含部署指南、监控方案、风险应对 - feature-tracker-无交易记录自动结算-附录.md - 功能设计附录 文档内容: - 开发进度总结(8个任务全部完成) - 核心实现详解(配置、数据转换、业务逻辑、定时任务、幂等保护、数据校验) - 文件清单(3个新增文件、7个修改文件) - 部署指南(代码合并、配置更新、定时任务添加、4阶段灰度发布流程) - 监控方案(日志监控、数据库监控、Redis监控) - 风险点与应对(6类风险及解决方案) - 后续优化建议(5个方向) - 验收标准(功能、性能、稳定性) --- ...ure-tracker-无交易记录自动结算-实施总结.md | 533 ++++++++++++++++++ ...feature-tracker-无交易记录自动结算-附录.md | 274 +++++++++ 2 files changed, 807 insertions(+) create mode 100644 docs/feature-tracker-无交易记录自动结算-实施总结.md create mode 100644 docs/feature-tracker-无交易记录自动结算-附录.md diff --git a/docs/feature-tracker-无交易记录自动结算-实施总结.md b/docs/feature-tracker-无交易记录自动结算-实施总结.md new file mode 100644 index 000000000..59b3018a0 --- /dev/null +++ b/docs/feature-tracker-无交易记录自动结算-实施总结.md @@ -0,0 +1,533 @@ +# 无交易记录自动结算功能 - 实施总结 + +## 版本信息 + +- **功能版本**: 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 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 selectPendingAutoSettleOrders( + @Param("cutoffTime") LocalDateTime cutoffTime, + @Param("stationIds") List 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 +**状态**: ✅ 开发完成,待灰度发布 diff --git a/docs/feature-tracker-无交易记录自动结算-附录.md b/docs/feature-tracker-无交易记录自动结算-附录.md new file mode 100644 index 000000000..74431f78b --- /dev/null +++ b/docs/feature-tracker-无交易记录自动结算-附录.md @@ -0,0 +1,274 @@ +# 附录:无交易记录自动结算功能 + +本文档为 `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: 查询待结算订单
(状态=待结算 + 桩在线 + 停止>10min) + DB-->>Timer: 订单列表(限batch-size条) + + loop 遍历每个订单 + Timer->>Redis: 尝试获取锁
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结束帧
或桩停止上报 + + 待结算 --> 已结算: 收到交易记录帧
(现有逻辑) + 待结算 --> 已结算: 定时任务自动结算
(新增逻辑) + 待结算 --> 已结算: 人工结算 + + 待结算 --> 异常订单: 桩离线
(现有逻辑) + + 已结算 --> [*] + 异常订单 --> [*] + + 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 | + +--- + +**文档结束** \ No newline at end of file