Parcourir la source

ruoyi-job 迁移

张同佳 il y a 6 mois
Parent
commit
70a6318d0b
23 fichiers modifiés avec 1631 ajouts et 2 suppressions
  1. 5 0
      services/ruoyi-job/pom.xml
  2. 29 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/controller/DealFeederBaseController.java
  3. 50 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/controller/DealPsrDeviceController.java
  4. 36 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/controller/DistOSSSvgFileController.java
  5. 27 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/convert/EntityConvertor.java
  6. 36 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/po/FeederBaseRequest.java
  7. 177 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/po/FeederBaseResponse.java
  8. 51 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/po/FhzgDistMappingFilePO.java
  9. 41 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/DealPsrDeviceMapper.java
  10. 53 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/DistOSSSvgFileMapper.java
  11. 174 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/mapping/DealPsrDeviceMapper.xml
  12. 50 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/mapping/DistOSSSvgFileMapper.xml
  13. 11 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IDealFeederBaseService.java
  14. 30 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IDealPsrDeviceService.java
  15. 15 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IDistOSSSvgFileService.java
  16. 72 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IPsrDeviceSaveService.java
  17. 126 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/DealFeederBaseServiceImpl.java
  18. 260 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/DealPsrDeviceServiceImpl.java
  19. 266 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/DistOSSSvgFileServiceImpl.java
  20. 97 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/PsrDeviceSaveServiceImpl.java
  21. 1 1
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/SysJobLogServiceImpl.java
  22. 2 1
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/SysJobServiceImpl.java
  23. 22 0
      services/ruoyi-job/src/main/java/com/hdkj/lt/job/task/StruTreeCacheRefreshProcessor.java

+ 5 - 0
services/ruoyi-job/pom.xml

@@ -42,6 +42,11 @@
             <artifactId>ruoyi-common-swagger</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>${pagehelper.boot.version}</version>
+        </dependency>
     </dependencies>
 
 <!--    <build>-->

+ 29 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/controller/DealFeederBaseController.java

@@ -0,0 +1,29 @@
+package com.hdkj.lt.job.controller;
+
+import com.hdkj.hussar.ApiResponse;
+import com.hdkj.lt.job.service.impl.DealFeederBaseServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author rj
+ * @date 2025/12/23 15:36
+ * @description
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/feederBase")
+public class DealFeederBaseController {
+
+
+    private final DealFeederBaseServiceImpl dealFeederBaseServiceImpl;
+
+    @GetMapping("/update")
+    public ApiResponse<String> updateFeederBase() {
+        dealFeederBaseServiceImpl.dealFeederBase();
+        return ApiResponse.success("获取电网资源业务中台线路基础数据并保存数据操作成功");
+    }
+
+}

+ 50 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/controller/DealPsrDeviceController.java

@@ -0,0 +1,50 @@
+package com.hdkj.lt.job.controller;
+
+
+import com.hdkj.hussar.ApiResponse;
+import com.hdkj.lt.job.service.IDealPsrDeviceService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Optional;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/6/25 8:59
+ * @Version 1.0
+ */
+@Validated
+@RestController
+@RequiredArgsConstructor
+public class DealPsrDeviceController {
+
+    private final IDealPsrDeviceService dealPsrDeviceService;
+
+    @GetMapping("saveJoinEcTransformer")
+    public ApiResponse<String> saveJoinEcTransformerInfo(String cityShort) {
+        dealPsrDeviceService.saveJoinEcTransformerInfo(Optional.ofNullable(cityShort).orElse("-"));
+        return ApiResponse.success("获取电网资源业务中台中压接入点和专变数据并保存数据操作成功");
+    }
+
+    @GetMapping("saveFeederSpanSegment")
+    public ApiResponse<String> saveFeederSpanSegmentInfo() {
+        dealPsrDeviceService.dealFeederSpanSegmentBak();
+        return ApiResponse.success("中台DI同步前备份数据操作成功");
+    }
+
+    @GetMapping("updateFeederSpanSegment")
+    public ApiResponse<String> updateFeederSpanSegmentInfo() {
+        dealPsrDeviceService.dealFeederSpanSegmentSupply();
+        return ApiResponse.success("中台DI同步后更新中台缺失字段数据操作成功");
+    }
+
+    @GetMapping("saveSegmentPsrDevice")
+    public ApiResponse<String> saveSegmentPsrDeviceInfo(String cityShort) {
+        dealPsrDeviceService.saveSegmentPsrDeviceInfo(Optional.ofNullable(cityShort).orElse("-"));
+        return ApiResponse.success("获取电网资源业务中台导线和电缆段数据并保存数据操作成功");
+    }
+
+}

+ 36 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/controller/DistOSSSvgFileController.java

@@ -0,0 +1,36 @@
+package com.hdkj.lt.job.controller;
+
+import com.hdkj.hussar.ApiResponse;
+import com.hdkj.lt.job.service.IDistOSSSvgFileService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/6/25 8:59
+ * @Version 1.0
+ */
+@Validated
+@RestController
+@RequiredArgsConstructor
+public class DistOSSSvgFileController {
+
+    private final IDistOSSSvgFileService distOSSSvgFileService;
+
+    @GetMapping("getAndSaveFile")
+    public ApiResponse<String> getAndSaveFileInfo(@NotBlank(message = "文件夹不能为空") String fileDir) {
+        distOSSSvgFileService.getAndSaveFileInfo(fileDir);
+        return ApiResponse.success("获取配电云主站并保存文件操作成功");
+    }
+
+    @GetMapping("batchSaveFile")
+    public ApiResponse<String> batchSaveFile(@NotBlank(message = "文件夹不能为空") String fileDir) {
+        distOSSSvgFileService.saveBatchFileInfo(fileDir);
+        return ApiResponse.success("获取配电云主站并下载文件操作成功");
+    }
+}

+ 27 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/convert/EntityConvertor.java

@@ -0,0 +1,27 @@
+package com.hdkj.lt.job.entity.convert;
+
+import com.hdkj.lt.core.bizms.modle.po.DwdShbDsFeederBase;
+import com.hdkj.lt.job.entity.po.FeederBaseResponse;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author rj
+ * @date 2025/6/27 9:57
+ * @description
+ */
+@Component
+@Mapper
+public interface EntityConvertor {
+
+    EntityConvertor INSTANCE = Mappers.getMapper(EntityConvertor.class);
+
+    @Mapping(source = "assets.astId",target = "astId")
+    @Mapping(source = "assets.feederRateCapacity",target = "feederRateCapacity")
+    @Mapping(source = "assets.ctime",target = "ctime")
+    @Mapping(source = "assets.lastUpdateTime",target = "lastUpdateTime")
+    @Mapping(expression = "java(new java.util.Date())",target = "updateTime")
+    DwdShbDsFeederBase toDwdShbDsFeederBase(FeederBaseResponse.ResultDTO.DkxDTO.RecordsDTO.ResourceDTO resourceDTO, FeederBaseResponse.ResultDTO.DkxDTO.RecordsDTO.AssetsDTO assets);
+}

+ 36 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/po/FeederBaseRequest.java

@@ -0,0 +1,36 @@
+package com.hdkj.lt.job.entity.po;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author rj
+ * @date 2025/12/22 15:31
+ * @description
+ */
+@NoArgsConstructor
+@Data
+public class FeederBaseRequest {
+
+    private ParamsDTO params;
+    private String psrType;
+    private Integer distribution;
+
+    @NoArgsConstructor
+    @Data
+    public static class ParamsDTO {
+        private Integer current;
+        private Integer size;
+        private List<FiltersDTO> filters;
+
+        @NoArgsConstructor
+        @Data
+        public static class FiltersDTO {
+            private String compare;
+            private String fieldName;
+            private String fieldValue;
+        }
+    }
+}

+ 177 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/po/FeederBaseResponse.java

@@ -0,0 +1,177 @@
+package com.hdkj.lt.job.entity.po;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author rj
+ * @date 2025/12/22 15:41
+ * @description
+ */
+@NoArgsConstructor
+@Data
+public class FeederBaseResponse {
+
+    private Integer count;
+    private String errors;
+    private String message;
+    private ResultDTO result;
+    private String status;
+
+    @NoArgsConstructor
+    @Data
+    public static class ResultDTO {
+        private DkxDTO dkx;
+
+        @NoArgsConstructor
+        @Data
+        public static class DkxDTO {
+            private Integer current;
+            private Integer total;
+            private Integer pages;
+            private Integer size;
+            private List<RecordsDTO> records;
+            private Boolean searchCount;
+            private List<?> orders;
+
+            @NoArgsConstructor
+            @Data
+            public static class RecordsDTO {
+                private List<AssetsDTO> assets;
+                private Integer distribution;
+                private String id;
+                private String modelId;
+                private ResourceDTO resource;
+
+                @NoArgsConstructor
+                @Data
+                public static class ResourceDTO {
+                    private String isCoaltoelectricity;
+                    private String normalEnergizingBusbar;
+                    private String startSwitchType;
+                    @JSONField(name = "erectionMethod#Name")
+                    private String erectionMethodName;
+                    private String dispatchOrg;
+                    private String maintOrg;
+                    @JSONField(name = "isCentralizedelechtg#Name")
+                    private String isCentralizedelechtgName;
+                    private Double referenceLength;
+                    private String cableMethod;
+                    @JSONField(name = "voltageLevel#Name")
+                    private String voltageLevelName;
+                    private String supplyArea;
+                    private String startStationType;
+                    @JSONField(name = "cableMethod#Name")
+                    private String cableMethodName;
+                    private String overheadMethod;
+                    @JSONField(name = "supplyArea#Name")
+                    private String supplyAreaName;
+                    @JSONField(name = "pubPrivFlag#Name")
+                    private String pubPrivFlagName;
+                    private String isDistributedelechtg;
+                    @JSONField(name = "regionalism#Name")
+                    private String regionalismName;
+                    @JSONField(name = "startSwitch#Name")
+                    private String startSwitchName;
+                    private String erectionMethod;
+                    private String dispatchLevel;
+                    private String isRural;
+                    @JSONField(name = "dispatchLevel#Name")
+                    private String dispatchLevelName;
+                    @JSONField(name = "isRural#Name")
+                    private String isRuralName;
+                    private Date ctime;
+                    private String startStation;
+                    private String startBay;
+                    private String equipmentOwner;
+                    @JSONField(name = "overheadMethod#Name")
+                    private String overheadMethodName;
+                    private String psrId;
+                    @JSONField(name = "startStation#Name")
+                    private String startStationName;
+                    private String astId;
+                    @JSONField(name = "maintOrg#Name")
+                    private String maintOrgName;
+                    private String name;
+                    @JSONField(name = "isDaFeeder#Name")
+                    private String isDaFeederName;
+                    @JSONField(name = "isCoaltoelectricity#Name")
+                    private String isCoaltoelectricityName;
+                    private Double overheadLength;
+                    private Double cableLength;
+                    private Date lastUpdateTime;
+                    private Double feederRateCapacity;
+                    private String psrState;
+                    private String regionalism;
+                    @JSONField(name = "startStationType#Name")
+                    private String startStationTypeName;
+                    private String city;
+                    private String importance;
+                    private Double supplyRadius;
+                    @JSONField(name = "isDistributedelechtg#Name")
+                    private String isDistributedelechtgName;
+                    @JSONField(name = "importance#Name")
+                    private String importanceName;
+                    private String runDevName;
+                    @JSONField(name = "psrState#Name")
+                    private String psrStateName;
+                    private Date startTime;
+                    private String pubPrivFlag;
+                    private String startSwitch;
+                    @JSONField(name = "maintGroup#Name")
+                    private String maintGroupName;
+                    private String maintGroup;
+                    private Double length;
+                    private String isCentralizedelechtg;
+                    private String consNo;
+                    private String isDaFeeder;
+                    @JSONField(name = "dispatchOrg#Name")
+                    private String dispatchOrgName;
+                    @JSONField(name = "city#Name")
+                    private String cityName;
+                    private String voltageLevel;
+                    @JSONField(name = "startBay#Name")
+                    private String startBayName;
+                    @JSONField(name = "equipmentOwner#Name")
+                    private String equipmentOwnerName;
+                }
+
+                @NoArgsConstructor
+                @Data
+                public static class AssetsDTO {
+                    private Double feederRateCapacity;
+                    private String astNature;
+                    @JSONField(name = "source#Name")
+                    private String sourceName;
+                    private String source;
+                    @JSONField(name = "astOrg#Name")
+                    private String astOrgName1;
+                    @JSONField(name = "isCommission#Name")
+                    private String isCommissionName;
+                    private String astOrg;
+                    private String astOrgName;
+                    private Date ctime;
+                    @JSONField(name = "astNature#Name")
+                    private String astNatureName;
+                    private String equipCode;
+                    private String isCommission;
+                    private String astId;
+                    private String astNum;
+                    private String wbsNum;
+                    @JSONField(name = "deployState#Name")
+                    private String deployStateName;
+                    private String projectNum;
+                    private Double maxAllowableCurrent;
+                    private String projectName;
+                    private Date operateDate;
+                    private String deployState;
+                    private Date lastUpdateTime;
+                }
+            }
+        }
+    }
+}

+ 51 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/entity/po/FhzgDistMappingFilePO.java

@@ -0,0 +1,51 @@
+package com.hdkj.lt.job.entity.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.hdkj.lt.base.model.entity.FhzgBaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/5/15 18:05
+ * @Version 1.0
+ */
+@ApiModel(value = "负荷转供配电成图文件关联信息表")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+@TableName("fhzg_dist_mapping_file")
+public class FhzgDistMappingFilePO extends FhzgBaseEntity {
+
+    /**
+     * 配电馈线线路ID
+     */
+    @ApiModelProperty(value = "配电馈线线路ID")
+    private String lineId;
+
+    /**
+     * 附件ID
+     */
+    @ApiModelProperty(value = "附件ID")
+    private Long fileId;
+
+    /**
+     * 附件类型(1:单线SVG图,2:单线图XML,3:开关段图,4:一级联络图,5:运行调优图)
+     */
+    @ApiModelProperty(value = "附件类型(1:单线SVG图,2:单线图XML,3:开关段图,4:一级联络图,5:运行调优图)")
+    private String fileType;
+
+    /**
+     * topo版本号(yyyymmdd)
+     */
+    @ApiModelProperty(value = "附件版本号(yyyymmdd)")
+    private String topoVersion;
+}

+ 41 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/DealPsrDeviceMapper.java

@@ -0,0 +1,41 @@
+package com.hdkj.lt.job.mapper;
+
+import com.hdkj.lt.modle.po.DistDeviceJoinEcPO;
+import com.hdkj.lt.modle.po.JoinEcTransformerPO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/7/30 15:22
+ * @Version 1.0
+ */
+@Mapper
+public interface DealPsrDeviceMapper {
+
+    List<String> getDistJoinEcBaseInfo();
+
+    List<String> getConfigCityInfo(List<String> cityShorts);
+
+    int saveDistJoinEcTransformer(List<JoinEcTransformerPO> saveList);
+
+    int saveDistDeviceJoinEc(List<DistDeviceJoinEcPO> joinEcList);
+
+    int savePsrDeviceDIBeforeBak();
+
+    int updateFeederDIAfter();
+
+    int updateFeederPsrDevice();
+
+    int updateSpanSegmentDIAfter();
+
+    int saveDistConductorSegment(List<Map<String, Object>> deviceList);
+
+    int saveDistCableSegment(List<Map<String, Object>> deviceList);
+
+    int updateSegmentPsrDevice();
+
+}

+ 53 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/DistOSSSvgFileMapper.java

@@ -0,0 +1,53 @@
+package com.hdkj.lt.job.mapper;
+
+import com.hdkj.lt.core.sys.dao.HussarMapper;
+import com.hdkj.lt.job.entity.po.FhzgDistMappingFilePO;
+import com.hdkj.lt.modle.FileUploadDTO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/6/19 15:52
+ * @Version 1.0
+ */
+@Mapper
+public interface DistOSSSvgFileMapper extends HussarMapper<FhzgDistMappingFilePO> {
+    /**
+     * 获取配置的自定义附件存储线路信息
+     */
+    List<String> getCustomStorageLine();
+
+    /**
+     * 修改配置的更新文件时间
+     */
+    void updateConfigFileChangeTime(LocalDateTime dateTime, LocalDateTime plusDayTime);
+
+    /**
+     * 修改配置的更新文件时间
+     */
+    String getConfigValueFileChange();
+
+    /**
+     * 获取需要配电云主站文件信息
+     */
+    List<String> getDistMasterStationFile(Integer size);
+
+    /**
+     * 添加需要上传本地oss的配电云主站变更的数据
+     */
+    void insertDistMasterStationFile(List<FileUploadDTO> fileList, LocalDateTime dateTime);
+
+    /**
+     * 逻辑删除已经上传本地oss的配电云主站变更的数据
+     */
+    void updateDistMasterStationFile(List<String> fileNames);
+
+    /**
+     * 获取所有的配电云主站文件信息
+     */
+    List<FileUploadDTO> getAllDistMasterStationFile();
+}

+ 174 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/mapping/DealPsrDeviceMapper.xml

@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.hdkj.lt.job.mapper.DealPsrDeviceMapper">
+
+    <insert id="saveDistJoinEcTransformer">
+        replace into dwd_shb_ds_joinec_transformer_base(psr_id, ast_Id, cons_no, `name`, psr_Type, join_ec, feeder,
+                city, maint_org, maint_group, psr_state, pub_priv_flag, start_time, use_nature, voltage_level)
+        values
+        <foreach collection="saveList" item="item" index="index" separator=",">
+            (#{item.psrId}, #{item.astId}, #{item.consNo}, #{item.name}, #{item.psrType}, #{item.joinEc}, #{item.feeder}, #{item.city},
+            #{item.maintOrg}, #{item.maintGroup}, #{item.psrState}, #{item.pubPrivFlag}, #{item.startTime}, #{item.useNature}, #{item.voltageLevel})
+        </foreach>
+    </insert>
+    <insert id="saveDistDeviceJoinEc">
+        replace into dwd_shb_ds_joinec_base(psr_id, `name`, branch_feeder, branch_feeder_name, feeder, feeder_name, feeder_segment, voltage_level, voltage_level_name, line,
+                line_name, installed_capacity, is_has_dg, join_dev_id, join_dev_type, psr_state, psr_state_name, pub_priv_flag, pub_priv_flag_name, reliable_segment, start_time,
+                stop_time, user_id, city, city_name, maint_org, maint_org_name, maint_group, maint_group_name, equipment_owner, equipment_owner_name, ctime, last_update_time, del_flag)
+        values
+        <foreach collection="joinEcList" item="item" index="index" separator=",">
+            (#{item.psrId}, #{item.name}, #{item.branchFeeder}, #{item.branchFeederName}, #{item.feeder}, #{item.feederName}, #{item.feederSegment}, #{item.voltageLevel},
+            #{item.voltageLevelName}, #{item.line}, #{item.lineName}, #{item.installedCapacity}, #{item.isHasDg}, #{item.joinDevId}, #{item.joinDevType}, #{item.psrState},
+            #{item.psrStateName}, #{item.pubPrivFlag}, #{item.pubPrivFlagName}, #{item.reliableSegment}, #{item.startTime}, #{item.stopTime}, #{item.userId}, #{item.city}, #{item.cityName},
+            #{item.maintOrg}, #{item.maintOrgName}, #{item.maintGroup}, #{item.maintGroupName}, #{item.equipmentOwner}, #{item.equipmentOwnerName}, #{item.ctime}, #{item.lastUpdateTime}, '0')
+        </foreach>
+    </insert>
+
+    <insert id="savePsrDeviceDIBeforeBak">
+        <!-- 先删后增DI同步之前的线路数据 (馈线、档距段设备)-->
+        truncate table dwd_shb_ds_feeder_base_di_before;
+        insert into dwd_shb_ds_feeder_base_di_before
+        select t.* from dwd_shb_ds_feeder_base t;
+
+        truncate table dwd_shb_ds_span_segment_base_di_before;
+        insert into dwd_shb_ds_span_segment_base_di_before
+        select t.* from dwd_shb_ds_span_segment_base t;
+    </insert>
+    <insert id="updateFeederPsrDevice">
+        truncate table T_PSR_DS_FEEDER;
+        insert into T_PSR_DS_FEEDER (AST_ID,CABLE_LENGTH,CABLE_METHOD,CABLE_METHOD_NAME,CITY,CITY_NAME,
+            CONS_NO,CTIME,DISPATCH_AREA,DISPATCH_ORG,EQUIPMENT_OWNER,EQUIPMENT_OWNER_NAME,ERECTION_METHOD,
+            ERECTION_METHOD_NAME,FEEDER_RATE_CAPACITY,GRID_CODE,IMPORTANCE,IMPORTANCE_NAME,IS_RURAL,
+            IS_RURAL_NAME,LAST_UPDATE_TIME,`LENGTH`,MAINT_GROUP,MAINT_GROUP_NAME,MAINT_ORG,MAINT_ORG_NAME,
+            `NAME`,OVERHEAD_LENGTH,OVERHEAD_METHOD,OVERHEAD_METHOD_NAME,PSR_ID,PSR_STATE,PSR_STATE_NAME,
+            PUB_PRIV_FLAG,PUB_PRIV_FLAG_NAME,REGIONALISM,REGIONALISM_NAME,RELIABLE_DEV_NAME,RELIABLE_DEV_NAME_NAME,
+            RUN_DEV_NAME,START_STATION,START_STATION_NAME,START_SWITCH,START_SWITCH_TYPE,START_TIME,STOP_TIME,
+            SUPPLY_AREA,SUPPLY_AREA_NAME,SUPPLY_RADIUS,VOLTAGE_LEVEL,VOLTAGE_LEVEL_NAME,CREATE_TIME,UPDATE_TIME)
+        select AST_ID,CABLE_LENGTH,CABLE_METHOD,CABLE_METHOD_NAME,CITY,CITY_NAME,CONS_NO,CTIME,
+               DISPATCH_LEVEL_NAME DISPATCH_AREA,DISPATCH_ORG,EQUIPMENT_OWNER,EQUIPMENT_OWNER_NAME,
+               ERECTION_METHOD,ERECTION_METHOD_NAME,FEEDER_RATE_CAPACITY,null GRID_CODE,IMPORTANCE,
+               IMPORTANCE_NAME,IS_RURAL,IS_RURAL_NAME,LAST_UPDATE_TIME,`LENGTH`,MAINT_GROUP,MAINT_GROUP_NAME,
+               MAINT_ORG,MAINT_ORG_NAME,`NAME`,OVERHEAD_LENGTH,OVERHEAD_METHOD,OVERHEAD_METHOD_NAME,PSR_ID,
+               PSR_STATE,PSR_STATE_NAME,PUB_PRIV_FLAG,PUB_PRIV_FLAG_NAME,REGIONALISM,REGIONALISM_NAME,
+               null RELIABLE_DEV_NAME,null RELIABLE_DEV_NAME_NAME,RUN_DEV_NAME,START_STATION,START_STATION_NAME,
+               START_SWITCH,START_SWITCH_TYPE,START_TIME,null STOP_TIME,SUPPLY_AREA,SUPPLY_AREA_NAME,
+               SUPPLY_RADIUS,VOLTAGE_LEVEL,VOLTAGE_LEVEL_NAME,null CREATE_TIME,UPDATE_TIME
+        from dwd_shb_ds_feeder_base t;
+    </insert>
+    <update id="updateFeederDIAfter">
+        update dwd_shb_ds_feeder_base x, dwd_shb_ds_feeder_base_di_before y
+        set x.voltage_level_name = y.voltage_level_name,x.cable_method_name = y.cable_method_name,
+            x.psr_state_name = y.psr_state_name,x.pub_priv_flag_name = y.pub_priv_flag_name,
+            x.start_station_name = y.start_station_name,x.start_switch_name = y.start_switch_name,
+            x.start_time = y.start_time,x.supply_area_name = y.supply_area_name,
+            x.regionalism_name = y.regionalism_name,x.dispatch_org_name = y.dispatch_org_name,
+            x.dispatch_level_name = y.dispatch_level_name,x.equipment_owner_name = y.equipment_owner_name,
+            x.erection_method_name = y.erection_method_name,x.feeder_rate_capacity = y.feeder_rate_capacity,
+            x.importance_name = y.importance_name,x.is_da_feeder = y.is_da_feeder,
+            x.is_rural_name = y.is_rural_name,x.city_name = y.city_name,x.maint_org_name = y.maint_org_name,
+            x.maint_group_name = y.maint_group_name,x.normal_energizing_busbar = y.normal_energizing_busbar,
+            x.overhead_method_name = y.overhead_method_name,x.ast_nature_name = y.ast_nature_name,
+            x.deploy_state_name = y.deploy_state_name,x.is_commission_name = y.is_commission_name,
+            x.source_name = y.source_name,x.del_flag=y.del_flag where x.psr_id=y.psr_id and x.ast_id=y.ast_id
+    </update>
+    <update id="updateSpanSegmentDIAfter">
+        update dwd_shb_ds_span_segment_base x, dwd_shb_ds_span_segment_base_di_before y
+        set x.feeder_name = y.feeder_name,x.branch_feeder_name = y.branch_feeder_name,x.line_name = y.line_name,
+            x.voltage_level_name = y.voltage_level_name,x.ast_nature_name = y.ast_nature_name,
+            x.manufacturer_name = y.manufacturer_name,x.start_pole_name = y.start_pole_name,
+            x.stop_pole_name = y.stop_pole_name,x.supply_area_name = y.supply_area_name,
+            x.wire_arrangement_name = y.wire_arrangement_name,x.wire_type_name = y.wire_type_name,
+            x.city_name = y.city_name,x.maint_org_name = y.maint_org_name,x.maint_group_name = y.maint_group_name,
+            x.equipment_owner_name = y.equipment_owner_name, x.del_flag = y.del_flag
+        where x.psr_id = y.psr_id and x.ast_id = y.ast_id
+    </update>
+
+    <insert id="saveDistConductorSegment">
+        replace into dwd_shb_ds_conductor_segment_base (psr_id, ast_id, `name`, feeder, feeder_name, branch_feeder, branch_feeder_name,
+            line, line_name, voltage_level, voltage_level_name, ast_nature, ast_nature_name, equip_code, is_commission, is_rural, `length`,
+            manufacturer, manufacturer_name, model, project_name, project_num, rated_ampacity, reference_length, start_pole, start_pole_name,
+            stop_pole, stop_pole_name, supply_area, supply_area_name, wire_arrangement, wire_arrangement_name, wire_cross_section,
+            wire_type, wire_type_name, city, city_name, maint_org, maint_org_name, maint_group, maint_group_name, equipment_owner,
+            equipment_owner_name, psr_state, deploy_state, start_time, stop_time, pub_priv_flag, ctime, last_update_time, del_flag)
+        values
+        <foreach collection="deviceList" item="item" index="index" separator=",">
+            (#{item.psrId}, #{item.astId}, #{item.name}, #{item.feeder}, #{item.feederName}, #{item.branchFeeder},
+            #{item.branchFeederName}, #{item.line}, #{item.lineName}, #{item.voltageLevel}, #{item.voltageLevelName},
+            #{item.astNature}, #{item.astNatureName}, #{item.equipCode}, #{item.isCommission}, #{item.isRural}, #{item.length},
+            #{item.manufacturer}, #{item.manufacturerName}, #{item.model}, #{item.projectName}, #{item.projectNum}, #{item.ratedAmpacity},
+            #{item.referenceLength}, #{item.startPole}, #{item.startPoleName}, #{item.stopPole}, #{item.stopPoleName}, #{item.supplyArea},
+            #{item.supplyAreaName}, #{item.wireArrangement}, #{item.wireArrangementName}, #{item.wireCrossSection}, #{item.wireType},
+            #{item.wireTypeName}, #{item.city}, #{item.cityName}, #{item.maintOrg}, #{item.maintOrgName}, #{item.maintGroup},
+            #{item.maintGroupName}, #{item.equipmentOwner}, #{item.equipmentOwnerName}, #{item.psrState}, #{item.deployState},
+            #{item.startTime}, #{item.stopTime}, #{item.pubPrivFlag}, #{item.ctime}, #{item.lastUpdateTime}, '0')
+        </foreach>
+    </insert>
+    <insert id="saveDistCableSegment">
+        replace into dwd_shb_ds_cable_segment_base (psr_id, ast_id, `name`, feeder, feeder_name, branch_feeder, branch_feeder_name,
+            feeder_segment, line, line_name, voltage_level, voltage_level_name, ast_nature, ast_nature_name, ast_org, ast_org_name,
+            cable, cable_name, core_material, core_material_name, core_quantity, core_quantity_name, cross_sectional_area, end_position,
+            end_position_name, end_type, equip_code, importance, importance_name,insulation_type,insulation_type_name,is_commission,is_rural,
+            lay_method, lay_method_name, `length`, manufacture_date, manufacturer, manufacturer_name, model, project_name, project_num,
+            rated_ampacity, rated_voltage, reference_length, regionalism, regionalism_name, start_position,start_position_name,start_type,
+            supply_area, supply_area_name, city, city_name, maint_org, maint_org_name, maint_group, maint_group_name, equipment_owner,
+            equipment_owner_name, psr_state, deploy_state, start_time, stop_time, pub_priv_flag, ctime, last_update_time, del_flag)
+        values
+        <foreach collection="deviceList" item="item" index="index" separator=",">
+            (#{item.psrId}, #{item.astId}, #{item.name}, #{item.feeder}, #{item.feederName}, #{item.branchFeeder}, #{item.branchFeederName},
+            #{item.feederSegment}, #{item.line}, #{item.lineName}, #{item.voltageLevel}, #{item.voltageLevelName}, #{item.astNature},
+            #{item.astNatureName}, #{item.astOrg}, #{item.astOrgName}, #{item.cable}, #{item.cableName}, #{item.coreMaterial},
+            #{item.coreMaterialName}, #{item.coreQuantity}, #{item.coreQuantityName}, #{item.crossSectionalArea}, #{item.endPosition},
+            #{item.endPositionName}, #{item.endType}, #{item.equipCode}, #{item.importance}, #{item.importanceName}, #{item.insulationType},
+            #{item.insulationTypeName}, #{item.isCommission}, #{item.isRural}, #{item.layMethod}, #{item.layMethodName}, #{item.length},
+            #{item.manufactureDate}, #{item.manufacturer}, #{item.manufacturerName}, #{item.model}, #{item.projectName}, #{item.projectNum},
+            #{item.ratedAmpacity}, #{item.ratedVoltage}, #{item.referenceLength}, #{item.regionalism}, #{item.regionalismName}, #{item.startPosition},
+            #{item.startPositionName}, #{item.startType}, #{item.supplyArea}, #{item.supplyAreaName}, #{item.city}, #{item.cityName}, #{item.maintOrg},
+            #{item.maintOrgName}, #{item.maintGroup}, #{item.maintGroupName}, #{item.equipmentOwner}, #{item.equipmentOwnerName}, #{item.psrState},
+            #{item.deployState}, #{item.startTime}, #{item.stopTime}, #{item.pubPrivFlag}, #{item.ctime}, #{item.lastUpdateTime}, '0')
+        </foreach>
+    </insert>
+    <insert id="updateSegmentPsrDevice">
+        truncate table T_PSR_DS_DX;
+        insert into T_PSR_DS_DX
+        select t.* from (
+            select ast_id, branch_feeder, branch_feeder_name, city, city_name,
+                   ctime, equipment_owner, equipment_owner_name, feeder, feeder_name,
+                   is_rural, null, last_update_time, length, line, line_name,
+                   maint_group, maint_group_name, maint_org, maint_org_name, name,
+                   psr_id, psr_state, null, pub_priv_flag, reference_length,
+                   start_pole, start_time, stop_pole, stop_time, supply_area, supply_area_name,
+                   voltage_level, voltage_level_name, wire , wire_arrangement, wire_arrangement_name
+            from dwd_shb_ds_span_segment_base
+            union all
+            select ast_id, branch_feeder, branch_feeder_name, city, city_name,
+                   ctime, equipment_owner, equipment_owner_name, feeder, feeder_name,
+                   is_rural, null, last_update_time, length, line, line_name,
+                   maint_group, maint_group_name, maint_org, maint_org_name, name,
+                   psr_id, psr_state, null, pub_priv_flag, reference_length,
+                   start_pole, start_time, stop_pole, stop_time, supply_area, supply_area_name,
+                   voltage_level, voltage_level_name, null , wire_arrangement, wire_arrangement_name
+            from dwd_shb_ds_conductor_segment_base
+            union all
+            select ast_id, branch_feeder, branch_feeder_name, city, city_name,
+                   ctime, equipment_owner, equipment_owner_name, feeder, feeder_name,
+                   is_rural, null, last_update_time, length, line, line_name,
+                   maint_group, maint_group_name, maint_org, maint_org_name, name,
+                   psr_id, psr_state, null, pub_priv_flag, reference_length,
+                   null, start_time, null, stop_time, supply_area, supply_area_name,
+                   voltage_level, voltage_level_name, null , null, null
+            from dwd_shb_ds_cable_segment_base) t;
+    </insert>
+
+    <select id="getDistJoinEcBaseInfo" resultType="java.lang.String">
+        select t.psr_id from dwd_shb_ds_joinec_base t where t.psr_state = '20'
+    </select>
+
+    <select id="getConfigCityInfo" resultType="java.lang.String">
+        select org_id from fhzg_dist_organ_table_suffix_config
+        where id &lt; 29 and tab_suffix in
+        <foreach collection="cityShorts" open="(" item="tabSuffix" separator="," close=")">
+            #{tabSuffix}
+        </foreach>
+    </select>
+
+</mapper>

+ 50 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/mapper/mapping/DistOSSSvgFileMapper.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.hdkj.lt.job.mapper.DistOSSSvgFileMapper">
+
+    <select id="getCustomStorageLine" resultType="java.lang.String">
+        select f.psr_id from dwd_shb_ds_feeder_base f join SYS_DICT_SINGLE s on f.city = s.VALUE
+        join SYS_DICT_TYPE t on s.TYPE_ID = t.TYPE_ID and t.TYPE_NAME = 'custom_storage'
+        where s.BAK_1 is null and f.psr_state= '20'
+        union all
+        select f.psr_id from dwd_shb_ds_feeder_base f join SYS_DICT_SINGLE s on f.city = s.LABEL
+        join SYS_DICT_TYPE t on s.TYPE_ID = t.TYPE_ID and t.TYPE_NAME = 'custom_storage'
+        where s.BAK_1 is null and f.psr_state= '20'
+    </select>
+
+    <insert id="insertDistMasterStationFile">
+        insert into fhzg_dist_master_station_file(file_path, upload_time, del_flag, create_time, last_time)
+        values
+        <foreach collection='fileList' item='item' separator=','>
+            (#{item.fileName}, #{item.uploadTime}, '0', #{dateTime}, #{dateTime})
+        </foreach>
+    </insert>
+
+    <update id="updateConfigFileChangeTime">
+        update SYS_BASE_CONFIG set LAST_TIME = #{dateTime}, CONFIG_VALUE = date(#{plusDayTime}) where CONFIG_KEY = 'ds_svg_file_config'
+    </update>
+
+    <update id="updateDistMasterStationFile">
+        update fhzg_dist_master_station_file set del_flag = '1' where del_flag = '0'
+        and file_path in
+        <foreach collection='fileNames' item='item' open='(' separator=',' close=')'>
+            #{item}
+        </foreach>
+    </update>
+
+    <select id="getConfigValueFileChange" resultType="java.lang.String">
+        select CONFIG_VALUE from SYS_BASE_CONFIG where CONFIG_KEY = 'ds_svg_file_config' limit 1
+    </select>
+
+    <select id="getDistMasterStationFile" resultType="java.lang.String">
+        select file_path from fhzg_dist_master_station_file where del_flag = '0' limit #{size}
+    </select>
+
+    <select id="getAllDistMasterStationFile" resultType="com.hdkj.lt.modle.FileUploadDTO">
+        select file_path file_name, max(upload_time) upload_time
+        from fhzg_dist_master_station_file s join (select config_key, max(LAST_TIME) conf_time
+            from SYS_BASE_CONFIG group by CONFIG_KEY) c on s.upload_time >= c.conf_time
+        where c.config_key = 'ds_svg_file_config' group by file_path
+    </select>
+
+</mapper>

+ 11 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IDealFeederBaseService.java

@@ -0,0 +1,11 @@
+package com.hdkj.lt.job.service;
+
+/**
+ * @author rj
+ * @date 2025/12/22 15:21
+ * @description
+ */
+public interface IDealFeederBaseService {
+
+    void dealFeederBase();
+}

+ 30 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IDealPsrDeviceService.java

@@ -0,0 +1,30 @@
+package com.hdkj.lt.job.service;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/7/30 15:11
+ * @Version 1.0
+ */
+public interface IDealPsrDeviceService {
+    /**
+     * 保存中压接入点和专变的数据
+     */
+    void saveJoinEcTransformerInfo(String cityShort);
+
+    /**
+     * 馈线和档距段在中台接入前备份
+     */
+    void dealFeederSpanSegmentBak();
+
+    /**
+     * 馈线和档距段根据备份表补充中台字段
+     */
+    void dealFeederSpanSegmentSupply();
+
+    /**
+     * 保存导线、电缆段台账信息
+     */
+    void saveSegmentPsrDeviceInfo(String cityShort);
+
+}

+ 15 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IDistOSSSvgFileService.java

@@ -0,0 +1,15 @@
+package com.hdkj.lt.job.service;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/6/19 15:53
+ * @Version 1.0
+ */
+public interface IDistOSSSvgFileService {
+
+    void saveBatchFileInfo(String fileDir);
+
+    void getAndSaveFileInfo(String fileDir);
+
+}

+ 72 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/IPsrDeviceSaveService.java

@@ -0,0 +1,72 @@
+package com.hdkj.lt.job.service;
+
+import com.hdkj.lt.modle.po.DistDeviceJoinEcPO;
+import com.hdkj.lt.modle.po.JoinEcTransformerPO;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/9/5 9:40
+ * @Version 1.0
+ */
+public interface IPsrDeviceSaveService {
+
+    /**
+     * 获取中压接入点信息(弃用)
+     */
+    List<String> getDistJoinEcBaseInfo();
+
+    /**
+     * 根据地市简称获取地市ID信息
+     */
+    List<String> getConfigCityInfo(List<String> cityShortlist);
+
+    /**
+     * 更新中压接入点的台账信息
+     */
+    int saveDistDeviceJoinEc(List<DistDeviceJoinEcPO> joinEcList);
+
+    /**
+     * 更新中压接入点和专变的台账信息
+     */
+    int saveDistJoinEcTransformer(List<JoinEcTransformerPO> saveList);
+
+    /**
+     * 更新DI同步之前的馈线和档距段台账信息
+     */
+    int savePsrDeviceDIBeforeBak();
+
+    /**
+     * 更新DI同步后丢失的馈线台账信息转义字段信息
+     */
+    int updateFeederDIAfter();
+
+    /**
+     * 更新T_PSR_DS_FEEDER拓扑解析需要的线段信息
+     */
+    int updateFeederPsrDevice();
+
+    /**
+     * 更新DI同步后丢失的档距段台账信息转义字段信息
+     */
+    int updateSpanSegmentDIAfter();
+
+    /**
+     * 保存更新导线设备台账信息
+     */
+    int saveDistConductorSegment(List<Map<String, Object>> deviceList);
+
+    /**
+     * 保存更新电缆段设备台账信息
+     */
+    int saveDistCableSegment(List<Map<String, Object>> deviceList);
+
+    /**
+     * 更新T_PSR_DS_DX拓扑解析需要的线段信息
+     */
+    int updateSegmentPsrDevice();
+
+}

+ 126 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/DealFeederBaseServiceImpl.java

@@ -0,0 +1,126 @@
+package com.hdkj.lt.job.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.hdkj.lt.base.exception.BusinessException;
+import com.hdkj.lt.core.bizms.modle.po.DwdShbDsFeederBase;
+import com.hdkj.lt.core.sys.service.DwdShbDsFeederBaseService;
+import com.hdkj.lt.job.entity.convert.EntityConvertor;
+import com.hdkj.lt.job.entity.po.FeederBaseRequest;
+import com.hdkj.lt.job.entity.po.FeederBaseResponse;
+import com.hdkj.lt.job.service.IDealFeederBaseService;
+import com.hdkj.lt.utils.cache.RedisUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author rj
+ * @date 2025/12/22 15:21
+ * @description
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DealFeederBaseServiceImpl implements IDealFeederBaseService {
+
+    @Qualifier("")
+    private final RestTemplate restTemplate;
+
+    private final RedisUtil redisUtil;
+
+    private final DwdShbDsFeederBaseService dwdShbDsFeederBaseService;
+
+    @Value("${feeder.cityCode}")
+    private List<String> cityCode;
+
+    @Override
+    public void dealFeederBase() {
+        log.info("开始同步FeederBase的cityCode:{}", cityCode);
+//        List<String> cityCode = Arrays.asList("42408", "42401", "4240917", "42402", "424090101", "4241105", "42405", "42409", "4240701", "42404", "42407", "42403", "42414", "42410", "42411", "42412", "42413", "4240605", "BD69707EA19E4FED85C595AFB5D06720", "A2EE03079A3A4D66A4036953EFDF782C", "E9BA9EE1E2814073BF8680780695261D", "67095412C3444BCE8FB3A8026C923EC0", "B25F17410548424D9AD6904A5F96CE7C", "22EC118826294148A4BAFA893AEA1B4C", "6592E8EEB2504C189B7C2F16DD177A31", "983CB2BA4CBA4526A8BAC471468831BD", "62F1D16638414148AFAD6AAA47154C9A", "A543F8924BAC46DC8B519E367EFC6231", "0BD945EF20A2424987DD2B411E873870", "6C36C5CFA9F947DFA88FED2B42E2816A", "E67DB57A13F342E1B45D5AF3641BA85C", "3D3F836CBD9F406185B96D37D3A04E5C", "424030302", "424070111", "4241206", "4241202", "424091701", "424110505", "4240401", "42406", "424060111", "4240603", "4240601", "4240311", "424070108", "4241401", "424080103", "424120212", "4240705", "424090106", "4241303", "4241302", "424060402", "4240604", "4241009", "424140101", "424060404", "424060304", "424120601", "4240901");
+        cityCode.forEach(code -> {
+            FeederBaseResponse feederBaseResponse = getFeederBaseDate(1, 100, code);
+            List<DwdShbDsFeederBase> dwdShbDsFeederBaseList = toDwdShbDsFeederBaseList(feederBaseResponse);
+            Integer pages = feederBaseResponse.getResult().getDkx().getPages();
+            log.info("地市编码:{},共{}条", code, feederBaseResponse.getResult().getDkx().getTotal());
+            for (Integer i = 2; i <= pages; i++) {
+                dwdShbDsFeederBaseList.addAll(toDwdShbDsFeederBaseList(getFeederBaseDate(i, 100, code)));
+            }
+            dwdShbDsFeederBaseService.remove(new LambdaUpdateWrapper<DwdShbDsFeederBase>().eq(DwdShbDsFeederBase::getCity, code));
+            dwdShbDsFeederBaseService.saveBatch(dwdShbDsFeederBaseList);
+            log.info("地市编码:{},{}条同步完成", code, dwdShbDsFeederBaseList.size());
+        });
+    }
+
+    private List<DwdShbDsFeederBase> toDwdShbDsFeederBaseList(FeederBaseResponse feederBaseResponse) {
+        List<DwdShbDsFeederBase> result = new ArrayList<>();
+        feederBaseResponse.getResult().getDkx().getRecords().forEach(i -> {
+            FeederBaseResponse.ResultDTO.DkxDTO.RecordsDTO.ResourceDTO resource = i.getResource();
+            FeederBaseResponse.ResultDTO.DkxDTO.RecordsDTO.AssetsDTO assets = i.getAssets().get(0);
+            DwdShbDsFeederBase feederBase = EntityConvertor.INSTANCE.toDwdShbDsFeederBase(resource, assets);
+            result.add(feederBase);
+        });
+        return result;
+    }
+
+
+    private FeederBaseResponse getFeederBaseDate(Integer current, Integer pageSize, String cityCode) {
+        String token;
+        try {
+            token = this.getToken();
+        } catch (JsonProcessingException e) {
+            throw new BusinessException("获取电网业务资源部token失败");
+        }
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        headers.set("x-token", token);
+        FeederBaseRequest.ParamsDTO.FiltersDTO filtersDTO = new FeederBaseRequest.ParamsDTO.FiltersDTO();
+        filtersDTO.setCompare("in");
+        filtersDTO.setFieldName("city");
+        filtersDTO.setFieldValue(cityCode);
+        FeederBaseRequest.ParamsDTO paramsDTO = new FeederBaseRequest.ParamsDTO();
+        paramsDTO.setSize(pageSize);
+        paramsDTO.setCurrent(current);
+        paramsDTO.setFilters(Collections.singletonList(filtersDTO));
+        FeederBaseRequest feederBaseRequest = new FeederBaseRequest();
+        feederBaseRequest.setDistribution(0);
+        feederBaseRequest.setPsrType("dkx");
+        feederBaseRequest.setParams(paramsDTO);
+        HttpEntity<String> entity = new HttpEntity<>(JSON.toJSONString(feederBaseRequest), headers);
+        String s = restTemplate.postForObject("http://25.55.59.21:30002/PSRCenter/queryServices/listPropertiesByFilters", entity, String.class);
+        return JSON.parseObject(s, FeederBaseResponse.class);
+    }
+
+    private String getToken() throws JsonProcessingException {
+        String token = (String) redisUtil.get("PSRCenter_Token");
+        if (token == null) {
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            HttpEntity<String> entity = new HttpEntity<>("{\n" +
+                    "\t\"client_id\": \"HB9f177012e6e14351847e451d5816be\",\n" +
+                    "\t\"client_secret\": \"uJdJDL/L82uHT7JYlTTXyfUzsu/9zG6GQYATpGqOCjkWSadcX0we2wffjyTUYGsK\",\n" +
+                    "\t\"grant_type\": \"client_credentials\"\n" +
+                    "}", headers);
+            String s = restTemplate.postForObject("http://25.55.59.21:30002/baseCenter/oauth2/accessToken", entity, String.class);
+            ObjectMapper objectMapper = new ObjectMapper();
+            JsonNode resp = objectMapper.readTree(s);
+            String newToken = resp.get("access_token").asText();
+            redisUtil.set("PSRCenter_Token", newToken, 7200);
+            return newToken;
+        }
+        return token;
+    }
+}

+ 260 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/DealPsrDeviceServiceImpl.java

@@ -0,0 +1,260 @@
+package com.hdkj.lt.job.service.impl;
+
+import cn.hutool.core.map.MapUtil;
+import com.alibaba.fastjson.JSON;
+import com.hdkj.hussar.ApiResponse;
+import com.hdkj.lt.core.plugins.thread.MultiThreadExecuteService;
+import com.hdkj.lt.feign.IPsrDeviceApiClient;
+import com.hdkj.lt.job.service.IDealPsrDeviceService;
+import com.hdkj.lt.job.service.IPsrDeviceSaveService;
+import com.hdkj.lt.job.util.JobUtils;
+import com.hdkj.lt.modle.dto.psrdevice.JoinEcTransformerDTO;
+import com.hdkj.lt.modle.dto.psrdevice.PsrDeviceFilterDTO;
+import com.hdkj.lt.modle.dto.psrdevice.PsrDeviceQueryDTO;
+import com.hdkj.lt.modle.po.DistDeviceJoinEcPO;
+import com.hdkj.lt.modle.po.JoinEcTransformerPO;
+import com.hdkj.lt.modle.vo.psrdevice.PsrDeviceCommonVO;
+import com.hdkj.lt.modle.vo.psrdevice.PsrDeviceResponseVO;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/7/30 15:13
+ * @Version 1.0
+ */
+@Service
+@RequiredArgsConstructor
+public class DealPsrDeviceServiceImpl implements IDealPsrDeviceService {
+
+    private final IPsrDeviceApiClient psrDeviceApiClient;
+    private final IPsrDeviceSaveService psrDeviceSaveService;
+    private final MultiThreadExecuteService multiThreadExecute;
+
+    @Value("${hussar.psr.device.max-query-size:500}")
+    private Integer maxQuerySize; // 电网资源业务中台最大查询数
+    @Value("${hussar.psr.device.max-save-size:100}")
+    private Integer maxSaveSize; // 设备台账批量最大数
+
+    @Override
+    public void saveJoinEcTransformerInfo(String cityShort) {
+        // 开启线程 处理保存中压接入点和专变数据,主要防止等待时间过程导致hussar-job报莫名错误
+        CompletableFuture.runAsync(() -> {
+            List<String> cityList = this.getCityListByShort(cityShort);
+            // 先批量处理中压用户接入点的数据
+            this.dealPsrDeviceJoinEcData(cityList, maxQuerySize);
+            // 接入点和专变接口慢,这里单独处理中压接入点和专变,根据最大保存数设置查询数量
+            this.saveJoinEcTransformerData(maxSaveSize);
+        }, multiThreadExecute.getExecutor());
+    }
+
+    @Override
+    public void dealFeederSpanSegmentBak() {
+        // 先删后增DI同步之前的馈线和档距段台账信息
+        psrDeviceSaveService.savePsrDeviceDIBeforeBak();
+    }
+
+    @Override
+    public void dealFeederSpanSegmentSupply() {
+        // 更新馈线台账信息转义字段信息
+        psrDeviceSaveService.updateFeederDIAfter();
+        // 更新T_PSR_DS_FEEDER拓扑解析需要的线段信息
+        psrDeviceSaveService.updateFeederPsrDevice();
+        // 更新档距段台账信息转义字段信息
+        psrDeviceSaveService.updateSpanSegmentDIAfter();
+    }
+
+    @Override
+    public void saveSegmentPsrDeviceInfo(String cityShort) {
+        // 开启线程 处理导线和电缆段数据, 主要防止等待时间过程导致hussar-job报莫名错误
+        CompletableFuture.runAsync(() -> {
+            List<String> cityList = this.getCityListByShort(cityShort);
+            // 处理电缆段台账信息
+            this.dealPsrDeviceDataCommon(cityList, "0201", maxQuerySize);
+            // 处理导线台账信息
+            this.dealPsrDeviceDataCommon(cityList, "0101", maxQuerySize);
+            // 更新T_PSR_DS_DX拓扑解析需要的线段信息, 数据量大时直接mysql执行不需要等待结果
+            CompletableFuture.runAsync(() -> psrDeviceSaveService.updateSegmentPsrDevice());
+        }, multiThreadExecute.getExecutor());
+    }
+
+
+    /**
+     * 获取地市ID信息根据传递地市简称
+     */
+    private List<String> getCityListByShort(String cityShort) {
+        // 定时任务参数 地市简称
+        List<String> cityShortlist = Arrays.asList(cityShort.split(","));
+        List<String> cityList = psrDeviceSaveService.getConfigCityInfo(cityShortlist);
+        return cityList;
+    }
+
+    /**
+     * 处理中压接入点的数据获取
+     */
+    private void dealPsrDeviceJoinEcData(List<String> cityList, Integer batchSize) {
+        PsrDeviceQueryDTO psrDeviceQuery = new PsrDeviceQueryDTO();
+        psrDeviceQuery.setPsrType("370000").setDistribution(0); // 设置中压接入点类型和主配网标识
+        psrDeviceQuery.getParams().setSize(batchSize); // 设置分页大小
+        if (ObjectUtils.isNotEmpty(cityList)) {
+            // 添加过滤条件
+            List<PsrDeviceFilterDTO> filters = psrDeviceQuery.getParams().getFilters();
+            filters.add(PsrDeviceFilterDTO.builder().compare("in").fieldName("city")
+                    .fieldValue(cityList.stream().collect(Collectors.joining(","))).build());
+            // 设置查询的过滤条件
+            psrDeviceQuery.getParams().setFilters(filters);
+        }
+        // 获取第一页的数据并且确定总页数
+        ApiResponse<PsrDeviceResponseVO> psrDeviceResponse = psrDeviceApiClient.getPsrDeviceByCommonInterface(psrDeviceQuery);
+        Optional.ofNullable(psrDeviceResponse).filter(e -> ObjectUtils.isNotEmpty(e.getData())).ifPresent(e -> {
+            // 处理中压接入点和中压接入点专变的数据
+            this.dealPsrDeviceJoinEcAndTransformerData(e.getData());
+            Integer pages = e.getData().getPages(); // 中压接入点总页数
+            // 分页获取剩余的数据(从第二页开始)
+            for (int page = 2; page <= pages; page++) {
+                psrDeviceQuery.getParams().setCurrent(page); // 设置当前页数
+                ApiResponse<PsrDeviceResponseVO> deviceResponse = psrDeviceApiClient.getPsrDeviceByCommonInterface(psrDeviceQuery);
+                Optional.ofNullable(deviceResponse).filter(en -> ObjectUtils.isNotEmpty(en.getData()))
+                        .ifPresent(en -> this.dealPsrDeviceJoinEcAndTransformerData(en.getData()));
+            }
+        });
+    }
+
+    /**
+     * 处理中压接入点和中压接入点专变的数据(接入点专变接口响应慢,弃用一个查询数量配置)
+     */
+    private void dealPsrDeviceJoinEcAndTransformerData(PsrDeviceResponseVO psrDeviceResponse) {
+        List<PsrDeviceCommonVO> psrDeviceList = psrDeviceResponse.getRecords();
+        if (ObjectUtils.isNotEmpty(psrDeviceList)) {
+            try {
+                // 处理中压接入点数据
+                List<DistDeviceJoinEcPO> joinEcList = psrDeviceList.stream()
+                        .filter(e -> ObjectUtils.isNotEmpty(e))
+                        .map(e -> {
+                            Map<String, Object> dataMap = e.getResource();
+                            dataMap.putAll(Optional.ofNullable(e.getAssets()).map(en -> en.get(0)).orElse(MapUtil.newHashMap()));
+                            DistDeviceJoinEcPO distDeviceJoinEcPO = JSON.parseObject(JSON.toJSONString(dataMap), DistDeviceJoinEcPO.class);
+                            return distDeviceJoinEcPO;
+                        }).collect(Collectors.toList());
+                Optional.ofNullable(joinEcList).filter(e -> ObjectUtils.isNotEmpty(e))
+                        .ifPresent(e -> JobUtils.saveBatchData(e, maxSaveSize, en -> psrDeviceSaveService.saveDistDeviceJoinEc(en)));
+            } catch (Exception ex) {
+                ex.printStackTrace(); // 定时任务不需要频繁执行,遇到异常可以继续,防止直接程序停止
+            }
+            /*// 处理中压接入点和专变数据,中压接入点和专变接口响应慢这里弃用和通用接口共用一个配置查询数
+            List<String> psrIdList = psrDeviceList.stream().filter(en -> ObjectUtils.isNotEmpty(en.getResource()))
+                    .map(en -> MapUtil.getStr(en.getResource(),"psrId")).collect(Collectors.toList());
+            if (ObjectUtils.isNotEmpty(psrIdList)) {
+                this.dealJoinEcTransformerData(psrIdList);
+            }*/
+        }
+    }
+
+    /**
+     * 处理中压接入点和专变关联表数据
+     */
+    private void dealJoinEcTransformerData(List<String> queryPsrIdList) {
+        // 处理中压接入点和专变关联表数据
+        if (ObjectUtils.isNotEmpty(queryPsrIdList)) {
+            try {
+                JoinEcTransformerDTO joinEcTransformer = JoinEcTransformerDTO.builder().psrIds(queryPsrIdList).build();
+                ApiResponse<List<JoinEcTransformerPO>> joinEcTransformerInfo = psrDeviceApiClient.getJoinEcTransformerInfo(joinEcTransformer);
+                // 处理中压接入点和专变的数据:先删后增
+                Optional.ofNullable(joinEcTransformerInfo).filter(e -> ObjectUtils.isNotEmpty(e.getData()))
+                        .ifPresent(e -> JobUtils.saveBatchData(e.getData(), maxSaveSize, en -> psrDeviceSaveService.saveDistJoinEcTransformer(en)));
+            } catch (Exception ex) {
+                ex.printStackTrace(); // 定时任务不需要频繁执行,遇到异常可以继续,防止直接程序停止
+            }
+        }
+    }
+
+    /**
+     * 处理电网资源业务中台数据公共方法
+     */
+    private void dealPsrDeviceDataCommon(List<String> cityList, String psrType, Integer batchSize) {
+        PsrDeviceQueryDTO psrDeviceQuery = new PsrDeviceQueryDTO();
+        psrDeviceQuery.setPsrType(psrType).setDistribution(0); // 设置中压接入点类型和主配网标识
+        psrDeviceQuery.getParams().setSize(batchSize); // 设置分页大小
+        if (ObjectUtils.isNotEmpty(cityList)) {
+            // 添加过滤条件
+            List<PsrDeviceFilterDTO> filters = psrDeviceQuery.getParams().getFilters();
+            filters.add(PsrDeviceFilterDTO.builder().compare("in").fieldName("city")
+                    .fieldValue(cityList.stream().collect(Collectors.joining(","))).build());
+            // 设置查询的过滤条件
+            psrDeviceQuery.getParams().setFilters(filters);
+        }
+        // 获取第一页的数据并且确定总页数
+        ApiResponse<PsrDeviceResponseVO> psrDeviceResponse = psrDeviceApiClient.getPsrDeviceByCommonInterface(psrDeviceQuery);
+        Optional.ofNullable(psrDeviceResponse).filter(e -> ObjectUtils.isNotEmpty(e.getData())).ifPresent(e -> {
+            // 保存电网资源业务中台的台账数据
+            this.savePsrDeviceDataByPsrType(e.getData(), psrType);
+            Integer pages = e.getData().getPages(); // 中压接入点总页数
+            // 分页获取剩余的数据(从第二页开始)
+            for (int page = 2; page <= pages; page++) {
+                psrDeviceQuery.getParams().setCurrent(page); // 设置当前页数
+                ApiResponse<PsrDeviceResponseVO> deviceResponse = psrDeviceApiClient.getPsrDeviceByCommonInterface(psrDeviceQuery);
+                Optional.ofNullable(deviceResponse).filter(en -> ObjectUtils.isNotEmpty(en.getData()))
+                        .ifPresent(en -> this.savePsrDeviceDataByPsrType(en.getData(), psrType));
+            }
+        });
+    }
+
+    /**
+     * 保存电网资源业务中台的台账数据根据
+     * 根据psrType更换不同表
+     */
+    private void savePsrDeviceDataByPsrType(PsrDeviceResponseVO psrDeviceResponse, String psrType) {
+        List<PsrDeviceCommonVO> psrDeviceList = psrDeviceResponse.getRecords();
+        if (ObjectUtils.isNotEmpty(psrDeviceList)) {
+            try {
+                // 保存电网资源业务中台的台账数据
+                List<Map<String, Object>> psrDevices = psrDeviceList.stream()
+                        .filter(e -> ObjectUtils.isNotEmpty(e))
+                        .map(e -> {
+                            Map<String, Object> dataMap = e.getResource();
+                            dataMap.putAll(Optional.ofNullable(e.getAssets()).map(en -> en.get(0)).orElse(MapUtil.newHashMap()));
+                            return dataMap;
+                        }).collect(Collectors.toList());
+                Optional.ofNullable(psrDevices).filter(e -> ObjectUtils.isNotEmpty(e)).ifPresent(e -> {
+                    if (StringUtils.equals("0101", psrType)) { // 导线设备台账
+                        JobUtils.saveBatchData(e, maxSaveSize, en -> psrDeviceSaveService.saveDistConductorSegment(en));
+                    } else if (StringUtils.equals("0201", psrType)) { // 电缆段设备台账
+                        JobUtils.saveBatchData(e, maxSaveSize, en -> psrDeviceSaveService.saveDistCableSegment(en));
+                    }
+                });
+            } catch (Exception ex) {
+                ex.printStackTrace(); // 定时任务不需要频繁执行,遇到异常可以继续,防止直接程序停止
+            }
+        }
+    }
+
+    /**
+     * 获取中压接入点psrId,根据psrId获取接入点和专变信息存库
+     */
+    private void saveJoinEcTransformerData(Integer batchSize) {
+        // 查询专变的数据, 后期有需要可以改成分页
+        List<String> psrIdList = psrDeviceSaveService.getDistJoinEcBaseInfo();
+        // 批量查询中压用户和专变的关联数据
+        if (ObjectUtils.isNotEmpty(psrIdList)) {
+            Integer totalSize = psrIdList.size();
+            Integer length = (totalSize + batchSize - 1) / batchSize; // 批量获取电网业务中台的数据次数
+            for (int i = 0; i < length; i++) {
+                Integer startIndex = i * batchSize;
+                Integer endIndex = Math.min((i + 1) * batchSize, totalSize);
+                List<String> queryPsrIdList = psrIdList.subList(startIndex, endIndex);
+                this.dealJoinEcTransformerData(queryPsrIdList);
+            }
+        }
+    }
+}

+ 266 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/DistOSSSvgFileServiceImpl.java

@@ -0,0 +1,266 @@
+package com.hdkj.lt.job.service.impl;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.map.MapUtil;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.hdkj.hussar.ApiResponse;
+import com.hdkj.hussar.ResultCode;
+import com.hdkj.lt.base.constants.RocketMQConstants;
+import com.hdkj.lt.base.constants.SymbolConstants;
+import com.hdkj.lt.core.mq.client.RocketMQTemplate;
+import com.hdkj.lt.core.mq.config.RocketMQProperties;
+import com.hdkj.lt.core.mq.model.RocketMQMessageEntity;
+import com.hdkj.lt.feign.IFileApiClient;
+import com.hdkj.lt.job.entity.po.FhzgDistMappingFilePO;
+import com.hdkj.lt.job.mapper.DistOSSSvgFileMapper;
+import com.hdkj.lt.job.service.IDistOSSSvgFileService;
+import com.hdkj.lt.job.util.JobUtils;
+import com.hdkj.lt.modle.FileInfoDTO;
+import com.hdkj.lt.modle.FileRequest;
+import com.hdkj.lt.modle.FileUploadDTO;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.zip.ZipInputStream;
+
+import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/6/19 15:54
+ * @Version 1.0
+ */
+@Service
+@RefreshScope
+@RequiredArgsConstructor
+public class DistOSSSvgFileServiceImpl implements IDistOSSSvgFileService {
+
+    private final IFileApiClient fileApiClient;
+    private final DistOSSSvgFileMapper distOSSSvgFileMapper;
+    private final RocketMQProperties rocketMQProperties;
+    private final RocketMQTemplate rocketMQTemplate;
+
+    @Value("${lt.custom-storage:false}")
+    private Boolean customStorage; // 配电云主站定制化存储,默认不需要定制化,设置true表示开启
+    @Value("${lt.file.max-size:100}")
+    private Integer maxFileSize;
+
+    @Override
+    public void getAndSaveFileInfo(String fileDir) {
+        this.saveDistMasterStationFile(fileDir);
+    }
+
+    @Override
+    public void saveBatchFileInfo(String fileDir) {
+        List<FileUploadDTO> fileData = this.saveDistMasterStationFile(fileDir);
+        if (ObjectUtils.isNotEmpty(fileData)) {
+            List<String> lineIds = new ArrayList<>();
+            // 1.获取还需上传的异动文件信息
+            List<String> fileNameList = distOSSSvgFileMapper.getDistMasterStationFile(maxFileSize);
+            fileNameList.forEach(fileName -> {
+                String updateFileName = fileName.substring(fileName.lastIndexOf(SymbolConstants.FORWARD_SLASH) + 1);
+                // 2.获取单个下载文件
+                byte[] fileByte = fileApiClient.getObject(fileName).getData();
+                if (ObjectUtils.isNotEmpty(fileByte)) {
+                    // 转换内容成svg
+                    String fileContent = this.decodeBase64AndZipData(fileByte);
+                    FileRequest fileRequest = FileRequest.builder().fileName(updateFileName).fileContent(fileContent).build();
+                    // 3.上传文件到本地oss
+                    FileInfoDTO fileInfo = fileApiClient.upload(fileRequest).getData();
+                    lineIds.add(fileInfo.getOriginName().substring(0, fileInfo.getOriginName().lastIndexOf(".")));
+                    // 4.处理需要保存的上传文件信息
+                    this.dealDistMappingFile(fileInfo);
+
+                    /*// 5.删除源文件信息,先上传保存数据后再处理源文件防止异常后丢失源文件
+                    fileApiClient.removeObject(fileName);*/
+                }
+                fileByte = null; // 快速回收可能站内存的byte数组
+            });
+            // 5.改成不删除异动文件,逻辑删除上传文件信息
+            Optional.ofNullable(fileNameList).filter(ObjectUtils::isNotEmpty)
+                    .ifPresent(e -> distOSSSvgFileMapper.updateDistMasterStationFile(e));
+            // 当发现.txt文件,且异动文件已经上传完成时, 需要修改配置数据和mq消息订阅
+            List<FileUploadDTO> txtFile = fileData.stream().filter(e -> e.getFileName().endsWith(".txt"))
+                    .sorted(Comparator.comparing(FileUploadDTO::getUploadTime)).collect(Collectors.toList());
+            if (ObjectUtils.isNotEmpty(txtFile) && ObjectUtils.isEmpty(fileNameList)) {
+                // 删除配电云主站异动文件上传完成标识的.txt文件,同-配置时间段内只保留一个txt文件
+                txtFile.stream().limit(Math.max(0, txtFile.size() - 1)).forEach(e -> fileApiClient.removeObject(e.getFileName()));
+                LocalDateTime currentTime = LocalDateTime.now();// 当前时间
+                distOSSSvgFileMapper.updateConfigFileChangeTime(currentTime, currentTime.plusDays(1L));
+                // 解析TOPO数据的MQ消息订阅
+                this.sendRocketMQMessage(lineIds);
+            }
+        }
+    }
+
+    /**
+     * 保存配电云主站获取的文件信息
+     */
+    private List<FileUploadDTO> saveDistMasterStationFile(String fileDir) {
+        String configValue = distOSSSvgFileMapper.getConfigValueFileChange();
+        // 当配置值为0时, 表示当前上传任务未执行完成
+        if (LocalDate.now().compareTo(LocalDate.parse(configValue)) >= 0) {
+            // 获取全量异动文件,根据异动文件上传时间来确定是否更新数据
+            ApiResponse<List<FileUploadDTO>> harmony = fileApiClient.listObject(fileDir);
+            if (harmony.getCode() == ResultCode.SUCCESS.getCode()) {
+                List<FileUploadDTO> fileData = harmony.getData();
+                // 处理更换需要上传的最新的异动文件并保存文件记录
+                this.dealDistMasterStationFile(fileData);
+                return fileData;
+            }
+        }
+        return Lists.newArrayList();
+    }
+
+    /**
+     * 处理需要上传的配电云主站异动文件
+     */
+    private void dealDistMasterStationFile(List<FileUploadDTO> fileData) {
+        // 定制化后的需要上传文件集合
+        List<FileUploadDTO> fileNameList = Lists.newArrayList();
+        // 定制化存储时,查询配置地市的线路信息
+        List<String> lineIdList = Lists.newArrayList();
+        if (customStorage) {
+            lineIdList.addAll(Optional.ofNullable(distOSSSvgFileMapper.getCustomStorageLine()).orElse(lineIdList));
+        }
+        // 定制化逻辑筛选文件
+        fileData.forEach(file -> {
+            String updateFileName = file.getFileName().substring(file.getFileName().lastIndexOf(SymbolConstants.FORWARD_SLASH) + 1);
+            // 执行上传文件的标识, 和定制化配置值相反(开启定制化后,需要根据配置信息来判断是否上传)
+            Boolean uploadFlag = !customStorage;
+            // 配电云主站自定义开启时,判断文件是否需要上传
+            if (customStorage && ObjectUtils.isNotEmpty(lineIdList)) {
+                // 根据文件名截取线路ID
+                String lineId = updateFileName.substring(0, updateFileName.lastIndexOf("."));
+                uploadFlag = lineIdList.contains(lineId); // 重新设置自定义上传标识
+            }
+            if (uploadFlag) {
+                fileNameList.add(file);
+            }
+        });
+        // 获取配电已知的全量异动文件
+        Map<String, FileUploadDTO> uploadFileMap = Optional.ofNullable(distOSSSvgFileMapper.getAllDistMasterStationFile())
+                .map(e -> e.stream().collect(Collectors.toMap(en -> en.getFileName(), en -> en))).orElse(MapUtil.newHashMap());
+        // 云主站需要补充删除的文件信息
+        List<String> deleteHarmonyFile = Optional.ofNullable(fileNameList)
+                .map(e -> e.stream().filter(en -> !en.getFileName().endsWith(".txt"))
+                        .filter(en -> ObjectUtils.isNotEmpty(uploadFileMap.get(en.getFileName()))
+                                && en.getUploadTime().isAfter(uploadFileMap.get(en.getFileName()).getUploadTime()))
+                        .map(FileUploadDTO::getFileName).collect(Collectors.toList()))
+                .orElse(Lists.newArrayList());
+        // 配电云主站需要补充的文件信息
+        List<FileUploadDTO> harmonyFile = Optional.ofNullable(fileNameList)
+                .map(e -> e.stream().filter(en -> !en.getFileName().endsWith(".txt"))
+                        .filter(en -> !(ObjectUtils.isNotEmpty(uploadFileMap.get(en.getFileName()))
+                                && en.getUploadTime().compareTo(uploadFileMap.get(en.getFileName()).getUploadTime()) <= 0))
+                        .collect(Collectors.toList()))
+                .orElse(Lists.newArrayList());
+        // 先逻辑删除文件,在增加最新的需要上传的文件
+        Optional.ofNullable(deleteHarmonyFile).filter(ObjectUtils::isNotEmpty)
+                .ifPresent(e -> JobUtils.saveBatchData(e, 2000, en -> this.updateDistMasterStationFile(en)));
+        Optional.ofNullable(harmonyFile).filter(ObjectUtils::isNotEmpty)
+                .ifPresent(e -> JobUtils.saveBatchData(e, 2000, en -> this.insertDistMasterStationFile(en)));
+    }
+
+    /**
+     * 处理业务数据信息
+     */
+    private void dealDistMappingFile(FileInfoDTO fileInfo) {
+        // 线路ID来源文件名
+        String lineId = fileInfo.getOriginName().substring(0, fileInfo.getOriginName().lastIndexOf("."));
+        // 文件类型来源文件名后缀
+        String fileType = (StringUtils.equals(fileInfo.getSuffix(), "svg") ? "1" : "2");
+
+        // 删除线路附件映射表文件信息
+        LambdaUpdateWrapper<FhzgDistMappingFilePO> deleteWrapper = new LambdaUpdateWrapper<FhzgDistMappingFilePO>()
+                .eq(FhzgDistMappingFilePO::getLineId, lineId)
+                .eq(FhzgDistMappingFilePO::getFileType, fileType);
+        distOSSSvgFileMapper.delete(deleteWrapper);
+
+        // 新增线路附件映射表文件信息
+        FhzgDistMappingFilePO distMappingFile = new FhzgDistMappingFilePO();
+        distMappingFile.setFileId(Long.valueOf(fileInfo.getId()))
+                .setLineId(lineId).setFileType(fileType)
+                .setTopoVersion(LocalDate.now().format(DateTimeFormatter.ofPattern(PURE_DATE_PATTERN)))
+                .setCreateTime(LocalDateTime.now());
+        distOSSSvgFileMapper.insert(distMappingFile);
+    }
+
+    /**
+     * 修改配电云主站已处理文件
+     */
+    private Boolean updateDistMasterStationFile(List<String> fileNameList) {
+        distOSSSvgFileMapper.updateDistMasterStationFile(fileNameList);
+        return Boolean.TRUE;
+    }
+
+    /**
+     * 新增配电云主站需要处理文件
+     */
+    private Boolean insertDistMasterStationFile(List<FileUploadDTO> fileList) {
+        distOSSSvgFileMapper.insertDistMasterStationFile(fileList, LocalDateTime.now());
+        return Boolean.TRUE;
+    }
+
+    /**
+     * 解析文件base64+zip的文件转换成文件内容
+     */
+    private String decodeBase64AndZipData(byte[] fileByte) {
+        try (ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(fileByte));
+             ZipInputStream zis = new ZipInputStream(bais);
+             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+
+            // ZIP解压(根据现有信息假设只有一个文件)
+            zis.getNextEntry(); //定位到压缩包中的第一个文件
+
+            // 读取解压后的数据
+            byte[] buffer = new byte[1024];
+            int len;
+            while ((len = zis.read(buffer)) > 0) {
+                baos.write(buffer, 0, len);
+            }
+
+            // 字节转字符串(默认UTF-8)
+            String fileContent = new String(baos.toByteArray(), StandardCharsets.UTF_8);
+            return fileContent;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return new String(fileByte, StandardCharsets.UTF_8);
+        }
+    }
+
+
+    /**
+     * 解析TOPO数据的MQ消息订阅
+     */
+    private void sendRocketMQMessage(List<String> lineIds) {
+        try {
+            // MQ消息订阅,当触发消息发送失败时不影响接口功能
+            if (rocketMQProperties.getEnabled()) {
+                RocketMQMessageEntity messageEntity = RocketMQMessageEntity.builder()
+                        .topicId(RocketMQConstants.TOPIC_ID_DO_CITY_FEEDER_TOPO_TICKET)
+                        .cacheKey(RocketMQConstants.TOPIC_ID_DO_CITY_FEEDER_TOPO_TICKET)
+                        .tag("1")
+                        .body(lineIds)
+                        .build();
+                rocketMQTemplate.sendAsyncMessage(messageEntity);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 97 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/PsrDeviceSaveServiceImpl.java

@@ -0,0 +1,97 @@
+package com.hdkj.lt.job.service.impl;
+
+import com.hdkj.lt.job.mapper.DealPsrDeviceMapper;
+import com.hdkj.lt.job.service.IPsrDeviceSaveService;
+import com.hdkj.lt.modle.po.DistDeviceJoinEcPO;
+import com.hdkj.lt.modle.po.JoinEcTransformerPO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description
+ * @Author wcx
+ * @Date 2025/9/5 9:41
+ * @Version 1.0
+ */
+@Service
+@RequiredArgsConstructor
+public class PsrDeviceSaveServiceImpl implements IPsrDeviceSaveService {
+
+    private final DealPsrDeviceMapper dealPsrDeviceMapper;
+
+    @Override
+    public List<String> getDistJoinEcBaseInfo() {
+        return dealPsrDeviceMapper.getDistJoinEcBaseInfo();
+    }
+
+    @Override
+    public List<String> getConfigCityInfo(List<String> cityShortlist) {
+        return dealPsrDeviceMapper.getConfigCityInfo(cityShortlist);
+    }
+
+    @Override
+
+    public int saveDistDeviceJoinEc(List<DistDeviceJoinEcPO> joinEcList) {
+        // 保存更新中压接入点的台账信息
+        return dealPsrDeviceMapper.saveDistDeviceJoinEc(joinEcList);
+    }
+
+    @Override
+
+    public int saveDistJoinEcTransformer(List<JoinEcTransformerPO> deviceList) {
+        // 保存更新中压接入点和专变的台账信息
+        return dealPsrDeviceMapper.saveDistJoinEcTransformer(deviceList);
+    }
+
+    @Override
+
+    public int savePsrDeviceDIBeforeBak() {
+        // 先删后增DI同步之前的馈线和档距段台账信息
+        return dealPsrDeviceMapper.savePsrDeviceDIBeforeBak();
+    }
+
+    @Override
+
+    public int updateFeederDIAfter() {
+        // 更新DI同步后丢失的馈线台账信息转义字段信息
+        return dealPsrDeviceMapper.updateFeederDIAfter();
+    }
+
+    @Override
+
+    public int updateFeederPsrDevice() {
+        // 更新T_PSR_DS_FEEDER拓扑解析需要的线段信息
+        return dealPsrDeviceMapper.updateFeederPsrDevice();
+    }
+
+    @Override
+
+    public int updateSpanSegmentDIAfter() {
+        // 更新DI同步后丢失的档距段台账信息转义字段信息
+        return dealPsrDeviceMapper.updateSpanSegmentDIAfter();
+    }
+
+    @Override
+
+    public int saveDistConductorSegment(List<Map<String, Object>> deviceList) {
+        // 保存更新导线设备台账信息
+        return dealPsrDeviceMapper.saveDistConductorSegment(deviceList);
+    }
+
+    @Override
+
+    public int saveDistCableSegment(List<Map<String, Object>> deviceList) {
+        // 保存更新电缆段设备台账信息
+        return dealPsrDeviceMapper.saveDistCableSegment(deviceList);
+    }
+
+    @Override
+
+    public int updateSegmentPsrDevice() {
+        // 更新T_PSR_DS_DX拓扑解析需要的线段信息
+        return dealPsrDeviceMapper.updateSegmentPsrDevice();
+    }
+}

+ 1 - 1
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/SysJobLogServiceImpl.java → services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/SysJobLogServiceImpl.java

@@ -1,4 +1,4 @@
-package com.hdkj.lt.job.service;
+package com.hdkj.lt.job.service.impl;
 
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;

+ 2 - 1
services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/SysJobServiceImpl.java → services/ruoyi-job/src/main/java/com/hdkj/lt/job/service/impl/SysJobServiceImpl.java

@@ -1,10 +1,11 @@
-package com.hdkj.lt.job.service;
+package com.hdkj.lt.job.service.impl;
 
 import java.util.List;
 import javax.annotation.PostConstruct;
 
 import com.hdkj.lt.job.domain.SysJob;
 import com.hdkj.lt.job.mapper.SysJobMapper;
+import com.hdkj.lt.job.service.ISysJobService;
 import org.quartz.JobDataMap;
 import org.quartz.JobKey;
 import org.quartz.Scheduler;

+ 22 - 0
services/ruoyi-job/src/main/java/com/hdkj/lt/job/task/StruTreeCacheRefreshProcessor.java

@@ -0,0 +1,22 @@
+package com.hdkj.lt.job.task;
+
+import com.hdkj.lt.core.sys.service.ISysStruService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component("struTreeCacheRefreshProcessor")
+@RequiredArgsConstructor
+public class StruTreeCacheRefreshProcessor {
+
+    private final ISysStruService sysStruService;
+
+    public void noParams() {
+        log.info("=====组织机构树缓存刷新定时任务=====");
+        long startTime = System.nanoTime();
+        sysStruService.refreshStruTreeByCache();
+        long endTime = System.nanoTime();
+        log.info("=====组织机构树缓存刷新定时任务执行完成耗时:{}ms=====", (endTime - startTime) / 1_000_000);
+    }
+}