Parcourir la source

提交收费系统对外对接接口包装

chenxiaofei il y a 23 heures
Parent
commit
91b5db632e
22 fichiers modifiés avec 1117 ajouts et 14 suppressions
  1. 57 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/ParkingPlateInfoController.java
  2. 31 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingCarTypeEnum.java
  3. 31 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingChargeTypeEnum.java
  4. 29 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingDeductStatusEnum.java
  5. 29 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingNeedDeductEnum.java
  6. 44 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ChargeInfoItemResp.java
  7. 46 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ChargeInfoQueryParam.java
  8. 28 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/EntryNotificationParam.java
  9. 31 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ExitNotificationParam.java
  10. 41 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ExitNotificationResp.java
  11. 41 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ParkingPlatformReportExtra.java
  12. 48 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ParkingPlatformReportResult.java
  13. 41 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ParkingPlatformResult.java
  14. 20 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/PlateInfoQueryParam.java
  15. 38 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/PlateInfoResp.java
  16. 3 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/vo/TradeOrderTransportInfoResp.java
  17. 377 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/ParkingPlateInfoService.java
  18. 27 14
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/TradeOrderTransportInfoService.java
  19. 85 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/util/ParkingPlatformResponseParser.java
  20. 59 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/util/ParkingPlatformSignUtil.java
  21. 6 0
      sckw-modules/sckw-transport/src/main/resources/bootstrap-cxf.yml
  22. 5 0
      sckw-modules/sckw-transport/src/main/resources/bootstrap-prod.yml

+ 57 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/ParkingPlateInfoController.java

@@ -0,0 +1,57 @@
+package com.sckw.transport.controller;
+
+import com.sckw.transport.model.param.ChargeInfoItemResp;
+import com.sckw.transport.model.param.ChargeInfoQueryParam;
+import com.sckw.transport.model.param.EntryNotificationParam;
+import com.sckw.transport.model.param.ExitNotificationParam;
+import com.sckw.transport.model.param.ExitNotificationResp;
+import com.sckw.transport.model.param.ParkingPlatformReportResult;
+import com.sckw.transport.model.param.ParkingPlatformResult;
+import com.sckw.transport.model.param.PlateInfoQueryParam;
+import com.sckw.transport.model.param.PlateInfoResp;
+import com.sckw.transport.service.ParkingPlateInfoService;
+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.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 停车平台对接
+ */
+@RestController
+@RequestMapping("/parking/plateInfo")
+@Tag(name = "停车平台对接")
+@RequiredArgsConstructor
+public class ParkingPlateInfoController {
+
+    private final ParkingPlateInfoService parkingPlateInfoService;
+
+    @PostMapping(value = "/query", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "查询车辆信息", description = "调用停车平台 /list-plate-info 接口,返回通用类格式")
+    public ParkingPlatformResult<PlateInfoResp> queryPlateInfo(@Valid @RequestBody PlateInfoQueryParam param) {
+        return parkingPlateInfoService.queryPlateInfo(param);
+    }
+
+    @PostMapping(value = "/entryNotification", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "入场通知", description = "调用停车平台 /entry-notification 接口,返回通用类格式")
+    public ParkingPlatformResult<Object> entryNotification(@Valid @RequestBody EntryNotificationParam param) {
+        return parkingPlateInfoService.entryNotification(param);
+    }
+
+    @PostMapping(value = "/exitNotification", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "出场扣费通知", description = "调用停车平台 /exit-notification 接口,返回通用类格式")
+    public ParkingPlatformResult<ExitNotificationResp> exitNotification(@Valid @RequestBody ExitNotificationParam param) {
+        return parkingPlateInfoService.exitNotification(param);
+    }
+
+    @PostMapping(value = "/listChargeInfo", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "充值扣费明细查询", description = "调用停车平台 /list-charge-info 接口,返回查询报表类格式")
+    public ParkingPlatformReportResult<ChargeInfoItemResp> listChargeInfo(@Valid @RequestBody ChargeInfoQueryParam param) {
+        return parkingPlateInfoService.listChargeInfo(param);
+    }
+}

+ 31 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingCarTypeEnum.java

@@ -0,0 +1,31 @@
+package com.sckw.transport.model.enuma;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 停车平台车辆类型
+ */
+@Getter
+@AllArgsConstructor
+public enum ParkingCarTypeEnum {
+
+    TEMPORARY(1, "临时车"),
+    MONTHLY(2, "月租车"),
+    FREE(3, "免费车"),
+    RECHARGE(5, "充值车");
+
+    private final Integer code;
+    private final String desc;
+
+    public static String getDescByCode(Integer code) {
+        for (ParkingCarTypeEnum item : values()) {
+            if (Objects.equals(item.getCode(), code)) {
+                return item.getDesc();
+            }
+        }
+        return null;
+    }
+}

+ 31 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingChargeTypeEnum.java

@@ -0,0 +1,31 @@
+package com.sckw.transport.model.enuma;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 充值扣费类型
+ */
+@Getter
+@AllArgsConstructor
+public enum ParkingChargeTypeEnum {
+
+    RECHARGE_CARD(1, "充值车充值"),
+    MONTHLY_RENEWAL(2, "月卡续费"),
+    RECHARGE_DEDUCT(3, "充值车扣费"),
+    TEMPORARY_DEDUCT(4, "临时车扣费");
+
+    private final Integer code;
+    private final String desc;
+
+    public static String getDescByCode(Integer code) {
+        for (ParkingChargeTypeEnum item : values()) {
+            if (Objects.equals(item.getCode(), code)) {
+                return item.getDesc();
+            }
+        }
+        return null;
+    }
+}

+ 29 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingDeductStatusEnum.java

@@ -0,0 +1,29 @@
+package com.sckw.transport.model.enuma;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 扣费成功标识
+ */
+@Getter
+@AllArgsConstructor
+public enum ParkingDeductStatusEnum {
+
+    FAIL(0, "扣费失败"),
+    SUCCESS(1, "扣费成功");
+
+    private final Integer code;
+    private final String desc;
+
+    public static String getDescByCode(Integer code) {
+        for (ParkingDeductStatusEnum item : values()) {
+            if (Objects.equals(item.getCode(), code)) {
+                return item.getDesc();
+            }
+        }
+        return null;
+    }
+}

+ 29 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/enuma/ParkingNeedDeductEnum.java

@@ -0,0 +1,29 @@
+package com.sckw.transport.model.enuma;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 出场是否扣款
+ */
+@Getter
+@AllArgsConstructor
+public enum ParkingNeedDeductEnum {
+
+    NO(0, "不需要扣款"),
+    YES(1, "需要扣款");
+
+    private final Integer code;
+    private final String desc;
+
+    public static String getDescByCode(Integer code) {
+        for (ParkingNeedDeductEnum item : values()) {
+            if (Objects.equals(item.getCode(), code)) {
+                return item.getDesc();
+            }
+        }
+        return null;
+    }
+}

+ 44 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ChargeInfoItemResp.java

@@ -0,0 +1,44 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 充值扣费明细
+ */
+@Data
+@Schema(description = "充值扣费明细")
+public class ChargeInfoItemResp {
+
+    @Schema(description = "车场ID")
+    private String parkId;
+
+    @Schema(description = "车牌号码")
+    private String carLicense;
+
+    @Schema(description = "业务单号")
+    private String businessId;
+
+    @Schema(description = "扣费时间,格式 yyyy-MM-dd HH:mm:ss")
+    private String chargeTime;
+
+    @Schema(description = "金额,保留两位小数")
+    private BigDecimal money;
+
+    @Schema(description = "充值扣费类型:1-充值卡充值,2-月卡续费,3-充值卡扣费,4-临时车扣费")
+    private Integer chargeType;
+
+    @Schema(description = "充值扣费类型描述")
+    private String chargeTypeDesc;
+
+    @Schema(description = "余额")
+    private BigDecimal balance;
+
+    @Schema(description = "有效期,格式 yyyy-MM-dd HH:mm:ss")
+    private String endTime;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 46 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ChargeInfoQueryParam.java

@@ -0,0 +1,46 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 充值扣费明细查询请求
+ */
+@Data
+@Schema(description = "充值扣费明细查询请求")
+public class ChargeInfoQueryParam {
+
+    @Schema(description = "车场ID,不传则使用系统配置默认值")
+    private String parkId;
+
+    @Schema(description = "车牌号码")
+    private String carLicense;
+
+    @Schema(description = "业务单号")
+    private String businessId;
+
+    @NotBlank(message = "开始时间不能为空")
+    @Schema(description = "开始时间,格式 yyyy-MM-dd HH:mm:ss", example = "2026-01-01 00:00:00",
+            requiredMode = Schema.RequiredMode.REQUIRED)
+    private String startTime;
+
+    @NotBlank(message = "结束时间不能为空")
+    @Schema(description = "结束时间,格式 yyyy-MM-dd HH:mm:ss", example = "2026-01-31 23:59:59",
+            requiredMode = Schema.RequiredMode.REQUIRED)
+    private String endTime;
+
+    @NotNull(message = "开始页数不能为空")
+    @Min(value = 1, message = "开始页数最小为1")
+    @Schema(description = "开始页数,1为第一页", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Integer start;
+
+    @NotNull(message = "每页记录条数不能为空")
+    @Min(value = 1, message = "每页记录条数最小为1")
+    @Max(value = 50, message = "每页记录条数最大为50")
+    @Schema(description = "每页记录条数,最大50", example = "20", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Integer limit;
+}

+ 28 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/EntryNotificationParam.java

@@ -0,0 +1,28 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 入场通知请求
+ */
+@Data
+@Schema(description = "入场通知请求")
+public class EntryNotificationParam {
+
+    @Schema(description = "车场ID,不传则使用系统配置默认值")
+    private String parkId;
+
+    @NotBlank(message = "车牌号码不能为空")
+    @Schema(description = "车牌号码", example = "川A12345", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String carLicense;
+
+    @NotBlank(message = "通道ID不能为空")
+    @Schema(description = "矿拉拉通道ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String gateId;
+
+    @NotBlank(message = "业务单号不能为空")
+    @Schema(description = "矿拉拉业务单号", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String businessId;
+}

+ 31 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ExitNotificationParam.java

@@ -0,0 +1,31 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 出场扣费通知请求
+ */
+@Data
+@Schema(description = "出场扣费通知请求")
+public class ExitNotificationParam {
+
+    @Schema(description = "车场ID,不传则使用系统配置默认值")
+    private String parkId;
+
+    @NotBlank(message = "车牌号码不能为空")
+    @Schema(description = "车牌号码", example = "川A12345", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String carLicense;
+
+    @NotBlank(message = "通道ID不能为空")
+    @Schema(description = "矿拉拉通道ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String gateId;
+
+    @NotBlank(message = "业务单号不能为空")
+    @Schema(description = "矿拉拉业务单号", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String businessId;
+
+    @Schema(description = "是否扣款:1-需要扣款,0-不扣款")
+    private Integer needDeduct;
+}

+ 41 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ExitNotificationResp.java

@@ -0,0 +1,41 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 出场扣费通知响应
+ */
+@Data
+@Schema(description = "出场扣费通知响应")
+public class ExitNotificationResp {
+
+    @Schema(description = "车场ID")
+    private String parkId;
+
+    @Schema(description = "车牌号码")
+    private String carLicense;
+
+    @Schema(description = "车辆类型:1-临时车,2-月租车,3-免费车,5-充值车")
+    private Integer carType;
+
+    @Schema(description = "车辆类型描述")
+    private String carTypeDesc;
+
+    @Schema(description = "余额")
+    private BigDecimal balance;
+
+    @Schema(description = "过期时间,格式 yyyy-MM-dd")
+    private String endTime;
+
+    @Schema(description = "业务单号")
+    private String businessId;
+
+    @Schema(description = "扣费成功标识:1-成功,0-失败")
+    private Integer deductStatus;
+
+    @Schema(description = "扣费成功标识描述")
+    private String deductStatusDesc;
+}

+ 41 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ParkingPlatformReportExtra.java

@@ -0,0 +1,41 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 停车平台查询报表分页扩展信息
+ */
+@Data
+@Schema(description = "停车平台查询报表分页扩展信息")
+public class ParkingPlatformReportExtra {
+
+    @Schema(description = "扩展信息")
+    private Map<String, Object> extraExt;
+
+    @Schema(description = "是否有下一页")
+    private Boolean hasNext;
+
+    @Schema(description = "是否有上一页")
+    private Boolean hasPre;
+
+    @Schema(description = "每页记录条数")
+    private Integer limit;
+
+    @Schema(description = "总页数")
+    private Integer pageTotal;
+
+    @Schema(description = "当前页码")
+    private Integer start;
+
+    @Schema(description = "原始起始位置")
+    private Integer startRaw;
+
+    @Schema(description = "总记录数")
+    private Integer total;
+
+    @Schema(description = "原始总记录数")
+    private Integer totalRaw;
+}

+ 48 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ParkingPlatformReportResult.java

@@ -0,0 +1,48 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 停车平台查询报表类返回
+ */
+@Data
+@Schema(description = "停车平台查询报表类返回")
+public class ParkingPlatformReportResult<T> {
+
+    @Schema(description = "状态码,0表示成功")
+    private int code;
+
+    @Schema(description = "报表数据列表")
+    private List<T> data = new ArrayList<>();
+
+    @Schema(description = "分页扩展信息")
+    private ParkingPlatformReportExtra extra;
+
+    @Schema(description = "返回消息")
+    private String msg;
+
+    @Schema(description = "是否成功")
+    private boolean success;
+
+    public static <T> ParkingPlatformReportResult<T> success(List<T> data, ParkingPlatformReportExtra extra) {
+        ParkingPlatformReportResult<T> result = new ParkingPlatformReportResult<>();
+        result.setCode(0);
+        result.setData(data == null ? new ArrayList<>() : data);
+        result.setExtra(extra);
+        result.setMsg("请求成功");
+        result.setSuccess(true);
+        return result;
+    }
+
+    public static <T> ParkingPlatformReportResult<T> failed(int code, String msg) {
+        ParkingPlatformReportResult<T> result = new ParkingPlatformReportResult<>();
+        result.setCode(code);
+        result.setMsg(msg);
+        result.setSuccess(false);
+        return result;
+    }
+}

+ 41 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/ParkingPlatformResult.java

@@ -0,0 +1,41 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 停车平台通用接口返回
+ */
+@Data
+@Schema(description = "停车平台通用接口返回")
+public class ParkingPlatformResult<T> {
+
+    @Schema(description = "状态码,0表示成功")
+    private int code;
+
+    @Schema(description = "业务数据")
+    private T data;
+
+    @Schema(description = "返回消息")
+    private String msg;
+
+    @Schema(description = "是否成功")
+    private boolean success;
+
+    public static <T> ParkingPlatformResult<T> success(T data) {
+        ParkingPlatformResult<T> result = new ParkingPlatformResult<>();
+        result.setCode(0);
+        result.setData(data);
+        result.setMsg("请求成功");
+        result.setSuccess(true);
+        return result;
+    }
+
+    public static <T> ParkingPlatformResult<T> failed(int code, String msg) {
+        ParkingPlatformResult<T> result = new ParkingPlatformResult<>();
+        result.setCode(code);
+        result.setMsg(msg);
+        result.setSuccess(false);
+        return result;
+    }
+}

+ 20 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/PlateInfoQueryParam.java

@@ -0,0 +1,20 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 车辆信息查询请求
+ */
+@Data
+@Schema(description = "车辆信息查询请求")
+public class PlateInfoQueryParam {
+
+    @NotBlank(message = "车牌号码不能为空")
+    @Schema(description = "车牌号码", example = "川A12345", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String carLicense;
+
+    @Schema(description = "车场ID,不传则使用系统配置默认值")
+    private String parkId;
+}

+ 38 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/PlateInfoResp.java

@@ -0,0 +1,38 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 停车平台车辆信息
+ */
+@Data
+@Schema(description = "停车平台车辆信息")
+public class PlateInfoResp {
+
+    @Schema(description = "车场ID")
+    private String parkId;
+
+    @Schema(description = "车牌号码")
+    private String carLicense;
+
+    @Schema(description = "车辆类型:1-临时车,2-月租车,3-免费车,5-充值车")
+    private Integer carType;
+
+    @Schema(description = "车辆类型描述")
+    private String carTypeDesc;
+
+    @Schema(description = "余额")
+    private BigDecimal balance;
+
+    @Schema(description = "过期时间,格式 yyyy-MM-dd,当天结束前有效")
+    private String endTime;
+
+    @Schema(description = "是否能接单:1-是,0-否")
+    private Integer acceptOrder;
+
+    @Schema(description = "充值/续费链接")
+    private String chargeUrl;
+}

+ 3 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/vo/TradeOrderTransportInfoResp.java

@@ -34,6 +34,9 @@ public class TradeOrderTransportInfoResp implements Serializable {
     @Schema(description = "任务信息列表")
     private List<TaskInfo> tasks = new ArrayList<>();
 
+    @Schema(description = "地磅编号")
+    private Long weighbridgeId;
+
     /**
      * 单车任务信息。
      */

+ 377 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/ParkingPlateInfoService.java

@@ -0,0 +1,377 @@
+package com.sckw.transport.service;
+
+import com.alibaba.fastjson2.JSON;
+import com.sckw.core.common.enums.enums.ErrorCodeEnum;
+import com.sckw.core.exception.BusinessPlatfromException;
+import com.sckw.core.utils.OkHttpUtils;
+import com.sckw.core.utils.TruckNoUtils;
+import com.sckw.transport.model.enuma.ParkingCarTypeEnum;
+import com.sckw.transport.model.enuma.ParkingChargeTypeEnum;
+import com.sckw.transport.model.enuma.ParkingDeductStatusEnum;
+import com.sckw.transport.model.param.ChargeInfoItemResp;
+import com.sckw.transport.model.param.ChargeInfoQueryParam;
+import com.sckw.transport.model.param.EntryNotificationParam;
+import com.sckw.transport.model.param.ExitNotificationParam;
+import com.sckw.transport.model.param.ExitNotificationResp;
+import com.sckw.transport.model.param.ParkingPlatformReportResult;
+import com.sckw.transport.model.param.ParkingPlatformResult;
+import com.sckw.transport.model.param.PlateInfoQueryParam;
+import com.sckw.transport.model.param.PlateInfoResp;
+import com.sckw.transport.util.ParkingPlatformResponseParser;
+import com.sckw.transport.util.ParkingPlatformSignUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 停车平台对接服务
+ */
+@Slf4j
+@Service
+public class ParkingPlateInfoService {
+
+    private static final String LIST_PLATE_INFO_PATH = "/list-plate-info";
+
+    private static final String ENTRY_NOTIFICATION_PATH = "/entry-notification";
+
+    private static final String EXIT_NOTIFICATION_PATH = "/exit-notification";
+
+    private static final String LIST_CHARGE_INFO_PATH = "/list-charge-info";
+
+    private static final int DEDUCT_STATUS_SUCCESS = 1;
+
+    @Value("${parking.platform.base-url:}")
+    private String baseUrl;
+
+    @Value("${parking.platform.park-id:}")
+    private String defaultParkId;
+
+    @Value("${parking.platform.sign-key:665@EOPqkl%hel}")
+    private String signKey;
+
+    /**
+     * 查询车辆信息
+     * <p>
+     * 调用停车平台接口查询指定车牌号的车辆信息,包括车辆类型、入场时间等
+     *
+     * @param param 查询参数,包含车牌号和车场ID
+     * @return 停车平台返回的车辆信息结果
+     * @throws BusinessPlatfromException 当车牌号格式不正确、车场ID为空或接口调用失败时抛出异常
+     */
+    public ParkingPlatformResult<PlateInfoResp> queryPlateInfo(PlateInfoQueryParam param) {
+        // 1. 参数校验与标准化:车牌号格式校验并转换为标准格式
+        String carLicense = normalizeCarLicense(param.getCarLicense());
+        log.debug("车牌号标准化完成,原始值={}, 标准化后={}", param.getCarLicense(), carLicense);
+
+        // 2. 解析车场ID:优先使用传入的车场ID,否则使用默认配置的车场ID
+        String parkId = resolveParkId(param.getParkId());
+        log.debug("车场ID解析完成,parkId={}", parkId);
+
+        // 3. 构建请求参数:包含车场ID、车牌号、时间戳和签名
+        Map<String, String> params = buildSignedParams(parkId, builder -> builder.put("carLicense", carLicense));
+        log.debug("请求参数构建完成,params={}", params);
+
+        // 4. 调用停车平台接口查询车辆信息
+        log.info("调用停车平台车辆信息查询,parkId={}, carLicense={}", parkId, carLicense);
+        String responseBody = postJsonToParkingPlatform(LIST_PLATE_INFO_PATH, params);
+        log.info("停车平台车辆信息查询返回,carLicense={}, response={}", carLicense, responseBody);
+
+        // 5. 解析响应结果
+        ParkingPlatformResult<PlateInfoResp> result = ParkingPlatformResponseParser.parseGeneral(responseBody, PlateInfoResp.class);
+
+        // 6. 数据增强:补充车牌号、车辆类型描述等信息
+        enrichPlateInfo(result.getData(), carLicense);
+
+        return result;
+    }
+
+    /**
+     * 入场通知
+     * <p>
+     * 向停车平台发送车辆入场通知,告知车辆已通过指定通道进入车场
+     *
+     * @param param 入场通知参数,包含车牌号、车场ID、通道ID和业务单号
+     * @return 停车平台返回的处理结果
+     * @throws BusinessPlatfromException 当车牌号格式不正确、车场ID/通道ID/业务单号为空或接口调用失败时抛出异常
+     */
+    public ParkingPlatformResult<Object> entryNotification(EntryNotificationParam param) {
+        log.info("调用停车平台入场通知,入参={}", JSON.toJSONString( param));
+        // 1. 参数校验与标准化:车牌号格式校验并转换为标准格式
+        String carLicense = normalizeCarLicense(param.getCarLicense());
+        log.debug("车牌号标准化完成,原始值={}, 标准化后={}", param.getCarLicense(), carLicense);
+
+        // 2. 解析车场ID:优先使用传入的车场ID,否则使用默认配置的车场ID
+        String parkId = resolveParkId(param.getParkId());
+        log.debug("车场ID解析完成,parkId={}", parkId);
+
+        // 3. 必填参数校验:通道ID和业务单号不能为空
+        String gateId = requireText(param.getGateId(), "通道ID不能为空");
+        String businessId = requireText(param.getBusinessId(), "业务单号不能为空");
+
+        // 4. 构建请求参数:包含车场ID、车牌号、通道ID、业务单号、时间戳和签名
+        Map<String, String> params = buildSignedParams(parkId, builder -> {
+            builder.put("carLicense", carLicense);
+            builder.put("gateId", gateId);
+            builder.put("businessId", businessId);
+        });
+        log.debug("请求参数构建完成,params={}", params);
+
+        // 5. 调用停车平台接口发送入场通知
+        log.info("调用停车平台入场通知,parkId={}, carLicense={}, gateId={}, businessId={}",
+                parkId, carLicense, gateId, businessId);
+        String responseBody = postJsonToParkingPlatform(ENTRY_NOTIFICATION_PATH, params);
+        log.info("停车平台入场通知返回,businessId={}, response={}", businessId, responseBody);
+
+        // 6. 解析响应结果
+        ParkingPlatformResult<Object> result = ParkingPlatformResponseParser.parseGeneral(responseBody, Object.class);
+        log.debug("响应解析完成,success={}", result.isSuccess());
+
+        return result;
+    }
+
+    /**
+     * 出场扣费通知
+     * <p>
+     * 向停车平台发送车辆出场通知,告知车辆已通过指定通道离开车场,并可选择是否进行扣费
+     *
+     * @param param 出场通知参数,包含车牌号、车场ID、通道ID、业务单号和是否需要扣费标识
+     * @return 停车平台返回的出场扣费结果,包含扣费状态和扣费金额等信息
+     * @throws BusinessPlatfromException 当车牌号格式不正确、车场ID/通道ID/业务单号为空、接口调用失败或扣费失败时抛出异常
+     */
+    public ParkingPlatformResult<ExitNotificationResp> exitNotification(ExitNotificationParam param) {
+        log.info("调用停车平台出场扣费通知,入参={}", JSON.toJSONString(param));
+
+        // 1. 参数校验与标准化:车牌号格式校验并转换为标准格式
+        String carLicense = normalizeCarLicense(param.getCarLicense());
+        log.debug("车牌号标准化完成,原始值={}, 标准化后={}", param.getCarLicense(), carLicense);
+
+        // 2. 解析车场ID:优先使用传入的车场ID,否则使用默认配置的车场ID
+        String parkId = resolveParkId(param.getParkId());
+        log.debug("车场ID解析完成,parkId={}", parkId);
+
+        // 3. 必填参数校验:通道ID和业务单号不能为空
+        String gateId = requireText(param.getGateId(), "通道ID不能为空");
+        String businessId = requireText(param.getBusinessId(), "业务单号不能为空");
+
+        // 4. 构建请求参数:包含车场ID、车牌号、通道ID、业务单号、是否扣费、时间戳和签名
+        Map<String, String> params = buildSignedParams(parkId, builder -> {
+            builder.put("carLicense", carLicense);
+            builder.put("gateId", gateId);
+            builder.put("businessId", businessId);
+            if (param.getNeedDeduct() != null) {
+                builder.put("needDeduct", String.valueOf(param.getNeedDeduct()));
+            }
+        });
+        log.debug("请求参数构建完成,params={}", params);
+
+        // 5. 调用停车平台接口发送出场扣费通知
+        log.info("调用停车平台出场扣费通知,parkId={}, carLicense={}, gateId={}, businessId={}, needDeduct={}",
+                parkId, carLicense, gateId, businessId, param.getNeedDeduct());
+        String responseBody = postJsonToParkingPlatform(EXIT_NOTIFICATION_PATH, params);
+        log.info("停车平台出场扣费通知返回,businessId={}, response={}", businessId, responseBody);
+
+        // 6. 解析响应结果
+        ParkingPlatformResult<ExitNotificationResp> result =
+                ParkingPlatformResponseParser.parseGeneral(responseBody, ExitNotificationResp.class);
+        log.debug("响应解析完成,success={}", result.isSuccess());
+
+        // 7. 数据增强:补充车牌号、业务单号、车辆类型描述、扣费状态描述等信息,并校验扣费状态
+        enrichExitNotification(result.getData(), carLicense, businessId);
+        log.debug("数据增强完成,最终结果={}", result.getData());
+
+        return result;
+    }
+
+    /**
+     * 充值扣费明细查询
+     * <p>
+     * 查询指定时间范围内的充值扣费明细记录,支持按车牌号和业务单号筛选
+     *
+     * @param param 查询参数,包含车场ID、车牌号(可选)、业务单号(可选)、开始时间、结束时间、分页参数
+     * @return 停车平台返回的充值扣费明细列表,包含分页信息和每条明细的详细信息
+     * @throws BusinessPlatfromException 当车场ID为空、开始/结束时间为空或接口调用失败时抛出异常
+     */
+    public ParkingPlatformReportResult<ChargeInfoItemResp> listChargeInfo(ChargeInfoQueryParam param) {
+        log.info("调用停车平台充值扣费明细查询,入参={}", JSON.toJSONString(param));
+
+        // 1. 解析车场ID:优先使用传入的车场ID,否则使用默认配置的车场ID
+        String parkId = resolveParkId(param.getParkId());
+        log.debug("车场ID解析完成,parkId={}", parkId);
+
+        // 2. 必填参数校验:开始时间和结束时间不能为空
+        String startTime = requireText(param.getStartTime(), "开始时间不能为空");
+        String endTime = requireText(param.getEndTime(), "结束时间不能为空");
+
+        // 3. 构建请求参数:包含车场ID、车牌号(可选)、业务单号(可选)、时间范围、分页参数、时间戳和签名
+        Map<String, String> params = buildSignedParams(parkId, builder -> {
+            putIfNotBlank(builder, "carLicense", normalizeCarLicenseIfPresent(param.getCarLicense()));
+            putIfNotBlank(builder, "businessId", param.getBusinessId());
+            builder.put("startTime", startTime);
+            builder.put("endTime", endTime);
+            builder.put("start", String.valueOf(param.getStart()));
+            builder.put("limit", String.valueOf(param.getLimit()));
+        });
+        log.debug("请求参数构建完成,params={}", params);
+
+        // 4. 调用停车平台接口查询充值扣费明细
+        log.info("调用停车平台充值扣费明细查询,parkId={}, carLicense={}, businessId={}, startTime={}, endTime={}",
+                parkId, param.getCarLicense(), param.getBusinessId(), startTime, endTime);
+        String responseBody = postJsonToParkingPlatform(LIST_CHARGE_INFO_PATH, params);
+        log.info("停车平台充值扣费明细查询返回,parkId={}, response={}", parkId, responseBody);
+
+        // 5. 解析响应结果
+        ParkingPlatformReportResult<ChargeInfoItemResp> result =
+                ParkingPlatformResponseParser.parseReport(responseBody, ChargeInfoItemResp.class);
+        log.debug("响应解析完成,success={}", result.isSuccess());
+
+        // 6. 数据增强:为每条明细记录补充扣费类型描述
+        if (result.getData() != null) {
+            result.getData().forEach(this::enrichChargeInfoItem);
+            log.debug("数据增强完成,明细记录数={}", result.getData().size());
+        }
+
+        return result;
+    }
+
+    /**
+     * 向停车平台发送POST请求
+     * <p>
+     * 构建完整的请求URL,将参数转换为JSON格式,通过OkHttp发送POST请求到停车平台
+     *
+     * @param path 接口路径,如 /list-plate-info
+     * @param params 请求参数Map,已包含签名信息
+     * @return 停车平台返回的响应体字符串
+     * @throws BusinessPlatfromException 当请求失败或响应为空时抛出异常
+     */
+    private String postJsonToParkingPlatform(String path, Map<String, String> params) {
+        // 1. 构建完整的请求URL
+        String requestUrl = buildRequestUrl(path);
+        log.debug("请求URL构建完成,requestUrl={}", requestUrl);
+
+        // 2. 将参数Map转换为JSON字符串
+        String requestBody = JSON.toJSONString(params);
+
+        // 3. 记录请求信息(包含敏感参数,生产环境需注意脱敏)
+        log.info("停车平台JSON请求,url={}, body={}", requestUrl, requestBody);
+
+        // 4. 发送HTTP POST请求
+        String responseBody = OkHttpUtils.builder()
+                .url(requestUrl)
+                .addHeader("Content-Type", "application/json;charset=UTF-8")
+                .addHeader("Accept", "application/json;charset=UTF-8")
+                .addBodyJsonStr(requestBody)
+                .post(true)
+                .sync();
+        log.debug("HTTP请求发送完成,responseBody={}", responseBody);
+
+        // 5. 校验响应结果
+        if (StringUtils.isBlank(responseBody) || responseBody.startsWith("请求失败")) {
+            log.error("停车平台接口调用失败,url={}, response={}", requestUrl, responseBody);
+            throw new BusinessPlatfromException(ErrorCodeEnum.INTERFACE_PARAM_MISMATCH, "停车平台接口调用失败");
+        }
+
+        log.debug("停车平台接口调用成功,responseLength={}", responseBody.length());
+        return responseBody;
+    }
+
+    private Map<String, String> buildSignedParams(String parkId, java.util.function.Consumer<Map<String, String>> consumer) {
+        Map<String, String> params = new HashMap<>(8);
+        params.put("parkId", parkId);
+        consumer.accept(params);
+        params.put("timestamp", String.valueOf(System.currentTimeMillis()));
+        params.put("sign", ParkingPlatformSignUtil.createSign(params, signKey));
+        return params;
+    }
+
+    private void enrichPlateInfo(PlateInfoResp plateInfo, String carLicense) {
+        if (plateInfo == null) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.RESOURCE_NOT_FOUND, "停车平台未返回车辆信息");
+        }
+        if (StringUtils.isBlank(plateInfo.getCarLicense())) {
+            plateInfo.setCarLicense(carLicense);
+        }
+        if (plateInfo.getCarType() != null) {
+            plateInfo.setCarTypeDesc(ParkingCarTypeEnum.getDescByCode(plateInfo.getCarType()));
+        }
+    }
+
+    private void enrichExitNotification(ExitNotificationResp resp, String carLicense, String businessId) {
+        if (resp == null) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.RESOURCE_NOT_FOUND, "停车平台未返回出场扣费结果");
+        }
+        if (StringUtils.isBlank(resp.getCarLicense())) {
+            resp.setCarLicense(carLicense);
+        }
+        if (StringUtils.isBlank(resp.getBusinessId())) {
+            resp.setBusinessId(businessId);
+        }
+        if (resp.getCarType() != null) {
+            resp.setCarTypeDesc(ParkingCarTypeEnum.getDescByCode(resp.getCarType()));
+        }
+        if (resp.getDeductStatus() != null) {
+            resp.setDeductStatusDesc(ParkingDeductStatusEnum.getDescByCode(resp.getDeductStatus()));
+            if (!Objects.equals(resp.getDeductStatus(), DEDUCT_STATUS_SUCCESS)) {
+                throw new BusinessPlatfromException(ErrorCodeEnum.INTERFACE_PARAM_MISMATCH,
+                        StringUtils.defaultIfBlank(resp.getDeductStatusDesc(), "停车平台出场扣费失败"));
+            }
+        }
+    }
+
+    private void enrichChargeInfoItem(ChargeInfoItemResp item) {
+        if (item != null && item.getChargeType() != null) {
+            item.setChargeTypeDesc(ParkingChargeTypeEnum.getDescByCode(item.getChargeType()));
+        }
+    }
+
+    private void putIfNotBlank(Map<String, String> params, String key, String value) {
+        if (StringUtils.isNotBlank(value)) {
+            params.put(key, value.trim());
+        }
+    }
+
+    private String normalizeCarLicenseIfPresent(String carLicense) {
+        if (StringUtils.isBlank(carLicense)) {
+            return null;
+        }
+        return normalizeCarLicense(carLicense);
+    }
+
+    private String normalizeCarLicense(String carLicense) {
+        if (StringUtils.isBlank(carLicense)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "车牌号码不能为空");
+        }
+        String cleaned = carLicense.trim().toUpperCase();
+        if (!TruckNoUtils.isValidTruckNo(cleaned)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "车牌号格式不正确");
+        }
+        return TruckNoUtils.formatTruckNo(cleaned);
+    }
+
+    private String requireText(String value, String message) {
+        if (StringUtils.isBlank(value)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, message);
+        }
+        return value.trim();
+    }
+
+    private String resolveParkId(String requestParkId) {
+        String parkId = StringUtils.isNotBlank(requestParkId) ? requestParkId.trim() : defaultParkId;
+        if (StringUtils.isBlank(parkId)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "车场ID不能为空");
+        }
+        return parkId;
+    }
+
+    private String buildRequestUrl(String path) {
+        if (StringUtils.isBlank(baseUrl)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "停车平台服务地址未配置");
+        }
+        String normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
+        return normalizedBaseUrl + path;
+    }
+}

+ 27 - 14
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/TradeOrderTransportInfoService.java

@@ -3,9 +3,10 @@ package com.sckw.transport.service;
 import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.sckw.core.common.enums.enums.DictTypeEnum;
+import com.sckw.core.common.enums.enums.ErrorCodeEnum;
+import com.sckw.core.exception.BusinessPlatfromException;
 import com.sckw.core.web.constant.HttpStatus;
 import com.sckw.core.web.response.BaseResult;
-import com.sckw.core.web.response.HttpResult;
 import com.sckw.fleet.api.feign.FleetTruckFeignService;
 import com.sckw.order.api.feign.TradeOrderApi;
 import com.sckw.order.api.model.OrderPara;
@@ -13,21 +14,12 @@ import com.sckw.order.api.model.TradeOrderVo;
 import com.sckw.product.api.dubbo.GoodsInfoService;
 import com.sckw.product.api.model.KwpGoods;
 import com.sckw.system.api.RemoteSystemService;
-import com.sckw.transport.model.KwtLogisticsOrder;
-import com.sckw.transport.model.KwtLogisticsOrderAddress;
-import com.sckw.transport.model.KwtLogisticsOrderGoods;
-import com.sckw.transport.model.KwtWaybillOrder;
-import com.sckw.transport.model.KwtWaybillOrderSubtask;
-import com.sckw.transport.model.KwtWaybillOrderTicket;
+import com.sckw.transport.model.*;
+import com.sckw.transport.model.enuma.WeighbridgeTypeEnum;
 import com.sckw.transport.model.param.TradeOrderTransportQueryReq;
 import com.sckw.transport.model.param.WaybillTransportQueryReq;
 import com.sckw.transport.model.vo.TradeOrderTransportInfoResp;
-import com.sckw.transport.repository.KwtLogisticsOrderAddressRepository;
-import com.sckw.transport.repository.KwtLogisticsOrderGoodsRepository;
-import com.sckw.transport.repository.KwtLogisticsOrderRepository;
-import com.sckw.transport.repository.KwtWaybillOrderRepository;
-import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
-import com.sckw.transport.repository.KwtWaybillOrderTicketRepository;
+import com.sckw.transport.repository.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
@@ -64,6 +56,7 @@ public class TradeOrderTransportInfoService {
     private final KwtWaybillOrderTicketRepository waybillOrderTicketRepository;
     private final KwtLogisticsOrderGoodsRepository logisticsOrderGoodsRepository;
     private final KwtLogisticsOrderAddressRepository logisticsOrderAddressRepository;
+    private final  KwtWaybillOrderWeighbridgeRepository waybillOrderWeighbridgeRepository;
 
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private GoodsInfoService goodsInfoService;
@@ -219,12 +212,32 @@ public class TradeOrderTransportInfoService {
                 Collections.singletonList(waybillOrder),
                 tradeOrder
         );
-        
+
+        // 7. 查询运单过磅信息
+        KwtWaybillOrderWeighbridge waybillOrderWeighbridge = queryWeighbridge(waybillOrder);
+        Long weighbridgeId = waybillOrderWeighbridge.getWeighbridgeId();
+        resp.setWeighbridgeId(weighbridgeId);
+
         log.info("根据运单号查询运输信息完成,waybillNo:{}, taskSize:{}", waybillNo, 
                 Objects.nonNull(resp.getTasks()) ? resp.getTasks().size() : 0);
         return resp;
     }
 
+    /**
+     * 查询过磅信息
+     * @param waybillOrder
+     * @return
+     */
+    private KwtWaybillOrderWeighbridge queryWeighbridge(KwtWaybillOrder waybillOrder) {
+        List<KwtWaybillOrderWeighbridge> weighbridges = waybillOrderWeighbridgeRepository
+                .queryWaybillOrderWeighbridgeByWOrderId(waybillOrder.getId(), WeighbridgeTypeEnum.LOADING.getCode());
+        if (CollectionUtils.isEmpty(weighbridges)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "未查询到地磅信息!");
+        }
+        return weighbridges.get(0);
+    }
+
+
     /**
      * 查询贸易订单详细信息。
      * <p>

+ 85 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/util/ParkingPlatformResponseParser.java

@@ -0,0 +1,85 @@
+package com.sckw.transport.util;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.sckw.core.common.enums.enums.ErrorCodeEnum;
+import com.sckw.core.exception.BusinessPlatfromException;
+import com.sckw.transport.model.param.ParkingPlatformReportExtra;
+import com.sckw.transport.model.param.ParkingPlatformReportResult;
+import com.sckw.transport.model.param.ParkingPlatformResult;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 停车平台响应解析工具
+ */
+public final class ParkingPlatformResponseParser {
+
+    private static final int SUCCESS_CODE = 0;
+
+    private ParkingPlatformResponseParser() {
+    }
+
+    /**
+     * 解析通用类返回
+     */
+    public static <T> ParkingPlatformResult<T> parseGeneral(String responseBody, Class<T> dataClass) {
+        JSONObject jsonObject = parseJson(responseBody);
+        ParkingPlatformResult<T> result = new ParkingPlatformResult<>();
+        result.setCode(jsonObject.getIntValue("code", SUCCESS_CODE));
+        result.setMsg(StringUtils.defaultIfBlank(jsonObject.getString("msg"), "请求成功"));
+        result.setSuccess(jsonObject.getBooleanValue("success", true));
+        validateResult(result.isSuccess(), result.getCode(), result.getMsg());
+
+        if (jsonObject.containsKey("data") && jsonObject.get("data") != null) {
+            result.setData(jsonObject.getObject("data", dataClass));
+        }
+        return result;
+    }
+
+    /**
+     * 解析查询报表类返回
+     */
+    public static <T> ParkingPlatformReportResult<T> parseReport(String responseBody, Class<T> itemClass) {
+        JSONObject jsonObject = parseJson(responseBody);
+        ParkingPlatformReportResult<T> result = new ParkingPlatformReportResult<>();
+        result.setCode(jsonObject.getIntValue("code", SUCCESS_CODE));
+        result.setMsg(StringUtils.defaultIfBlank(jsonObject.getString("msg"), "请求成功"));
+        result.setSuccess(jsonObject.getBooleanValue("success", true));
+        validateResult(result.isSuccess(), result.getCode(), result.getMsg());
+
+        Object data = jsonObject.get("data");
+        if (data instanceof JSONArray dataArray) {
+            result.setData(dataArray.toJavaList(itemClass));
+        } else {
+            result.setData(new ArrayList<>());
+        }
+
+        JSONObject extraObject = jsonObject.getJSONObject("extra");
+        if (extraObject != null) {
+            result.setExtra(extraObject.toJavaObject(ParkingPlatformReportExtra.class));
+        }
+        return result;
+    }
+
+    private static JSONObject parseJson(String responseBody) {
+        if (StringUtils.isBlank(responseBody)) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.INTERFACE_PARAM_MISMATCH, "停车平台返回数据为空");
+        }
+        JSONObject jsonObject = JSON.parseObject(responseBody);
+        if (jsonObject == null) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.INTERFACE_PARAM_MISMATCH, "停车平台返回数据格式异常");
+        }
+        return jsonObject;
+    }
+
+    private static void validateResult(boolean success, int code, String msg) {
+        if (!success || code != SUCCESS_CODE) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.INTERFACE_PARAM_MISMATCH,
+                    StringUtils.defaultIfBlank(msg, "停车平台接口调用失败"));
+        }
+    }
+}

+ 59 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/util/ParkingPlatformSignUtil.java

@@ -0,0 +1,59 @@
+package com.sckw.transport.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+/**
+ * 停车平台签名工具,实现规则与平台 MD5 签名 DEMO 一致:
+ * <ol>
+ *     <li>参数按 key 字典序排序</li>
+ *     <li>排除 sign、key、appId 及空值</li>
+ *     <li>拼接 key1=value1&key2=value2&{appSecret}</li>
+ *     <li>MD5 后转大写</li>
+ * </ol>
+ */
+@Slf4j
+public final class ParkingPlatformSignUtil {
+
+    private static final Set<String> EXCLUDE_KEYS = Set.of("sign", "key", "appId");
+
+    private ParkingPlatformSignUtil() {
+    }
+
+    /**
+     * 生成停车平台请求签名
+     *
+     * @param params  请求参数
+     * @param signKey 签名密钥
+     * @return 大写 MD5 签名
+     */
+    public static String createSign(Map<String, String> params, String signKey) {
+        TreeMap<String, String> sortedParams = new TreeMap<>();
+        if (params != null) {
+            params.forEach((key, value) -> {
+                if (StringUtils.isBlank(key) || EXCLUDE_KEYS.contains(key)) {
+                    return;
+                }
+                if (value == null || StringUtils.isEmpty(value)) {
+                    return;
+                }
+                sortedParams.put(key, value);
+            });
+        }
+        String stringSignTemp = sortedParams.entrySet().stream()
+                .map(entry -> entry.getKey() + "=" + entry.getValue())
+                .collect(Collectors.joining("&"))
+                .concat("&")
+                .concat(signKey);
+        String sign = DigestUtils.md5Hex(stringSignTemp.getBytes(StandardCharsets.UTF_8)).toUpperCase();
+        log.info("停车平台MD5签名,待签名字符串: {}, sign: {}", stringSignTemp, sign);
+        return sign;
+    }
+}

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

@@ -27,3 +27,9 @@ spring:
           - dataId: sckw-common.yml
             group: sckw-ng-service-platform
             refresh: true
+# 停车平台配置
+parking:
+  platform:
+    base-url: ${PARKING_PLATFORM_BASE_URL:http://beta.qtck.net/openapi/v2/api/kll}
+    park-id: ${PARKING_PLATFORM_PARK_ID:2}
+    sign-key: ${PARKING_PLATFORM_SIGN_KEY:665@EOPqkl%hel}

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

@@ -26,3 +26,8 @@ spring:
           - dataId: sckw-common.yml
             group: sckw-ng-service-platform
             refresh: true
+parking:
+  platform:
+    base-url: ${PARKING_PLATFORM_BASE_URL:http://api.qtck.net/openpai/v2/api/kll}
+    park-id: ${PARKING_PLATFORM_PARK_ID:2869}
+    sign-key: ${PARKING_PLATFORM_SIGN_KEY:665@EOPqkl%hel}