瀏覽代碼

Merge remote-tracking branch 'origin/dev_20251130' into dev_20251130

donglang 1 月之前
父節點
當前提交
f86550c1d7
共有 27 個文件被更改,包括 1629 次插入42 次删除
  1. 4 0
      sckw-modules-api/sckw-transport-api/pom.xml
  2. 47 0
      sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/feign/VehicleTraceClient.java
  3. 64 0
      sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/feign/VehicleTraceFeignConfig.java
  4. 37 0
      sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/dto/VehicleDataDTO.java
  5. 82 0
      sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/dto/VehicleReturnData.java
  6. 42 0
      sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/vo/VehicleTraceResponse.java
  7. 2 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/FleetApplication.java
  8. 28 18
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/service/KwfTruckService.java
  9. 23 0
      sckw-modules/sckw-fleet/src/main/resources/bootstrap.yml
  10. 98 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/SysDictFlexController.java
  11. 78 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/SysDictTypeFlexController.java
  12. 2 1
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/SysDictDao.java
  13. 2 1
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/SysDictTypeDao.java
  14. 152 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SysDictReqVo.java
  15. 92 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SysDictTypeReqVo.java
  16. 130 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SysDictResp.java
  17. 84 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SysDictTypeResp.java
  18. 8 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/repository/KwsEnterpriseRepository.java
  19. 125 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/repository/SysDictRepository.java
  20. 74 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/repository/SysDictTypeRepository.java
  21. 6 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsEnterpriseService.java
  22. 231 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/SysDictFlexBusinessService.java
  23. 138 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/SysDictTypeService.java
  24. 2 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/TransPortApplication.java
  25. 26 8
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtWaybillOrderV1Service.java
  26. 29 14
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/kwfTruckTraceService.java
  27. 23 0
      sckw-modules/sckw-transport/src/main/resources/bootstrap.yml

+ 4 - 0
sckw-modules-api/sckw-transport-api/pom.xml

@@ -38,5 +38,9 @@
             <version>2.2.22</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-core</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 47 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/feign/VehicleTraceClient.java

@@ -0,0 +1,47 @@
+package com.sckw.transport.api.feign;
+
+import com.sckw.core.web.response.BaseResult;
+import com.sckw.transport.api.model.dto.VehicleDataDTO;
+import com.sckw.transport.api.model.vo.VehicleTraceResponse;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+/**
+ * 车辆轨迹服务 Feign 客户端
+ * 调用数据中台的车辆轨迹接口
+ * @author system
+ * @date 2024
+ */
+@FeignClient(
+    name = "iot-platform",
+    url = "${vehicle.trace.api.base-url}",
+    path = "/api/transfer",
+    configuration = VehicleTraceFeignConfig.class
+)
+public interface VehicleTraceClient {
+
+    /**
+     * 查询实时位置
+     * @param vehicleDataDTO 车辆数据
+     * @return 车辆位置信息
+     */
+    @PostMapping("/queryRealTimeLocation")
+    BaseResult<VehicleTraceResponse> queryRealTimeLocation(@RequestBody VehicleDataDTO vehicleDataDTO);
+
+    /**
+     * 查询车辆轨迹列表
+     * @param vehicleDataDTO 车辆数据
+     * @return 车辆轨迹列表
+     */
+    @PostMapping("/queryVehicleDataList")
+    BaseResult<VehicleTraceResponse> queryVehicleDataList(@RequestBody VehicleDataDTO vehicleDataDTO);
+
+    /**
+     * 上报车辆轨迹
+     * @param request 车辆轨迹数据
+     * @return 上报结果
+     */
+    @PostMapping("/saveVehicleData")
+    BaseResult<Void> saveVehicleData(@RequestBody Object request);
+}

+ 64 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/feign/VehicleTraceFeignConfig.java

@@ -0,0 +1,64 @@
+package com.sckw.transport.api.feign;
+
+import feign.Logger;
+import feign.Request;
+import feign.Retryer;
+import feign.codec.ErrorDecoder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 车辆轨迹服务 Feign 配置
+ * @author system
+ * @date 2024
+ */
+@Slf4j
+@Configuration
+public class VehicleTraceFeignConfig {
+
+    /**
+     * 配置日志级别
+     */
+    @Bean
+    public Logger.Level feignLoggerLevel() {
+        return Logger.Level.FULL;
+    }
+
+    /**
+     * 配置请求超时时间
+     * 连接超时:5秒
+     * 读取超时:10秒
+     */
+    @Bean
+    public Request.Options options() {
+        return new Request.Options(
+            5000, TimeUnit.MILLISECONDS,  // 连接超时
+            10000, TimeUnit.MILLISECONDS, // 读取超时
+            true  // 跟随重定向
+        );
+    }
+
+    /**
+     * 配置重试策略
+     * 最多重试2次,初始间隔100ms,最大间隔1000ms
+     */
+    @Bean
+    public Retryer retryer() {
+        return new Retryer.Default(100, 1000, 2);
+    }
+
+    /**
+     * 自定义错误解码器
+     */
+    @Bean
+    public ErrorDecoder errorDecoder() {
+        return (methodKey, response) -> {
+            log.error("车辆轨迹服务调用失败, 方法: {}, 状态码: {}", 
+                methodKey, response.status());
+            return new RuntimeException("车辆轨迹服务调用失败: " + response.reason());
+        };
+    }
+}

+ 37 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/dto/VehicleDataDTO.java

@@ -0,0 +1,37 @@
+package com.sckw.transport.api.model.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 车辆数据请求 DTO
+ * @author system
+ * @date 2024
+ */
+@Data
+public class VehicleDataDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运单号
+     */
+    private String wOrderNo;
+
+    /**
+     * 车牌号
+     */
+    private String carNo;
+
+    /**
+     * 开始时间
+     */
+    private LocalDateTime startTime;
+
+    /**
+     * 结束时间
+     */
+    private LocalDateTime endTime;
+}

+ 82 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/dto/VehicleReturnData.java

@@ -0,0 +1,82 @@
+package com.sckw.transport.api.model.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 车辆返回数据
+ * @author system
+ * @date 2024
+ */
+@Data
+public class VehicleReturnData implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 时间戳
+     */
+    private LocalDateTime ts;
+
+    /**
+     * 车牌号
+     */
+    private String carNo;
+
+    /**
+     * 经度
+     */
+    private String longitude;
+
+    /**
+     * 纬度
+     */
+    private String latitude;
+
+    /**
+     * 车速(km/h)
+     */
+    private Float speed;
+
+    /**
+     * 行驶方向(角度)
+     */
+    private Integer direction;
+
+    /**
+     * 燃油液位(%)
+     */
+    private Float fuelLevel;
+
+    /**
+     * 里程数(km)
+     */
+    private Double mileage;
+
+    /**
+     * 发动机温度(℃)
+     */
+    private Float engineTemp;
+
+    /**
+     * 电池电压(V)
+     */
+    private Float batteryVoltage;
+
+    /**
+     * 车辆状态
+     */
+    private String status;
+
+    /**
+     * 运单号
+     */
+    private String wOrderNo;
+
+    /**
+     * 报警代码
+     */
+    private Integer alarmCode;
+}

+ 42 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/vo/VehicleTraceResponse.java

@@ -0,0 +1,42 @@
+package com.sckw.transport.api.model.vo;
+
+import com.sckw.transport.api.model.dto.VehicleReturnData;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 车辆轨迹响应对象
+ * @author system
+ * @date 2024
+ */
+@Data
+public class VehicleTraceResponse implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 单个车辆位置数据
+     */
+    private VehicleReturnData data;
+
+    /**
+     * 车辆轨迹列表数据
+     */
+    private List<VehicleReturnData> dataList;
+    
+    /**
+     * 兼容方法:获取列表数据
+     * 如果 dataList 不为空则返回 dataList,否则返回包含 data 的列表
+     */
+    public List<VehicleReturnData> getDataList() {
+        if (dataList != null) {
+            return dataList;
+        }
+        if (data != null) {
+            return List.of(data);
+        }
+        return List.of();
+    }
+}

+ 2 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/FleetApplication.java

@@ -3,6 +3,7 @@ package com.sckw.fleet;
 import com.sckw.remote.annotation.SckwRemoteApplication;
 import com.sckw.startup.annotation.SckwCloudApplication;
 import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 
 /**
  * desc 车队
@@ -11,6 +12,7 @@ import org.springframework.boot.SpringApplication;
  */
 @SckwRemoteApplication
 @SckwCloudApplication
+@EnableFeignClients(basePackages = "com.sckw.transport.api.feign")
 public class FleetApplication {
 
     public static void main(String[] args) {

+ 28 - 18
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/service/KwfTruckService.java

@@ -27,6 +27,7 @@ import com.sckw.core.web.context.LoginEntHolder;
 import com.sckw.core.web.context.LoginUserHolder;
 import com.sckw.core.web.request.HttpClientUtil;
 import com.sckw.core.web.response.HttpResult;
+import com.sckw.core.web.response.BaseResult;
 import com.sckw.core.web.response.result.PageDataResult;
 import com.sckw.excel.easyexcel.ExcelImportListener;
 import com.sckw.excel.utils.ExcelUtil;
@@ -44,9 +45,13 @@ import com.sckw.system.api.model.dto.res.KwsEnterpriseResDto;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
 import com.sckw.system.api.model.dto.res.UserCacheResDto;
 import com.sckw.transport.api.dubbo.TransportRemoteService;
+import com.sckw.transport.api.feign.VehicleTraceClient;
 import com.sckw.transport.api.model.dto.RWaybillOrderDto;
+import com.sckw.transport.api.model.dto.VehicleDataDTO;
+import com.sckw.transport.api.model.dto.VehicleReturnData;
 import com.sckw.transport.api.model.vo.RTruckMonitorVo;
 import com.sckw.transport.api.model.vo.RWaybillOrderVo;
+import com.sckw.transport.api.model.vo.VehicleTraceResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
@@ -95,6 +100,7 @@ public class KwfTruckService {
     private final KwfTruckAxleNumRepository truckAxleNumRepository;
 
     private final KwfFleetTruckRepository kwfFleetTruckRepository;
+    private final VehicleTraceClient vehicleTraceClient;
     @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
     private RemoteSystemService remoteSystemService;
     @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
@@ -1620,37 +1626,41 @@ public class KwfTruckService {
     private VehicleReturnData getVehicleReturnDataByOrderNo(String odrderNo) {
         VehicleDataDTO vehicleDataDTO = new VehicleDataDTO();
         vehicleDataDTO.setWOrderNo(odrderNo);
-        String res = null;
+        
         try {
-            res = HttpUtil.postJson(urlConfigProperties.getApiBaseUrl()+ UrlConstants.QUERY_REAL_TIME_TRACE_URL, JSON.toJSONString(vehicleDataDTO), null);
+            // 使用 Feign 调用查询实时位置
+            BaseResult<VehicleTraceResponse> result = vehicleTraceClient.queryRealTimeLocation(vehicleDataDTO);
+            
+            if (result == null || result.getCode() != HttpStatus.SUCCESS_CODE || result.getData() == null) {
+                log.warn("查询实时轨迹返回空数据, 运单号: {}", odrderNo);
+                return null;
+            }
+            
+            return result.getData().getData();
         } catch (Exception e) {
-            log.error("查询任务轨迹异常",e);
-            return null;
-            // throw new BusinessException("查询任务轨迹异常");
-        }
-        if (org.apache.commons.lang3.StringUtils.isBlank(res)){
+            log.error("查询任务轨迹异常, 运单号: {}", odrderNo, e);
             return null;
         }
-        JSONObject jsonObject  = JSON.parseObject(res);
-        return JSON.parseObject(JSON.toJSONString(jsonObject.get("data")), VehicleReturnData.class);
     }
 
     public VehicleReturnData getVehicleReturnData(String truckId) {
         VehicleDataDTO vehicleDataDTO = new VehicleDataDTO();
         vehicleDataDTO.setCarNo(truckId);
-        String res = null;
+        
         try {
-            res = HttpUtil.postJson(urlConfigProperties.getApiBaseUrl()+ UrlConstants.QUERY_REAL_TIME_TRACE_URL, JSON.toJSONString(vehicleDataDTO), null);
+            // 使用 Feign 调用查询实时位置
+            BaseResult<VehicleTraceResponse> result = vehicleTraceClient.queryRealTimeLocation(vehicleDataDTO);
+            
+            if (result == null || result.getCode() != HttpStatus.SUCCESS_CODE || result.getData() == null) {
+                log.warn("查询实时轨迹返回空数据, 车牌号: {}", truckId);
+                return null;
+            }
+            
+            return result.getData().getData();
         } catch (Exception e) {
-            log.error("查询任务轨迹异常",e);
-            return null;
-           // throw new BusinessException("查询任务轨迹异常");
-        }
-        if (org.apache.commons.lang3.StringUtils.isBlank(res)){
+            log.error("查询任务轨迹异常, 车牌号: {}", truckId, e);
             return null;
         }
-        JSONObject jsonObject  = JSON.parseObject(res);
-        return JSON.parseObject(JSON.toJSONString(jsonObject.get("data")), VehicleReturnData.class);
     }
 
     public PageDataResult<AppTruckInfoVo> pageAppTruckInfo(TruckInfoReq req) {

+ 23 - 0
sckw-modules/sckw-fleet/src/main/resources/bootstrap.yml

@@ -10,6 +10,29 @@ spring:
   main:
     allow-bean-definition-overriding: true
     allow-circular-references: true
+
+# 车辆轨迹服务配置
+vehicle:
+  trace:
+    api:
+      base-url: ${VEHICLE_TRACE_API_URL:http://localhost:8080}
+
+# Feign 配置
+feign:
+  client:
+    config:
+      default:
+        connect-timeout: 5000
+        read-timeout: 10000
+      vehicle-trace-service:
+        connect-timeout: 5000
+        read-timeout: 10000
+        logger-level: full
+  httpclient:
+    enabled: true
+    max-connections: 200
+    max-connections-per-route: 50
+
   cloud:
     nacos:
       discovery:

+ 98 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/SysDictFlexController.java

@@ -0,0 +1,98 @@
+package com.sckw.system.controller;
+
+import com.sckw.core.web.response.BaseResult;
+import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.system.model.vo.req.SysDictReqVo;
+import com.sckw.system.model.vo.res.SysDictResp;
+import com.sckw.system.service.SysDictFlexBusinessService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 字典控制器 (MyBatis-Flex)
+ * @author system
+ * @date 2024
+ */
+@Tag(name = "字典管理")
+@RestController
+@RequestMapping("/flex/sysDict")
+@RequiredArgsConstructor
+public class SysDictFlexController {
+
+    private final SysDictFlexBusinessService sysDictService;
+
+    /**
+     * 新增字典
+     */
+    @Operation(summary = "新增字典")
+    @PostMapping("/insert")
+    public BaseResult<Boolean> insert(@RequestBody @Valid SysDictReqVo reqVo) {
+        return BaseResult.success(sysDictService.insert(reqVo.getEntity()));
+    }
+
+
+
+
+
+    /**
+     * 逻辑删除字典
+     */
+    @Operation(summary = "逻辑删除字典")
+    @PostMapping("/logicDelete")
+    public BaseResult<Integer> logicDeleteById(@RequestBody SysDictReqVo reqVo) {
+        int rows = sysDictService.logicDeleteById(reqVo.getId());
+        return BaseResult.success(rows);
+    }
+
+    /**
+     * 更新字典
+     */
+    @Operation(summary = "更新字典")
+    @PostMapping("/update")
+    public BaseResult<Boolean> update(@RequestBody SysDictReqVo reqVo) {
+        return BaseResult.success(sysDictService.update(reqVo.getEntity()));
+    }
+    
+    /**
+     * 分页查询字典
+     */
+    @Operation(summary = "分页查询字典")
+    @PostMapping("/selectPage")
+    public BaseResult<PageDataResult<SysDictResp>> selectPage(@RequestBody SysDictReqVo reqVo) {
+        return BaseResult.success(sysDictService.selectPage(reqVo));
+    }
+
+    /**
+     * 统计字典数量
+     */
+    @Operation(summary = "统计字典数量")
+    @PostMapping("/count")
+    public BaseResult<Long> count(@RequestBody SysDictReqVo reqVo) {
+        long count = sysDictService.count(reqVo.getType());
+        return BaseResult.success(count);
+    }
+
+    /**
+     * 查询上级字典单选下拉(支持输入搜索)
+     * 用于字典项选择父级字典
+     */
+    @Operation(summary = "查询上级字典下拉列表")
+    @PostMapping("/searchParentForSelect")
+    public BaseResult<List<SysDictResp>> searchParentForSelect(@RequestBody SysDictReqVo reqVo) {
+        return BaseResult.success(sysDictService.searchParentDictForSelect( reqVo));
+    }
+
+    /**
+     * 快捷更改字典状态(启用/停用)
+     */
+    @Operation(summary = "快捷更改字典状态")
+    @PostMapping("/updateStatus")
+    public BaseResult<Boolean> updateStatus(@RequestBody SysDictReqVo reqVo) {
+        return BaseResult.success(sysDictService.updateStatus(reqVo.getId(), reqVo.getStatus()));
+    }
+}

+ 78 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/SysDictTypeFlexController.java

@@ -0,0 +1,78 @@
+package com.sckw.system.controller;
+
+import com.sckw.core.web.response.BaseResult;
+import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.system.model.vo.req.SysDictTypeReqVo;
+import com.sckw.system.model.vo.res.SysDictTypeResp;
+import com.sckw.system.service.SysDictTypeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 字典类型控制器 (MyBatis-Flex)
+ * @author system
+ * @date 2024
+ */
+@Tag(name = "字典类型管理")
+@RestController
+@RequestMapping("/flex/sysDictType")
+@RequiredArgsConstructor
+public class SysDictTypeFlexController {
+
+    private final SysDictTypeService sysDictTypeService;
+
+    /**
+     * 新增字典类型
+     */
+    @Operation(summary = "新增字典类型")
+    @PostMapping("/insert")
+    public BaseResult<Integer> insert(@RequestBody SysDictTypeReqVo reqVo) {
+        return BaseResult.success(sysDictTypeService.insert(reqVo.getEntity()));
+    }
+
+
+    /**
+     * 逻辑删除字典类型
+     */
+    @Operation(summary = "逻辑删除字典类型")
+    @PostMapping("/logicDelete")
+    public BaseResult<Integer> logicDeleteById(@RequestBody SysDictTypeReqVo reqVo) {
+        int rows = sysDictTypeService.logicDeleteById(reqVo.getId());
+        return BaseResult.success(rows);
+    }
+
+    /**
+     * 更新字典类型
+     */
+    @Operation(summary = "更新字典类型")
+    @PostMapping("/update")
+    public BaseResult<Integer> update(@RequestBody SysDictTypeReqVo reqVo) {
+        int rows = sysDictTypeService.update(reqVo.getEntity());
+        return BaseResult.success(rows);
+    }
+
+
+    /**
+     * 分页查询字典类型
+     */
+    @Operation(summary = "分页查询字典类型")
+    @PostMapping("/selectPage")
+    public BaseResult<PageDataResult<SysDictTypeResp>> selectPage(@RequestBody SysDictTypeReqVo reqVo) {
+
+        return BaseResult.success(sysDictTypeService.selectPage(reqVo));
+    }
+
+    /**
+     * 搜索字典类型(支持下拉选择,支持输入搜索)
+     */
+    @Operation(summary = "搜索字典类型下拉列表", description = "支持按名称或类型模糊搜索,返回id、name、type字段")
+    @PostMapping("/searchForSelect")
+    public BaseResult<List<SysDictTypeResp>> searchForSelect(@RequestBody SysDictTypeReqVo reqVo) {
+        return BaseResult.success(sysDictTypeService.searchForSelect(reqVo.getKeyword()));
+    }
+
+}

+ 2 - 1
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/SysDictDao.java

@@ -1,5 +1,6 @@
 package com.sckw.system.dao;
 
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.sckw.system.model.SysDict;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -13,7 +14,7 @@ import java.util.Map;
  * @date 2023-05-30
  */
 @Mapper
-public interface SysDictDao {
+public interface SysDictDao extends BaseMapper<SysDict> {
 
     /**
      * 新增

+ 2 - 1
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/SysDictTypeDao.java

@@ -1,5 +1,6 @@
 package com.sckw.system.dao;
 
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.sckw.system.model.SysDict;
 import com.sckw.system.model.SysDictType;
 import org.apache.ibatis.annotations.Mapper;
@@ -12,7 +13,7 @@ import java.util.Map;
  * @date 2023-05-30
  */
 @Mapper
-public interface SysDictTypeDao {
+public interface SysDictTypeDao extends BaseMapper<SysDictType> {
 
     /**
      * 新增

+ 152 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SysDictReqVo.java

@@ -0,0 +1,152 @@
+package com.sckw.system.model.vo.req;
+
+import com.sckw.core.web.request.PageReq;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 字典请求参数对象
+ *
+ * @author system
+ * @date 2024
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "字典请求参数对象")
+public class SysDictReqVo extends PageReq implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -1221233313543576005L;
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    private Long id;
+
+    /**
+     * 字典实体
+     */
+    @Schema(description = "字典实体")
+    @Valid
+    private SysDictInfo entity;
+
+    /**
+     * 类型
+     */
+    @Schema(description = "类型")
+    private String type;
+
+    /**
+     * 选项值
+     */
+    @Schema(description = "选项值")
+    private String value;
+
+    /**
+     * 选项标签
+     */
+    @Schema(description = "选项标签")
+    private String label;
+
+    /**
+     * 字典类型ID
+     */
+    @Schema(description = "字典类型ID")
+    private Long dictId;
+
+    /**
+     * 父级ID
+     */
+    @Schema(description = "父级ID")
+    private String parentId;
+
+    /**
+     * 状态
+     */
+    @Schema(description = "状态")
+    private Integer status;
+
+    /**
+     * 搜索关键词(用于下拉搜索)
+     */
+    @Schema(description = "搜索关键词(用于下拉搜索)")
+    private String keyword;
+
+    @Data
+    @Schema(description = "字典信息")
+    public static class SysDictInfo implements Serializable {
+        @Serial
+        private static final long serialVersionUID = -2031642328618437889L;
+
+        /**
+         * 主键
+         */
+        @Schema(description = "主键")
+        private Long id;
+
+        /**
+         * 字典类型id
+         */
+        @Schema(description = "字典类型id")
+        @NotNull(message = "字典类型id不能为空")
+        private Long dictId;
+
+        /**
+         * 字典值
+         */
+        @Schema(description = "字典值")
+        @NotBlank(message = "字典值不能为空")
+        private String value;
+
+        /**
+         * 选项
+         */
+        @Schema(description = "字典名称")
+        @NotBlank(message = "字典名称不能为空")
+        private String label;
+
+        /**
+         * 类型
+         */
+        @Schema(description = "类型")
+        @NotBlank(message = "字典类型不能为空")
+        private String type;
+
+        /**
+         * 描述
+         */
+        @Schema(description = "描述")
+        private String description;
+
+        /**
+         * 排序(升序)
+         */
+        @Schema(description = "排序(升序)")
+        @NotNull(message = "排序不能为空")
+        private Long sort;
+
+        /**
+         * 父级编号
+         */
+        @Schema(description = "父级编号")
+        private String parentId;
+
+        /**
+         * 备注
+         */
+        @Size(max = 200, message = "备注长度不能超过200")
+        @Schema(description = "备注", maxLength = 200)
+        private String remark;
+
+    }
+}

+ 92 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SysDictTypeReqVo.java

@@ -0,0 +1,92 @@
+package com.sckw.system.model.vo.req;
+
+import com.sckw.core.web.request.PageReq;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 字典类型请求参数对象
+ * @author system
+ * @date 2024
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Schema(description = "字典类型请求参数对象")
+public class SysDictTypeReqVo extends PageReq {
+
+    @Serial
+    private static final long serialVersionUID = 7701153842223116600L;
+
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    private Long id;
+
+    /**
+     * 字典类型实体
+     */
+    @Schema(description = "字典类型实体")
+    private SysDictTypeInfo entity;
+
+    /**
+     * 字典类型名称
+     */
+    @Schema(description = "字典类型名称")
+    private String name;
+
+    /**
+     * 类型
+     */
+    @Schema(description = "类型")
+    private String type;
+
+    /**
+     * 状态
+     */
+    @Schema(description = "状态")
+    private Integer status;
+
+    /**
+     * 搜索关键词(用于下拉搜索)
+     */
+    @Schema(description = "搜索关键词(用于下拉搜索)")
+    private String keyword;
+
+    @Data
+    @Schema(description = "字典类型信息")
+    public static class SysDictTypeInfo implements Serializable {
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 主键
+         */
+        @Schema(description = "主键")
+        private Long id;
+
+        /**
+         * 字典类型名称
+         */
+        @Schema(description = "字典类型名称")
+        private String name;
+
+        /**
+         * 类型
+         */
+        @Schema(description = "类型")
+        private String type;
+
+        /**
+         * 备注
+         */
+        @Size(max = 200, message = "备注长度不能超过200")
+        @Schema(description = "备注", maxLength = 200)
+        private String remark;
+    }
+}

+ 130 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SysDictResp.java

@@ -0,0 +1,130 @@
+package com.sckw.system.model.vo.res;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 字典响应类
+ * @author system
+ * @date 2024
+ */
+@Data
+public class SysDictResp implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @Schema(description = "主键")
+    private Long id;
+
+    /**
+     * 字典类型id
+     */
+    @Schema(description = "字典类型id")
+    private Long dictId;
+
+    /**
+     * 选项值
+     */
+    @Schema(description = "选项值")
+    private String value;
+
+    /**
+     * 字典名称
+     */
+    @Schema(description = "字典名称")
+    private String label;
+
+    /**
+     * 类型
+     */
+    @Schema(description = "类型")
+    private String type;
+
+    /**
+     * 描述
+     */
+    @Schema(description = "描述")
+    private String description;
+
+    /**
+     * 排序(升序)
+     */
+    @Schema(description = "排序(升序)")
+    private Long sort;
+
+    /**
+     * 父级编号
+     */
+    @Schema(description = "父级编号")
+    private String parentId;
+    /**
+     * 上级字典名称
+     */
+    @Schema(description = "上级字典名称")
+    private String parentName;
+
+    /**
+     * 图标、文件等
+     */
+    @Schema(description = "图标、文件等")
+    private String url;
+
+    /**
+     * 备注
+     */
+    @Schema(description = "备注")
+    private String remark;
+
+    /**
+     * 状态:0正常/1锁定
+     */
+    @Schema(description = "状态:0正常/1锁定")
+    private Integer status;
+    /**
+     * 状态名称
+     */
+    @Schema(description = "状态名称")
+    private String statusName;
+
+
+    /**
+     * 创建人
+     */
+    @Schema(description = "创建人")
+    private Long createBy;
+
+    /**
+     * 创建时间
+     */
+    @Schema(description = "创建时间")
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * 更新人
+     */
+    @Schema(description = "更新人")
+    private Long updateBy;
+
+    /**
+     * 更新时间
+     */
+    @Schema(description = "更新时间")
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+
+    /**
+     * 删除标识(0正常/-1删除)
+     */
+    @Schema(description = "删除标识(0正常/-1删除)")
+    private Integer delFlag;
+}

+ 84 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SysDictTypeResp.java

@@ -0,0 +1,84 @@
+package com.sckw.system.model.vo.res;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 字典类型实体类 (MyBatis-Flex)
+ * @author system
+ * @date 2024
+ */
+@Data
+public class SysDictTypeResp implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @Schema(description = "主键")
+    private Long id;
+
+    /**
+     * 字典类型名称
+     */
+    @Schema(description = "字典类型名称")
+    private String name;
+
+    /**
+     * 类型
+     */
+    @Schema(description = "类型")
+    private String type;
+
+    /**
+     * 备注
+     */
+    @Schema(description = "备注")
+    private String remark;
+
+    /**
+     * 状态:0正常/1锁定
+     */
+    @Schema(description = "状态:0正常/1锁定")
+    private Integer status;
+
+
+    /**
+     * 创建人
+     */
+    @Schema(description = "创建人")
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    @Schema(description = "创建时间")
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * 更新人
+     */
+    @Schema(description = "更新人")
+    private String updateBy;
+
+    /**
+     * 更新时间
+     */
+    @Schema(description = "更新时间")
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+
+    /**
+     * 删除标识(0正常/-1删除)
+     */
+    @Schema(description = "删除标识(0正常/-1删除)")
+    private Integer delFlag;
+}

+ 8 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/repository/KwsEnterpriseRepository.java

@@ -45,4 +45,12 @@ public class KwsEnterpriseRepository extends ServiceImpl<KwsEnterpriseDao,KwsEnt
                 .in(KwsEnterprise::getId, entIds)
                 .like(StringUtils.isNotBlank(entName), KwsEnterprise::getFirmName, entName));
     }
+
+    public List<KwsEnterprise> queryByParentEntIds(Set<Long> entIds) {
+        return list(Wrappers.<KwsEnterprise>lambdaQuery()
+                .eq(BaseModel::getDelFlag, 0)
+                .eq(BaseModel::getStatus, 0)
+                .eq(KwsEnterprise::getApproval, ApprovalEnum.OK.getCode())
+                .in(KwsEnterprise::getPid, entIds));
+    }
 }

+ 125 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/repository/SysDictRepository.java

@@ -0,0 +1,125 @@
+package com.sckw.system.repository;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.sckw.core.model.base.BaseModel;
+import com.sckw.system.dao.SysDictDao;
+import com.sckw.system.model.SysDict;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * 字典服务层(MyBatis-Plus 版本)
+ * @author system
+ * @date 2024
+ */
+@Repository
+public class SysDictRepository extends ServiceImpl<SysDictDao, SysDict> {
+
+
+    /**
+     * 根据条件查询唯一字典
+     */
+    public SysDict selectUniqueDict(Long dictTypeId, String value, String label,String type, String parentId) {
+        return this.getOne(Wrappers.<SysDict>lambdaQuery()
+                .eq(BaseModel::getDelFlag,0)
+                .eq(Objects.nonNull(dictTypeId),SysDict::getDictId, dictTypeId)
+                .eq(StringUtils.isNotBlank(value),SysDict::getValue, value)
+                .eq(StringUtils.isNotBlank(label),SysDict::getLabel, label)
+                .eq(StringUtils.isNotBlank( type),SysDict::getType, type)
+                .eq(StringUtils.isNotBlank(parentId),SysDict::getParentId, parentId)
+                .last("limit 1"));
+    }
+
+    /**
+     * 逻辑删除
+     */
+    public int logicDeleteById(Long id) {
+        return this.baseMapper.deleteById(id);
+    }
+
+    /**
+     * 根据主键查询
+     */
+    public SysDict selectById(Long id) {
+        return this.getById(id);
+    }
+
+    /**
+     * 更新字典
+     */
+    public Boolean update(SysDict entity) {
+        return updateById(entity);
+    }
+
+    /**
+     * 分页查询字典(包含父级名称)
+     */
+    public IPage<SysDict> selectPage(int pageNum, int pageSize, String type, String parentId, String label) {
+        Page<SysDict> page = new Page<>(pageNum, pageSize);
+        
+        // 使用自定义 SQL 查询(包含父级名称)
+        LambdaQueryWrapper<SysDict> wrapper = Wrappers.<SysDict>lambdaQuery()
+                .eq(SysDict::getDelFlag, 0)
+                .eq(StringUtils.isNotBlank(type), SysDict::getType, type)
+                .eq(StringUtils.isNotBlank(parentId), SysDict::getParentId, parentId)
+                .eq(StringUtils.isNotBlank(label), SysDict::getLabel, label)
+                .orderByDesc(BaseModel::getCreateTime)
+                .orderByAsc(SysDict::getSort);
+        return page(page,wrapper);
+    }
+
+    /**
+     * 统计字典数量
+     */
+    public long count(String type) {
+        LambdaQueryWrapper<SysDict> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysDict::getDelFlag, 0);
+        
+        if (StringUtils.isNotBlank(type)) {
+            wrapper.eq(SysDict::getType, type);
+        }
+        
+        return this.count(wrapper);
+    }
+
+    /**
+     * 查询上级字典下拉列表
+     */
+    public List<SysDict> searchParentDictForSelect(String keyword) {
+        return list(Wrappers.<SysDict>lambdaQuery()
+                .eq(BaseModel::getDelFlag,0)
+                .eq(BaseModel::getStatus,0)
+                .like (StringUtils.isNotBlank( keyword), SysDict::getLabel, keyword));
+    }
+
+    /**
+     * 快捷更改字典状态
+     */
+    public Boolean updateStatus(Long id, Integer status) {
+        SysDict sysDict = new SysDict();
+        sysDict.setStatus( status);
+        return update(sysDict, Wrappers.<SysDict>lambdaUpdate().eq(BaseModel::getId, id));
+    }
+
+    public List<SysDict> queryByIds(Set<String> parentIds) {
+        return list(Wrappers.<SysDict>lambdaQuery()
+                .eq(BaseModel::getDelFlag,0)
+                .eq(BaseModel::getStatus,0)
+                .in(SysDict::getId, parentIds));
+    }
+
+    public List<SysDict> selectByType(String testDictType) {
+        return list(Wrappers.<SysDict>lambdaQuery()
+                .eq(BaseModel::getDelFlag,0)
+                .eq(BaseModel::getStatus,0)
+                .eq(SysDict::getType, testDictType));
+    }
+}

+ 74 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/repository/SysDictTypeRepository.java

@@ -0,0 +1,74 @@
+package com.sckw.system.repository;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.sckw.system.dao.SysDictTypeDao;
+import com.sckw.system.model.SysDictType;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 字典类型服务层(MyBatis-Plus 版本)
+ * @author system
+ * @date 2024
+ */
+@Repository
+public class SysDictTypeRepository extends ServiceImpl<SysDictTypeDao, SysDictType> {
+
+    /**
+     * 根据类型查询字典类型
+     */
+    public SysDictType selectByType(String type,String name) {
+        return getOne(Wrappers.<SysDictType>lambdaQuery()
+                .eq(SysDictType::getType, type)
+                .eq(SysDictType::getName, name)
+                .eq(SysDictType::getDelFlag, 0)
+                .last("LIMIT 1"));
+    }
+
+    /**
+     * 逻辑删除
+     */
+    public int logicDeleteById(Long id) {
+        return this.baseMapper.deleteById(id);
+    }
+
+    /**
+     * 更新字典类型
+     */
+    public int update(SysDictType entity) {
+        return this.baseMapper.updateById(entity);
+    }
+
+    /**
+     * 分页查询字典类型
+     */
+    public IPage<SysDictType> selectPage(int pageNum, int pageSize, String name, String type, Integer status) {
+        return this.page(new Page<>(pageNum, pageSize), Wrappers.<SysDictType>lambdaQuery()
+                .eq(SysDictType::getDelFlag, 0)
+                .eq(Objects.nonNull( status), SysDictType::getStatus, status)
+                .like(StringUtils.isNotBlank( name),SysDictType::getName, name)
+                .like(StringUtils.isNotBlank(type), SysDictType::getType, type)
+                .orderByDesc(SysDictType::getCreateTime)
+               );
+    }
+
+    /**
+     * 搜索字典类型(支持下拉选择)
+     */
+    public List<SysDictType> searchForSelect(String keyword) {
+        return list(Wrappers.<SysDictType>lambdaQuery()
+                .select(SysDictType::getId, SysDictType::getName, SysDictType::getType)
+                .eq(SysDictType::getDelFlag, 0)
+                .eq(SysDictType::getStatus, 0)
+                .and(StringUtils.isNotBlank(keyword),w -> w.like(SysDictType::getName, keyword)
+                        .or()
+                        .like(SysDictType::getType, keyword))
+                .last("LIMIT 100"));
+    }
+}

+ 6 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsEnterpriseService.java

@@ -1506,6 +1506,12 @@ public class KwsEnterpriseService {
             return new EntInfoResp();
         }
         Set<Long> entIds = kwsEnterprises.stream().map(BaseModel::getId).collect(Collectors.toSet());
+
+        //查询父企业
+        List<KwsEnterprise> parentEnts  =kwsEnterpriseRepository.queryByParentEntIds(entIds);
+        if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(parentEnts)){
+            kwsEnterprises.addAll(parentEnts);
+        }
         //根据类型进行过滤
         List<KwsEntType> kwsEntTypes = kwsEntTypeRepository.queryByEntIdsAndType(entIds, req.getEntType());
         if (CollectionUtils.isEmpty(kwsEntTypes)) {

+ 231 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/SysDictFlexBusinessService.java

@@ -0,0 +1,231 @@
+package com.sckw.system.service;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.google.common.collect.Maps;
+import com.sckw.core.model.base.BaseModel;
+import com.sckw.core.utils.IdWorker;
+import com.sckw.core.web.context.LoginUserHolder;
+import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.system.model.SysDict;
+import com.sckw.system.model.vo.req.SysDictReqVo;
+import com.sckw.system.model.vo.res.SysDictResp;
+import com.sckw.system.repository.SysDictRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 字典业务服务层(中间层) - MyBatis-Plus 版本
+ * 在 SysDictService 基础上添加业务逻辑
+ * @author system
+ * @date 2024
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SysDictFlexBusinessService {
+
+    private final SysDictRepository sysDictRepository;
+
+    /**
+     * 新增字典
+     */
+    public Boolean insert(SysDictReqVo.SysDictInfo entity) {
+        log.info("新增字典,请求参数:{}", JSON.toJSONString(entity));
+        
+        // 业务校验:检查是否存在相同的字典项
+        SysDict existDict = sysDictRepository.selectUniqueDict(
+            entity.getDictId(), 
+            entity.getValue(), 
+            entity.getLabel(),
+            entity.getType(),
+            entity.getParentId()
+        );
+        if (existDict != null) {
+            log.error("字典项已存在,请勿重复添加");
+            return Boolean.FALSE;
+        }
+        
+        // 设置创建信息
+        Long userId = LoginUserHolder.getUserId();
+        long id = new IdWorker(1L).nextId();
+        SysDict sysDict = getSysDict(entity, id, userId);
+        return sysDictRepository.save(sysDict) ;
+    }
+
+    @NotNull
+    private static SysDict getSysDict(SysDictReqVo.SysDictInfo entity, long id, Long userId) {
+        SysDict sysDict = new SysDict();
+        sysDict.setId(id);
+        sysDict.setDictId(entity.getDictId());
+        sysDict.setValue(entity.getValue());
+        sysDict.setLabel(entity.getLabel());
+        sysDict.setType(entity.getType());
+        sysDict.setDescription(entity.getLabel());
+        sysDict.setSort(entity.getSort());
+        sysDict.setParentId(entity.getParentId());
+        sysDict.setRemark(entity.getRemark());
+        sysDict.setStatus(0);
+        sysDict.setCreateBy(userId);
+        sysDict.setUpdateBy(userId);
+        sysDict.setCreateTime(new Date());
+        sysDict.setUpdateTime(new Date());
+        sysDict.setDelFlag(0);
+        return sysDict;
+    }
+
+
+    /**
+     * 逻辑删除
+     */
+    public int logicDeleteById(Long id) {
+        log.info("逻辑删除字典,ID:{}", id);
+        return sysDictRepository.logicDeleteById(id);
+    }
+
+    /**
+     * 更新字典
+     */
+    public Boolean update(SysDictReqVo.SysDictInfo entity) {
+        log.info("更新字典,ID:{}", entity.getId());
+        
+        // 业务校验:检查是否存在相同的字典项(排除自己)
+        SysDict existDict = sysDictRepository.selectById(entity.getId());
+        if (Objects.isNull(existDict)) {
+            log.error("字典项不存在");
+            return Boolean.FALSE;
+        }
+
+        // 设置更新信息
+        Long userId = LoginUserHolder.getUserId();
+        SysDict sysDict = new SysDict();
+        sysDict.setId(existDict.getId());
+        sysDict.setSort(entity.getSort());
+        sysDict.setRemark(entity.getRemark());
+        sysDict.setUpdateBy(userId);
+        sysDict.setUpdateTime(new Date());
+        
+        return sysDictRepository.update(sysDict);
+    }
+
+
+
+
+
+
+
+    /**
+     * 分页查询字典
+     */
+    public PageDataResult<SysDictResp> selectPage(SysDictReqVo reqVo) {
+        log.info("分页查询字典,请求参数:{}", JSON.toJSONString(reqVo));
+        int pageNum = reqVo.getPageNum();
+        int pageSize = reqVo.getPageSize();
+
+        IPage<SysDict> page = sysDictRepository.selectPage(pageNum, pageSize, reqVo.getType(), reqVo.getParentId(), reqVo.getLabel());
+        
+        List<SysDict> records = page.getRecords();
+        if (records == null || records.isEmpty()) {
+            return PageDataResult.empty(reqVo.getPageNum(), reqVo.getPageSize());
+        }
+        Set<String> parentIds = records.stream()
+                .map(SysDict::getParentId)
+                .collect(Collectors.toSet());
+        //查询父字典
+        List<SysDict> parentDicts = sysDictRepository.queryByIds(parentIds);
+        Map<Long, SysDict> idAndParentDictMap = Maps.newHashMap();
+        if (CollectionUtils.isNotEmpty(parentDicts)){
+            idAndParentDictMap = parentDicts.stream()
+                    .collect(Collectors.toMap(BaseModel::getId, Function.identity(), (k1, k2) -> k1));
+        }
+
+        List<SysDictResp> sysDictResps = convertToRespList(records,idAndParentDictMap);
+        return PageDataResult.of(page, sysDictResps);
+    }
+
+    /**
+     * 统计字典数量
+     */
+    public long count(String type) {
+        log.info("统计字典数量,type:{}", type);
+        return sysDictRepository.count(type);
+    }
+
+    /**
+     * 查询上级字典下拉列表(支持输入搜索)
+     * 用于字典项选择父级字典
+     * @return 字典响应列表
+     */
+    public List<SysDictResp> searchParentDictForSelect(SysDictReqVo reqVo) {
+        log.info("查询上级字典下拉列表,请求参数:{}", JSON.toJSONString(reqVo));
+        List<SysDict> list = sysDictRepository.searchParentDictForSelect(reqVo.getKeyword());
+        return convertToRespList(list,new HashMap<>());
+    }
+
+    /**
+     * 快捷更改字典状态(启用/停用)
+     * @param id 字典ID
+     * @param status 状态(0正常/1锁定)
+     * @return 影响行数
+     */
+    public Boolean updateStatus(Long id, Integer status) {
+        log.info("快捷更改字典状态,ID:{}, 新状态:{}", id, status);
+        
+        // 业务校验
+        if (id == null) {
+            log.error("字典ID不能为空");
+            return Boolean.FALSE;
+        }
+        if (status == null || (status != 0 && status != 1)) {
+            log.error("状态值无效,只能为0(正常)或1(锁定)");
+            return Boolean.FALSE;
+        }
+        
+        // 检查字典是否存在
+        SysDict existDict = sysDictRepository.selectById(id);
+        if (Objects.isNull(existDict)) {
+            log.error("字典项不存在,ID:{}", id);
+            return Boolean.FALSE;
+        }
+        
+        // 更新状态
+        return sysDictRepository.updateStatus(id, status);
+    }
+
+    /**
+     * 将实体转换为响应对象
+     */
+    private SysDictResp convertToResp(SysDict entity,Map<Long, SysDict> idAndParentDictMap) {
+        if (entity == null) {
+            return null;
+        }
+        SysDictResp resp = new SysDictResp();
+        BeanUtils.copyProperties(entity, resp);
+        resp.setStatusName(Objects.equals(entity.getStatus(), 0) ? " 正常" : "锁定");
+        // 设置父级字典名称
+        Long parentId = Optional.ofNullable(entity.getParentId()).map(Long::parseLong).orElse(null);
+        SysDict parentDict = idAndParentDictMap.getOrDefault(parentId, new SysDict());
+        resp.setParentName(parentDict.getLabel());
+        return resp;
+    }
+
+    /**
+     * 将实体列表转换为响应对象列表
+     */
+    private List<SysDictResp> convertToRespList(List<SysDict> entities,Map<Long, SysDict> idAndParentDictMap ) {
+        if (entities == null || entities.isEmpty()) {
+            return List.of();
+        }
+        return entities.stream()
+                .map(x->convertToResp(x,idAndParentDictMap))
+                .collect(Collectors.toList());
+    }
+}

+ 138 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/SysDictTypeService.java

@@ -0,0 +1,138 @@
+package com.sckw.system.service;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sckw.core.utils.IdWorker;
+import com.sckw.core.web.context.LoginUserHolder;
+import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.system.model.SysDictType;
+import com.sckw.system.model.vo.req.SysDictTypeReqVo;
+import com.sckw.system.model.vo.res.SysDictTypeResp;
+import com.sckw.system.repository.SysDictTypeRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 字典类型业务服务层(中间层) - MyBatis-Plus 版本
+ * @author system
+ * @date 2024
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SysDictTypeService {
+
+    private final SysDictTypeRepository sysDictTypeService;
+
+    /**
+     * 新增字典类型
+     */
+    public int insert(SysDictTypeReqVo.SysDictTypeInfo entity) {
+        log.info("新增字典类型,请求参数:{}", JSON.toJSONString(entity));
+        SysDictType existType = sysDictTypeService.selectByType(entity.getType(),entity.getName());
+        if (existType != null) {
+            log.error("字典类型已存在,请勿重复添加");
+            return 0;
+        }
+        Long userId = LoginUserHolder.getUserId();
+
+        long id = new IdWorker(1L).nextId();
+        SysDictType sysDictType = getSysDictType(entity, id, userId);
+        return sysDictTypeService.save(sysDictType) ? 1 : 0;
+    }
+
+    @NotNull
+    private static SysDictType getSysDictType(SysDictTypeReqVo.SysDictTypeInfo entity, long id, Long userId) {
+        SysDictType sysDictType = new SysDictType();
+        sysDictType.setId(id);
+        sysDictType.setName(entity.getName());
+        sysDictType.setType(entity.getType());
+        sysDictType.setRemark(entity.getRemark());
+        sysDictType.setStatus(0);
+        sysDictType.setCreateBy(userId);
+        sysDictType.setUpdateBy(userId);
+        sysDictType.setCreateTime(new Date());
+        sysDictType.setUpdateTime(new Date());
+        return sysDictType;
+    }
+
+
+    /**
+     * 逻辑删除
+     */
+    public int logicDeleteById(Long id) {
+        return sysDictTypeService.logicDeleteById(id);
+    }
+
+    /**
+     * 更新字典类型
+     */
+    public int update(SysDictTypeReqVo.SysDictTypeInfo entity) {
+        SysDictType sysDictType = new SysDictType();
+        sysDictType.setId(entity.getId());
+        sysDictType.setName(entity.getName());
+        sysDictType.setType(entity.getType());
+        sysDictType.setRemark(entity.getRemark());
+        sysDictType.setUpdateBy(LoginUserHolder.getUserId());
+        sysDictType.setUpdateTime(new Date());
+        return sysDictTypeService.update(sysDictType);
+    }
+
+
+
+    /**
+     * 分页查询字典类型
+     */
+    public PageDataResult<SysDictTypeResp> selectPage(SysDictTypeReqVo reqVo) {
+        log.info("分页查询字典类型,请求参数:{}", JSON.toJSONString(reqVo));
+        int pageNum = reqVo.getPageNum();
+        int pageSize = reqVo.getPageSize();
+        IPage<SysDictType> page = sysDictTypeService.selectPage(pageNum, pageSize, reqVo.getName(), reqVo.getType(), reqVo.getStatus());
+        List<SysDictType> records = page.getRecords();
+        if (CollectionUtils.isEmpty(records)) {
+            return PageDataResult.empty(reqVo.getPageNum(), reqVo.getPageSize());
+        }
+        List<SysDictTypeResp> sysDictTypes = records.stream()
+                .map(SysDictTypeService::getSysDictTypeResp)
+                .collect(Collectors.toList());
+        return PageDataResult.of(page, sysDictTypes);
+    }
+
+    @NotNull
+    private static SysDictTypeResp getSysDictTypeResp(SysDictType s) {
+        SysDictTypeResp sysDictTypeResp = new SysDictTypeResp();
+        sysDictTypeResp.setId(s.getId());
+        sysDictTypeResp.setName(s.getName());
+        sysDictTypeResp.setType(s.getType());
+        sysDictTypeResp.setRemark(s.getRemark());
+        sysDictTypeResp.setStatus(s.getStatus());
+        sysDictTypeResp.setCreateTime(s.getCreateTime());
+        sysDictTypeResp.setUpdateTime(s.getUpdateTime());
+        sysDictTypeResp.setDelFlag(s.getDelFlag());
+        return sysDictTypeResp;
+    }
+
+    /**
+     * 搜索字典类型(支持下拉选择)
+     */
+    public List<SysDictTypeResp> searchForSelect(String keyword) {
+        List<SysDictType> sysDictTypes = sysDictTypeService.searchForSelect(keyword);
+        if (CollectionUtils.isEmpty(sysDictTypes)) {
+            return List.of();
+        }
+
+        return sysDictTypes.stream()
+                .map(SysDictTypeService::getSysDictTypeResp)
+                .collect(Collectors.toList());
+    }
+
+
+}

+ 2 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/TransPortApplication.java

@@ -5,6 +5,7 @@ import com.sckw.startup.annotation.SckwCloudApplication;
 import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 
 /**
  * @author lfdc
@@ -15,6 +16,7 @@ import org.springframework.boot.SpringApplication;
 @SckwRemoteApplication
 @SckwCloudApplication
 @MapperScan("com.sckw.transport.dao")
+@EnableFeignClients(basePackages = "com.sckw.transport.api.feign")
 public class TransPortApplication {
     public static void main(String[] args) {
         SpringApplication.run(TransPortApplication.class, args);

+ 26 - 8
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtWaybillOrderV1Service.java

@@ -26,6 +26,7 @@ import com.sckw.core.web.constant.CommonConstants;
 import com.sckw.core.web.constant.HttpStatus;
 import com.sckw.core.web.context.LoginUserHolder;
 import com.sckw.core.web.response.HttpResult;
+import com.sckw.core.web.response.BaseResult;
 import com.sckw.core.web.response.result.PageDataResult;
 import com.sckw.excel.utils.DateUtil;
 import com.sckw.fleet.api.RemoteFleetService;
@@ -41,6 +42,8 @@ import com.sckw.stream.model.UserInfo;
 import com.sckw.system.api.RemoteSystemService;
 import com.sckw.system.api.RemoteUserService;
 import com.sckw.system.api.model.dto.res.*;
+import com.sckw.transport.api.feign.VehicleTraceClient;
+import com.sckw.transport.api.model.vo.VehicleTraceResponse;
 import com.sckw.transport.common.config.UrlConfigProperties;
 import com.sckw.transport.dao.*;
 import com.sckw.transport.model.*;
@@ -132,6 +135,7 @@ public class KwtWaybillOrderV1Service {
     private final KwtLogisticsOrderRepository kwtLogisticsOrderRepository;
     private final KwtWaybillOrderTicketRepository  kwtWaybillOrderTicketRepository;
     private final KwtWaybillOrderNodeRepository kwtWaybillOrderNodeRepository;
+    private final VehicleTraceClient vehicleTraceClient;
     @Resource
     private StreamBridge streamBridge;
     @DubboReference(version = "1.0.0", group = "design", check = false)
@@ -2195,16 +2199,30 @@ public class KwtWaybillOrderV1Service {
                 //从中台查询轨迹信息
                 VehicleDataDTO vehicleDataDTO = new VehicleDataDTO();
                 vehicleDataDTO.setWOrderNo(waybillOrder.getWOrderNo());
-                String s = "";
+                
                 try {
-                    s = HttpUtil.postJson(urlConfigProperties.getApiBaseUrl()+ UrlConstants.QUERY_TRACE_URL, com.alibaba.fastjson.JSON.toJSONString(vehicleDataDTO), null);
+                    // 使用 Feign 调用查询车辆轨迹列表
+                    com.sckw.transport.api.model.dto.VehicleDataDTO vehicleDataDTO1 = new com.sckw.transport.api.model.dto.VehicleDataDTO();
+                    vehicleDataDTO.setWOrderNo(waybillOrder.getWOrderNo());
+                    BaseResult<VehicleTraceResponse> result = vehicleTraceClient.queryVehicleDataList(vehicleDataDTO1);
+                    
+                    if (result != null && result.getCode()!= HttpStatus.SUCCESS_CODE && result.getData() != null) {
+                        List<com.sckw.transport.api.model.dto.VehicleReturnData> vehicleReturn = result.getData().getDataList();
+                        List<VehicleRouteData> vehicleReturnData = vehicleReturn.stream().map(x->{
+                            VehicleRouteData vehicleRouteData = new VehicleRouteData();
+                            vehicleRouteData.setTs(x.getTs());
+                            vehicleRouteData.setLongitude(x.getLongitude());
+                            vehicleRouteData.setLatitude(x.getLatitude());
+                            return vehicleRouteData;
+                        }).collect(Collectors.toList());
+                        if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(vehicleReturnData)) {
+                            rTruckRouteVo.setPointInfo(com.alibaba.fastjson.JSON.toJSONString(vehicleReturnData));
+                        }
+                    } else {
+                        log.warn("查询车辆轨迹返回空数据, 运单号: {}", waybillOrder.getWOrderNo());
+                    }
                 } catch (Exception e) {
-                    log.error("查询任务轨迹异常",e);
-
-                }
-                if (org.apache.commons.lang3.StringUtils.isNotBlank(s)){
-                    List<VehicleRouteData> vehicleReturnData = com.alibaba.fastjson.JSON.parseArray(s, VehicleRouteData.class);
-                    rTruckRouteVo.setPointInfo(com.alibaba.fastjson.JSON.toJSONString(vehicleReturnData));
+                    log.error("查询任务轨迹异常, 运单号: {}", waybillOrder.getWOrderNo(), e);
                 }
 
                 remoteFleetService.saveOrUpdate(rTruckRouteVo);

+ 29 - 14
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/kwfTruckTraceService.java

@@ -12,8 +12,10 @@ import com.sckw.core.model.enums.CarWaybillEnum;
 import com.sckw.core.utils.DateUtils;
 import com.sckw.core.utils.HttpUtil;
 import com.sckw.core.web.constant.CommonConstants;
+import com.sckw.core.web.constant.HttpStatus;
 import com.sckw.core.web.context.LoginUserHolder;
 import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.core.web.response.BaseResult;
 import com.sckw.fleet.api.RemoteFleetService;
 import com.sckw.fleet.api.model.vo.RDriverVo;
 import com.sckw.fleet.api.model.vo.RFleetDriverVo;
@@ -33,8 +35,10 @@ import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrderAddress;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.model.dto.TruckDto;
-import com.sckw.transport.model.dto.VehicleDataDTO;
-import com.sckw.transport.model.dto.VehicleReturnData;
+import com.sckw.transport.api.feign.VehicleTraceClient;
+import com.sckw.transport.api.model.dto.VehicleDataDTO;
+import com.sckw.transport.api.model.dto.VehicleReturnData;
+import com.sckw.transport.api.model.vo.VehicleTraceResponse;
 import com.sckw.transport.model.param.CurrentTaskTraceReq;
 import com.sckw.transport.model.param.KwfTruckTraceReplayReq;
 import com.sckw.transport.model.param.TruckInfoReq;
@@ -79,6 +83,7 @@ public class kwfTruckTraceService {
     private final KwtWaybillOrderSubtaskRepository kwtWaybillOrderSubtaskRepository;
     private final VehicleCollectService vehicleCollectService;
     private final KwtLogisticsOrderUnitRepository kwtLogisticsOrderUnitRepository;
+    private final VehicleTraceClient vehicleTraceClient;
     @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
     RemoteSystemService remoteSystemService;
 
@@ -528,19 +533,21 @@ public class kwfTruckTraceService {
     public VehicleReturnData getVehicleReturnData(String truckId) {
         VehicleDataDTO vehicleDataDTO = new VehicleDataDTO();
         vehicleDataDTO.setCarNo(truckId);
-        String s = null;
+
         try {
-            s = HttpUtil.postJson(urlConfigProperties.getApiBaseUrl()+ UrlConstants.QUERY_REAL_TIME_TRACE_URL, JSON.toJSONString(vehicleDataDTO), null);
-        } catch (Exception e) {
-            log.error("查询任务轨迹异常",e);
+            // 使用 Feign 调用查询实时位置
+            BaseResult<VehicleTraceResponse> result = vehicleTraceClient.queryRealTimeLocation(vehicleDataDTO);
 
-        }
-        if (StringUtils.isBlank(s)){
+            if (result == null || result.getCode() != HttpStatus.SUCCESS_CODE || result.getData() == null) {
+                log.warn("查询实时轨迹返回空数据, 车牌号: {}", truckId);
+                return null;
+            }
+
+            return result.getData().getData();
+        } catch (Exception e) {
+            log.error("查询任务轨迹异常, 车牌号: {}", truckId, e);
             return null;
         }
-        JSONObject jsonObject = JSON.parseObject(s, JSONObject.class);
-        String data = JSON.toJSONString(jsonObject.get("data"));
-        return JSON.parseObject(data, VehicleReturnData.class);
     }
 
     private static CurrentTaskTraceReqVo.CurrentTaskTrace getCurrentTaskTrace(VehicleReturnData e) {
@@ -601,11 +608,19 @@ public class kwfTruckTraceService {
                 .orElse(null)));
         req.setVehicleDataVO(vehicleDataVO);
         req.setStatus("1");
+        
         try {
-            String url = urlConfigProperties.getApiBaseUrl();
-            HttpUtil.postJson(url+ UrlConstants.VEHICLE_TRAJECTORY, JSON.toJSONString(req), null);
+            // 使用 Feign 调用上报车辆轨迹
+            BaseResult<Void> result = vehicleTraceClient.saveVehicleData(req);
+            
+            if (result == null || result.getCode() != HttpStatus.SUCCESS_CODE) {
+                log.error("app上报车辆轨迹失败, 响应: {}", result);
+                throw new BusinessException("app上报车辆轨迹异常");
+            }
+        } catch (BusinessException e) {
+            throw e;
         } catch (Exception e) {
-            log.error("app上报车辆轨迹异常",e);
+            log.error("app上报车辆轨迹异常", e);
             throw new BusinessException("app上报车辆轨迹异常");
         }
         String key = CommonConstants.TRUCK_LOCATION + truckId+CommonConstants.UNDERSCORE;

+ 23 - 0
sckw-modules/sckw-transport/src/main/resources/bootstrap.yml

@@ -10,6 +10,29 @@ spring:
   main:
     allow-bean-definition-overriding: true
     allow-circular-references: true
+
+# 车辆轨迹服务配置
+vehicle:
+  trace:
+    api:
+      base-url: ${VEHICLE_TRACE_API_URL:http://localhost:8080}
+
+# Feign 配置
+feign:
+  client:
+    config:
+      default:
+        connect-timeout: 5000
+        read-timeout: 10000
+      vehicle-trace-service:
+        connect-timeout: 5000
+        read-timeout: 10000
+        logger-level: full
+  httpclient:
+    enabled: true
+    max-connections: 200
+    max-connections-per-route: 50
+
 mybatis-plus:
   configuration:
     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl