| | |
| | | @PostMapping("/app/franchisee/income") |
| | | public R<Map<String, Object>> franchiseeIncome(@RequestBody(required = false) Map<String, Object> params) { |
| | | List<Integer> siteIds = franchiseeSiteIds(params); |
| | | LocalDate firstMonth = YearMonth.now().minusMonths(5).atDay(1); |
| | | LocalDate nextMonth = YearMonth.now().plusMonths(1).atDay(1); |
| | | List<FranchiseeDayStat> dayStats = franchiseeDayStats(siteIds, |
| | | new LocalDateTime[]{firstMonth.atStartOfDay(), nextMonth.atStartOfDay()}); |
| | | List<SixChargingDto> sixChargingDtos; |
| | | if (siteIds.isEmpty()) { |
| | | // 无授权站点:返回近6月全零骨架,避免空 IN 导致全站点统计 |
| | | sixChargingDtos = generateLastSixMonths(LocalDate.now()); |
| | | } else { |
| | | // 与后台 数据分析-平台总收入分析 电站收入分析口径一致:历史月读结算账单、当月补原始订单 |
| | | LocalDate sixBefore = PreviousSixMonths.get(); |
| | | List<SixChargingDto> chargingDtos = chargingOrderService.charge(sixBefore, siteIds, false); |
| | | LocalDate statisticsEndMonth = resolveStatisticsEndMonth(LocalDate.now(), chargingDtos); |
| | | sixChargingDtos = generateLastSixMonths(statisticsEndMonth); |
| | | for (SixChargingDto sixChargingDto : sixChargingDtos) { |
| | | for (SixChargingDto chargingDto : chargingDtos) { |
| | | if (sixChargingDto.getMonth().equals(chargingDto.getMonth())) { |
| | | BeanUtils.copyProperties(chargingDto, sixChargingDto); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | BigDecimal electricity = sixChargingDtos.stream() |
| | | .filter(Objects::nonNull) |
| | | .map(SixChargingDto::getElectrovalence) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal service = sixChargingDtos.stream() |
| | | .filter(Objects::nonNull) |
| | | .map(SixChargingDto::getServiceCharge) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("totalIncome", sum(dayStats, FranchiseeDayStat::getPaymentAmount)); |
| | | result.put("electricityFee", sum(dayStats, FranchiseeDayStat::getElectrovalence)); |
| | | result.put("serviceFee", sum(dayStats, FranchiseeDayStat::getServiceFee)); |
| | | result.put("incomeColumn", franchiseeIncomeTrend(dayStats, firstMonth, 6)); |
| | | result.put("totalIncome", electricity.add(service)); |
| | | result.put("electricityFee", electricity); |
| | | result.put("serviceFee", service); |
| | | result.put("incomeColumn", franchiseeIncomeTrendFromSix(sixChargingDtos)); |
| | | List<Map<String, Object>> incomePie = new ArrayList<>(); |
| | | Map<String, Object> electricityItem = new LinkedHashMap<>(); |
| | | electricityItem.put("name", "充电费"); |
| | | electricityItem.put("value", sum(dayStats, FranchiseeDayStat::getElectrovalence)); |
| | | electricityItem.put("value", electricity); |
| | | incomePie.add(electricityItem); |
| | | Map<String, Object> serviceItem = new LinkedHashMap<>(); |
| | | serviceItem.put("name", "服务费"); |
| | | serviceItem.put("value", sum(dayStats, FranchiseeDayStat::getServiceFee)); |
| | | serviceItem.put("value", service); |
| | | incomePie.add(serviceItem); |
| | | result.put("incomePie", incomePie); |
| | | return R.ok(result); |
| | |
| | | public R<Map<String, Object>> franchiseeOperation(@RequestBody(required = false) Map<String, Object> params) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | List<Integer> siteIds = franchiseeSiteIds(params); |
| | | List<TChargingPile> piles = Collections.emptyList(); |
| | | if (!siteIds.isEmpty()) { R<List<TChargingPile>> pileResult = chargingPileClient.getChargingPileBySiteIds(siteIds); if (pileResult != null && pileResult.getData() != null) piles = pileResult.getData(); } |
| | | List<TChargingGun> guns = Collections.emptyList(); |
| | | List<Integer> pileIds = piles.stream().map(TChargingPile::getId).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | if (!pileIds.isEmpty()) { |
| | | GetChargingGunByChargingPileIdsVo query = new GetChargingGunByChargingPileIdsVo(); query.setChargingPileIds(pileIds); |
| | | R<List<TChargingGun>> gunResult = chargingGunClient.getChargingGunByChargingPileIds(query); |
| | | if (gunResult != null && gunResult.getData() != null) guns = gunResult.getData(); |
| | | } |
| | | BigDecimal total = BigDecimal.valueOf(guns.size()); |
| | | BigDecimal available = BigDecimal.valueOf(guns.stream().filter(this::isAvailableGun).count()); |
| | | BigDecimal fault = countGuns(guns, 7), offline = countGuns(guns, 1); |
| | | LocalDateTime[] range = franchiseeRange(params); |
| | | List<FranchiseeDayStat> operationStats = franchiseeDayStats(siteIds, range); |
| | | BigDecimal powerSatisfaction = demandPowerSatisfaction(operationStats); |
| | | Map<Integer, FranchiseeGunSat> gunSatMap = Collections.emptyMap(); |
| | | if (!siteIds.isEmpty() && range[0] != null && range[1] != null) { |
| | | gunSatMap = chargingOrderMapper.franchiseeGunSatisfaction(siteIds, range[0], range[1]).stream() |
| | | .filter(sat -> sat != null && sat.getChargingGunId() != null) |
| | | .collect(Collectors.toMap(FranchiseeGunSat::getChargingGunId, sat -> sat, (left, right) -> left)); |
| | | ChargingStatisticsQueryDto query = new ChargingStatisticsQueryDto(); |
| | | String dateType = params == null ? "today" : String.valueOf(params.getOrDefault("dateType", "today")); |
| | | int dayType = "week".equals(dateType) ? 2 : "month".equals(dateType) ? 3 : "year".equals(dateType) ? 4 : "custom".equals(dateType) ? 5 : 1; |
| | | query.setDayType(dayType); |
| | | if (dayType == 5) { |
| | | query.setStartTime(range[0].toLocalDate()); |
| | | query.setEndTime(range[1].toLocalDate().minusDays(1)); |
| | | } |
| | | Map<String, Object> summary = new LinkedHashMap<>(); summary.put("availableRate", rate(available, total)); summary.put("faultRate", rate(fault, total)); summary.put("offlineRate", rate(offline, total)); summary.put("powerSatisfactionRate", powerSatisfaction); |
| | | result.put("summary", summary); result.put("dcCharts", operationCharts(guns, gunSatMap, true)); result.put("acCharts", operationCharts(guns, gunSatMap, false)); |
| | | |
| | | List<Map<String, Object>> dcAvailable = safeList(chargingOrderService.equipmentUserType1(siteIds, query)); |
| | | List<Map<String, Object>> acAvailable = safeList(chargingOrderService.equipmentUserType2(siteIds, query)); |
| | | List<Map<String, Object>> dcFault = safeList(chargingOrderService.equipmentMapbroke1(siteIds, query)); |
| | | List<Map<String, Object>> acFault = safeList(chargingOrderService.equipmentMapbroke2(siteIds, query)); |
| | | List<Map<String, Object>> dcOffline = safeList(chargingOrderService.equipmentMapOut1(siteIds, query)); |
| | | List<Map<String, Object>> acOffline = safeList(chargingOrderService.equipmentMapOut2(siteIds, query)); |
| | | List<Map<String, Object>> dcSatisfaction = safeList(chargingOrderService.needElec(siteIds, query)); |
| | | List<Map<String, Object>> acSatisfaction = safeList(chargingOrderService.needElec1(siteIds, query)); |
| | | normalizeOperationPercents(dcAvailable); |
| | | normalizeOperationPercents(acAvailable); |
| | | normalizeOperationPercents(dcFault); |
| | | normalizeOperationPercents(acFault); |
| | | normalizeOperationPercents(dcOffline); |
| | | normalizeOperationPercents(acOffline); |
| | | normalizeOperationPercents(dcSatisfaction); |
| | | normalizeOperationPercents(acSatisfaction); |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("availableRate", calculateAveragePercent(dcAvailable, acAvailable)); |
| | | summary.put("faultRate", calculateAveragePercent(dcFault, acFault)); |
| | | summary.put("offlineRate", calculateAveragePercent(dcOffline, acOffline)); |
| | | summary.put("powerSatisfactionRate", calculateAveragePercent(dcSatisfaction, acSatisfaction)); |
| | | result.put("summary", summary); |
| | | result.put("dcCharts", Arrays.asList( |
| | | operationChart(dcAvailable, "可用率"), |
| | | operationChart(dcFault, "故障率"), |
| | | operationChart(dcOffline, "离网率"), |
| | | operationChart(dcSatisfaction, "需求电流满足率"))); |
| | | result.put("acCharts", Arrays.asList( |
| | | operationChart(acAvailable, "可用率"), |
| | | operationChart(acFault, "故障率"), |
| | | operationChart(acOffline, "离网率"), |
| | | operationChart(acSatisfaction, "需求电流满足率"))); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | private Map<String, Object> operationChart(List<Map<String, Object>> rows, String name) { |
| | | List<String> categories = rows.stream().map(row -> { |
| | | String gunName = row.get("name") == null ? "" : String.valueOf(row.get("name")); |
| | | return StringUtils.isBlank(gunName) ? "未命名充电枪" : gunName; |
| | | }).collect(Collectors.toList()); |
| | | List<Object> values = rows.stream().map(row -> row.getOrDefault("percent", "0")).collect(Collectors.toList()); |
| | | Map<String, Object> chart = new LinkedHashMap<>(); |
| | | chart.put("categories", categories); |
| | | Map<String, Object> series = new LinkedHashMap<>(); |
| | | series.put("name", name); |
| | | series.put("data", values); |
| | | chart.put("series", Collections.singletonList(series)); |
| | | return chart; |
| | | } |
| | | |
| | | private void normalizeOperationPercents(List<Map<String, Object>> rows) { |
| | | for (Map<String, Object> row : rows) { |
| | | Object raw = row.get("percent"); |
| | | try { |
| | | BigDecimal percent = raw == null ? BigDecimal.ZERO : new BigDecimal(String.valueOf(raw)); |
| | | row.put("percent", percent.abs().toPlainString()); |
| | | } catch (NumberFormatException ignored) { |
| | | row.put("percent", "0"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 加盟商看板按日聚合(siteIds 为空直接返回空,避免空 IN)。 */ |
| | |
| | | List<Map<String, Object>> data = new ArrayList<>(); for (int i = 0; i < names.length; i++) { Map<String, Object> item = new LinkedHashMap<>(); item.put("name", names[i]); item.put("value", counts[i]); data.add(item); } |
| | | return Collections.singletonMap("series", Collections.singletonList(Collections.singletonMap("data", data))); |
| | | } |
| | | private Map<String, Object> franchiseeIncomeTrend(List<FranchiseeDayStat> stats, LocalDate firstMonth, int monthCount) { |
| | | /** 收支分析近6月柱状图:与后台 电站收入分析 一致(历史月结算账单 + 当月原始订单)。 */ |
| | | private Map<String, Object> franchiseeIncomeTrendFromSix(List<SixChargingDto> months) { |
| | | List<String> categories = new ArrayList<>(); |
| | | List<BigDecimal> electricity = new ArrayList<>(), service = new ArrayList<>(); |
| | | Map<YearMonth, List<FranchiseeDayStat>> byMonth = stats.stream().filter(stat -> stat != null && stat.getStatDay() != null) |
| | | .collect(Collectors.groupingBy(stat -> YearMonth.from(stat.getStatDay()))); |
| | | for (int i = 0; i < monthCount; i++) { |
| | | YearMonth month = YearMonth.from(firstMonth).plusMonths(i); |
| | | categories.add(String.format("%02d月", month.getMonthValue())); |
| | | List<FranchiseeDayStat> monthStats = byMonth.getOrDefault(month, Collections.emptyList()); |
| | | electricity.add(sum(monthStats, FranchiseeDayStat::getElectrovalence)); |
| | | service.add(sum(monthStats, FranchiseeDayStat::getServiceFee)); |
| | | for (SixChargingDto month : months) { |
| | | if (month == null) { |
| | | continue; |
| | | } |
| | | categories.add(month.getMonth() + "月"); |
| | | electricity.add(month.getElectrovalence() == null ? BigDecimal.ZERO : month.getElectrovalence()); |
| | | service.add(month.getServiceCharge() == null ? BigDecimal.ZERO : month.getServiceCharge()); |
| | | } |
| | | Map<String, Object> chart = new LinkedHashMap<>(); chart.put("categories", categories); |
| | | Map<String, Object> electricitySeries = new LinkedHashMap<>(); electricitySeries.put("name", "充电费"); electricitySeries.put("data", electricity); |
| | | Map<String, Object> serviceSeries = new LinkedHashMap<>(); serviceSeries.put("name", "服务费"); serviceSeries.put("data", service); |
| | | chart.put("series", new ArrayList<>(Arrays.asList(electricitySeries, serviceSeries))); return chart; |
| | | Map<String, Object> chart = new LinkedHashMap<>(); |
| | | chart.put("categories", categories); |
| | | Map<String, Object> electricitySeries = new LinkedHashMap<>(); |
| | | electricitySeries.put("name", "充电费"); |
| | | electricitySeries.put("data", electricity); |
| | | Map<String, Object> serviceSeries = new LinkedHashMap<>(); |
| | | serviceSeries.put("name", "服务费"); |
| | | serviceSeries.put("data", service); |
| | | chart.put("series", new ArrayList<>(Arrays.asList(electricitySeries, serviceSeries))); |
| | | return chart; |
| | | } |
| | | |
| | | |