package org;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
/**
* [
* {
* "weeks": [
* 1,
* 2,
* 3,
* 4,
* 5
* ],
* "weeksRules": [
* {
* "beginHour": 0,
* "beginMinute": 0,
* "beginTime": "00:00",
* "endHour": 23,
* "endMinute": 59,
* "endTime": "23:59",
* "level": 1,
* "unitPrice": 100.0
* }
* ]
* },
* {
* "weeks": [
* 6,
* 7
* ],
* "weeksRules": [
* {
* "beginHour": 0,
* "beginMinute": 0,
* "beginTime": "00:00",
* "endHour": 23,
* "endMinute": 59,
* "endTime": "23:59",
* "level": 3,
* "unitPrice": 1.0
* }
* ]
* }
* ]
*/
@Data
class WeeklyTOUPricing implements Serializable {
@ApiModelProperty(value = "适用星期数")
private List<Integer> weeks;
@ApiModelProperty(value = "电价计费规则")
private List<DailyTOUPricing> weeksRules;
}
@Data
@ApiModel
class DailyTOUPricing implements Serializable {
@ApiModelProperty(value = "电量单价")
private BigDecimal unitPrice;
@ApiModelProperty(value = "开始时间-小时部分")
private Integer beginHour = 0;
@ApiModelProperty(value = "开始时间-分钟部分")
private Integer beginMinute = 0;
@ApiModelProperty(value = "结束时间-小时部分")
private Integer endHour = 0;
@ApiModelProperty(value = "结束时间-分钟部分")
private Integer endMinute = 0;
@ApiModelProperty(value = "开始时间(HH:mm)")
private String beginTime;
@ApiModelProperty(value = "结束时间(HH:mm)")
private String endTime;
}
public class WeekRule {
public static void main(String[] args) {
String a = "[\n" +
" {\n" +
" \"weeks\": [\n" +
" 1,\n" +
" 2,\n" +
" 3,\n" +
" 4,\n" +
" 5\n" +
" ],\n" +
" \"weeksRules\": [\n" +
" {\n" +
" \"beginHour\": 0,\n" +
" \"beginMinute\": 0,\n" +
" \"beginTime\": \"00:00\",\n" +
" \"endHour\": 23,\n" +
" \"endMinute\": 59,\n" +
" \"endTime\": \"23:59\",\n" +
" \n" +
" \"unitPrice\": 100.0\n" +
" }\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"weeks\": [\n" +
" 6,\n" +
" 7\n" +
" ],\n" +
" \"weeksRules\": [\n" +
" {\n" +
" \"beginHour\": 0,\n" +
" \"beginMinute\": 0,\n" +
" \"beginTime\": \"00:00\",\n" +
" \"endHour\": 21,\n" +
" \"endMinute\": 59,\n" +
" \"endTime\": \"21:59\",\n" +
" \n" +
" \"unitPrice\": 1.0\n" +
" }\n" +
"{" +
" \"beginHour\": 22,\n" +
" \"beginMinute\": 00,\n" +
" \"beginTime\": \"22:00\",\n" +
" \"endHour\": 23,\n" +
" \"endMinute\": 59,\n" +
" \"endTime\": \"23:59\",\n" +
" \n" +
" \"unitPrice\": 11.0\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]";
List<WeeklyTOUPricing> weeklyTOUPricings = JSON.parseArray(a, WeeklyTOUPricing.class);
ZonedDateTime beginZonedDateTime = ZonedDateTime.now();
TOUPricingHolder touPricingHolder = futureTOUPricing(
beginZonedDateTime, beginZonedDateTime.plusDays(2), weeklyTOUPricings);
System.out.println(JSON.toJSONString(touPricingHolder));
List<TOUPricing> touPricingList = touPricingHolder.getTouPricingList();
System.out.println(JSON.toJSONString(touPricingList));
BigDecimal offPeakPrice = touPricingHolder.getOffPeakPrice();
AtomicLong sumChargeTime = new AtomicLong(0);
BigDecimal minChargeTime = estimateChargeTime(BigDecimal.valueOf(50), BigDecimal.valueOf(220), BigDecimal.valueOf(6));
for (TOUPricing t : touPricingList) {
if (t.getPrice().equals(offPeakPrice)) {
long beforeAddValue = sumChargeTime.get();
long afterAddValue = sumChargeTime.addAndGet(t.getDuration());
if (BigDecimal.valueOf(afterAddValue).compareTo(minChargeTime) < 0) {
System.out.println("deadline , charge time is" + t.getEndDateTime() + afterAddValue);
} else {
if (BigDecimal.valueOf(afterAddValue).compareTo(minChargeTime) == 0) {
long val = Duration.between(beginZonedDateTime, t.getEndDateTime()).toMinutes();
System.out.println("想要最低价格优惠 所需要等待时间 " + BigDecimal.valueOf(val) + "分钟");
System.out.println("想要最低价格优惠 截至时间" + beginZonedDateTime.plusMinutes(val));
} else {
long val = Duration.between(beginZonedDateTime, t.getStartDateTime()).toMinutes();
BigDecimal minGreenTime = BigDecimal.valueOf(val).add(minChargeTime.subtract(BigDecimal.valueOf(beforeAddValue)));
System.out.println("想要最低价格优惠 所需要等待时间 " + minGreenTime + "分钟");
System.out.println("想要最低价格优惠 截至时间" + beginZonedDateTime.plusMinutes(minGreenTime.longValue()));
}
break;
}
}
}
}
public static BigDecimal estimateChargeTime(BigDecimal chargeLevel, BigDecimal voltage, BigDecimal current) {
return chargeLevel.multiply(BigDecimal.valueOf(1000)).divide(voltage.multiply(current), 3, RoundingMode.UP)
.multiply(BigDecimal.valueOf(60)).setScale(0, RoundingMode.UP);
}
/**
* 从开始充电到下次用车时间端的 电价
* 未来电价走势
*
* @param startScheduleTime 用户开始智能调度时间
* @param nextDriveTime 下一次出行时间
* @return 到下一次出行时
*/
protected static TOUPricingHolder futureTOUPricing(ZonedDateTime startScheduleTime, ZonedDateTime nextDriveTime, List<WeeklyTOUPricing> rules) {
List<TOUPricing> result = new ArrayList<>();
// 启动充电时当天的价格走势
for (WeeklyTOUPricing ruleWeeksDTO : rules) {
List<Integer> weeks = ruleWeeksDTO.getWeeks();
if (weeks.contains(startScheduleTime.getDayOfWeek().getValue())) {
buildTOUPricing(startScheduleTime, ruleWeeksDTO, result);
}
}
// 启动充电时次天的价格走势
int offset = 1;
while (nextDriveTime.isAfter(startScheduleTime.plusDays(offset).withHour(0).withMinute(0).withSecond(0))) {
for (WeeklyTOUPricing ruleWeeksDTO : rules) {
List<Integer> weeks = ruleWeeksDTO.getWeeks();
if (weeks.contains(startScheduleTime.plusDays(offset).getDayOfWeek().getValue())) {
buildTOUPricing(startScheduleTime.plusDays(offset), ruleWeeksDTO, result);
}
}
offset++;
}
return TOUPricingHolder.hold(result);
}
private static void buildTOUPricing(ZonedDateTime startScheduleTime, WeeklyTOUPricing ruleWeeksDTO, List<TOUPricing> result) {
List<DailyTOUPricing> weeksRules = ruleWeeksDTO.getWeeksRules();
for (DailyTOUPricing costRulesDTO : weeksRules) {
ZonedDateTime startDateTime = startScheduleTime.withHour(costRulesDTO.getBeginHour()).withMinute(costRulesDTO.getBeginMinute());
// 给前面的那个电价结束值补上
if (!result.isEmpty()) {
TOUPricing touPricing = result.get(result.size() - 1);
touPricing.setEndDateTime(startDateTime);
}
TOUPricing touPricing = new TOUPricing();
touPricing.setStartDateTime(startDateTime);
touPricing.setPrice(costRulesDTO.getUnitPrice());
result.add(touPricing);
}
}
}
@Data
@ApiModel("未来的时间段电价")
class TOUPricingHolder {
private BigDecimal offPeakPrice;
private List<TOUPricing> touPricingList;
private TOUPricingHolder(List<TOUPricing> touPricingList) {
this.touPricingList = touPricingList;
BigDecimal lowestPrice = null;
for (TOUPricing touPricing : touPricingList) {
if (Objects.isNull(lowestPrice)) {
lowestPrice = touPricing.getPrice();
} else {
if (lowestPrice.compareTo(touPricing.getPrice()) > 0) {
lowestPrice = touPricing.getPrice();
}
}
}
this.offPeakPrice = lowestPrice;
}
public static TOUPricingHolder hold(List<TOUPricing> touPricingList) {
return new TOUPricingHolder(touPricingList);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("分时电价")
class TOUPricing {
@ApiModelProperty("开始时间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm '['VV']'")
@JSONField(format = "yyyy-MM-dd HH:mm E '['VV']'")
private ZonedDateTime startDateTime;
@ApiModelProperty("结束时间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm '['VV']'")
@JSONField(format = "yyyy-MM-dd HH:mm E '['VV']'")
private ZonedDateTime endDateTime;
@ApiModelProperty("电价")
private BigDecimal price;
@ApiModelProperty("持续时间 分钟 ")
private int duration;
public Long getDuration() {
if (Objects.isNull(startDateTime) || Objects.isNull(endDateTime)) {
return -1L;
}
return Duration.between(startDateTime, endDateTime).toMinutes();
}
}