package com.trafficaudit.reportexport.calc;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.trafficaudit.common.util.RegionUtil;
|
import com.trafficaudit.dataimport.entity.FreightTurnoverImport;
|
import com.trafficaudit.dataimport.entity.H2032EnterpriseMonthly;
|
import com.trafficaudit.dataimport.entity.ScaleSplitTransport;
|
import com.trafficaudit.dataimport.mapper.FreightTurnoverImportMapper;
|
import com.trafficaudit.dataimport.mapper.H2032EnterpriseMonthlyMapper;
|
import com.trafficaudit.dataimport.mapper.ScaleSplitTransportMapper;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 货运口径计算服务(汇总大表数据驱动改造 M1-货运,2026-09-08)。
|
*
|
* 口径(决策基线 docs/确认回执汇总…_2026-09-07.md B1;2026-07 与已修正母版两张成品页
|
* 规上/规下 当月+累计 逐格回归 0 差异):
|
* 1. 规上货运量 = h2032_enterprise_monthly.freight_total(吨)按 17 市州汇总 ÷10000(万吨)。
|
* 2. 合计货运量 = freight_turnover_import.freight_mMM(当月)/ freight_m01..mMM(累计)。
|
* 3. 规下货运量 = 合计 − 规上(拆分表不存货运量列,不能直接取规下)。
|
* 4. 规上/合计周转量 = scale_split_transport MONTH|CUMULATIVE 的 above_scale_turnover / total_turnover
|
* (合计与 freight_turnover_import.turnover_mMM 同源,已核对一致);规下周转 = 合计 − 规上。
|
* 5. 同比(去年同口径)不在本服务计算:按 Q1 以母版缓存 2025 参照组为基准,由写回层填表时计算。
|
*
|
* 输出 key = RegionUtil 规范市州名(武汉市…神农架林区)+ PROVINCE(湖北省,全省行 = 17 市州之和,
|
* 与母版“全省行=Σ城市行”公式一致)。
|
*/
|
@Service
|
public class FreightCalc {
|
|
public static final String PROVINCE = "湖北省";
|
|
/** 单个期间(当月或累计)的货运量/周转量三口径指标;无数据为 null */
|
public static class FreightMetrics {
|
private final Double aboveFreight; // 规上货运量(万吨)
|
private final Double belowFreight; // 规下货运量(万吨)
|
private final Double totalFreight; // 规上+规下货运量(万吨)
|
private final Double aboveTurnover; // 规上货物周转量(万吨公里)
|
private final Double belowTurnover; // 规下货物周转量(万吨公里)
|
private final Double totalTurnover; // 规上+规下货物周转量(万吨公里)
|
|
public FreightMetrics(Double aboveFreight, Double belowFreight, Double totalFreight,
|
Double aboveTurnover, Double belowTurnover, Double totalTurnover) {
|
this.aboveFreight = aboveFreight;
|
this.belowFreight = belowFreight;
|
this.totalFreight = totalFreight;
|
this.aboveTurnover = aboveTurnover;
|
this.belowTurnover = belowTurnover;
|
this.totalTurnover = totalTurnover;
|
}
|
|
public Double getAboveFreight() { return aboveFreight; }
|
public Double getBelowFreight() { return belowFreight; }
|
public Double getTotalFreight() { return totalFreight; }
|
public Double getAboveTurnover() { return aboveTurnover; }
|
public Double getBelowTurnover() { return belowTurnover; }
|
public Double getTotalTurnover() { return totalTurnover; }
|
}
|
|
/** 全量货运矩阵:month=当月,cumulative=截至 period 月累计 */
|
public static class FreightMatrix {
|
private final String period;
|
private final Map<String, FreightMetrics> month;
|
private final Map<String, FreightMetrics> cumulative;
|
|
public FreightMatrix(String period, Map<String, FreightMetrics> month, Map<String, FreightMetrics> cumulative) {
|
this.period = period;
|
this.month = month;
|
this.cumulative = cumulative;
|
}
|
|
public String getPeriod() { return period; }
|
public Map<String, FreightMetrics> getMonth() { return month; }
|
public Map<String, FreightMetrics> getCumulative() { return cumulative; }
|
}
|
|
@Resource
|
private H2032EnterpriseMonthlyMapper h2032Mapper;
|
@Resource
|
private FreightTurnoverImportMapper freightTurnoverMapper;
|
@Resource
|
private ScaleSplitTransportMapper scaleSplitMapper;
|
|
/** 计算 period(如 2026-07)的当月与累计货运矩阵 */
|
public FreightMatrix calc(String period) {
|
int year = Integer.parseInt(period.substring(0, 4));
|
int month = Integer.parseInt(period.substring(5, 7));
|
|
// H2032 规上货运量(吨):市州 -> 月桶
|
Map<String, Map<Integer, Double>> h2032TonByCityMonth = new HashMap<>();
|
List<H2032EnterpriseMonthly> h2032List = h2032Mapper.selectList(
|
new LambdaQueryWrapper<H2032EnterpriseMonthly>().likeRight(H2032EnterpriseMonthly::getReportPeriod, year));
|
for (H2032EnterpriseMonthly rec : h2032List) {
|
String rp = rec.getReportPeriod();
|
if (rp == null || rp.compareTo(period) > 0) continue;
|
String city = RegionUtil.cityByCode(rec.getRegionCode());
|
if (city == null) continue;
|
double freight = rec.getFreightTotal() == null ? 0.0 : rec.getFreightTotal();
|
h2032TonByCityMonth.computeIfAbsent(city, k -> new HashMap<>())
|
.merge(Integer.parseInt(rp.substring(5, 7)), freight, Double::sum);
|
}
|
|
// freight_turnover_import:合计货运量/周转量(仅存城市行;湖北省行由求和得出)
|
Map<String, FreightTurnoverImport> ftMap = new HashMap<>();
|
for (FreightTurnoverImport e : freightTurnoverMapper.selectList(
|
new LambdaQueryWrapper<FreightTurnoverImport>()
|
.eq(FreightTurnoverImport::getReportPeriod, period))) {
|
String city = RegionUtil.normalizeCityName(e.getRegionName());
|
if (city != null && !PROVINCE.equals(city)) ftMap.put(city, e);
|
}
|
|
// scale_split_transport:规上/合计周转量
|
Map<String, ScaleSplitTransport> splitMonth = new HashMap<>();
|
Map<String, ScaleSplitTransport> splitCumulative = new HashMap<>();
|
for (ScaleSplitTransport rec : scaleSplitMapper.selectList(
|
new LambdaQueryWrapper<ScaleSplitTransport>()
|
.eq(ScaleSplitTransport::getReportPeriod, period))) {
|
String city = RegionUtil.normalizeCityName(rec.getRegionName());
|
if (city == null || PROVINCE.equals(city)) continue;
|
if ("MONTH".equals(rec.getPeriodType())) splitMonth.put(city, rec);
|
else if ("CUMULATIVE".equals(rec.getPeriodType())) splitCumulative.put(city, rec);
|
}
|
|
Map<String, FreightMetrics> monthMap = new LinkedHashMap<>();
|
Map<String, FreightMetrics> cumulativeMap = new LinkedHashMap<>();
|
for (String city : RegionUtil.cityList()) {
|
monthMap.put(city, buildMetrics(city, month, h2032TonByCityMonth, ftMap, splitMonth, false));
|
cumulativeMap.put(city, buildMetrics(city, month, h2032TonByCityMonth, ftMap, splitCumulative, true));
|
}
|
monthMap.put(PROVINCE, sumOf(monthMap));
|
cumulativeMap.put(PROVINCE, sumOf(cumulativeMap));
|
return new FreightMatrix(period, monthMap, cumulativeMap);
|
}
|
|
/** 单城市指标。cumulative=true 时 H2032 取 1..month 全桶、合计取 m01..mMM 之和;false 时只取当月。 */
|
private FreightMetrics buildMetrics(String city, int month,
|
Map<String, Map<Integer, Double>> h2032TonByCityMonth,
|
Map<String, FreightTurnoverImport> ftMap,
|
Map<String, ScaleSplitTransport> splitMap,
|
boolean cumulative) {
|
Double aboveFreight = null;
|
Map<Integer, Double> h2032Buckets = h2032TonByCityMonth.get(city);
|
if (h2032Buckets != null) {
|
if (cumulative) {
|
double ton = 0.0;
|
for (int m = 1; m <= month; m++) ton += h2032Buckets.getOrDefault(m, 0.0);
|
aboveFreight = ton / 10000.0;
|
} else {
|
// 当月无规上记录 -> 留空(不再写 0);有记录(含真实 0 值)才写数
|
Double ton = h2032Buckets.get(month);
|
aboveFreight = ton == null ? null : ton / 10000.0;
|
}
|
}
|
|
FreightTurnoverImport ft = ftMap.get(city);
|
Double totalFreight = null;
|
Double totalTurnoverFromFt = null;
|
if (ft != null) {
|
if (cumulative) {
|
double f = 0.0, t = 0.0;
|
for (int m = 1; m <= month; m++) {
|
f += monthField(ft, m, true);
|
t += monthField(ft, m, false);
|
}
|
totalFreight = f;
|
totalTurnoverFromFt = t;
|
} else {
|
totalFreight = monthField(ft, month, true);
|
totalTurnoverFromFt = monthField(ft, month, false);
|
}
|
}
|
|
ScaleSplitTransport split = splitMap.get(city);
|
Double aboveTurnover = split == null ? null : split.getAboveScaleTurnover();
|
Double totalTurnover = split != null && split.getTotalTurnover() != null
|
? split.getTotalTurnover() : totalTurnoverFromFt;
|
|
Double belowFreight = totalFreight == null || aboveFreight == null ? null : totalFreight - aboveFreight;
|
Double belowTurnover = totalTurnover == null ? null
|
: totalTurnover - (aboveTurnover == null ? 0.0 : aboveTurnover);
|
return new FreightMetrics(aboveFreight, belowFreight, totalFreight,
|
aboveTurnover, belowTurnover, totalTurnover);
|
}
|
|
/** 湖北省行 = 17 市州之和(与母版全省行 Σ 公式一致;全市州无数据时输出 0) */
|
private FreightMetrics sumOf(Map<String, FreightMetrics> byCity) {
|
double af = 0.0, bf = 0.0, tf = 0.0, at = 0.0, bt = 0.0, tt = 0.0;
|
for (String city : RegionUtil.cityList()) {
|
FreightMetrics m = byCity.get(city);
|
if (m == null) continue;
|
af += nz(m.getAboveFreight());
|
bf += nz(m.getBelowFreight());
|
tf += nz(m.getTotalFreight());
|
at += nz(m.getAboveTurnover());
|
bt += nz(m.getBelowTurnover());
|
tt += nz(m.getTotalTurnover());
|
}
|
return new FreightMetrics(af, bf, tf, at, bt, tt);
|
}
|
|
private double monthField(FreightTurnoverImport e, int m, boolean freight) {
|
switch (m) {
|
case 1: return freight ? nz(e.getFreightM01()) : nz(e.getTurnoverM01());
|
case 2: return freight ? nz(e.getFreightM02()) : nz(e.getTurnoverM02());
|
case 3: return freight ? nz(e.getFreightM03()) : nz(e.getTurnoverM03());
|
case 4: return freight ? nz(e.getFreightM04()) : nz(e.getTurnoverM04());
|
case 5: return freight ? nz(e.getFreightM05()) : nz(e.getTurnoverM05());
|
case 6: return freight ? nz(e.getFreightM06()) : nz(e.getTurnoverM06());
|
case 7: return freight ? nz(e.getFreightM07()) : nz(e.getTurnoverM07());
|
case 8: return freight ? nz(e.getFreightM08()) : nz(e.getTurnoverM08());
|
case 9: return freight ? nz(e.getFreightM09()) : nz(e.getTurnoverM09());
|
case 10: return freight ? nz(e.getFreightM10()) : nz(e.getTurnoverM10());
|
case 11: return freight ? nz(e.getFreightM11()) : nz(e.getTurnoverM11());
|
case 12: return freight ? nz(e.getFreightM12()) : nz(e.getTurnoverM12());
|
default: return 0.0;
|
}
|
}
|
|
private double nz(Double v) { return v == null ? 0.0 : v; }
|
}
|