liuaini 4 долоо хоног өмнө
parent
commit
b269e8d1e9
35 өөрчлөгдсөн 1964 нэмэгдсэн , 0 устгасан
  1. 33 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/controller/recon/ReconResultController.java
  2. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconFeederResultMapper.java
  3. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconMetricsMapper.java
  4. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconResultMapper.java
  5. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconSwitchOpMapper.java
  6. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconSwitchResultDetailMapper.java
  7. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconSwitchSeqMapper.java
  8. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconVolDetailMapper.java
  9. 10 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconVolResultMapper.java
  10. 58 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconFeederResult.java
  11. 131 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconMetrics.java
  12. 64 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconResult.java
  13. 48 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconSwitchOp.java
  14. 61 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconSwitchResultDetail.java
  15. 80 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconSwitchSeq.java
  16. 58 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconVolDetail.java
  17. 82 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconVolResult.java
  18. 33 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/BreakerOpDTO.java
  19. 47 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/FeederFlowDTO.java
  20. 20 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/FeederResultDTO.java
  21. 20 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/FinalCheckDTO.java
  22. 20 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/HotCheckDTO.java
  23. 171 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/MetricsDTO.java
  24. 60 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/RawResultDTO.java
  25. 19 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/ReconApiResponseDTO.java
  26. 27 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/ScoreItemsDTO.java
  27. 20 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/SwitchOpDTO.java
  28. 63 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/SwitchResultDTO.java
  29. 50 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/SwitchSeqDTO.java
  30. 30 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/VolExtremesDTO.java
  31. 92 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/VolResultDTO.java
  32. 18 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/vo/ReconstructionParam.java
  33. 17 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/service/recon/ReconResultService.java
  34. 487 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/service/recon/impl/ReconResultServiceImpl.java
  35. 75 0
      services/load-transfer-si/src/main/java/com/hdkj/lt/si/service/remote/impl/ReconRemoteCallServiceImpl.java

+ 33 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/controller/recon/ReconResultController.java

@@ -0,0 +1,33 @@
+package com.hdkj.lt.si.controller.recon;
+
+
+import com.hdkj.lt.si.modle.vo.ReconstructionParam;
+import com.hdkj.lt.si.service.recon.ReconResultService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 重构结果入库接口.
+ * 接收算法服务响应 (含 raw_result), 取 raw_result 入库.
+ */
+@RestController
+@RequestMapping("/recon")
+public class ReconResultController {
+
+    @Autowired
+    private ReconResultService reconResultService;
+
+    @PostMapping("/trigger")
+    public String triggerReconfiguration(@RequestBody ReconstructionParam param) {
+        try {
+            reconResultService.saveReconResult(param.getEventId(),param.getFeederIds());
+            return "重构数据拉取并入库成功!";
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "重构数据处理失败: " + e.getMessage();
+        }
+    }
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconFeederResultMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconFeederResult;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 馈线结果 Mapper */
+@Mapper
+public interface FhzgReconFeederResultMapper extends BaseMapper<FhzgReconFeederResult> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconMetricsMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconMetrics;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 运行指标 Mapper */
+@Mapper
+public interface FhzgReconMetricsMapper extends BaseMapper<FhzgReconMetrics> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconResultMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconResult;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 重构结果主表 Mapper */
+@Mapper
+public interface FhzgReconResultMapper extends BaseMapper<FhzgReconResult> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconSwitchOpMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconSwitchOp;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 开关操作-简化版 Mapper */
+@Mapper
+public interface FhzgReconSwitchOpMapper extends BaseMapper<FhzgReconSwitchOp> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconSwitchResultDetailMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconSwitchResultDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 开关状态-详情 Mapper */
+@Mapper
+public interface FhzgReconSwitchResultDetailMapper extends BaseMapper<FhzgReconSwitchResultDetail> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconSwitchSeqMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconSwitchSeq;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 开关操作序列-详细 Mapper */
+@Mapper
+public interface FhzgReconSwitchSeqMapper extends BaseMapper<FhzgReconSwitchSeq> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconVolDetailMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconVolDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 电压结果-节点详情 Mapper */
+@Mapper
+public interface FhzgReconVolDetailMapper extends BaseMapper<FhzgReconVolDetail> {
+}

+ 10 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/dao/FhzgReconVolResultMapper.java

@@ -0,0 +1,10 @@
+package com.hdkj.lt.si.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hdkj.lt.si.modle.FhzgReconVolResult;
+import org.apache.ibatis.annotations.Mapper;
+
+/** 电压结果-汇总 Mapper */
+@Mapper
+public interface FhzgReconVolResultMapper extends BaseMapper<FhzgReconVolResult> {
+}

+ 58 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconFeederResult.java

@@ -0,0 +1,58 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 馈线结果 (feederResult.listFeeder, 1:N, 已天然 before/after 扁平)
+ * 对应表: fhzg_recon_feeder_result
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_feeder_result")
+public class FhzgReconFeederResult {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID */
+    private Long resultId;
+
+    /** 馈线序号(数组下标) */
+    private Integer feederNo;
+
+    /** 资源ID(psrID) */
+    private String psrId;
+
+    /** 资源类型(psrType) */
+    private String psrType;
+
+    /** 馈线名称 */
+    private String name;
+
+    /** 额定电流(A) */
+    private BigDecimal rateCurrent;
+
+    /** 重构前电流(A) */
+    private BigDecimal beforeI;
+
+    /** 重构后电流(A) */
+    private BigDecimal afterI;
+
+    /** 重构前日间电流(A) */
+    private BigDecimal beforeDayI;
+
+    /** 重构后日间电流(A) */
+    private BigDecimal afterDayI;
+
+    /** 重构前线损率 */
+    private BigDecimal lineLossBefore;
+
+    /** 重构后线损率 */
+    private BigDecimal lineLossAfter;
+}

+ 131 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconMetrics.java

@@ -0,0 +1,131 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 运行指标 (metrics, 1:1, metrics_before/after 扁平 + delta/judgement 用JSON)
+ * health.weights / health.components 同构固定键, 一并扁平
+ * 对应表: fhzg_recon_metrics
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_metrics")
+public class FhzgReconMetrics {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID */
+    private Long resultId;
+
+    // ---- loading 线路负载 ----
+    private Integer beforeLoadingLineCount;
+    private Integer beforeLoadingExtGridCount;
+    private BigDecimal beforeLoadingMaxLoadingPct;
+    private BigDecimal beforeLoadingAvgLoadingPct;
+    private Integer beforeLoadingOverLimitCount;
+    private BigDecimal beforeLoadingLoadingLimitPct;
+    private String beforeLoadingUnit;
+    private String beforeLoadingLoadingSource;
+    private BigDecimal beforeLoadingLineMaxLoadingPct;
+    private BigDecimal beforeLoadingLineAvgLoadingPct;
+    private Integer beforeLoadingLineOverLimitCount;
+
+    private Integer afterLoadingLineCount;
+    private Integer afterLoadingExtGridCount;
+    private BigDecimal afterLoadingMaxLoadingPct;
+    private BigDecimal afterLoadingAvgLoadingPct;
+    private Integer afterLoadingOverLimitCount;
+    private BigDecimal afterLoadingLoadingLimitPct;
+    private String afterLoadingUnit;
+    private String afterLoadingLoadingSource;
+    private BigDecimal afterLoadingLineMaxLoadingPct;
+    private BigDecimal afterLoadingLineAvgLoadingPct;
+    private Integer afterLoadingLineOverLimitCount;
+
+    // ---- loss 损耗 ----
+    private BigDecimal beforeLossLineLossMw;
+    private BigDecimal beforeLossTrafoLossMw;
+    private BigDecimal beforeLossTotalLossMw;
+    private String beforeLossUnit;
+
+    private BigDecimal afterLossLineLossMw;
+    private BigDecimal afterLossTrafoLossMw;
+    private BigDecimal afterLossTotalLossMw;
+    private String afterLossUnit;
+
+    // ---- voltage 电压 ----
+    private BigDecimal beforeVoltageMinVmPu;
+    private BigDecimal beforeVoltageMaxVmPu;
+    private BigDecimal beforeVoltageAvgVmPu;
+    private Integer beforeVoltageUnderVminCount;
+    private Integer beforeVoltageOverVmaxCount;
+    private BigDecimal beforeVoltageVminPu;
+    private BigDecimal beforeVoltageVmaxPu;
+    private Integer beforeVoltageBusCount;
+    private String beforeVoltageUnit;
+
+    private BigDecimal afterVoltageMinVmPu;
+    private BigDecimal afterVoltageMaxVmPu;
+    private BigDecimal afterVoltageAvgVmPu;
+    private Integer afterVoltageUnderVminCount;
+    private Integer afterVoltageOverVmaxCount;
+    private BigDecimal afterVoltageVminPu;
+    private BigDecimal afterVoltageVmaxPu;
+    private Integer afterVoltageBusCount;
+    private String afterVoltageUnit;
+
+    // ---- reliability 可靠性(预留) ----
+    private Boolean beforeReliabilityEnabled;
+    private String beforeReliabilityComment;
+    private Boolean afterReliabilityEnabled;
+    private String afterReliabilityComment;
+
+    // ---- health 健康度 (score/base/penalty + weights[5] + components[5]) ----
+    private BigDecimal beforeHealthScore;
+    private BigDecimal beforeHealthBase;
+    private BigDecimal beforeHealthPenalty;
+    private BigDecimal beforeHealthWeightSafe;
+    private BigDecimal beforeHealthWeightVoltage;
+    private BigDecimal beforeHealthWeightLoss;
+    private BigDecimal beforeHealthWeightBalance;
+    private BigDecimal beforeHealthWeightRisk;
+    private BigDecimal beforeHealthComponentSafe;
+    private BigDecimal beforeHealthComponentVoltage;
+    private BigDecimal beforeHealthComponentLoss;
+    private BigDecimal beforeHealthComponentBalance;
+    private BigDecimal beforeHealthComponentRisk;
+
+    private BigDecimal afterHealthScore;
+    private BigDecimal afterHealthBase;
+    private BigDecimal afterHealthPenalty;
+    private BigDecimal afterHealthWeightSafe;
+    private BigDecimal afterHealthWeightVoltage;
+    private BigDecimal afterHealthWeightLoss;
+    private BigDecimal afterHealthWeightBalance;
+    private BigDecimal afterHealthWeightRisk;
+    private BigDecimal afterHealthComponentSafe;
+    private BigDecimal afterHealthComponentVoltage;
+    private BigDecimal afterHealthComponentLoss;
+    private BigDecimal afterHealthComponentBalance;
+    private BigDecimal afterHealthComponentRisk;
+
+    // ---- 动态键/差值/评判 用 JSON ----
+    /** 重构前各馈线负载率(键为馈线ID) */
+    private String beforeFeederLoadRatesPct;
+
+    /** 重构后各馈线负载率(键为馈线ID) */
+    private String afterFeederLoadRatesPct;
+
+    /** 指标差值对比(delta 整块) */
+    private String delta;
+
+    /** 智能评判(judgement 整块) */
+    private String judgement;
+}

+ 64 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconResult.java

@@ -0,0 +1,64 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 重构结果主表 (L0 标量 + 折叠 finalCheck / switchResult.summary)
+ * 对应表: fhzg_recon_result
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_result")
+public class FhzgReconResult {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 事件ID */
+    private String eventId;
+
+    /** 馈线组ID */
+    private Integer groupId;
+
+    /** 馈线组SID */
+    private String groupSid;
+
+    /** 馈线组名称 */
+    private String groupName;
+
+    /** 区域ID */
+    private Integer stAreaId;
+
+    /** 计算状态 */
+    private String status;
+
+    /** 重构算法 */
+    private String algo;
+
+    /** 计算代号(computeResult) */
+    private String computeResult;
+
+    /** 详细描述(detalDesc) */
+    private String detalDesc;
+
+    /** 安全校核是否安全(finalCheck.is_safe) */
+    private Boolean finalCheckIsSafe;
+
+    /** 安全校核详情(finalCheck.details) */
+    private String finalCheckDetails;
+
+    /** 开关状态概要(switchResult.summary) */
+    private String switchResultSummary;
+
+    /** 快照时间(运行时间) */
+    private LocalDateTime snapshotTime;
+
+    /** 入库时间 */
+    private LocalDateTime createTime;
+}

+ 48 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconSwitchOp.java

@@ -0,0 +1,48 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 开关操作-简化版 (SwitchOp.listSwitchOp, 1:N)
+ * 对应表: fhzg_recon_switch_op
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_switch_op")
+public class FhzgReconSwitchOp {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID(fhzg_recon_result.id) */
+    private Long resultId;
+
+    /** 操作序号(数组下标) */
+    private Integer opNo;
+
+    /** 开关ID */
+    private String breakerId;
+
+    /** 开关名称 */
+    private String name;
+
+    /** 资源类型编码(psrType) */
+    private String psrType;
+
+    /** 自动类型(autoType) */
+    private String autoType;
+
+    /** 操作类型(operation_type, close/open) */
+    private String operationType;
+
+    /** 操作时间(ISO8601); timestamp 为 MySQL 保留字, 需反引号 */
+    @TableField("`timestamp`")
+    private LocalDateTime timestamp;
+}

+ 61 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconSwitchResultDetail.java

@@ -0,0 +1,61 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 开关状态-详情 (switchResult.details, 1:N; summary 折叠进主表)
+ * 对应表: fhzg_recon_switch_result_detail
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_switch_result_detail")
+public class FhzgReconSwitchResultDetail {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID */
+    private Long resultId;
+
+    /** 详情序号(数组下标) */
+    private Integer detailNo;
+
+    /** 开关ID(psrID) */
+    private String psrId;
+
+    /** 资源类型编码(psrType) */
+    private String psrType;
+
+    /** 节点1 rdfID */
+    private String node1Rdfid;
+
+    /** 节点2 rdfID */
+    private String node2Rdfid;
+
+    /** 自动类型(autoType) */
+    private String autoType;
+
+    /** 重构前状态(open/close) */
+    private String beforeState;
+
+    /** 重构前电流(A) */
+    private BigDecimal beforeI;
+
+    /** 重构前电压(kV) */
+    private BigDecimal beforeU;
+
+    /** 重构后状态(open/close) */
+    private String afterState;
+
+    /** 重构后电流(A) */
+    private BigDecimal afterI;
+
+    /** 重构后电压(kV) */
+    private BigDecimal afterU;
+}

+ 80 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconSwitchSeq.java

@@ -0,0 +1,80 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 开关操作序列-详细 (switchSeq.listSwitchOp, 1:N, 成对同构扁平)
+ * closeBreakerOp/openBreakerOp -> close_/open_
+ * feederIn/feederOut           -> feeder_in_/feeder_out_
+ * hotCheck                     -> hot_check_
+ * 对应表: fhzg_recon_switch_seq
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_switch_seq")
+public class FhzgReconSwitchSeq {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID */
+    private Long resultId;
+
+    /** 序列序号(数组下标) */
+    private Integer seqNo;
+
+    // ---- 合闸开关 closeBreakerOp ----
+    private String closeBreakerId;
+    private String closeName;
+    private String closePsrType;
+    private String closeAutoType;
+    private String closeOperationType;
+    private LocalDateTime closeTimestamp;
+
+    // ---- 分闸开关 openBreakerOp ----
+    private String openBreakerId;
+    private String openName;
+    private String openPsrType;
+    private String openAutoType;
+    private String openOperationType;
+    private LocalDateTime openTimestamp;
+
+    // ---- 热校核 hotCheck ----
+    private Boolean hotCheckIsSafe;
+    private String hotCheckReason;
+
+    // ---- 流入馈线 feederIn ----
+    private String feederInPsrId;
+    private String feederInPsrType;
+    private String feederInName;
+    private BigDecimal feederInRateCurrent;
+    private BigDecimal feederInBeforeI;
+    private BigDecimal feederInAfterI;
+    private BigDecimal feederInBeforeDayI;
+    private BigDecimal feederInAfterDayI;
+    private BigDecimal feederInLineLossBefore;
+    private BigDecimal feederInLineLossAfter;
+
+    // ---- 流出馈线 feederOut ----
+    private String feederOutPsrId;
+    private String feederOutPsrType;
+    private String feederOutName;
+    private BigDecimal feederOutRateCurrent;
+    private BigDecimal feederOutBeforeI;
+    private BigDecimal feederOutAfterI;
+    private BigDecimal feederOutBeforeDayI;
+    private BigDecimal feederOutAfterDayI;
+    private BigDecimal feederOutLineLossBefore;
+    private BigDecimal feederOutLineLossAfter;
+
+    // ---- 变化量 ----
+    private BigDecimal deltaLoss;
+    private BigDecimal deltaI;
+}

+ 58 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconVolDetail.java

@@ -0,0 +1,58 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 电压结果-节点详情 (volResult.details, 1:N)
+ * 对应表: fhzg_recon_vol_detail
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_vol_detail")
+public class FhzgReconVolDetail {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID */
+    private Long resultId;
+
+    /** 详情序号(数组下标) */
+    private Integer detailNo;
+
+    /** 负荷ID(psrID) */
+    private String psrId;
+
+    /** 资源类型编码(psrType) */
+    private String psrType;
+
+    /** 负荷名称 */
+    private String name;
+
+    /** 负荷类型(loadType) */
+    private String loadType;
+
+    /** 重构前中压侧电压(kV) */
+    private BigDecimal beforeU;
+
+    /** 重构后中压侧电压(kV) */
+    private BigDecimal afterU;
+
+    /** 重构前低压侧电压(kV) */
+    private BigDecimal beforeUlv;
+
+    /** 重构后低压侧电压(kV) */
+    private BigDecimal afterUlv;
+
+    /** 重构前有功(kW) */
+    private BigDecimal beforeP;
+
+    /** 重构前无功(kvar) */
+    private BigDecimal beforeQ;
+}

+ 82 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/FhzgReconVolResult.java

@@ -0,0 +1,82 @@
+package com.hdkj.lt.si.modle;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 电压结果-汇总 (volResult.summary, 1:1, before/after 扁平, 越限列表用JSON)
+ * 对应表: fhzg_recon_vol_result
+ */
+@Data
+@NoArgsConstructor
+@TableName("fhzg_recon_vol_result")
+public class FhzgReconVolResult {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 主表ID */
+    private Long resultId;
+
+    /** 电压限值说明(_comment) */
+    private String commentText;
+
+    /** 电压上限(pu) */
+    private BigDecimal vlimitMax;
+
+    /** 电压下限(pu) */
+    private BigDecimal vlimitMin;
+
+    /** 总负荷数 */
+    private Integer loadCount;
+
+    /** 中压配变数 */
+    private Integer loadCountMvtran;
+
+    /** 中压用户数 */
+    private Integer loadCountMvcustomer;
+
+    /** 中压光伏数 */
+    private Integer loadCountMvpv;
+
+    /** 重构前最高电压(kV) */
+    private BigDecimal beforeMaxVol;
+
+    /** 重构前越上限次数 */
+    private Integer beforeOverMaxCount;
+
+    /** 重构前越上限列表(JSON数组) */
+    private String beforeOverMaxList;
+
+    /** 重构前最低电压(kV) */
+    private BigDecimal beforeMinVol;
+
+    /** 重构前越下限次数 */
+    private Integer beforeUnderMinCount;
+
+    /** 重构前越下限列表(JSON数组) */
+    private String beforeUnderMinList;
+
+    /** 重构后最高电压(kV) */
+    private BigDecimal afterMaxVol;
+
+    /** 重构后越上限次数 */
+    private Integer afterOverMaxCount;
+
+    /** 重构后越上限列表(JSON数组) */
+    private String afterOverMaxList;
+
+    /** 重构后最低电压(kV) */
+    private BigDecimal afterMinVol;
+
+    /** 重构后越下限次数 */
+    private Integer afterUnderMinCount;
+
+    /** 重构后越下限列表(JSON数组) */
+    private String afterUnderMinList;
+}

+ 33 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/BreakerOpDTO.java

@@ -0,0 +1,33 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 开关操作信息 (复用于 SwitchOp.listSwitchOp / switchSeq 的 closeBreakerOp / openBreakerOp).
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BreakerOpDTO {
+
+    @JsonProperty("breaker_id")
+    private String breakerId;
+
+    private String name;
+
+    @JsonProperty("psrType")
+    private String psrType;
+
+    @JsonProperty("autoType")
+    private String autoType;
+
+    @JsonProperty("operation_type")
+    private String operationType;
+
+    /** ISO8601 字符串, 如 2026-07-13T16:12:40+08:00 */
+    @JsonProperty("timestamp")
+    private String timestamp;
+}

+ 47 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/FeederFlowDTO.java

@@ -0,0 +1,47 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 馈线电气量 (复用于 switchSeq.feederIn / feederOut / feederResult.listFeeder).
+ * 字段已天然 before/after 扁平.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FeederFlowDTO {
+
+    @JsonProperty("psrID")
+    private String psrId;
+
+    @JsonProperty("psrType")
+    private String psrType;
+
+    private String name;
+
+    @JsonProperty("rateCurrent")
+    private BigDecimal rateCurrent;
+
+    @JsonProperty("beforeI")
+    private BigDecimal beforeI;
+
+    @JsonProperty("afterI")
+    private BigDecimal afterI;
+
+    @JsonProperty("beforeDayI")
+    private BigDecimal beforeDayI;
+
+    @JsonProperty("afterDayI")
+    private BigDecimal afterDayI;
+
+    @JsonProperty("lineLossBefore")
+    private BigDecimal lineLossBefore;
+
+    @JsonProperty("lineLossAfter")
+    private BigDecimal lineLossAfter;
+}

+ 20 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/FeederResultDTO.java

@@ -0,0 +1,20 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 馈线结果 (feederResult).
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FeederResultDTO {
+
+    @JsonProperty("listFeeder")
+    private List<FeederFlowDTO> listFeeder;
+}

+ 20 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/FinalCheckDTO.java

@@ -0,0 +1,20 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 安全校核 (finalCheck). 折叠进主表.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FinalCheckDTO {
+
+    @JsonProperty("is_safe")
+    private Boolean safe;
+
+    private String details;
+}

+ 20 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/HotCheckDTO.java

@@ -0,0 +1,20 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 热校核 (switchSeq.listSwitchOp[].hotCheck).
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class HotCheckDTO {
+
+    @JsonProperty("is_safe")
+    private Boolean safe;
+
+    private String reason;
+}

+ 171 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/MetricsDTO.java

@@ -0,0 +1,171 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 运行指标 (metrics).
+ * metrics_before / metrics_after 同构, 展开为实体扁平列;
+ * feeder_load_rates_pct (动态键) 及 delta / judgement 整块用 JsonNode 承载, 入库为 JSON 列.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class MetricsDTO {
+
+    @JsonProperty("metrics_before")
+    private MetricsBeforeAfterDTO metricsBefore;
+
+    @JsonProperty("metrics_after")
+    private MetricsBeforeAfterDTO metricsAfter;
+
+    @JsonProperty("delta")
+    private JsonNode delta;
+
+    @JsonProperty("judgement")
+    private JsonNode judgement;
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class MetricsBeforeAfterDTO {
+
+        @JsonProperty("loading")
+        private LoadingDTO loading;
+
+        @JsonProperty("loss")
+        private LossDTO loss;
+
+        @JsonProperty("voltage")
+        private VoltageDTO voltage;
+
+        @JsonProperty("reliability")
+        private ReliabilityDTO reliability;
+
+        @JsonProperty("health")
+        private HealthDTO health;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class LoadingDTO {
+
+        @JsonProperty("line_count")
+        private Integer lineCount;
+
+        @JsonProperty("ext_grid_count")
+        private Integer extGridCount;
+
+        @JsonProperty("max_loading_pct")
+        private BigDecimal maxLoadingPct;
+
+        @JsonProperty("avg_loading_pct")
+        private BigDecimal avgLoadingPct;
+
+        @JsonProperty("over_limit_count")
+        private Integer overLimitCount;
+
+        @JsonProperty("loading_limit_pct")
+        private BigDecimal loadingLimitPct;
+
+        private String unit;
+
+        @JsonProperty("loading_source")
+        private String loadingSource;
+
+        /** 动态键: 键为馈线ID, 值为负载率 */
+        @JsonProperty("feeder_load_rates_pct")
+        private JsonNode feederLoadRatesPct;
+
+        @JsonProperty("line_max_loading_pct")
+        private BigDecimal lineMaxLoadingPct;
+
+        @JsonProperty("line_avg_loading_pct")
+        private BigDecimal lineAvgLoadingPct;
+
+        @JsonProperty("line_over_limit_count")
+        private Integer lineOverLimitCount;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class LossDTO {
+
+        @JsonProperty("line_loss_mw")
+        private BigDecimal lineLossMw;
+
+        @JsonProperty("trafo_loss_mw")
+        private BigDecimal trafoLossMw;
+
+        @JsonProperty("total_loss_mw")
+        private BigDecimal totalLossMw;
+
+        private String unit;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class VoltageDTO {
+
+        @JsonProperty("min_vm_pu")
+        private BigDecimal minVmPu;
+
+        @JsonProperty("max_vm_pu")
+        private BigDecimal maxVmPu;
+
+        @JsonProperty("avg_vm_pu")
+        private BigDecimal avgVmPu;
+
+        @JsonProperty("under_vmin_count")
+        private Integer underVminCount;
+
+        @JsonProperty("over_vmax_count")
+        private Integer overVmaxCount;
+
+        @JsonProperty("vmin_pu")
+        private BigDecimal vminPu;
+
+        @JsonProperty("vmax_pu")
+        private BigDecimal vmaxPu;
+
+        @JsonProperty("bus_count")
+        private Integer busCount;
+
+        private String unit;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class ReliabilityDTO {
+
+        private Boolean enabled;
+
+        @JsonProperty("_comment")
+        private String comment;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class HealthDTO {
+
+        private BigDecimal score;
+
+        private BigDecimal base;
+
+        private BigDecimal penalty;
+
+        private ScoreItemsDTO weights;
+
+        private ScoreItemsDTO components;
+    }
+}

+ 60 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/RawResultDTO.java

@@ -0,0 +1,60 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 算法响应 raw_result 对象 (字段表 L0 全部字段).
+ * 注意: 源 JSON 命名混合 snake_case / camelCase / PascalCase, 逐字段用 @JsonProperty 对齐.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class RawResultDTO {
+
+    @JsonProperty("group_id")
+    private Integer groupId;
+
+    @JsonProperty("group_sid")
+    private String groupSid;
+
+    @JsonProperty("group_name")
+    private String groupName;
+
+    @JsonProperty("st_area_id")
+    private Integer stAreaId;
+
+    private String status;
+
+    private String algo;
+
+    @JsonProperty("computeResult")
+    private String computeResult;
+
+    @JsonProperty("detalDesc")
+    private String detalDesc;
+
+    /** 开关操作(简化版), 源 JSON 为 PascalCase "SwitchOp" */
+    @JsonProperty("SwitchOp")
+    private SwitchOpDTO switchOp;
+
+    @JsonProperty("switchSeq")
+    private SwitchSeqDTO switchSeq;
+
+    @JsonProperty("feederResult")
+    private FeederResultDTO feederResult;
+
+    @JsonProperty("volResult")
+    private VolResultDTO volResult;
+
+    @JsonProperty("switchResult")
+    private SwitchResultDTO switchResult;
+
+    @JsonProperty("finalCheck")
+    private FinalCheckDTO finalCheck;
+
+    @JsonProperty("metrics")
+    private MetricsDTO metrics;
+}

+ 19 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/ReconApiResponseDTO.java

@@ -0,0 +1,19 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 算法服务响应外层包装.
+ * 入库只取 raw_result, 其余字段忽略.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ReconApiResponseDTO {
+
+    @JsonProperty("raw_result")
+    private RawResultDTO rawResult;
+}

+ 27 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/ScoreItemsDTO.java

@@ -0,0 +1,27 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 评分项集合 (复用于 health.weights / health.components).
+ * 固定键: safe / voltage / loss / balance / risk
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ScoreItemsDTO {
+
+    private BigDecimal safe;
+
+    private BigDecimal voltage;
+
+    private BigDecimal loss;
+
+    private BigDecimal balance;
+
+    private BigDecimal risk;
+}

+ 20 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/SwitchOpDTO.java

@@ -0,0 +1,20 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 开关操作-简化版 (SwitchOp).
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SwitchOpDTO {
+
+    @JsonProperty("listSwitchOp")
+    private List<BreakerOpDTO> listSwitchOp;
+}

+ 63 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/SwitchResultDTO.java

@@ -0,0 +1,63 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 开关状态 (switchResult): summary 字符串(折叠进主表) + details 详情(1:N).
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SwitchResultDTO {
+
+    @JsonProperty("summary")
+    private String summary;
+
+    @JsonProperty("details")
+    private List<SwitchResultDetailDTO> details;
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class SwitchResultDetailDTO {
+
+        @JsonProperty("psrID")
+        private String psrId;
+
+        @JsonProperty("psrType")
+        private String psrType;
+
+        @JsonProperty("node1_rdfid")
+        private String node1Rdfid;
+
+        @JsonProperty("node2_rdfid")
+        private String node2Rdfid;
+
+        @JsonProperty("autoType")
+        private String autoType;
+
+        @JsonProperty("before_state")
+        private String beforeState;
+
+        @JsonProperty("before_I")
+        private BigDecimal beforeI;
+
+        @JsonProperty("before_U")
+        private BigDecimal beforeU;
+
+        @JsonProperty("after_state")
+        private String afterState;
+
+        @JsonProperty("after_I")
+        private BigDecimal afterI;
+
+        @JsonProperty("after_U")
+        private BigDecimal afterU;
+    }
+}

+ 50 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/SwitchSeqDTO.java

@@ -0,0 +1,50 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 开关操作序列-详细 (switchSeq).
+ * 每条 listSwitchOp 含: 合闸closeBreakerOp / 分闸openBreakerOp / 热校核hotCheck /
+ * 流入馈线feederIn / 流出馈线feederOut / 线损变化deltaLoss / 电流变化deltaI.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SwitchSeqDTO {
+
+    @JsonProperty("listSwitchOp")
+    private List<SwitchSeqItemDTO> listSwitchOp;
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class SwitchSeqItemDTO {
+
+        @JsonProperty("closeBreakerOp")
+        private BreakerOpDTO closeBreakerOp;
+
+        @JsonProperty("openBreakerOp")
+        private BreakerOpDTO openBreakerOp;
+
+        @JsonProperty("hotCheck")
+        private HotCheckDTO hotCheck;
+
+        @JsonProperty("feederIn")
+        private FeederFlowDTO feederIn;
+
+        @JsonProperty("feederOut")
+        private FeederFlowDTO feederOut;
+
+        @JsonProperty("deltaLoss")
+        private BigDecimal deltaLoss;
+
+        @JsonProperty("deltaI")
+        private BigDecimal deltaI;
+    }
+}

+ 30 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/VolExtremesDTO.java

@@ -0,0 +1,30 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 电压极值 (复用于 volResult.summary.before / after).
+ * 越限列表 overMaxList / underMinList 为数组, 用 JsonNode 承载, 入库时序列化为 JSON 字符串.
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class VolExtremesDTO {
+
+    private BigDecimal maxVol;
+
+    private Integer overMaxCount;
+
+    private JsonNode overMaxList;
+
+    private BigDecimal minVol;
+
+    private Integer underMinCount;
+
+    private JsonNode underMinList;
+}

+ 92 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/dto/VolResultDTO.java

@@ -0,0 +1,92 @@
+package com.hdkj.lt.si.modle.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 电压结果 (volResult): summary 汇总(1:1) + details 节点详情(1:N).
+ */
+@Data
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class VolResultDTO {
+
+    @JsonProperty("summary")
+    private VolSummaryDTO summary;
+
+    @JsonProperty("details")
+    private List<VolDetailDTO> details;
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class VolSummaryDTO {
+
+        @JsonProperty("_comment")
+        private String comment;
+
+        @JsonProperty("vlimit_max")
+        private BigDecimal vlimitMax;
+
+        @JsonProperty("vlimit_min")
+        private BigDecimal vlimitMin;
+
+        @JsonProperty("load_count")
+        private Integer loadCount;
+
+        @JsonProperty("load_count_mvtran")
+        private Integer loadCountMvtran;
+
+        @JsonProperty("load_count_mvcustomer")
+        private Integer loadCountMvcustomer;
+
+        @JsonProperty("load_count_mvpv")
+        private Integer loadCountMvpv;
+
+        @JsonProperty("before")
+        private VolExtremesDTO before;
+
+        @JsonProperty("after")
+        private VolExtremesDTO after;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class VolDetailDTO {
+
+        @JsonProperty("psrID")
+        private String psrId;
+
+        @JsonProperty("psrType")
+        private String psrType;
+
+        private String name;
+
+        @JsonProperty("loadType")
+        private String loadType;
+
+        @JsonProperty("before_U")
+        private BigDecimal beforeU;
+
+        @JsonProperty("after_U")
+        private BigDecimal afterU;
+
+        @JsonProperty("before_Ulv")
+        private BigDecimal beforeUlv;
+
+        @JsonProperty("after_Ulv")
+        private BigDecimal afterUlv;
+
+        @JsonProperty("before_P")
+        private BigDecimal beforeP;
+
+        @JsonProperty("before_Q")
+        private BigDecimal beforeQ;
+    }
+}

+ 18 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/modle/vo/ReconstructionParam.java

@@ -0,0 +1,18 @@
+package com.hdkj.lt.si.modle.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @Description:
+ * @Author: LiuAini
+ * @Date 2026/7/17 11:13
+ */
+@Data
+public class ReconstructionParam {
+    //事件id
+    private String eventId;
+    //线路ids
+    private List<String> feederIds;
+}

+ 17 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/service/recon/ReconResultService.java

@@ -0,0 +1,17 @@
+package com.hdkj.lt.si.service.recon;
+
+
+import java.util.List;
+
+/**
+ * 重构结果入库服务.
+ * 将算法响应 raw_result 整棵树写入数据库 (主表 + 7 子表), 单事务.
+ */
+public interface ReconResultService {
+
+    /**
+     * @param eventId 事件id
+     * @param feederIds 问题线路id
+     */
+    void saveReconResult(String eventId, List<String> feederIds);
+}

+ 487 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/service/recon/impl/ReconResultServiceImpl.java

@@ -0,0 +1,487 @@
+package com.hdkj.lt.si.service.recon.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.hdkj.lt.base.exception.ExceptionCast;
+import com.hdkj.lt.core.bizms.modle.dto.StateEstimation;
+import com.hdkj.lt.core.rest.service.IRemoteCallService;
+import com.hdkj.lt.si.dao.*;
+import com.hdkj.lt.si.modle.dto.*;
+import com.hdkj.lt.si.service.recon.ReconResultService;
+import com.hdkj.lt.si.service.remote.impl.PsrDeviceRemoteCallServiceImpl;
+import com.hdkj.lt.si.service.remote.impl.ReconRemoteCallServiceImpl;
+import lombok.RequiredArgsConstructor;
+import com.hdkj.lt.si.modle.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StreamUtils;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.function.BiConsumer;
+
+/**
+ * 重构结果入库服务实现.
+ * 单事务写入主表 + 7 子表, 任一失败全部回滚.
+ * 类型转换: String(ISO8601)->LocalDateTime, JsonNode->JSON串, Boolean->tinyint(由MP处理), 数值->BigDecimal.
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ReconResultServiceImpl implements ReconResultService {
+
+    private final FhzgReconResultMapper resultMapper;
+    private final FhzgReconSwitchOpMapper switchOpMapper;
+    private final FhzgReconSwitchSeqMapper switchSeqMapper;
+    private final FhzgReconFeederResultMapper feederResultMapper;
+    private final FhzgReconVolResultMapper volResultMapper;
+    private final FhzgReconVolDetailMapper volDetailMapper;
+    private final FhzgReconSwitchResultDetailMapper switchResultDetailMapper;
+    private final FhzgReconMetricsMapper metricsMapper;
+
+    private static final ObjectMapper OM = new ObjectMapper()
+            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    private static final DateTimeFormatter ISO_OFFSET = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+
+    private static final String REQUEST_URI = "http://25.91.249.219:18080/api/reconfiguration";
+    @Resource(type = ReconRemoteCallServiceImpl.class)
+    private IRemoteCallService remoteCallService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void saveReconResult(String eventId, List<String> feederIds) {
+        // 1. 构造请求参数
+        Map<String, Object> params = new HashMap<>();
+//        params.put("county_sid", "honghu");
+//        params.put("county_name", "honghu");
+//        params.put("problem_feeder_ids", Collections.singletonList("15DKX-20690"));
+        String str;
+        RawResultDTO dto;
+        try {
+            Object data = remoteCallService.call(REQUEST_URI, HttpMethod.POST, params, Object.class);
+            str = JSON.toJSONString(data);
+
+        // 3. 解析返回的JSON数据
+//        try {
+//            str = StreamUtils.copyToString(new ClassPathResource("1.txt").getInputStream(), StandardCharsets.UTF_8);
+//        } catch (IOException e) {
+//            throw new RuntimeException("读取算法响应样本 1.txt 失败", e);
+//        }
+
+            JsonNode root = OM.readTree(str);
+            log.info("重构方案接口返回参数{}",str);
+            JsonNode rawNode = root.get("raw_result");
+            if (rawNode == null || rawNode.isNull()) {
+                throw new RuntimeException("算法响应缺少 raw_result 节点");
+            }
+            dto = OM.treeToValue(rawNode, RawResultDTO.class);
+        } catch (IOException e) {
+            throw new RuntimeException("解析算法响应 raw_result 失败", e);
+        }
+        Map<String, Integer> counts = new LinkedHashMap<>();
+
+        // 1. 主表
+        FhzgReconResult main = buildMain(dto);
+        main.setEventId(eventId);
+        resultMapper.insert(main);
+        Long resultId = main.getId();
+        counts.put("result", 1);
+
+        // 2. 开关操作(简化版)
+        if (dto.getSwitchOp() != null) {
+            int n = insertSwitchOp(dto.getSwitchOp(), resultId);
+            counts.put("switch_op", n);
+        }
+
+        // 3. 开关操作序列(详细)
+        if (dto.getSwitchSeq() != null) {
+            int n = insertSwitchSeq(dto.getSwitchSeq(), resultId);
+            counts.put("switch_seq", n);
+        }
+
+        // 4. 馈线结果
+        if (dto.getFeederResult() != null) {
+            int n = insertFeederResult(dto.getFeederResult(), resultId);
+            counts.put("feeder_result", n);
+        }
+
+        // 5. 电压结果(汇总+详情)
+        if (dto.getVolResult() != null) {
+            if (dto.getVolResult().getSummary() != null) {
+                volResultMapper.insert(buildVolResult(dto.getVolResult().getSummary(), resultId));
+                counts.put("vol_result", 1);
+            }
+            if (dto.getVolResult().getDetails() != null) {
+                int n = insertVolDetail(dto.getVolResult().getDetails(), resultId);
+                counts.put("vol_detail", n);
+            }
+        }
+
+        // 6. 开关状态详情(summary 折叠进主表)
+        if (dto.getSwitchResult() != null && dto.getSwitchResult().getDetails() != null) {
+            int n = insertSwitchResultDetail(dto.getSwitchResult().getDetails(), resultId);
+            counts.put("switch_result_detail", n);
+        }
+
+        // 7. 运行指标
+        if (dto.getMetrics() != null) {
+            metricsMapper.insert(buildMetrics(dto.getMetrics(), resultId));
+            counts.put("metrics", 1);
+        }
+        log.info("重构方案counts:{}", JSON.toJSONString(counts));
+    }
+
+    // ==================== 构建主表 ====================
+
+    private FhzgReconResult buildMain(RawResultDTO dto) {
+        FhzgReconResult m = new FhzgReconResult();
+        m.setGroupId(dto.getGroupId());
+        m.setGroupSid(dto.getGroupSid());
+        m.setGroupName(dto.getGroupName());
+        m.setStAreaId(dto.getStAreaId());
+        m.setStatus(dto.getStatus());
+        m.setAlgo(dto.getAlgo());
+        m.setComputeResult(dto.getComputeResult());
+        m.setDetalDesc(dto.getDetalDesc());
+
+        // finalCheck 折叠
+        if (dto.getFinalCheck() != null) {
+            m.setFinalCheckIsSafe(dto.getFinalCheck().getSafe());
+            m.setFinalCheckDetails(dto.getFinalCheck().getDetails());
+        }
+        // switchResult.summary 折叠
+        if (dto.getSwitchResult() != null) {
+            m.setSwitchResultSummary(dto.getSwitchResult().getSummary());
+        }
+        m.setSnapshotTime(LocalDateTime.now());
+        return m;
+    }
+
+    // ==================== 开关操作(简化版) ====================
+
+    private int insertSwitchOp(SwitchOpDTO dto, Long resultId) {
+        if (dto.getListSwitchOp() == null) return 0;
+        int i = 1;
+        for (BreakerOpDTO b : dto.getListSwitchOp()) {
+            FhzgReconSwitchOp e = new FhzgReconSwitchOp();
+            e.setResultId(resultId);
+            e.setOpNo(i++);
+            e.setBreakerId(b.getBreakerId());
+            e.setName(b.getName());
+            e.setPsrType(b.getPsrType());
+            e.setAutoType(b.getAutoType());
+            e.setOperationType(b.getOperationType());
+            e.setTimestamp(parseTime(b.getTimestamp()));
+            switchOpMapper.insert(e);
+        }
+        return i;
+    }
+
+    // ==================== 开关操作序列(详细, 扁平) ====================
+
+    private int insertSwitchSeq(SwitchSeqDTO dto, Long resultId) {
+        if (dto.getListSwitchOp() == null) return 0;
+        int i = 0;
+        for (SwitchSeqDTO.SwitchSeqItemDTO it : dto.getListSwitchOp()) {
+            FhzgReconSwitchSeq e = new FhzgReconSwitchSeq();
+            e.setResultId(resultId);
+            e.setSeqNo(i++);
+            fillBreaker(e, it.getCloseBreakerOp(), true);
+            fillBreaker(e, it.getOpenBreakerOp(), false);
+            HotCheckDTO h = it.getHotCheck();
+            if (h != null) {
+                e.setHotCheckIsSafe(h.getSafe());
+                e.setHotCheckReason(h.getReason());
+            }
+            fillFeeder(e, it.getFeederIn(), true);
+            fillFeeder(e, it.getFeederOut(), false);
+            e.setDeltaLoss(it.getDeltaLoss());
+            e.setDeltaI(it.getDeltaI());
+            switchSeqMapper.insert(e);
+        }
+        return i;
+    }
+
+    private void fillBreaker(FhzgReconSwitchSeq e, BreakerOpDTO b, boolean close) {
+        if (b == null) return;
+        if (close) {
+            e.setCloseBreakerId(b.getBreakerId());
+            e.setCloseName(b.getName());
+            e.setClosePsrType(b.getPsrType());
+            e.setCloseAutoType(b.getAutoType());
+            e.setCloseOperationType(b.getOperationType());
+            e.setCloseTimestamp(parseTime(b.getTimestamp()));
+        } else {
+            e.setOpenBreakerId(b.getBreakerId());
+            e.setOpenName(b.getName());
+            e.setOpenPsrType(b.getPsrType());
+            e.setOpenAutoType(b.getAutoType());
+            e.setOpenOperationType(b.getOperationType());
+            e.setOpenTimestamp(parseTime(b.getTimestamp()));
+        }
+    }
+
+    private void fillFeeder(FhzgReconSwitchSeq e, FeederFlowDTO f, boolean in) {
+        if (f == null) return;
+        if (in) {
+            e.setFeederInPsrId(f.getPsrId());
+            e.setFeederInPsrType(f.getPsrType());
+            e.setFeederInName(f.getName());
+            e.setFeederInRateCurrent(f.getRateCurrent());
+            e.setFeederInBeforeI(f.getBeforeI());
+            e.setFeederInAfterI(f.getAfterI());
+            e.setFeederInBeforeDayI(f.getBeforeDayI());
+            e.setFeederInAfterDayI(f.getAfterDayI());
+            e.setFeederInLineLossBefore(f.getLineLossBefore());
+            e.setFeederInLineLossAfter(f.getLineLossAfter());
+        } else {
+            e.setFeederOutPsrId(f.getPsrId());
+            e.setFeederOutPsrType(f.getPsrType());
+            e.setFeederOutName(f.getName());
+            e.setFeederOutRateCurrent(f.getRateCurrent());
+            e.setFeederOutBeforeI(f.getBeforeI());
+            e.setFeederOutAfterI(f.getAfterI());
+            e.setFeederOutBeforeDayI(f.getBeforeDayI());
+            e.setFeederOutAfterDayI(f.getAfterDayI());
+            e.setFeederOutLineLossBefore(f.getLineLossBefore());
+            e.setFeederOutLineLossAfter(f.getLineLossAfter());
+        }
+    }
+
+    // ==================== 馈线结果 ====================
+
+    private int insertFeederResult(FeederResultDTO dto, Long resultId) {
+        if (dto.getListFeeder() == null) return 0;
+        int i = 1;
+        for (FeederFlowDTO f : dto.getListFeeder()) {
+            FhzgReconFeederResult e = new FhzgReconFeederResult();
+            e.setResultId(resultId);
+            e.setFeederNo(i++);
+            e.setPsrId(f.getPsrId());
+            e.setPsrType(f.getPsrType());
+            e.setName(f.getName());
+            e.setRateCurrent(f.getRateCurrent());
+            e.setBeforeI(f.getBeforeI());
+            e.setAfterI(f.getAfterI());
+            e.setBeforeDayI(f.getBeforeDayI());
+            e.setAfterDayI(f.getAfterDayI());
+            e.setLineLossBefore(f.getLineLossBefore());
+            e.setLineLossAfter(f.getLineLossAfter());
+            feederResultMapper.insert(e);
+        }
+        return i;
+    }
+
+    // ==================== 电压结果-汇总 ====================
+
+    private FhzgReconVolResult buildVolResult(VolResultDTO.VolSummaryDTO s, Long resultId) {
+        FhzgReconVolResult e = new FhzgReconVolResult();
+        e.setResultId(resultId);
+        e.setCommentText(s.getComment());
+        e.setVlimitMax(s.getVlimitMax());
+        e.setVlimitMin(s.getVlimitMin());
+        e.setLoadCount(s.getLoadCount());
+        e.setLoadCountMvtran(s.getLoadCountMvtran());
+        e.setLoadCountMvcustomer(s.getLoadCountMvcustomer());
+        e.setLoadCountMvpv(s.getLoadCountMvpv());
+        fillVolExtremes(e, s.getBefore(), true);
+        fillVolExtremes(e, s.getAfter(), false);
+        return e;
+    }
+
+    private void fillVolExtremes(FhzgReconVolResult e, VolExtremesDTO x, boolean before) {
+        if (x == null) return;
+        if (before) {
+            e.setBeforeMaxVol(x.getMaxVol());
+            e.setBeforeOverMaxCount(x.getOverMaxCount());
+            e.setBeforeOverMaxList(toJson(x.getOverMaxList()));
+            e.setBeforeMinVol(x.getMinVol());
+            e.setBeforeUnderMinCount(x.getUnderMinCount());
+            e.setBeforeUnderMinList(toJson(x.getUnderMinList()));
+        } else {
+            e.setAfterMaxVol(x.getMaxVol());
+            e.setAfterOverMaxCount(x.getOverMaxCount());
+            e.setAfterOverMaxList(toJson(x.getOverMaxList()));
+            e.setAfterMinVol(x.getMinVol());
+            e.setAfterUnderMinCount(x.getUnderMinCount());
+            e.setAfterUnderMinList(toJson(x.getUnderMinList()));
+        }
+    }
+
+    // ==================== 电压结果-节点详情 ====================
+
+    private int insertVolDetail(List<VolResultDTO.VolDetailDTO> details, Long resultId) {
+        int i = 1;
+        for (VolResultDTO.VolDetailDTO d : details) {
+            FhzgReconVolDetail e = new FhzgReconVolDetail();
+            e.setResultId(resultId);
+            e.setDetailNo(i++);
+            e.setPsrId(d.getPsrId());
+            e.setPsrType(d.getPsrType());
+            e.setName(d.getName());
+            e.setLoadType(d.getLoadType());
+            e.setBeforeU(d.getBeforeU());
+            e.setAfterU(d.getAfterU());
+            e.setBeforeUlv(d.getBeforeUlv());
+            e.setAfterUlv(d.getAfterUlv());
+            e.setBeforeP(d.getBeforeP());
+            e.setBeforeQ(d.getBeforeQ());
+            volDetailMapper.insert(e);
+        }
+        return i;
+    }
+
+    // ==================== 开关状态-详情 ====================
+
+    private int insertSwitchResultDetail(List<SwitchResultDTO.SwitchResultDetailDTO> details, Long resultId) {
+        int i = 1;
+        for (SwitchResultDTO.SwitchResultDetailDTO d : details) {
+            FhzgReconSwitchResultDetail e = new FhzgReconSwitchResultDetail();
+            e.setResultId(resultId);
+            e.setDetailNo(i++);
+            e.setPsrId(d.getPsrId());
+            e.setPsrType(d.getPsrType());
+            e.setNode1Rdfid(d.getNode1Rdfid());
+            e.setNode2Rdfid(d.getNode2Rdfid());
+            e.setAutoType(d.getAutoType());
+            e.setBeforeState(d.getBeforeState());
+            e.setBeforeI(d.getBeforeI());
+            e.setBeforeU(d.getBeforeU());
+            e.setAfterState(d.getAfterState());
+            e.setAfterI(d.getAfterI());
+            e.setAfterU(d.getAfterU());
+            switchResultDetailMapper.insert(e);
+        }
+        return i;
+    }
+
+    // ==================== 运行指标(扁平 + JSON) ====================
+
+    private FhzgReconMetrics buildMetrics(MetricsDTO dto, Long resultId) {
+        FhzgReconMetrics e = new FhzgReconMetrics();
+        e.setResultId(resultId);
+        fillMetricsSide(e, dto.getMetricsBefore(), true);
+        fillMetricsSide(e, dto.getMetricsAfter(), false);
+        e.setDelta(toJson(dto.getDelta()));
+        e.setJudgement(toJson(dto.getJudgement()));
+        return e;
+    }
+
+    /** 按 before/after 将指标子对象扁平写入实体对应字段; val 为 null 时跳过. */
+    private void fillMetricsSide(FhzgReconMetrics e, MetricsDTO.MetricsBeforeAfterDTO m, boolean before) {
+        if (m == null) return;
+
+        MetricsDTO.LoadingDTO ld = m.getLoading();
+        if (ld != null) {
+            side(e, before, FhzgReconMetrics::setBeforeLoadingLineCount, FhzgReconMetrics::setAfterLoadingLineCount, ld.getLineCount());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingExtGridCount, FhzgReconMetrics::setAfterLoadingExtGridCount, ld.getExtGridCount());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingMaxLoadingPct, FhzgReconMetrics::setAfterLoadingMaxLoadingPct, ld.getMaxLoadingPct());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingAvgLoadingPct, FhzgReconMetrics::setAfterLoadingAvgLoadingPct, ld.getAvgLoadingPct());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingOverLimitCount, FhzgReconMetrics::setAfterLoadingOverLimitCount, ld.getOverLimitCount());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingLoadingLimitPct, FhzgReconMetrics::setAfterLoadingLoadingLimitPct, ld.getLoadingLimitPct());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingUnit, FhzgReconMetrics::setAfterLoadingUnit, ld.getUnit());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingLoadingSource, FhzgReconMetrics::setAfterLoadingLoadingSource, ld.getLoadingSource());
+            side(e, before, FhzgReconMetrics::setBeforeFeederLoadRatesPct, FhzgReconMetrics::setAfterFeederLoadRatesPct, toJson(ld.getFeederLoadRatesPct()));
+            side(e, before, FhzgReconMetrics::setBeforeLoadingLineMaxLoadingPct, FhzgReconMetrics::setAfterLoadingLineMaxLoadingPct, ld.getLineMaxLoadingPct());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingLineAvgLoadingPct, FhzgReconMetrics::setAfterLoadingLineAvgLoadingPct, ld.getLineAvgLoadingPct());
+            side(e, before, FhzgReconMetrics::setBeforeLoadingLineOverLimitCount, FhzgReconMetrics::setAfterLoadingLineOverLimitCount, ld.getLineOverLimitCount());
+        }
+
+        MetricsDTO.LossDTO ls = m.getLoss();
+        if (ls != null) {
+            side(e, before, FhzgReconMetrics::setBeforeLossLineLossMw, FhzgReconMetrics::setAfterLossLineLossMw, ls.getLineLossMw());
+            side(e, before, FhzgReconMetrics::setBeforeLossTrafoLossMw, FhzgReconMetrics::setAfterLossTrafoLossMw, ls.getTrafoLossMw());
+            side(e, before, FhzgReconMetrics::setBeforeLossTotalLossMw, FhzgReconMetrics::setAfterLossTotalLossMw, ls.getTotalLossMw());
+            side(e, before, FhzgReconMetrics::setBeforeLossUnit, FhzgReconMetrics::setAfterLossUnit, ls.getUnit());
+        }
+
+        MetricsDTO.VoltageDTO v = m.getVoltage();
+        if (v != null) {
+            side(e, before, FhzgReconMetrics::setBeforeVoltageMinVmPu, FhzgReconMetrics::setAfterVoltageMinVmPu, v.getMinVmPu());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageMaxVmPu, FhzgReconMetrics::setAfterVoltageMaxVmPu, v.getMaxVmPu());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageAvgVmPu, FhzgReconMetrics::setAfterVoltageAvgVmPu, v.getAvgVmPu());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageUnderVminCount, FhzgReconMetrics::setAfterVoltageUnderVminCount, v.getUnderVminCount());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageOverVmaxCount, FhzgReconMetrics::setAfterVoltageOverVmaxCount, v.getOverVmaxCount());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageVminPu, FhzgReconMetrics::setAfterVoltageVminPu, v.getVminPu());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageVmaxPu, FhzgReconMetrics::setAfterVoltageVmaxPu, v.getVmaxPu());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageBusCount, FhzgReconMetrics::setAfterVoltageBusCount, v.getBusCount());
+            side(e, before, FhzgReconMetrics::setBeforeVoltageUnit, FhzgReconMetrics::setAfterVoltageUnit, v.getUnit());
+        }
+
+        MetricsDTO.ReliabilityDTO r = m.getReliability();
+        if (r != null) {
+            side(e, before, FhzgReconMetrics::setBeforeReliabilityEnabled, FhzgReconMetrics::setAfterReliabilityEnabled, r.getEnabled());
+            side(e, before, FhzgReconMetrics::setBeforeReliabilityComment, FhzgReconMetrics::setAfterReliabilityComment, r.getComment());
+        }
+
+        MetricsDTO.HealthDTO h = m.getHealth();
+        if (h != null) {
+            side(e, before, FhzgReconMetrics::setBeforeHealthScore, FhzgReconMetrics::setAfterHealthScore, h.getScore());
+            side(e, before, FhzgReconMetrics::setBeforeHealthBase, FhzgReconMetrics::setAfterHealthBase, h.getBase());
+            side(e, before, FhzgReconMetrics::setBeforeHealthPenalty, FhzgReconMetrics::setAfterHealthPenalty, h.getPenalty());
+            ScoreItemsDTO w = h.getWeights();
+            if (w != null) {
+                side(e, before, FhzgReconMetrics::setBeforeHealthWeightSafe, FhzgReconMetrics::setAfterHealthWeightSafe, w.getSafe());
+                side(e, before, FhzgReconMetrics::setBeforeHealthWeightVoltage, FhzgReconMetrics::setAfterHealthWeightVoltage, w.getVoltage());
+                side(e, before, FhzgReconMetrics::setBeforeHealthWeightLoss, FhzgReconMetrics::setAfterHealthWeightLoss, w.getLoss());
+                side(e, before, FhzgReconMetrics::setBeforeHealthWeightBalance, FhzgReconMetrics::setAfterHealthWeightBalance, w.getBalance());
+                side(e, before, FhzgReconMetrics::setBeforeHealthWeightRisk, FhzgReconMetrics::setAfterHealthWeightRisk, w.getRisk());
+            }
+            ScoreItemsDTO c = h.getComponents();
+            if (c != null) {
+                side(e, before, FhzgReconMetrics::setBeforeHealthComponentSafe, FhzgReconMetrics::setAfterHealthComponentSafe, c.getSafe());
+                side(e, before, FhzgReconMetrics::setBeforeHealthComponentVoltage, FhzgReconMetrics::setAfterHealthComponentVoltage, c.getVoltage());
+                side(e, before, FhzgReconMetrics::setBeforeHealthComponentLoss, FhzgReconMetrics::setAfterHealthComponentLoss, c.getLoss());
+                side(e, before, FhzgReconMetrics::setBeforeHealthComponentBalance, FhzgReconMetrics::setAfterHealthComponentBalance, c.getBalance());
+                side(e, before, FhzgReconMetrics::setBeforeHealthComponentRisk, FhzgReconMetrics::setAfterHealthComponentRisk, c.getRisk());
+            }
+        }
+    }
+
+    /** before 时调 beforeSetter, 否则调 afterSetter; val 为 null 跳过. */
+    private <T> void side(FhzgReconMetrics e, boolean before,
+                          BiConsumer<FhzgReconMetrics, T> beforeSetter,
+                          BiConsumer<FhzgReconMetrics, T> afterSetter, T val) {
+        if (val == null) return;
+        (before ? beforeSetter : afterSetter).accept(e, val);
+    }
+
+    // ==================== 类型转换工具 ====================
+
+    /** ISO8601 字符串(含时区) -> LocalDateTime(取本地壁钟时间); 无法解析返回 null. */
+    private LocalDateTime parseTime(String s) {
+        if (s == null || s.trim().isEmpty()) return null;
+        try {
+            return OffsetDateTime.parse(s, ISO_OFFSET).toLocalDateTime();
+        } catch (Exception e) {
+            try {
+                return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+            } catch (Exception e2) {
+                return null;
+            }
+        }
+    }
+
+    /** JsonNode -> JSON 字符串; null 或 null 节点返回 null. */
+    private String toJson(JsonNode node) {
+        if (node == null || node.isNull()) return null;
+        try {
+            return OM.writeValueAsString(node);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

+ 75 - 0
services/load-transfer-si/src/main/java/com/hdkj/lt/si/service/remote/impl/ReconRemoteCallServiceImpl.java

@@ -0,0 +1,75 @@
+package com.hdkj.lt.si.service.remote.impl;
+
+import com.hdkj.lt.core.rest.model.AbstractCallResponseEntity;
+import com.hdkj.lt.core.rest.service.impl.AbstractDefaultRemoteCallService;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.Charsets;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ReconRemoteCallServiceImpl extends AbstractDefaultRemoteCallService {
+
+    private static final String SUCCESS_CODE = "200";
+
+    private static final String REQUEST_URI = "http://25.91.249.219:18080/api/reconfiguration";
+
+    @Override
+    protected CallConfig buildCallConfig() {
+        return CallConfig.builder()
+                .requestUri(REQUEST_URI)
+                .successCode(SUCCESS_CODE)
+                .responseType(ReconRemoteCallServiceImpl.DataManagerCallResponseEntity.class)
+                .build();
+    }
+
+    @Override
+    protected <B> HttpEntity<B> createHttpEntity(B body) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+        headers.set(HttpHeaders.ACCEPT_CHARSET, Charsets.UTF_8.toString());
+        return new HttpEntity<>(body, headers);
+    }
+
+    @Getter
+    @Setter
+    public static class DataManagerCallResponseEntity extends AbstractCallResponseEntity {
+
+        private String code; //响应状态码
+
+        private Object data; //响应数据
+
+        private String message; //响应信息
+
+        private String errMsg; //错误信息
+
+        private String status; //响应状态
+
+
+        @Override
+        public Object getCode() {
+            return code;
+        }
+
+        @Override
+        public Object getData() {
+            return data;
+        }
+
+        @Override
+        public String getMsg() {
+            return Optional.ofNullable(errMsg).orElse(message);
+        }
+
+    }
+
+}