Merge branch 'dev-tw'

This commit is contained in:
tangwei 2026-08-07 13:46:45 +08:00
commit 33dd34edcd
89 changed files with 6727 additions and 209 deletions

View File

@ -68,9 +68,10 @@ public class SecurityConfig {
.requestMatchers("/dict/cache/**").permitAll()
.requestMatchers("/sys/psbmodulelbb/**").permitAll()
.requestMatchers("/base/operationLog/**").permitAll()
.requestMatchers("/system/**").permitAll()
// .requestMatchers("/system/**").permitAll()
.requestMatchers("/qgcExport/**").permitAll()
// .requestMatchers("/eng/**").permitAll()
// .requestMatchers("/eq/**").permitAll()
.requestMatchers("/eq/**").permitAll()
// .requestMatchers("/env/**").permitAll()
// .requestMatchers("/warn/**").permitAll()
// .requestMatchers("/threedroamb/**").permitAll()

View File

@ -299,4 +299,12 @@ public class SwaggerConfig {
.packagesToScan("com.yfd.platform.qgc_lygk.along.controller")
.build();
}
@Bean
public GroupedOpenApi groupExportApi() {
return GroupedOpenApi.builder()
.group("7. 综合导出")
.packagesToScan("com.yfd.platform.qgc_export.controller")
.build();
}
}

View File

@ -13,7 +13,7 @@ import java.util.Map;
public class EngStInfoResultVo {
@Schema(description = "主信息兼容对象")
private Map<String, Object> msStbprpT = new LinkedHashMap<>();
private EngVmsstbprptVo msStbprpT;
@Schema(description = "测值列表")
private List<Object> stcdValVoList = new ArrayList<>();

View File

@ -14,8 +14,14 @@ public class EngVmsstbprptVo implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String sttp;
private String sttpCode;
private String sttpMap;
private String logo;
private String sttpName;
private String sttpFullPath;
private String sttpTreeLevel;
private String rvcdFullPath;
private String stcd;
private String stnm;
private String ennm;

View File

@ -80,7 +80,7 @@ public interface ISdEngInfoBHService extends IService<SdEngInfoBH> {
List<SdEngInfoBH> selectRegDropdown(SdEngInfoBHRequest sdEngInfoBHRequest);
EngBaseInfoVo getStInfoByStcd(String stcd);
EngVmsstbprptVo getStInfoByStcd(String stcd);
EngStInfoResultVo getStcdInfo(String stcd);

View File

@ -1001,68 +1001,269 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl<SdEngInfoBHMapper, SdEng
}
@Override
public EngBaseInfoVo getStInfoByStcd(String stcd) {
SdEngInfoBH entity = this.getById(stcd);
if (entity == null) {
return null;
public EngVmsstbprptVo getStInfoByStcd(String stcd) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT *")
.append(" FROM (")
.append(buildVmsstbprptViewSqlNew())
.append(") t WHERE 1 = 1 AND t.STCD = #{map.stcd}");
EngVmsstbprptVo vo = microservicDynamicSQLMapper.getOneBySqlWithResultType(sql.toString(), Collections.singletonMap("stcd", stcd), EngVmsstbprptVo.class);
// SdEngInfoBH entity = this.getById(stcd);
// if (entity == null) {
// return null;
// }
//
// EngBaseInfoVo vo = new EngBaseInfoVo();
// BeanUtil.copyProperties(entity, vo);
// vo.setId(entity.getStcd());
// vo.setStnm(entity.getEnnm());
//
// String sql = "SELECT " +
// "sttp.ID AS sttp, " +
// "sttp.STTP_CODE AS sttpCode, " +
// "sttp.STTP_NAME AS sttpName, " +
// "sttp.FULL_PATH AS sttpFullPath, " +
// "sttp.TREE_LEVEL AS sttpTreeLevel, " +
// "hb.BASENAME AS baseName, " +
// "hbrv.HBRVNM AS hbrvcdName, " +
// "rv.RVNM AS rvcdName, " +
// "rv.PATH AS rvcdFullPath, " +
// "addv.ADDVNM AS addvcdName, " +
// "addv.PATH AS addvcdFullPath, " +
// "country.COUNTRY_NAME AS countryName, " +
// "hy.HYNM AS hynm, " +
// "topHy.HYNM AS topHynm " +
// "FROM SD_ENGINFO_B_H eng " +
// "LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = 'ENG' " +
// " AND NVL(sttp.IS_DELETED, 0) = 0 " +
// " AND NVL(sttp.ENABLE, 1) = 1 " +
// "LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID " +
// "LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD AND hbrv.BASEID = eng.BASE_ID " +
// " AND NVL(hbrv.ENABLED, 1) = 1 " +
// "LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = eng.RVCD " +
// "LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = eng.ADDVCD " +
// "LEFT JOIN SD_COUNTRY_B country ON country.COUNTRY_ID = eng.COUNTRY AND NVL(country.ENABLED, 1) = 1 " +
// "LEFT JOIN SD_HYCD_DIC hy ON hy.HYCD = eng.HYCD " +
// "LEFT JOIN SD_HYCD_DIC topHy ON topHy.HYCD = eng.TOP_HYCD " +
// "WHERE eng.STCD = #{map.stcd}";
//
// EngBaseInfoVo extra = microservicDynamicSQLMapper.getOneBySqlWithResultType(
// sql, java.util.Collections.singletonMap("stcd", stcd), EngBaseInfoVo.class);
// if (extra != null) {
// BeanUtil.copyProperties(extra, vo, CopyOptions.create().setIgnoreNullValue(true));
// }
//
// vo.setSttpCode("ENG");
// vo.setBlprdCode(entity.getBlprd() == null ? null : String.valueOf(entity.getBlprd()));
// vo.setBlprdCcode(entity.getBlprd() == null ? null : String.valueOf(entity.getBlprd()));
// vo.setBlprdName(resolveBlprdName(entity.getBlprd()));
// vo.setBlprdCname(vo.getBlprdName());
// vo.setDtinName(resolveDtinName(entity.getDtin()));
// vo.setBldsttName(resolveBldsttName(entity.getBldstt()));
// vo.setBldsttCcode(entity.getBldsttCode() == null ? null : String.valueOf(entity.getBldsttCode()));
// vo.setBldsttCcodeName(resolveBldsttCodeName(entity.getBldsttCode()));
// vo.setDvtpName(resolveDvtpName(entity.getDvtp()));
// vo.setRgcpName(resolveRgcpName(entity.getRgcp()));
// vo.setEngtpName(resolveEngtpName(entity.getEngtp()));
// vo.setPrscName(resolvePrscName(entity.getPrsc()));
// vo.setScrscName(resolveScrscName(entity.getScrsc()));
// vo.setPrgrName(resolvePrgrName(entity.getPrgr()));
return vo;
}
EngBaseInfoVo vo = new EngBaseInfoVo();
BeanUtil.copyProperties(entity, vo);
vo.setId(entity.getStcd());
vo.setStnm(entity.getEnnm());
String sql = "SELECT " +
"sttp.ID AS sttp, " +
"sttp.STTP_CODE AS sttpCode, " +
"sttp.STTP_NAME AS sttpName, " +
private String buildVmsstbprptViewSqlNew() {
return "SELECT " +
"eng.STCD AS id, " +
"'ENG' AS sttpCode, " +
"'ENG' AS sttpMap, " +
"sttp.FULL_PATH AS sttpFullPath, " +
"sttp.TREE_LEVEL AS sttpTreeLevel, " +
"sttp.ID AS sttp, " +
"'电站' AS sttpName, " +
"eng.STCD AS stcd, " +
"eng.ENNM AS stnm, " +
"eng.LGTD AS lgtd, " +
"eng.LTTD AS lttd, " +
"eng.REACHCD AS reachcd, " +
"eng.REACHCD_NAME AS reachcdName, " +
"eng.ENNM AS ennm, " +
"eng.BASE_ID AS baseId, " +
"hb.BASENAME AS baseName, " +
"eng.HBRVCD AS hbrvcd, " +
"hbrv.HBRVNM AS hbrvcdName, " +
"eng.RVCD AS rvcd, " +
"eng.LOGO AS logo, " +
"rv.RVNM AS rvcdName, " +
"rv.PATH AS rvcdFullPath, " +
"addv.ADDVNM AS addvcdName, " +
"addv.PATH AS addvcdFullPath, " +
"country.COUNTRY_NAME AS countryName, " +
"hy.HYNM AS hynm, " +
"topHy.HYNM AS topHynm " +
"topHy.HYNM AS topHynm, " +
"hy.HYCD AS hycd, " +
"topHy.HYCD AS topHycd, " +
"eng.STLC AS stlc, " +
"eng.JCDT AS jcdt, " +
"eng.PIODT AS piodt, " +
"eng.AIODT AS aiodt, " +
"eng.BLDSTT AS bldstt, " +
"eng.BLDSTT_CODE AS bldsttCcode, " +
"CASE " +
" WHEN eng.BLDSTT_CODE = 0 OR eng.BLDSTT_CODE IS NULL THEN '未建/规划' " +
" WHEN eng.BLDSTT_CODE = 1 THEN '在建' " +
" WHEN eng.BLDSTT_CODE = 2 THEN '已建' " +
" ELSE NULL END AS bldsttCcodeName, " +
"eng.NRUPAR AS nrupar, " +
"eng.DSTCRVR AS dstcrvr, " +
"eng.AVYRP AS avyrp, " +
"eng.AVW AS avw, " +
"eng.AVQ AS avq, " +
"eng.AVQ01 AS avq01, " +
"eng.AVQ02 AS avq02, " +
"eng.AVQ03 AS avq03, " +
"eng.AVQ04 AS avq04, " +
"eng.AVQ05 AS avq05, " +
"eng.AVQ06 AS avq06, " +
"eng.AVQ07 AS avq07, " +
"eng.AVQ08 AS avq08, " +
"eng.AVQ09 AS avq09, " +
"eng.AVQ10 AS avq10, " +
"eng.AVQ11 AS avq11, " +
"eng.AVQ12 AS avq12, " +
"rbh.OBMXQ AS obmxq, " +
"rbh.OBMNQ AS obmnq, " +
"rbh.HMXQ AS hmxq, " +
"eng.DSNFQFRQ AS dsnfqfrq, " +
"eng.DSRCIN AS dsrcin, " +
"eng.CKFQFQR AS ckfqfqr, " +
"eng.CHRCIN AS chrcin, " +
"eng.OBMXW3 AS obmxw3, " +
"eng.DSW3 AS dsw3, " +
"eng.CKW3 AS ckw3, " +
"eng.AVSD AS avsd, " +
"eng.AVS AS avs, " +
"rbh.OBMXS AS obmxs, " +
"rbh.CKFLZ AS ckflz, " +
"rbh.DSFLZ AS dsflz, " +
"rbh.NORMZ AS normz, " +
"rbh.UZFC AS uzfc, " +
"eng.FSLTDZ AS fsltdz, " +
"eng.YDOWNZ AS ydownz, " +
"rbh.DDZ AS ddz, " +
"eng.NRZAR AS nrzar, " +
"eng.BCKLEN AS bcklen, " +
"rbh.TTCP AS ttcp, " +
"rbh.NMZCP AS nmzcp, " +
"b.VAL AS fldcpStr, " +
"rbh.ACTCP AS actcp, " +
"rbh.DDCP AS ddcp, " +
"eng.CPSC AS cpsc, " +
"eng.PRSC AS prsc, " +
"eng.RGCP AS rgcp, " +
"CASE eng.RGCP " +
" WHEN '1' THEN '多年调节' " +
" WHEN '2' THEN '年调节' " +
" WHEN '3' THEN '不完全年调节' " +
" WHEN '4' THEN '径流式(无调节)' " +
" WHEN '5' THEN '季调节' " +
" WHEN '6' THEN '月调节' " +
" WHEN '7' THEN '周调节' " +
" WHEN '8' THEN '日调节' " +
" WHEN '9' THEN '其他' " +
" ELSE NULL END AS rgcpName, " +
"eng.DSMXQ AS dsflzmxq, " +
"rbh.DSFLZXYZ AS dsflzxyz, " +
"eng.CHMXQ AS ckflzmxq, " +
"rbh.CKFLZXYZ AS ckflzxyz, " +
"rbh.KSQADJQ AS ksqadjq, " +
"rbh.KSQXYZ AS ksqxyz, " +
"eng.GEMAXRQ AS gemaxrq, " +
"rbh.MXGEQXYZ AS mxgeqxyz, " +
"pwr.TTPWR AS ttpwr, " +
"pwr.GNCNT AS gncnt, " +
"eng.GNTP AS gntp, " +
"pwr.GRNTPWR AS grntpwr, " +
"pwr.YRGE AS yrge, " +
"pwr.USHR AS ushr, " +
"pwr.K AS k, " +
"eng.DMTP AS dmtp, " +
"eng.DMCREL AS dmcrel, " +
"eng.MXDMHG AS mxdmhg, " +
"eng.DMLEN AS dmlen, " +
"pwr.SGYGE AS sgyge, " +
"UNYGE AS unyge, " +
"eng.GAP AS gap, " +
"eng.DVTP AS dvtp, " +
"CASE eng.DVTP " +
" WHEN '1' THEN '堤坝式' " +
" WHEN '2' THEN '引水式' " +
" WHEN '3' THEN '混合式' " +
" ELSE NULL END AS dvtpName, " +
"eng.DTIN AS dtin, " +
"eng.RUN_STATE AS runSTATE, " +
"eng.DTIN_ENV AS dtinEnv, " +
"CASE NVL(eng.DTIN, 0) " +
" WHEN 0 THEN '否' " +
" WHEN 1 THEN '是' " +
" ELSE NULL END AS dtinName, " +
"NVL(hb.ORDER_INDEX, 999999) AS baseStepSort, " +
"NVL(along.ORDER_INDEX, 999999) AS rvcdStepSort, " +
"NVL(rstSort.SORT, 999999) AS rstcdStepSort, " +
"NVL(siteSort.SORT, 999999) AS siteStepSort " +
"FROM SD_ENGINFO_B_H eng " +
"LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = 'ENG' " +
" AND NVL(sttp.IS_DELETED, 0) = 0 " +
" AND NVL(sttp.ENABLE, 1) = 1 " +
"LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID " +
"LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " +
"LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD AND hbrv.BASEID = eng.BASE_ID " +
" AND NVL(hbrv.ENABLED, 1) = 1 " +
" AND NVL(hbrv.IS_DELETED, 0) = 0 AND NVL(hbrv.ENABLED, 1) = 1 " +
"LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = eng.RVCD " +
"LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = eng.ADDVCD " +
"LEFT JOIN SD_COUNTRY_B country ON country.COUNTRY_ID = eng.COUNTRY AND NVL(country.ENABLED, 1) = 1 " +
"LEFT JOIN SD_HYCD_DIC hy ON hy.HYCD = eng.HYCD " +
"LEFT JOIN SD_HYCD_DIC topHy ON topHy.HYCD = eng.TOP_HYCD " +
"WHERE eng.STCD = #{map.stcd}";
EngBaseInfoVo extra = microservicDynamicSQLMapper.getOneBySqlWithResultType(
sql, java.util.Collections.singletonMap("stcd", stcd), EngBaseInfoVo.class);
if (extra != null) {
BeanUtil.copyProperties(extra, vo, CopyOptions.create().setIgnoreNullValue(true));
}
vo.setSttpCode("ENG");
vo.setBlprdCode(entity.getBlprd() == null ? null : String.valueOf(entity.getBlprd()));
vo.setBlprdCcode(entity.getBlprd() == null ? null : String.valueOf(entity.getBlprd()));
vo.setBlprdName(resolveBlprdName(entity.getBlprd()));
vo.setBlprdCname(vo.getBlprdName());
vo.setDtinName(resolveDtinName(entity.getDtin()));
vo.setBldsttName(resolveBldsttName(entity.getBldstt()));
vo.setBldsttCcode(entity.getBldsttCode() == null ? null : String.valueOf(entity.getBldsttCode()));
vo.setBldsttCcodeName(resolveBldsttCodeName(entity.getBldsttCode()));
vo.setDvtpName(resolveDvtpName(entity.getDvtp()));
vo.setRgcpName(resolveRgcpName(entity.getRgcp()));
vo.setEngtpName(resolveEngtpName(entity.getEngtp()));
vo.setPrscName(resolvePrscName(entity.getPrsc()));
vo.setScrscName(resolveScrscName(entity.getScrsc()));
vo.setPrgrName(resolvePrgrName(entity.getPrgr()));
return vo;
"LEFT JOIN SD_HYCD_DIC hy ON hy.HYCD = eng.HYCD AND NVL(hy.IS_DELETED, 0) = 0 AND NVL(hy.ENABLED, 1) = 1 " +
"LEFT JOIN SD_HYCD_DIC topHy ON topHy.HYCD = eng.TOP_HYCD AND NVL(topHy.IS_DELETED, 0) = 0 AND NVL(topHy.ENABLED, 1) = 1 " +
"LEFT JOIN SD_PWR_B_H pwr ON pwr.STCD = eng.STCD AND NVL(pwr.IS_DELETED, 0) = 0 " +
"LEFT JOIN SD_RSVRFCCH_B_H rbh ON rbh.STCD = eng.STCD " +
"LEFT JOIN ( SELECT STCD\n" +
" , MAX(CASE WHEN RN = 1 THEN TM END) AS TM\n" +
" , MAX(CASE WHEN RN = 1 THEN VAL END) AS VAL\n" +
" , MAX(VLSR) AS VLSR\n" +
" , MAX(VLSR_TM) AS VLSR_TM\n" +
" , LISTAGG('起始时间' || TO_CHAR(TM, 'YYYY-MM-DD') || '' || VAL || '' ||\n" +
" CASE WHEN VLSR IS NOT NULL THEN '来源' || VLSR ELSE '' END ||\n" +
" CASE WHEN VLSR_TM IS NOT NULL THEN ',来源时间' || TO_CHAR(VLSR_TM, 'YYYY-MM-DD') ELSE '' END,\n" +
" CHR(10)) WITHIN GROUP (ORDER BY TM DESC) AS ALL_VAL\n" +
" FROM ( SELECT STCD\n" +
" , TM\n" +
" , MAX(VLSR) AS VLSR\n" +
" , MAX(VLSR_TM) AS VLSR_TM\n" +
" , LISTAGG(\n" +
" SUBSTR(BGMD, 0, 2) || '月' || SUBSTR(BGMD, 3, 2) || '日' || '~' || SUBSTR(EDMD, 0, 2) || '月' ||\n" +
" SUBSTR(EDMD, 3, 2) || '日' || '限值为' ||\n" +
" (CASE WHEN FLDCPMN IS NOT NULL THEN (DECODE(SUBSTR(FLDCPMN, 1, 1), '.', '0' || FLDCPMN, FLDCPMN) ||\n" +
" '~' || DECODE(SUBSTR(FLDCP, 1, 1), '.', '0' || FLDCP, FLDCP))\n" +
" ELSE TO_CHAR(FLDCP) END), '') WITHIN GROUP (ORDER BY BGMD) AS VAL\n" +
" , ROW_NUMBER() OVER (PARTITION BY STCD ORDER BY TM DESC) RN\n" +
" FROM SD_RSVRFSR_B\n" +
" WHERE BGMD IS NOT NULL\n" +
" AND EDMD IS NOT NULL\n" +
" AND FLDCPMN IS NOT NULL\n" +
" AND FLDCPMN > 0\n" +
" GROUP BY STCD, TM ) temp\n" +
" GROUP BY STCD) b ON b.STCD = eng.STCD " +
"LEFT JOIN MS_ALONG_B along ON along.BASEID = eng.BASE_ID AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 " +
"LEFT JOIN ( " +
" SELECT det.SORT,cfg.BASEID, cfg.RVCD, det.STCD " +
" FROM MS_ALONGDET_B det " +
" INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " +
" WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " +
") rstSort ON rstSort.BASEID = eng.BASE_ID AND rstSort.STCD = eng.STCD " +
"LEFT JOIN ( " +
" SELECT det.SORT,cfg.BASEID, cfg.RVCD, det.STCD " +
" FROM MS_ALONGDET_B det " +
" INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " +
" WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " +
") siteSort ON siteSort.BASEID = eng.BASE_ID AND siteSort.STCD = eng.STCD " +
"WHERE NVL(eng.IS_DELETED, 0) = 0 ";
}
@Override
@ -1070,27 +1271,27 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl<SdEngInfoBHMapper, SdEng
if (StrUtil.isBlank(stcd)) {
return null;
}
EngVmsstbprptVo baseInfo = getStInfoByStcd(stcd);
if (baseInfo == null) {
return null;
}
Map<String, Object> stInfoMap = microservicDynamicSQLMapper.getOneBySql(buildStcdInfoViewSql(), Collections.singletonMap("stcd", stcd));
if (stInfoMap != null && !stInfoMap.isEmpty()) {
EngStInfoResultVo result = new EngStInfoResultVo();
result.setMsStbprpT(buildCamelCaseMsStbprpTMap(stInfoMap));
result.setMsStbprpT(baseInfo);
result.setStBaseInfo(buildStBaseInfo(stInfoMap));
return result;
}
EngBaseInfoVo baseInfo = getStInfoByStcd(stcd);
if (baseInfo == null) {
return null;
}
EngStBaseInfoVo stBaseInfo = new EngStBaseInfoVo();
stBaseInfo.setStcd(baseInfo.getStcd());
stBaseInfo.setStnm(baseInfo.getStnm());
stBaseInfo.setSttpCode(baseInfo.getSttpCode());
stBaseInfo.setSttpName(baseInfo.getSttpName());
stBaseInfo.setSttpFullPath(baseInfo.getSttpFullPath());
stBaseInfo.setSttpTreeLevel(baseInfo.getSttpTreeLevel());
// stBaseInfo.setSttpFullPath(baseInfo.getSttpFullPath());
// stBaseInfo.setSttpTreeLevel(baseInfo.getSttpTreeLevel());
stBaseInfo.setStCode(null);
stBaseInfo.setStName(null);
stBaseInfo.setBaseId(baseInfo.getBaseId());
@ -1340,7 +1541,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl<SdEngInfoBHMapper, SdEng
"rbh.KSQXYZ AS ksqxyz, " +
"eng.GEMAXRQ AS gemaxrq, " +
"rbh.MXGEQXYZ AS mxgeqxyz, " +
"eng.YRGEB AS ttpwr, " +
"pwr.TTPWR AS ttpwr, " +
"pwr.GNCNT AS gncnt, " +
"eng.GNTP AS gntp, " +
"pwr.GRNTPWR AS grntpwr, " +
@ -3120,7 +3321,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl<SdEngInfoBHMapper, SdEng
"eng.LGTD AS lgtd, " +
"eng.LTTD AS lttd, " +
"eng.ELEV AS dtmel, " +
"eng.YRGEB AS ttpwr, " +
"pwr.TTPWR AS ttpwr, " +
"'ENG' AS sttpMap, " +
"CASE WHEN eng.PRSC IN (1, 2) THEN 1 WHEN eng.PRSC = 3 THEN 2 END AS endInstalledType, " +
"CASE " +
@ -3138,6 +3339,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl<SdEngInfoBHMapper, SdEng
"NVL(rstSort.SORT, 999999) AS rstcdStepSort, " +
"NVL(siteSort.SORT, 999999) AS siteStepSort " +
"FROM SD_ENGINFO_B_H eng " +
"LEFT JOIN SD_PWR_B_H pwr ON pwr.STCD = eng.STCD AND NVL(pwr.IS_DELETED, 0) = 0 " +
"LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " +
"LEFT JOIN MS_ALONG_B along ON along.BASEID = eng.BASE_ID AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 " +
"LEFT JOIN ( " +

View File

@ -1,5 +1,6 @@
package com.yfd.platform.qgc_base.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yfd.platform.qgc_base.domain.SdEngInfoBH;
import com.yfd.platform.qgc_base.domain.SdHydrobase;
import com.yfd.platform.qgc_base.domain.SdHbrvDic;
@ -363,25 +364,40 @@ public class TreeStructureServiceImpl implements ITreeStructureService {
.collect(Collectors.toList());
}
// 2. 预加载所有流域下的电站按流域分组
Map<String, List<SdEngInfoBH>> rvcdEngMap = new HashMap<>();
for (SdRvcdDic rvcdDic : rvcdList) {
List<SdEngInfoBH> engList = engInfoBHMapper.selectByRvcd(null,rvcdDic.getRvcd());
if (rvcdList.isEmpty()) {
return result;
}
// 如果提供了电站名称参数进行模糊匹配筛选
if (StringUtils.hasText(engName) && engList != null) {
engList = engList.stream()
.filter(eng -> eng.getEnnm() != null && eng.getEnnm().contains(engName))
// 2. 收集所有流域编码一次批量查询替代 N 次循环查询
List<String> rvcdCodeList = rvcdList.stream()
.map(SdRvcdDic::getRvcd)
.collect(Collectors.toList());
}
List<SdEngInfoBH> allEngList = engInfoBHMapper.selectList(new LambdaQueryWrapper<SdEngInfoBH>().in(SdEngInfoBH::getReachcd, rvcdCodeList));
// List<SdEngInfoBH> allEngList = engInfoBHMapper.selectByRvcdList(rvcdCodeList);
if (engList != null && !engList.isEmpty()) {
rvcdEngMap.put(rvcdDic.getRvcd(), engList);
// 3. 按流域分组同时在内存中过滤电站名称
Map<String, List<SdEngInfoBH>> rvcdEngMap = new LinkedHashMap<>();
if (allEngList != null) {
for (SdEngInfoBH eng : allEngList) {
String engRvcd = eng.getReachcd();
if (!StringUtils.hasText(engRvcd) || !rvcdCodeList.contains(engRvcd)) {
continue;
}
if (StringUtils.hasText(engName) && eng.getEnnm() != null
&& !eng.getEnnm().contains(engName)) {
continue;
}
rvcdEngMap.computeIfAbsent(engRvcd, k -> new ArrayList<>()).add(eng);
}
}
// 3. 构建二级树形结构流域 -> 电站
// 4. 构建二级树形结构流域 -> 电站
for (SdRvcdDic rvcdDic : rvcdList) {
List<SdEngInfoBH> engList = rvcdEngMap.get(rvcdDic.getRvcd());
if (engList == null || engList.isEmpty()) {
continue;
}
Map<String, Object> rvcdNode = new LinkedHashMap<>();
rvcdNode.put("type", "RVCD");
rvcdNode.put("code", rvcdDic.getRvcd());
@ -389,22 +405,14 @@ public class TreeStructureServiceImpl implements ITreeStructureService {
rvcdNode.put("path", rvcdDic.getPath());
rvcdNode.put("orgLevel", 0);
rvcdNode.put("grd", rvcdDic.getGrd());
// rvcdNode.put("lgtd", rvcdDic.getLgtd());
// rvcdNode.put("lttd", rvcdDic.getLttd());
rvcdNode.put("parentId", "0");
// 获取该流域下的所有电站
List<SdEngInfoBH> engList = rvcdEngMap.get(rvcdDic.getRvcd());
List<Map<String, Object>> engChildren = new ArrayList<>();
if (engList != null && !engList.isEmpty()) {
List<Map<String, Object>> engChildren = new ArrayList<>(engList.size());
for (SdEngInfoBH eng : engList) {
Map<String, Object> engNode = new LinkedHashMap<>();
engNode.put("type", "STATION");
engNode.put("code", eng.getStcd());
engNode.put("name", eng.getEnnm());
// engNode.put("lgtd", eng.getLgtd());
// engNode.put("lttd", eng.getLttd());
engNode.put("elev", eng.getElev());
engNode.put("usfl", eng.getUsfl());
engNode.put("bldstt", eng.getBldstt());
@ -413,14 +421,10 @@ public class TreeStructureServiceImpl implements ITreeStructureService {
engNode.put("orgLevel", 1);
engChildren.add(engNode);
}
}
// 只有当该流域下有电站时才添加该流域节点
if (!engChildren.isEmpty()) {
rvcdNode.put("children", engChildren);
result.add(rvcdNode);
}
}
return result;
}

View File

@ -24,6 +24,7 @@ import com.yfd.platform.qgc_eng.eq.entity.vo.StcdInfoVo;
import com.yfd.platform.qgc_eng.eq.entity.vo.WbsbVo;
import java.util.List;
import java.util.Map;
public interface EngEqDataService {
@ -53,6 +54,26 @@ public interface EngEqDataService {
EngEqRuleVo getEngLimit(DataSourceRequest dataSourceRequest);
/**
* 根据测站编码和时间范围查询生态流量限值及来源
*
* @param stcd 测站编码
* @param startTime 开始时间 yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间 yyyy-MM-dd HH:mm:ss
* @return 生态流量限值信息含环保部/水利部限值及来源多年平均流量及来源
*/
EngEqRuleVo getEngLimitByTime(String stcd, String startTime, String endTime);
/**
* 批量查询生态流量限值及来源
*
* @param stcdList 测站编码列表
* @param startTime 开始时间 yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间 yyyy-MM-dd HH:mm:ss
* @return STCD 生态流量限值信息
*/
Map<String, EngEqRuleVo> getEngLimitByTimeBatch(List<String> stcdList, String startTime, String endTime);
QgcQecStaticVo getQgcStaticData(DataSourceRequest dataSourceRequest, String type);
DataSourceResult<WbsbVo> getWbsbList(DataSourceRequest dataSourceRequest);

View File

@ -47,9 +47,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Date;
import java.math.BigDecimal;
import java.math.RoundingMode;
@ -57,6 +59,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
@Service
public class EngEqDataServiceImpl implements EngEqDataService {
@ -978,6 +981,143 @@ public class EngEqDataServiceImpl implements EngEqDataService {
return vo;
}
@Override
public EngEqRuleVo getEngLimitByTime(String stcd, String startTime, String endTime) {
EngEqRuleVo vo = new EngEqRuleVo().setStcd(stcd);
// 1. 查询电站信息多年平均流量
Map<String, Object> stationParamMap = new HashMap<>();
stationParamMap.put("stcd", stcd);
List<Map<String, Object>> stationList = microservicDynamicSQLMapper.pageAllList(
null,
"SELECT STCD, ENNM AS STNM, AVQ, AVQ_SRC FROM SD_ENGINFO_B_H " +
"WHERE STCD = #{map.stcd} AND NVL(IS_DELETED, 0) = 0",
stationParamMap
);
if (CollUtil.isNotEmpty(stationList)) {
Map<String, Object> station = stationList.get(0);
vo.setStnm(asString(station.get("STNM")));
BigDecimal avq = toBigDecimal(station.get("AVQ"));
if (avq != null) {
vo.setAvqStr(stripTrailingZeros(avq));
}
String avqSrc = asString(station.get("AVQ_SRC"));
if (StrUtil.isNotBlank(avqSrc)) {
vo.setAvqVlsr(avqSrc);
}
}
// 2. 查询环保部限值规则
List<Map<String, Object>> eqmnList = queryEngLimitRuleDetails(stcd, startTime, endTime, "EQMN");
vo.setEqmnStr(buildEngLimitValue(eqmnList));
vo.setVlsr(buildEngLimitVlsr(eqmnList));
// 3. 查询水利部限值规则
List<Map<String, Object>> eqmnMwrList = queryEngLimitRuleDetails(stcd, startTime, endTime, "EQMNMWR");
vo.setEqmnmwrStr(buildEngLimitValue(eqmnMwrList));
vo.setMwrVlsr(buildEngLimitVlsr(eqmnMwrList));
return vo;
}
@Override
public Map<String, EngEqRuleVo> getEngLimitByTimeBatch(List<String> stcdList, String startTime, String endTime) {
Map<String, EngEqRuleVo> result = new LinkedHashMap<>();
if (CollUtil.isEmpty(stcdList)) {
return result;
}
// 把列表转为 ('A','B','C') 格式的字符串
String stcdStr = stcdList.stream()
.map(s -> "'" + s + "'") // 手动加单引号
.collect(Collectors.joining(","));
String sql = "SELECT STCD, ENNM AS STNM, AVQ, AVQ_SRC FROM SD_ENGINFO_B_H " +
"WHERE STCD IN (" + stcdStr + ") " +
"AND NVL(IS_DELETED, 0) = 0";
// 此时 paramMap 甚至可以传空因为值已经写死在 SQL 里了
List<Map<String, Object>> stationList = microservicDynamicSQLMapper.pageAllList(null, sql, new HashMap<>());
Map<String, Map<String, Object>> stationMap = new LinkedHashMap<>();
if (CollUtil.isNotEmpty(stationList)) {
for (Map<String, Object> row : stationList) {
stationMap.put(asString(row.get("STCD")), row);
}
}
// 2. 逐个查询限值规则并组装结果
for (String stcd : stcdList) {
EngEqRuleVo vo = new EngEqRuleVo().setStcd(stcd);
Map<String, Object> station = stationMap.get(stcd);
if (station != null) {
vo.setStnm(asString(station.get("STNM")));
BigDecimal avq = toBigDecimal(station.get("AVQ"));
if (avq != null) {
vo.setAvqStr(stripTrailingZeros(avq));
}
String avqSrc = asString(station.get("AVQ_SRC"));
if (StrUtil.isNotBlank(avqSrc)) {
vo.setAvqVlsr(avqSrc);
}
}
List<Map<String, Object>> eqmnList = queryEngLimitRuleDetails(stcd, startTime, endTime, "EQMN");
vo.setEqmnStr(buildEngLimitValue(eqmnList));
vo.setVlsr(buildEngLimitVlsr(eqmnList));
List<Map<String, Object>> eqmnMwrList = queryEngLimitRuleDetails(stcd, startTime, endTime, "EQMNMWR");
vo.setEqmnmwrStr(buildEngLimitValue(eqmnMwrList));
vo.setMwrVlsr(buildEngLimitVlsr(eqmnMwrList));
result.put(stcd, vo);
}
return result;
}
/**
* 构建生态流量限值展示不含前缀仅时间段和限值
*/
private String buildEngLimitValue(List<Map<String, Object>> detailList) {
if (CollUtil.isEmpty(detailList)) {
return "无限值要求";
}
List<String> parts = new ArrayList<>();
for (Map<String, Object> row : detailList) {
String beginTime = asString(row.get("BEGIN_TIME"));
String endTime = asString(row.get("END_TIME"));
BigDecimal minVal = toBigDecimal(row.get("MIN_VAL"));
if (StrUtil.length(beginTime) != 4 || StrUtil.length(endTime) != 4 || minVal == null) {
continue;
}
parts.add(String.format("%s月%s日~%s月%s日限值为:%s",
beginTime.substring(0, 2), beginTime.substring(2, 4),
endTime.substring(0, 2), endTime.substring(2, 4),
stripTrailingZeros(minVal)));
}
if (parts.isEmpty()) {
return "无限值要求";
}
return String.join(";", parts);
}
/**
* 提取限值来源VLSR去重后用逗号拼接
*/
private String buildEngLimitVlsr(List<Map<String, Object>> detailList) {
if (CollUtil.isEmpty(detailList)) {
return "";
}
Set<String> vlsrSet = new LinkedHashSet<>();
for (Map<String, Object> row : detailList) {
String vlsr = asString(row.get("VLSR"));
if (StrUtil.isNotBlank(vlsr)) {
vlsrSet.add(vlsr);
}
}
return vlsrSet.isEmpty() ? "" : String.join(",", vlsrSet);
}
@Override
public DataSourceResult<String> getFidKendoListCust(DataSourceRequest dataSourceRequest) {
DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest();

View File

@ -531,14 +531,15 @@ public class VdMonitorServiceImpl implements VdMonitorService {
if (StrUtil.isNotBlank(filterSql)) {
sql.append(" AND ").append(filterSql).append(" ");
}
sql.append(buildVdPointOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort()));
DataSourceResult<VdPointVo> result = new DataSourceResult<>();
DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.RSTCD");
DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.rstcds");
if (scopeApplyResult.isEmptyResult()) {
result.setData(new ArrayList<>());
result.setTotal(0L);
return result;
}
sql.append(buildVdPointOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort()));
List<VdPointVo> list = microservicDynamicSQLMapper.pageAllListWithResultType(
page, sql.toString(), paramMap, VdPointVo.class);
result.setData(list);

View File

@ -1138,7 +1138,7 @@ public class WarnDataServiceImpl implements WarnDataService {
"eng.LGTD AS lgtd, " +
"eng.LTTD AS lttd, " +
"eng.ELEV AS dtmel, " +
"eng.YRGEB AS ttpwr, " +
"pwr.TTPWR AS ttpwr, " +
"'ENG' AS sttpMap, " +
"CASE WHEN eng.PRSC IN (1, 2) THEN 1 WHEN eng.PRSC = 3 THEN 2 END AS endInstalledType, " +
"CASE " +
@ -1153,6 +1153,7 @@ public class WarnDataServiceImpl implements WarnDataService {
"NVL(rstSort.SORT, 999999) AS rstcdStepSort, " +
"NVL(siteSort.SORT, 999999) AS siteStepSort " +
"FROM SD_ENGINFO_B_H eng " +
"LEFT JOIN SD_PWR_B_H pwr ON pwr.STCD = eng.STCD AND NVL(pwr.IS_DELETED, 0) = 0 " +
"LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " +
"LEFT JOIN MS_ALONG_B along ON along.BASEID = eng.BASE_ID AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 " +
"LEFT JOIN ( " +

View File

@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* 垂向水温年份数据Mapper
@ -28,4 +29,20 @@ public interface SdWtvtRMapper extends BaseMapper<SdWtvtYearVo> {
"ORDER BY STCD, TO_CHAR(TM, 'yyyy') DESC" +
"</script>")
List<SdWtvtYearVo> getWtrvDefaultYear(@Param("stcd") String stcd);
/**
* 查询各测站水深对应的高程HGT STCDWTHG 分组取最大值
*/
@Select("<script>" +
"SELECT STCD, WTHG, MAX(HGT) AS HGT " +
"FROM SD_WTVT_R " +
"WHERE IS_DELETED = 0 " +
"AND HGT IS NOT NULL " +
"AND STCD IN " +
"<foreach collection='stcdList' item='stcd' open='(' separator=',' close=')'>" +
"#{stcd}" +
"</foreach>" +
"GROUP BY STCD, WTHG" +
"</script>")
List<Map<String, Object>> selectDepthElevation(@Param("stcdList") List<String> stcdList);
}

View File

@ -3033,7 +3033,7 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService {
// paramMap.put("dtinType", dtinType);
// }
DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.RSTCD");
DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "wt.RSTCD");
if (scopeApplyResult.isEmptyResult()) {
dataSourceResult.setData(new ArrayList<>());
dataSourceResult.setTotal(0L);

View File

@ -0,0 +1,198 @@
package com.yfd.platform.qgc_export.controller;
import cn.hutool.core.util.StrUtil;
import com.yfd.platform.qgc_export.domain.ExportTask;
import com.yfd.platform.qgc_export.service.IQgcExportService;
import com.yfd.platform.qgc_export.service.ExportTaskService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 综合导出控制器
*/
@RestController
@RequestMapping("/qgcExport")
@Tag(name = "综合导出")
public class QgcExportController {
@Resource
private IQgcExportService qgcExportService;
@Resource
private ExportTaskService exportTaskService;
/**
* 原有同步导出接口保留兼容
*/
@PostMapping("/exportData")
@Operation(summary = "综合数据导出ZIP 含多 Sheet Excel— 同步返回文件流")
public void exportData(@RequestParam(required = false) String tmDimension,
@RequestParam(required = false) String dataField,
@RequestParam(required = false) String months,
@RequestParam(required = false) String stcd,
@RequestParam(required = false) String sysId,
@RequestParam(defaultValue = "true") boolean isDailyDimension,
@RequestParam(defaultValue = "false") boolean isCalcQec,
@RequestParam(required = false) String yearStatType,
@RequestParam(required = false) String year,
@RequestParam(required = false) String baseId,
HttpServletResponse response) {
List<String> dataFields = parseCsv(dataField);
List<String> monthList = parseCsv(months);
List<String> stationList = parseCsv(stcd);
List<String> baseIdList = parseCsv(baseId);
if (tmDimension == null || tmDimension.isEmpty()) {
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,
isDailyDimension, tmDimension, response);
}
// ======================== 异步导出三接口 ========================
/**
* 接口一创建导出任务返回唯一任务 ID
*/
@PostMapping("/create")
@Operation(summary = "创建异步导出任务,返回 taskId")
public Map<String, Object> createTask(@RequestParam(required = false) String tmDimension,
@RequestParam(required = false) String dataField,
@RequestParam(required = false) String months,
@RequestParam(required = false) String stcd,
@RequestParam(defaultValue = "true") boolean isDailyDimension,
@RequestParam(required = false) String yearStatType,
@RequestParam(required = false) String year,
@RequestParam(required = false) String baseId) {
List<String> dataFields = parseCsv(dataField);
List<String> monthList = parseCsv(months);
List<String> stationList = parseCsv(stcd);
List<String> baseIdList = parseCsv(baseId);
if (tmDimension == null || tmDimension.isEmpty()) {
tmDimension = "month";
}
String taskId;
if ("year".equals(tmDimension)) {
List<String> yearStatTypeList = parseCsv(yearStatType);
List<String> yearList = parseCsv(year);
taskId = exportTaskService.submitYearExport(yearStatTypeList, yearList, stationList, baseIdList);
} else {
taskId = exportTaskService.submitMonthExport(dataFields, monthList, stationList,
isDailyDimension, tmDimension);
}
Map<String, Object> result = new HashMap<>();
result.put("taskId", taskId);
return result;
}
/**
* 接口二查询任务状态
*/
@GetMapping("/status")
@Operation(summary = "查询异步导出任务状态")
public Map<String, Object> getStatus(@RequestParam String taskId) {
ExportTask task = exportTaskService.getTask(taskId);
Map<String, Object> result = new HashMap<>();
if (task == null) {
result.put("exists", false);
return result;
}
result.put("exists", true);
result.put("taskId", task.getTaskId());
result.put("status", task.getStatus().name());
result.put("statusDesc", task.getStatus().getDesc());
if (task.getErrorMessage() != null) {
result.put("errorMessage", task.getErrorMessage());
}
return result;
}
/**
* 接口三下载导出文件参数与 /exportData 一致 + taskId文件名根据 tmDimension 生成
*/
@PostMapping("/download")
@Operation(summary = "下载异步导出的 ZIP 文件")
public void download(@RequestParam String taskId,
@RequestParam(required = false) String tmDimension,
@RequestParam(required = false) String dataField,
@RequestParam(required = false) String months,
@RequestParam(required = false) String stcd,
@RequestParam(defaultValue = "true") boolean isDailyDimension,
@RequestParam(required = false) String yearStatType,
@RequestParam(required = false) String year,
@RequestParam(required = false) String baseId,
HttpServletResponse response) {
ExportTask task = exportTaskService.getTask(taskId);
if (task == null || task.getStatus() != ExportTask.Status.DONE) {
response.setStatus(404);
return;
}
if (tmDimension == null || tmDimension.isEmpty()) {
tmDimension = "month";
}
String zipFileName = getFileNamePrefix(tmDimension) + ".zip";
response.setContentType("application/zip");
response.setCharacterEncoding("UTF-8");
try {
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
} catch (IOException e) {
response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
}
try (OutputStream os = response.getOutputStream()) {
exportTaskService.downloadTask(taskId, os);
} catch (IOException e) {
response.setStatus(500);
}
}
// ======================== 工具方法 ========================
private static String getFileNamePrefix(String tmDimension) {
return switch (tmDimension) {
case "year" -> "年度报表";
case "month" -> "月度报表";
case "day" -> "日度报表";
case "hour" -> "小时报表";
default -> "未知报表";
};
}
private List<String> parseCsv(String value) {
if (StrUtil.isBlank(value)) {
return Collections.emptyList();
}
return Arrays.stream(value.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.collect(java.util.stream.Collectors.toList());
}
}

View File

@ -0,0 +1,58 @@
package com.yfd.platform.qgc_export.domain;
import java.io.File;
/**
* 导出任务 用于异步导出
*/
public class ExportTask {
public enum Status {
CREATED("已创建"),
PROCESSING("处理中"),
DONE("已完成"),
ERROR("失败");
private final String desc;
Status(String desc) { this.desc = desc; }
public String getDesc() { return desc; }
}
/** 任务唯一 ID */
private String taskId;
/** 任务状态 */
private Status status;
/** 导出完成的临时文件 */
private File file;
/** ZIP 文件名 */
private String fileName;
/** 错误信息status=ERROR 时) */
private String errorMessage;
/** 创建时间戳 */
private long createTime;
public ExportTask(String taskId) {
this.taskId = taskId;
this.status = Status.CREATED;
this.createTime = System.currentTimeMillis();
}
public String getTaskId() { return taskId; }
public void setTaskId(String taskId) { this.taskId = taskId; }
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
public File getFile() { return file; }
public void setFile(File file) { this.file = file; }
public String getFileName() { return fileName; }
public void setFileName(String fileName) { this.fileName = fileName; }
public String getErrorMessage() { return errorMessage; }
public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
public long getCreateTime() { return createTime; }
}

View File

@ -0,0 +1,89 @@
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_HYDROPW_R
*/
@Data
@TableName("SD_HYDROPW_R")
public class SdHydropwR implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 水电工程编码 */
private String stcd;
/** 数据时间 */
private Date tm;
/** 坝上水位 (m) */
private BigDecimal rz;
/** 蓄水量 */
private BigDecimal w;
/** 坝下水位 (m) */
private BigDecimal dz;
/** 入库流量 */
private BigDecimal qi;
/** 出库流量 */
private BigDecimal qo;
/** 发电流量 */
private BigDecimal qn;
/** 泄洪流量 */
private BigDecimal qgt;
/** 生态流量 */
private BigDecimal qec;
/** 灌溉流量 */
private BigDecimal qc;
/** 发电量 */
private BigDecimal ge;
/** 平均出力 */
private BigDecimal na;
/** 生态流量是否达标 */
private Integer sfdb;
/** 泄洪水量 */
private BigDecimal gtw;
/** 发电水量 */
private BigDecimal wge;
/** 区间流量 */
private BigDecimal qr;
/** 耗水率 */
private BigDecimal wr;
/** 记录人等... */
private String recordUser;
private Date recordTime;
private String modifyUser;
private Date modifyTime;
private Integer isDeleted;
private String deleteUser;
private Date deleteTime;
private String fid;
private String remark;
}

View File

@ -0,0 +1,72 @@
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_HYDROPWDAY_S
*/
@Data
@TableName("SD_HYDROPWDAY_S")
public class SdHydropwdayS implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 水电工程编码 */
private String stcd;
/** 时间 */
private Date dt;
/** 坝上水位 (m) */
private BigDecimal rz;
/** 蓄水量 */
private BigDecimal w;
/** 坝下水位 (m) */
private BigDecimal dz;
/** 入库流量 */
private BigDecimal qi;
/** 出库流量 */
private BigDecimal qo;
/** 发电流量 */
private BigDecimal qn;
/** 泄洪流量 */
private BigDecimal qgt;
/** 生态流量 */
private BigDecimal qec;
/** 灌溉流量 */
private BigDecimal qc;
/** 发电量 */
private BigDecimal ge;
/** 平均出力 */
private BigDecimal na;
/** 记录人等... */
private String recordUser;
private Date recordTime;
private String modifyUser;
private Date modifyTime;
private Integer isDeleted;
private String deleteUser;
private Date deleteTime;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,69 @@
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_RIVER_R
*/
@Data
@TableName("SD_RIVER_R")
public class SdRiverR implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 站码 */
private String stcd;
/** 时间 */
private Date tm;
/** 水位 (m) */
private BigDecimal z;
/** 流量 (m³/s) */
private BigDecimal q;
/** 流速 (m/s) */
private BigDecimal v;
/** 测流方法 */
private String msqmt;
/** 创建人 */
private String recordUser;
/** 创建时间 */
private Date recordTime;
/** 更新人 */
private String modifyUser;
/** 更新时间 */
private Date modifyTime;
/** 是否已删除: 0=未删除 1=已删除 */
private Integer isDeleted;
/** 删除人 */
private String deleteUser;
/** 删除时间 */
private Date deleteTime;
/** 附件ID */
private String fid;
/** 备注 */
private String remark;
}

View File

@ -0,0 +1,63 @@
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_RIVERDAY_S
*/
@Data
@TableName("SD_RIVERDAY_S")
public class SdRiverdayS implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 站码 */
private String stcd;
/** 时间 */
private Date dt;
/** 水位 (m) */
private BigDecimal z;
/** 流量 (m³/s) */
private BigDecimal q;
/** 流速 (m/s) */
private BigDecimal v;
/** 测流方法 */
private String msqmt;
/** 创建人 */
private String recordUser;
/** 创建时间 */
private Date recordTime;
/** 更新人 */
private String modifyUser;
/** 更新时间 */
private Date modifyTime;
/** 是否已删除: 0=未删除 1=已删除 */
private Integer isDeleted;
/** 删除人 */
private String deleteUser;
/** 删除时间 */
private Date deleteTime;
}

View File

@ -0,0 +1,51 @@
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_WQ_R
*/
@Data
@TableName("SD_WQ_R")
public class SdWqR implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 站码 */
private String stcd;
/** 采样时间 */
private Date tm;
/** 水温 (℃) */
private BigDecimal wtmp;
/** PH */
private BigDecimal ph;
/** 溶解氧 (mg/L) */
private BigDecimal dox;
/** 电导率 (μS/cm) */
private BigDecimal cond;
/** 浊度 (NTU) */
private BigDecimal tu;
/** 是否达标: 0=不达标 1=达标 */
private Integer sfdb;
/** 是否已删除 */
private Integer isDeleted;
}

View File

@ -0,0 +1,51 @@
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_WQDAY_S
*/
@Data
@TableName("SD_WQDAY_S")
public class SdWqdayS implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 站码 */
private String stcd;
/** 时间 */
private Date dt;
/** 水温 (℃) */
private BigDecimal wtmp;
/** PH */
private BigDecimal ph;
/** 溶解氧 (mg/L) */
private BigDecimal dox;
/** 电导率 (μS/cm) */
private BigDecimal cond;
/** 浊度 (NTU) */
private BigDecimal tu;
/** 是否达标 */
private Integer sfdb;
/** 是否已删除 */
private Integer isDeleted;
}

View File

@ -0,0 +1,63 @@
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_WTRV_R
*/
@Data
@TableName("SD_WTRV_R")
public class SdWtrvR implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 水温站编码 */
private String stcd;
/** 数据时间 */
private Date tm;
/** 水温 (℃) */
private BigDecimal wt;
/** 备注 */
private String remark;
/** 附件ID */
private String fid;
/** 所属水生生态调查数据表ID */
private String werId;
/** 创建人 */
private String recordUser;
/** 创建时间 */
private Date recordTime;
/** 更新人 */
private String modifyUser;
/** 更新时间 */
private Date modifyTime;
/** 是否已删除 */
private Integer isDeleted;
/** 删除人 */
private String deleteUser;
/** 删除时间 */
private Date deleteTime;
}

View File

@ -0,0 +1,54 @@
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_WTRVDAY_S
*/
@Data
@TableName("SD_WTRVDAY_S")
public class SdWtrvdayS implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.INPUT)
private String id;
/** 水温站编码 */
private String stcd;
/** 数据时间 */
private Date dt;
/** 水温 (℃) */
private BigDecimal wt;
/** 创建人 */
private String recordUser;
/** 创建时间 */
private Date recordTime;
/** 更新人 */
private String modifyUser;
/** 更新时间 */
private Date modifyTime;
/** 是否已删除 */
private Integer isDeleted;
/** 删除人 */
private String deleteUser;
/** 删除时间 */
private Date deleteTime;
}

View File

@ -0,0 +1,27 @@
package com.yfd.platform.qgc_export.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* SD_WTVTDAY_S 垂向水温日统计表
*/
@Data
@TableName("SD_WTVTDAY_S")
public class SdWtvtdayS implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String stcd;
private Date dt;
private BigDecimal wthg; // 水深
private BigDecimal vwt; // 水温
private String recordUser;
private Date recordTime;
private Date modifyTime;
private Integer isDeleted;
}

View File

@ -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;
}

View File

@ -0,0 +1,38 @@
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 StationMapping implements Serializable {
private static final long serialVersionUID = 1L;
@TableField("STCD")
private String stcd;
@TableField("RSTCD")
private String rstcd;
@TableField("ENNM")
private String ennm;
/** 测站类型(如 ZQ=河道水情, XX=生态流量等),用于区分同一电站下不同类型的测站 */
@TableField("STTP")
private String sttp;
/** 测站名称 */
@TableField("STNM")
private String stnm;
/** 所属水电基地名称 */
@TableField("BASE_NAME")
private String baseName;
}

View File

@ -0,0 +1,25 @@
package com.yfd.platform.qgc_export.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* MS_ALONGDET_B 沿程配置详情表 Mapper
*/
public interface MsAlongdetBMapper {
/**
* 查询指定 STTP 类型的出库沿程配置IO_DIRECTION=1 SORT 排序
*/
List<Map<String, Object>> selectAlongStations(@Param("sttp") String sttp,
@Param("rstcdList") List<String> rstcdList);
/**
* 查询所有沿程配置的电站排序MS_ALONG_B JOIN MS_ALONGDET_B
* MS_ALONG_B.ORDER_INDEX, MS_ALONGDET_B.SORT 排序
* 返回 RSTCD, ORDER_INDEX
*/
List<Map<String, Object>> selectAlongSortOrder(@Param("rstcdList") List<String> rstcdList);
}

View File

@ -0,0 +1,28 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdHydropwR;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_HYDROPW_R 电站运行小时数据 Mapper
*/
public interface SdHydropwRMapper extends BaseMapper<SdHydropwR> {
/**
* 批量查询测站在指定时间范围内的原始小时数据动态字段
*
* @param stcdList 测站编码列表
* @param fieldName 查询字段名RZ/DZ
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, TM, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("fieldName") String fieldName,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,28 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdHydropwdayS;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_HYDROPWDAY_S 电站运行日数据 Mapper
*/
public interface SdHydropwdaySMapper extends BaseMapper<SdHydropwdayS> {
/**
* 批量查询测站在指定时间范围内的原始日数据动态字段
*
* @param stcdList 测站编码列表
* @param fieldName 查询字段名RZ/DZ
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, DT, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("fieldName") String fieldName,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1,28 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdRiverR;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_RIVER_R 河道水情小时数据 Mapper
*/
public interface SdRiverRMapper extends BaseMapper<SdRiverR> {
/**
* 批量查询站点在指定时间范围内的原始小时数据
*
* @param stcdList 测站编码列表
* @param fieldName 查询字段名V/Q/Z
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, TM, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("fieldName") String fieldName,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,28 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdRiverdayS;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_RIVERDAY_S 河道水情日数据 Mapper
*/
public interface SdRiverdaySMapper extends BaseMapper<SdRiverdayS> {
/**
* 批量查询站点在指定时间范围内的原始日数据
*
* @param stcdList 测站编码列表
* @param fieldName 查询字段名V/Q/Z
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, DT, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("fieldName") String fieldName,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,47 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdWqR;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_WQ_R 水质小时数据 Mapper
*/
public interface SdWqRMapper extends BaseMapper<SdWqR> {
/**
* 批量查询测站在指定时间范围内的原始小时数据含达标标志
*
* @param stcdList 测站编码列表
* @param fieldName 查询字段名WTMP/PH/DOX/COND/TU
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, TM, VALUE, SFDB]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("fieldName") String fieldName,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
* 查询水质达标数据小时级 STCD 聚合 SFDB=1 达标数SFDB IN (0,1) 总数
*/
List<Map<String, Object>> selectComplianceData(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
* 查询各测站不达标要素SFDB=0 时的 WQ_SFDBHN_YS
*/
List<Map<String, Object>> selectPollutants(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
* 查询 SD_WQ_R 表的字段注释字段名 中文描述
*/
List<Map<String, Object>> selectColumnComments();
}

View File

@ -0,0 +1,35 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdWqdayS;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_WQDAY_S 水质日数据 Mapper
*/
public interface SdWqdaySMapper extends BaseMapper<SdWqdayS> {
/**
* 批量查询测站在指定时间范围内的原始日数据
*
* @param stcdList 测站编码列表
* @param fieldName 查询字段名WTMP/PH/DOX/COND/TU
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, DT, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("fieldName") String fieldName,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
* 查询水质达标数据日级 STCD 聚合 SFDB=1 达标数SFDB IN (0,1) 总数
*/
List<Map<String, Object>> selectComplianceData(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,26 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdWtrvR;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_WTRV_R 河道水温小时数据 Mapper
*/
public interface SdWtrvRMapper extends BaseMapper<SdWtrvR> {
/**
* 批量查询测站在指定时间范围内的原始小时数据
*
* @param stcdList 测站编码列表
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, TM, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,26 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdWtrvdayS;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_WTRVDAY_S 河道水温日数据 Mapper
*/
public interface SdWtrvdaySMapper extends BaseMapper<SdWtrvdayS> {
/**
* 批量查询测站在指定时间范围内的原始日数据
*
* @param stcdList 测站编码列表
* @param startTime 起始时间
* @param endTime 结束时间
* @return [STCD, DT, VALUE]
*/
List<Map<String, Object>> selectRawData(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,28 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.SdWtvtdayS;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* SD_WTVTDAY_S 垂向水温日数据 Mapper
*/
public interface SdWtvtdaySMapper extends BaseMapper<SdWtvtdayS> {
/**
* 查询月内每日垂向水温数据 EXTRACT(DAY FROM DT)用于按指定日期筛选
*/
List<Map<String, Object>> selectDailyData(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
* 查询月均垂向水温 STCDWTHG 分组
*/
List<Map<String, Object>> selectMonthlyAvg(@Param("stcdList") List<String> stcdList,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
}

View File

@ -0,0 +1,10 @@
package com.yfd.platform.qgc_export.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_export.domain.StationMapping;
/**
* V_MS_STBPRP_T 视图 Mapper 用于电站编码测站编码转换
*/
public interface StationMappingMapper extends BaseMapper<StationMapping> {
}

View File

@ -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);
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 电导率(COND) 指标处理器
*/
@Component
public class CodcrProcessor extends WqIndicatorProcessor {
@Override
public String getFieldName() {
return "CODCR";
}
@Override
public String getSheetName() {
return "水质(化学需氧量)";
}
@Override
public int getDecimalPlaces() {
return 2;
}
@Override
public int getOrder() {
return 12;
}
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 电导率(COND) 指标处理器
*/
@Component
public class CondProcessor extends WqIndicatorProcessor {
@Override
public String getFieldName() {
return "COND";
}
@Override
public String getSheetName() {
return "水质(电导率)";
}
@Override
public int getDecimalPlaces() {
return 2;
}
@Override
public int getOrder() {
return 13;
}
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 溶解氧(DOX) 指标处理器
*/
@Component
public class DoxProcessor extends WqIndicatorProcessor {
@Override
public String getFieldName() {
return "DOX";
}
@Override
public String getSheetName() {
return "水质(溶解氧)";
}
@Override
public int getDecimalPlaces() {
return 2;
}
@Override
public int getOrder() {
return 11;
}
}

View File

@ -0,0 +1,27 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 出库水温(DWT) 标准格式处理器
* <p>
* Sheet 格式与水情指标一致日数据(01-31) + 7个汇总指标
*/
@Component
public class DwtProcessor extends WtrvIndicatorProcessor {
@Override
public String getFieldName() {
return "DWT";
}
@Override
public String getSheetName() {
return "出库水温";
}
@Override
public int getOrder() {
return 8;
}
}

View File

@ -0,0 +1,32 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 出入库水温日变幅 处理器
* <p>
* 日变幅格式日数据行展示每日变幅当日最大-最小汇总指标为"日变幅均值""日变幅最大值"
*/
@Component
public class DwtVariationProcessor extends WtrvIndicatorProcessor {
@Override
public String getFieldName() {
return "DWT";
}
@Override
public String getSheetName() {
return "出入库水温日变幅";
}
@Override
public boolean isVariation() {
return true;
}
@Override
public int getOrder() {
return 9;
}
}

View File

@ -0,0 +1,25 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 坝下水位(DZ) 标准格式处理器
*/
@Component
public class DzProcessor extends HydropwIndicatorProcessor {
@Override
public String getFieldName() {
return "DZ";
}
@Override
public String getSheetName() {
return "坝下水位";
}
@Override
public int getOrder() {
return 3;
}
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 坝下水位(DZ) 日变幅格式处理器
*/
@Component
public class DzVariationProcessor extends HydropwIndicatorProcessor {
@Override
public String getFieldName() {
return "DZ";
}
@Override
public String getSheetName() {
return "坝下水位日变幅";
}
@Override
public boolean isVariation() {
return true;
}
@Override
public int getOrder() {
return 4;
}
}

View File

@ -0,0 +1,77 @@
package com.yfd.platform.qgc_export.processor;
import java.util.Date;
import java.util.List;
/**
* 指标处理器接口 每种指标v/q/z/wq/dwt/qec 实现此接口
* <p>
* 新增指标时实现本接口 + QgcExportServiceImpl.resolveProcessors() 中注入即可
*/
public interface ExportIndicatorProcessor {
/**
* 指标字段名与前端 dataField 中的值对应 "v", "q", "z", "qec"
*/
String getFieldName();
/**
* Excel Sheet 页中文标题 "流速(m/s)"
*/
String getSheetName();
/**
* 测站类型STTP用于区分同一电站下不同用途的测站
* <ul>
* <li>V/Q/Z 河道水情 "ZQ"</li>
* <li>WQ 水质 "WQ"</li>
* <li>DWT 出库水温 "WTRV"</li>
* <li>RZ/DZ 坝上/坝下水位 "ENG"</li>
* <li>QEC 生态流量 待定</li>
* </ul>
*/
default String getSttpType() {
return null;
}
/**
* 数值保留的小数位数默认 3
*/
default int getDecimalPlaces() {
return 3;
}
/**
* 是否有小时达标率指标水质指标返回 true在月均下方追加一行"小时达标率"
*/
default boolean hasComplianceRate() {
return false;
}
/**
* 是否为日变幅格式出库水温日变幅 Sheet
* 日变幅格式日数据行展示每日变幅(=当日最大-最小)汇总指标为"日变幅均值""日变幅最大值"
*/
default boolean isVariation() {
return false;
}
/**
* Sheet 排序号升序排列
* 数值越小越靠前同组指标标准格式应排在日变幅格式之前
*/
default int getOrder() {
return 100;
}
/**
* 查询原始数据
*
* @param stcds 测站编码列表已从电站编码转换且已按 STTP 过滤
* @param startTime 起始时间
* @param endTime 结束时间
* @param isDaily 是否查询日表true=日表, false=小时表
* @return 原始数据行列表
*/
List<IndicatorDataRow> queryRawData(List<String> stcds, Date startTime, Date endTime, boolean isDaily);
}

View File

@ -0,0 +1,69 @@
package com.yfd.platform.qgc_export.processor;
import com.yfd.platform.qgc_export.mapper.SdHydropwRMapper;
import com.yfd.platform.qgc_export.mapper.SdHydropwdaySMapper;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 电站运行指标处理器基类 RZ(坝上水位)/DZ(坝下水位) 使用 SD_HYDROPW_R / SD_HYDROPWDAY_S
* <p>
* 子类标注 @ComponentgetSttpType() 固定返回 "ENG"
* 动态字段名由子类 getFieldName() 返回
*/
public abstract class HydropwIndicatorProcessor implements ExportIndicatorProcessor {
protected SdHydropwRMapper sdHydropwRMapper;
protected SdHydropwdaySMapper sdHydropwdaySMapper;
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void setMappers(SdHydropwRMapper rMapper, SdHydropwdaySMapper sMapper) {
this.sdHydropwRMapper = rMapper;
this.sdHydropwdaySMapper = sMapper;
}
@Override
public String getSttpType() {
return "ENG";
}
@Override
public int getDecimalPlaces() {
return 3;
}
@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 = sdHydropwdaySMapper.selectRawData(stcds, getFieldName(), startStr, endStr);
} else {
rawList = sdHydropwRMapper.selectRawData(stcds, getFieldName(), 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;
}
}

View File

@ -0,0 +1,35 @@
package com.yfd.platform.qgc_export.processor;
import java.math.BigDecimal;
import java.util.Date;
/**
* 指标数据行 从数据库查询后的原始数据封装
*/
public class IndicatorDataRow {
/** 测站编码 */
private String stcd;
/** 数据时间 */
private Date tm;
/** 指标值 */
private BigDecimal value;
/** 是否达标仅水质指标使用0=不达标 1=达标) */
private Integer sfdb;
public IndicatorDataRow() {}
public IndicatorDataRow(String stcd, Date tm, BigDecimal value) {
this.stcd = stcd;
this.tm = tm;
this.value = value;
}
public String getStcd() { return stcd; }
public void setStcd(String stcd) { this.stcd = stcd; }
public Date getTm() { return tm; }
public void setTm(Date tm) { this.tm = tm; }
public BigDecimal getValue() { return value; }
public void setValue(BigDecimal value) { this.value = value; }
public Integer getSfdb() { return sfdb; }
public void setSfdb(Integer sfdb) { this.sfdb = sfdb; }
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* PH 指标处理器
*/
@Component
public class PhProcessor extends WqIndicatorProcessor {
@Override
public String getFieldName() {
return "PH";
}
@Override
public String getSheetName() {
return "水质(PH)";
}
@Override
public int getDecimalPlaces() {
return 2;
}
@Override
public int getOrder() {
return 15;
}
}

View File

@ -0,0 +1,22 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 流量(Q) 指标处理器
*/
@Component
public class QProcessor extends RiverIndicatorProcessor {
@Override
public String getFieldName() { return "Q"; }
@Override
public String getSheetName() { return "流量(m³/s)"; }
@Override
public String getSttpType() { return "ZQ"; }
@Override
public int getOrder() { return 32; }
}

View File

@ -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_SSTTP="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;
}
}

View File

@ -0,0 +1,25 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 入库流量(QI) 处理器
*/
@Component
public class QiProcessor extends HydropwIndicatorProcessor {
@Override
public String getFieldName() {
return "QI";
}
@Override
public String getSheetName() {
return "入库流量";
}
@Override
public int getOrder() {
return 5;
}
}

View File

@ -0,0 +1,25 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 出库流量(QO) 处理器
*/
@Component
public class QoProcessor extends HydropwIndicatorProcessor {
@Override
public String getFieldName() {
return "QO";
}
@Override
public String getSheetName() {
return "出库流量";
}
@Override
public int getOrder() {
return 6;
}
}

View File

@ -0,0 +1,58 @@
package com.yfd.platform.qgc_export.processor;
import com.yfd.platform.qgc_export.mapper.SdRiverRMapper;
import com.yfd.platform.qgc_export.mapper.SdRiverdaySMapper;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 河道水情指标处理器基类 V/Q/Z 三个指标共用查询逻辑
* <p>
* 子类VProcessor/QProcessor/ZProcessor需标注 @Component 并提供 getFieldName/getSheetName
*/
public abstract class RiverIndicatorProcessor implements ExportIndicatorProcessor {
protected SdRiverRMapper sdRiverRMapper;
protected SdRiverdaySMapper sdRiverdaySMapper;
private static final SimpleDateFormat SDF_DATETIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void setMappers(SdRiverRMapper rMapper, SdRiverdaySMapper sMapper) {
this.sdRiverRMapper = rMapper;
this.sdRiverdaySMapper = sMapper;
}
@Override
public List<IndicatorDataRow> queryRawData(List<String> stcds, Date startTime, Date endTime, boolean isDaily) {
String startStr = SDF_DATETIME.format(startTime);
String endStr = SDF_DATETIME.format(endTime);
List<Map<String, Object>> rawList;
if (isDaily) {
rawList = sdRiverdaySMapper.selectRawData(stcds, getFieldName(), startStr, endStr);
} else {
rawList = sdRiverRMapper.selectRawData(stcds, getFieldName(), 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 = null;
Object val = map.get("VALUE");
if (val instanceof BigDecimal) {
value = (BigDecimal) val;
} else if (val instanceof Number) {
value = BigDecimal.valueOf(((Number) val).doubleValue());
}
if (stcd != null && tm != null && value != null) {
rows.add(new IndicatorDataRow(stcd, tm, value));
}
}
}
return rows;
}
}

View File

@ -0,0 +1,60 @@
package com.yfd.platform.qgc_export.processor;
import com.yfd.platform.qgc_env.wt.mapper.SdWtvtRMapper;
import com.yfd.platform.qgc_export.mapper.SdWtvtdaySMapper;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* 垂向水温(RWT) 处理器 双表头格式按水深分层展示
* <p>
* STTP: WTVT, 使用 SD_WTVT_R / SD_WTVTDAY_S
*/
@Component
public class RwtProcessor implements ExportIndicatorProcessor {
private SdWtvtRMapper sdWtvtRMapper;
private SdWtvtdaySMapper sdWtvtdaySMapper;
public void setMappers(SdWtvtRMapper rMapper, SdWtvtdaySMapper sMapper) {
this.sdWtvtRMapper = rMapper;
this.sdWtvtdaySMapper = sMapper;
}
public SdWtvtRMapper getSdWtvtRMapper() { return sdWtvtRMapper; }
public SdWtvtdaySMapper getSdWtvtdaySMapper() { return sdWtvtdaySMapper; }
@Override
public String getFieldName() {
return "rwt";
}
@Override
public String getSheetName() {
return "垂向水温";
}
@Override
public String getSttpType() {
return "WTVT";
}
@Override
public int getDecimalPlaces() {
return 2;
}
@Override
public int getOrder() {
return 7;
}
@Override
public List<IndicatorDataRow> queryRawData(List<String> stcds, Date startTime, Date endTime, boolean isDaily) {
// RWT 使用自定义 Sheet 构建不走标准数据查询流程
return Collections.emptyList();
}
}

View File

@ -0,0 +1,25 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 坝上水位(RZ) 标准格式处理器
*/
@Component
public class RzProcessor extends HydropwIndicatorProcessor {
@Override
public String getFieldName() {
return "RZ";
}
@Override
public String getSheetName() {
return "坝上水位";
}
@Override
public int getOrder() {
return 1;
}
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 坝上水位(RZ) 日变幅格式处理器
*/
@Component
public class RzVariationProcessor extends HydropwIndicatorProcessor {
@Override
public String getFieldName() {
return "RZ";
}
@Override
public String getSheetName() {
return "坝上水位日变幅";
}
@Override
public boolean isVariation() {
return true;
}
@Override
public int getOrder() {
return 2;
}
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 浊度(TU) 指标处理器
*/
@Component
public class TuProcessor extends WqIndicatorProcessor {
@Override
public String getFieldName() {
return "TU";
}
@Override
public String getSheetName() {
return "水质(浊度)";
}
@Override
public int getDecimalPlaces() {
return 2;
}
@Override
public int getOrder() {
return 14;
}
}

View File

@ -0,0 +1,22 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 流速(V) 指标处理器
*/
@Component
public class VProcessor extends RiverIndicatorProcessor {
@Override
public String getFieldName() { return "V"; }
@Override
public String getSheetName() { return "流速(m/s)"; }
@Override
public String getSttpType() { return "ZQ"; }
@Override
public int getOrder() { return 33; }
}

View File

@ -0,0 +1,77 @@
package com.yfd.platform.qgc_export.processor;
import com.yfd.platform.qgc_export.mapper.SdWqRMapper;
import com.yfd.platform.qgc_export.mapper.SdWqdaySMapper;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 水质指标处理器基类 5个水质指标WTMP/PH/DOX/COND/TU共用查询逻辑
* <p>
* 小时表查询额外提取 SFDB是否达标字段用于计算小时达标率
* 子类需标注 @Component 并提供 getFieldName/getSheetName/getDecimalPlaces
*/
public abstract class WqIndicatorProcessor implements ExportIndicatorProcessor {
protected SdWqRMapper sdWqRMapper;
protected SdWqdaySMapper sdWqdaySMapper;
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void setMappers(SdWqRMapper rMapper, SdWqdaySMapper sMapper) {
this.sdWqRMapper = rMapper;
this.sdWqdaySMapper = sMapper;
}
@Override
public String getSttpType() {
return "WQ";
}
@Override
public boolean hasComplianceRate() {
return true;
}
@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 = sdWqdaySMapper.selectRawData(stcds, getFieldName(), startStr, endStr);
} else {
rawList = sdWqRMapper.selectRawData(stcds, getFieldName(), 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) {
IndicatorDataRow row = new IndicatorDataRow(stcd, tm, value);
// 小时表额外携带 SFDB达标标志
if (!isDaily) {
Object sfdbObj = map.get("SFDB");
if (sfdbObj instanceof Number) {
row.setSfdb(((Number) sfdbObj).intValue());
}
}
rows.add(row);
}
}
}
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;
}
}

View File

@ -0,0 +1,30 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 水温(WTMP) 指标处理器
*/
@Component
public class WtmpProcessor extends WqIndicatorProcessor {
@Override
public String getFieldName() {
return "WTMP";
}
@Override
public String getSheetName() {
return "水质(水温)";
}
@Override
public int getDecimalPlaces() {
return 3;
}
@Override
public int getOrder() {
return 10;
}
}

View File

@ -0,0 +1,68 @@
package com.yfd.platform.qgc_export.processor;
import com.yfd.platform.qgc_export.mapper.SdWtrvRMapper;
import com.yfd.platform.qgc_export.mapper.SdWtrvdaySMapper;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 河道水温指标处理器基类 DWT出库水温使用 SD_WTRV_R / SD_WTRVDAY_S
* <p>
* 子类标注 @ComponentgetSttpType() 固定返回 "WTRV"
*/
public abstract class WtrvIndicatorProcessor implements ExportIndicatorProcessor {
protected SdWtrvRMapper sdWtrvRMapper;
protected SdWtrvdaySMapper sdWtrvdaySMapper;
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void setMappers(SdWtrvRMapper rMapper, SdWtrvdaySMapper sMapper) {
this.sdWtrvRMapper = rMapper;
this.sdWtrvdaySMapper = sMapper;
}
@Override
public String getSttpType() {
return "WTRV";
}
@Override
public int getDecimalPlaces() {
return 3;
}
@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 = sdWtrvdaySMapper.selectRawData(stcds, startStr, endStr);
} else {
rawList = sdWtrvRMapper.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;
}
}

View File

@ -0,0 +1,22 @@
package com.yfd.platform.qgc_export.processor;
import org.springframework.stereotype.Component;
/**
* 水位(Z) 指标处理器
*/
@Component
public class ZProcessor extends RiverIndicatorProcessor {
@Override
public String getFieldName() { return "Z"; }
@Override
public String getSheetName() { return "水位(m)"; }
@Override
public String getSttpType() { return "ZQ"; }
@Override
public int getOrder() { return 31; }
}

View File

@ -0,0 +1,227 @@
package com.yfd.platform.qgc_export.service;
import com.yfd.platform.qgc_export.domain.ExportTask;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* 导出任务管理服务 异步导出 + 状态查询 + 文件下载
*/
@Service
public class ExportTaskService {
private static final Logger log = LoggerFactory.getLogger(ExportTaskService.class);
@Resource
private IQgcExportService qgcExportService;
/** 任务缓存taskId → task */
private final Map<String, ExportTask> taskMap = new ConcurrentHashMap<>();
/**
* 创建月度/小时/日导出任务返回任务 ID
*/
public String submitMonthExport(List<String> dataFields, List<String> months,
List<String> stationCodes, boolean isDailyDimension,
String tmDimension) {
String taskId = UUID.randomUUID().toString().replace("-", "");
ExportTask task = new ExportTask(taskId);
task.setStatus(ExportTask.Status.PROCESSING);
taskMap.put(taskId, task);
CompletableFuture.runAsync(() -> {
File tempFile = null;
try {
tempFile = File.createTempFile("qgc_export_", ".zip");
FileOutputStream fos = new FileOutputStream(tempFile);
CapturingResponse capturing = new CapturingResponse(fos);
qgcExportService.exportData(dataFields, months, stationCodes,
isDailyDimension, tmDimension, capturing);
capturing.flushAndClose();
fos.close();
task.setFile(tempFile);
task.setStatus(ExportTask.Status.DONE);
log.info("导出任务 {} 完成,文件: {}", taskId, tempFile.getAbsolutePath());
} catch (Exception e) {
log.error("导出任务 {} 失败", taskId, e);
task.setErrorMessage(e.getMessage());
task.setStatus(ExportTask.Status.ERROR);
if (tempFile != null) {
try { Files.deleteIfExists(tempFile.toPath()); } catch (IOException ignored) {}
}
}
});
return taskId;
}
/**
* 创建年度导出任务返回任务 ID
*/
public String submitYearExport(List<String> yearStatType, List<String> years,
List<String> stationCodes, List<String> baseIds) {
String taskId = UUID.randomUUID().toString().replace("-", "");
ExportTask task = new ExportTask(taskId);
task.setStatus(ExportTask.Status.PROCESSING);
taskMap.put(taskId, task);
CompletableFuture.runAsync(() -> {
File tempFile = null;
try {
tempFile = File.createTempFile("qgc_export_", ".zip");
FileOutputStream fos = new FileOutputStream(tempFile);
CapturingResponse capturing = new CapturingResponse(fos);
qgcExportService.exportYearData(yearStatType, years, stationCodes,
baseIds, capturing);
capturing.flushAndClose();
fos.close();
task.setFile(tempFile);
task.setStatus(ExportTask.Status.DONE);
log.info("年度导出任务 {} 完成,文件: {}", taskId, tempFile.getAbsolutePath());
} catch (Exception e) {
log.error("年度导出任务 {} 失败", taskId, e);
task.setErrorMessage(e.getMessage());
task.setStatus(ExportTask.Status.ERROR);
if (tempFile != null) {
try { Files.deleteIfExists(tempFile.toPath()); } catch (IOException ignored) {}
}
}
});
return taskId;
}
/**
* 查询任务状态
*/
public ExportTask getTask(String taskId) {
return taskMap.get(taskId);
}
/**
* 将任务文件写入 OutputStream下载写完后删除临时文件和任务缓存
*/
public void downloadTask(String taskId, OutputStream os) throws IOException {
ExportTask task = taskMap.get(taskId);
if (task == null || task.getStatus() != ExportTask.Status.DONE || task.getFile() == null) {
throw new IllegalStateException("任务未完成或文件不存在");
}
try (FileInputStream fis = new FileInputStream(task.getFile())) {
byte[] buf = new byte[8192];
int len;
while ((len = fis.read(buf)) != -1) {
os.write(buf, 0, len);
}
} finally {
try { Files.deleteIfExists(task.getFile().toPath()); } catch (IOException e) {
log.warn("删除临时文件失败: {}", task.getFile(), e);
}
taskMap.remove(taskId);
}
}
/**
* 清理超时任务可由定时任务调用
*/
public void cleanExpiredTasks(long timeoutMs) {
long now = System.currentTimeMillis();
taskMap.entrySet().removeIf(entry -> {
ExportTask task = entry.getValue();
if (now - task.getCreateTime() > timeoutMs) {
if (task.getFile() != null) {
try { Files.deleteIfExists(task.getFile().toPath()); } catch (IOException ignored) {}
}
return true;
}
return false;
});
}
// ==================== 内部类 ====================
/**
* 最小化 HttpServletResponse 基于 HttpServletResponseWrapper仅重写导出用到的 5 个方法
*/
private static class CapturingResponse extends HttpServletResponseWrapper {
private final FileOutputStream fos;
private final ServletOutputStream sos;
/** 兜底 response仅用于满足 super() 非 null 约束,实际所有调用都被本类重写 */
private static final HttpServletResponse NOOP = new HttpServletResponse() {
@Override public ServletOutputStream getOutputStream() { throw new UnsupportedOperationException(); }
@Override public java.io.PrintWriter getWriter() { throw new UnsupportedOperationException(); }
@Override public String getCharacterEncoding() { return null; }
@Override public String getContentType() { return null; }
@Override public void setCharacterEncoding(String charset) {}
@Override public void setContentLength(int len) {}
@Override public void setContentLengthLong(long len) {}
@Override public void setContentType(String type) {}
@Override public void setBufferSize(int size) {}
@Override public int getBufferSize() { return 0; }
@Override public void flushBuffer() {}
@Override public void resetBuffer() {}
@Override public boolean isCommitted() { return false; }
@Override public void reset() {}
@Override public void setLocale(java.util.Locale loc) {}
@Override public java.util.Locale getLocale() { return java.util.Locale.getDefault(); }
@Override public void addCookie(jakarta.servlet.http.Cookie cookie) {}
@Override public boolean containsHeader(String name) { return false; }
@Override public String encodeURL(String url) { return url; }
@Override public String encodeRedirectURL(String url) { return url; }
@Override public void sendError(int sc, String msg) {}
@Override public void sendError(int sc) {}
@Override public void sendRedirect(String location) {}
@Override public void sendRedirect(String location, int sc, boolean clearBuffer) {}
@Override public void setDateHeader(String name, long date) {}
@Override public void addDateHeader(String name, long date) {}
@Override public void setHeader(String name, String value) {}
@Override public void addHeader(String name, String value) {}
@Override public void setIntHeader(String name, int value) {}
@Override public void addIntHeader(String name, int value) {}
@Override public void setStatus(int sc) {}
@Override public int getStatus() { return 200; }
@Override public String getHeader(String name) { return null; }
@Override public java.util.Collection<String> getHeaders(String name) { return java.util.Collections.emptyList(); }
@Override public java.util.Collection<String> getHeaderNames() { return java.util.Collections.emptyList(); }
};
CapturingResponse(FileOutputStream fos) {
super(NOOP);
this.fos = fos;
this.sos = new ServletOutputStream() {
@Override public void write(int b) throws IOException { fos.write(b); }
@Override public boolean isReady() { return true; }
@Override public void setWriteListener(WriteListener listener) {}
};
}
void flushAndClose() throws IOException {
sos.flush();
fos.flush();
}
@Override public ServletOutputStream getOutputStream() { return sos; }
@Override public void setContentType(String type) {}
@Override public void setCharacterEncoding(String charset) {}
@Override public void setHeader(String name, String value) {}
@Override public String getCharacterEncoding() { return "UTF-8"; }
}
}

View File

@ -0,0 +1,42 @@
package com.yfd.platform.qgc_export.service;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 综合导出服务接口
*/
public interface IQgcExportService {
/**
* 执行综合导出生成 ZIP 并写入 HttpServletResponse
*
* @param dataFields 指标字段列表 ["v","q","z"]
* @param months 月份列表 ["2026-08","2026-07"]
* @param stationCodes 电站编码列表RSTCD前端传入
* @param isDailyDimension 是否查日表
* @param tmDimension 时间维度 (hour/day/month/year)
* @param response HttpServletResponse
*/
void exportData(List<String> dataFields,
List<String> months,
List<String> stationCodes,
boolean isDailyDimension,
String tmDimension,
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);
}

View File

@ -12,10 +12,7 @@ import com.yfd.platform.qgc_base.domain.SdEngInfoBH;
import com.yfd.platform.qgc_base.service.ISdEngInfoBHService;
import com.yfd.platform.qgc_base.service.ISdHbrvDicService;
import com.yfd.platform.system.domain.*;
import com.yfd.platform.system.mapper.SysMenuMapper;
import com.yfd.platform.system.mapper.SysOrganizationMapper;
import com.yfd.platform.system.mapper.SysRoleMapper;
import com.yfd.platform.system.mapper.SysUserTenantMapper;
import com.yfd.platform.system.mapper.*;
import com.yfd.platform.system.service.ISmsVerifyCodeService;
import com.yfd.platform.system.service.ISysLogService;
import com.yfd.platform.system.service.IUserService;
@ -85,6 +82,10 @@ public class SmsVerifyCodeController {
@Resource
private ISdHbrvDicService hbrvDicService;
@Resource
private SysUserMapper sysUserMapper;
@Value("${rsa.private_key}")
private String privateKey;
private static final String URGE_CONTENT = "根据生态环境部要求,请贵电站尽快完成过鱼数据的报送工作,感谢支持。";
@ -478,7 +479,7 @@ public class SmsVerifyCodeController {
return ResponseResult.error("验证码错误或已过期");
}
SysUser user = userService.getUserByPhone(phone, tenantId);
SysUser user = userService.getUserByPhone(phone, null);
if (user == null) {
return ResponseResult.error("该手机号未注册");
}
@ -500,19 +501,43 @@ public class SmsVerifyCodeController {
Authentication authenticate = new UsernamePasswordAuthenticationToken(user, null, authenticationToken.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticate);
LoginUser loginUser = new LoginUser();
loginUser.setUser(user);
loginUser.setUsername(user.getUsername());
//Todo 根据用户查询权限信息 添加到LoginUser中
// 判断是否为超级管理员LEVEL=1
boolean isSuperAdmin = (isSuperAdmin(user.getId())||user.getUsertype()==0);
// 查询用户可访问的门户列表
List<String> accessibleTenantIds = sysUserTenantMapper.getTenantIdsByUserId(user.getId());
// 非超级管理员必须指定门户且必须在可访问列表中
if (!isSuperAdmin) {
if (StrUtil.isBlank(tenantId)) {
throw new RuntimeException("用户无权限访问该平台");
}
if (accessibleTenantIds == null || !accessibleTenantIds.contains(tenantId)) {
throw new RuntimeException("用户无权限访问该平台");
}
}
// 加载权限超级管理员加载全部权限普通用户按门户过滤
boolean isSuperAdmin = userService.isSuperAdmin(user.getId());
List<String> permissions;
if (isSuperAdmin) {
permissions = sysMenuMapper.selectPermsByUserId(user.getId(), null);
} else {
permissions = sysMenuMapper.selectPermsByUserId(user.getId(), tenantId);
}
loginUser.setPermissions(permissions);
LoginUser loginUser = new LoginUser(user, permissions, tenantId, accessibleTenantIds, isSuperAdmin);
// LoginUser loginUser = new LoginUser();
// loginUser.setUser(user);
// loginUser.setUsername(user.getUsername());
// boolean isSuperAdmin = userService.isSuperAdmin(user.getId());
// List<String> permissions;
// if (isSuperAdmin) {
// permissions = sysMenuMapper.selectPermsByUserId(user.getId(), null);
// } else {
// permissions = sysMenuMapper.selectPermsByUserId(user.getId(), tenantId);
// }
// loginUser.setPermissions(permissions);
HttpServletRequest request = RequestHolder.getHttpServletRequest();
SysLog sysLog = new SysLog();
sysLog.setUsercode(user.getUsername());
@ -549,4 +574,12 @@ public class SmsVerifyCodeController {
return ResponseResult.successData(map);
}
/**
* 判断用户是否为超级管理员拥有LEVEL=1的角色
*/
private boolean isSuperAdmin(String userId) {
String maxLevel = sysUserMapper.getMaxLevel(userId);
return "1".equals(maxLevel);
}
}

View File

@ -76,30 +76,65 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
String name,
String isdisplay,
String tenantId) {
List<Map<String, Object>> mapList=null;
List<Map<String, Object>> listMap =new ArrayList<>();
if(StrUtil.isEmpty(name)){//不带名称查询返回树结构
if (StrUtil.isEmpty(name)) {
// 一次查询该系统下所有菜单内存中构建树替代递归逐层查 DB
QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parentid", "0").eq("systemcode", systemcode)
queryWrapper.eq("systemcode", systemcode)
.eq(StrUtil.isNotBlank(tenantId), "tenant_id", tenantId)
.orderByAsc("orderno");
mapList = this.listMaps(queryWrapper);
listMap.addAll(ObjectConverterUtil.convertMapFieldsToEntityFormat(SysMenu.class, mapList));
for (int i = 0; i < listMap.size(); i++) {
//查询下一子集
List<Map<String, Object>> childList = child(listMap.get(i).get(
"id").toString(), systemcode, name, null, null, tenantId);
listMap.get(i).put("children", childList); //添加新列 子集
List<Map<String, Object>> allMenus = this.listMaps(queryWrapper);
List<Map<String, Object>> allConverted = ObjectConverterUtil.convertMapFieldsToEntityFormat(SysMenu.class, allMenus);
// 构建 id -> node 索引并预初始化 children 空列表
Map<String, Map<String, Object>> nodeMap = new LinkedHashMap<>();
for (Map<String, Object> menu : allConverted) {
menu.put("children", new ArrayList<>());
nodeMap.put(String.valueOf(menu.get("id")), menu);
}
}else{ //根据菜单名称查询直接返回类别
// 单次遍历挂载子节点到父节点
List<Map<String, Object>> roots = new ArrayList<>();
for (Map<String, Object> menu : allConverted) {
String parentId = String.valueOf(menu.get("parentid"));
Map<String, Object> parent = nodeMap.get(parentId);
if (parent != null) {
// @SuppressWarnings("unchecked")
List<Map<String, Object>> children = (List<Map<String, Object>>) parent.get("children");
children.add(menu);
} else {
roots.add(menu);
}
}
// 递归按 orderno 排序
sortTreeByOrderno(roots);
return roots;
} else {
// 根据菜单名称查询直接返回列表
QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", name).eq("systemcode", systemcode)
.eq(StrUtil.isNotBlank(tenantId), "tenant_id", tenantId)
.orderByAsc("name");
mapList = this.listMaps(queryWrapper);
listMap.addAll(ObjectConverterUtil.convertMapFieldsToEntityFormat(SysMenu.class, mapList));
List<Map<String, Object>> mapList = this.listMaps(queryWrapper);
return ObjectConverterUtil.convertMapFieldsToEntityFormat(SysMenu.class, mapList);
}
}
/**
* 递归按 orderno 排序树节点保持与原 child() 相同的排序语义
*/
private void sortTreeByOrderno(List<Map<String, Object>> nodes) {
nodes.sort(Comparator.comparingInt(m -> {
Object orderno = m.get("orderno");
return orderno instanceof Number ? ((Number) orderno).intValue() : 0;
}));
for (Map<String, Object> node : nodes) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> children = (List<Map<String, Object>>) node.get("children");
if (children != null && !children.isEmpty()) {
sortTreeByOrderno(children);
}
}
return listMap;
}
/***********************************

View File

@ -641,11 +641,38 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
Page<SysUser> page) {
Page<SysUser> mapPage = sysUserMapper.queryUsers(orgid,
username, tenantId, page);
mapPage.getRecords().forEach(record -> {
String id = record.getId();
List<SysRole> sysRoles = sysRoleMapper.getRoleByUserId(id);
record.setRoles(sysRoles);
});
List<SysUser> records = mapPage.getRecords();
if (records.isEmpty()) {
return mapPage;
}
// 收集所有用户ID一次批量查询角色替代 N+1 查询
List<String> userIds = records.stream()
.map(SysUser::getId)
.collect(Collectors.toList());
List<Map<String, Object>> allRoleMaps = sysRoleMapper.getUserRolesByUserIds(userIds);
// userId 分组 Map 转为 SysRole 对象
Map<String, List<SysRole>> userRoleMap = new HashMap<>();
if (allRoleMaps != null) {
for (Map<String, Object> roleMap : allRoleMaps) {
String userId = (String) roleMap.get("userId");
if (userId == null) continue;
SysRole role = new SysRole();
role.setId((String) roleMap.get("id"));
role.setRolecode((String) roleMap.get("rolecode"));
role.setRolename((String) roleMap.get("rolename"));
role.setLevel((String) roleMap.get("level"));
role.setDescription((String) roleMap.get("description"));
role.setIsvaild((String) roleMap.get("isvaild"));
role.setOrgscope((String) roleMap.get("orgscope"));
role.setOptscope((String) roleMap.get("optscope"));
role.setBusscope((String) roleMap.get("busscope"));
userRoleMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(role);
}
}
records.forEach(r -> r.setRoles(userRoleMap.getOrDefault(r.getId(), Collections.emptyList())));
return mapPage;
}

View File

@ -0,0 +1,765 @@
package com.yfd.platform.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 综合导出工具类 将多维度数据导出为 ZIP 压缩包内含若干 Excel (.xlsx) 文件每个 Excel 支持多 Sheet
*
* <h3>使用示例</h3>
* <pre>{@code
* // 1. 构建 Sheet 数据
* SheetData stationSheet = new SheetData("电站基础信息",
* Arrays.asList("测站编码", "测站名称", "经度", "纬度"),
* stationRows); // List<List<Object>>
*
* SheetData monitorSheet = new SheetData("监测数据",
* Arrays.asList("时间", "水位", "流量"),
* monitorRows);
*
* // 2. 构建 Excel 数据一个 Excel = 多个 Sheet
* ExcelData excel1 = new ExcelData("电站数据", Arrays.asList(stationSheet, monitorSheet));
* ExcelData excel2 = new ExcelData("流域数据", Arrays.asList(basinSheet));
*
* // 3. 导出到 HttpServletResponse
* ExportZipUtil.exportToResponse(response, "数据导出_20260731.zip", Arrays.asList(excel1, excel2));
*
* // 或导出到文件
* ExportZipUtil.exportToFile(new File("D:/export.zip"), Arrays.asList(excel1, excel2));
*
* // 类型安全的构建方式
* SheetData sheet = ExportZipUtil.fromTypedData("电站信息",
* Arrays.asList("编码", "名称", "经度"),
* Arrays.asList(Station::getStcd, Station::getStnm, Station::getLgtd),
* stationList);
* }</pre>
*
* @author system
* @since 2026-07-31
*/
public class ExportZipUtil {
private static final Logger log = LoggerFactory.getLogger(ExportZipUtil.class);
private ExportZipUtil() {
// 工具类禁止实例化
}
// ======================== 数据模型 ========================
/**
* 单个 Sheet 页数据定义
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class SheetData {
/** Sheet 页名称 */
private String sheetName;
/** 表头列名集合 */
private List<String> headers;
/** 数据行集合(每行是一个 Object 列表,与 headers 一一对应) */
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;
}
}
/**
* 单个 Excel 文件数据定义一个 Excel = 多个 Sheet
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ExcelData {
/** Excel 文件名(不含 .xlsx 扩展名,在 ZIP 中自动添加) */
private String fileName;
/** 该 Excel 包含的所有 Sheet 页 */
private List<SheetData> sheets;
}
// ======================== 分组表头数据模型 ========================
/**
* 带分组表头的 Sheet 页数据定义支持合并单元格
*
* <p>生成的 Excel 结构
* <pre>
* Row 0: [rowHeaderLabel(合并)] [group0Name(合并 subCols )] [group1Name(合并 subCols )] ...
* Row 1: [ ] [sub0] [sub1] ... [subN] [sub0] [sub1] ... [subN] ...
* Row 2+: rowLabel0 data...
* Row 3 : rowLabel1 data...
* </pre></p>
*
* <h3>使用示例</h3>
* <pre>{@code
* GroupedSheetData sheet = new GroupedSheetData("流速(m/s)", "日期",
* Arrays.asList("XX水电站", "YY水电站"),
* Arrays.asList("日均最低", "日均最高", "月内最低", "月内最低时间", "月内最高", "月内最高时间"));
* // 每行[rowLabel, g0_sub0, g0_sub1, ..., g0_sub5, g1_sub0, ...]
* sheet.getRows().add(Arrays.asList("2026-08", 0.5, 1.2, 0.3, "2026-08-15 03:00:00", 2.1, "...", ...));
* }</pre>
*/
@Data
@NoArgsConstructor
public static class GroupedSheetData {
/** Sheet 页名称 */
private String sheetName;
/** 行标题列名(如"日期",该列在 Row0+Row1 纵向合并) */
private String rowHeaderLabel;
/** 分组名称列表(如["XX水电站","YY水电站"] */
private List<String> groupNames;
/** 子表头列表(每个分组下都有相同的子表头) */
private List<String> subHeaders;
/** 数据行:每行为 [rowLabel, group0.sub0, group0.sub1, ..., group0.subN, group1.sub0, ...] */
private List<List<Object>> rows = new ArrayList<>();
public GroupedSheetData(String sheetName, String rowHeaderLabel,
List<String> groupNames, List<String> subHeaders) {
this.sheetName = sheetName;
this.rowHeaderLabel = rowHeaderLabel;
this.groupNames = groupNames;
this.subHeaders = subHeaders;
}
}
// ======================== 核心导出方法 ========================
/**
* 导出 ZIP 压缩包到 HttpServletResponse浏览器下载
*
* @param response HttpServletResponse
* @param zipFileName 下载文件名 .zip 扩展名
* @param excelList 要导出的 Excel 数据集合
*/
public static void exportToResponse(HttpServletResponse response, String zipFileName, List<ExcelData> excelList) {
response.setContentType("application/zip");
response.setCharacterEncoding("UTF-8");
try {
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
response.setHeader("Content-Disposition", "attachment; filename=export.zip");
}
try (ServletOutputStream os = response.getOutputStream()) {
exportToStream(os, excelList);
os.flush();
} catch (IOException e) {
log.error("导出 ZIP 到 Response 失败", e);
throw new RuntimeException("导出失败: " + e.getMessage(), e);
}
}
/**
* 导出 ZIP 压缩包到本地文件
*
* @param targetFile 目标文件
* @param excelList 要导出的 Excel 数据集合
*/
public static void exportToFile(File targetFile, List<ExcelData> excelList) {
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
exportToStream(fos, excelList);
} catch (IOException e) {
log.error("导出 ZIP 到文件失败: {}", targetFile.getAbsolutePath(), e);
throw new RuntimeException("导出失败: " + e.getMessage(), e);
}
}
/**
* 导出 ZIP 压缩包到任意 OutputStream
* <p>
* 实现原理先将每个 Excel 写入临时字节数组再用内存 ZIP 打包避免磁盘 IO
*
* @param os 目标输出流
* @param excelList 要导出的 Excel 数据集合
*/
public static void exportToStream(OutputStream os, List<ExcelData> excelList) throws IOException {
if (excelList == null || excelList.isEmpty()) {
log.warn("导出数据为空,跳过 ZIP 生成");
return;
}
// 延迟创建 ZIP累计所有 Excel 字节数组
ByteArrayOutputStream zipBaos = new ByteArrayOutputStream();
try (java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(zipBaos)) {
for (ExcelData excelData : excelList) {
if (excelData.getSheets() == null || excelData.getSheets().isEmpty()) {
continue;
}
byte[] excelBytes = workbookToBytes(createWorkbook(excelData));
String entryName = sanitizeFileName(excelData.getFileName()) + ".xlsx";
java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry(entryName);
zos.putNextEntry(entry);
zos.write(excelBytes);
zos.closeEntry();
}
zos.finish();
}
os.write(zipBaos.toByteArray());
}
// ======================== 分组表头导出方法 ========================
/**
* 导出分组表头数据为 ZIP每个 GroupedSheetData 生成一个独立的 Excel 文件在 ZIP
*
* @param response HttpServletResponse
* @param zipFileName 下载文件名
* @param sheets 分组表头数据列表每个元素 = ZIP 中一个 .xlsx 文件
*/
public static void exportGroupedToResponse(HttpServletResponse response, String zipFileName,
List<GroupedSheetData> sheets) {
response.setContentType("application/zip");
response.setCharacterEncoding("UTF-8");
try {
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
response.setHeader("Content-Disposition", "attachment; filename=export.zip");
}
try (ServletOutputStream os = response.getOutputStream()) {
exportGroupedToStream(os, sheets);
os.flush();
} catch (IOException e) {
log.error("导出分组 ZIP 到 Response 失败", e);
throw new RuntimeException("导出失败: " + e.getMessage(), e);
}
}
/**
* 导出分组表头数据 ZIP 到任意 OutputStream
* <p>
* 每个 GroupedSheetData 作为一个 .xlsx 文件打入 ZIP
*/
public static void exportGroupedToStream(OutputStream os, List<GroupedSheetData> sheets) throws IOException {
if (sheets == null || sheets.isEmpty()) {
log.warn("导出数据为空,跳过 ZIP 生成");
return;
}
ByteArrayOutputStream zipBaos = new ByteArrayOutputStream();
try (java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(zipBaos)) {
for (GroupedSheetData sheet : sheets) {
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
try {
fillGroupedSheet(workbook, sheet);
byte[] bytes = workbookToBytes(workbook);
String entryName = sanitizeFileName(sheet.getSheetName()) + ".xlsx";
java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry(entryName);
zos.putNextEntry(entry);
zos.write(bytes);
zos.closeEntry();
} finally {
workbook.close();
workbook.dispose();
}
}
zos.finish();
}
os.write(zipBaos.toByteArray());
}
/**
* Workbook 中填充一个带分组表头的 Sheet
*
* @param workbook 目标 Workbook
* @param data 分组表头数据
*/
public static void fillGroupedSheet(Workbook workbook, GroupedSheetData data) {
String name = safeName(data.getSheetName());
Sheet sheet = workbook.createSheet(name);
if (sheet instanceof SXSSFSheet) {
((SXSSFSheet) sheet).trackAllColumnsForAutoSizing();
}
CellStyle groupHeaderStyle = createHeaderStyle(workbook);
CellStyle subHeaderStyle = createSubHeaderStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
int groupCount = data.getGroupNames() != null ? data.getGroupNames().size() : 0;
int subCols = data.getSubHeaders() != null ? data.getSubHeaders().size() : 0;
int totalCols = 1 + groupCount * subCols; // 1 = rowHeaderLabel
// Row 0: 分组表头
Row groupRow = sheet.createRow(0);
groupRow.setHeightInPoints(24);
Cell labelCell = groupRow.createCell(0);
labelCell.setCellValue(data.getRowHeaderLabel());
labelCell.setCellStyle(groupHeaderStyle);
// 行标题列 Row0+Row1 合并
if (data.getRowHeaderLabel() != null) {
sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, 0));
}
int colIdx = 1;
for (int g = 0; g < groupCount; g++) {
String groupName = data.getGroupNames().get(g);
Cell gc = groupRow.createCell(colIdx);
gc.setCellValue(groupName);
gc.setCellStyle(groupHeaderStyle);
if (subCols > 1) {
sheet.addMergedRegion(new CellRangeAddress(0, 0, colIdx, colIdx + subCols - 1));
}
colIdx += subCols;
}
// Row 1: 子表头
Row subRow = sheet.createRow(1);
subRow.setHeightInPoints(22);
colIdx = 1;
for (int g = 0; g < groupCount; g++) {
for (int s = 0; s < subCols; s++) {
Cell sc = subRow.createCell(colIdx++);
sc.setCellValue(data.getSubHeaders().get(s));
sc.setCellStyle(subHeaderStyle);
}
}
// Row 2+: 数据行
if (data.getRows() != null) {
int rowIdx = 2;
for (List<Object> rowData : data.getRows()) {
Row dataRow = sheet.createRow(rowIdx++);
for (int c = 0; c < Math.min(rowData.size(), totalCols); c++) {
Cell cell = dataRow.createCell(c);
Object val = rowData.get(c);
if (c == 0) {
// 行标题列用居中对齐
cell.setCellStyle(groupHeaderStyle);
} else {
cell.setCellStyle(dataStyle);
}
setCellValue(cell, val);
}
}
}
// 自动列宽
autoSizeColumns(sheet, totalCols);
// 冻结前两行
sheet.createFreezePane(0, 2);
}
/**
* 创建子表头样式浅蓝背景加粗居中带边框
*/
public static CellStyle createSubHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
setThinBorder(style);
return style;
}
// ======================== Excel 生成方法 ========================
/**
* 根据 ExcelData 创建一个完整的 Excel Workbook含所有 Sheet
* <p>
* 使用 SXSSFWorkbook流式写入支持大数据量导出内存占用可控
*
* @param excelData 单个 Excel 的数据定义
* @return 填充完毕的 Workbook调用方负责关闭以释放临时文件
*/
public static Workbook createWorkbook(ExcelData excelData) {
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 内存中保留 100 其余写临时文件
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
for (SheetData sheetData : excelData.getSheets()) {
if (sheetData == null) {
continue;
}
fillSheet(workbook, sheetData, headerStyle, dataStyle);
}
return workbook;
}
/**
* 填充单个 Sheet 页的数据
*
* @param workbook 目标 Workbook
* @param sheetData Sheet 数据定义
* @param headerStyle 表头样式
* @param dataStyle 数据行样式
*/
public static void fillSheet(Workbook workbook, SheetData sheetData, CellStyle headerStyle, CellStyle dataStyle) {
String name = safeName(sheetData.getSheetName());
Sheet sheet = workbook.createSheet(name);
// SXSSFSheet 必须在写入数据前开启列追踪否则 autoSizeColumn 会报错
if (sheet instanceof SXSSFSheet) {
((SXSSFSheet) sheet).trackAllColumnsForAutoSizing();
}
List<String> headers = sheetData.getHeaders();
List<List<Object>> rows = sheetData.getRows();
int colCount = headers != null ? headers.size() : 0;
// 设置列宽默认值后续在数据写入后统一自动调整
int currentRow = 0;
// ---- 写表头 ----
if (headers != null && !headers.isEmpty()) {
Row headerRow = sheet.createRow(currentRow++);
headerRow.setHeightInPoints(22);
for (int i = 0; i < colCount; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers.get(i));
cell.setCellStyle(headerStyle);
}
}
// ---- 写数据行 ----
if (rows != null) {
CellStyle subHeaderStyle = createSubHeaderStyle(workbook);
for (int r = 0; r < rows.size(); r++) {
List<Object> rowData = rows.get(r);
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++) {
Cell cell = dataRow.createCell(i);
Object value = rowData.get(i);
setCellValue(cell, value);
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);
// 冻结表头行
if (currentRow > 1) {
int freezeRows = 1 + sheetData.getHeaderRowCount();
sheet.createFreezePane(0, Math.min(freezeRows, currentRow - 1));
}
}
// ======================== 样式定义 ========================
/**
* 创建表头样式蓝色背景白色加粗字体居中对齐带边框
*/
public static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
// 背景色浅淡的苍白蓝
style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 字体黑色加粗
Font font = workbook.createFont();
font.setBold(true);
font.setColor(IndexedColors.BLACK.getIndex());
font.setFontHeightInPoints((short) 11);
style.setFont(font);
// 对齐居中
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 边框
setThinBorder(style);
return style;
}
/**
* 创建数据行样式无背景正常字体左对齐带边框
*/
public static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 自动换行
style.setWrapText(true);
setThinBorder(style);
return style;
}
/**
* 设置细线边框
*/
private static void setThinBorder(CellStyle style) {
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
}
// ======================== 列宽自动调整 ========================
/**
* 自动调整列宽根据内容宽度
* <p>
* 遍历每一列取表头和所有数据行中最宽的内容作为列宽基准
*
* @param sheet Sheet 对象
* @param colCount 列数
*/
public static void autoSizeColumns(Sheet sheet, int colCount) {
for (int i = 0; i < colCount; i++) {
sheet.autoSizeColumn(i);
// 自动列宽后留一点余量避免内容紧贴边框
int currentWidth = sheet.getColumnWidth(i);
if (currentWidth > 0) {
sheet.setColumnWidth(i, Math.min(currentWidth + 512, 255 * 256)); // 最大 255 字符宽
}
}
}
// ======================== 辅助方法 ========================
/**
* 类型安全方式构建 SheetData
* <p>
* 适用于已有实体类 List 的场景通过方法引用指定字段取值避免手动逐字段提取
*
* <pre>{@code
* SheetData data = ExportZipUtil.fromTypedData("电站信息",
* Arrays.asList("编码", "名称", "经度", "纬度"),
* Arrays.asList(Station::getStcd, Station::getStnm, Station::getLgtd, Station::getLttd),
* stationList);
* }</pre>
*
* @param sheetName Sheet 名称
* @param headers 表头列表
* @param extractors 字段取值函数 headers 一一对应
* @param data 实体数据集合
* @param <T> 实体类型
* @return 构建好的 SheetData
*/
public static <T> SheetData fromTypedData(String sheetName,
List<String> headers,
List<Function<T, Object>> extractors,
List<T> data) {
List<List<Object>> rows = data.stream()
.map(entity -> extractors.stream()
.map(ext -> ext.apply(entity))
.collect(Collectors.toList()))
.collect(Collectors.toList());
return new SheetData(sheetName, headers, rows);
}
/**
* Workbook 写入字节数组并关闭 Workbook
*/
private static byte[] workbookToBytes(Workbook workbook) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
workbook.write(baos);
} finally {
workbook.close();
// SXSSFWorkbook 会清理临时文件
if (workbook instanceof SXSSFWorkbook) {
((SXSSFWorkbook) workbook).dispose();
}
}
return baos.toByteArray();
}
/**
* 文件名非法字符过滤Windows + 通用
*/
private static String sanitizeFileName(String fileName) {
if (fileName == null || fileName.isEmpty()) {
return "sheet";
}
return fileName.replaceAll("[\\\\/:*?\"<>|]", "_");
}
/**
* Sheet 名称安全处理Excel Sheet 名最长 31 字符
*/
private static String safeName(String name) {
if (name == null || name.isEmpty()) {
return "Sheet1";
}
// Excel Sheet 名禁止包含 [ ] : * ? / \
String safe = name.replaceAll("[\\[\\]:*?/\\\\]", "_");
return safe.length() > 31 ? safe.substring(0, 31) : safe;
}
/**
* 设置单元格值自动适配类型
*/
private static void setCellValue(Cell cell, Object value) {
if (value == null) {
cell.setCellValue("");
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else if (value instanceof java.util.Date) {
cell.setCellValue((java.util.Date) value);
} else {
cell.setCellValue(String.valueOf(value));
}
}
// ======================== 测试方法 ========================
/**
* 本地运行测试生成模拟数据 ZIP 文件到 D:/export_sample.zip
*/
public static void main(String[] args) {
// ---------- Excel 1电站基础数据1个Sheet基础信息 ----------
List<List<Object>> stationRows = new ArrayList<>();
for (int i = 1; i <= 50; i++) {
stationRows.add(Arrays.asList(
"STCD" + String.format("%05d", i), // 测站编码
"XX水电站" + i + "", // 测站名称
"长江流域", // 所在流域
110.123456 + Math.random() * 10, // 经度
30.654321 + Math.random() * 10, // 纬度
"坝式水电站", // 工程类型
Math.random() > 0.5 ? "运行中" : "在建", // 建设状态
"省级", // 级别
2000 + (i % 24) // 建成时间
));
}
SheetData stationSheet = new SheetData("电站基础信息",
Arrays.asList("测站编码", "测站名称", "所在流域", "经度", "纬度",
"工程类型", "建设状态", "级别", "建成时间"),
stationRows);
// ---------- Sheet 2水位监测数据 ----------
List<List<Object>> monitorRows = new ArrayList<>();
String[] months = {"2026-01", "2026-02", "2026-03", "2026-04", "2026-05", "2026-06"};
for (int i = 1; i <= 30; i++) {
for (String month : months) {
monitorRows.add(Arrays.asList(
"STCD" + String.format("%05d", i),
"XX水电站" + i + "",
month,
String.format("%.2f", 100 + Math.random() * 50), // 水位(m)
String.format("%.2f", 500 + Math.random() * 2000), // 流量(m³/s)
String.format("%.1f", 15 + Math.random() * 20), // 水温()
Math.random() > 0.8 ? "超标" : "正常" // 水质状态
));
}
}
SheetData monitorSheet = new SheetData("水位监测数据",
Arrays.asList("测站编码", "测站名称", "监测月份", "水位(m)", "流量(m³/s)",
"水温(℃)", "水质状态"),
monitorRows);
// ---------- Excel 2流域概况数据 ----------
List<List<Object>> basinRows = new ArrayList<>();
String[] basinNames = {"长江流域", "黄河流域", "珠江流域", "淮河流域", "松花江流域", "海河流域"};
String[] provinces = {"湖北/湖南/四川", "河南/山东/陕西", "广东/广西", "安徽/江苏", "黑龙江/吉林", "河北/天津"};
for (int i = 0; i < basinNames.length; i++) {
basinRows.add(Arrays.asList(
"RV" + String.format("%03d", i + 1), // 流域编码
basinNames[i], // 流域名称
i == 0 ? "一级" : "二级", // 级别
provinces[i], // 覆盖省份
100 + Math.random() * 900, // 流域面积(万km²)
500 + Math.random() * 5000, // 年径流量(亿m³)
(int) (1000 + Math.random() * 2000), // 电站数量
"良好" // 生态状态
));
}
SheetData basinSheet = new SheetData("流域概况",
Arrays.asList("流域编码", "流域名称", "级别", "覆盖省份", "流域面积(万km²)",
"年径流量(亿m³)", "电站数量(个)", "生态状态"),
basinRows);
// ---------- Sheet 4鱼道设施数据 ----------
List<List<Object>> fishwayRows = new ArrayList<>();
String[] types = {"竖缝式鱼道", "池堰式鱼道", "升鱼机", "集运鱼船"};
for (int i = 1; i <= 20; i++) {
fishwayRows.add(Arrays.asList(
"STCD" + String.format("%05d", i),
"XX水电站" + i + "",
types[i % types.length], // 鱼道类型
String.format("%.1f", 5 + Math.random() * 95), // 过鱼效率(%)
String.format("%.2f", 50 + Math.random() * 200), // 鱼道长度(m)
String.format("%.1f", 2 + Math.random() * 8), // 鱼道宽度(m)
Math.random() > 0.3 ? "运行中" : "检修中", // 运行状态
"2025-0" + (1 + i % 9) + "-01" // 投运日期
));
}
SheetData fishwaySheet = new SheetData("鱼道设施",
Arrays.asList("电站编码", "电站名称", "鱼道类型", "过鱼效率(%)",
"鱼道长度(m)", "鱼道宽度(m)", "运行状态", "投运日期"),
fishwayRows);
// ---------- 组装导出 ----------
ExcelData excel1 = new ExcelData("电站综合数据", Arrays.asList(stationSheet, monitorSheet));
ExcelData excel2 = new ExcelData("流域与鱼道数据", Arrays.asList(basinSheet, fishwaySheet));
String outputPath = "D:/export_sample.zip";
try {
log.info("开始生成测试数据...");
exportToFile(new File(outputPath), Arrays.asList(excel1, excel2));
log.info("导出成功!文件路径: {}", outputPath);
System.out.println("导出成功!文件路径: " + outputPath);
} catch (Exception e) {
log.error("导出失败", e);
System.err.println("导出失败: " + e.getMessage());
}
}
}

View File

@ -13,8 +13,8 @@ spring:
# ==================== 达梦(DM) 数据源 ====================
dm-master:
driverClassName: dm.jdbc.driver.DmDriver
url: "${DB_DM_MASTER_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA}"
username: "${DB_DM_MASTER_USERNAME:QGC_REFA}"
url: "${DB_DM_MASTER_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA_TEST}"
username: "${DB_DM_MASTER_USERNAME:QGC_REFA_TEST}"
password: "${DB_DM_MASTER_PASSWORD:Y4M4K1oCkL8U}"
initial-size: 5
min-idle: 5
@ -42,8 +42,8 @@ spring:
max-pool-prepared-statement-per-connection-size: 100
dm-slave:
driverClassName: dm.jdbc.driver.DmDriver
url: "${DB_DM_SLAVE_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA}"
username: "${DB_DM_SLAVE_USERNAME:QGC_REFA}"
url: "${DB_DM_SLAVE_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA_TEST}"
username: "${DB_DM_SLAVE_USERNAME:QGC_REFA_TEST}"
password: "${DB_DM_SLAVE_PASSWORD:Y4M4K1oCkL8U}"
initial-size: 5
min-idle: 5

View File

@ -53,83 +53,97 @@
ORDER BY ORDER_INDEX ASC
</select>
<select id="selectRegDropdown" resultMap="BaseResultMap">
SELECT
t.RVCD,
t.DISPLAY_RVNM AS RVNM,
t.PRVCD,
t.GRD,
t.PATH,
t.ORDER_INDEX,
-- t.LGTD,
-- t.LTTD,
-- t.AREA,
-- t.PERIMETER,
-- t.COUNTRY,
-- t.LOGO,
-- t.INTRODUCE,
-- t.INFFILE,
t.SHORTNAME,
t.SHEPRANGE,
t.BASEID
-- ,
-- t.ENABLED,
-- t.RECORD_USER,
-- t.RECORD_TIME,
-- t.MODIFY_USER,
-- t.MODIFY_TIME,
-- t.IS_DELETED,
-- t.DELETE_USER,
-- t.DELETE_TIME
FROM (
SELECT
d.RVCD,
LTRIM(SYS_CONNECT_BY_PATH(d.RVNM, '-'), '-') AS DISPLAY_RVNM,
d.PRVCD,
d.GRD,
d.PATH,
d.ORDER_INDEX,
-- d.LGTD,
-- d.LTTD,
-- d.AREA,
-- d.PERIMETER,
-- d.COUNTRY,
-- d.LOGO,
-- d.INTRODUCE,
-- d.INFFILE,
d.SHORTNAME,
d.SHEPRANGE,
d.BASEID,
-- d.ENABLED,
-- d.RECORD_USER,
-- d.RECORD_TIME,
-- d.MODIFY_USER,
-- d.MODIFY_TIME,
-- d.IS_DELETED,
-- d.DELETE_USER,
-- d.DELETE_TIME,
SYS_CONNECT_BY_PATH(LPAD(NVL(d.ORDER_INDEX, 999999999), 10, '0'), '/') AS ORDER_PATH,
LEVEL AS TREE_LEVEL
FROM (
SELECT *
FROM SD_RVCD_DIC
WHERE NVL(ENABLED, 1) = 1
AND NVL(IS_DELETED, 0) = 0
) d
START WITH d.PRVCD IS NULL OR d.PRVCD = '' OR d.PRVCD = '0'
CONNECT BY PRIOR d.RVCD = d.PRVCD
ORDER SIBLINGS BY d.ORDER_INDEX ASC, d.RVCD ASC
) t
WHERE t.TREE_LEVEL > 1
FROM V_SD_RVCD_DIC_TREE
WHERE 1=1
<if test="rvcd != null and rvcd != ''">
AND t.RVCD = #{rvcd}
AND RVCD = #{rvcd}
</if>
<if test="rvnm != null and rvnm != ''">
AND t.DISPLAY_RVNM LIKE '%' || #{rvnm} || '%'
AND RVNM LIKE '%' || #{rvnm} || '%'
</if>
ORDER BY t.ORDER_PATH ASC, t.RVCD ASC
ORDER BY ORDER_PATH ASC, RVCD ASC
</select>
<!-- <select id="selectRegDropdown" resultMap="BaseResultMap">-->
<!-- SELECT-->
<!-- t.RVCD,-->
<!-- t.DISPLAY_RVNM AS RVNM,-->
<!-- t.PRVCD,-->
<!-- t.GRD,-->
<!-- t.PATH,-->
<!-- t.ORDER_INDEX,-->
<!--&#45;&#45; t.LGTD,-->
<!--&#45;&#45; t.LTTD,-->
<!--&#45;&#45; t.AREA,-->
<!--&#45;&#45; t.PERIMETER,-->
<!--&#45;&#45; t.COUNTRY,-->
<!--&#45;&#45; t.LOGO,-->
<!--&#45;&#45; t.INTRODUCE,-->
<!--&#45;&#45; t.INFFILE,-->
<!-- t.SHORTNAME,-->
<!-- t.SHEPRANGE,-->
<!-- t.BASEID-->
<!--&#45;&#45; ,-->
<!--&#45;&#45; t.ENABLED,-->
<!--&#45;&#45; t.RECORD_USER,-->
<!--&#45;&#45; t.RECORD_TIME,-->
<!--&#45;&#45; t.MODIFY_USER,-->
<!--&#45;&#45; t.MODIFY_TIME,-->
<!--&#45;&#45; t.IS_DELETED,-->
<!--&#45;&#45; t.DELETE_USER,-->
<!--&#45;&#45; t.DELETE_TIME-->
<!-- FROM (-->
<!-- SELECT-->
<!-- d.RVCD,-->
<!-- LTRIM(SYS_CONNECT_BY_PATH(d.RVNM, '-'), '-') AS DISPLAY_RVNM,-->
<!-- d.PRVCD,-->
<!-- d.GRD,-->
<!-- d.PATH,-->
<!-- d.ORDER_INDEX,-->
<!--&#45;&#45; d.LGTD,-->
<!--&#45;&#45; d.LTTD,-->
<!--&#45;&#45; d.AREA,-->
<!--&#45;&#45; d.PERIMETER,-->
<!--&#45;&#45; d.COUNTRY,-->
<!--&#45;&#45; d.LOGO,-->
<!--&#45;&#45; d.INTRODUCE,-->
<!--&#45;&#45; d.INFFILE,-->
<!-- d.SHORTNAME,-->
<!-- d.SHEPRANGE,-->
<!-- d.BASEID,-->
<!--&#45;&#45; d.ENABLED,-->
<!--&#45;&#45; d.RECORD_USER,-->
<!--&#45;&#45; d.RECORD_TIME,-->
<!--&#45;&#45; d.MODIFY_USER,-->
<!--&#45;&#45; d.MODIFY_TIME,-->
<!--&#45;&#45; d.IS_DELETED,-->
<!--&#45;&#45; d.DELETE_USER,-->
<!--&#45;&#45; d.DELETE_TIME,-->
<!-- SYS_CONNECT_BY_PATH(LPAD(NVL(d.ORDER_INDEX, 999999999), 10, '0'), '/') AS ORDER_PATH,-->
<!-- LEVEL AS TREE_LEVEL-->
<!-- FROM (-->
<!-- SELECT *-->
<!-- FROM SD_RVCD_DIC-->
<!-- WHERE NVL(ENABLED, 1) = 1-->
<!-- AND NVL(IS_DELETED, 0) = 0-->
<!-- ) d-->
<!-- START WITH d.PRVCD IS NULL OR d.PRVCD = '' OR d.PRVCD = '0'-->
<!-- CONNECT BY PRIOR d.RVCD = d.PRVCD-->
<!-- ORDER SIBLINGS BY d.ORDER_INDEX ASC, d.RVCD ASC-->
<!-- ) t-->
<!-- WHERE t.TREE_LEVEL > 1-->
<!-- <if test="rvcd != null and rvcd != ''">-->
<!-- AND t.RVCD = #{rvcd}-->
<!-- </if>-->
<!-- <if test="rvnm != null and rvnm != ''">-->
<!-- AND t.DISPLAY_RVNM LIKE '%' || #{rvnm} || '%'-->
<!-- </if>-->
<!-- ORDER BY t.ORDER_PATH ASC, t.RVCD ASC-->
<!-- </select>-->
<select id="selectRegDropdownWithEng" resultMap="BaseResultMap">
SELECT
t.RVCD,

View File

@ -0,0 +1,42 @@
<?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.MsAlongdetBMapper">
<!-- 查询指定 STTP 的出库沿程配置,关联 V_MS_STBPRP_T 获取测站名称 -->
<select id="selectAlongStations" resultType="java.util.HashMap">
SELECT
A.RSTCD,
A.STCD,
A.SORT,
A.STTP,
S.STNM,
S.BASE_NAME
FROM MS_ALONGDET_B A
LEFT JOIN V_MS_STBPRP_T S ON S.STCD = A.STCD AND S.STTP = A.STTP
WHERE NVL(A.IS_DELETED, 0) = 0
AND A.IO_DIRECTION = '1'
AND A.STTP = #{sttp}
AND A.RSTCD IN
<foreach collection="rstcdList" item="rstcd" open="(" separator="," close=")">
#{rstcd}
</foreach>
ORDER BY A.SORT
</select>
<!-- 查询所有沿程配置的电站排序MS_ALONG_B + MS_ALONGDET_B
按 MS_ALONG_B.ORDER_INDEX, MS_ALONGDET_B.SORT 排序 -->
<select id="selectAlongSortOrder" resultType="java.util.HashMap">
SELECT D.STCD, B.ORDER_INDEX
FROM MS_ALONGDET_B D
INNER JOIN MS_ALONG_B B ON B.ID = D.ALONG_ID AND NVL(B.IS_DELETED, 0) = 0
WHERE NVL(D.IS_DELETED, 0) = 0
-- AND D.IO_DIRECTION = '1'
AND D.STCD IN
<foreach collection="rstcdList" item="rstcd" open="(" separator="," close=")">
#{rstcd}
</foreach>
ORDER BY B.ORDER_INDEX, D.SORT
</select>
</mapper>

View File

@ -0,0 +1,23 @@
<?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.SdHydropwRMapper">
<!-- 电站运行小时数据动态字段查询 -->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
TM,
${fieldName} AS VALUE
FROM SD_HYDROPW_R
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND TM &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND ${fieldName} IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,23 @@
<?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.SdHydropwdaySMapper">
<!-- 电站运行日数据动态字段查询 -->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
DT,
${fieldName} AS VALUE
FROM SD_HYDROPWDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND ${fieldName} IS NOT NULL
</select>
</mapper>

View File

@ -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 &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; 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 &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
</select>
</mapper>

View File

@ -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 &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; 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 &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
</select>
</mapper>

View File

@ -0,0 +1,26 @@
<?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.SdRiverRMapper">
<!--
动态字段查询:根据 fieldName 参数选择 V/Q/Z 列
利用 STCD + TM + IS_DELETED 复合索引
-->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
TM,
${fieldName} AS VALUE
FROM SD_RIVER_R
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND TM &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND ${fieldName} IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,26 @@
<?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.SdRiverdaySMapper">
<!--
动态字段查询:根据 fieldName 参数选择 V/Q/Z 列
利用 STCD + DT + IS_DELETED 复合索引
-->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
DT,
${fieldName} AS VALUE
FROM SD_RIVERDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND ${fieldName} IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,71 @@
<?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.SdWqRMapper">
<!--
水质小时数据动态字段查询
利用 STCD + TM + IS_DELETED 复合索引
-->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
TM,
${fieldName} AS VALUE,
SFDB
FROM SD_WQ_R
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND TM &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND ${fieldName} IS NOT NULL
</select>
<!-- 水质小时达标率统计:按 STCD 聚合 SFDB=1 达标数、总数 -->
<select id="selectComplianceData" resultType="java.util.HashMap">
SELECT
STCD,
SUM(CASE WHEN SFDB = 1 THEN 1 ELSE 0 END) AS COMPLIANT_CNT,
SUM(CASE WHEN SFDB IN (0, 1) THEN 1 ELSE 0 END) AS TOTAL_CNT
FROM SD_WQ_R
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND TM &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND SFDB IS NOT NULL
GROUP BY STCD
</select>
<!-- 查询各测站主要污染物SFDB=0 时的 WQ_SFDBHN_YS -->
<select id="selectPollutants" resultType="java.util.HashMap">
SELECT
STCD,
WQ_SFDBHN_YS AS POLLUTANT
FROM SD_WQ_R
WHERE IS_DELETED = 0
AND SFDB = 0
AND WQ_SFDBHN_YS IS NOT NULL
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND TM &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
</select>
<!-- 查询 SD_WQ_R 表的要素中文名(通过 ST_TB_B + ST_TB_YS_B 关联) -->
<select id="selectColumnComments" resultType="java.util.HashMap">
SELECT YS.YS AS COLUMN_NAME, YS.YS_SHOW_NAME AS COMMENTS
FROM ST_TB_B TB
INNER JOIN ST_TB_YS_B YS ON YS.TB_ID = TB.ID AND NVL(YS.IS_DELETED, 0) = 0
WHERE NVL(TB.IS_DELETED, 0) = 0
AND TB.TB = 'SD_WQ_R'
</select>
</mapper>

View File

@ -0,0 +1,44 @@
<?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.SdWqdaySMapper">
<!--
水质日数据动态字段查询
利用 STCD + DT + IS_DELETED 复合索引
-->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
DT,
${fieldName} AS VALUE
FROM SD_WQDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND ${fieldName} IS NOT NULL
</select>
<!-- 水质日均达标率统计:按 STCD 聚合 SFDB=1 达标数、总数 -->
<select id="selectComplianceData" resultType="java.util.HashMap">
SELECT
STCD,
SUM(CASE WHEN SFDB = 1 THEN 1 ELSE 0 END) AS COMPLIANT_CNT,
SUM(CASE WHEN SFDB IN (0, 1) THEN 1 ELSE 0 END) AS TOTAL_CNT
FROM SD_WQDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND SFDB IS NOT NULL
GROUP BY STCD
</select>
</mapper>

View File

@ -0,0 +1,23 @@
<?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.SdWtrvRMapper">
<!-- 河道水温小时数据查询 -->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
TM,
WT AS VALUE
FROM SD_WTRV_R
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND TM &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND TM &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND WT IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,23 @@
<?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.SdWtrvdaySMapper">
<!-- 河道水温日数据查询 -->
<select id="selectRawData" resultType="java.util.HashMap">
SELECT
STCD,
DT,
WT AS VALUE
FROM SD_WTRVDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND WT IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,42 @@
<?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.SdWtvtdaySMapper">
<!-- 查询月内每日垂向水温数据 -->
<select id="selectDailyData" resultType="java.util.HashMap">
SELECT
STCD,
WTHG,
VWT,
EXTRACT(DAY FROM DT) AS DAY_NUM
FROM SD_WTVTDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND VWT IS NOT NULL
</select>
<!-- 查询月均垂向水温 -->
<select id="selectMonthlyAvg" resultType="java.util.HashMap">
SELECT
STCD,
WTHG,
AVG(VWT) AS VWT
FROM SD_WTVTDAY_S
WHERE IS_DELETED = 0
AND STCD IN
<foreach collection="stcdList" item="stcd" open="(" separator="," close=")">
#{stcd}
</foreach>
AND DT &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
AND DT &lt; TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
AND VWT IS NOT NULL
GROUP BY STCD, WTHG
</select>
</mapper>

View File

@ -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>