fix: 优化综合导出逻辑
This commit is contained in:
parent
ac4bf3465d
commit
2837e9e28d
@ -0,0 +1,50 @@
|
|||||||
|
package com.yfd.platform.qgc_export.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生态流量监测数据表(小时级)
|
||||||
|
* 对应 Oracle 表: QGC_REFA.SD_QEC_R
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("SD_QEC_R")
|
||||||
|
public class SdQecR implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.INPUT)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/** 水电工程编码 */
|
||||||
|
private String stcd;
|
||||||
|
|
||||||
|
/** 数据识别时间 */
|
||||||
|
private Date tm;
|
||||||
|
|
||||||
|
/** 生态流量 (m³/s) */
|
||||||
|
private BigDecimal qec;
|
||||||
|
|
||||||
|
/** AI识别生态流量值 */
|
||||||
|
private BigDecimal waterflow;
|
||||||
|
|
||||||
|
/** 生态流量泄放方式 */
|
||||||
|
private String tpqec;
|
||||||
|
|
||||||
|
/** 生态流量是否达标 */
|
||||||
|
private Integer sfdb;
|
||||||
|
|
||||||
|
private String recordUser;
|
||||||
|
private Date recordTime;
|
||||||
|
private String modifyUser;
|
||||||
|
private Date modifyTime;
|
||||||
|
private Integer isDeleted;
|
||||||
|
private String deleteUser;
|
||||||
|
private Date deleteTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.yfd.platform.qgc_export.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生态流量监测数据日统计表
|
||||||
|
* 对应 Oracle 表: QGC_REFA.SD_QECDAY_S
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("SD_QECDAY_S")
|
||||||
|
public class SdQecdayS implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.INPUT)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/** 水电工程编码 */
|
||||||
|
private String stcd;
|
||||||
|
|
||||||
|
/** 数据识别时间 */
|
||||||
|
private Date dt;
|
||||||
|
|
||||||
|
/** 生态流量 (m³/s) */
|
||||||
|
private BigDecimal qec;
|
||||||
|
|
||||||
|
/** AI识别生态流量值 */
|
||||||
|
private BigDecimal waterflow;
|
||||||
|
|
||||||
|
/** 生态流量泄放方式 */
|
||||||
|
private String tpqec;
|
||||||
|
|
||||||
|
/** 生态流量是否达标 */
|
||||||
|
private Integer sfdb;
|
||||||
|
|
||||||
|
private String recordUser;
|
||||||
|
private Date recordTime;
|
||||||
|
private String modifyUser;
|
||||||
|
private Date modifyTime;
|
||||||
|
private Integer isDeleted;
|
||||||
|
private String deleteUser;
|
||||||
|
private Date deleteTime;
|
||||||
|
}
|
||||||
@ -31,4 +31,8 @@ public class StationMapping implements Serializable {
|
|||||||
/** 测站名称 */
|
/** 测站名称 */
|
||||||
@TableField("STNM")
|
@TableField("STNM")
|
||||||
private String stnm;
|
private String stnm;
|
||||||
|
|
||||||
|
/** 所属水电基地名称 */
|
||||||
|
@TableField("BASE_NAME")
|
||||||
|
private String baseName;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.yfd.platform.qgc_export.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yfd.platform.qgc_export.domain.SdQecR;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SD_QEC_R 生态流量小时数据 Mapper
|
||||||
|
*/
|
||||||
|
public interface SdQecRMapper extends BaseMapper<SdQecR> {
|
||||||
|
|
||||||
|
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
|
||||||
|
@Param("startTime") String startTime,
|
||||||
|
@Param("endTime") String endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询生态流量达标数据(小时级),返回三种 SFDB 字段
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> selectComplianceData(@Param("stcdList") List<String> stcdList,
|
||||||
|
@Param("startTime") String startTime,
|
||||||
|
@Param("endTime") String endTime);
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.yfd.platform.qgc_export.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yfd.platform.qgc_export.domain.SdQecdayS;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SD_QECDAY_S 生态流量日数据 Mapper
|
||||||
|
*/
|
||||||
|
public interface SdQecdaySMapper extends BaseMapper<SdQecdayS> {
|
||||||
|
|
||||||
|
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
|
||||||
|
@Param("startTime") String startTime,
|
||||||
|
@Param("endTime") String endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询生态流量达标数据(日级),返回三种 SFDB 字段
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> selectComplianceData(@Param("stcdList") List<String> stcdList,
|
||||||
|
@Param("startTime") String startTime,
|
||||||
|
@Param("endTime") String endTime);
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.yfd.platform.qgc_export.processor;
|
||||||
|
|
||||||
|
import com.yfd.platform.qgc_export.mapper.SdQecRMapper;
|
||||||
|
import com.yfd.platform.qgc_export.mapper.SdQecdaySMapper;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生态流量(QEC) 处理器
|
||||||
|
* <p>
|
||||||
|
* 数据来源: SD_QEC_R / SD_QECDAY_S,STTP="ENG"。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class QecProcessor implements ExportIndicatorProcessor {
|
||||||
|
|
||||||
|
private SdQecRMapper sdQecRMapper;
|
||||||
|
private SdQecdaySMapper sdQecdaySMapper;
|
||||||
|
|
||||||
|
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
public void setMappers(SdQecRMapper rMapper, SdQecdaySMapper sMapper) {
|
||||||
|
this.sdQecRMapper = rMapper;
|
||||||
|
this.sdQecdaySMapper = sMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFieldName() {
|
||||||
|
return "QEC";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSheetName() {
|
||||||
|
return "生态流量";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSttpType() {
|
||||||
|
return "ENG";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDecimalPlaces() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 37;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IndicatorDataRow> queryRawData(List<String> stcds, Date startTime, Date endTime, boolean isDaily) {
|
||||||
|
String startStr = SDF.format(startTime);
|
||||||
|
String endStr = SDF.format(endTime);
|
||||||
|
|
||||||
|
List<Map<String, Object>> rawList;
|
||||||
|
if (isDaily) {
|
||||||
|
rawList = sdQecdaySMapper.selectRawData(stcds, startStr, endStr);
|
||||||
|
} else {
|
||||||
|
rawList = sdQecRMapper.selectRawData(stcds, startStr, endStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IndicatorDataRow> rows = new ArrayList<>();
|
||||||
|
if (rawList != null) {
|
||||||
|
for (Map<String, Object> map : rawList) {
|
||||||
|
String stcd = (String) map.get("STCD");
|
||||||
|
Date tm = (Date) map.get(isDaily ? "DT" : "TM");
|
||||||
|
BigDecimal value = toBigDecimal(map.get("VALUE"));
|
||||||
|
if (stcd != null && tm != null && value != null) {
|
||||||
|
rows.add(new IndicatorDataRow(stcd, tm, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal toBigDecimal(Object val) {
|
||||||
|
if (val instanceof BigDecimal) return (BigDecimal) val;
|
||||||
|
if (val instanceof Number) return BigDecimal.valueOf(((Number) val).doubleValue());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.yfd.platform.qgc_export.service.impl;
|
package com.yfd.platform.qgc_export.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.yfd.platform.qgc_export.domain.StationMapping;
|
import com.yfd.platform.qgc_export.domain.StationMapping;
|
||||||
import com.yfd.platform.qgc_export.mapper.SdRiverRMapper;
|
import com.yfd.platform.qgc_export.mapper.SdRiverRMapper;
|
||||||
import com.yfd.platform.qgc_export.mapper.SdRiverdaySMapper;
|
import com.yfd.platform.qgc_export.mapper.SdRiverdaySMapper;
|
||||||
@ -9,6 +10,8 @@ import com.yfd.platform.qgc_export.mapper.SdWtrvRMapper;
|
|||||||
import com.yfd.platform.qgc_export.mapper.SdWtrvdaySMapper;
|
import com.yfd.platform.qgc_export.mapper.SdWtrvdaySMapper;
|
||||||
import com.yfd.platform.qgc_export.mapper.SdHydropwRMapper;
|
import com.yfd.platform.qgc_export.mapper.SdHydropwRMapper;
|
||||||
import com.yfd.platform.qgc_export.mapper.SdHydropwdaySMapper;
|
import com.yfd.platform.qgc_export.mapper.SdHydropwdaySMapper;
|
||||||
|
import com.yfd.platform.qgc_export.mapper.SdQecRMapper;
|
||||||
|
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.processor.*;
|
import com.yfd.platform.qgc_export.processor.*;
|
||||||
import com.yfd.platform.qgc_export.service.IQgcExportService;
|
import com.yfd.platform.qgc_export.service.IQgcExportService;
|
||||||
@ -126,7 +129,16 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
@Resource
|
@Resource
|
||||||
private QoProcessor qoProcessor;
|
private QoProcessor qoProcessor;
|
||||||
|
|
||||||
|
// QEC 生态流量相关(STTP=ENG)
|
||||||
|
@Resource
|
||||||
|
private SdQecRMapper sdQecRMapper;
|
||||||
|
@Resource
|
||||||
|
private SdQecdaySMapper sdQecdaySMapper;
|
||||||
|
@Resource
|
||||||
|
private QecProcessor qecProcessor;
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
/** 基础汇总指标(7个) */
|
/** 基础汇总指标(7个) */
|
||||||
private static final List<String> BASE_LABELS = Collections.unmodifiableList(
|
private static final List<String> BASE_LABELS = Collections.unmodifiableList(
|
||||||
@ -161,6 +173,7 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
List<String> stationOrder = new ArrayList<>(stationRes.displayNames.keySet());
|
List<String> stationOrder = new ArrayList<>(stationRes.displayNames.keySet());
|
||||||
|
|
||||||
// 5. 每月一个 ExcelData
|
// 5. 每月一个 ExcelData
|
||||||
|
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||||
List<ExportZipUtil.ExcelData> excelList = new ArrayList<>();
|
List<ExportZipUtil.ExcelData> excelList = new ArrayList<>();
|
||||||
for (String month : months) {
|
for (String month : months) {
|
||||||
MonthRange mr = buildMonthRange(month);
|
MonthRange mr = buildMonthRange(month);
|
||||||
@ -168,21 +181,53 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
|
|
||||||
List<ExportZipUtil.SheetData> sheets = new ArrayList<>();
|
List<ExportZipUtil.SheetData> sheets = new ArrayList<>();
|
||||||
for (ExportIndicatorProcessor processor : processors) {
|
for (ExportIndicatorProcessor processor : processors) {
|
||||||
List<List<Object>> rows = buildSheetRows(processor, mr, stationOrder, stationRes);
|
List<List<Object>> rows;
|
||||||
|
// QEC 自定义三栏达标率格式
|
||||||
|
if (processor instanceof QecProcessor) {
|
||||||
|
rows = buildQecSheetRows(mr, stationOrder, stationRes);
|
||||||
|
} else {
|
||||||
|
rows = buildSheetRows(processor, mr, stationOrder, stationRes);
|
||||||
|
}
|
||||||
if (rows != null) {
|
if (rows != null) {
|
||||||
// 按当前处理器 STTP 类型获取对应的测站名称
|
List<String> headers;
|
||||||
String sttp = processor.getSttpType();
|
String sttp = processor.getSttpType();
|
||||||
List<String> headers = new ArrayList<>();
|
if (processor instanceof QecProcessor) {
|
||||||
headers.add("日期");
|
// 分部标题作为列标题(合并单元格)
|
||||||
for (String rstcd : stationOrder) {
|
String[] sectionTitles = {"生态流量达标率(环保部)", "生态流量达标率(水利部)", "生态流量达标率(多年平均)"};
|
||||||
headers.add(stationRes.getDisplayName(rstcd, sttp));
|
headers = new ArrayList<>();
|
||||||
|
for (String title : sectionTitles) {
|
||||||
|
headers.add(title);
|
||||||
|
for (int i = 1; i < 6; i++) headers.add("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 仅包含当前处理器 STTP 类型有对应测站的电站
|
||||||
|
headers = new ArrayList<>();
|
||||||
|
headers.add("日期");
|
||||||
|
for (String rstcd : stationOrder) {
|
||||||
|
if (stationRes.getStcd(rstcd, sttp) != null) {
|
||||||
|
String displayName = stationRes.getDisplayName(rstcd, sttp);
|
||||||
|
if (StrUtil.isNotBlank(displayName)) {
|
||||||
|
headers.add(displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sheets.add(new ExportZipUtil.SheetData(processor.getSheetName(), headers, rows));
|
ExportZipUtil.SheetData sd = new ExportZipUtil.SheetData(processor.getSheetName(), headers, rows);
|
||||||
|
if (processor instanceof QecProcessor) {
|
||||||
|
// 分部标题合并单元格(Row 0 是 headers),子标题行用 subHeaderStyle
|
||||||
|
sd.setHeaderRowCount(1);
|
||||||
|
sd.setMergedRegions(Arrays.asList(
|
||||||
|
new int[]{0, 0, 0, 5}, // 环保部
|
||||||
|
new int[]{0, 0, 6, 11}, // 水利部
|
||||||
|
new int[]{0, 0, 12, 17} // 多年平均
|
||||||
|
));
|
||||||
|
}
|
||||||
|
sheets.add(sd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sheets.isEmpty()) {
|
if (!sheets.isEmpty()) {
|
||||||
excelList.add(new ExportZipUtil.ExcelData(month, sheets));
|
excelList.add(new ExportZipUtil.ExcelData(month + "统计数据_" + timestamp, sheets));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,9 +293,9 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
for (int d = 1; d <= daysInMonth; d++) {
|
for (int d = 1; d <= daysInMonth; d++) {
|
||||||
List<Object> row = new ArrayList<>();
|
List<Object> row = new ArrayList<>();
|
||||||
row.add(String.format("%02d", d));
|
row.add(String.format("%02d", d));
|
||||||
for (String rstcd : stationOrder) {
|
for (String rstcd : rstcdToStcd.keySet()) {
|
||||||
String stcd = rstcdToStcd.get(rstcd);
|
String stcd = rstcdToStcd.get(rstcd);
|
||||||
Map<Integer, BigDecimal> dayMap = stcd != null ? dailyByStcd.get(stcd) : null;
|
Map<Integer, BigDecimal> dayMap = dailyByStcd.get(stcd);
|
||||||
BigDecimal val = dayMap != null ? dayMap.get(d) : null;
|
BigDecimal val = dayMap != null ? dayMap.get(d) : null;
|
||||||
row.add(formatOrEmpty(val, decimals));
|
row.add(formatOrEmpty(val, decimals));
|
||||||
}
|
}
|
||||||
@ -262,7 +307,7 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
List<Object> row = new ArrayList<>();
|
List<Object> row = new ArrayList<>();
|
||||||
row.add(labels.get(i));
|
row.add(labels.get(i));
|
||||||
|
|
||||||
for (String rstcd : stationOrder) {
|
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: row.add(formatOrEmpty(agg != null ? agg.getDailyMin() : null, decimals)); break;
|
||||||
@ -283,6 +328,133 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======================== QEC 生态流量三栏达标率 Sheet ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建生态流量三栏达标率 Sheet 的数据行(不含标题行,标题由调用方放入 headers)。
|
||||||
|
* <p>
|
||||||
|
* 返回的 rows[0] 是子标题行(电站名称|所属水电基地|...),后续为电站数据行。
|
||||||
|
*/
|
||||||
|
private List<List<Object>> buildQecSheetRows(
|
||||||
|
MonthRange mr, List<String> stationOrder, StationResolution stationRes) {
|
||||||
|
|
||||||
|
String sttp = "ENG";
|
||||||
|
Map<String, String> rstcdToStcd = new LinkedHashMap<>();
|
||||||
|
for (String rstcd : stationOrder) {
|
||||||
|
String stcd = stationRes.getStcd(rstcd, sttp);
|
||||||
|
if (stcd != null) {
|
||||||
|
rstcdToStcd.put(rstcd, stcd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rstcdToStcd.isEmpty()) {
|
||||||
|
log.warn("生态流量未找到 STTP=ENG 的测站数据");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> uniqueStcds = rstcdToStcd.values().stream().distinct().toList();
|
||||||
|
|
||||||
|
// 查询日达标数据 + 小时达标数据
|
||||||
|
String startStr = SDF.format(mr.getStart());
|
||||||
|
String endStr = SDF.format(mr.getEnd());
|
||||||
|
List<Map<String, Object>> dailyList = sdQecdaySMapper.selectComplianceData(uniqueStcds, startStr, endStr);
|
||||||
|
List<Map<String, Object>> hourlyList = sdQecRMapper.selectComplianceData(uniqueStcds, startStr, endStr);
|
||||||
|
|
||||||
|
// 按 STCD 分组计算达标率
|
||||||
|
Map<String, ComplianceStats> statsByStcd = new LinkedHashMap<>();
|
||||||
|
for (String stcd : uniqueStcds) {
|
||||||
|
ComplianceStats s = new ComplianceStats();
|
||||||
|
List<Map<String, Object>> dayForStcd = dailyList.stream()
|
||||||
|
.filter(m -> stcd.equals(m.get("STCD"))).toList();
|
||||||
|
s.dailyHb = calcComplianceRate(dayForStcd, "SFDB");
|
||||||
|
s.dailySl = calcComplianceRate(dayForStcd, "MWR_SFDB");
|
||||||
|
s.dailyDn = calcComplianceRate(dayForStcd, "AVQ_SFDB");
|
||||||
|
|
||||||
|
List<Map<String, Object>> hourForStcd = hourlyList.stream()
|
||||||
|
.filter(m -> stcd.equals(m.get("STCD"))).toList();
|
||||||
|
s.hourlyHb = calcComplianceRate(hourForStcd, "SFDB");
|
||||||
|
s.hourlySl = calcComplianceRate(hourForStcd, "MWR_SFDB");
|
||||||
|
s.hourlyDn = calcComplianceRate(hourForStcd, "AVQ_SFDB");
|
||||||
|
|
||||||
|
statsByStcd.put(stcd, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======== Row 0: 子标题行 ========
|
||||||
|
List<String> subHeaders = Arrays.asList("电站名称", "所属水电基地", "生态流量限值", "日达标率", "小时达标率", "备注");
|
||||||
|
List<Object> subRow = new ArrayList<>();
|
||||||
|
subRow.addAll(subHeaders);
|
||||||
|
subRow.addAll(subHeaders);
|
||||||
|
subRow.addAll(subHeaders);
|
||||||
|
|
||||||
|
// ======== 数据行 ========
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
rows.add(subRow); // 子标题作为第一行
|
||||||
|
|
||||||
|
for (String rstcd : stationOrder) {
|
||||||
|
String stcd = rstcdToStcd.get(rstcd);
|
||||||
|
String displayName = stationRes.getDisplayName(rstcd, sttp);
|
||||||
|
String baseName = stationRes.getBaseName(rstcd, sttp);
|
||||||
|
ComplianceStats s = stcd != null ? statsByStcd.get(stcd) : null;
|
||||||
|
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
// 环保部
|
||||||
|
row.add(displayName != null ? displayName : rstcd);
|
||||||
|
row.add(baseName);
|
||||||
|
row.add(""); // 生态流量限值(暂空)
|
||||||
|
row.add(s != null ? s.dailyHb : "");
|
||||||
|
row.add(s != null ? s.hourlyHb : "");
|
||||||
|
row.add(""); // 备注(暂空)
|
||||||
|
// 水利部
|
||||||
|
row.add(displayName != null ? displayName : rstcd);
|
||||||
|
row.add(baseName);
|
||||||
|
row.add("");
|
||||||
|
row.add(s != null ? s.dailySl : "");
|
||||||
|
row.add(s != null ? s.hourlySl : "");
|
||||||
|
row.add("");
|
||||||
|
// 多年平均
|
||||||
|
row.add(displayName != null ? displayName : rstcd);
|
||||||
|
row.add(baseName);
|
||||||
|
row.add("");
|
||||||
|
row.add(s != null ? s.dailyDn : "");
|
||||||
|
row.add(s != null ? s.hourlyDn : "");
|
||||||
|
row.add("");
|
||||||
|
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算达标率:SFDB=1 达标, SFDB=0 不达标, SFDB=2/3 不参与计算
|
||||||
|
*/
|
||||||
|
private String calcComplianceRate(List<Map<String, Object>> records, String sfdbField) {
|
||||||
|
long total = 0;
|
||||||
|
long compliant = 0;
|
||||||
|
for (Map<String, Object> m : records) {
|
||||||
|
Object v = m.get(sfdbField);
|
||||||
|
if (v == null) continue;
|
||||||
|
int sfdb = toInt(v);
|
||||||
|
if (sfdb == 0 || sfdb == 1) {
|
||||||
|
total++;
|
||||||
|
if (sfdb == 1) compliant++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (total == 0) return "";
|
||||||
|
BigDecimal rate = BigDecimal.valueOf(compliant * 100.0 / total);
|
||||||
|
return rate.setScale(1, RoundingMode.HALF_UP).toString() + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int toInt(Object v) {
|
||||||
|
if (v instanceof Number) return ((Number) v).intValue();
|
||||||
|
try { return Integer.parseInt(v.toString()); } catch (Exception e) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分部达标统计 */
|
||||||
|
private static class ComplianceStats {
|
||||||
|
String dailyHb, dailySl, dailyDn;
|
||||||
|
String hourlyHb, hourlySl, hourlyDn;
|
||||||
|
}
|
||||||
|
|
||||||
// ======================== 指标处理器匹配 ========================
|
// ======================== 指标处理器匹配 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -308,6 +480,8 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
// 出入库流量(STTP=ENG,仅标准格式无日变幅)
|
// 出入库流量(STTP=ENG,仅标准格式无日变幅)
|
||||||
processorMap.put("qi", qiProcessor);
|
processorMap.put("qi", qiProcessor);
|
||||||
processorMap.put("qo", qoProcessor);
|
processorMap.put("qo", qoProcessor);
|
||||||
|
// 生态流量(STTP=ENG,仅标准格式无日变幅)
|
||||||
|
processorMap.put("qec", qecProcessor);
|
||||||
|
|
||||||
// WQ 别名:展开为全部水质指标
|
// WQ 别名:展开为全部水质指标
|
||||||
List<String> expanded = new ArrayList<>();
|
List<String> expanded = new ArrayList<>();
|
||||||
@ -368,6 +542,8 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
((WqIndicatorProcessor) p).setMappers(sdWqRMapper, sdWqdaySMapper);
|
((WqIndicatorProcessor) p).setMappers(sdWqRMapper, sdWqdaySMapper);
|
||||||
} else if (p instanceof HydropwIndicatorProcessor) {
|
} else if (p instanceof HydropwIndicatorProcessor) {
|
||||||
((HydropwIndicatorProcessor) p).setMappers(sdHydropwRMapper, sdHydropwdaySMapper);
|
((HydropwIndicatorProcessor) p).setMappers(sdHydropwRMapper, sdHydropwdaySMapper);
|
||||||
|
} else if (p instanceof QecProcessor) {
|
||||||
|
((QecProcessor) p).setMappers(sdQecRMapper, sdQecdaySMapper);
|
||||||
}
|
}
|
||||||
result.add(p);
|
result.add(p);
|
||||||
}
|
}
|
||||||
@ -455,6 +631,16 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.sttpStcdMap.put(rstcd, sttpMap);
|
result.sttpStcdMap.put(rstcd, sttpMap);
|
||||||
|
|
||||||
|
// STTP → BASE_NAME 映射(所属水电基地)
|
||||||
|
Map<String, String> baseNames = new LinkedHashMap<>();
|
||||||
|
for (StationMapping m : rows) {
|
||||||
|
String sttp = m.getSttp();
|
||||||
|
if (sttp != null && m.getBaseName() != null && !baseNames.containsKey(sttp)) {
|
||||||
|
baseNames.put(sttp, m.getBaseName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.baseNameMap.put(rstcd, baseNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("电站映射解析完成,RSTCD数量={}, STTP类型={}", result.displayNames.size(), sttpTypes);
|
log.info("电站映射解析完成,RSTCD数量={}, STTP类型={}", result.displayNames.size(), sttpTypes);
|
||||||
@ -628,9 +814,9 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
for (int d = 1; d <= daysInMonth; d++) {
|
for (int d = 1; d <= daysInMonth; d++) {
|
||||||
List<Object> row = new ArrayList<>();
|
List<Object> row = new ArrayList<>();
|
||||||
row.add(String.format("%02d", d));
|
row.add(String.format("%02d", d));
|
||||||
for (String rstcd : stationOrder) {
|
for (String rstcd : rstcdToStcd.keySet()) {
|
||||||
String stcd = rstcdToStcd.get(rstcd);
|
String stcd = rstcdToStcd.get(rstcd);
|
||||||
Map<Integer, BigDecimal> dayMap = stcd != null ? variationByStcd.get(stcd) : null;
|
Map<Integer, BigDecimal> dayMap = variationByStcd.get(stcd);
|
||||||
BigDecimal val = dayMap != null ? dayMap.get(d) : null;
|
BigDecimal val = dayMap != null ? dayMap.get(d) : null;
|
||||||
row.add(formatOrEmpty(val, decimals));
|
row.add(formatOrEmpty(val, decimals));
|
||||||
}
|
}
|
||||||
@ -641,7 +827,7 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
for (int i = 0; i < VARIATION_LABELS.size(); i++) {
|
for (int i = 0; i < VARIATION_LABELS.size(); i++) {
|
||||||
List<Object> row = new ArrayList<>();
|
List<Object> row = new ArrayList<>();
|
||||||
row.add(VARIATION_LABELS.get(i));
|
row.add(VARIATION_LABELS.get(i));
|
||||||
for (String rstcd : stationOrder) {
|
for (String rstcd : rstcdToStcd.keySet()) {
|
||||||
BigDecimal[] agg = variationAgg.get(rstcd);
|
BigDecimal[] agg = variationAgg.get(rstcd);
|
||||||
row.add(formatOrEmpty(agg != null ? agg[i] : null, decimals));
|
row.add(formatOrEmpty(agg != null ? agg[i] : null, decimals));
|
||||||
}
|
}
|
||||||
@ -680,6 +866,8 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
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} */
|
||||||
|
final Map<String, Map<String, String>> baseNameMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
/** 获取默认测站名称 */
|
/** 获取默认测站名称 */
|
||||||
String getDisplayName(String rstcd) {
|
String getDisplayName(String rstcd) {
|
||||||
@ -696,7 +884,8 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
String name = inner.get(sttp);
|
String name = inner.get(sttp);
|
||||||
if (name != null) return name;
|
if (name != null) return name;
|
||||||
}
|
}
|
||||||
return getDisplayName(rstcd);
|
return null;
|
||||||
|
// return getDisplayName(rstcd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -707,6 +896,16 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
if (inner == null || inner.isEmpty()) return null;
|
if (inner == null || inner.isEmpty()) return null;
|
||||||
return inner.get(sttp);
|
return inner.get(sttp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取指定 STTP 的水电基地名称 */
|
||||||
|
String getBaseName(String rstcd, String sttp) {
|
||||||
|
Map<String, String> inner = baseNameMap.get(rstcd);
|
||||||
|
if (inner != null && sttp != null) {
|
||||||
|
String name = inner.get(sttp);
|
||||||
|
if (name != null) return name;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@lombok.Data
|
@lombok.Data
|
||||||
|
|||||||
@ -75,6 +75,23 @@ public class ExportZipUtil {
|
|||||||
private List<String> headers;
|
private List<String> headers;
|
||||||
/** 数据行集合(每行是一个 Object 列表,与 headers 一一对应) */
|
/** 数据行集合(每行是一个 Object 列表,与 headers 一一对应) */
|
||||||
private List<List<Object>> rows;
|
private List<List<Object>> rows;
|
||||||
|
/**
|
||||||
|
* 需要合并的区域列表,每项 int[4] = {firstRow, lastRow, firstCol, lastCol}(0-based)。
|
||||||
|
* 在数据写入后由 fillSheet 统一合并。
|
||||||
|
*/
|
||||||
|
private List<int[]> mergedRegions;
|
||||||
|
/**
|
||||||
|
* rows 中前 headerRowCount 行使用双层标题样式:
|
||||||
|
* 第 0 行用 headerStyle,第 1 行用 subHeaderStyle,以此类推。
|
||||||
|
* 默认 0 表示全部行使用 dataStyle。
|
||||||
|
*/
|
||||||
|
private int headerRowCount;
|
||||||
|
|
||||||
|
public SheetData(String sheetName, List<String> headers, List<List<Object>> rows) {
|
||||||
|
this.sheetName = sheetName;
|
||||||
|
this.headers = headers;
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -438,23 +455,41 @@ public class ExportZipUtil {
|
|||||||
|
|
||||||
// ---- 写数据行 ----
|
// ---- 写数据行 ----
|
||||||
if (rows != null) {
|
if (rows != null) {
|
||||||
for (List<Object> rowData : rows) {
|
CellStyle subHeaderStyle = createSubHeaderStyle(workbook);
|
||||||
|
for (int r = 0; r < rows.size(); r++) {
|
||||||
|
List<Object> rowData = rows.get(r);
|
||||||
Row dataRow = sheet.createRow(currentRow++);
|
Row dataRow = sheet.createRow(currentRow++);
|
||||||
|
int hc = sheetData.getHeaderRowCount();
|
||||||
|
CellStyle rowStyle;
|
||||||
|
if (r < hc) {
|
||||||
|
// 多层标题:第0行=headerStyle, 第1行=subHeaderStyle, 后续=subHeaderStyle
|
||||||
|
rowStyle = r == 0 ? headerStyle : subHeaderStyle;
|
||||||
|
} else {
|
||||||
|
rowStyle = dataStyle;
|
||||||
|
}
|
||||||
for (int i = 0; i < Math.min(rowData.size(), colCount); i++) {
|
for (int i = 0; i < Math.min(rowData.size(), colCount); i++) {
|
||||||
Cell cell = dataRow.createCell(i);
|
Cell cell = dataRow.createCell(i);
|
||||||
Object value = rowData.get(i);
|
Object value = rowData.get(i);
|
||||||
setCellValue(cell, value);
|
setCellValue(cell, value);
|
||||||
cell.setCellStyle(dataStyle);
|
cell.setCellStyle(rowStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 合并单元格 ----
|
||||||
|
if (sheetData.getMergedRegions() != null) {
|
||||||
|
for (int[] region : sheetData.getMergedRegions()) {
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(region[0], region[1], region[2], region[3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- 自动调整列宽 ----
|
// ---- 自动调整列宽 ----
|
||||||
autoSizeColumns(sheet, colCount);
|
autoSizeColumns(sheet, colCount);
|
||||||
|
|
||||||
// 冻结首行(表头固定)
|
// 冻结表头行
|
||||||
if (currentRow > 1) {
|
if (currentRow > 1) {
|
||||||
sheet.createFreezePane(0, 1);
|
int freezeRows = 1 + sheetData.getHeaderRowCount();
|
||||||
|
sheet.createFreezePane(0, Math.min(freezeRows, currentRow - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
<?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.SdQecRMapper">
|
||||||
|
|
||||||
|
<select id="selectRawData" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
STCD,
|
||||||
|
TM,
|
||||||
|
QEC AS VALUE
|
||||||
|
FROM SD_QEC_R
|
||||||
|
WHERE IS_DELETED = 0
|
||||||
|
AND STCD IN
|
||||||
|
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
|
||||||
|
#{stcd}
|
||||||
|
</foreach>
|
||||||
|
AND TM >= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
AND TM < TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
AND QEC IS NOT NULL
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 生态流量达标数据:返回三种 SFDB 用于计算各部达标率 -->
|
||||||
|
<select id="selectComplianceData" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
STCD,
|
||||||
|
SFDB,
|
||||||
|
MWR_SFDB,
|
||||||
|
AVQ_SFDB
|
||||||
|
FROM SD_QEC_R
|
||||||
|
WHERE IS_DELETED = 0
|
||||||
|
AND STCD IN
|
||||||
|
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
|
||||||
|
#{stcd}
|
||||||
|
</foreach>
|
||||||
|
AND TM >= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
AND TM < TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
<?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.SdQecdaySMapper">
|
||||||
|
|
||||||
|
<select id="selectRawData" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
STCD,
|
||||||
|
DT,
|
||||||
|
QEC AS VALUE
|
||||||
|
FROM SD_QECDAY_S
|
||||||
|
WHERE IS_DELETED = 0
|
||||||
|
AND STCD IN
|
||||||
|
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
|
||||||
|
#{stcd}
|
||||||
|
</foreach>
|
||||||
|
AND DT >= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
AND DT < TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
AND QEC IS NOT NULL
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 生态流量达标数据:返回三种 SFDB 用于计算各部达标率 -->
|
||||||
|
<select id="selectComplianceData" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
STCD,
|
||||||
|
SFDB,
|
||||||
|
MWR_SFDB,
|
||||||
|
AVQ_SFDB
|
||||||
|
FROM SD_QECDAY_S
|
||||||
|
WHERE IS_DELETED = 0
|
||||||
|
AND STCD IN
|
||||||
|
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
|
||||||
|
#{stcd}
|
||||||
|
</foreach>
|
||||||
|
AND DT >= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
AND DT < TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
Loading…
Reference in New Issue
Block a user