diff --git a/backend/src/main/java/com/yfd/platform/config/JobRunner.java b/backend/src/main/java/com/yfd/platform/config/JobRunner.java index f6d180e5..00a1f203 100644 --- a/backend/src/main/java/com/yfd/platform/config/JobRunner.java +++ b/backend/src/main/java/com/yfd/platform/config/JobRunner.java @@ -75,16 +75,16 @@ public class JobRunner implements ApplicationRunner { ao.setStartTime(DateUtil.beginOfYear(DateUtil.offset(ao.getEndTime(), DateField.YEAR,-1))); } envWqDataService.wqLastData(ao); - log.info("--------------------wq最新一条数据---------------------"); - eqJobService.eqAnchorPointLastDate(ao); - log.info("--------------------eq最新一条数据---------------------"); - eqJobService.eqAnchorPointLastDate(ao); - log.info("--------------------eq最新一条数据---------------------"); - dwJobService.wtLastData(ao); - log.info("--------------------wt最新一条数据---------------------"); - dwJobService.wtvtPointData(ao); - log.info("--------------------wt最新一条数据---------------------"); - fhJobService.zqLastData(ao); - log.info("--------------------fh最新一条数据---------------------"); +// log.info("--------------------wq最新一条数据---------------------"); +// eqJobService.eqAnchorPointLastDate(ao); +// log.info("--------------------eq最新一条数据---------------------"); +// eqJobService.eqAnchorPointLastDate(ao); +// log.info("--------------------eq最新一条数据---------------------"); +// dwJobService.wtLastData(ao); +// log.info("--------------------wt最新一条数据---------------------"); +// dwJobService.wtvtPointData(ao); +// log.info("--------------------wt最新一条数据---------------------"); +// fhJobService.zqLastData(ao); +// log.info("--------------------fh最新一条数据---------------------"); } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java index 13f1f80b..a62bc1c3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java @@ -42,7 +42,8 @@ public class MsOperationLog implements Serializable { /** * 操作记录的名称(瞬态字段,不持久化,由后端查询后填充) */ - @TableField(exist = false) +// @TableField(exist = false) + @TableField("RECORD_NAME") private String recordName; } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdDwBH.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdDwBH.java index f652c727..1b097d7c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdDwBH.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdDwBH.java @@ -63,6 +63,9 @@ public class SdDwBH implements Serializable { @FieldChinese("是否启用") private Integer usfl; + @TableField(exist = false) + private String usflName; + /** 数据是否接入:0=未接入 1=已接入 */ @FieldChinese("数据是否接入") private Integer dtin; @@ -154,10 +157,16 @@ public class SdDwBH implements Serializable { @FieldChinese("投资是否计入主体工程") private Integer invinmn; + @TableField(exist = false) + private String invinmnName; + /** 是否有调度规程分析:0=无调度规程 1=有调度规程 */ @FieldChinese("是否有调度规程分析") private Integer dispatch; + @TableField(exist = false) + private String dispatchName; + /** 调度规程文字描述 */ @FieldChinese("调度规程文字描述") private String dispatchDes; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/entity/vo/BatchDeleteByIdsAo.java b/backend/src/main/java/com/yfd/platform/qgc_base/entity/vo/BatchDeleteByIdsAo.java new file mode 100644 index 00000000..5f87ad38 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/entity/vo/BatchDeleteByIdsAo.java @@ -0,0 +1,23 @@ +package com.yfd.platform.qgc_base.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +@Schema(description = "批量删除参数") +public class BatchDeleteByIdsAo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "数据来源") + private String source; + + @Schema(description = "数据类型") + private String dataType; + + @Schema(description = "删除数据列表") + private List ids; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsEiaApprovalBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsEiaApprovalBServiceImpl.java index 9735a4f8..b80c1e07 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsEiaApprovalBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsEiaApprovalBServiceImpl.java @@ -67,7 +67,7 @@ public class MsEiaApprovalBServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -379,7 +381,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -416,7 +418,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -446,14 +448,15 @@ public class MsOperationLogServiceImpl extends ServiceImpl codeToNameFieldMap = buildCodeToNameFieldMap(codeToNameMetaList); - MsOperationLog mainLog = buildMainLog(tableName, recordId, "新增", source,"01"); + // 调用示例:尝试获取 name,如果没有则尝试获取 code + String stnm = getFieldValue(entity, "stnm"); + MsOperationLog mainLog = buildMainLog(tableName, recordId, stnm, "新增", source,"01"); msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); @@ -578,6 +583,17 @@ public class MsOperationLogServiceImpl extends ServiceImpl void recordDeleteDetailLog(String recordId,String tableName, T entity, String source) { if (entity == null) { @@ -619,7 +635,8 @@ public class MsOperationLogServiceImpl extends ServiceImpl codeToNameFieldMap = buildCodeToNameFieldMap(codeToNameMetaList); - MsOperationLog mainLog = buildMainLog(tableName, recordId, "修改", source,"02"); + String stnm = getFieldValue(after, "stnm"); + MsOperationLog mainLog = buildMainLog(tableName, recordId,stnm, "修改", source,"02"); msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); @@ -662,8 +679,8 @@ public class MsOperationLogServiceImpl extends ServiceImpl codeToNameFieldMap = buildCodeToNameFieldMap(codeToNameMetaList); - - MsOperationLog mainLog = buildMainLog(tableName, recordId, "删除", source,"03"); + String stnm = getFieldValue(entity, "stnm"); + MsOperationLog mainLog = buildMainLog(tableName, recordId,stnm, "删除", source,"03"); msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsPsbmodulelbBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsPsbmodulelbBServiceImpl.java index 8dfd2ad4..60a192f5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsPsbmodulelbBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsPsbmodulelbBServiceImpl.java @@ -210,7 +210,7 @@ public class MsPsbmodulelbBServiceImpl extends ServiceImpl 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java index bd2e71de..201e7bd6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java @@ -110,7 +110,7 @@ public class SdAimonitorBHServiceImpl extends ServiceImpl 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java index 12b53e3f..12655a0c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java @@ -59,6 +59,10 @@ public class SdDwBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("usfl").modifyProperty("usflName").dictType("STATIC").dictSource("TY_SF").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("invinmn").modifyProperty("invinmnName").dictType("STATIC").dictSource("TY_SF").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dispatch").modifyProperty("dispatchName").dictType("STATIC").dictSource("DISPATCH").build()); + } @Override @@ -119,7 +123,7 @@ public class SdDwBHServiceImpl extends ServiceImpl impleme wrapper.set(SdDwBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd, TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(), TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java index 00b4eacc..3567b48e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java @@ -119,7 +119,7 @@ public class SdEqBHServiceImpl extends ServiceImpl impleme wrapper.set(SdEqBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } return count > 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java index 04b91361..47ceb010 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java @@ -116,7 +116,7 @@ public class SdFbrdBHServiceImpl extends ServiceImpl i SdFbrdBH entity = this.getById(stcd); if (this.removeById(stcd)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java index 2b16f0a8..24bf94d9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java @@ -120,7 +120,7 @@ public class SdFhbtBHServiceImpl extends ServiceImpl i wrapper.set(SdFhbtBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } return count > 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java index 270aaa7a..d2dec864 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java @@ -126,7 +126,7 @@ public class SdFishDictoryBServiceImpl extends ServiceImpl i wrapper.set(SdFpssBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } return count > 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java index 7d9dff9d..c23ad73b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java @@ -129,7 +129,7 @@ public class SdFpssRServiceImpl extends ServiceImpl impl wrapper.set(SdFpssR::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(id, TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getFtpName(), TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOpinfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOpinfoBHServiceImpl.java index da8fdfe3..69320de5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOpinfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOpinfoBHServiceImpl.java @@ -120,7 +120,7 @@ public class SdOpinfoBHServiceImpl extends ServiceImpl i SdOtteBH entity = this.getById(stcd); if (this.removeById(stcd)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java index 4543e6fb..da13127f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java @@ -117,7 +117,7 @@ public class SdOtweBHServiceImpl extends ServiceImpl i SdOtweBH entity = this.getById(stcd); if (this.removeById(stcd)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java index 0e9d549b..37ff0d59 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java @@ -122,7 +122,7 @@ public class SdSonarBHServiceImpl extends ServiceImpl impleme for (String stcd : stcds) { SdTeBH entity = this.getById(stcd); if (this.removeById(stcd)) { - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java index d752bbe6..606283d0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java @@ -118,7 +118,7 @@ public class SdVaBHServiceImpl extends ServiceImpl impleme wrapper.set(SdVaBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java index 00a72cbb..5777062e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java @@ -121,7 +121,7 @@ public class SdVdinfoBServiceImpl extends ServiceImpl 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java index ccf84432..6bba2a61 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java @@ -118,7 +118,7 @@ public class SdVpBHServiceImpl extends ServiceImpl impleme wrapper.set(SdVpBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source,CODE_TO_NAME_META_LIST); count++; + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source,CODE_TO_NAME_META_LIST); count++; } } return count > 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java index b5c46966..ebbbf1d9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java @@ -119,7 +119,7 @@ public class SdWeBHServiceImpl extends ServiceImpl impleme SdWeBH entity = this.getById(stcd); dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); if (this.removeById(stcd)) { - msOperationLogService.recordDeleteDetailLog(stcd, TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(), TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java index eb95b699..5358fa56 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java @@ -122,7 +122,7 @@ public class SdWqBHServiceImpl extends ServiceImpl impleme wrapper.set(SdWqBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } return count > 0; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java index fd918038..c29ff7e6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java @@ -124,7 +124,7 @@ public class SdWtBHServiceImpl extends ServiceImpl impleme wrapper.set(SdWtBH::getDeleteTime, new Date()); if (this.update(wrapper)) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); - msOperationLogService.recordDeleteDetailLog(stcd,TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(entity.getStcd(),TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); count++; } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java index 4df52a05..7ea2d5cf 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java @@ -47,6 +47,7 @@ import com.yfd.platform.qgc_base.mapper.MsOperationLogDetailMapper; import com.yfd.platform.qgc_base.mapper.MsOperationLogMapper; import com.yfd.platform.qgc_env.wq.entity.vo.MsWarnRuleDetailBVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; +import com.yfd.platform.utils.LogValueUtils; import com.yfd.platform.utils.QgcQueryWrapperUtil; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; @@ -6845,7 +6846,7 @@ public class EngEqDataServiceImpl implements EngEqDataService { Object oldValue = beforeHour == null ? null : beforeHour.get(entry.getKey()); Object newValue = updateData.get(entry.getKey()); // 数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00) - if (sameLogValue(oldValue, newValue)) { + if (LogValueUtils.sameLogValue(oldValue, newValue)) { continue; } details.add(buildQecDetail(null, QEC_HOUR_TABLE_NAME, stcd, column, @@ -6865,24 +6866,6 @@ public class EngEqDataServiceImpl implements EngEqDataService { batchInsertQecLogDetails(details); } - private boolean sameLogValue(Object oldValue, Object newValue) { - if (oldValue == null && newValue == null) { - return true; - } - if (oldValue == null || newValue == null) { - return false; - } - // BigDecimal/Double 等数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00) - if (oldValue instanceof Number && newValue instanceof Number) { - try { - return new BigDecimal(oldValue.toString()).compareTo(new BigDecimal(newValue.toString())) == 0; - } catch (NumberFormatException e) { - return false; - } - } - return StrUtil.equals(formatLogValue(oldValue), formatLogValue(newValue)); - } - @Override @Transactional(rollbackFor = Exception.class) public boolean removeKendoByIds(BatchDeleteAo batchDeleteAo) { @@ -7182,10 +7165,10 @@ public class EngEqDataServiceImpl implements EngEqDataService { detail.setStationCode(stationCode); detail.setFieldCode(fieldCode); detail.setFieldMeaning(QEC_FIELD_MEANING_MAP.getOrDefault(fieldCode, fieldCode)); - detail.setOldValueCode(formatLogValue(oldValueCode)); - detail.setOldValueName(formatLogValue(oldValueName)); - detail.setNewValueCode(formatLogValue(newValueCode)); - detail.setNewValueName(formatLogValue(newValueName)); + detail.setOldValueCode(LogValueUtils.formatLogValue(oldValueCode)); + detail.setOldValueName(LogValueUtils.formatLogValue(oldValueName)); + detail.setNewValueCode(LogValueUtils.formatLogValue(newValueCode)); + detail.setNewValueName(LogValueUtils.formatLogValue(newValueName)); detail.setRemark(remark); return detail; } @@ -7203,7 +7186,7 @@ public class EngEqDataServiceImpl implements EngEqDataService { } private Object resolveQecDisplayValue(String fieldCode, Object rawValue) { - String text = formatLogValue(rawValue); + String text = LogValueUtils.formatLogValue(rawValue); if (StrUtil.isBlank(text)) { return rawValue; } @@ -7232,16 +7215,6 @@ public class EngEqDataServiceImpl implements EngEqDataService { } } - private String formatLogValue(Object value) { - if (value == null) { - return null; - } - if (value instanceof Date date) { - return DateUtil.formatDateTime(date); - } - return String.valueOf(value); - } - private Integer parseInteger(Object value) { if (value == null) { return null; diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java index 35fab7a8..68b3afa7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java @@ -4,8 +4,10 @@ import com.yfd.platform.annotation.Log; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_base.entity.vo.BatchDeleteByIdsAo; import com.yfd.platform.qgc_env.fb.entity.vo.BreedStageVO; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcDeleteParam; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcOperateRequest; import com.yfd.platform.qgc_env.fb.service.FbStationService; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; @@ -214,6 +216,12 @@ public class FbStationController { return ResponseResult.successData(fbStationService.getFishRunQgcKendoListCust(dataSourceRequest)); } + @PostMapping("/fishrun/qgc/detail/GetKendoListCust") + @Operation(summary = "全过程鱼类放鱼数据明细(细粒度,按站点+年份+鱼种下钻)") + public ResponseResult getFishRunQgcDetailKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishRunQgcDetailKendoListCust(dataSourceRequest)); + } + @Log(module = "鱼类增殖站模块", value = "删除全过程鱼类放鱼数据") @PostMapping("/fishrun/qgc/removeFishRunQgcData") @Operation(summary = "删除全过程鱼类放鱼数据(按站点+年份+鱼种)") @@ -222,6 +230,25 @@ public class FbStationController { return ResponseResult.success(); } + @Log(module = "鱼类增殖站模块", value = "删除全过程鱼类放鱼明细数据") + @PostMapping("/fishrun/qgc/removeFishRunQgcDetailData") + @Operation(summary = "删除全过程鱼类放鱼明细数据(按明细ID批量)") + public ResponseResult removeFishRunQgcDetailData(@RequestBody BatchDeleteByIdsAo batchDeleteByIdsAo) { + fbStationService.removeFishRunQgcDetailData(batchDeleteByIdsAo); + return ResponseResult.success(); + } + + @Log(module = "鱼类增殖站模块", value = "修改全过程鱼类放鱼明细数据") + @PostMapping("/fishrun/qgc/updateFishRunQgcDetailData") + @Operation(summary = "修改全过程鱼类放鱼明细数据(按明细ID)") + public ResponseResult updateFishRunQgcDetailData(@RequestBody FbFishRunQgcOperateRequest operateRequest) { + fbStationService.updateFishRunQgcDetailData( + operateRequest == null ? null : operateRequest.getUpdateData(), + operateRequest == null ? null : operateRequest.getSource() + ); + return ResponseResult.success(); + } + @PostMapping("/fishType/GetKendoListCust") @Operation(summary = "增殖站鱼种完成情况统计") public ResponseResult getFishTypeKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcDetailVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcDetailVo.java new file mode 100644 index 00000000..11959b6f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcDetailVo.java @@ -0,0 +1,69 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 全过程鱼类放鱼数据明细(细粒度),对应 SD_RPIMNFISH_R 明细行。 + */ +@Data +@Schema(description = "全过程鱼类放鱼数据明细") +public class FbFishRunQgcDetailVo { + + @Schema(description = "明细ID (SD_RPIMNFISH_R.ID)") + private String id; + + @Schema(description = "放流主表ID (SD_ENGFR_R.ID)") + private String rpimnId; + + @Schema(description = "电站站码") + private String stcd; + + @Schema(description = "电站名称") + private String stnm; + + @Schema(description = "放流年份 yyyy") + private String flyr; + + @Schema(description = "鱼种类") + private String ftp; + + @Schema(description = "鱼种类名称") + private String ftpName; + + @Schema(description = "平均长度") + private BigDecimal fsZ; + + @Schema(description = "平均体重") + private BigDecimal fwet; + + @Schema(description = "计划放殖鱼数量") + private Long fcnt; + + @Schema(description = "标记类型") + private String tagte; + + @Schema(description = "标记数量") + private Long tagcnt; + + @Schema(description = "鱼龄") + private String fage; + + @Schema(description = "鱼类档案ID") + private String fid; + + @Schema(description = "保护等级") + private String protectlvl; + + @Schema(description = "亲本来源") + private String fa; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "放流时间") + private Date tm; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcOperateRequest.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcOperateRequest.java new file mode 100644 index 00000000..004f2a35 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcOperateRequest.java @@ -0,0 +1,24 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 全过程鱼类放鱼明细数据操作请求 + *

updateData: 修改数据,其中 id 为定位条件,其余 key 对应明细表可修改字段

+ */ +@Data +@Schema(description = "全过程鱼类放鱼明细数据操作请求") +public class FbFishRunQgcOperateRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "数据来源") + private String source; + + @Schema(description = "修改数据") + private Map updateData; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java index a4b5bc41..8896c6ed 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_env.fb.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_base.entity.vo.BatchDeleteByIdsAo; import com.yfd.platform.qgc_env.fb.entity.vo.*; import java.util.List; @@ -21,6 +22,13 @@ public interface FbStationService { DataSourceResult getFishRunQgcKendoListCust(DataSourceRequest dataSourceRequest); + /** + * 查询全过程鱼类放鱼数据明细(细粒度),用于聚合行下钻查看底层放鱼明细。 + * + * @param dataSourceRequest 支持 stcd/flyr/ftp 等过滤条件与分页排序 + */ + DataSourceResult getFishRunQgcDetailKendoListCust(DataSourceRequest dataSourceRequest); + /** * 批量删除全过程鱼类放鱼数据(按 站点+年份+鱼种 维度逻辑删除 SD_RPIMNFISH_R 明细) * @@ -28,6 +36,21 @@ public interface FbStationService { */ boolean removeFishRunQgcData(List params); + /** + * 按明细ID批量逻辑删除全过程鱼类放鱼数据,并记录操作日志。 + * + * @param batchDeleteByIdsAo 删除参数(ids + source) + */ + boolean removeFishRunQgcDetailData(BatchDeleteByIdsAo batchDeleteByIdsAo); + + /** + * 修改全过程鱼类放鱼明细数据(按明细ID定位),并记录操作日志。 + * + * @param updateData 修改数据,id 为定位条件,其余 key 为可修改字段 + * @param source 数据来源 + */ + boolean updateFishRunQgcDetailData(Map updateData, String source); + DataSourceResult getFishTypeKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getFishTypeStcdList(DataSourceRequest dataSourceRequest); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java index e49e5681..5309b35d 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java @@ -11,6 +11,8 @@ import com.yfd.platform.common.Group; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.common.exception.BizException; +import com.yfd.platform.qgc_base.entity.vo.BatchDeleteByIdsAo; import com.yfd.platform.qgc_env.fb.entity.vo.*; import com.yfd.platform.qgc_env.fb.service.FbStationService; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; @@ -18,6 +20,7 @@ import com.yfd.platform.qgc_base.domain.MsOperationLog; import com.yfd.platform.qgc_base.domain.MsOperationLogDetail; import com.yfd.platform.qgc_base.mapper.MsOperationLogDetailMapper; import com.yfd.platform.qgc_base.mapper.MsOperationLogMapper; +import com.yfd.platform.utils.LogValueUtils; import com.yfd.platform.utils.QgcQueryWrapperUtil; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; @@ -63,6 +66,25 @@ public class FbStationServiceImpl implements FbStationService { FISH_RUN_FIELD_MEANING_MAP.put("REMARK", "备注"); } + /** + * 全过程鱼类放鱼明细可修改字段映射(前端入参 key -> 数据库列) + */ + private static final LinkedHashMap FISH_RUN_UPDATE_COLUMN_MAP = new LinkedHashMap<>(); + + static { + FISH_RUN_UPDATE_COLUMN_MAP.put("ftp", "FTP"); + FISH_RUN_UPDATE_COLUMN_MAP.put("fsZ", "FSZ"); + FISH_RUN_UPDATE_COLUMN_MAP.put("fwet", "FWET"); + FISH_RUN_UPDATE_COLUMN_MAP.put("fcnt", "FCNT"); + FISH_RUN_UPDATE_COLUMN_MAP.put("tagte", "TAGTE"); + FISH_RUN_UPDATE_COLUMN_MAP.put("tagcnt", "TAGCNT"); + FISH_RUN_UPDATE_COLUMN_MAP.put("fage", "FAGE"); + FISH_RUN_UPDATE_COLUMN_MAP.put("fid", "FID"); + FISH_RUN_UPDATE_COLUMN_MAP.put("protectlvl", "PROTECTLVL"); + FISH_RUN_UPDATE_COLUMN_MAP.put("fa", "FA"); + FISH_RUN_UPDATE_COLUMN_MAP.put("remark", "REMARK"); + } + @Override public List getYearRpStatistics(String year) { Map paramMap = new HashMap<>(); @@ -195,6 +217,74 @@ public class FbStationServiceImpl implements FbStationService { return result; } + @Override + public DataSourceResult getFishRunQgcDetailKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String stcd = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + String flyr = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "flyr"); + String ftp = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "ftp"); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT det.ID AS id, ") + .append("run.ID AS rpimnId, ") + .append("run.STCD AS stcd, ") + .append("eng.ENNM AS stnm, ") + .append("TO_CHAR(run.TM, 'YYYY') AS flyr, ") + .append("det.FTP AS ftp, ") + .append(buildFishNameExpr("det.FTP")).append(" AS ftpName, ") + .append("det.FSZ AS fsZ, ") + .append("det.FWET AS fwet, ") + .append("det.FCNT AS fcnt, ") + .append("det.TAGTE AS tagte, ") + .append("det.TAGCNT AS tagcnt, ") + .append("det.FAGE AS fage, ") + .append("det.FID AS fid, ") + .append("det.PROTECTLVL AS protectlvl, ") + .append("det.FA AS fa, ") + .append("det.REMARK AS remark, ") + .append("run.TM AS tm ") + .append("FROM SD_RPIMNFISH_R det ") + .append("INNER JOIN SD_ENGFR_R run ON det.RPIMN_ID = run.ID AND NVL(run.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = run.STCD AND NVL(eng.IS_DELETED, 0) = 0 ") + .append(buildFishJoinSql("det.FTP", "eng.HBRVCD")) + .append("WHERE NVL(det.IS_DELETED, 0) = 0 "); + if (StrUtil.isNotBlank(stcd)) { + paramMap.put("detailStcd", stcd); + sql.append("AND run.STCD = #{map.detailStcd} "); + } + if (StrUtil.isNotBlank(flyr)) { + paramMap.put("detailFlyr", flyr); + sql.append("AND TO_CHAR(run.TM, 'YYYY') = #{map.detailFlyr} "); + } + if (StrUtil.isNotBlank(ftp)) { + paramMap.put("detailFtp", ftp); + sql.append("AND det.FTP = #{map.detailFtp} "); + } + String filterSql = buildFishRunQgcDetailFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishRunQgcDetailOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, + sql.toString(), + paramMap, + FbFishRunQgcDetailVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + @Override @Transactional(rollbackFor = Exception.class) public boolean removeFishRunQgcData(List params) { @@ -224,19 +314,167 @@ public class FbStationServiceImpl implements FbStationService { " AND TO_CHAR(run.TM, 'YYYY') = ? )", resolveLogOperator(), param.getFtp(), param.getStcd(), param.getFlyr() ); - recordFishRunDeleteLog(beforeRows, param, deleteCount); + recordFishRunDeleteLog(beforeRows, String.format("删除全过程鱼类放鱼数据(站点:%s,年份:%s,删除条数:%d)", + param.getStcd(), param.getFlyr(), deleteCount),null, deleteCount); } return true; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean removeFishRunQgcDetailData(BatchDeleteByIdsAo batchDeleteByIdsAo) { + List ids = batchDeleteByIdsAo.getIds(); + if (CollUtil.isEmpty(ids)) { + return true; + } + List distinctIds = ids.stream() + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + if (distinctIds.isEmpty()) { + return true; + } + // 1. 查询删除前的明细行(含站点等信息, 用于记录日志) + List> beforeRows = queryFishRunDetailRowsByIds(distinctIds); + // 2. 分批逻辑删除(ID IN, 防止超过数据库 IN 限制) + int deleteCount = 0; + for (List part : CollUtil.split(distinctIds, 500)) { + deleteCount += batchSoftDeleteFishRunDetail(part); + } + // 3. 记录删除日志(每条明细一条主日志, 子日志挂对应主日志) + recordFishRunDeleteLog(beforeRows, String.format("删除全过程鱼类放鱼数据(明细删除,删除条数:%d)", deleteCount),batchDeleteByIdsAo.getSource(), deleteCount); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateFishRunQgcDetailData(Map updateData, String source) { + if (updateData == null || updateData.isEmpty()) { + return true; + } + String id = asString(updateData.get("id")); + if (StrUtil.isBlank(id)) { + throw new BizException("明细ID不能为空"); + } + // 1. 查询修改前的明细行(用于记录日志) + Map beforeRow = querySingleFishRunDetailRow(id); + if (beforeRow == null || beforeRow.isEmpty()) { + throw new BizException("放鱼明细不存在或已被删除"); + } + // 2. 更新允许修改的字段 + updateFishRunDetailData(updateData, id); + // 3. 记录修改日志(只记录真正发生变化的字段) + recordFishRunUpdateLog(beforeRow, updateData, source); + return true; + } + + private Map querySingleFishRunDetailRow(String id) { + List> rows = queryFishRunDetailRowsByIds(Collections.singletonList(id)); + return rows.isEmpty() ? new HashMap<>() : rows.get(0); + } + + private void updateFishRunDetailData(Map updateData, String id) { + List setClauses = new ArrayList<>(); + List params = new ArrayList<>(); + for (Map.Entry entry : FISH_RUN_UPDATE_COLUMN_MAP.entrySet()) { + if (!updateData.containsKey(entry.getKey())) { + continue; + } + setClauses.add(entry.getValue() + " = ?"); + params.add(updateData.get(entry.getKey())); + } + if (setClauses.isEmpty()) { + return; + } + String sql = "UPDATE SD_RPIMNFISH_R SET " + String.join(", ", setClauses) + + " WHERE ID = ? AND NVL(IS_DELETED, 0) = 0"; + params.add(id); + int updated = jdbcTemplate.update(sql, params.toArray()); + if (updated == 0) { + throw new BizException("放鱼明细不存在或已被删除"); + } + } + + private void recordFishRunUpdateLog(Map beforeRow, + Map updateData, + String source) { + if (updateData == null || updateData.isEmpty()) { + return; + } + String recordId = asString(beforeRow.get("ID")); + String stcd = asString(beforeRow.get("STCD")); + List details = new ArrayList<>(); + for (Map.Entry entry : FISH_RUN_UPDATE_COLUMN_MAP.entrySet()) { + if (!updateData.containsKey(entry.getKey())) { + continue; + } + String column = entry.getValue(); + Object oldValue = beforeRow.get(column); + Object newValue = updateData.get(entry.getKey()); + // 数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00) + if (LogValueUtils.sameLogValue(oldValue, newValue)) { + continue; + } + MsOperationLogDetail detail = new MsOperationLogDetail(); + detail.setTableName(FISH_RUN_DETAIL_TABLE_NAME); + detail.setStationCode(stcd); + detail.setFieldCode(column); + detail.setFieldMeaning(FISH_RUN_FIELD_MEANING_MAP.getOrDefault(column, column)); + detail.setOldValueCode(formatFishRunLogValue(oldValue)); + detail.setOldValueName(formatFishRunLogValue(oldValue)); + detail.setNewValueCode(formatFishRunLogValue(newValue)); + detail.setNewValueName(formatFishRunLogValue(newValue)); + detail.setRemark("修改字段值"); + details.add(detail); + } + if (details.isEmpty()) { + return; + } + MsOperationLog mainLog = new MsOperationLog(); + mainLog.setOperator(resolveLogOperator()); + mainLog.setOperationType("02"); + mainLog.setRecordId(recordId); + mainLog.setOperateTime(new Date()); + mainLog.setTableName(FISH_RUN_DETAIL_TABLE_NAME); + mainLog.setSource(StrUtil.trimToNull(source)); + mainLog.setRemark("修改全过程鱼类放鱼数据(明细ID:" + recordId + ")"); + msOperationLogMapper.insert(mainLog); + for (MsOperationLogDetail detail : details) { + detail.setMainId(mainLog.getId()); + msOperationLogDetailMapper.insert(detail); + } + } + private List> queryFishRunDetailRows(FbFishRunQgcDeleteParam param) { - String sql = "SELECT det.ID, det.RPIMN_ID, det.FTP, det.FSZ, det.FWET, det.FCNT, " + + String sql = "SELECT det.ID, det.RPIMN_ID, run.STCD, det.FTP, det.FSZ, det.FWET, det.FCNT, " + "det.TAGTE, det.TAGCNT, det.FAGE, det.FID, det.PROTECTLVL, det.FA, det.REMARK " + "FROM SD_RPIMNFISH_R det " + "INNER JOIN SD_ENGFR_R run ON det.RPIMN_ID = run.ID AND NVL(run.IS_DELETED, 0) = 0 " + "WHERE NVL(det.IS_DELETED, 0) = 0 " + " AND det.FTP = ? AND run.STCD = ? AND TO_CHAR(run.TM, 'YYYY') = ?"; - List> rows = jdbcTemplate.queryForList(sql, param.getFtp(), param.getStcd(), param.getFlyr()); + return queryFishRunDetailRowsInternal(sql, param.getFtp(), param.getStcd(), param.getFlyr()); + } + + private List> queryFishRunDetailRowsByIds(List ids) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT det.ID, det.RPIMN_ID, run.STCD, det.FTP, det.FSZ, det.FWET, det.FCNT, ") + .append("det.TAGTE, det.TAGCNT, det.FAGE, det.FID, det.PROTECTLVL, det.FA, det.REMARK ") + .append("FROM SD_RPIMNFISH_R det ") + .append("INNER JOIN SD_ENGFR_R run ON det.RPIMN_ID = run.ID AND NVL(run.IS_DELETED, 0) = 0 ") + .append("WHERE NVL(det.IS_DELETED, 0) = 0 ") + .append(" AND det.ID IN ("); + for (int i = 0; i < ids.size(); i++) { + if (i > 0) { + sql.append(", "); + } + sql.append("?"); + } + sql.append(")"); + return queryFishRunDetailRowsInternal(sql.toString(), ids.toArray()); + } + + private List> queryFishRunDetailRowsInternal(String sql, Object... args) { + List> rows = jdbcTemplate.queryForList(sql, args); List> normalized = new ArrayList<>(); for (Map row : rows) { normalized.add(normalizeFishRunLogRow(row)); @@ -244,6 +482,28 @@ public class FbStationServiceImpl implements FbStationService { return normalized; } + private int batchSoftDeleteFishRunDetail(List ids) { + if (CollUtil.isEmpty(ids)) { + return 0; + } + StringBuilder sql = new StringBuilder(); + sql.append("UPDATE SD_RPIMNFISH_R det ") + .append("SET det.IS_DELETED = 1, det.DELETE_USER = ?, det.DELETE_TIME = SYSDATE ") + .append("WHERE NVL(det.IS_DELETED, 0) = 0 ") + .append(" AND det.ID IN ("); + List params = new ArrayList<>(); + params.add(resolveLogOperator()); + for (int i = 0; i < ids.size(); i++) { + if (i > 0) { + sql.append(", "); + } + sql.append("?"); + params.add(ids.get(i)); + } + sql.append(")"); + return jdbcTemplate.update(sql.toString(), params.toArray()); + } + private Map normalizeFishRunLogRow(Map row) { Map normalized = new LinkedHashMap<>(); if (row == null || row.isEmpty()) { @@ -255,19 +515,22 @@ public class FbStationServiceImpl implements FbStationService { return normalized; } - private void recordFishRunDeleteLog(List> rows, FbFishRunQgcDeleteParam param, int deleteCount) { + private void recordFishRunDeleteLog(List> rows, String remark,String source, int deleteCount) { if (CollUtil.isEmpty(rows)) { return; } List details = new ArrayList<>(); for (Map row : rows) { + String recordId = asString(row.get("ID")); + String stcd = asString(row.get("STCD")); MsOperationLog mainLog = new MsOperationLog(); mainLog.setOperator(resolveLogOperator()); - mainLog.setRecordId(String.valueOf(row.get("ID"))); + mainLog.setRecordId(recordId); mainLog.setOperateTime(new Date()); + mainLog.setSource(source); + mainLog.setOperationType("03"); mainLog.setTableName(FISH_RUN_DETAIL_TABLE_NAME); - mainLog.setRemark("删除全过程鱼类放鱼数据(站点:" + param.getStcd() - + ",年份:" + param.getFlyr() + ",删除条数:" + deleteCount + ")"); + mainLog.setRemark(remark); msOperationLogMapper.insert(mainLog); for (Map.Entry entry : row.entrySet()) { if (StrUtil.isBlank(entry.getKey()) || entry.getValue() == null) { @@ -276,7 +539,7 @@ public class FbStationServiceImpl implements FbStationService { MsOperationLogDetail detail = new MsOperationLogDetail(); detail.setMainId(mainLog.getId()); detail.setTableName(FISH_RUN_DETAIL_TABLE_NAME); - detail.setStationCode(param.getStcd()); + detail.setStationCode(stcd); detail.setFieldCode(entry.getKey()); detail.setFieldMeaning(FISH_RUN_FIELD_MEANING_MAP.getOrDefault(entry.getKey(), entry.getKey())); detail.setOldValueCode(formatFishRunLogValue(entry.getValue())); @@ -290,6 +553,10 @@ public class FbStationServiceImpl implements FbStationService { } } + private String asString(Object value) { + return value == null ? null : String.valueOf(value); + } + private String resolveLogOperator() { try { return SecurityUtils.getCurrentUsername(); @@ -1412,6 +1679,184 @@ public class FbStationServiceImpl implements FbStationService { return " ORDER BY " + String.join(", ", orderByParts); } + private String mapFishRunQgcDetailColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "id" -> "det.ID"; + case "rpimnId" -> "det.RPIMN_ID"; + case "stcd" -> "run.STCD"; + case "stnm" -> "eng.ENNM"; + case "flyr" -> "TO_CHAR(run.TM, 'YYYY')"; + case "ftp" -> "det.FTP"; + case "ftpName" -> buildFishNameExpr("det.FTP"); + case "fsZ" -> "det.FSZ"; + case "fwet" -> "det.FWET"; + case "fcnt" -> "det.FCNT"; + case "tagte" -> "det.TAGTE"; + case "tagcnt" -> "det.TAGCNT"; + case "fage" -> "det.FAGE"; + case "fid" -> "det.FID"; + case "protectlvl" -> "det.PROTECTLVL"; + case "fa" -> "det.FA"; + case "remark" -> "det.REMARK"; + case "tm" -> "run.TM"; + default -> null; + }; + } + + private Object normalizeFishRunQgcDetailValue(String field, Object value) { + if (value == null) { + return null; + } + if ("fcnt".equals(field) || "tagcnt".equals(field)) { + try { + return Long.parseLong(String.valueOf(value).trim()); + } catch (Exception ex) { + return null; + } + } + return value; + } + + private String buildFishRunQgcDetailFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildFishRunQgcDetailLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishRunQgcDetailFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) { + conditions.add("(" + childSql + ")"); + } + } + if (conditions.isEmpty()) { + return ""; + } + String logic = "or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND "; + return String.join(logic, conditions); + } + + private String buildFishRunQgcDetailLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + // 这三个字段已在主 SQL 中作为下钻条件处理,避免重复拼接 + if ("stcd".equalsIgnoreCase(filter.getField()) || "flyr".equalsIgnoreCase(filter.getField()) + || "ftp".equalsIgnoreCase(filter.getField())) { + return ""; + } + String column = mapFishRunQgcDetailColumn(filter.getField()); + if (StrUtil.isBlank(column)) { + return ""; + } + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + if ("isempty".equals(operator)) { + return "(" + column + " IS NULL OR " + column + " = '')"; + } + if ("isnotempty".equals(operator)) { + return "(" + column + " IS NOT NULL AND " + column + " <> '')"; + } + if ("in".equals(operator)) { + List values = normalizeQgcBsmfRValues(filter.getValue()); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object value : values) { + String key = "fishRunQgcDetailP" + indexHolder[0]++; + paramMap.put(key, normalizeFishRunQgcDetailValue(filter.getField(), value)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + Object value = filter.getValue(); + if (value == null) { + return ""; + } + String key = "fishRunQgcDetailP" + indexHolder[0]++; + Object normalizedValue = normalizeFishRunQgcDetailValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { + paramMap.put(key, normalizedValue); + yield column + " = #{map." + key + "}"; + } + case "neq" -> { + paramMap.put(key, normalizedValue); + yield column + " <> #{map." + key + "}"; + } + case "gt" -> { + paramMap.put(key, normalizedValue); + yield column + " > #{map." + key + "}"; + } + case "gte" -> { + paramMap.put(key, normalizedValue); + yield column + " >= #{map." + key + "}"; + } + case "lt" -> { + paramMap.put(key, normalizedValue); + yield column + " < #{map." + key + "}"; + } + case "lte" -> { + paramMap.put(key, normalizedValue); + yield column + " <= #{map." + key + "}"; + } + case "contains" -> { + paramMap.put(key, "%" + String.valueOf(normalizedValue).trim() + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "doesnotcontain" -> { + paramMap.put(key, "%" + String.valueOf(normalizedValue).trim() + "%"); + yield column + " NOT LIKE #{map." + key + "}"; + } + case "startswith" -> { + paramMap.put(key, String.valueOf(normalizedValue).trim() + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "endswith" -> { + paramMap.put(key, "%" + String.valueOf(normalizedValue).trim()); + yield column + " LIKE #{map." + key + "}"; + } + default -> ""; + }; + } + + private String buildFishRunQgcDetailOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY run.TM DESC, det.ID DESC"; + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapFishRunQgcDetailColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderByParts.add(column + " " + direction); + } + if (orderByParts.isEmpty()) { + return " ORDER BY run.TM DESC, det.ID DESC"; + } + return " ORDER BY " + String.join(", ", orderByParts); + } + private Date[] parseFishRunFlyrRange(String flyrFilter) { if (StrUtil.isBlank(flyrFilter)) { return new Date[]{null, null}; diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java index a330286c..713303a5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java @@ -4168,7 +4168,7 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { } String recordId = ObjectUtil.isNotEmpty(beforeHour) && ObjectUtil.isNotEmpty(beforeHour.get("ID")) ? beforeHour.get("ID").toString() - : null; MsOperationLog mainLog = buildWqMainLog(recordId,"修改水质小时数据", source); + : null; MsOperationLog mainLog = buildWqMainLog(recordId,"修改水质小时数据", source,"02"); msOperationLogMapper.insert(mainLog); for (MsOperationLogDetail detail : details) { detail.setMainId(mainLog.getId()); @@ -4184,7 +4184,7 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { for (Map row : rows) { String stcd = asString(row.get("STCD")); String recordId = asString(row.get("ID")); - MsOperationLog mainLog = buildWqMainLog(recordId,remark, source); + MsOperationLog mainLog = buildWqMainLog(recordId,remark, source,"03"); msOperationLogMapper.insert(mainLog); for (Map.Entry entry : row.entrySet()) { String fieldCode = entry.getKey(); @@ -4203,11 +4203,12 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { batchInsertWqLogDetails(details); } - private MsOperationLog buildWqMainLog(String recordId,String remark, String source) { + private MsOperationLog buildWqMainLog(String recordId,String remark, String source,String operatorType) { MsOperationLog log = new MsOperationLog(); log.setOperator(resolveLogOperator()); log.setRecordId(recordId); log.setOperateTime(new Date()); + log.setOperationType(operatorType); log.setTableName(WQ_HOUR_TABLE_NAME); log.setSource(StrUtil.trimToNull(source)); log.setRemark(remark); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/AlongDetailServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/AlongDetailServiceImpl.java index 13596442..48da18eb 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/AlongDetailServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/AlongDetailServiceImpl.java @@ -1189,7 +1189,7 @@ public class AlongDetailServiceImpl extends ServiceImpl row : rows) { String stcd = asString(row.get("STCD")); String recordId = asString(row.get("ID")); - MsOperationLog mainLog = buildWtMainLog(recordId,remark, source); + MsOperationLog mainLog = buildWtMainLog(recordId,remark, source,"03"); msOperationLogMapper.insert(mainLog); for (Map.Entry entry : row.entrySet()) { String fieldCode = entry.getKey(); @@ -1222,11 +1222,12 @@ public class AlongDetailServiceImpl extends ServiceImpl Map beforeRow = beforeRowMap.get(depthKey); Object oldValue = beforeRow == null ? null : beforeRow.get("VWT"); Object newValue = item.get("vwt"); - if (StringUtils.equals(formatWtvtLogValue(oldValue), formatWtvtLogValue(newValue))) { + if (LogValueUtils.sameLogValue(oldValue, newValue)) { return; } logDetails.add(buildWtvtDetail(null, stcd, "VWT", @@ -251,7 +252,7 @@ public class SdWtvtRServiceImpl extends ServiceImpl } batchUpdateWtvtRData(dataList); if (CollUtil.isNotEmpty(logDetails)) { - MsOperationLog mainLog = buildWtvtMainLog( stcd,"修改垂向水温数据", source); + MsOperationLog mainLog = buildWtvtMainLog( stcd,"修改垂向水温数据", source,"02"); msOperationLogMapper.insert(mainLog); for (MsOperationLogDetail detail : logDetails) { detail.setMainId(mainLog.getId()); @@ -437,12 +438,12 @@ public class SdWtvtRServiceImpl extends ServiceImpl List params = new ArrayList<>(); List conditions = new ArrayList<>(); for (DataParam item : dataParamList) { - if (item == null || StringUtils.isBlank(item.getId()) || StringUtils.isBlank(item.getDt())) { + if (item == null || StringUtils.isBlank(item.getId()) || StringUtils.isBlank(item.getTm())) { continue; } conditions.add("(STCD = ? AND TM = TO_DATE(SUBSTR(?, 1, 19), 'YYYY-MM-DD HH24:MI:SS'))"); params.add(item.getId()); - params.add(item.getDt()); + params.add(item.getTm()); } if (conditions.isEmpty()) { return new ArrayList<>(); @@ -486,12 +487,13 @@ public class SdWtvtRServiceImpl extends ServiceImpl if (CollUtil.isEmpty(rows)) { return; } - MsOperationLog mainLog = buildWtvtMainLog(null,remark, source); - msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); for (Map row : rows) { String stcd = asString(row.get("STCD")); +// String recordId = asString(row.get("ID")); Object depth = row.get("WTHG"); + MsOperationLog mainLog = buildWtvtMainLog(stcd, remark, source, "03"); + msOperationLogMapper.insert(mainLog); for (Map.Entry entry : row.entrySet()) { String fieldCode = entry.getKey(); Object oldValue = entry.getValue(); @@ -506,11 +508,12 @@ public class SdWtvtRServiceImpl extends ServiceImpl batchInsertWtvtLogDetails(details); } - private MsOperationLog buildWtvtMainLog(String recordId,String remark, String source) { + private MsOperationLog buildWtvtMainLog(String recordId,String remark, String source,String operationType) { MsOperationLog log = new MsOperationLog(); log.setOperator(resolveWtvtLogOperator()); log.setOperateTime(new Date()); log.setTableName(WT_CX_TABLE_NAME); + log.setOperationType(operationType); log.setRecordId(recordId); log.setSource(StringUtils.trimToNull(source)); log.setRemark(remark); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java index f956532c..b956e260 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java @@ -1806,7 +1806,7 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { if (details.isEmpty()) { return; } - MsOperationLog mainLog = buildRiverMainLog(recordId,"修改流量站小时数据", source); + MsOperationLog mainLog = buildRiverMainLog(recordId,"修改流量站小时数据", source,"02"); msOperationLogMapper.insert(mainLog); for (MsOperationLogDetail detail : details) { detail.setMainId(mainLog.getId()); @@ -1823,7 +1823,7 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { for (Map row : rows) { String stcd = asString(row.get("STCD")); String recordId = asString(row.get("ID")); - MsOperationLog mainLog = buildRiverMainLog(recordId,remark, source); + MsOperationLog mainLog = buildRiverMainLog(recordId,remark, source,"03"); msOperationLogMapper.insert(mainLog); for (Map.Entry entry : row.entrySet()) { String fieldCode = entry.getKey(); @@ -1839,9 +1839,10 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { batchInsertRiverLogDetails(details); } - private MsOperationLog buildRiverMainLog(String recordId,String remark, String source) { + private MsOperationLog buildRiverMainLog(String recordId,String remark, String source,String operationType) { MsOperationLog log = new MsOperationLog(); log.setOperator(resolveRiverLogOperator()); + log.setOperationType(operationType); log.setOperateTime(new Date()); log.setRecordId(recordId); log.setTableName(ZQ_HOUR_TABLE_NAME); diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java index e3d8497f..040e1dd0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java @@ -27,6 +27,7 @@ import com.yfd.platform.utils.ExportZipUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -362,8 +363,26 @@ public class QgcExportServiceImpl implements IQgcExportService { } if (rstcdToStcd.isEmpty()) { - log.warn("指标 {} 未找到 STTP={} 的测站数据", processor.getFieldName(), sttp); - return null; + // 无对应测站时仍生成 Sheet,仅保留日期列(无电站列),避免整表缺失 + log.warn("指标 {} 未找到 STTP={} 的测站数据,生成空 Sheet(仅日期列)", processor.getFieldName(), sttp); + List> emptyRows = new ArrayList<>(); + int daysInMonth = getDaysInMonth(mr); + for (int d = 1; d <= daysInMonth; d++) { + List row = new ArrayList<>(); + row.add(String.format("%02d", d)); + emptyRows.add(row); + } + List emptyLabels = processor.isVariation() + ? VARIATION_LABELS + : (processor.hasComplianceRate() + ? List.of("日均最低", "日均最高", "月内最低", "月内最低时间", "月内最高", "月内最高时间", "月均", LABEL_COMPLIANCE) + : BASE_LABELS); + for (String label : emptyLabels) { + List row = new ArrayList<>(); + row.add(label); + emptyRows.add(row); + } + return emptyRows; } List uniqueStcds = rstcdToStcd.values().stream().distinct().toList(); @@ -466,8 +485,16 @@ public class QgcExportServiceImpl implements IQgcExportService { } } if (rstcdToStcd.isEmpty()) { - log.warn("生态流量未找到 STTP=ENG 的测站数据"); - return null; + // 未找到电站时仍生成 Sheet,仅保留子标题行(无电站数据行) + log.warn("生态流量未找到 STTP=ENG 的测站数据,生成空 Sheet"); + List subHeaders = Arrays.asList("电站名称", "所属水电基地", "生态流量限值", "日达标率", "小时达标率", "备注"); + List subRow = new ArrayList<>(); + subRow.addAll(subHeaders); + subRow.addAll(subHeaders); + subRow.addAll(subHeaders); + List> rows = new ArrayList<>(); + rows.add(subRow); + return rows; } List uniqueStcds = rstcdToStcd.values().stream().distinct().toList(); @@ -773,8 +800,20 @@ public class QgcExportServiceImpl implements IQgcExportService { } } if (rstcdToStcd.isEmpty()) { - log.warn("水质达标率未找到 STTP=WQ 的测站数据"); - return null; + // 未找到测站时仍生成 Sheet,仅保留子标题行(无电站数据行) + log.warn("水质达标率未找到 STTP=WQ 的测站数据,生成空 Sheet"); + List subRow = new ArrayList<>(); + subRow.add(""); + subRow.add("达标数"); + subRow.add("总数"); + subRow.add("达标率(%)"); + subRow.add("达标数"); + subRow.add("总数"); + subRow.add("达标率(%)"); + subRow.add(""); + List> rows = new ArrayList<>(); + rows.add(subRow); + return rows; } List uniqueStcds = rstcdToStcd.values().stream().distinct().toList(); @@ -1007,13 +1046,13 @@ public class QgcExportServiceImpl implements IQgcExportService { private StationResolution resolveStations(List rstcds, Set sttpTypes) { StationResolution result = new StationResolution(); if (rstcds == null || rstcds.isEmpty()) return result; - - List mappings = stationMappingMapper.selectList( - new LambdaQueryWrapper() - .in(StationMapping::getRstcd, rstcds) - .or() - .in(StationMapping::getStcd, rstcds) - ); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.and(w -> { + if (CollectionUtils.isNotEmpty(rstcds)) { + w.in(StationMapping::getRstcd, rstcds).or().in(StationMapping::getStcd, rstcds); + } + }); + List mappings = stationMappingMapper.selectList(wrapper); // 添加逻辑:如果 sttp 是 "ENG",则 rstcd 赋值为 stcd mappings.forEach(mapping -> { @@ -1503,7 +1542,7 @@ public class QgcExportServiceImpl implements IQgcExportService { } // QEC 水电基地维度 - if (!baseIds.isEmpty()) { + if (hasQec && !baseIds.isEmpty()) { ExportZipUtil.SheetData baseEpaSheet = buildYearQecBaseSheet(baseIds, year, "QEC"); ExportZipUtil.SheetData baseMwrSheet = buildYearQecBaseSheet(baseIds, year, "MWR"); ExportZipUtil.SheetData baseAvqSheet = buildYearQecBaseSheet(baseIds, year, "AVQ"); @@ -2013,22 +2052,20 @@ public class QgcExportServiceImpl implements IQgcExportService { stcdDisplayMap.put(stcd, StrUtil.isNotBlank(name) ? name : stcd); } } - if (stcds.isEmpty()) continue; - List> rows; List headers; - if (processor instanceof RwtProcessor) { + if (stcds.isEmpty()) { + // 无测站:仍生成带表头的空 sheet,保证有标题可下载 + rows = Collections.emptyList(); + headers = buildRawHeaders(processor, stcdDisplayMap, Collections.emptyList()); + } else if (processor instanceof RwtProcessor) { rows = buildRwtRawSheetRows(stcds, stcdDisplayMap, startTime, endTime); + if (rows == null) rows = Collections.emptyList(); headers = Arrays.asList("测站", "时间", "水深(m)", "水温(℃)"); } else { rows = buildRawSheetRows(processor, stcds, stcdDisplayMap, startTime, endTime, isDaily); - if (rows == null) continue; - // 表头:时间 | 测站1 | 测站2 | ... - headers = new ArrayList<>(); - headers.add("时间"); - for (String stcd : stcds) { - headers.add(stcdDisplayMap.getOrDefault(stcd, stcd)); - } + if (rows == null) rows = Collections.emptyList(); + headers = buildRawHeaders(processor, stcdDisplayMap, stcds); } ExportZipUtil.SheetData sd = new ExportZipUtil.SheetData(processor.getSheetName(), headers, rows); @@ -2036,8 +2073,10 @@ public class QgcExportServiceImpl implements IQgcExportService { } if (sheets.isEmpty()) { - log.warn("hour/day 模式:没有可用数据"); - return; + // 无可用数据时也返回 Excel 空表,保证前端能下载到文件 + log.warn("hour/day 模式:没有可用数据,返回空表"); + sheets.add(new ExportZipUtil.SheetData("数据", + Collections.singletonList("无可用数据"), Collections.emptyList())); } // 5. ZIP 导出 @@ -2094,6 +2133,19 @@ public class QgcExportServiceImpl implements IQgcExportService { } } + /** + * 构建原始数据表头:时间 | 测站1 | 测站2 | ...(无测站时仅剩"时间"列) + */ + private List buildRawHeaders(ExportIndicatorProcessor processor, + Map stcdDisplayMap, List stcds) { + List headers = new ArrayList<>(); + headers.add("时间"); + for (String stcd : stcds) { + headers.add(stcdDisplayMap.getOrDefault(stcd, stcd)); + } + return headers; + } + /** * 构建 RWT 垂向水温原始数据行 * 格式:测站 | 时间 | 水深(m) | 水温(℃) diff --git a/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java b/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java index 47f1c8fa..167dd8b8 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java @@ -24,6 +24,7 @@ import com.yfd.platform.system.service.ISysLogService; import com.yfd.platform.system.service.IUserService; import com.yfd.platform.utils.RequestHolder; import com.yfd.platform.utils.RsaUtils; +import com.yfd.platform.utils.SecurityUtils; import com.yfd.platform.utils.StringUtils; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; @@ -479,7 +480,8 @@ public class LoginController { * 判断是否为超级管理员 */ private boolean checkIsSuperAdmin(String userId) { - String maxLevel = sysUserMapper.getMaxLevel(userId); + String tenantId = SecurityUtils.getTenantId(); + String maxLevel = sysUserMapper.getMaxLevel(userId,tenantId); return "1".equals(maxLevel); } diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java index dc4dda21..93ae9f61 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java @@ -18,6 +18,7 @@ import com.yfd.platform.system.service.ISysLogService; import com.yfd.platform.system.service.IUserService; import com.yfd.platform.utils.RequestHolder; import com.yfd.platform.utils.RsaUtils; +import com.yfd.platform.utils.SecurityUtils; import com.yfd.platform.utils.StringUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -579,7 +580,8 @@ public class SmsVerifyCodeController { * 判断用户是否为超级管理员(拥有LEVEL=1的角色) */ private boolean isSuperAdmin(String userId) { - String maxLevel = sysUserMapper.getMaxLevel(userId); + String tenantId = SecurityUtils.getTenantId(); + String maxLevel = sysUserMapper.getMaxLevel(userId,tenantId); return "1".equals(maxLevel); } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java index a9a45137..a9aa29a5 100644 --- a/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java +++ b/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java @@ -62,7 +62,7 @@ public interface SysUserMapper extends BaseMapper { * userid 用户id * 返回值说明: ************************************/ - String getMaxLevel(@Param("userId") String userId); + String getMaxLevel(@Param("userId") String userId,@Param("tenantId") String tenantId); /*********************************** * 用途说明:根据用户id删除所分配的角色 diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java index 0afe0e2c..c3001500 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java @@ -75,7 +75,8 @@ public class AdminAuthServiceImpl implements IAdminAuthService { @Override public boolean isCurrentManagedAdmin() { // SysUser sysUser = userMapper.selectById(SecurityUtils.getUserId()); - String maxLevel = userMapper.getMaxLevel(SecurityUtils.getUserId()); + String tenantId = SecurityUtils.getTenantId(); + String maxLevel = userMapper.getMaxLevel(SecurityUtils.getUserId(),tenantId); boolean adminRole = "1".equals(maxLevel); String currentUsername = SecurityUtils.getCurrentUsername(); return adminRole||isManagedAdminUsername(currentUsername); diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java index 45735c42..5aac70d8 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java @@ -11,6 +11,7 @@ import com.yfd.platform.system.mapper.SysUserMapper; import com.yfd.platform.system.mapper.SysUserTenantMapper; import com.yfd.platform.system.service.IAdminAuthService; import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.SecurityUtils; import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -105,7 +106,8 @@ public class UserDetailsServiceImpl implements UserDetailsService { * 判断用户是否为超级管理员(拥有LEVEL=1的角色) */ private boolean isSuperAdmin(String userId) { - String maxLevel = sysUserMapper.getMaxLevel(userId); + String tenantId = SecurityUtils.getTenantId(); + String maxLevel = sysUserMapper.getMaxLevel(userId,tenantId); return "1".equals(maxLevel); } } diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java index 64e6220e..b41ed881 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java @@ -543,7 +543,8 @@ public class UserServiceImpl extends ServiceImpl impleme return true; } String currentUserId = SecurityUtils.getUserId(); - String level = sysUserMapper.getMaxLevel(currentUserId); + String tenantId = SecurityUtils.getTenantId(); + String level = sysUserMapper.getMaxLevel(currentUserId, tenantId); Integer roleLevel = parseRoleLevel(level); return roleLevel != null && roleLevel <= 2; } @@ -1030,7 +1031,8 @@ public class UserServiceImpl extends ServiceImpl impleme public boolean isSuperAdmin(String userId) { - String maxLevel = sysUserMapper.getMaxLevel(userId); + String tenantId = SecurityUtils.getTenantId(); + String maxLevel = sysUserMapper.getMaxLevel(userId,tenantId); SysUser sysUser = sysUserMapper.selectById(userId); return (sysUser!=null && sysUser.getUsertype()==0) || "1".equals(maxLevel); } diff --git a/backend/src/main/java/com/yfd/platform/utils/ExportZipUtil.java b/backend/src/main/java/com/yfd/platform/utils/ExportZipUtil.java index 6670a6af..b11940e8 100644 --- a/backend/src/main/java/com/yfd/platform/utils/ExportZipUtil.java +++ b/backend/src/main/java/com/yfd/platform/utils/ExportZipUtil.java @@ -486,10 +486,10 @@ public class ExportZipUtil { // ---- 自动调整列宽 ---- autoSizeColumns(sheet, colCount); - // 冻结表头行 + // 冻结表头行(含多层表头:headerRowCount 表示除首行外的子表头,总冻结行数 = 1 + headerRowCount) if (currentRow > 1) { - int freezeRows = 1 + sheetData.getHeaderRowCount(); - sheet.createFreezePane(0, Math.min(freezeRows, currentRow - 1)); + int freezeRows = Math.min(1 + sheetData.getHeaderRowCount(), currentRow); + sheet.createFreezePane(0, freezeRows); } } diff --git a/backend/src/main/java/com/yfd/platform/utils/JasyptUtil.java b/backend/src/main/java/com/yfd/platform/utils/JasyptUtil.java new file mode 100644 index 00000000..5eaeaaf7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/JasyptUtil.java @@ -0,0 +1,64 @@ +package com.yfd.platform.utils; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig; + +/** + * Jasypt 加密解密工具 + * 配置:算法 PBEWithMD5AndTripleDES,密钥 dec,迭代次数 1000(默认) + */ +public class JasyptUtil { + + private static final String PASSWORD = "dec"; + // 建议使用标准名称(Java 内部识别),原写法也兼容但推荐此写法 + private static final String ALGORITHM = "PBEWithMD5AndTripleDES"; + private static final int ITERATIONS = 1000; + + private static StandardPBEStringEncryptor getEncryptor() { + StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); + EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig(); + config.setPassword(PASSWORD); + config.setAlgorithm(ALGORITHM); + config.setKeyObtentionIterations(ITERATIONS); + encryptor.setConfig(config); + return encryptor; + } + + /** + * 加密明文 + * @param plainText 明文 + * @return 密文(Base64 字符串) + */ + public static String encrypt(String plainText) { + StandardPBEStringEncryptor encryptor = getEncryptor(); + return encryptor.encrypt(plainText); + } + + /** + * 解密密文 + * @param cipherText 密文(Base64 字符串) + * @return 明文 + * @throws org.jasypt.exceptions.EncryptionOperationNotPossibleException 如果解密失败(密钥/算法不匹配) + */ + public static String decrypt(String cipherText) { + StandardPBEStringEncryptor encryptor = getEncryptor(); + return encryptor.decrypt(cipherText); + } + + // 测试入口 + public static void main(String[] args) { + // 1. 加密测试 + String plain = "Lcj@5678."; + String encrypted = encrypt(plain); + System.out.println("加密后的密文:" + encrypted); + + // 2. 解密测试(用你自己的密文) + String yourCipher = "w8ffsaU1GBbVeJ5wnBeCwoUDcadbD2oL"; + try { + String decrypted = decrypt(yourCipher); + System.out.println("解密结果:" + decrypted); + } catch (Exception e) { + System.err.println("解密失败,请检查密钥或算法是否正确:" + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/utils/LogValueUtils.java b/backend/src/main/java/com/yfd/platform/utils/LogValueUtils.java new file mode 100644 index 00000000..cd97a15f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/LogValueUtils.java @@ -0,0 +1,67 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 日志值比较与格式化工具类,供各业务模块(生态流量、垂向水温等)操作日志记录时共用。 + * + * @author supeng + */ +public class LogValueUtils { + + private LogValueUtils() { + } + + /** + * 比较新旧两个值是否相同,用于判断字段值是否发生变化。 + *

+ * 规则: + *

    + *
  • 两者都为 null 视为相同
  • + *
  • 一者为 null 视为不同
  • + *
  • BigDecimal/Double 等数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00)
  • + *
  • 其余类型按 {@link #formatLogValue(Object)} 格式化后字符串比较
  • + *
+ * + * @param oldValue 旧值 + * @param newValue 新值 + * @return true 表示相同 + */ + public static boolean sameLogValue(Object oldValue, Object newValue) { + if (oldValue == null && newValue == null) { + return true; + } + if (oldValue == null || newValue == null) { + return false; + } + // BigDecimal/Double 等数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00) + if (oldValue instanceof Number && newValue instanceof Number) { + try { + return new BigDecimal(oldValue.toString()).compareTo(new BigDecimal(newValue.toString())) == 0; + } catch (NumberFormatException e) { + return false; + } + } + return StrUtil.equals(formatLogValue(oldValue), formatLogValue(newValue)); + } + + /** + * 格式化日志中的值:Date 格式化为日期时间字符串,其余直接转字符串。 + * + * @param value 原始值 + * @return 格式化后的字符串 + */ + public static String formatLogValue(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date date) { + return DateUtil.formatDateTime(date); + } + return String.valueOf(value); + } +} diff --git a/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java b/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java index 8c90a931..6f07d336 100644 --- a/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java +++ b/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java @@ -15,11 +15,13 @@ */ package com.yfd.platform.utils; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.yfd.platform.exception.BadRequestException; import com.yfd.platform.system.domain.LoginUser; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; @@ -27,6 +29,8 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import java.util.Collection; import java.util.List; @@ -38,7 +42,7 @@ import java.util.List; */ @Slf4j public class SecurityUtils { - + private static final String TENANT_HEADER = "Tenant_Id"; /** * 获取当前登录的用户 * @return UserDetails @@ -142,4 +146,18 @@ public class SecurityUtils { LoginUser loginUser = (LoginUser) authentication.getPrincipal(); return loginUser.getAccessibleTenantIds(); } + + /** + * 从当前请求头中获取租户ID + * @return 租户ID,未获取到返回 null + */ + public static String getTenantId() { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes == null) { + return null; + } + HttpServletRequest request = attributes.getRequest(); + String tenantId = request.getHeader(TENANT_HEADER); + return StrUtil.isNotBlank(tenantId) ? tenantId : null; + } } diff --git a/backend/src/main/resources/mapper/system/SysUserMapper.xml b/backend/src/main/resources/mapper/system/SysUserMapper.xml index 8ba9cf6e..df58edcf 100644 --- a/backend/src/main/resources/mapper/system/SysUserMapper.xml +++ b/backend/src/main/resources/mapper/system/SysUserMapper.xml @@ -53,7 +53,7 @@ FROM ( SELECT r."LEVEL" AS level_col FROM sys_role r - WHERE r.id IN (SELECT roleid FROM sys_role_users WHERE userid = #{userId}) + WHERE TENANT_ID = #{tenantId} AND r.id IN (SELECT roleid FROM sys_role_users WHERE userid = #{userId}) ) t