package com.kym.admin.jobs; import cn.hutool.core.date.DateUtil; import com.kym.common.utils.CommUtil; import com.kym.entity.admin.ConnectorInfo; import com.kym.entity.admin.StationStatDay; import com.kym.entity.admin.StationStatMonth; import com.kym.entity.miniapp.ChargeOrder; import com.kym.service.admin.ConnectorInfoService; import com.kym.service.admin.StationStatDayService; import com.kym.service.admin.StationStatMonthService; import com.kym.service.miniapp.ChargeOrderService; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; /** * 站点统计定时任务 * * @author skyline */ @Component @Slf4j public class StationStatJob { private final ChargeOrderService chargeOrderService; private final StationStatDayService dayService; private final StationStatMonthService monthService; private final ConnectorInfoService connectorInfoService; public StationStatJob(ChargeOrderService chargeOrderService, StationStatDayService stationStatDayService, StationStatMonthService stationStatMonthService, ConnectorInfoService connectorInfoService) { this.chargeOrderService = chargeOrderService; this.dayService = stationStatDayService; this.monthService = stationStatMonthService; this.connectorInfoService = connectorInfoService; } // 每天14:30执行一次,通过charge_app.t_charge_order表统计start_time为前一天且充电金额大于100的充电订单,分别统计充电人数、订单数、充电总费用、充电总电量、平均充电电量、平均充电费用、平均订单费用、平均订单电量 /** * 日统计,每日下午14:30启动,统计上日数据 */ // @Scheduled(cron = "0 30 14 * * ?") // 定时每日下午14:30 private void dayStat() { log.info("执行站点日统计定时任务-开始"); var statDay = LocalDateTime.now().minusDays(1); var startTime = LocalDateTime.of(statDay.toLocalDate(), LocalTime.MIN); var endTime = LocalDateTime.of(statDay.toLocalDate(), LocalTime.MAX); var chargeOrderList = getChargeOrders(startTime, endTime); dayService.saveBatch((Collection) getStationStat(chargeOrderList, true)); log.info("执行站点日统计定时任务-结束"); } /** * 月统计,每月第一天下午15:00启动,统计上月数据 */ // @Scheduled(cron = "0 0 15 1 * ?") private void monthStat() { log.info("执行站点月统计定时任务-开始"); var statMonth = LocalDateTime.now().minusMonths(1); var startTime = statMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); var endTime = statMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); var chargeOrderList = getChargeOrders(startTime, endTime); monthService.saveBatch((Collection) getStationStat(chargeOrderList, false)); log.info("执行站点月统计定时任务-结束"); } // 只执行一次 // @PostConstruct private void init() { log.info("执行站点初始化定时任务-开始"); // 2023-08-01 00:00:00 // var statMonth = LocalDateTime.of(2023, 12, 1, 0, 0); // var endMonth = LocalDateTime.of(2023, 12, 1, 0, 0); // var startTime = statMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); // var endTime = endMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); // var chargeOrderList = getChargeOrders(startTime, endTime); // monthService.saveBatch((Collection) getStationStat(chargeOrderList, false)); // // // 将订单按照日为单位分组 // var chargeOrderMap = chargeOrderList.stream().collect(Collectors.groupingBy(order -> // order.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) // )); // // chargeOrderMap按照key升序排序 // var chargeOrderMapSort = chargeOrderMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect( // Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)); // // chargeOrderMapSort.forEach((k, v) -> dayService.saveBatch(((Collection) getStationStat(v, true)))); log.info("执行站点初始化定时任务-结束"); } private List getChargeOrders(LocalDateTime startTime, LocalDateTime endTime) { // 通过charge_app.t_charge_order表统计start_time为前一天且充电金额大于0的有效充电订单 var res = chargeOrderService.lambdaQuery() .ge(ChargeOrder::getStartTime, startTime) .le(ChargeOrder::getStartTime, endTime) .gt(ChargeOrder::getTotalMoney, 0) .list(); return res; } private Collection getStationStat(List chargeOrderList, boolean isDayStat) { // 统计每个站点的connector_id数量 var stationConnectorCountMap = connectorInfoService.list().stream().collect(Collectors.groupingBy(ConnectorInfo::getStationId, Collectors.counting())); // 将chargeOrderList按照stationId分组 var chargeOrderMap = chargeOrderList.stream().collect(Collectors.groupingBy(ChargeOrder::getStationId)); // 根据分组计算每个站点的充电人数、订单数、充电总费用、充电总电量、平均充电电量、平均充电费用、平均订单费用、平均订单电量 return chargeOrderMap.entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, entry -> { var chargeOrders = entry.getValue(); var chargeUsers = chargeOrders.stream().collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ChargeOrder::getUserId))), ArrayList::new)).size(); var totalMoney = chargeOrders.stream().mapToInt(ChargeOrder::getTotalMoney).sum(); var elecMoney = chargeOrders.stream().mapToInt(ChargeOrder::getElecMoney).sum(); var serviceMoney = chargeOrders.stream().mapToInt(ChargeOrder::getServiceMoney).sum(); var serviceMoneyDiscount = chargeOrders.stream().mapToInt(ChargeOrder::getServiceMoneyDiscount).sum(); var discountAmount = chargeOrders.stream().mapToInt(ChargeOrder::getDiscountAmount).sum(); var totalPower = chargeOrders.stream().mapToDouble(ChargeOrder::getTotalPower).sum(); var avgPower = chargeOrders.stream().mapToDouble(ChargeOrder::getTotalPower).average().orElse(0.0); var avgOrderMoney = chargeOrders.stream().mapToInt(ChargeOrder::getTotalMoney).average().orElse(0); // 充电桩使用率 = 有订单的充电桩数量 / 总充电桩数 var connectorUsageRate = (double) chargeOrders.stream().filter(CommUtil.distinctByKey(ChargeOrder::getConnectorId)).count() / stationConnectorCountMap.get(entry.getKey()); ; return isDayStat ? new StationStatDay() .setStationId(entry.getKey()) .setStatDay(DateUtil.format(LocalDateTime.now().minusDays(1), "yyyy-MM-dd")) .setChargeUsers(chargeUsers) .setValidOrders(chargeOrders.size()) .setTotalMoney(totalMoney) .setElecMoney(elecMoney) .setServiceMoney(serviceMoney) .setServiceMoneyDiscount(serviceMoneyDiscount) .setDiscountAmount(discountAmount) .setTotalPower(totalPower) .setAvgOrderElec(avgPower) .setAvgOrderMoney((int) avgOrderMoney) .setAvgConnectorElec(totalPower / (stationConnectorCountMap.get(entry.getKey()))) .setConnectorUsageRate(connectorUsageRate) : new StationStatMonth() .setStationId(entry.getKey()) .setStatMonth(DateUtil.format(LocalDateTime.now().minusMonths(1), "yyyy-MM")) .setChargeUsers(chargeUsers) .setValidOrders(chargeOrders.size()) .setTotalMoney(totalMoney) .setElecMoney(elecMoney) .setServiceMoney(serviceMoney) .setServiceMoneyDiscount(serviceMoneyDiscount) .setDiscountAmount(discountAmount) .setTotalPower(totalPower) .setAvgOrderElec(avgPower) .setAvgOrderMoney((int) avgOrderMoney) .setAvgConnectorElec(totalPower / (stationConnectorCountMap.get(entry.getKey()) * (LocalDate.now().minusMonths(1).lengthOfMonth()))); })).values(); } }