| | |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.List; |
| | | import java.util.concurrent.CompletableFuture; |
| | | import java.util.concurrent.ExecutorService; |
| | | import java.util.concurrent.Executors; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import com.ruoyi.common.core.utils.DateUtils; |
| | |
| | | @Resource |
| | | private TChargingOrderMapper chargingOrderMapper; |
| | | |
| | | /** 尖峰平谷反查时分块 IN 的上限,避免超大 IN 触发 MyCat 读超时。 */ |
| | | private static final int PHASE_ID_BATCH_SIZE = 5000; |
| | | /** 尖峰平谷反查分块 id 上限:MyCat/druid 对 IN 列表的解析路由成本随 id 数非线性增长 |
| | | * (实测 500 个约 5s、5000 个 70s+ 触发读超时;100 个内约 0.2s/块,约 2ms/个)。 */ |
| | | private static final int PHASE_ID_BATCH_SIZE = 100; |
| | | /** 尖峰平谷分块反查并发度:46k id 实测 ~20s,典型加盟商 <3s(串行 5000/块 >70s 读超时)。 */ |
| | | private static final ExecutorService PHASE_QUERY_EXECUTOR = Executors.newFixedThreadPool(16); |
| | | |
| | | @GetMapping("/reservationRecommendation") |
| | | @ApiOperation(value = "获取站点预约充电推荐时段", tags = {"管理后台-充电桩信息"}) |
| | |
| | | Map<Integer, BigDecimal> phase = new LinkedHashMap<>(); phase.put(1, BigDecimal.ZERO); phase.put(2, BigDecimal.ZERO); phase.put(3, BigDecimal.ZERO); phase.put(4, BigDecimal.ZERO); |
| | | if (siteIds != null && !siteIds.isEmpty() && range != null && range[0] != null && range[1] != null) { |
| | | List<Long> ids = chargingOrderMapper.franchiseeOrderIds(siteIds, range[0], range[1]); |
| | | for (int from = 0; from < ids.size(); from += PHASE_ID_BATCH_SIZE) { |
| | | List<Long> chunk = ids.subList(from, Math.min(from + PHASE_ID_BATCH_SIZE, ids.size())); |
| | | chargingOrderMapper.franchiseePhaseStatsByOrderIds(chunk).forEach(item -> { |
| | | if (item != null && item.getType() != null) { |
| | | phase.computeIfPresent(item.getType(), (key, total) -> total.add(value(item.getCapacity()))); |
| | | } |
| | | }); |
| | | if (!ids.isEmpty()) { |
| | | // 分块并行反查 strategy 表(分片键 charging_order_id):MyCat 按 id 路由成本约 2ms/个, |
| | | // 串行 5000/块在大范围下 70s+ 读超时;100/块 × 16 线程实测 46k id 约 20s(代理并发吞吐有限)。 |
| | | List<CompletableFuture<Void>> futures = new ArrayList<>(); |
| | | for (int from = 0; from < ids.size(); from += PHASE_ID_BATCH_SIZE) { |
| | | List<Long> chunk = ids.subList(from, Math.min(from + PHASE_ID_BATCH_SIZE, ids.size())); |
| | | futures.add(CompletableFuture.runAsync(() -> { |
| | | List<FranchiseePhaseStat> items = chargingOrderMapper.franchiseePhaseStatsByOrderIds(chunk); |
| | | if (items != null) { |
| | | synchronized (phase) { |
| | | items.forEach(item -> { |
| | | if (item != null && item.getType() != null) { |
| | | phase.computeIfPresent(item.getType(), (key, total) -> total.add(value(item.getCapacity()))); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }, PHASE_QUERY_EXECUTOR)); |
| | | } |
| | | CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); |
| | | } |
| | | } |
| | | String[] names = {"尖", "峰", "平", "谷"}; List<Map<String, Object>> data = new ArrayList<>(); |