Files
jsowell-charger-web/jsowell-pile/src/main/java/com/jsowell/pile/config/AutoSettleConfig.java
jsowell 121b357a20 feat: 实现无交易记录自动结算功能
核心功能:
- 配置开关:11个配置项,支持灰度发布
- 定时任务:Quartz调度,可配置执行间隔
- 幂等保护:Redis分布式锁 + 数据库乐观锁
- 数据校验:充电桩在线检测、数据新鲜度、金额异常告警
- 结算逻辑:实时数据转换为结算数据,自动处理退款
- 单元测试:6个测试用例,覆盖主要场景

新增文件:
- AutoSettleConfig.java - 配置属性类
- SettlementDataConverter.java - 数据转换工具
- AutoSettleOrdersWithoutTransactionTest.java - 单元测试

修改文件:
- OrderBasicInfoService.java - 添加自动结算接口
- OrderBasicInfoServiceImpl.java - 实现自动结算逻辑
- OrderBasicInfoMapper.java - 添加查询和乐观锁更新方法
- OrderBasicInfoMapper.xml - 添加SQL实现
- JsowellTask.java - 添加定时任务方法
- application.yml - 添加配置项示例

详细设计:见 docs/feature-tracker-无交易记录自动结算.md
2026-08-11 16:40:35 +08:00

96 lines
2.2 KiB
Java
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.
package com.jsowell.pile.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 无交易记录自动结算配置类
*
* @author jsowell
*/
@Data
@Component
@ConfigurationProperties(prefix = "auto-settle")
public class AutoSettleConfig {
/**
* 功能总开关
* 默认false关闭
*/
private Boolean enabled = false;
/**
* 停止超时阈值(分钟)
* 订单停止超过该时间才触发自动结算
* 默认10分钟
*/
private Integer timeoutMinutes = 10;
/**
* 定时任务执行间隔cron表达式
* 默认每10分钟执行一次
*/
private String interval = "0 */10 * * * ?";
/**
* 灰度站点ID列表
* 为空表示全量上线
*/
private List<Long> grayscaleStationIds;
/**
* 单次扫描订单数量上限
* 防止一次性处理过多订单
* 默认100
*/
private Integer batchSize = 100;
/**
* 异常金额阈值比例
* chargingAmount > payAmount * 该比例时告警跳过
* 默认1.5倍
*/
private Double amountThresholdRatio = 1.5;
/**
* 实时数据新鲜度阈值(分钟)
* 最后一条实时数据距当前时间超过该阈值则认为桩"假在线"
* 默认30分钟
*/
private Integer dataFreshnessMinutes = 30;
/**
* 告警开关
* 异常情况是否发送告警
* 默认true
*/
private Boolean alertEnabled = true;
/**
* Redis锁超时时间
* 防止分布式锁死锁
* 默认300秒5分钟
*/
private Long lockTimeout = 300L;
/**
* 是否启用灰度(根据灰度站点列表判断)
*/
public boolean isGrayscaleEnabled() {
return grayscaleStationIds != null && !grayscaleStationIds.isEmpty();
}
/**
* 判断站点是否在灰度范围内
*/
public boolean isStationInGrayscale(Long stationId) {
if (!isGrayscaleEnabled()) {
return true; // 未启用灰度,所有站点都算在范围内
}
return grayscaleStationIds.contains(stationId);
}
}