一 项目背景
某省某市的一个充电桩项目近日收到业主需求,需在国庆节增加一个时间段(深谷计费段),但充电桩设备仅支持4段(尖时段,峰时段,平时段,谷时段),今天已是23号下周就是国庆了任务时间紧,如果改充电桩设备并不能保证可以按时交付完成,原因:硬件升级开发不好评估:开发,与平台调试对接,测试,还要升级每台桩…最后决定使用软件扩展实现,下面我们详细介绍。
二 项目升级
1 需求与分析
先看业主方的的需求:在原有的计费基础(尖时段,峰时段,平时段,谷时段)上增加一段计费,如下:
从上图看,这个需求应该早就有了,只是业主现在才传达到我们开发这边。
这个需求本身不是很难,但今天已是23号下周就是国庆了任务时间紧,由于充电设备厂家这边实现与对接不敢保证可以按时交付完成,经分析软件实现也存在一些小问题,在深谷段充电时由于深谷时间段的价格是最低的,我们下发深谷时段的价格只能使用最接近的谷段价格代替,这就会存在充电桩设备计算出来的金额(谷段电价*电量)大于平台计算金额(深谷段电价*电量),会引起充电桩设备提前结束充电(至少不会出现超充,保证业主权益,客户钱包则可能留有少量余额),之前我们也有类似实现案例,在经过和老板的讨论与业主确认后同意了,最最终决定使用软件扩展实现。
2 平台实现
平台实现包含了运营管理平台,订单数据,报表,都要升级增加相应的字段,这里只列出涉及本次升级的关键点解释:
1 操作平台界面升级实现
先看平台已实现的需求图:
平台只需增加深谷段即可:
在操作上原来的功能不变,只是追加了深谷字段。深谷时间设置要在谷时段的范围内,这是因为谷段的价格与深谷时段最接近,桩计算时误差也少,后台数据库增加相应的字段即可。
2 充电订单数据生成实现
确认业务逻辑
充电订单的实现逻辑大体不需要改变,只需要在生成订单前的分段时间时增加以下逻辑。
首先明确:谷时间 包含了深谷,具体业务逻辑:
- 软处理需要准确提取订单中含有谷区域时间范围。
- 软处理需要准确提取订单中含有深谷区域时间范围。
- 按平均计算电量(充电功率随着soc的变化会有偏差,业主同意) :谷每分钟充电量=谷电量/谷时间(分钟)。
- 对比谷 与 深谷 时长,正常是谷大于或等于深谷,当时长相等不用直接移到深谷即可,不作计算。深谷电量=深谷 时长*谷每分钟充电量,谷电量=原谷电量-深谷电量 即可。
实现关键点:
多个深谷段,跨天时某个区域在时间范围的计算:
用java实现计算多个深谷段,跨天时某个区域在时间范围具体测试代码:
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* @author hua
* @date 2024-09-23 14:18
*/
public class TimeCheckDemo {
/**
* 计算在给定时间范围内的重叠分钟数
* @param inputStartTime
* @param inputEndTime
* @param timeRange
* @return
*/
public static int calculateOverlapMinutes(String inputStartTime, String inputEndTime, String timeRange) {
// 时间格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 将输入的字符串转换为LocalDateTime对象
LocalDateTime startTime = LocalDateTime.parse(inputStartTime, formatter);
LocalDateTime endTime = LocalDateTime.parse(inputEndTime, formatter);
// 解析时间范围字符串,格式: "01:30,04:30"
String[] rangeParts = timeRange.split(",");
LocalTime rangeStart = LocalTime.parse(rangeParts[0]);
LocalTime rangeEnd = LocalTime.parse(rangeParts[1]);
// 时间范围跨天判断
boolean isRangeCrossMidnight = rangeEnd.isBefore(rangeStart);
// 初始化总重叠分钟数
int totalOverlapMinutes = 0;
// 处理当前天的重叠部分
totalOverlapMinutes += calculateDayOverlapMinutes(startTime, endTime, rangeStart, rangeEnd, isRangeCrossMidnight);
// 如果开始时间和结束时间不在同一天,处理跨天的重叠部分
if (!startTime.toLocalDate().equals(endTime.toLocalDate())) {
// 第二天的00:00到结束时间的部分
LocalDateTime midnight = startTime.toLocalDate().plusDays(1).atStartOfDay();
totalOverlapMinutes += calculateDayOverlapMinutes(midnight, endTime, rangeStart, rangeEnd, isRangeCrossMidnight);
}
return totalOverlapMinutes;
}
/**
* 计算一天内的重叠分钟数
* @param startTime
* @param endTime
* @param rangeStart
* @param rangeEnd
* @param isRangeCrossMidnight
* @return
*/
private static long calculateDayOverlapMinutes(LocalDateTime startTime, LocalDateTime endTime, LocalTime rangeStart, LocalTime rangeEnd, boolean isRangeCrossMidnight) {
// 获取开始时间和结束时间的本地时间部分
LocalTime startTimeOfDay = startTime.toLocalTime();
LocalTime endTimeOfDay = endTime.toLocalTime();
if (isRangeCrossMidnight) {
// 如果时间范围跨越午夜,将时间范围分成两部分处理
long overlapMinutes1 = calculateOverlap(startTimeOfDay, endTimeOfDay, rangeStart, LocalTime.MAX);
long overlapMinutes2 = calculateOverlap(startTimeOfDay, endTimeOfDay, LocalTime.MIN, rangeEnd);
return overlapMinutes1 + overlapMinutes2;
} else {
// 非跨天的正常重叠计算
return calculateOverlap(startTimeOfDay, endTimeOfDay, rangeStart, rangeEnd);
}
}
// 计算两个时间段的重叠部分
private static long calculateOverlap(LocalTime start1, LocalTime end1, LocalTime start2, LocalTime end2) {
LocalTime effectiveStart = start1.isAfter(start2) ? start1 : start2;
LocalTime effectiveEnd = end1.isBefore(end2) ? end1 : end2;
if (effectiveStart.isBefore(effectiveEnd)) {
return Duration.between(effectiveStart, effectiveEnd).toMinutes();
}
return 0;
}
public static void main(String[] args) {
// 假设谷段的时间范围格式:"01:30,04:30"
String timeRange = "01:30,04:30";
// 输入的开始时间和结束时间
String inputStartTime = "2024-09-02 02:00:00";
String inputEndTime = "2024-09-02 04:00:00";
int overlapMinutes = calculateOverlapMinutes(inputStartTime, inputEndTime, timeRange);
System.out.println("测试1 中部分重叠的分钟数: " + overlapMinutes);
// 输入的开始时间和结束时间
inputStartTime = "2024-09-01 23:00:00";
inputEndTime = "2024-09-02 01:40:00";
overlapMinutes = calculateOverlapMinutes(inputStartTime, inputEndTime, timeRange);
System.out.println("测试2 跨天之前半部分重叠的分钟数: " + overlapMinutes);
// 输入的开始时间和结束时间
inputStartTime = "2024-09-02 04:10:00";
inputEndTime = "2024-09-02 05:30:00";
overlapMinutes = calculateOverlapMinutes(inputStartTime, inputEndTime, timeRange);
System.out.println("测试3 后半部分重叠的分钟数: " + overlapMinutes);
// 输入的开始时间和结束时间
inputStartTime = "2024-09-01 23:10:00";
inputEndTime = "2024-09-02 05:30:00";
overlapMinutes = calculateOverlapMinutes(inputStartTime, inputEndTime, timeRange);
System.out.println("测试4 跨天包含重叠的分钟数: " + overlapMinutes);
}
}
代码解释:
-
calculateOverlapMinutes
方法用于计算两个时间段的重叠部分(以分钟为单位)。它接收三个参数:startTime
:开始时间endTime
:结束时间timeRange
:时间范围的字符串,格式为“HH:mm”。
-
解析时间范围并确定在时间段内是否有重叠。如果有重叠,计算重叠部分的分钟数返回。
执行测试结果正确,如下图:
把上面测试代码再封装成一个工具类即可计算出充电时长所在区域范围的时间分钟,也可以方便实现以上业务逻辑了。
三 小结
通过软件扩展代替硬件升级,项目在时间紧迫的情况下实现了需求,降低了实施风险,并保障了业主与客户的权益。