fix: 增加年度导出
This commit is contained in:
parent
29105cf1f5
commit
b19db9e8d2
@ -47,20 +47,29 @@ public class QgcExportController {
|
|||||||
@RequestParam(required = false) String sysId,
|
@RequestParam(required = false) String sysId,
|
||||||
@RequestParam(defaultValue = "true") boolean isDailyDimension,
|
@RequestParam(defaultValue = "true") boolean isDailyDimension,
|
||||||
@RequestParam(defaultValue = "false") boolean isCalcQec,
|
@RequestParam(defaultValue = "false") boolean isCalcQec,
|
||||||
|
@RequestParam(required = false) String yearStatType,
|
||||||
|
@RequestParam(required = false) String year,
|
||||||
|
@RequestParam(required = false) String baseId,
|
||||||
HttpServletResponse response) {
|
HttpServletResponse response) {
|
||||||
|
|
||||||
// 解析逗号分隔参数
|
// 解析逗号分隔参数
|
||||||
List<String> dataFields = parseCsv(dataField);
|
List<String> dataFields = parseCsv(dataField);
|
||||||
List<String> monthList = parseCsv(months);
|
List<String> monthList = parseCsv(months);
|
||||||
List<String> stationList = parseCsv(stcd);
|
List<String> stationList = parseCsv(stcd);
|
||||||
|
List<String> baseIdList = parseCsv(baseId);
|
||||||
|
|
||||||
// if (dataFields.isEmpty()) {
|
|
||||||
// dataFields = Arrays.asList("v", "q", "z");
|
|
||||||
// }
|
|
||||||
if (tmDimension == null || tmDimension.isEmpty()) {
|
if (tmDimension == null || tmDimension.isEmpty()) {
|
||||||
tmDimension = "month";
|
tmDimension = "month";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 年度导出分支
|
||||||
|
if ("year".equals(tmDimension)) {
|
||||||
|
List<String> yearStatTypeList = parseCsv(yearStatType);
|
||||||
|
List<String> yearList = parseCsv(year);
|
||||||
|
qgcExportService.exportYearData(yearStatTypeList, yearList, stationList, baseIdList, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qgcExportService.exportData(dataFields, monthList, stationList,
|
qgcExportService.exportData(dataFields, monthList, stationList,
|
||||||
isDailyDimension, tmDimension, response);
|
isDailyDimension, tmDimension, response);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.yfd.platform.qgc_export.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V_MS_STBPRP_T 视图扩展映射 — 用于年度导出时获取流域、基地等信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("V_MS_STBPRP_T")
|
||||||
|
public class StationInfo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("STCD")
|
||||||
|
private String stcd;
|
||||||
|
|
||||||
|
@TableField("STNM")
|
||||||
|
private String stnm;
|
||||||
|
|
||||||
|
@TableField("ENNM")
|
||||||
|
private String ennm;
|
||||||
|
|
||||||
|
@TableField("STTP")
|
||||||
|
private String sttp;
|
||||||
|
|
||||||
|
@TableField("RSTCD")
|
||||||
|
private String rstcd;
|
||||||
|
|
||||||
|
@TableField("BASE_ID")
|
||||||
|
private String baseId;
|
||||||
|
|
||||||
|
@TableField("BASE_NAME")
|
||||||
|
private String baseName;
|
||||||
|
|
||||||
|
@TableField("HBRVCD_NAME")
|
||||||
|
private String hbrvcdName;
|
||||||
|
|
||||||
|
@TableField("RVCD_NAME")
|
||||||
|
private String rvcdName;
|
||||||
|
|
||||||
|
@TableField("REACHCD")
|
||||||
|
private String reachcd;
|
||||||
|
|
||||||
|
@TableField("REACHCD_NAME")
|
||||||
|
private String reachcdName;
|
||||||
|
|
||||||
|
@TableField("HYNM")
|
||||||
|
private String hynm;
|
||||||
|
|
||||||
|
@TableField("MWAY")
|
||||||
|
private Integer mway;
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.yfd.platform.qgc_export.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yfd.platform.qgc_export.domain.StationInfo;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 年度统计导出 Mapper
|
||||||
|
*/
|
||||||
|
public interface YearStatMapper extends BaseMapper<StationInfo> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询生态流量月度达标数据(SD_QECDRTP_S DRTP='MON')
|
||||||
|
* 返回 STCD、三部分达标数
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> selectQecMonthlyCompliance(@Param("stcdList") List<String> stcdList,
|
||||||
|
@Param("year") int year);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询生态流量月度达标数据(按 BASE_ID 分组)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> selectQecMonthlyComplianceByBase(@Param("baseIdList") List<String> baseIdList,
|
||||||
|
@Param("year") int year);
|
||||||
|
}
|
||||||
@ -24,4 +24,19 @@ public interface IQgcExportService {
|
|||||||
boolean isDailyDimension,
|
boolean isDailyDimension,
|
||||||
String tmDimension,
|
String tmDimension,
|
||||||
HttpServletResponse response);
|
HttpServletResponse response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 年度统计导出(水质达标率 + 生态流量达标率)
|
||||||
|
*
|
||||||
|
* @param yearStatType 统计指标类型(wq, qec)
|
||||||
|
* @param years 年份列表(如 ["2026","2025"])
|
||||||
|
* @param stationCodes 电站编码列表(RSTCD)
|
||||||
|
* @param baseIds 水电基地编码列表(BASE_ID)
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
*/
|
||||||
|
void exportYearData(List<String> yearStatType,
|
||||||
|
List<String> years,
|
||||||
|
List<String> stationCodes,
|
||||||
|
List<String> baseIds,
|
||||||
|
HttpServletResponse response);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import com.yfd.platform.qgc_export.mapper.SdQecRMapper;
|
|||||||
import com.yfd.platform.qgc_export.mapper.SdQecdaySMapper;
|
import com.yfd.platform.qgc_export.mapper.SdQecdaySMapper;
|
||||||
import com.yfd.platform.qgc_export.mapper.StationMappingMapper;
|
import com.yfd.platform.qgc_export.mapper.StationMappingMapper;
|
||||||
import com.yfd.platform.qgc_export.mapper.MsAlongdetBMapper;
|
import com.yfd.platform.qgc_export.mapper.MsAlongdetBMapper;
|
||||||
|
import com.yfd.platform.qgc_export.mapper.YearStatMapper;
|
||||||
|
import com.yfd.platform.qgc_export.domain.StationInfo;
|
||||||
import com.yfd.platform.qgc_env.wt.mapper.SdWtvtRMapper;
|
import com.yfd.platform.qgc_env.wt.mapper.SdWtvtRMapper;
|
||||||
import com.yfd.platform.qgc_export.mapper.SdWtvtdaySMapper;
|
import com.yfd.platform.qgc_export.mapper.SdWtvtdaySMapper;
|
||||||
import com.yfd.platform.qgc_export.processor.*;
|
import com.yfd.platform.qgc_export.processor.*;
|
||||||
@ -156,13 +158,20 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
@Resource
|
@Resource
|
||||||
private RwtProcessor rwtProcessor;
|
private RwtProcessor rwtProcessor;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private YearStatMapper yearStatMapper;
|
||||||
|
|
||||||
private static final SimpleDateFormat SDF_TIME = new SimpleDateFormat("dd日 HH时");
|
private static final SimpleDateFormat SDF_TIME = new SimpleDateFormat("dd日 HH时");
|
||||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
/** 基础汇总指标(7个) */
|
/**
|
||||||
|
* 基础汇总指标(7个)
|
||||||
|
*/
|
||||||
private static final List<String> BASE_LABELS = Collections.unmodifiableList(
|
private static final List<String> BASE_LABELS = Collections.unmodifiableList(
|
||||||
Arrays.asList("日均最低", "日均最高", "月内最低", "月内最低时间", "月内最高", "月内最高时间", "月均"));
|
Arrays.asList("日均最低", "日均最高", "月内最低", "月内最低时间", "月内最高", "月内最高时间", "月均"));
|
||||||
/** 小时达标率标签 */
|
/**
|
||||||
|
* 小时达标率标签
|
||||||
|
*/
|
||||||
private static final String LABEL_COMPLIANCE = "小时达标率";
|
private static final String LABEL_COMPLIANCE = "小时达标率";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -233,10 +242,14 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
headers = new ArrayList<>();
|
headers = new ArrayList<>();
|
||||||
headers.add("测站名称");
|
headers.add("测站名称");
|
||||||
headers.add("水深(m)");
|
headers.add("水深(m)");
|
||||||
headers.add("5日"); headers.add("");
|
headers.add("5日");
|
||||||
headers.add("15日"); headers.add("");
|
headers.add("");
|
||||||
headers.add("25日"); headers.add("");
|
headers.add("15日");
|
||||||
headers.add("月均"); headers.add("");
|
headers.add("");
|
||||||
|
headers.add("25日");
|
||||||
|
headers.add("");
|
||||||
|
headers.add("月均");
|
||||||
|
headers.add("");
|
||||||
} else {
|
} else {
|
||||||
// 仅包含当前处理器 STTP 类型有对应测站的电站
|
// 仅包含当前处理器 STTP 类型有对应测站的电站
|
||||||
headers = new ArrayList<>();
|
headers = new ArrayList<>();
|
||||||
@ -400,16 +413,32 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
for (String rstcd : rstcdToStcd.keySet()) {
|
for (String rstcd : rstcdToStcd.keySet()) {
|
||||||
Aggregation agg = aggMap.get(rstcd);
|
Aggregation agg = aggMap.get(rstcd);
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0: row.add(formatOrEmpty(agg != null ? agg.getDailyMin() : null, decimals)); break;
|
case 0:
|
||||||
case 1: row.add(formatOrEmpty(agg != null ? agg.getDailyMax() : null, decimals)); break;
|
row.add(formatOrEmpty(agg != null ? agg.getDailyMin() : null, decimals));
|
||||||
case 2: row.add(formatOrEmpty(agg != null ? agg.getExtremeMin() : null, decimals)); break;
|
break;
|
||||||
case 3: row.add(agg != null && agg.getExtremeMinTime() != null
|
case 1:
|
||||||
? SDF_TIME.format(agg.getExtremeMinTime()) : ""); break;
|
row.add(formatOrEmpty(agg != null ? agg.getDailyMax() : null, decimals));
|
||||||
case 4: row.add(formatOrEmpty(agg != null ? agg.getExtremeMax() : null, decimals)); break;
|
break;
|
||||||
case 5: row.add(agg != null && agg.getExtremeMaxTime() != null
|
case 2:
|
||||||
? SDF_TIME.format(agg.getExtremeMaxTime()) : ""); break;
|
row.add(formatOrEmpty(agg != null ? agg.getExtremeMin() : null, decimals));
|
||||||
case 6: row.add(formatOrEmpty(agg != null ? agg.getMonthlyAvg() : null, decimals)); break;
|
break;
|
||||||
case 7: row.add(formatComplianceRate(agg != null ? agg.getComplianceRate() : null)); break;
|
case 3:
|
||||||
|
row.add(agg != null && agg.getExtremeMinTime() != null
|
||||||
|
? SDF_TIME.format(agg.getExtremeMinTime()) : "");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
row.add(formatOrEmpty(agg != null ? agg.getExtremeMax() : null, decimals));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
row.add(agg != null && agg.getExtremeMaxTime() != null
|
||||||
|
? SDF_TIME.format(agg.getExtremeMaxTime()) : "");
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
row.add(formatOrEmpty(agg != null ? agg.getMonthlyAvg() : null, decimals));
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
row.add(formatComplianceRate(agg != null ? agg.getComplianceRate() : null));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows.add(row);
|
rows.add(row);
|
||||||
@ -540,7 +569,11 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
|
|
||||||
private int toInt(Object v) {
|
private int toInt(Object v) {
|
||||||
if (v instanceof Number) return ((Number) v).intValue();
|
if (v instanceof Number) return ((Number) v).intValue();
|
||||||
try { return Integer.parseInt(v.toString()); } catch (Exception e) { return -1; }
|
try {
|
||||||
|
return Integer.parseInt(v.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== WQ 水质达标率 Sheet(双表头) ========================
|
// ======================== WQ 水质达标率 Sheet(双表头) ========================
|
||||||
@ -651,11 +684,16 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
List<List<Object>> rows = new ArrayList<>();
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
// 子标题行(Row 0)
|
// 子标题行(Row 0)
|
||||||
List<Object> subHeaderRow = new ArrayList<>();
|
List<Object> subHeaderRow = new ArrayList<>();
|
||||||
subHeaderRow.add(""); subHeaderRow.add("");
|
subHeaderRow.add("");
|
||||||
subHeaderRow.add("水温(℃)"); subHeaderRow.add("相应高程(m)");
|
subHeaderRow.add("");
|
||||||
subHeaderRow.add("水温(℃)"); subHeaderRow.add("相应高程(m)");
|
subHeaderRow.add("水温(℃)");
|
||||||
subHeaderRow.add("水温(℃)"); subHeaderRow.add("相应高程(m)");
|
subHeaderRow.add("相应高程(m)");
|
||||||
subHeaderRow.add("水温(℃)"); subHeaderRow.add("相应高程(m)");
|
subHeaderRow.add("水温(℃)");
|
||||||
|
subHeaderRow.add("相应高程(m)");
|
||||||
|
subHeaderRow.add("水温(℃)");
|
||||||
|
subHeaderRow.add("相应高程(m)");
|
||||||
|
subHeaderRow.add("水温(℃)");
|
||||||
|
subHeaderRow.add("相应高程(m)");
|
||||||
rows.add(subHeaderRow);
|
rows.add(subHeaderRow);
|
||||||
|
|
||||||
for (String key : sortedKeys) {
|
for (String key : sortedKeys) {
|
||||||
@ -855,7 +893,9 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分部达标统计 */
|
/**
|
||||||
|
* 分部达标统计
|
||||||
|
*/
|
||||||
private static class ComplianceStats {
|
private static class ComplianceStats {
|
||||||
String dailyHb, dailySl, dailyDn;
|
String dailyHb, dailySl, dailyDn;
|
||||||
String hourlyHb, hourlySl, hourlyDn;
|
String hourlyHb, hourlySl, hourlyDn;
|
||||||
@ -1011,7 +1051,10 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
}
|
}
|
||||||
if (defaultDisplayName == null) {
|
if (defaultDisplayName == null) {
|
||||||
for (StationMapping m : rows) {
|
for (StationMapping m : rows) {
|
||||||
if (m.getStnm() != null) { defaultDisplayName = m.getStnm(); break; }
|
if (m.getStnm() != null) {
|
||||||
|
defaultDisplayName = m.getStnm();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (defaultDisplayName == null) {
|
if (defaultDisplayName == null) {
|
||||||
@ -1157,7 +1200,9 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
|
|
||||||
// ======================== 日变幅格式(出入库水温日变幅) ========================
|
// ======================== 日变幅格式(出入库水温日变幅) ========================
|
||||||
|
|
||||||
/** 日变幅汇总标签 */
|
/**
|
||||||
|
* 日变幅汇总标签
|
||||||
|
*/
|
||||||
private static final List<String> VARIATION_LABELS = List.of("日变幅均值", "日变幅最大值");
|
private static final List<String> VARIATION_LABELS = List.of("日变幅均值", "日变幅最大值");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1270,16 +1315,26 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
// ======================== 内部数据类 ========================
|
// ======================== 内部数据类 ========================
|
||||||
|
|
||||||
private static class StationResolution {
|
private static class StationResolution {
|
||||||
/** 默认测站名称(fallback),RSTCD → displayName */
|
/**
|
||||||
|
* 默认测站名称(fallback),RSTCD → displayName
|
||||||
|
*/
|
||||||
final Map<String, String> displayNames = new LinkedHashMap<>();
|
final Map<String, String> displayNames = new LinkedHashMap<>();
|
||||||
/** 按 STTP 分组的测站名称,RSTCD → {STTP → displayName} */
|
/**
|
||||||
|
* 按 STTP 分组的测站名称,RSTCD → {STTP → displayName}
|
||||||
|
*/
|
||||||
final Map<String, Map<String, String>> sttpDisplayNames = new LinkedHashMap<>();
|
final Map<String, Map<String, String>> sttpDisplayNames = new LinkedHashMap<>();
|
||||||
/** STTP → STCD 映射,RSTCD → {STTP → STCD} */
|
/**
|
||||||
|
* STTP → STCD 映射,RSTCD → {STTP → STCD}
|
||||||
|
*/
|
||||||
final Map<String, Map<String, String>> sttpStcdMap = new LinkedHashMap<>();
|
final Map<String, Map<String, String>> sttpStcdMap = new LinkedHashMap<>();
|
||||||
/** 所属水电基地名称,RSTCD → {STTP → BASE_NAME} */
|
/**
|
||||||
|
* 所属水电基地名称,RSTCD → {STTP → BASE_NAME}
|
||||||
|
*/
|
||||||
final Map<String, Map<String, String>> baseNameMap = new LinkedHashMap<>();
|
final Map<String, Map<String, String>> baseNameMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
/** 获取默认测站名称 */
|
/**
|
||||||
|
* 获取默认测站名称
|
||||||
|
*/
|
||||||
String getDisplayName(String rstcd) {
|
String getDisplayName(String rstcd) {
|
||||||
return displayNames.getOrDefault(rstcd, rstcd);
|
return displayNames.getOrDefault(rstcd, rstcd);
|
||||||
}
|
}
|
||||||
@ -1307,7 +1362,9 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
return inner.get(sttp);
|
return inner.get(sttp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取指定 STTP 的水电基地名称 */
|
/**
|
||||||
|
* 获取指定 STTP 的水电基地名称
|
||||||
|
*/
|
||||||
String getBaseName(String rstcd, String sttp) {
|
String getBaseName(String rstcd, String sttp) {
|
||||||
Map<String, String> inner = baseNameMap.get(rstcd);
|
Map<String, String> inner = baseNameMap.get(rstcd);
|
||||||
if (inner != null && sttp != null) {
|
if (inner != null && sttp != null) {
|
||||||
@ -1334,10 +1391,510 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
private BigDecimal extremeMax;
|
private BigDecimal extremeMax;
|
||||||
private Date extremeMaxTime;
|
private Date extremeMaxTime;
|
||||||
private BigDecimal monthlyAvg;
|
private BigDecimal monthlyAvg;
|
||||||
/** 小时达标率(百分比,仅水质指标使用) */
|
/**
|
||||||
|
* 小时达标率(百分比,仅水质指标使用)
|
||||||
|
*/
|
||||||
private BigDecimal complianceRate;
|
private BigDecimal complianceRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======================== 年度统计导出 ========================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exportYearData(List<String> yearStatType, List<String> years,
|
||||||
|
List<String> stationCodes, List<String> baseIds,
|
||||||
|
HttpServletResponse response) {
|
||||||
|
if ((years == null || years.isEmpty()) || (stationCodes.isEmpty() && baseIds.isEmpty())) {
|
||||||
|
log.warn("年度导出需要传入年份和电站/基地编码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasWq = yearStatType.contains("wq");
|
||||||
|
boolean hasQec = yearStatType.contains("qec");
|
||||||
|
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||||
|
|
||||||
|
// 预查询测站信息(年维度无关,一次查询即可)
|
||||||
|
List<StationInfo> wqStations = null;
|
||||||
|
List<StationInfo> engStations = null;
|
||||||
|
if (hasWq && !stationCodes.isEmpty()) {
|
||||||
|
wqStations = yearStatMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<StationInfo>()
|
||||||
|
.eq(StationInfo::getSttp, "WQ")
|
||||||
|
.eq(StationInfo::getMway, 2)
|
||||||
|
.and(w -> w.in(StationInfo::getRstcd, stationCodes)
|
||||||
|
.or()
|
||||||
|
.in(StationInfo::getStcd, stationCodes))
|
||||||
|
);
|
||||||
|
if (!wqStations.isEmpty()) {
|
||||||
|
wqStations = sortStationsByOrder(wqStations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasQec && !stationCodes.isEmpty()) {
|
||||||
|
engStations = yearStatMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<StationInfo>()
|
||||||
|
.eq(StationInfo::getSttp, "ENG")
|
||||||
|
.and(w -> w.in(StationInfo::getRstcd, stationCodes)
|
||||||
|
.or()
|
||||||
|
.in(StationInfo::getStcd, stationCodes))
|
||||||
|
);
|
||||||
|
if (!engStations.isEmpty()) {
|
||||||
|
engStations = sortStationsByOrder(engStations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多年份:每年一个 Excel 文件
|
||||||
|
List<ExportZipUtil.ExcelData> excelList = new ArrayList<>();
|
||||||
|
for (String yearStr : years) {
|
||||||
|
int year = Integer.parseInt(yearStr);
|
||||||
|
List<ExportZipUtil.SheetData> sheets = new ArrayList<>();
|
||||||
|
|
||||||
|
// WQ
|
||||||
|
if (wqStations != null && !wqStations.isEmpty()) {
|
||||||
|
ExportZipUtil.SheetData wqSheet = buildYearWqSheet(wqStations, year);
|
||||||
|
if (wqSheet != null) sheets.add(wqSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// QEC 电站维度
|
||||||
|
if (engStations != null && !engStations.isEmpty()) {
|
||||||
|
ExportZipUtil.SheetData qecEpaSheet = buildYearQecStationSheet(engStations, year, "QEC");
|
||||||
|
ExportZipUtil.SheetData qecMwrSheet = buildYearQecStationSheet(engStations, year, "MWR");
|
||||||
|
ExportZipUtil.SheetData qecAvqSheet = buildYearQecStationSheet(engStations, year, "AVQ");
|
||||||
|
if (qecEpaSheet != null) sheets.add(qecEpaSheet);
|
||||||
|
if (qecMwrSheet != null) sheets.add(qecMwrSheet);
|
||||||
|
if (qecAvqSheet != null) sheets.add(qecAvqSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// QEC 水电基地维度
|
||||||
|
if (!baseIds.isEmpty()) {
|
||||||
|
ExportZipUtil.SheetData baseEpaSheet = buildYearQecBaseSheet(baseIds, year, "QEC");
|
||||||
|
ExportZipUtil.SheetData baseMwrSheet = buildYearQecBaseSheet(baseIds, year, "MWR");
|
||||||
|
ExportZipUtil.SheetData baseAvqSheet = buildYearQecBaseSheet(baseIds, year, "AVQ");
|
||||||
|
if (baseEpaSheet != null) sheets.add(baseEpaSheet);
|
||||||
|
if (baseMwrSheet != null) sheets.add(baseMwrSheet);
|
||||||
|
if (baseAvqSheet != null) sheets.add(baseAvqSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sheets.isEmpty()) {
|
||||||
|
excelList.add(new ExportZipUtil.ExcelData(year + "年度报表_" + timestamp, sheets));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excelList.isEmpty()) {
|
||||||
|
log.warn("年度导出没有可用的数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String zipFileName = "年度报表.zip";
|
||||||
|
ExportZipUtil.exportToResponse(response, zipFileName, excelList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建年度水质达标率 Sheet
|
||||||
|
* 格式:电站 | 所在流域 | 小时尺度(%) | 日均尺度(%) | 主要污染物
|
||||||
|
*/
|
||||||
|
private ExportZipUtil.SheetData buildYearWqSheet(List<StationInfo> wqStations, int year) {
|
||||||
|
List<String> stcdList = wqStations.stream().map(StationInfo::getStcd).distinct().toList();
|
||||||
|
if (stcdList.isEmpty()) return null;
|
||||||
|
|
||||||
|
String startTime = year + "-01-01 00:00:00";
|
||||||
|
String endTime = (year + 1) + "-01-01 00:00:00";
|
||||||
|
|
||||||
|
// 查询小时/日均达标数据
|
||||||
|
List<Map<String, Object>> hourlyData = sdWqRMapper.selectComplianceData(stcdList, startTime, endTime);
|
||||||
|
List<Map<String, Object>> dailyData = sdWqdaySMapper.selectComplianceData(stcdList, startTime, endTime);
|
||||||
|
// 查询主要污染物
|
||||||
|
List<Map<String, Object>> pollutantData = sdWqRMapper.selectPollutants(stcdList, startTime, endTime);
|
||||||
|
// 查询要素中文名
|
||||||
|
Map<String, String> colCommentMap = new LinkedHashMap<>();
|
||||||
|
List<Map<String, Object>> colComments = sdWqRMapper.selectColumnComments();
|
||||||
|
for (Map<String, Object> cm : colComments) {
|
||||||
|
String colName = (String) cm.get("COLUMN_NAME");
|
||||||
|
String comment = (String) cm.get("COMMENTS");
|
||||||
|
if (colName != null && comment != null && !comment.isEmpty()) {
|
||||||
|
colCommentMap.put(colName, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按 STCD 聚合污染物
|
||||||
|
Map<String, Set<String>> pollutantSetMap = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> m : pollutantData) {
|
||||||
|
String stcd = (String) m.get("STCD");
|
||||||
|
String pol = (String) m.get("POLLUTANT");
|
||||||
|
if (pol != null && !pol.isEmpty()) {
|
||||||
|
Set<String> set = pollutantSetMap.computeIfAbsent(stcd, k -> new LinkedHashSet<>());
|
||||||
|
for (String field : pol.split(",")) {
|
||||||
|
String f = field.trim();
|
||||||
|
if (!f.isEmpty()) {
|
||||||
|
String desc = colCommentMap.getOrDefault(f, f);
|
||||||
|
set.add(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 索引达标统计
|
||||||
|
Map<String, Map<String, Object>> hourlyMap = indexByStcd(hourlyData);
|
||||||
|
Map<String, Map<String, Object>> dailyMap = indexByStcd(dailyData);
|
||||||
|
|
||||||
|
// 表头
|
||||||
|
List<String> headers = Arrays.asList("电站", "所在流域", "小时尺度(%)", "日均尺度(%)", "主要污染物");
|
||||||
|
|
||||||
|
// 数据行
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
for (StationInfo station : wqStations) {
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
row.add(station.getEnnm() != null ? station.getEnnm() : station.getStnm());
|
||||||
|
row.add(buildBasinPath(station));
|
||||||
|
// 小时达标率
|
||||||
|
row.add(calcComplianceRate(hourlyMap.get(station.getStcd())));
|
||||||
|
// 日均达标率
|
||||||
|
row.add(calcComplianceRate(dailyMap.get(station.getStcd())));
|
||||||
|
// 主要污染物
|
||||||
|
Set<String> pollutants = pollutantSetMap.get(station.getStcd());
|
||||||
|
row.add(pollutants != null && !pollutants.isEmpty() ? String.join(", ", pollutants) : "");
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExportZipUtil.SheetData("电站-自动站水质达标率", headers, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建年度生态流量电站维度 Sheet(环保部/水利部/多年平均)
|
||||||
|
* 格式:电站 | 所在流域 | 所在水电基地 | 小时尺度(%) | 日尺度(%) | 月尺度(%)
|
||||||
|
*/
|
||||||
|
private ExportZipUtil.SheetData buildYearQecStationSheet(List<StationInfo> engStations, int year, String dept) {
|
||||||
|
List<String> stcdList = engStations.stream().map(StationInfo::getStcd).distinct().toList();
|
||||||
|
if (stcdList.isEmpty()) return null;
|
||||||
|
|
||||||
|
String startTime = year + "-01-01 00:00:00";
|
||||||
|
String endTime = (year + 1) + "-01-01 00:00:00";
|
||||||
|
|
||||||
|
// 查询三种维度达标数据
|
||||||
|
List<Map<String, Object>> hourlyData = sdQecRMapper.selectComplianceData(stcdList, startTime, endTime);
|
||||||
|
List<Map<String, Object>> dailyData = sdQecdaySMapper.selectComplianceData(stcdList, startTime, endTime);
|
||||||
|
List<Map<String, Object>> monthlyData = yearStatMapper.selectQecMonthlyCompliance(stcdList, year);
|
||||||
|
|
||||||
|
// 按 STCD 聚合小时/日数据(原始数据是逐行明细,需聚合)
|
||||||
|
Map<String, long[]> hourlyAgg = aggregateQecCompliance(hourlyData, dept);
|
||||||
|
Map<String, long[]> dailyAgg = aggregateQecCompliance(dailyData, dept);
|
||||||
|
Map<String, Map<String, Object>> monthlyMap = indexByStcd(monthlyData);
|
||||||
|
|
||||||
|
// 部门名称和对应的 SFDB 字段
|
||||||
|
String deptName;
|
||||||
|
String sfdbField;
|
||||||
|
String compliantField;
|
||||||
|
String totalField;
|
||||||
|
switch (dept) {
|
||||||
|
case "MWR":
|
||||||
|
deptName = "水利部";
|
||||||
|
sfdbField = "MWR_SFDB";
|
||||||
|
compliantField = "MWR_COMPLIANT";
|
||||||
|
totalField = "MWR_TOTAL";
|
||||||
|
break;
|
||||||
|
case "AVQ":
|
||||||
|
deptName = "多年平均";
|
||||||
|
sfdbField = "AVQ_SFDB";
|
||||||
|
compliantField = "AVQ_COMPLIANT";
|
||||||
|
totalField = "AVQ_TOTAL";
|
||||||
|
break;
|
||||||
|
default: // QEC 环保部
|
||||||
|
deptName = "环保部";
|
||||||
|
sfdbField = "SFDB";
|
||||||
|
compliantField = "QEC_COMPLIANT";
|
||||||
|
totalField = "QEC_TOTAL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> headers = Arrays.asList("电站", "所在流域", "所在水电基地", "小时尺度(%)", "日尺度(%)", "月尺度(%)");
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
|
||||||
|
for (StationInfo station : engStations) {
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
row.add(station.getEnnm() != null ? station.getEnnm() : station.getStnm());
|
||||||
|
row.add(buildBasinPath(station));
|
||||||
|
row.add(station.getBaseName() != null ? station.getBaseName() : "");
|
||||||
|
// 小时达标率
|
||||||
|
long[] hAgg = hourlyAgg.get(station.getStcd());
|
||||||
|
row.add(formatRate(hAgg != null ? hAgg[0] : 0, hAgg != null ? hAgg[1] : 0));
|
||||||
|
// 日达标率
|
||||||
|
long[] dAgg = dailyAgg.get(station.getStcd());
|
||||||
|
row.add(formatRate(dAgg != null ? dAgg[0] : 0, dAgg != null ? dAgg[1] : 0));
|
||||||
|
// 月度达标率
|
||||||
|
row.add(calcMonthlyComplianceRate(monthlyMap.get(station.getStcd()), compliantField, totalField));
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExportZipUtil.SheetData("电站-生态流量达标率(" + deptName + ")", headers, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建年度生态流量水电基地维度 Sheet
|
||||||
|
* 格式:水电基地 | 小时尺度(%) | 日尺度(%) | 月尺度(%)
|
||||||
|
*/
|
||||||
|
private ExportZipUtil.SheetData buildYearQecBaseSheet(List<String> baseIds, int year, String dept) {
|
||||||
|
if (baseIds.isEmpty()) return null;
|
||||||
|
|
||||||
|
// 查询该 BASE_ID 下的所有 ENG 测站
|
||||||
|
List<StationInfo> baseStations = yearStatMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<StationInfo>()
|
||||||
|
.eq(StationInfo::getSttp, "ENG")
|
||||||
|
.in(StationInfo::getBaseId, baseIds)
|
||||||
|
);
|
||||||
|
if (baseStations.isEmpty()) return null;
|
||||||
|
|
||||||
|
// 按 BASE_ID 分组
|
||||||
|
Map<String, List<StationInfo>> byBase = new LinkedHashMap<>();
|
||||||
|
Map<String, String> baseNameMap = new LinkedHashMap<>();
|
||||||
|
for (StationInfo s : baseStations) {
|
||||||
|
String bid = s.getBaseId();
|
||||||
|
if (bid == null) continue;
|
||||||
|
byBase.computeIfAbsent(bid, k -> new ArrayList<>()).add(s);
|
||||||
|
if (s.getBaseName() != null) baseNameMap.put(bid, s.getBaseName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集所有 STCD
|
||||||
|
List<String> allStcds = baseStations.stream().map(StationInfo::getStcd).distinct().toList();
|
||||||
|
String startTime = year + "-01-01 00:00:00";
|
||||||
|
String endTime = (year + 1) + "-01-01 00:00:00";
|
||||||
|
|
||||||
|
// 查询达标数据
|
||||||
|
List<Map<String, Object>> hourlyData = sdQecRMapper.selectComplianceData(allStcds, startTime, endTime);
|
||||||
|
List<Map<String, Object>> dailyData = sdQecdaySMapper.selectComplianceData(allStcds, startTime, endTime);
|
||||||
|
List<Map<String, Object>> monthlyData = yearStatMapper.selectQecMonthlyComplianceByBase(baseIds, year);
|
||||||
|
|
||||||
|
Map<String, Map<String, Object>> monthlyMap = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> m : monthlyData) {
|
||||||
|
String bid = (String) m.get("BASE_ID");
|
||||||
|
if (bid != null) monthlyMap.put(bid, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
String deptName;
|
||||||
|
String compliantField;
|
||||||
|
String totalField;
|
||||||
|
switch (dept) {
|
||||||
|
case "MWR":
|
||||||
|
deptName = "水利部";
|
||||||
|
compliantField = "MWR_COMPLIANT";
|
||||||
|
totalField = "MWR_TOTAL";
|
||||||
|
break;
|
||||||
|
case "AVQ":
|
||||||
|
deptName = "多年平均";
|
||||||
|
compliantField = "AVQ_COMPLIANT";
|
||||||
|
totalField = "AVQ_TOTAL";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
deptName = "环保部";
|
||||||
|
compliantField = "QEC_COMPLIANT";
|
||||||
|
totalField = "QEC_TOTAL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> headers = Arrays.asList("水电基地", "小时尺度(%)", "日尺度(%)", "月尺度(%)");
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String baseId : baseIds) {
|
||||||
|
List<StationInfo> sts = byBase.get(baseId);
|
||||||
|
if (sts == null || sts.isEmpty()) continue;
|
||||||
|
|
||||||
|
List<String> baseStcds = sts.stream().map(StationInfo::getStcd).distinct().toList();
|
||||||
|
|
||||||
|
// 按 BASE 聚合达标率
|
||||||
|
long hourCompliant = 0, hourTotal = 0;
|
||||||
|
long dayCompliant = 0, dayTotal = 0;
|
||||||
|
|
||||||
|
for (Map<String, Object> m : hourlyData) {
|
||||||
|
if (baseStcds.contains(m.get("STCD"))) {
|
||||||
|
hourCompliant += countCompliant(m, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
hourTotal += countTotal(m, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map<String, Object> m : dailyData) {
|
||||||
|
if (baseStcds.contains(m.get("STCD"))) {
|
||||||
|
dayCompliant += countCompliant(m, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
dayTotal += countTotal(m, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
row.add(baseNameMap.getOrDefault(baseId, baseId));
|
||||||
|
row.add(formatRate(hourCompliant, hourTotal));
|
||||||
|
row.add(formatRate(dayCompliant, dayTotal));
|
||||||
|
|
||||||
|
// 月度达标率(直接从 SQL 聚合结果获取)
|
||||||
|
Map<String, Object> mStat = monthlyMap.get(baseId);
|
||||||
|
if (mStat != null) {
|
||||||
|
Object cObj = mStat.get(compliantField);
|
||||||
|
Object tObj = mStat.get(totalField);
|
||||||
|
long c = cObj instanceof Number ? ((Number) cObj).longValue() : 0;
|
||||||
|
long t = tObj instanceof Number ? ((Number) tObj).longValue() : 0;
|
||||||
|
row.add(formatRate(c, t));
|
||||||
|
} else {
|
||||||
|
row.add("");
|
||||||
|
}
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExportZipUtil.SheetData("水电基地-生态流量达标率(" + deptName + ")", headers, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 年度导出辅助方法 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 STCD 索引 Map 列表
|
||||||
|
*/
|
||||||
|
private Map<String, Map<String, Object>> indexByStcd(List<Map<String, Object>> data) {
|
||||||
|
Map<String, Map<String, Object>> result = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> m : data) {
|
||||||
|
String stcd = (String) m.get("STCD");
|
||||||
|
if (stcd != null) result.put(stcd, m);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建流域路径:一级流域-二级流域-水系
|
||||||
|
*/
|
||||||
|
private String buildBasinPath(StationInfo station) {
|
||||||
|
// StringBuilder sb = new StringBuilder();
|
||||||
|
// if (station.getHbrvcdName() != null) sb.append(station.getHbrvcdName());
|
||||||
|
// if (station.getRvcdName() != null) {
|
||||||
|
// if (sb.length() > 0) sb.append("-");
|
||||||
|
// sb.append(station.getRvcdName());
|
||||||
|
// }
|
||||||
|
// if (station.getHynm() != null) {
|
||||||
|
// if (sb.length() > 0) sb.append("-");
|
||||||
|
// sb.append(station.getHynm());
|
||||||
|
// }
|
||||||
|
// return sb.toString();
|
||||||
|
return station.getReachcdName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算水质达标率(%, 保留2位小数)
|
||||||
|
*/
|
||||||
|
private String calcComplianceRate(Map<String, Object> stat) {
|
||||||
|
if (stat == null) return "";
|
||||||
|
Object cObj = stat.get("COMPLIANT_CNT");
|
||||||
|
Object tObj = stat.get("TOTAL_CNT");
|
||||||
|
long compliant = cObj instanceof Number ? ((Number) cObj).longValue() : 0;
|
||||||
|
long total = tObj instanceof Number ? ((Number) tObj).longValue() : 0;
|
||||||
|
return formatRate(compliant, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算 QEC 部门达标率(从明细数据 SFDB/MWR_SFDB/AVQ_SFDB)
|
||||||
|
*/
|
||||||
|
private String calcQecComplianceRate(Map<String, Object> stat, String dept) {
|
||||||
|
if (stat == null) return "";
|
||||||
|
long compliant = countCompliant(stat, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
long total = countTotal(stat, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
return formatRate(compliant, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聚合 QEC 达标明细数据:按 STCD 汇总达标数/总数
|
||||||
|
*
|
||||||
|
* @return Map<STCD, long [ 2 ]> 其中 [0]=compliant, [1]=total
|
||||||
|
*/
|
||||||
|
private Map<String, long[]> aggregateQecCompliance(List<Map<String, Object>> data, String dept) {
|
||||||
|
Map<String, long[]> result = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> m : data) {
|
||||||
|
String stcd = (String) m.get("STCD");
|
||||||
|
if (stcd == null) continue;
|
||||||
|
result.computeIfAbsent(stcd, k -> new long[2]);
|
||||||
|
long[] arr = result.get(stcd);
|
||||||
|
arr[0] += countCompliant(m, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
arr[1] += countTotal(m, "SFDB", "MWR_SFDB", "AVQ_SFDB", dept);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long countCompliant(Map<String, Object> map, String qecField, String mwrField, String avqField, String dept) {
|
||||||
|
String field;
|
||||||
|
switch (dept) {
|
||||||
|
case "MWR":
|
||||||
|
field = mwrField;
|
||||||
|
break;
|
||||||
|
case "AVQ":
|
||||||
|
field = avqField;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
field = qecField;
|
||||||
|
}
|
||||||
|
Object val = map.get(field);
|
||||||
|
if (val instanceof Number && ((Number) val).intValue() == 1) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long countTotal(Map<String, Object> map, String qecField, String mwrField, String avqField, String dept) {
|
||||||
|
String field;
|
||||||
|
switch (dept) {
|
||||||
|
case "MWR":
|
||||||
|
field = mwrField;
|
||||||
|
break;
|
||||||
|
case "AVQ":
|
||||||
|
field = avqField;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
field = qecField;
|
||||||
|
}
|
||||||
|
Object val = map.get(field);
|
||||||
|
if (val instanceof Number) {
|
||||||
|
int v = ((Number) val).intValue();
|
||||||
|
if (v == 0 || v == 1) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算月度达标率(从聚合结果 COMPLIANT/TOTAL 字段)
|
||||||
|
*/
|
||||||
|
private String calcMonthlyComplianceRate(Map<String, Object> stat, String compliantField, String totalField) {
|
||||||
|
if (stat == null) return "";
|
||||||
|
Object cObj = stat.get(compliantField);
|
||||||
|
Object tObj = stat.get(totalField);
|
||||||
|
long c = cObj instanceof Number ? ((Number) cObj).longValue() : 0;
|
||||||
|
long t = tObj instanceof Number ? ((Number) tObj).longValue() : 0;
|
||||||
|
return formatRate(c, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatRate(long compliant, long total) {
|
||||||
|
if (total > 0) {
|
||||||
|
BigDecimal rate = BigDecimal.valueOf(compliant * 100.0 / total);
|
||||||
|
return rate.setScale(2, RoundingMode.HALF_UP).toPlainString();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按沿程排序测站
|
||||||
|
*/
|
||||||
|
private List<StationInfo> sortStationsByOrder(List<StationInfo> stations) {
|
||||||
|
// 按 RSTCD 收集沿程排序
|
||||||
|
List<String> rstcds = stations.stream().map(StationInfo::getRstcd).filter(Objects::nonNull).distinct().toList();
|
||||||
|
if (rstcds.isEmpty()) return stations;
|
||||||
|
|
||||||
|
List<Map<String, Object>> orderList = msAlongdetBMapper.selectAlongSortOrder(rstcds);
|
||||||
|
Map<String, Integer> orderMap = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> m : orderList) {
|
||||||
|
String rstcd = (String) m.get("RSTCD");
|
||||||
|
Object sortObj = m.get("SORT");
|
||||||
|
if (rstcd != null && sortObj instanceof Number) {
|
||||||
|
orderMap.put(rstcd, ((Number) sortObj).intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StationInfo> sorted = new ArrayList<>(stations);
|
||||||
|
sorted.sort((a, b) -> {
|
||||||
|
Integer oa = orderMap.get(a.getRstcd());
|
||||||
|
Integer ob = orderMap.get(b.getRstcd());
|
||||||
|
if (oa != null && ob != null) return oa.compareTo(ob);
|
||||||
|
if (oa != null) return -1;
|
||||||
|
if (ob != null) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
// ======================== hour/day 原始数据导出 ========================
|
// ======================== hour/day 原始数据导出 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.yfd.platform.qgc_export.mapper.YearStatMapper">
|
||||||
|
|
||||||
|
<!-- 生态流量月度达标统计:按 STCD 聚合三种 SFDB -->
|
||||||
|
<select id="selectQecMonthlyCompliance" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
STCD,
|
||||||
|
SUM(CASE WHEN SFDB = 1 THEN 1 ELSE 0 END) AS QEC_COMPLIANT,
|
||||||
|
SUM(CASE WHEN SFDB IN (0, 1) THEN 1 ELSE 0 END) AS QEC_TOTAL,
|
||||||
|
SUM(CASE WHEN MWR_SFDB = 1 THEN 1 ELSE 0 END) AS MWR_COMPLIANT,
|
||||||
|
SUM(CASE WHEN MWR_SFDB IN (0, 1) THEN 1 ELSE 0 END) AS MWR_TOTAL,
|
||||||
|
SUM(CASE WHEN AVQ_SFDB = 1 THEN 1 ELSE 0 END) AS AVQ_COMPLIANT,
|
||||||
|
SUM(CASE WHEN AVQ_SFDB IN (0, 1) THEN 1 ELSE 0 END) AS AVQ_TOTAL
|
||||||
|
FROM SD_QECDRTP_S
|
||||||
|
WHERE IS_DELETED = 0
|
||||||
|
AND DRTP = 'MON'
|
||||||
|
AND STCD IN
|
||||||
|
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
|
||||||
|
#{stcd}
|
||||||
|
</foreach>
|
||||||
|
AND YEAR = #{year}
|
||||||
|
GROUP BY STCD
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 生态流量月度达标统计:按 BASE_ID 聚合 -->
|
||||||
|
<select id="selectQecMonthlyComplianceByBase" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
v.BASE_ID,
|
||||||
|
SUM(CASE WHEN d.SFDB = 1 THEN 1 ELSE 0 END) AS QEC_COMPLIANT,
|
||||||
|
SUM(CASE WHEN d.SFDB IN (0, 1) THEN 1 ELSE 0 END) AS QEC_TOTAL,
|
||||||
|
SUM(CASE WHEN d.MWR_SFDB = 1 THEN 1 ELSE 0 END) AS MWR_COMPLIANT,
|
||||||
|
SUM(CASE WHEN d.MWR_SFDB IN (0, 1) THEN 1 ELSE 0 END) AS MWR_TOTAL,
|
||||||
|
SUM(CASE WHEN d.AVQ_SFDB = 1 THEN 1 ELSE 0 END) AS AVQ_COMPLIANT,
|
||||||
|
SUM(CASE WHEN d.AVQ_SFDB IN (0, 1) THEN 1 ELSE 0 END) AS AVQ_TOTAL
|
||||||
|
FROM SD_QECDRTP_S d
|
||||||
|
INNER JOIN V_MS_STBPRP_T v ON v.STCD = d.STCD AND v.STTP = 'ENG'
|
||||||
|
WHERE d.IS_DELETED = 0
|
||||||
|
AND d.DRTP = 'MON'
|
||||||
|
AND v.BASE_ID IN
|
||||||
|
<foreach collection="baseIdList" item="bid" open="(" separator="," close=")">
|
||||||
|
#{bid}
|
||||||
|
</foreach>
|
||||||
|
AND d.YEAR = #{year}
|
||||||
|
GROUP BY v.BASE_ID
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
Loading…
Reference in New Issue
Block a user