From 37f52097847f9025589c79034be2454432c55433 Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期五, 02 十月 2026 10:12:27 +0800
Subject: [PATCH] docs(交接): 记录关机前开发服务已停止
---
traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayStatService.java | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 297 insertions(+), 0 deletions(-)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayStatService.java b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayStatService.java
new file mode 100644
index 0000000..8ccd624
--- /dev/null
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayStatService.java
@@ -0,0 +1,297 @@
+package com.trafficaudit.holiday.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.trafficaudit.dataimport.entity.ImportBatch;
+import com.trafficaudit.dataimport.mapper.ImportBatchMapper;
+import com.trafficaudit.holiday.HolidayConstants;
+import com.trafficaudit.holiday.HolidayNumberUtils;
+import com.trafficaudit.holiday.entity.HolidayCalendar;
+import com.trafficaudit.holiday.entity.HolidayPassengerFlow;
+import com.trafficaudit.holiday.entity.HolidayVehicleFlow;
+import com.trafficaudit.holiday.mapper.HolidayPassengerFlowMapper;
+import com.trafficaudit.holiday.mapper.HolidayVehicleFlowMapper;
+import lombok.Data;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class HolidayStatService {
+
+ @Resource
+ private HolidayConfigService configService;
+ @Resource
+ private HolidayWorkbookService workbookService;
+ @Resource
+ private HolidayPassengerFlowMapper passengerFlowMapper;
+ @Resource
+ private HolidayVehicleFlowMapper vehicleFlowMapper;
+ @Resource
+ private ImportBatchMapper importBatchMapper;
+
+ public Map<String, Object> overview(int year, String holidayType) {
+ List<HolidayCalendar> calendar = configService.findHolidayDays(year, holidayType);
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("year", year);
+ result.put("holidayType", holidayType);
+ result.put("holidayTypeLabel", HolidayConstants.HOLIDAY_TYPE_LABELS.get(holidayType));
+ result.put("days", calendar);
+ result.put("rows", buildOverviewRows(year, holidayType, calendar));
+ result.put("templateFileName", workbookService.referenceFileName());
+ return result;
+ }
+
+ @Transactional
+ public Map<String, Object> importWorkbook(MultipartFile file, int year, String holidayType, boolean allowHistoricalOverwrite) {
+ HolidayWorkbookService.ImportSummary summary = workbookService.importWorkbook(file, year, holidayType, allowHistoricalOverwrite);
+ if (summary.isConfirmationRequired()) {
+ return importResult(summary);
+ }
+ ImportBatch batch = new ImportBatch();
+ batch.setFileName(file == null ? null : file.getOriginalFilename());
+ batch.setImportType("HOLIDAY_FLOW");
+ batch.setReportPeriod(year + "-" + holidayType);
+ batch.setTotalRows(summary.getSuccessRows() + summary.getFailRows());
+ batch.setSuccessRows(summary.getSuccessRows());
+ batch.setFailRows(summary.getFailRows());
+ batch.setErrorDetail(joinErrors(summary.getErrors()));
+ batch.setCreatedAt(LocalDateTime.now());
+ importBatchMapper.insert(batch);
+
+ return importResult(summary);
+ }
+
+ private Map<String, Object> importResult(HolidayWorkbookService.ImportSummary summary) {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("passengerRows", summary.getPassengerRows());
+ result.put("vehicleRows", summary.getVehicleRows());
+ result.put("skippedRows", summary.getSkippedRows());
+ result.put("successRows", summary.getSuccessRows());
+ result.put("failRows", summary.getFailRows());
+ result.put("errors", summary.getErrors());
+ result.put("confirmationRequired", summary.isConfirmationRequired());
+ result.put("historicalChanges", summary.getHistoricalChanges());
+ return result;
+ }
+
+ public byte[] exportWorkbook(int year, String holidayType) {
+ return workbookService.exportWorkbook(year, holidayType);
+ }
+
+ public List<Map<String, Object>> buildOverviewRows(int year, String holidayType, List<HolidayCalendar> calendar) {
+ Map<LocalDate, Map<String, BigDecimal>> passengerByDate = loadPassengerByDate(year);
+ Map<LocalDate, Map<String, BigDecimal>> vehicleByDate = loadVehicleByDate(year);
+ Map<LocalDate, BigDecimal> comparisonBaseByDate = loadComparisonBaseByDate(year);
+ BigDecimal coefficient = configService.coefficientOf(year, HolidayConstants.CATEGORY_HOLIDAY);
+ List<Map<String, Object>> rows = new ArrayList<>();
+ BigDecimal previousNationalProvincial = null;
+ for (HolidayCalendar day : calendar) {
+ Map<String, BigDecimal> passenger = passengerByDate.getOrDefault(day.getHolidayDate(), Collections.emptyMap());
+ Map<String, BigDecimal> vehicle = vehicleByDate.getOrDefault(day.getHolidayDate(), Collections.emptyMap());
+ BigDecimal expressway = vehicle.get(HolidayConstants.ROAD_EXPRESSWAY);
+ BigDecimal nationalProvincial = vehicle.get(HolidayConstants.ROAD_NATIONAL_PROVINCIAL);
+ BigDecimal comparisonBase = comparisonBaseByDate.get(day.getHolidayDate());
+ BigDecimal nationalProvincialRingRatio = nationalProvincialRingRatio(
+ year, nationalProvincial, previousNationalProvincial, comparisonBase);
+ BigDecimal nonBusinessPersonFlow = null;
+ if (coefficient != null && (expressway != null || nationalProvincial != null)) {
+ nonBusinessPersonFlow = HolidayNumberUtils.multiply(
+ HolidayNumberUtils.sumNullable(expressway, nationalProvincial), coefficient);
+ }
+ Map<String, Object> row = new LinkedHashMap<>();
+ row.put("dayIndex", day.getDayIndex());
+ row.put("date", day.getHolidayDate());
+ row.put("rail", passenger.get(HolidayConstants.MODE_RAIL));
+ row.put("civilAviation", passenger.get(HolidayConstants.MODE_CIVIL_AVIATION));
+ row.put("road", passenger.get(HolidayConstants.MODE_ROAD));
+ row.put("water", passenger.get(HolidayConstants.MODE_WATER));
+ row.put("expressway", expressway);
+ row.put("nationalProvincial", nationalProvincial);
+ row.put("comparisonBase", comparisonBase);
+ row.put("nationalProvincialRingRatio", nationalProvincialRingRatio);
+ row.put("coefficient", coefficient);
+ row.put("nonBusinessPersonFlow", nonBusinessPersonFlow);
+ row.put("totalPersonFlow", totalPersonFlow(passenger, nonBusinessPersonFlow));
+ rows.add(row);
+ previousNationalProvincial = nationalProvincial;
+ }
+ return rows;
+ }
+
+ public DailyMetrics dailyMetrics(int year, String holidayType, int dayIndex) {
+ List<HolidayCalendar> calendar = configService.findHolidayDays(year, holidayType);
+ HolidayCalendar target = null;
+ for (HolidayCalendar day : calendar) {
+ if (day.getDayIndex() != null && day.getDayIndex() == dayIndex) {
+ target = day;
+ break;
+ }
+ }
+ if (target == null) return null;
+
+ Map<LocalDate, Map<String, BigDecimal>> passengerByDate = loadPassengerByDate(year);
+ Map<LocalDate, Map<String, BigDecimal>> vehicleByDate = loadVehicleByDate(year);
+ Map<LocalDate, BigDecimal> comparisonBaseByDate = loadComparisonBaseByDate(year);
+ Map<String, BigDecimal> passenger = passengerByDate.getOrDefault(target.getHolidayDate(), Collections.emptyMap());
+ Map<String, BigDecimal> vehicle = vehicleByDate.getOrDefault(target.getHolidayDate(), Collections.emptyMap());
+ BigDecimal coefficient = configService.coefficientOf(year, HolidayConstants.CATEGORY_HOLIDAY);
+ BigDecimal expressway = vehicle.get(HolidayConstants.ROAD_EXPRESSWAY);
+ BigDecimal nationalProvincial = vehicle.get(HolidayConstants.ROAD_NATIONAL_PROVINCIAL);
+ BigDecimal comparisonBase = comparisonBaseByDate.get(target.getHolidayDate());
+ BigDecimal nonBusiness = null;
+ if (coefficient != null && (expressway != null || nationalProvincial != null)) {
+ nonBusiness = HolidayNumberUtils.multiply(
+ HolidayNumberUtils.sumNullable(expressway, nationalProvincial), coefficient);
+ }
+
+ DailyMetrics metrics = new DailyMetrics();
+ metrics.setYear(year);
+ metrics.setHolidayType(holidayType);
+ metrics.setDayIndex(dayIndex);
+ metrics.setDate(target.getHolidayDate());
+ metrics.setRail(passenger.get(HolidayConstants.MODE_RAIL));
+ metrics.setCivilAviation(passenger.get(HolidayConstants.MODE_CIVIL_AVIATION));
+ metrics.setRoad(passenger.get(HolidayConstants.MODE_ROAD));
+ metrics.setWater(passenger.get(HolidayConstants.MODE_WATER));
+ metrics.setExpressway(expressway);
+ metrics.setNationalProvincial(nationalProvincial);
+ metrics.setComparisonBase(comparisonBase);
+ metrics.setCoefficient(coefficient);
+ metrics.setNonBusinessPersonFlow(nonBusiness);
+ metrics.setRoadPersonFlow(sumNullable(metrics.getRoad(), nonBusiness));
+ metrics.setTotalPersonFlow(totalPersonFlow(passenger, nonBusiness));
+ return metrics;
+ }
+
+ public DailyMetrics cumulativeMetrics(int year, String holidayType, int dayIndex) {
+ DailyMetrics cumulative = new DailyMetrics();
+ cumulative.setYear(year);
+ cumulative.setHolidayType(holidayType);
+ cumulative.setDayIndex(dayIndex);
+ for (int day = 1; day <= dayIndex; day++) {
+ DailyMetrics item = dailyMetrics(year, holidayType, day);
+ if (item == null) continue;
+ cumulative.setRail(sumNullable(cumulative.getRail(), item.getRail()));
+ cumulative.setCivilAviation(sumNullable(cumulative.getCivilAviation(), item.getCivilAviation()));
+ cumulative.setRoad(sumNullable(cumulative.getRoad(), item.getRoad()));
+ cumulative.setWater(sumNullable(cumulative.getWater(), item.getWater()));
+ cumulative.setExpressway(sumNullable(cumulative.getExpressway(), item.getExpressway()));
+ cumulative.setNationalProvincial(sumNullable(cumulative.getNationalProvincial(), item.getNationalProvincial()));
+ cumulative.setNonBusinessPersonFlow(sumNullable(cumulative.getNonBusinessPersonFlow(), item.getNonBusinessPersonFlow()));
+ cumulative.setRoadPersonFlow(sumNullable(cumulative.getRoadPersonFlow(), item.getRoadPersonFlow()));
+ cumulative.setTotalPersonFlow(sumNullable(cumulative.getTotalPersonFlow(), item.getTotalPersonFlow()));
+ }
+ return cumulative;
+ }
+
+
+ public DailyMetrics findMetricsByDate(LocalDate date) {
+ HolidayCalendar calendar = configService.findHolidayByDate(date);
+ if (calendar == null) return null;
+ return dailyMetrics(calendar.getYear(), calendar.getHolidayType(), calendar.getDayIndex());
+ }
+
+ private Map<LocalDate, Map<String, BigDecimal>> loadPassengerByDate(int year) {
+ Map<LocalDate, Map<String, BigDecimal>> result = new LinkedHashMap<>();
+ List<HolidayPassengerFlow> rows = passengerFlowMapper.selectList(
+ new LambdaQueryWrapper<HolidayPassengerFlow>().eq(HolidayPassengerFlow::getYear, year));
+ for (HolidayPassengerFlow row : rows) {
+ result.computeIfAbsent(row.getFlowDate(), key -> new LinkedHashMap<>())
+ .put(row.getTransportMode(), row.getPassengerCount());
+ }
+ return result;
+ }
+
+ private Map<LocalDate, Map<String, BigDecimal>> loadVehicleByDate(int year) {
+ Map<LocalDate, Map<String, BigDecimal>> result = new LinkedHashMap<>();
+ List<HolidayVehicleFlow> rows = vehicleFlowMapper.selectList(
+ new LambdaQueryWrapper<HolidayVehicleFlow>().eq(HolidayVehicleFlow::getYear, year));
+ for (HolidayVehicleFlow row : rows) {
+ result.computeIfAbsent(row.getFlowDate(), key -> new LinkedHashMap<>())
+ .put(row.getRoadCategory(), row.getVehicleCount());
+ }
+ return result;
+ }
+
+ private Map<LocalDate, BigDecimal> loadComparisonBaseByDate(int year) {
+ Map<LocalDate, BigDecimal> result = new LinkedHashMap<>();
+ List<HolidayVehicleFlow> rows = vehicleFlowMapper.selectList(
+ new LambdaQueryWrapper<HolidayVehicleFlow>()
+ .eq(HolidayVehicleFlow::getYear, year)
+ .eq(HolidayVehicleFlow::getRoadCategory, HolidayConstants.ROAD_NATIONAL_PROVINCIAL));
+ for (HolidayVehicleFlow row : rows) {
+ if (row.getComparisonBase() != null) result.put(row.getFlowDate(), row.getComparisonBase());
+ }
+ return result;
+ }
+
+ private BigDecimal totalPersonFlow(Map<String, BigDecimal> passenger, BigDecimal nonBusinessPersonFlow) {
+ BigDecimal total = nonBusinessPersonFlow;
+ total = sumNullable(total, passenger.get(HolidayConstants.MODE_ROAD));
+ total = sumNullable(total, passenger.get(HolidayConstants.MODE_RAIL));
+ total = sumNullable(total, passenger.get(HolidayConstants.MODE_CIVIL_AVIATION));
+ total = sumNullable(total, passenger.get(HolidayConstants.MODE_WATER));
+ return total;
+ }
+
+ private BigDecimal nationalProvincialRingRatio(int year, BigDecimal current,
+ BigDecimal previous, BigDecimal comparisonBase) {
+ if (current == null) return null;
+ if (HolidayConstants.supportsComparisonBase(year) && comparisonBase != null) {
+ return ratio(current, comparisonBase);
+ }
+ return previous == null ? null : ratio(current, previous);
+ }
+
+ private BigDecimal ratio(BigDecimal current, BigDecimal base) {
+ BigDecimal quotient = HolidayNumberUtils.divide(current, base);
+ return quotient == null ? null : HolidayNumberUtils.normalize(quotient.subtract(BigDecimal.ONE));
+ }
+
+ private BigDecimal sumNullable(BigDecimal left, BigDecimal right) {
+ return HolidayNumberUtils.sumNullable(left, right);
+ }
+
+ private String joinErrors(List<String> errors) {
+ if (errors == null || errors.isEmpty()) return null;
+ StringBuilder builder = new StringBuilder();
+ int limit = Math.min(errors.size(), 200);
+ for (int i = 0; i < limit; i++) {
+ if (i > 0) builder.append('\n');
+ builder.append(errors.get(i));
+ }
+ if (errors.size() > limit) {
+ builder.append("\n... ").append(errors.size() - limit).append(" more");
+ }
+ return builder.toString();
+ }
+
+ @Data
+ public static class DailyMetrics {
+ private int year;
+ private String holidayType;
+ private int dayIndex;
+ private LocalDate date;
+ private BigDecimal rail;
+ private BigDecimal civilAviation;
+ private BigDecimal road;
+ private BigDecimal water;
+ private BigDecimal expressway;
+ private BigDecimal nationalProvincial;
+ private BigDecimal comparisonBase;
+ private BigDecimal coefficient;
+ private BigDecimal nonBusinessPersonFlow;
+ private BigDecimal roadPersonFlow;
+ private BigDecimal totalPersonFlow;
+ }
+}
--
Gitblit v1.9.1