Jelajahi Sumber

feat(charging): 实现完整充电业务控制器和第三方推送接收服务

- 新增AESCryptoUtils工具类,实现AES-128-CBC-PKCS5Padding加解密功能
- 添加Token实体ApiToken,支持有效性和剩余时间检查
- 新增响应VO类ChargeResponseVO封装充电响应信息
- 重构ChargingController,提供充电业务相关API接口
- 删除ChargingOrder及其相关服务和Mapper,优化代码结构
- 定义ChargingReceptionService接口及实现ChargingReceptionServiceImpl
- 实现多种推送数据的接收处理方法,包含启动充电结果、充电状态、停止充电、充电订单和设备状态推送
- 实现获取Token接口,包含签名校验和Token生成逻辑
- 添加详细日志记录与异常处理,保证服务稳定性
SheepHy 1 Minggu lalu
induk
melakukan
d9ade8ba94
40 mengubah file dengan 2833 tambahan dan 581 penghapusan
  1. 10 0
      pom.xml
  2. 52 0
      src/main/java/com/zsElectric/openapi/business/entity/ApiToken.java
  3. 31 0
      src/main/java/com/zsElectric/openapi/business/entity/ChargeResponseVO.java
  4. 0 118
      src/main/java/com/zsElectric/openapi/business/entity/ChargingOrder.java
  5. 0 99
      src/main/java/com/zsElectric/openapi/business/entity/ChargingStation.java
  6. 39 0
      src/main/java/com/zsElectric/openapi/business/entity/ConnectorInfo.java
  7. 19 0
      src/main/java/com/zsElectric/openapi/business/entity/ConnectorStatusInfo.java
  8. 45 0
      src/main/java/com/zsElectric/openapi/business/entity/EquipmentInfo.java
  9. 32 0
      src/main/java/com/zsElectric/openapi/business/entity/QueryTokenParms.java
  10. 22 0
      src/main/java/com/zsElectric/openapi/business/entity/QueryTokenRequestParms.java
  11. 47 0
      src/main/java/com/zsElectric/openapi/business/entity/QueryTokenResponseData.java
  12. 53 0
      src/main/java/com/zsElectric/openapi/business/entity/RequestParmsEntity.java
  13. 47 0
      src/main/java/com/zsElectric/openapi/business/entity/ResponseParmsEntity.java
  14. 116 0
      src/main/java/com/zsElectric/openapi/business/entity/StationInfo.java
  15. 18 0
      src/main/java/com/zsElectric/openapi/business/entity/StationStatusInfo.java
  16. 0 1
      src/main/java/com/zsElectric/openapi/business/mapper/ChargingOrderMapper.java
  17. 0 1
      src/main/java/com/zsElectric/openapi/business/mapper/ChargingStationMapper.java
  18. 77 0
      src/main/java/com/zsElectric/openapi/business/service/ChargingBusinessService.java
  19. 0 122
      src/main/java/com/zsElectric/openapi/business/service/ChargingOrderService.java
  20. 48 0
      src/main/java/com/zsElectric/openapi/business/service/ChargingReceptionService.java
  21. 0 62
      src/main/java/com/zsElectric/openapi/business/service/ChargingStationService.java
  22. 136 0
      src/main/java/com/zsElectric/openapi/business/service/impl/ChargingBusinessServiceImpl.java
  23. 338 0
      src/main/java/com/zsElectric/openapi/business/service/impl/ChargingReceptionServiceImpl.java
  24. 203 0
      src/main/java/com/zsElectric/openapi/common/AESCryptoUtils.java
  25. 184 0
      src/main/java/com/zsElectric/openapi/common/ChargingUtil.java
  26. 160 0
      src/main/java/com/zsElectric/openapi/common/ConnectivityConstants.java
  27. 298 0
      src/main/java/com/zsElectric/openapi/common/HmacMD5Util.java
  28. 191 0
      src/main/java/com/zsElectric/openapi/common/JwtTokenUtil.java
  29. 99 74
      src/main/java/com/zsElectric/openapi/controller/ChargingController.java
  30. 86 0
      src/main/java/com/zsElectric/openapi/controller/LinkDataController.java
  31. 0 103
      src/main/java/com/zsElectric/openapi/controller/OrderController.java
  32. 52 0
      src/main/java/com/zsElectric/openapi/dto/StartChargingRequestDTO.java
  33. 64 0
      src/main/java/com/zsElectric/openapi/vo/ChargingPricePolicyVO.java
  34. 162 0
      src/main/java/com/zsElectric/openapi/vo/ChargingStatusQueryResponseVO.java
  35. 40 0
      src/main/java/com/zsElectric/openapi/vo/EquipmentAuthResponseVO.java
  36. 31 0
      src/main/java/com/zsElectric/openapi/vo/QueryStationStatusVO.java
  37. 32 0
      src/main/java/com/zsElectric/openapi/vo/QueryStationsInfoVO.java
  38. 53 0
      src/main/java/com/zsElectric/openapi/vo/StartChargingResponseVO.java
  39. 47 0
      src/main/java/com/zsElectric/openapi/vo/StopChargingOperationResponseVO.java
  40. 1 1
      src/main/resources/sql/init.sql

+ 10 - 0
pom.xml

@@ -168,6 +168,16 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>3.51.0</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>

+ 52 - 0
src/main/java/com/zsElectric/openapi/business/entity/ApiToken.java

@@ -0,0 +1,52 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+/**
+ * Token实体类
+ * 封装Token相关信息及有效性检查[2](@ref)
+ */
+@Data
+public class ApiToken {
+    /**
+     * 获取的凭证
+     */
+    private String accessToken;
+    
+    /**
+     * 凭证有效期(单位:秒)
+     */
+    private Integer tokenAvailableTime;
+    
+    /**
+     * 令牌获取时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime obtainTime;
+    
+    /**
+     * 令牌过期时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime expireTime;
+    
+    /**
+     * 检查令牌是否有效
+     * 提前60秒过期,避免网络延迟等问题导致的令牌无效[2](@ref)
+     * @return true-有效,false-无效
+     */
+    public boolean isValid() {
+        return LocalDateTime.now().plusSeconds(60).isBefore(expireTime);
+    }
+    
+    /**
+     * 计算剩余有效时间(秒)
+     */
+    public long getRemainingSeconds() {
+        return Duration.between(LocalDateTime.now(), expireTime).getSeconds();
+    }
+}

+ 31 - 0
src/main/java/com/zsElectric/openapi/business/entity/ChargeResponseVO.java

@@ -0,0 +1,31 @@
+
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "响应VO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ChargeResponseVO {
+
+    @Schema(description = "充电订单号(格式:运营商ID+唯一编号)",
+            example = "123456789201805141125123456")
+    @JsonProperty("StartChargeSeq")
+    private String StartChargeSeq;
+
+    @Schema(description = "操作结果(0:成功;1:失败)",
+            example = "0",
+            allowableValues = {"0", "1"})
+    @JsonProperty("SuccStat")
+    private Integer SuccStat;
+
+    @Schema(description = "失败原因(0:无;1:接收失败)",
+            example = "0")
+    @JsonProperty("FailReason")
+    private Integer FailReason;
+}

+ 0 - 118
src/main/java/com/zsElectric/openapi/business/entity/ChargingOrder.java

@@ -1,118 +0,0 @@
-package com.zsElectric.openapi.business.entity;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 充电订单实体(业务系统数据)
- * 注意: 使用@DS("business")注解指定使用业务数据库
- *
- * @author Ray.Hao
- */
-@Data
-@TableName("charging_order")
-public class ChargingOrder implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 订单ID
-     */
-    @TableField("id")
-    private Long id;
-
-    /**
-     * 订单编号
-     */
-    @TableField("order_no")
-    private String orderNo;
-
-    /**
-     * 站点ID
-     */
-    @TableField("station_id")
-    private Long stationId;
-
-    /**
-     * 充电桩ID
-     */
-    @TableField("pile_id")
-    private Long pileId;
-
-    /**
-     * 用户ID
-     */
-    @TableField("user_id")
-    private Long userId;
-
-    /**
-     * 用户手机号
-     */
-    @TableField("user_phone")
-    private String userPhone;
-
-    /**
-     * 订单状态:0-待支付,1-充电中,2-已完成,3-已取消,4-已退款
-     */
-    @TableField("status")
-    private Integer status;
-
-    /**
-     * 充电开始时间
-     */
-    @TableField("start_time")
-    private LocalDateTime startTime;
-
-    /**
-     * 充电结束时间
-     */
-    @TableField("end_time")
-    private LocalDateTime endTime;
-
-    /**
-     * 充电时长(秒)
-     */
-    @TableField("duration")
-    private Long duration;
-
-    /**
-     * 充电量(度)
-     */
-    @TableField("electricity")
-    private BigDecimal electricity;
-
-    /**
-     * 订单金额(元)
-     */
-    @TableField("amount")
-    private BigDecimal amount;
-
-    /**
-     * 实付金额(元)
-     */
-    @TableField("pay_amount")
-    private BigDecimal payAmount;
-
-    /**
-     * 支付状态:0-未支付,1-已支付,2-已退款
-     */
-    @TableField("pay_status")
-    private Integer payStatus;
-
-    /**
-     * 创建时间
-     */
-    @TableField("create_time")
-    private LocalDateTime createTime;
-
-    /**
-     * 更新时间
-     */
-    @TableField("update_time")
-    private LocalDateTime updateTime;
-}

+ 0 - 99
src/main/java/com/zsElectric/openapi/business/entity/ChargingStation.java

@@ -1,99 +0,0 @@
-package com.zsElectric.openapi.business.entity;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.time.LocalDateTime;
-
-/**
- * 充电站实体(业务系统数据)
- * 注意: 使用@DS("business")注解指定使用业务数据库
- *
- * @author Ray.Hao
- */
-@Data
-@TableName("charging_station")
-public class ChargingStation implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 主键ID
-     */
-    @TableField("id")
-    private Long id;
-
-    /**
-     * 站点名称
-     */
-    @TableField("station_name")
-    private String stationName;
-
-    /**
-     * 站点编码
-     */
-    @TableField("station_code")
-    private String stationCode;
-
-    /**
-     * 经度
-     */
-    @TableField("longitude")
-    private String longitude;
-
-    /**
-     * 纬度
-     */
-    @TableField("latitude")
-    private String latitude;
-
-    /**
-     * 省份
-     */
-    @TableField("province")
-    private String province;
-
-    /**
-     * 城市
-     */
-    @TableField("city")
-    private String city;
-
-    /**
-     * 区县
-     */
-    @TableField("district")
-    private String district;
-
-    /**
-     * 详细地址
-     */
-    @TableField("address")
-    private String address;
-
-    /**
-     * 联系电话
-     */
-    @TableField("phone")
-    private String phone;
-
-    /**
-     * 运营状态:0-停运,1-运营中
-     */
-    @TableField("status")
-    private Integer status;
-
-    /**
-     * 创建时间
-     */
-    @TableField("create_time")
-    private LocalDateTime createTime;
-
-    /**
-     * 更新时间
-     */
-    @TableField("update_time")
-    private LocalDateTime updateTime;
-}

+ 39 - 0
src/main/java/com/zsElectric/openapi/business/entity/ConnectorInfo.java

@@ -0,0 +1,39 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 充电接口信息实体类
+ */
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ConnectorInfo {
+
+    /** 充电接口编码 - 充电桩+接口序号,如3702120244102_1 */
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    /** 充电接口类型 */
+    @JsonProperty("ConnectorType")
+    private Integer ConnectorType;
+
+    /** 额定功率(单位:W) */
+    @JsonProperty("RatedPower")
+    private BigDecimal RatedPower;
+
+    /** 额定电压(单位:V) */
+    @JsonProperty("RatedVoltage")
+    private BigDecimal RatedVoltage;
+
+    /** 额定电流(单位:A) */
+    @JsonProperty("RatedCurrent")
+    private BigDecimal RatedCurrent;
+
+    /** 国标版本 - 2011,2015 */
+    @JsonProperty("NationalStandards")
+    private String NationalStandards;
+}

+ 19 - 0
src/main/java/com/zsElectric/openapi/business/entity/ConnectorStatusInfo.java

@@ -0,0 +1,19 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ConnectorStatusInfo {
+
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    /**
+     * 0-离网,1-空闲,2-占用(未充电),3-占用(充电中),4-占用(预约锁定),255-故障
+     * */
+    @JsonProperty("Status")
+    private Integer Status;
+}

+ 45 - 0
src/main/java/com/zsElectric/openapi/business/entity/EquipmentInfo.java

@@ -0,0 +1,45 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 充电设备信息实体类
+ */
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class EquipmentInfo {
+
+    /** 设备ID - 运营商自定义的唯一编码,≤64字符 */
+    @JsonProperty("EquipmentID")
+    private String EquipmentID;
+
+    /** 设备名称 - ≤128字符 */
+    @JsonProperty("EquipmentName")
+    private String EquipmentName;
+
+    /** 设备型号 - ≤64字符 */
+    @JsonProperty("EquipmentModel")
+    private String EquipmentModel;
+
+    /** 设备类型 - 0:直流桩 1:交流桩 */
+    @JsonProperty("EquipmentType")
+    private Integer EquipmentType;
+
+    /** 设备厂商名称 - ≤64字符 */
+    @JsonProperty("ManufacturerName")
+    private String ManufacturerName;
+
+    /** 设备生产日期 */
+    @JsonProperty("ManufacturerDate")
+    private String ManufacturerDate;
+
+    /** 充电接口数量 */
+    @JsonProperty("ConnectorNums")
+    private Integer ConnectorNums;
+
+    /** 充电接口信息列表 */
+    @JsonProperty("ConnectorInfos")
+    private java.util.List<ConnectorInfo> ConnectorInfos;
+}

+ 32 - 0
src/main/java/com/zsElectric/openapi/business/entity/QueryTokenParms.java

@@ -0,0 +1,32 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+@Accessors(chain = true)
+@ToString
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class QueryTokenParms implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商ID
+     */
+    @JsonProperty("OperatorID")
+    private String OperatorID;
+
+    /**
+     * 运营商密钥
+     */
+    @JsonProperty("OperatorSecret")
+    private String OperatorSecret;
+}

+ 22 - 0
src/main/java/com/zsElectric/openapi/business/entity/QueryTokenRequestParms.java

@@ -0,0 +1,22 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class QueryTokenRequestParms implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty("OperatorID")
+    private String OperatorID;
+
+    @JsonProperty("OperatorSecret")
+    private String OperatorSecret;
+}

+ 47 - 0
src/main/java/com/zsElectric/openapi/business/entity/QueryTokenResponseData.java

@@ -0,0 +1,47 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class QueryTokenResponseData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商ID
+     */
+    @JsonProperty("OperatorID")
+    private String OperatorID;
+
+    /**
+     * 响应状态 0-成功,1-失败
+     */
+    @JsonProperty("SuccStat")
+    private Integer SuccStat;
+
+    /**
+     * 访问令牌
+     */
+    @JsonProperty("AccessToken")
+    private String AccessToken;
+
+    /**
+     * 令牌有效期,单位秒
+     */
+    @JsonProperty("TokenAvailableTime")
+    private Integer TokenAvailableTime;
+
+    /**
+     * 错误原因 0-无,1-OperatorID无效
+     */
+    @JsonProperty("FailReason")
+    private Integer FailReason;
+
+}

+ 53 - 0
src/main/java/com/zsElectric/openapi/business/entity/RequestParmsEntity.java

@@ -0,0 +1,53 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author wzq
+ * @date 2025/11/25
+ * @description 请求参数实体
+ */
+@Data
+@Accessors(chain = true)
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class RequestParmsEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商标识
+     */
+    @JsonProperty("OperatorID")
+    private String OperatorID;
+
+    /**
+     * 加密后的参数
+     */
+    @JsonProperty("Data")
+    private String Data;
+
+    /**
+     * 时间戳
+     */
+    @JsonProperty("TimeStamp")
+    private String TimeStamp;
+
+    /**
+     * 序列号
+     */
+    @JsonProperty("Seq")
+    private String Seq;
+
+    /**
+     * 签名
+     */
+    @JsonProperty("Sig")
+    private String Sig;
+}

+ 47 - 0
src/main/java/com/zsElectric/openapi/business/entity/ResponseParmsEntity.java

@@ -0,0 +1,47 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ResponseParmsEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 响应状态
+     * -1:系统繁忙,此时请求方稍后重试
+     * 0:请求成功
+     * 4001:签名错误
+     * 4002:Token错误
+     * 4003:参数不合法,缺少必需的示例:OperatorID、Sig、TimeStamp、Data、Seq五个参数
+     * 4004:请求的业务参数不合法,各接口定义自己的必须参数
+     * 500:系统异常
+     */
+    @JsonProperty("Ret")
+    private Integer Ret;
+
+    /**
+     * 响应消息
+     */
+    @JsonProperty("Msg")
+    private String Msg;
+
+    /**
+     * 响应加密数据
+     */
+    @JsonProperty("Data")
+    private String Data;
+
+    /**
+     * 响应签名
+     */
+    @JsonProperty("Sig")
+    private String Sig;
+}

+ 116 - 0
src/main/java/com/zsElectric/openapi/business/entity/StationInfo.java

@@ -0,0 +1,116 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import java.util.List;
+
+/**
+ * 充电站信息实体类 - 字段首字母大写格式
+ */
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class StationInfo {
+
+    /** 充电站ID - 运营商自定义的唯一编码,≤64字符 */
+    @JsonProperty("StationID")
+    private String StationID;
+
+    /** 运营商ID - 9字符 */
+    @JsonProperty("OperatorID")
+    private String OperatorID;
+
+    /** 设备所属方ID - 设备所属运营平台组织机构代码,9字符 */
+    @JsonProperty("EquipmentOwnerID")
+    private String EquipmentOwnerID;
+
+    /** 充电站名称 - ≤128字符 */
+    @JsonProperty("StationName")
+    private String StationName;
+
+    /** 充电站国家代码 - 比如CN,≤32字符 */
+    @JsonProperty("CountryCode")
+    private String CountryCode;
+
+    /** 充电站省市辖区编码 - 参照GB/T2260-2015,20字符 */
+    @JsonProperty("AreaCode")
+    private String AreaCode;
+
+    /** 详细地址 - ≤255字符 */
+    @JsonProperty("Address")
+    private String Address;
+
+    /** 站点电话 - ≤128字符 */
+    @JsonProperty("StationTel")
+    private String StationTel;
+
+    /** 服务电话 - 平台服务电话,≤128字符 */
+    @JsonProperty("ServiceTel")
+    private String ServiceTel;
+
+    /** 站点类型 */
+    @JsonProperty("StationType")
+    private Integer StationType;
+
+    /** 站点状态 */
+    @JsonProperty("StationStatus")
+    private Integer StationStatus;
+
+    /** 车位数量 - 可停放进行充电的车位总数 */
+    @JsonProperty("ParkNums")
+    private Integer ParkNums;
+
+    /** 经度 - GCJ-02坐标系,保留小数点后6位 */
+    @JsonProperty("StationLng")
+    private Double StationLng;
+
+    /** 纬度 - GCJ-02坐标系,保留小数点后6位 */
+    @JsonProperty("StationLat")
+    private Double StationLat;
+
+    /** 站点引导 - ≤1024字符 */
+    @JsonProperty("SiteGuide")
+    private String SiteGuide;
+
+    /** 建设场所 */
+    @JsonProperty("Construction")
+    private Integer Construction;
+
+    /** 站点照片URL列表 */
+    @JsonProperty("Pictures")
+    private List<String> Pictures;
+
+    /** 营业时间描述 - ≤128字符 */
+    @JsonProperty("BusineHours")
+    private String BusineHours;
+
+    /** 充电电费率描述 - ≤255字符 */
+    @JsonProperty("ElectricityFee")
+    private String ElectricityFee;
+
+    /** 服务费率描述 - ≤255字符 */
+    @JsonProperty("ServiceFee")
+    private String ServiceFee;
+
+    /** 停车费率描述 - ≤255字符 */
+    @JsonProperty("ParkFee")
+    private String ParkFee;
+
+    /** 支付方式描述 - ≤64字符 */
+    @JsonProperty("Payment")
+    private String Payment;
+
+    /** 是否支持预约 - 0-不支持,1-支持 */
+    @JsonProperty("SupportOrder")
+    private Integer SupportOrder;
+
+    /** 备注信息 - ≤255字符 */
+    @JsonProperty("Remark")
+    private String Remark;
+
+    /** 充电设备信息列表 */
+    @JsonProperty("EquipmentInfos")
+    private List<EquipmentInfo> EquipmentInfos;
+}

+ 18 - 0
src/main/java/com/zsElectric/openapi/business/entity/StationStatusInfo.java

@@ -0,0 +1,18 @@
+package com.zsElectric.openapi.business.entity;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class StationStatusInfo {
+
+    @JsonProperty("StationID")
+    private String StationID;
+
+    @JsonProperty("ConnectorStatusInfos")
+    private List<ConnectorStatusInfo> ConnectorStatusInfos;
+}

+ 0 - 1
src/main/java/com/zsElectric/openapi/business/mapper/ChargingOrderMapper.java

@@ -2,7 +2,6 @@ package com.zsElectric.openapi.business.mapper;
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.zsElectric.openapi.business.entity.ChargingOrder;
 import org.apache.ibatis.annotations.Mapper;
 
 /**

+ 0 - 1
src/main/java/com/zsElectric/openapi/business/mapper/ChargingStationMapper.java

@@ -2,7 +2,6 @@ package com.zsElectric.openapi.business.mapper;
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.zsElectric.openapi.business.entity.ChargingStation;
 import org.apache.ibatis.annotations.Mapper;
 
 /**

+ 77 - 0
src/main/java/com/zsElectric/openapi/business/service/ChargingBusinessService.java

@@ -0,0 +1,77 @@
+package com.zsElectric.openapi.business.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.zsElectric.openapi.business.entity.ApiToken;
+import com.zsElectric.openapi.dto.StartChargingRequestDTO;
+import com.zsElectric.openapi.vo.*;
+
+import java.util.List;
+
+/**
+ * 充电业务服务接口
+ *
+ * @author Ray.Hao
+ */
+public interface ChargingBusinessService {
+
+    /**
+     * 查询业务策略信息
+     *
+     * @param EquipBizSeq  设备业务流水号
+     * @param ConnectorID 充电设备接口编码
+     * @return 充电价格策略
+     */
+    ChargingPricePolicyVO queryEquipBusinessPolicy(String EquipBizSeq, String ConnectorID) throws JsonProcessingException;
+
+    /**
+     * 请求设备认证
+     *
+     * @param EquipAuthSeq 设备认证流水号
+     * @param ConnectorID 充电设备接口编码
+     * @return 设备认证响应
+     */
+    EquipmentAuthResponseVO queryEquipAuth(String EquipAuthSeq, String ConnectorID) throws JsonProcessingException;
+
+    /**
+     * 查询充电站信息
+     *
+     * @param LastQueryTime 最后一次查询时间
+     * @param PageNo       页码
+     * @param PageSize      页大小
+     * @return 充电站信息列表
+     */
+    QueryStationsInfoVO queryStationsInfo(String LastQueryTime, Integer PageNo, Integer PageSize) throws JsonProcessingException;
+
+    /**
+     * 设备接口状态查询
+     *
+     * @param stationIDs 充电站ID列表
+     * @return 充电站状态列表
+     */
+    QueryStationStatusVO queryStationStatus(List<String> stationIDs) throws JsonProcessingException;
+
+    /**
+     * 请求启动充电
+     *
+     * @param requestDTO 启动充电请求参数
+     * @return 启动充电响应
+     */
+    StartChargingResponseVO startCharging(StartChargingRequestDTO requestDTO) throws JsonProcessingException;
+
+    /**
+     * 查询充电状态
+     *
+     * @param StartChargeSeq 充电订单号
+     * @return 充电状态查询响应
+     */
+    ChargingStatusQueryResponseVO queryChargingStatus(String StartChargeSeq) throws JsonProcessingException;
+
+    /**
+     * 请求停止充电
+     *
+     * @param StartChargeSeq 充电订单号
+     * @param ConnectorID   充电设备接口编码
+     * @return 停止充电操作响应
+     */
+    StopChargingOperationResponseVO stopCharging(String StartChargeSeq, String ConnectorID) throws JsonProcessingException;
+}

+ 0 - 122
src/main/java/com/zsElectric/openapi/business/service/ChargingOrderService.java

@@ -1,122 +0,0 @@
-package com.zsElectric.openapi.business.service;
-
-import com.baomidou.dynamic.datasource.annotation.DS;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zsElectric.openapi.business.entity.ChargingOrder;
-import com.zsElectric.openapi.business.mapper.ChargingOrderMapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-/**
- * 充电订单服务(业务系统数据)
- * 使用@DS("business")注解指定使用业务数据库
- *
- * @author Ray.Hao
- */
-@Slf4j
-@Service
-@DS("business") // 指定使用业务数据库
-public class ChargingOrderService extends ServiceImpl<ChargingOrderMapper, ChargingOrder> {
-
-    /**
-     * 根据订单ID查询订单详情
-     *
-     * @param orderId 订单ID
-     * @return 订单详情
-     */
-    public ChargingOrder getOrderById(Long orderId) {
-        log.info("查询订单详情, orderId={}", orderId);
-        return getById(orderId);
-    }
-
-    /**
-     * 根据订单编号查询订单详情
-     *
-     * @param orderNo 订单编号
-     * @return 订单详情
-     */
-    public ChargingOrder getOrderByOrderNo(String orderNo) {
-        log.info("根据订单编号查询订单详情, orderNo={}", orderNo);
-        return lambdaQuery()
-                .eq(ChargingOrder::getOrderNo, orderNo)
-                .one();
-    }
-
-    /**
-     * 根据用户ID查询订单列表
-     *
-     * @param userId 用户ID
-     * @return 订单列表
-     */
-    public List<ChargingOrder> getOrdersByUserId(Long userId) {
-        log.info("根据用户ID查询订单列表, userId={}", userId);
-        return lambdaQuery()
-                .eq(ChargingOrder::getUserId, userId)
-                .orderByDesc(ChargingOrder::getCreateTime)
-                .list();
-    }
-
-    /**
-     * 根据充电站ID查询订单列表
-     *
-     * @param stationId 充电站ID
-     * @return 订单列表
-     */
-    public List<ChargingOrder> getOrdersByStationId(Long stationId) {
-        log.info("根据充电站ID查询订单列表, stationId={}", stationId);
-        return lambdaQuery()
-                .eq(ChargingOrder::getStationId, stationId)
-                .orderByDesc(ChargingOrder::getCreateTime)
-                .list();
-    }
-
-    /**
-     * 根据订单状态查询订单列表
-     *
-     * @param status 订单状态
-     * @return 订单列表
-     */
-    public List<ChargingOrder> getOrdersByStatus(Integer status) {
-        log.info("根据订单状态查询订单列表, status={}", status);
-        return lambdaQuery()
-                .eq(ChargingOrder::getStatus, status)
-                .orderByDesc(ChargingOrder::getCreateTime)
-                .list();
-    }
-
-    /**
-     * 根据订单编号或用户手机号查询订单列表
-     *
-     * @param orderNo     订单编号(可选)
-     * @param userPhone   用户手机号(可选)
-     * @param status      订单状态(可选)
-     * @param page        页码
-     * @param size        每页数量
-     * @return 订单列表
-     */
-    public List<ChargingOrder> searchOrders(String orderNo, String userPhone, Integer status, int page, int size) {
-        log.info("搜索订单列表, orderNo={}, userPhone={}, status={}, page={}, size={}", orderNo, userPhone, status, page, size);
-
-        LambdaQueryWrapper<ChargingOrder> wrapper = new LambdaQueryWrapper<>();
-
-        if (orderNo != null && !orderNo.isEmpty()) {
-            wrapper.like(ChargingOrder::getOrderNo, orderNo);
-        }
-
-        if (userPhone != null && !userPhone.isEmpty()) {
-            wrapper.eq(ChargingOrder::getUserPhone, userPhone);
-        }
-
-        if (status != null) {
-            wrapper.eq(ChargingOrder::getStatus, status);
-        }
-
-        wrapper.orderByDesc(ChargingOrder::getCreateTime)
-                .last("LIMIT " + size + " OFFSET " + (page - 1) * size);
-
-        return list(wrapper);
-    }
-}

+ 48 - 0
src/main/java/com/zsElectric/openapi/business/service/ChargingReceptionService.java

@@ -0,0 +1,48 @@
+package com.zsElectric.openapi.business.service;
+
+
+import com.zsElectric.openapi.business.entity.RequestParmsEntity;
+import com.zsElectric.openapi.business.entity.ResponseParmsEntity;
+
+/**
+ * 接收互联互通(平台)推送数据接口
+ * */
+public interface ChargingReceptionService {
+
+    /**
+     * <p>2.4 推送启动充电结果</p>
+     * @author SheepHy
+     * @param requestDTO,参数为类则包含{@link RequestParmsEntity}
+     * @return 停推送启动充电结果VO
+     */
+    ResponseParmsEntity chargeResponse(RequestParmsEntity requestDTO);
+
+    /**
+     * 2.6 推送充电状态
+     * */
+    ResponseParmsEntity chargeStatusResponse(RequestParmsEntity requestDTO);
+
+    /**
+     * 2.8 推送停止充电结果
+     * */
+    ResponseParmsEntity stopChargeResponse(RequestParmsEntity requestDTO);
+
+    /**
+     * 2.9 推送充电订单信息
+     * */
+    ResponseParmsEntity chargeOrderResponse(RequestParmsEntity requestDTO) throws Exception;
+
+    /**
+     * 3.2 设备状态变化推送
+     * */
+    ResponseParmsEntity stationStatus(RequestParmsEntity requestDTO);
+
+    /**
+     * 获取Token
+     * Token作为全局唯一凭证,调用各接口时均需要使用,该接口作为双方获取Token的接口,双方均需要实现。
+     * @author wzq
+     * @param request,参数为类则包含{@link RequestParmsEntity}
+     * @return ResponseParmsEntity
+     */
+    ResponseParmsEntity getToken(RequestParmsEntity request) throws Exception;
+}

+ 0 - 62
src/main/java/com/zsElectric/openapi/business/service/ChargingStationService.java

@@ -1,62 +0,0 @@
-package com.zsElectric.openapi.business.service;
-
-import com.baomidou.dynamic.datasource.annotation.DS;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zsElectric.openapi.business.entity.ChargingStation;
-import com.zsElectric.openapi.business.mapper.ChargingStationMapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-/**
- * 充电站服务(业务系统数据)
- * 使用@DS("business")注解指定使用业务数据库
- *
- * @author Ray.Hao
- */
-@Slf4j
-@Service
-@DS("business") // 指定使用业务数据库
-public class ChargingStationService extends ServiceImpl<ChargingStationMapper, ChargingStation> {
-
-    /**
-     * 获取所有运营中的充电站列表
-     *
-     * @return 充电站列表
-     */
-    public List<ChargingStation> getActiveStations() {
-        log.info("查询运营中的充电站列表");
-        return lambdaQuery()
-                .eq(ChargingStation::getStatus, 1)
-                .orderByAsc(ChargingStation::getId)
-                .list();
-    }
-
-    /**
-     * 根据ID获取充电站详情
-     *
-     * @param stationId 充电站ID
-     * @return 充电站详情
-     */
-    public ChargingStation getStationById(Long stationId) {
-        log.info("查询充电站详情, stationId={}", stationId);
-        return lambdaQuery()
-                .eq(ChargingStation::getId, stationId)
-                .one();
-    }
-
-    /**
-     * 根据城市获取充电站列表
-     *
-     * @param city 城市名称
-     * @return 充电站列表
-     */
-    public List<ChargingStation> getStationsByCity(String city) {
-        log.info("根据城市查询充电站列表, city={}", city);
-        return lambdaQuery()
-                .eq(ChargingStation::getCity, city)
-                .eq(ChargingStation::getStatus, 1)
-                .list();
-    }
-}

+ 136 - 0
src/main/java/com/zsElectric/openapi/business/service/impl/ChargingBusinessServiceImpl.java

@@ -0,0 +1,136 @@
+package com.zsElectric.openapi.business.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.zsElectric.openapi.business.entity.ApiToken;
+import com.zsElectric.openapi.business.entity.QueryTokenParms;
+import com.zsElectric.openapi.business.entity.QueryTokenResponseData;
+import com.zsElectric.openapi.business.entity.ResponseParmsEntity;
+import com.zsElectric.openapi.business.service.ChargingBusinessService;
+import com.zsElectric.openapi.common.AESCryptoUtils;
+import com.zsElectric.openapi.common.ChargingUtil;
+import com.zsElectric.openapi.common.ConnectivityConstants;
+import com.zsElectric.openapi.common.HmacMD5Util;
+import com.zsElectric.openapi.dto.StartChargingRequestDTO;
+import com.zsElectric.openapi.vo.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * 充电业务服务实现类
+ * 使用业务数据库数据源
+ *
+ * @author Ray.Hao
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ChargingBusinessServiceImpl implements ChargingBusinessService {
+
+    private final ChargingUtil chargingUtil;
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    @Override
+    public ChargingPricePolicyVO queryEquipBusinessPolicy(String equipBizSeq, String connectorID) throws JsonProcessingException {
+        Map<String, Object> queryTokenParms = new HashMap<>();
+        // 注意:第三方接口要求大驼峰命名
+        queryTokenParms.put("EquipBizSeq", equipBizSeq);
+        queryTokenParms.put("ConnectorID", connectorID);
+        log.info("查询设备价格策略请求参数:{}", queryTokenParms);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_EQUIP_BUSINESS_POLICY, queryTokenParms, true);
+        log.info("查询设备价格策略返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("查询设备价格策略返回结果解密后:{}", responseDecode);
+
+        return objectMapper.readValue(responseDecode.toString(), ChargingPricePolicyVO.class);
+    }
+
+    @Override
+    public EquipmentAuthResponseVO queryEquipAuth(String EquipAuthSeq, String ConnectorID) throws JsonProcessingException {
+        Map<String, Object> queryTokenParms = new HashMap<>();
+        queryTokenParms.put("EquipAuthSeq", EquipAuthSeq);
+        queryTokenParms.put("ConnectorID", ConnectorID);
+        log.info("查询设备认证请求参数:{}", queryTokenParms);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_EQUIP_AUTH, queryTokenParms, true);
+        log.info("查询设备认证返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("查询设备认证返回结果解密后:{}", responseDecode);
+        return objectMapper.readValue(responseDecode.toString(), EquipmentAuthResponseVO.class);
+    }
+
+    @Override
+    public QueryStationsInfoVO queryStationsInfo(String LastQueryTime, Integer PageNo, Integer PageSize) throws JsonProcessingException {
+        Map<String, Object> queryParms = new HashMap<>();
+        queryParms.put("LastQueryTime", LastQueryTime);
+        queryParms.put("PageNo", PageNo);
+        queryParms.put("PageSize", PageSize);
+        log.info("查询充电站信息请求参数:{}", queryParms);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_STATIONS_INFO, queryParms, true);
+        log.info("查询充电站信息返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("查询充电站信息返回结果解密后:{}", responseDecode);
+        if (responseDecode == null) {
+            return null;
+        }
+        QueryStationsInfoVO result = objectMapper.readValue(responseDecode.toString(), QueryStationsInfoVO.class);
+
+        return result;
+    }
+
+    @Override
+    public QueryStationStatusVO queryStationStatus(List<String> stationIDs) throws JsonProcessingException {
+        Map<String, Object> queryParms = new HashMap<>();
+        queryParms.put("StationIDs", stationIDs);
+        log.info("设备接口状态查询请求参数:{}", queryParms);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_STATION_STATUS, queryParms, true);
+        log.info("设备接口状态查询返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("设备接口状态查询返回结果解密后:{}", responseDecode);
+        return objectMapper.readValue(responseDecode.toString(), QueryStationStatusVO.class);
+    }
+
+    @Override
+    public StartChargingResponseVO startCharging(StartChargingRequestDTO requestDTO) throws JsonProcessingException {
+        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(requestDTO);
+        log.info("设备接口状态查询请求参数:{}", stringObjectMap);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_START_CHARGE, stringObjectMap, true);
+        log.info("设备接口状态查询返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("设备接口状态查询返回结果解密后:{}", responseDecode);
+        return objectMapper.readValue(responseDecode.toString(), StartChargingResponseVO.class);
+    }
+
+    @Override
+    public ChargingStatusQueryResponseVO queryChargingStatus(String StartChargeSeq) throws JsonProcessingException {
+        Map<String, Object> queryParms = new HashMap<>();
+        queryParms.put("StartChargeSeq", StartChargeSeq);
+        log.info("查询充电订单状态请求参数:{}", queryParms);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_EQUIP_CHARGE_STATUS, queryParms, true);
+        log.info("查询充电订单状态返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("查询充电订单状态返回结果解密后:{}", responseDecode);
+        return objectMapper.readValue(responseDecode.toString(), ChargingStatusQueryResponseVO.class);
+    }
+
+    @Override
+    public StopChargingOperationResponseVO stopCharging(String StartChargeSeq, String ConnectorID) throws JsonProcessingException {
+        Map<String, Object> queryTokenParms = new HashMap<>();
+        queryTokenParms.put("StartChargeSeq", StartChargeSeq);
+        queryTokenParms.put("ConnectorID", ConnectorID);
+        log.info("停止充电请求参数:{}", queryTokenParms);
+        JsonNode jsonObject = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_STOP_CHARGE, queryTokenParms, true);
+        log.info("停止充电返回结果:{}", jsonObject);
+        JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
+        log.info("停止充电返回结果解密后:{}", responseDecode);
+        return objectMapper.readValue(responseDecode.toString(), StopChargingOperationResponseVO.class);
+    }
+}

+ 338 - 0
src/main/java/com/zsElectric/openapi/business/service/impl/ChargingReceptionServiceImpl.java

@@ -0,0 +1,338 @@
+package com.zsElectric.openapi.business.service.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.zsElectric.openapi.business.entity.ChargeResponseVO;
+import com.zsElectric.openapi.business.entity.QueryTokenRequestParms;
+import com.zsElectric.openapi.business.entity.QueryTokenResponseData;
+import com.zsElectric.openapi.business.entity.RequestParmsEntity;
+import com.zsElectric.openapi.business.entity.ResponseParmsEntity;
+import com.zsElectric.openapi.business.service.ChargingReceptionService;
+import com.zsElectric.openapi.common.ChargingUtil;
+import com.zsElectric.openapi.common.ConnectivityConstants;
+import com.zsElectric.openapi.common.HmacMD5Util;
+import com.zsElectric.openapi.common.JwtTokenUtil;
+import com.zsElectric.openapi.common.exception.BusinessException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import static com.zsElectric.openapi.common.ConnectivityConstants.FAIL_REASON_NONE;
+import static com.zsElectric.openapi.common.ConnectivityConstants.STATUS_OK;
+import static com.zsElectric.openapi.common.HmacMD5Util.genSign;
+import static com.zsElectric.openapi.common.HmacMD5Util.verify;
+
+
+/**
+ * 第三方充电推送接收服务实现
+ *
+ * @author system
+ * @since 2025-12-11
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ChargingReceptionServiceImpl implements ChargingReceptionService {
+
+    private final ChargingUtil chargingUtil;
+    private final ObjectMapper objectMapper;
+    private final JwtTokenUtil jwtTokenUtil;
+    // ==================== 接口实现 ====================
+
+    @Override
+    public ResponseParmsEntity chargeResponse(RequestParmsEntity requestDTO) {
+        log.info("接收推送启动充电结果请求参数:{}", requestDTO);
+        return processStartChargeResultRequest(requestDTO);
+    }
+
+    @Override
+    public ResponseParmsEntity chargeStatusResponse(RequestParmsEntity requestDTO) {
+        log.info("接收推送充电状态请求参数:{}", requestDTO);
+        return processChargeStatusRequest(requestDTO);
+    }
+
+    @Override
+    public ResponseParmsEntity stopChargeResponse(RequestParmsEntity requestDTO) {
+        log.info("接收推送停止充电结果请求参数:{}", requestDTO);
+        return processStopChargeResultRequest(requestDTO);
+    }
+
+
+
+    @Override
+    public ResponseParmsEntity chargeOrderResponse(RequestParmsEntity requestDTO) throws Exception {
+        log.info("接收推送充电订单信息请求参数:{}", requestDTO);
+        return processChargeRequest(requestDTO, jsonNode -> {
+            log.debug("充电订单信息 - StartChargeSeq: {}", getTextValue(jsonNode, "StartChargeSeq"));
+        });
+    }
+
+    @Override
+    public ResponseParmsEntity stationStatus(RequestParmsEntity requestDTO) {
+        log.info("接收设备状态变化推送请求参数:{}", requestDTO);
+        return processStationStatusRequest(requestDTO);
+    }
+
+    @Override
+    public ResponseParmsEntity getToken(RequestParmsEntity request) throws Exception {
+        log.info("接收获取Token请求参数:{}", request);
+        return processGetTokenRequest(request);
+    }
+
+    // ==================== 公共处理方法 ====================
+
+    /**
+     * 通用充电请求处理模板
+     */
+    private ResponseParmsEntity processChargeRequest(RequestParmsEntity requestDTO, Consumer<JsonNode> businessHandler) {
+        try {
+            JsonNode jsonNode = verifyAndDecrypt(requestDTO);
+            // 执行业务处理
+            businessHandler.accept(jsonNode);
+
+            // 构建响应
+            return buildChargeResponse(getTextValue(jsonNode, "StartChargeSeq"));
+        } catch (BusinessException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("处理请求失败:{}", e.getMessage());
+            throw new BusinessException("处理请求失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理启动充电结果推送请求
+     * 数据格式:{"ConnectorID":"xxx","StartChargeSeq":"xxx","StartChargeSeqStat":2,"StartTime":"xxx"}
+     */
+    private ResponseParmsEntity processStartChargeResultRequest(RequestParmsEntity requestDTO) {
+        try {
+            JsonNode jsonNode = verifyAndDecrypt(requestDTO);
+
+            // 启动充电结果业务处理
+            String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq");
+            return buildChargeResponse(startChargeSeq);
+        } catch (BusinessException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("处理启动充电结果推送失败:{}", e.getMessage(), e);
+            throw new BusinessException("处理启动充电结果推送失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理停止充电结果推送请求
+     * 数据格式:{"ConnectorID":"xxx","FailReason":0,"StartChargeSeq":"xxx","StartChargeSeqStat":4,"SuccStat":0}
+     */
+    private ResponseParmsEntity processStopChargeResultRequest(RequestParmsEntity requestDTO) {
+        try {
+            JsonNode jsonNode = verifyAndDecrypt(requestDTO);
+
+            // 停止充电结果业务处理
+            String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq");
+            Integer startChargeSeqStat = getIntValue(jsonNode, "StartChargeSeqStat");
+            Integer succStat = getIntValue(jsonNode, "SuccStat");
+            Integer failReason = getIntValue(jsonNode, "FailReason");
+
+            log.info("停止充电结果 - StartChargeSeq: {}, Stat: {}, SuccStat: {}, FailReason: {}",
+                    startChargeSeq, startChargeSeqStat, succStat, failReason);
+
+
+            // 构建响应
+            return buildChargeResponse(startChargeSeq);
+        } catch (BusinessException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("处理停止充电结果推送失败:{}", e.getMessage(), e);
+            throw new BusinessException("处理停止充电结果推送失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理实时充电状态推送请求
+     * 数据格式:{"ConnectorID":"xxx","ConnectorStatus":3,"TotalPower":1.95,"ElecMoney":1.89,...}
+     */
+    private ResponseParmsEntity processChargeStatusRequest(RequestParmsEntity requestDTO) {
+        try {
+            JsonNode jsonNode = verifyAndDecrypt(requestDTO);
+            
+            // 构建响应
+            return buildChargeResponse(getTextValue(jsonNode, "StartChargeSeq"));
+        } catch (BusinessException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("处理充电状态推送失败:{}", e.getMessage(), e);
+            throw new BusinessException("处理充电状态推送失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理设备状态变化推送请求
+     */
+    private ResponseParmsEntity processStationStatusRequest(RequestParmsEntity requestDTO) {
+        try {
+            String decryptData = verifyAndDecryptRaw(requestDTO);
+
+            // 构建响应
+            return buildStatusResponse();
+        } catch (BusinessException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("处理设备状态推送失败:{}", e.getMessage());
+            throw new BusinessException("处理设备状态推送失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理获取Token请求
+     */
+    private ResponseParmsEntity processGetTokenRequest(RequestParmsEntity request) throws Exception {
+        ResponseParmsEntity responseParmsEntity = new ResponseParmsEntity();
+        try {
+            // 验证签名
+            if (!HmacMD5Util.verify(request.getOperatorID() + request.getData() + request.getTimeStamp() + request.getSeq(),
+                    ConnectivityConstants.SIG_SECRET, request.getSig())) {
+                responseParmsEntity.setRet(4001);
+                responseParmsEntity.setMsg("签名验证失败");
+                responseParmsEntity.setData("");
+                responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                        ConnectivityConstants.SIG_SECRET));
+                return responseParmsEntity;
+            }
+
+            String data = request.getData();
+            String string = chargingUtil.decryptData(data);
+            QueryTokenRequestParms queryTokenRequestParms = objectMapper.readValue(string, QueryTokenRequestParms.class);
+            if (queryTokenRequestParms == null || queryTokenRequestParms.getOperatorID() == null || queryTokenRequestParms.getOperatorSecret() == null) {
+
+                responseParmsEntity.setRet(4003);
+                responseParmsEntity.setMsg("参数错误");
+                responseParmsEntity.setData("");
+                responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                        ConnectivityConstants.SIG_SECRET));
+                return responseParmsEntity;
+            }
+
+            // 判断运营商ID与密钥是否正确
+            if (!queryTokenRequestParms.getOperatorID().equals(ConnectivityConstants.PLATFORM_OPERATOR_ID) && !queryTokenRequestParms.getOperatorSecret().equals(ConnectivityConstants.PLATFORM_OPERATOR_SECRET)) {
+                responseParmsEntity.setRet(4004);
+                responseParmsEntity.setMsg("OperatorID或OperatorSecret错误!");
+                responseParmsEntity.setData("");
+                responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                        ConnectivityConstants.SIG_SECRET));
+                return responseParmsEntity;
+            }
+
+            // redis获取token,不存在则创建
+            String accessToken = jwtTokenUtil.generateToken(queryTokenRequestParms.getOperatorID());
+            Integer remainingTTL = jwtTokenUtil.getRemainingTTL(accessToken).intValue();
+            // 构建Data(token信息)
+            QueryTokenResponseData queryTokenResponseData = new QueryTokenResponseData();
+            queryTokenResponseData.setOperatorID(queryTokenRequestParms.getOperatorID());
+            queryTokenResponseData.setAccessToken(accessToken);
+            queryTokenResponseData.setTokenAvailableTime(remainingTTL);
+            queryTokenResponseData.setSuccStat(0);
+            queryTokenResponseData.setFailReason(0);
+
+            log.info("生成token信息:{}", objectMapper.writeValueAsString(queryTokenResponseData));
+
+            String encodeData = chargingUtil.encryptData(objectMapper.writeValueAsString(queryTokenResponseData));
+
+            responseParmsEntity.setRet(0);
+            responseParmsEntity.setMsg("成功");
+            responseParmsEntity.setData(encodeData);
+            responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                    ConnectivityConstants.SIG_SECRET));
+            return responseParmsEntity;
+        } catch (Exception e) {
+            log.error("系统错误:{}", e.getMessage());
+            responseParmsEntity.setRet(500);
+            responseParmsEntity.setMsg("系统错误");
+            responseParmsEntity.setData("");
+            responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                    ConnectivityConstants.SIG_SECRET));
+            return responseParmsEntity;
+        }
+    }
+
+    /**
+     * 验签并解密请求数据
+     */
+    private JsonNode verifyAndDecrypt(RequestParmsEntity requestDTO) throws Exception {
+        String decryptData = verifyAndDecryptRaw(requestDTO);
+        return objectMapper.readTree(decryptData);
+    }
+
+    /**
+     * 验签并解密请求数据(返回原始字符串)
+     */
+    private String verifyAndDecryptRaw(RequestParmsEntity requestDTO) throws Exception {
+        String signData = requestDTO.getOperatorID() + requestDTO.getData() + requestDTO.getTimeStamp() + requestDTO.getSeq();
+        if (!verify(signData, ConnectivityConstants.SIG_SECRET, requestDTO.getSig())) {
+            log.error("数据验签失败");
+            throw new BusinessException("数据验签失败");
+        }
+        String decryptData = chargingUtil.decryptData(requestDTO.getData());
+        log.info("==================== 解密数据开始 ====================");
+        log.info("操作员ID: {}", requestDTO.getOperatorID());
+        log.info("解密后的数据:{}", decryptData);
+        log.info("==================== 解密数据结束 ====================");
+        return decryptData;
+    }
+
+    // ==================== 响应构建 ====================
+
+    /**
+     * 构建充电响应
+     */
+    private ResponseParmsEntity buildChargeResponse(String startChargeSeq) throws Exception {
+        ChargeResponseVO chargeResponseVO = new ChargeResponseVO();
+        chargeResponseVO.setStartChargeSeq(startChargeSeq);
+        chargeResponseVO.setSuccStat(STATUS_OK);
+        chargeResponseVO.setFailReason(FAIL_REASON_NONE);
+
+        String encryptData = chargingUtil.encryptData(objectMapper.writeValueAsString(chargeResponseVO));
+        String sign = genSign(STATUS_OK, "请求成功", encryptData, ConnectivityConstants.SIG_SECRET);
+
+        ResponseParmsEntity response = new ResponseParmsEntity();
+        response.setRet(STATUS_OK);
+        response.setMsg("请求成功");
+        response.setData(encryptData);
+        response.setSig(sign);
+        return response;
+    }
+
+    /**
+     * 构建设备状态响应
+     */
+    private ResponseParmsEntity buildStatusResponse() throws Exception {
+        Map<String, Integer> statusMap = new HashMap<>();
+        statusMap.put("Status", 0);
+
+        String encryptData = chargingUtil.encryptData(objectMapper.writeValueAsString(statusMap));
+        String sign = genSign(STATUS_OK, "", encryptData, ConnectivityConstants.SIG_SECRET);
+
+        ResponseParmsEntity response = new ResponseParmsEntity();
+        response.setRet(STATUS_OK);
+        response.setMsg("");
+        response.setData(encryptData);
+        response.setSig(sign);
+        return response;
+    }
+
+
+    // ==================== JSON解析工具方法 ====================
+
+    private String getTextValue(JsonNode node, String fieldName) {
+        JsonNode field = node.get(fieldName);
+        return (field != null && !field.isNull()) ? field.asText() : null;
+    }
+
+    private Integer getIntValue(JsonNode node, String fieldName) {
+        JsonNode field = node.get(fieldName);
+        return (field != null && !field.isNull()) ? field.asInt() : null;
+    }
+}

+ 203 - 0
src/main/java/com/zsElectric/openapi/common/AESCryptoUtils.java

@@ -0,0 +1,203 @@
+package com.zsElectric.openapi.common;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import static com.zsElectric.openapi.common.ConnectivityConstants.DATA_SECRET;
+import static com.zsElectric.openapi.common.ConnectivityConstants.DATA_SECRET_IV;
+
+/**
+ * AES加解密工具类
+ * 支持AES-128-CBC-PKCS5Padding对称加解密算法
+ * @version 1.0
+ */
+@Slf4j
+public class AESCryptoUtils {
+    
+    // 加密算法/模式/填充方式
+    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
+    private static final String AES = "AES";
+    private static final int IV_SIZE = 16;   // 16字节
+
+    /**
+     * AES加密
+     * @param data 待加密的明文
+     * @param key 密钥(必须为16字节)
+     * @param iv 初始化向量(必须为16字节)
+     * @return Base64编码的加密结果
+     * @throws Exception 加密异常
+     */
+    public static String encrypt(String data, String key, String iv) throws Exception {
+
+        log.info("待加密数据:{}", data);
+
+        // 参数校验
+        if (data == null || data.isEmpty()) {
+            throw new IllegalArgumentException("加密数据不能为空");
+        }
+        if (key == null || key.length() != 16) {
+            throw new IllegalArgumentException("密钥必须为16位字符");
+        }
+        if (iv == null || iv.length() != IV_SIZE) {
+            throw new IllegalArgumentException("初始化向量必须为16位字符");
+        }
+
+        try {
+            // 创建密钥规范
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
+            // 创建初始化向量规范
+            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+
+            // 获取加密实例并初始化
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+
+            // 执行加密
+            byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
+
+            // 返回Base64编码的加密结果
+            return Base64.getEncoder().encodeToString(encryptedBytes);
+
+        } catch (Exception e) {
+            throw new Exception("AES加密失败: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * AES解密
+     * @param encryptedData Base64编码的加密数据
+     * @param key 密钥(必须为16字节)
+     * @param iv 初始化向量(必须为16字节)
+     * @return 解密后的明文
+     * @throws Exception 解密异常
+     */
+    public static String decrypt(String encryptedData, String key, String iv) throws Exception {
+        // 参数校验
+        if (encryptedData == null || encryptedData.isEmpty()) {
+            throw new IllegalArgumentException("解密数据不能为空");
+        }
+        if (key == null || key.length() != 16) {
+            throw new IllegalArgumentException("密钥必须为16位字符");
+        }
+        if (iv == null || iv.length() != 16) {
+            throw new IllegalArgumentException("初始化向量必须为16位字符");
+        }
+
+        try {
+            // 创建密钥规范
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
+            // 创建初始化向量规范
+            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+
+            // 获取解密实例并初始化
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
+
+            // Base64解码并执行解密
+            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
+            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+
+            // 返回解密结果
+            return new String(decryptedBytes, StandardCharsets.UTF_8);
+
+        } catch (Exception e) {
+            throw new Exception("AES解密失败: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 生成随机密钥(16字节)
+     * @return 16字节的随机密钥
+     */
+    public static String generateRandomKey() {
+        return generateRandomString(16);
+    }
+
+    /**
+     * 生成随机初始化向量(16字节)
+     * @return 16字节的随机IV
+     */
+    public static String generateRandomIV() {
+        return generateRandomString(16);
+    }
+
+    /**
+     * 生成指定长度的随机字符串
+     * @param length 字符串长度
+     * @return 随机字符串
+     */
+    private static String generateRandomString(int length) {
+        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        StringBuilder sb = new StringBuilder(length);
+        for (int i = 0; i < length; i++) {
+            int index = (int) (characters.length() * Math.random());
+            sb.append(characters.charAt(index));
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * 测试方法
+     */
+    public static void main(String[] args) throws Exception {
+
+        try {
+            // 测试数据
+            String originalData = "{\"OperatorID\":\"MA6DP6BE7\",\"SuccStat\":0,\"AccessToken\":\"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJNQTZEUDZCRTciLCJpYXQiOjE3NjQ1ODE0NzQsImV4cCI6MTc2NDU4ODY3NH0.mfXp_Wly4QiVYILK6hNSOEGSU7XejlYZRing0CRc8MQW1xuUxrQQwOxeCAwI_Bnay5WzqaYYFQeVuNeignxAuQ\",\"TokenAvailableTime\":6885,\"FailReason\":0}";
+            String key = DATA_SECRET;   // 16字节密钥
+            String iv = DATA_SECRET_IV;   // 16字节初始化向量
+
+            System.out.println("=== AES-128-CBC-PKCS5Padding 加解密测试 ===");
+            System.out.println("原始数据: " + originalData);
+            System.out.println("密钥: " + key);
+            System.out.println("初始化向量: " + iv);
+
+            // 加密
+            long startTime = System.currentTimeMillis();
+            String encryptedData = encrypt(originalData, key, iv);
+            long encryptTime = System.currentTimeMillis() - startTime;
+            System.out.println("加密结果: " + encryptedData);
+            System.out.println("加密耗时: " + encryptTime + "ms");
+
+            String data = "KYWxoKWK3w8a8867aXCha+tgVE2cbZ4eR1Dc1YExri06DfZWBpUMAzlhY7rWR5SeU+xCVOauk4F7MxCJLN+5aJCBENCOAZtUksMM7VgsOz0=";
+            System.out.println("测试的加密数据:"+data);
+
+            String key1 = DATA_SECRET;   // 16字节密钥
+            String iv1 = DATA_SECRET_IV;
+            System.out.println("密钥: " + key);
+            System.out.println("初始化向量: " + iv);
+            String string = decrypt(data, key1, iv1);
+            System.out.println("测试的解密:"+string);
+            // 解密
+            startTime = System.currentTimeMillis();
+            String decryptedData = decrypt(encryptedData, key, iv);
+            long decryptTime = System.currentTimeMillis() - startTime;
+            System.out.println("解密结果: " + decryptedData);
+            System.out.println("解密耗时: " + decryptTime + "ms");
+
+            // 验证加解密一致性
+            boolean isSuccess = originalData.equals(decryptedData);
+            System.out.println("加解密验证: " + (isSuccess ? "成功" : "失败"));
+
+            // 测试随机密钥生成
+            System.out.println("\n=== 随机密钥生成测试 ===");
+            String randomKey = generateRandomKey();
+            String randomIV = generateRandomIV();
+            System.out.println("随机密钥: " + randomKey);
+            System.out.println("随机IV: " + randomIV);
+
+            // 使用随机密钥进行加解密测试
+            String testEncrypted = encrypt("测试数据", randomKey, randomIV);
+            String testDecrypted = decrypt(testEncrypted, randomKey, randomIV);
+            System.out.println("随机密钥加解密测试: " + ("测试数据".equals(testDecrypted) ? "成功" : "失败"));
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 184 - 0
src/main/java/com/zsElectric/openapi/common/ChargingUtil.java

@@ -0,0 +1,184 @@
+package com.zsElectric.openapi.common;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.zsElectric.boot.common.constant.ConnectivityConstants;
+import com.zsElectric.boot.common.util.AESCryptoUtils;
+import com.zsElectric.boot.common.util.HmacMD5Util;
+import com.zsElectric.boot.common.util.OkHttpUtil;
+import com.zsElectric.boot.common.util.SequenceGenUtil;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+@Slf4j
+@Component
+public class ChargingUtil {
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    
+    /**
+     * 应用层最大重试次数(除了OkHttp层的重试)
+     */
+    private static final int MAX_APP_RETRY_COUNT = 2;
+    
+    /**
+     * 重试等待时间(毫秒)
+     */
+    private static final long RETRY_WAIT_TIME_MS = 1000;
+
+    @Resource
+    private ElectricTokenManager tokenManager;
+    @Resource
+    private OkHttpUtil okHttpUtil;
+
+    /**
+     * 请求封装(带重试机制)
+     * @param url 请求URL
+     * @param queryParms 请求参数
+     * @param tokenRequired 是否需要Token
+     * @return 响应结果
+     */
+    public JsonNode chargingRequest(String url, Map<String,Object> queryParms, boolean tokenRequired){
+        Exception lastException = null;
+        
+        // 应用层重试机制(除了OkHttp层的重试之外)
+        for (int attempt = 0; attempt <= MAX_APP_RETRY_COUNT; attempt++) {
+            try {
+                if (attempt > 0) {
+                    log.warn("第三方接口调用失败,正在进行第 {} 次应用层重试 - URL: {}", attempt, url);
+                    // 重试前等待一段时间
+                    Thread.sleep(RETRY_WAIT_TIME_MS * attempt); // 线性递增等待时间
+                }
+                
+                return doChargingRequest(url, queryParms, tokenRequired);
+                
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                log.error("重试被中断 - URL: {}", url, e);
+                throw new RuntimeException("调用第三方接口被中断: " + e.getMessage(), e);
+            } catch (Exception e) {
+                lastException = e;
+                log.error("调用第三方接口失败 - URL: {}, 第 {} 次尝试", url, attempt + 1, e);
+                
+                // 如果是最后一次尝试,不再重试
+                if (attempt >= MAX_APP_RETRY_COUNT) {
+                    break;
+                }
+            }
+        }
+        
+        // 所有重试都失败
+        log.error("调用第三方接口所有重试都失败 - URL: {}, 总共尝试 {} 次", url, MAX_APP_RETRY_COUNT + 1);
+        if (lastException != null) {
+            throw new RuntimeException("调用第三方接口失败: " + lastException.getMessage(), lastException);
+        } else {
+            throw new RuntimeException("调用第三方接口失败,原因未知");
+        }
+    }
+    
+    /**
+     * 实际执行请求的方法
+     * @param url 请求URL
+     * @param queryParms 请求参数
+     * @param tokenRequired 是否需要Token
+     * @return 响应结果
+     */
+    private JsonNode doChargingRequest(String url, Map<String,Object> queryParms, boolean tokenRequired) throws Exception {
+        Map<String, String> headers = new HashMap<>();
+        if(tokenRequired){
+            headers.put("Authorization", "Bearer " + tokenManager.getValidAccessToken());
+        }
+        
+        RequestParmsEntity requestParms = new RequestParmsEntity();
+        SequenceGenUtil.SequenceResult result = SequenceGenUtil.generate();
+
+        requestParms.setOperatorID(ConnectivityConstants.OPERATOR_ID);
+        requestParms.setData(AESCryptoUtils.encrypt(objectMapper.writeValueAsString(queryParms),ConnectivityConstants.PLATFORM_DATA_SECRET,
+                        ConnectivityConstants.PLATFORM_DATA_SECRET_IV));
+        requestParms.setTimeStamp(result.getTimestamp());
+        requestParms .setSeq(result.getSequence());
+        requestParms.setSig(HmacMD5Util.genSign(requestParms.getOperatorID(),requestParms.getData(),
+                        requestParms.getTimeStamp(),
+                        requestParms.getSeq(),ConnectivityConstants.PLATFORM_SIG_SECRET));
+
+        String requestJson = objectMapper.writeValueAsString(requestParms);
+        log.info("调用第三方接口,URL: {}, 请求参数: {}", url, requestJson);
+        JsonNode response = okHttpUtil.doPostForm(url, requestJson, headers);
+
+        if (Objects.isNull(response)) {
+            log.error("调用第三方接口返回为空,URL: {}", url);
+            throw new RuntimeException("第三方接口返回为空");
+        }
+        log.info("调用第三方接口成功,URL: {}, 响应: {}", url, response);
+        return response;
+    }
+
+    /**
+     * 响应解密
+     * @param response
+     * @return
+     */
+    public JsonNode responseDecode(JsonNode response){
+
+        try {
+            if (Objects.isNull(response)) {
+                log.error("第三方接口响应数据为空");
+                return null;
+             }
+            ResponseParmsEntity responseParms = objectMapper.readValue(response.toString(), ResponseParmsEntity.class);
+            String data = responseParms.getRet() + responseParms.getMsg() + responseParms.getData();
+            boolean verify = HmacMD5Util.verify(data, ConnectivityConstants.PLATFORM_SIG_SECRET, responseParms.getSig());
+            if (!verify) {
+                log.error("第三方接口响应数据签名验证失败");
+                return null;
+            }
+            if (responseParms.getRet() != 0){
+                switch (responseParms.getRet()) {
+                    case -1:
+                        log.error("系统繁忙,此时请求方稍后重试");
+                        break;
+                    case 4001:
+                        log.error("签名错误");
+                        break;
+                    case 4002:
+                        log.error("Token错误");
+                        break;
+                    case 4003:
+                        log.error("参数不合法,缺少必需的示例:OperatorID、Sig、TimeStamp、Data、Seq五个参数");
+                        break;
+                    case 4004:
+                        log.error("请求的业务参数不合法,各接口定义自己的必须参数");
+                        break;
+                    case 500:
+                        log.error("系统错误");
+                        break;
+                }
+            }
+            String decodeData = AESCryptoUtils.decrypt(responseParms.getData(), ConnectivityConstants.PLATFORM_DATA_SECRET,
+                    ConnectivityConstants.PLATFORM_DATA_SECRET_IV);
+            return objectMapper.readTree(decodeData);
+        }catch (Exception e){
+            throw new RuntimeException("第三方接口响应发生异常", e);
+        }
+    }
+
+
+    /**
+     * 统一解密接口数据-己方响应
+     * */
+    public String decryptData(String data) throws Exception {
+        return AESCryptoUtils.decrypt(data, ConnectivityConstants.DATA_SECRET, ConnectivityConstants.DATA_SECRET_IV);
+    }
+
+    /**
+     * 统一加密接口数据-己方响应
+     * */
+    public String encryptData(String data) throws Exception {
+        return AESCryptoUtils.encrypt(data, ConnectivityConstants.DATA_SECRET, ConnectivityConstants.DATA_SECRET_IV);
+    }
+}

+ 160 - 0
src/main/java/com/zsElectric/openapi/common/ConnectivityConstants.java

@@ -0,0 +1,160 @@
+package com.zsElectric.openapi.common;
+
+/**
+ * 连接性对接信息常量
+ *
+ * @author Ray.Hao
+ * @since 1.0.0
+ */
+public interface ConnectivityConstants {
+
+    /*-------------------------------以下为互联互通配置------------------------------**/
+
+    /**
+     * 平台运营商ID(URL路径中使用)
+     */
+    String PLATFORM_OPERATOR_ID = "MA6HWN8L3";
+
+    /**
+     * 平台运营商密钥
+     */
+    String PLATFORM_OPERATOR_SECRET = "vfkh4k740lfg88kq";
+
+    /**
+     * 平台签名密钥
+     */
+    String PLATFORM_SIG_SECRET = "8h9sf4zd5cbtlu8x";
+
+    /**
+     * 平台数据密钥
+     */
+    String PLATFORM_DATA_SECRET = "bbkwy062pzyjhqmg";
+
+    /**
+     * 平台数据密钥向量
+     */
+    String PLATFORM_DATA_SECRET_IV = "xgbzfgwz6ki2gm5j";
+
+    /*-------------------------------以下为我方配置------------------------------**/
+
+    /**
+     * 运营商ID(请求参数中使用,与平台运营商ID相同)
+     */
+    String OPERATOR_ID = "MAA9A6L75";
+
+    /**
+     * 运营商密钥
+     */
+    String OPERATOR_SECRET = "o2LrzJmu9sgND5HB";
+
+    /**
+     * 签名密钥
+     */
+    String SIG_SECRET = "ZA8LquIf0Kgt3eDM";
+
+    /**
+     * 数据密钥
+     */
+    String DATA_SECRET = "L00AmpEUkmlPYOxy";
+
+    /**
+     * 数据密钥向量
+     */
+    String DATA_SECRET_IV = "l5AT0JaY1a1OGCrT";
+
+    /*------------------------------------------请求URL-----------------------------------------------*/
+
+    /**
+     * 互联互通平台域名
+     * */
+    String TEST_DOMAIN = "https://new-parking.gyzhtc.com/energy-interconnection/evcs/v1.1/1/MA6HWN8L3";
+
+    /**
+     * 获取Token
+     * */
+    String QUERY_TOKEN = "/query_token";
+
+    /**
+     * 查询充电站信息
+     */
+    String QUERY_STATIONS_INFO = "/query_stations_info";
+
+    /**
+     * 设备状态变化推送
+     */
+    String NOTIFICATION_STATIONSTATUS = "/notification_stationStatus";
+
+    /**
+     * 设备接口状态查询 (选接)
+     */
+    String QUERY_STATION_STATUS = "/query_station_status";
+
+    /**
+     * 查询业务策略信息
+     * */
+    String QUERY_EQUIP_BUSINESS_POLICY = "/query_equip_business_policy";
+
+    /**
+     * 请求设备认证
+     * */
+    String QUERY_EQUIP_AUTH = "/query_equip_auth";
+
+    /**
+     * 请求启动充电
+     * */
+    String QUERY_START_CHARGE = "/query_start_charge";
+
+    /**
+     * 推送启动充电结果
+     * */
+    String NOTIFICATION_START_CHARGE_RESULT = "/notification_start_charge_result";
+
+    /**
+     * 查询充电状态
+     * */
+    String QUERY_EQUIP_CHARGE_STATUS = "/query_equip_charge_status";
+
+    /**
+     * 推送充电状态
+     * */
+    String NOTIFICATION_EQUIP_CHARGE_STATUS = "/notification_equip_charge_status";
+
+    /**
+     * 请求停止充电
+     * */
+    String QUERY_STOP_CHARGE = "/query_stop_charge";
+
+    /**
+     * 推送停止充电结果
+     * */
+    String NOTIFICATION_STOP_CHARGE_RESULT = "/notification_stop_charge_result";
+
+    /**
+     * 推送充电订单信息
+     * */
+    String NOTIFICATION_CHARGE_ORDER_INFO = "/notification_charge_order_info";
+
+
+
+    /*------------------------------------------请求URL_END-----------------------------------------------*/
+
+    /*------------------------------------------响应状态-----------------------------------------------*/
+    /**
+     * 操作结果成功
+     * */
+    int STATUS_OK = 0;
+    /**
+     * 操作结果失败
+     * */
+    int STATUS_ERROR = 1;
+    /**
+     * 失败原因无
+     * */
+    int FAIL_REASON_NONE = 0;
+    /**
+     * 失败原因接收失败
+     * */
+    int FAIL_REASON_RECEIVE_FAIL = 1;
+
+    /*------------------------------------------响应状态_END-----------------------------------------------*/
+}

+ 298 - 0
src/main/java/com/zsElectric/openapi/common/HmacMD5Util.java

@@ -0,0 +1,298 @@
+package com.zsElectric.openapi.common;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * HMAC-MD5参数签名工具类
+ * 严格按照RFC 2104标准的7个步骤实现
+ * @version 1.0
+ */
+public class HmacMD5Util {
+    
+    private static final int BLOCK_SIZE = 64; // 块大小为64字节
+    private static final byte IPAD = 0x36;   // 内部填充常量
+    private static final byte OPAD = 0x5C;   // 外部填充常量
+
+
+    /**
+     * 根据入参顺序生成签名: OperatorID+Data+TimeStamp+Seq
+     * @param operatorId 操作员ID
+     * @param data 数据内容
+     * @param timeStamp 时间戳
+     * @param seq 序列号
+     * @param sigSecret 签名密钥
+     * @return 签名结果(大写)
+     * @throws NoSuchAlgorithmException
+     */
+    public static String genSign(
+            String operatorId,
+            String data,
+            String timeStamp,
+            String seq,
+            String sigSecret) throws NoSuchAlgorithmException {
+
+        String content = (operatorId + data + timeStamp + seq);
+        return hmacMD5Hex(content, sigSecret);
+    }
+
+
+    /**
+     * 出参
+     * */
+    public static String genSign(
+            int Ret,
+            String Msg,
+            String Data,
+            String sigSecret) throws NoSuchAlgorithmException {
+
+        String content = (Ret + Msg + Data).toUpperCase();
+        return hmacMD5Hex(content, sigSecret);
+    }
+    
+    /**
+     * HMAC-MD5签名生成
+     * @param data 待签名的消息内容
+     * @param key 签名密钥
+     * @return 16字节的HMAC-MD5签名结果
+     * @throws NoSuchAlgorithmException
+     */
+    public static byte[] hmacMD5(byte[] data, byte[] key) throws NoSuchAlgorithmException {
+        // 步骤1:在签名密钥后面添加0创建长为64字节的字符串
+        byte[] k = prepareKey(key);
+        
+        // 步骤2:将密钥与ipad(0x36)做异或运算
+        byte[] iPadXor = xorWithPad(k, IPAD);
+        
+        // 步骤3:将消息内容附加到第二步的结果字符串的末尾
+        byte[] firstInput = concatenate(iPadXor, data);
+        
+        // 步骤4:对第三步生成的数据流做MD5运算
+        byte[] firstHash = md5(firstInput);
+        
+        // 步骤5:将第一步生成的字符串与opad(0x5c)做异或运算
+        byte[] oPadXor = xorWithPad(k, OPAD);
+        
+        // 步骤6:将第四步的结果附加到第五步的结果字符串的末尾
+        byte[] secondInput = concatenate(oPadXor, firstHash);
+        
+        // 步骤7:对第六步生成的数据流做MD5运算,输出最终结果
+        return md5(secondInput);
+    }
+    
+    /**
+     * 步骤1:准备密钥 - 填充或哈希到64字节
+     * @param key 原始密钥
+     * @return 64字节的密钥
+     * @throws NoSuchAlgorithmException
+     */
+    private static byte[] prepareKey(byte[] key) throws NoSuchAlgorithmException {
+        byte[] result = new byte[BLOCK_SIZE];
+        
+        if (key.length > BLOCK_SIZE) {
+            // 如果密钥长度超过64字节,先进行MD5哈希
+            byte[] hashedKey = md5(key);
+            System.arraycopy(hashedKey, 0, result, 0, hashedKey.length);
+            // 剩余部分填充0
+            for (int i = hashedKey.length; i < BLOCK_SIZE; i++) {
+                result[i] = 0;
+            }
+        } else if (key.length < BLOCK_SIZE) {
+            // 如果密钥长度不足64字节,后面补0
+            System.arraycopy(key, 0, result, 0, key.length);
+            for (int i = key.length; i < BLOCK_SIZE; i++) {
+                result[i] = 0;
+            }
+        } else {
+            // 密钥正好64字节
+            result = key.clone();
+        }
+        
+        return result;
+    }
+    
+    /**
+     * 步骤2/5:密钥与pad常量进行异或运算
+     * @param key 64字节的密钥
+     * @param pad 填充常量(IPAD或OPAD)
+     * @return 异或结果
+     */
+    private static byte[] xorWithPad(byte[] key, byte pad) {
+        byte[] result = new byte[BLOCK_SIZE];
+        for (int i = 0; i < BLOCK_SIZE; i++) {
+            result[i] = (byte) (key[i] ^ pad);
+        }
+        return result;
+    }
+    
+    /**
+     * 字节数组拼接
+     * @param a 第一个字节数组
+     * @param b 第二个字节数组
+     * @return 拼接后的字节数组
+     */
+    private static byte[] concatenate(byte[] a, byte[] b) {
+        byte[] result = new byte[a.length + b.length];
+        System.arraycopy(a, 0, result, 0, a.length);
+        System.arraycopy(b, 0, result, a.length, b.length);
+        return result;
+    }
+    
+    /**
+     * MD5哈希计算
+     * @param input 输入数据
+     * @return MD5哈希结果(16字节)
+     * @throws NoSuchAlgorithmException
+     */
+    private static byte[] md5(byte[] input) throws NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        return md.digest(input);
+    }
+    
+    /**
+     * 字节数组转换为十六进制字符串
+     * @param bytes 字节数组
+     * @return 十六进制字符串
+     */
+    public static String bytesToHex(byte[] bytes) {
+        StringBuilder hexString = new StringBuilder();
+        for (byte b : bytes) {
+            String hex = Integer.toHexString(0xff & b);
+            if (hex.length() == 1) {
+                hexString.append('0');
+            }
+            hexString.append(hex);
+        }
+        return hexString.toString().toUpperCase();
+    }
+    
+    /**
+     * 生成HMAC-MD5签名(十六进制字符串形式)
+     * @param data 消息内容
+     * @param key 密钥
+     * @return 32位十六进制签名字符串
+     * @throws NoSuchAlgorithmException
+     */
+    public static String hmacMD5Hex(String data, String key) throws NoSuchAlgorithmException {
+        byte[] signature = hmacMD5(data.getBytes(StandardCharsets.UTF_8), 
+                                 key.getBytes(StandardCharsets.UTF_8));
+        return bytesToHex(signature);
+    }
+    
+    /**
+     * 生成HMAC-MD5签名(十六进制字符串形式)
+     * @param data 消息内容字节数组
+     * @param key 密钥字节数组
+     * @return 32位十六进制签名字符串
+     * @throws NoSuchAlgorithmException
+     */
+    public static String hmacMD5Hex(byte[] data, byte[] key) throws NoSuchAlgorithmException {
+        byte[] signature = hmacMD5(data, key);
+        return bytesToHex(signature);
+    }
+    
+    /**
+     * 验证HMAC-MD5签名
+     * @param data 原始消息内容
+     * @param key 密钥
+     * @param signature 待验证的签名(十六进制字符串)
+     * @return 验证结果
+     * @throws NoSuchAlgorithmException
+     */
+    public static boolean verify(String data, String key, String signature) throws NoSuchAlgorithmException {
+        String calculatedSignature = hmacMD5Hex(data, key);
+        return calculatedSignature.equalsIgnoreCase(signature);
+    }
+    
+    /**
+     * 测试方法
+     */
+    public static void main(String[] args) {
+        try {
+            // 测试数据
+            String data = "KYWxoKWK3w8a8867aXCha+tgVE2cbZ4eR1Dc1YExri06DfZWBpUMAzlhY7rWR5SeU+xCVOauk4F7MxCJLN+5aJCBENCOAZtUksMM7VgsOz0=";
+            String key = "U9xFXjjdYAycq30C";
+            
+            System.out.println("=== HMAC-MD5签名测试 ===");
+            System.out.println("原始数据: " + data);
+            System.out.println("密钥: " + key);
+            
+            // 生成签名
+            long startTime = System.nanoTime();
+            String signature = hmacMD5Hex(data, key);
+            long signTime = System.nanoTime() - startTime;
+            
+            System.out.println("HMAC-MD5签名: " + signature);
+            System.out.println("签名长度: " + signature.length() + "字符(32字节)");
+            System.out.println("签名耗时: " + signTime + "纳秒");
+            
+            // 验证签名
+            startTime = System.nanoTime();
+            boolean isValid = verify(data, key, signature);
+            long verifyTime = System.nanoTime() - startTime;
+            
+            System.out.println("签名验证: " + (isValid ? "成功" : "失败"));
+            System.out.println("验证耗时: " + verifyTime + "纳秒");
+            
+            // 测试不同长度密钥
+            System.out.println("\n=== 不同长度密钥测试 ===");
+            testWithDifferentKeyLengths();
+            
+            // 测试签名一致性
+            System.out.println("\n=== 签名一致性测试 ===");
+            testSignatureConsistency();
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    /**
+     * 测试不同长度密钥的签名
+     */
+    private static void testWithDifferentKeyLengths() throws NoSuchAlgorithmException {
+        String data = "测试数据";
+        
+        // 短密钥(小于64字节)
+        String shortKey = "short";
+        String signature1 = hmacMD5Hex(data, shortKey);
+        System.out.println("短密钥签名: " + signature1);
+        
+        // 长密钥(等于64字节)
+        String longKey = "thisIsAExactly64BytesLongKeyUsedForHMACMD5SignatureTest123";
+        String signature2 = hmacMD5Hex(data, longKey);
+        System.out.println("64字节密钥签名: " + signature2);
+        
+        // 超长密钥(大于64字节)
+        String veryLongKey = "thisIsAVeryLongKeyThatExceedsThe64BytesBlockSizeUsedForHMACMD5SignatureTest123456789";
+        String signature3 = hmacMD5Hex(data, veryLongKey);
+        System.out.println("超长密钥签名: " + signature3);
+    }
+    
+    /**
+     * 测试签名一致性(相同输入应产生相同输出)
+     */
+    private static void testSignatureConsistency() throws NoSuchAlgorithmException {
+        String data = "一致性测试数据";
+        String key = "testKey";
+        
+        // 多次签名应该结果一致
+        String signature1 = hmacMD5Hex(data, key);
+        String signature2 = hmacMD5Hex(data, key);
+        String signature3 = hmacMD5Hex(data, key);
+        
+        System.out.println("第一次签名: " + signature1);
+        System.out.println("第二次签名: " + signature2);
+        System.out.println("第三次签名: " + signature3);
+        
+        boolean consistent = signature1.equals(signature2) && signature2.equals(signature3);
+        System.out.println("签名一致性: " + (consistent ? "通过" : "失败"));
+        
+        // 测试数据微小变化会导致签名完全不同
+        String similarData = "一致性测试数据 "; // 多一个空格
+        String differentSignature = hmacMD5Hex(similarData, key);
+        System.out.println("微小变化后签名: " + differentSignature);
+        System.out.println("敏感性测试: " + (!signature1.equals(differentSignature) ? "通过" : "失败"));
+    }
+}

+ 191 - 0
src/main/java/com/zsElectric/openapi/common/JwtTokenUtil.java

@@ -0,0 +1,191 @@
+package com.zsElectric.openapi.common;
+
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * JWT Token工具类
+ * 专门用于第三方接口的Token生成和验证
+ */
+@Slf4j
+@Component
+public class JwtTokenUtil {
+
+    private final SecretKey secretKey;
+    private final Long expireSeconds;
+    private final RedisTemplate<String, Object> redisTemplate;
+    
+    // Redis key前缀
+    private static final String REDIS_TOKEN_PREFIX = "third_party:token:";
+    private static final String REDIS_OPERATOR_TOKENS_PREFIX = "third_party:operator_tokens:";
+
+    public JwtTokenUtil(@Value("${third-party.jwt.secret:vgct1TZ4ZikKjaaeIiq3LUwIvpmcgYa6}") String secret,
+                        @Value("${third-party.jwt.expire:7200}") Long expireSeconds,
+                        RedisTemplate<String, Object> redisTemplate) {
+        // 生成安全的密钥
+        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
+        this.expireSeconds = expireSeconds;
+        this.redisTemplate = redisTemplate;
+    }
+
+    /**
+     * 生成JWT Token
+     */
+    public String generateToken(String operatorId) {
+
+        // 先查询Redis中是否已有有效Token
+        String existingToken = getExistingValidToken(operatorId);
+        if (existingToken != null) {
+            return existingToken;
+        }
+        Date now = new Date();
+        Date expiryDate = new Date(now.getTime() + expireSeconds * 1000);
+        String token = Jwts.builder()
+                .setSubject(operatorId)
+                .setIssuedAt(now)
+                .setExpiration(expiryDate)
+                .signWith(secretKey, SignatureAlgorithm.HS512)
+                .compact();
+        // 存储到Redis,支持Token主动撤销
+        storeTokenInRedis(token, operatorId);
+        return token;
+    }
+
+    /**
+     * 验证JWT Token有效性
+     */
+    public boolean validateToken(String token) {
+        try {
+            log.info("开始验证Token: {}...", token.substring(0, Math.min(20, token.length())));
+            // 首先检查Redis中是否存在该token(支持主动撤销)
+            if (!isTokenInRedis(token)) {
+                log.error("Token在Redis中不存在,可能已被撤销或过期");
+                return false;
+            }
+            log.info("Token在Redis中存在,继续验证JWT签名");
+            // 验证JWT签名和过期时间
+            Jwts.parserBuilder()
+                .setSigningKey(secretKey)
+                .build()
+                .parseClaimsJws(token);
+            log.info("Token验证通过");
+            return true;
+        } catch (ExpiredJwtException e) {
+            log.error("Token已过期: {}" , e.getMessage());
+        } catch (UnsupportedJwtException e) {
+            log.error("不支持的Token格式: {}" , e.getMessage());
+        } catch (MalformedJwtException e) {
+            log.error("Token格式错误: {}" , e.getMessage());
+        } catch (SignatureException e) {
+            log.error("Token签名验证失败: {}" , e.getMessage());
+        } catch (IllegalArgumentException e) {
+            log.error("Token参数错误: {}" , e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * 从Token中提取OperatorID
+     */
+    public String getOperatorIdFromToken(String token) {
+        try {
+            Claims claims = Jwts.parserBuilder()
+                    .setSigningKey(secretKey)
+                    .build()
+                    .parseClaimsJws(token)
+                    .getBody();
+            return claims.getSubject();
+        } catch (Exception e) {
+            System.out.println("从Token提取OperatorID失败: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 存储Token到Redis
+     */
+    private void storeTokenInRedis(String token, String operatorId) {
+        String tokenKey = REDIS_TOKEN_PREFIX + token;
+        String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
+        
+        // 存储token基本信息,设置过期时间
+        redisTemplate.opsForValue().set(tokenKey, operatorId, expireSeconds, TimeUnit.SECONDS);
+        
+        // 将token添加到operator的token集合中
+        redisTemplate.opsForSet().add(operatorTokensKey, token);
+        redisTemplate.expire(operatorTokensKey, expireSeconds, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 检查Token是否在Redis中
+     */
+    private boolean isTokenInRedis(String token) {
+        String tokenKey = REDIS_TOKEN_PREFIX + token;
+        Boolean exists = redisTemplate.hasKey(tokenKey);
+        log.info("检查Token是否在Redis中, key: {}, exists: {}", tokenKey, exists);
+        return Boolean.TRUE.equals(exists);
+    }
+
+    /**
+     * 获取Token剩余有效时间
+     */
+    public Long getRemainingTTL(String token) {
+        String tokenKey = REDIS_TOKEN_PREFIX + token;
+        return redisTemplate.getExpire(tokenKey, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 撤销Token(使其立即失效)
+     */
+    public void revokeToken(String token) {
+        String operatorId = getOperatorIdFromToken(token);
+        if (operatorId != null) {
+            String tokenKey = REDIS_TOKEN_PREFIX + token;
+            String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
+            
+            redisTemplate.delete(tokenKey);
+            redisTemplate.opsForSet().remove(operatorTokensKey, token);
+        }
+    }
+
+    /**
+     * 查询操作员现有的有效Token
+     * 如果Token存在且剩余过期时间大于1分钟,则返回该Token
+     */
+    private String getExistingValidToken(String operatorId) {
+        String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
+        Set<Object> tokenObjects = redisTemplate.opsForSet().members(operatorTokensKey);
+        Set<String> tokens = tokenObjects.stream()
+                .filter(Objects::nonNull)
+                .map(Object::toString)
+                .collect(Collectors.toSet());
+
+        if (tokens != null && !tokens.isEmpty()) {
+            for (String token : tokens) {
+                String tokenKey = REDIS_TOKEN_PREFIX + token;
+                // 检查token是否存在
+                if (Boolean.TRUE.equals(redisTemplate.hasKey(tokenKey))) {
+                    // 获取剩余过期时间
+                    Long remainingTTL = redisTemplate.getExpire(tokenKey, TimeUnit.SECONDS);
+                    // 如果剩余时间大于1分钟(60秒),则返回该token
+                    if (remainingTTL != null && remainingTTL > 60) {
+                        return token;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}

+ 99 - 74
src/main/java/com/zsElectric/openapi/controller/ChargingController.java

@@ -1,105 +1,130 @@
 package com.zsElectric.openapi.controller;
 
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.zsElectric.openapi.business.entity.ChargingStation;
-import com.zsElectric.openapi.business.mapper.ChargingStationMapper;
-import com.zsElectric.openapi.business.service.ChargingStationService;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.zsElectric.openapi.business.service.ChargingBusinessService;
 import com.zsElectric.openapi.common.Result;
-import com.zsElectric.openapi.entity.ApiLog;
+import com.zsElectric.openapi.dto.StartChargingRequestDTO;
 import com.zsElectric.openapi.mapper.ApiLogMapper;
+import com.zsElectric.openapi.vo.*;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
- * 充电桩API控制器
+ * 充电业务控制器
  *
  * @author Ray.Hao
  */
-@Tag(name = "充电桩管理", description = "充电桩相关API接口")
+@Slf4j
+@Tag(name = "充电业务管理", description = "充电业务相关API接口")
 @RestController
 @RequestMapping("/api/v1/charging")
 @RequiredArgsConstructor
 public class ChargingController {
 
-    private final ChargingStationService chargingStationService;
-    private final ChargingStationMapper chargingStationMapper;
-    private final ApiLogMapper apiLogMapper;
+    private final ChargingBusinessService chargingBusinessService;
 
-    @Operation(summary = "获取充电桩列表", description = "获取所有可用充电桩列表")
-    @GetMapping("/stations")
-    public Result<Map<String, Object>> getStationList(
-            @Parameter(description = "城市名称")
-            @RequestParam(required = false) String city,
-            HttpServletRequest request) {
-
-        // 调用业务系统数据源获取充电站数据
-        List<ChargingStation> stations;
-        if (city != null && !city.isEmpty()) {
-            stations = chargingStationService.getStationsByCity(city);
-        } else {
-            stations = chargingStationService.getActiveStations();
-        }
-
-        Map<String, Object> data = new HashMap<>();
-        data.put("total", stations.size());
-        data.put("list", stations);
-        return Result.success(data);
+    /**
+     * 查询业务策略信息
+     */
+    @Operation(summary = "查询业务策略信息", description = "查询充电桩的价格策略信息")
+    @GetMapping("/query_equip_business_policy")
+    public Result<ChargingPricePolicyVO> queryEquipBusinessPolicy(
+            @Parameter(description = "设备业务流水号", example = "123456789123456789123456789")
+            @RequestParam("EquipBizSeq") String EquipBizSeq,
+            @Parameter(description = "充电设备接口编码")
+            @RequestParam("ConnectorID") String ConnectorID) throws Exception {
+        log.info("查询业务策略信息, EquipBizSeq={}, ConnectorID={}", EquipBizSeq, ConnectorID);
+        ChargingPricePolicyVO result = chargingBusinessService.queryEquipBusinessPolicy(EquipBizSeq, ConnectorID);
+        return Result.success(result);
     }
 
-    @Operation(summary = "获取充电桩详情", description = "根据ID获取充电桩详细信息")
-    @GetMapping("/stations/{stationId}")
-    public Result<Map<String, Object>> getStationDetail(
-            @Parameter(description = "充电桩ID", required = true)
-            @PathVariable("stationId") Long stationId) {
+    /**
+     * 请求设备认证
+     */
+    @Operation(summary = "请求设备认证", description = "设备认证请求")
+    @GetMapping("/query_equip_auth")
+    public Result<EquipmentAuthResponseVO> queryEquipAuth(
+            @Parameter(description = "设备认证流水号")
+            @RequestParam("EquipAuthSeq") String EquipAuthSeq,
+            @Parameter(description = "充电设备接口编码")
+            @RequestParam("ConnectorID") String ConnectorID) throws JsonProcessingException {
+        log.info("请求设备认证, EquipAuthSeq={}, ConnectorID={}", EquipAuthSeq, ConnectorID);
+        EquipmentAuthResponseVO result = chargingBusinessService.queryEquipAuth(EquipAuthSeq, ConnectorID);
+        return Result.success(result);
+    }
 
-        // 调用业务系统数据源获取充电站详情
-        ChargingStation station = chargingStationService.getStationById(stationId);
+    /**
+     * 查询充电站信息
+     */
+    @Operation(summary = "查询充电站信息", description = "查询充电站基本信息")
+    @GetMapping("/query_stations_info")
+    public Result<QueryStationsInfoVO> queryStationsInfo(
+            @Parameter(description = "最后一次查询时间")
+            @RequestParam(value = "LastQueryTime", required = false) String LastQueryTime,
+            @Parameter(description = "页码")
+            @RequestParam(value = "PageNo", defaultValue = "1") Integer PageNo,
+            @Parameter(description = "页大小")
+            @RequestParam(value = "PageSize", defaultValue = "10") Integer PageSize) throws Exception {
+        log.info("查询充电站信息, LastQueryTime={}, PageNo={}, PageSize={}", LastQueryTime, PageNo, PageSize);
+        QueryStationsInfoVO result = chargingBusinessService.queryStationsInfo(LastQueryTime, PageNo, PageSize);
+        return Result.success(result);
+    }
 
-        if (station == null) {
-            return Result.error(404, "充电站不存在");
-        }
+    /**
+     * 设备接口状态查询
+     */
+    @Operation(summary = "设备接口状态查询", description = "查询充电站设备接口状态")
+    @PostMapping("/query_station_status")
+    public Result<QueryStationStatusVO> queryStationStatus(
+            @RequestBody List<String> stationIDs) throws JsonProcessingException {
+        log.info("设备接口状态查询, stationIDs={}", stationIDs);
+        QueryStationStatusVO result = chargingBusinessService.queryStationStatus(stationIDs);
+        return Result.success(result);
+    }
 
-        Map<String, Object> data = new HashMap<>();
-        data.put("stationId", station.getId());
-        data.put("stationCode", station.getStationCode());
-        data.put("stationName", station.getStationName());
-        data.put("longitude", station.getLongitude());
-        data.put("latitude", station.getLatitude());
-        data.put("province", station.getProvince());
-        data.put("city", station.getCity());
-        data.put("district", station.getDistrict());
-        data.put("address", station.getAddress());
-        data.put("phone", station.getPhone());
-        data.put("status", station.getStatus());
-        return Result.success(data);
+    /**
+     * 请求启动充电
+     */
+    @Operation(summary = "请求启动充电", description = "启动充电")
+    @PostMapping("/query_start_charge")
+    public Result<StartChargingResponseVO> startCharging(
+            @RequestBody StartChargingRequestDTO requestDTO) throws JsonProcessingException {
+        log.info("请求启动充电, requestDTO={}", requestDTO);
+        StartChargingResponseVO result = chargingBusinessService.startCharging(requestDTO);
+        return Result.success(result);
     }
 
-    @Operation(summary = "获取充电桩实时状态", description = "获取充电桩的实时使用状态")
-    @GetMapping("/stations/{stationId}/status")
-    public Result<Map<String, Object>> getStationStatus(
-            @Parameter(description = "充电桩ID", required = true)
-            @PathVariable("stationId") Long stationId) {
+    /**
+     * 查询充电状态
+     */
+    @Operation(summary = "查询充电状态", description = "查询当前充电状态")
+    @PostMapping("/query_equip_charge_status")
+    public Result<ChargingStatusQueryResponseVO> queryChargingStatus(
+            @Parameter(description = "充电订单号(格式:运营商ID+唯一编号)")
+            @RequestParam("StartChargeSeq") String StartChargeSeq) throws JsonProcessingException {
+        log.info("查询充电状态, StartChargeSeq={}", StartChargeSeq);
+        ChargingStatusQueryResponseVO result = chargingBusinessService.queryChargingStatus(StartChargeSeq);
+        return Result.success(result);
+    }
 
-        // TODO: 从业务系统的实时数据表中查询充电桩状态
-        // 这里先返回模拟数据
-        Map<String, Object> data = new HashMap<>();
-        data.put("stationId", stationId);
-        data.put("status", "AVAILABLE");
-        data.put("availablePiles", 10);
-        data.put("totalPiles", 20);
-        data.put("chargingCount", 10);
-        return Result.success(data);
+    /**
+     * 请求停止充电
+     */
+    @Operation(summary = "请求停止充电", description = "停止充电")
+    @GetMapping("/query_stop_charge")
+    public Result<StopChargingOperationResponseVO> stopCharging(
+            @Parameter(description = "充电订单号(格式:运营商ID+唯一编号)")
+            @RequestParam("StartChargeSeq") String StartChargeSeq,
+            @Parameter(description = "充电设备接口编码")
+            @RequestParam("ConnectorID") String ConnectorID) throws JsonProcessingException {
+        log.info("请求停止充电, StartChargeSeq={}, ConnectorID={}", StartChargeSeq, ConnectorID);
+        StopChargingOperationResponseVO result = chargingBusinessService.stopCharging(StartChargeSeq, ConnectorID);
+        return Result.success(result);
     }
 }

+ 86 - 0
src/main/java/com/zsElectric/openapi/controller/LinkDataController.java

@@ -0,0 +1,86 @@
+package com.zsElectric.openapi.controller;
+
+import com.zsElectric.openapi.business.entity.RequestParmsEntity;
+import com.zsElectric.openapi.business.entity.ResponseParmsEntity;
+import com.zsElectric.openapi.business.service.ChargingReceptionService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@Tag(name = "充电业务相关接口_第三方")
+@RequestMapping("/charge-business/v1/linkData")
+public class LinkDataController {
+
+    private final ChargingReceptionService chargingReceptionService;
+
+    /**
+     *  获取Token
+     *  Token作为全局唯一凭证,调用各接口时均需要使用,该接口作为双方获取Token的接口,双方均需要实现。
+     * @author wzq
+     * @param request,参数为类则包含{@link RequestParmsEntity}
+     * @return ResponseParmsEntity
+     */
+    @Operation(summary = "获取token")
+    @PostMapping("/query_token")
+    public ResponseParmsEntity getToken(@RequestBody RequestParmsEntity request) throws Exception {
+        return chargingReceptionService.getToken(request);
+    }
+
+    /**
+     * <p>2.4 推送启动充电结果</p>
+     * @author SheepHy
+     * @param requestDTO,参数为类则包含{@link RequestParmsEntity}
+     * @return 推送启动充电结果VO
+     */
+    @Operation(summary = "推送启动充电结果")
+    @PostMapping("/notification_start_charge_result")
+    public ResponseParmsEntity chargeResponse(@RequestBody RequestParmsEntity requestDTO){
+        return chargingReceptionService.chargeResponse(requestDTO);
+    }
+
+    /**
+     * 2.6 推送充电状态
+     * */
+    @Operation(summary = "推送充电状态")
+    @PostMapping("/notification_equip_charge_status")
+    public ResponseParmsEntity chargeStatusResponse(@RequestBody RequestParmsEntity requestDTO){
+        return chargingReceptionService.chargeStatusResponse(requestDTO);
+    }
+
+    /**
+     * 2.8 推送停止充电结果
+     * */
+    @Operation(summary = "推送停止充电结果")
+    @PostMapping("/notification_stop_charge_result")
+    public ResponseParmsEntity stopChargeResponse(@RequestBody RequestParmsEntity requestDTO){
+        return chargingReceptionService.stopChargeResponse(requestDTO);
+    }
+
+    /**
+     * 2.9 推送充电订单信息
+     * */
+    @Operation(summary = "推送充电订单信息")
+    @PostMapping("/notification_charge_order_info")
+    public ResponseParmsEntity chargeOrderResponse(@RequestBody RequestParmsEntity requestDTO) throws Exception {
+        return chargingReceptionService.chargeOrderResponse(requestDTO);
+    }
+
+    /**
+     * 3.2 设备状态变化推送
+     * @param requestDTO
+     * @return
+     */
+    @Operation(summary = "设备状态变化推送")
+    @PostMapping("/notification_stationStatus")
+    public ResponseParmsEntity stationStatus(@RequestBody RequestParmsEntity requestDTO){
+        return chargingReceptionService.stationStatus(requestDTO);
+    }
+}

+ 0 - 103
src/main/java/com/zsElectric/openapi/controller/OrderController.java

@@ -1,103 +0,0 @@
-package com.zsElectric.openapi.controller;
-
-import com.zsElectric.openapi.business.entity.ChargingOrder;
-import com.zsElectric.openapi.business.service.ChargingOrderService;
-import com.zsElectric.openapi.common.Result;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 订单API控制器
- *
- * @author Ray.Hao
- */
-@Tag(name = "订单管理", description = "订单相关API接口")
-@RestController
-@RequestMapping("/api/v1/order")
-@RequiredArgsConstructor
-public class OrderController {
-
-    private final ChargingOrderService chargingOrderService;
-
-    @Operation(summary = "查询订单列表", description = "根据条件查询订单列表")
-    @GetMapping("/list")
-    public Result<Map<String, Object>> getOrderList(
-            @Parameter(description = "订单编号")
-            @RequestParam(required = false) String orderNo,
-            @Parameter(description = "用户手机号")
-            @RequestParam(required = false) String userPhone,
-            @Parameter(description = "订单状态")
-            @RequestParam(required = false) Integer status,
-            @Parameter(description = "页码")
-            @RequestParam(defaultValue = "1") Integer page,
-            @Parameter(description = "每页数量")
-            @RequestParam(defaultValue = "10") Integer size) {
-
-        // 调用业务系统数据源查询订单列表
-        List<ChargingOrder> orders = chargingOrderService.searchOrders(orderNo, userPhone, status, page, size);
-
-        Map<String, Object> data = new HashMap<>();
-        data.put("total", orders.size());
-        data.put("page", page);
-        data.put("size", size);
-        data.put("list", orders);
-        return Result.success(data);
-    }
-
-    @Operation(summary = "查询订单详情", description = "根据订单ID查询订单详细信息")
-    @GetMapping("/{orderId}")
-    public Result<Map<String, Object>> getOrderDetail(
-            @Parameter(description = "订单ID", required = true)
-            @PathVariable("orderId") Long orderId) {
-
-        // 调用业务系统数据源查询订单详情
-        ChargingOrder order = chargingOrderService.getOrderById(orderId);
-
-        if (order == null) {
-            return Result.error(404, "订单不存在");
-        }
-
-        Map<String, Object> data = new HashMap<>();
-        data.put("orderId", order.getId());
-        data.put("orderNo", order.getOrderNo());
-        data.put("stationId", order.getStationId());
-        data.put("pileId", order.getPileId());
-        data.put("userId", order.getUserId());
-        data.put("userPhone", order.getUserPhone());
-        data.put("status", order.getStatus());
-        data.put("startTime", order.getStartTime());
-        data.put("endTime", order.getEndTime());
-        data.put("duration", order.getDuration());
-        data.put("electricity", order.getElectricity());
-        data.put("amount", order.getAmount());
-        data.put("payAmount", order.getPayAmount());
-        data.put("payStatus", order.getPayStatus());
-        return Result.success(data);
-    }
-
-    @Operation(summary = "根据用户ID查询订单", description = "根据用户ID查询该用户的所有订单")
-    @GetMapping("/user/{userId}")
-    public Result<Map<String, Object>> getOrdersByUserId(
-            @Parameter(description = "用户ID", required = true)
-            @PathVariable("userId") Long userId) {
-
-        // 调用业务系统数据源查询用户订单
-        List<ChargingOrder> orders = chargingOrderService.getOrdersByUserId(userId);
-
-        Map<String, Object> data = new HashMap<>();
-        data.put("total", orders.size());
-        data.put("list", orders);
-        return Result.success(data);
-    }
-}

+ 52 - 0
src/main/java/com/zsElectric/openapi/dto/StartChargingRequestDTO.java

@@ -0,0 +1,52 @@
+package com.zsElectric.openapi.dto;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "请求启动充电DTO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class StartChargingRequestDTO {
+
+    @NotBlank(message = "充电订单号不能为空")
+    @Pattern(regexp = "^\\d{27}$", message = "充电订单号必须为27位数字")
+    @Schema(description = "充电订单号(格式:运营商ID+唯一编号,27位)",
+            example = "123456789201805071630123456",
+            required = true)
+    @JsonProperty("StartChargeSeq")
+    private String StartChargeSeq;
+
+    @NotBlank(message = "充电设备接口编码不能为空")
+    @Schema(description = "充电设备接口编码(见中电联协议 102.2-2016)",
+            example = "3702120244102_1",
+            required = true)
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    @Schema(description = "手机号(平台扩展字段)",
+            example = "13800138000")
+    @JsonProperty("PhoneNum")
+    private String PhoneNum;
+
+    @Schema(description = "车牌号(平台扩展字段)",
+            example = "京A12345")
+    @JsonProperty("PlateNum")
+    private String PlateNum;
+
+    @Schema(description = "预付金额(单位元)(平台扩展字段)",
+            example = "50.00")
+    @JsonProperty("ChargingAmt")
+    private String ChargingAmt;
+
+    @Schema(description = "二维码其他信息(二维码中含有自定义部分的,需要将二维码自定义部分的原文传输)",
+            example = "custom_qr_info_123456")
+    @JsonProperty("QRCode")
+    private String QRCode;
+}

+ 64 - 0
src/main/java/com/zsElectric/openapi/vo/ChargingPricePolicyVO.java

@@ -0,0 +1,64 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "充电桩价格策略响应VO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ChargingPricePolicyVO {
+
+    @Schema(description = "设备业务序列号", example = "123456789123456789123456789")
+    @JsonProperty("EquipBizSeq")
+    private String EquipBizSeq;
+
+    @Schema(description = "充电桩ID", example = "TEST0000901_1")
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    @Schema(description = "成功状态(0-成功,1-失败)", example = "0")
+    @JsonProperty("SuccStat")
+    private Integer SuccStat;
+
+    @Schema(description = "失败原因(0-无错误)", example = "0")
+    @JsonProperty("FailReason")
+    private Integer FailReason;
+
+    @Schema(description = "总时段数", example = "3")
+    @JsonProperty("SumPeriod")
+    private Integer SumPeriod;
+
+    @Schema(description = "价格策略信息列表")
+    @JsonProperty("PolicyInfos")
+    private List<PolicyInfo> PolicyInfos;
+
+    @Data
+    @Accessors(chain = true)
+    @Schema(description = "价格策略信息")
+    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+    public static class PolicyInfo {
+
+        @Schema(description = "时段开始时间(HHmmss格式)", example = "000000")
+        @JsonProperty("StartTime")
+        private String StartTime;
+
+        @Schema(description = "电价(元/度)", example = "0.7")
+        @JsonProperty("ElecPrice")
+        private BigDecimal ElecPrice;
+
+        @Schema(description = "服务费(元/度)", example = "1.0")
+        @JsonProperty("ServicePrice")
+        private BigDecimal ServicePrice;
+
+        @Schema(description = "时段标志(平台扩展字段,1-尖,2-峰,3-平,4-谷)", example = "1")
+        @JsonProperty("PeriodFlag")
+        private Integer PeriodFlag;
+    }
+}

+ 162 - 0
src/main/java/com/zsElectric/openapi/vo/ChargingStatusQueryResponseVO.java

@@ -0,0 +1,162 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "充电状态查询响应VO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ChargingStatusQueryResponseVO {
+
+    @Schema(description = "订单状态(1-启动中,2-启动成功,3-启动失败,4-结束)",
+            example = "2",
+            allowableValues = {"1", "2", "3", "4"},
+            required = true)
+    @JsonProperty("StartChargeSeqStat")
+    private Integer StartChargeSeqStat;
+
+    @Schema(description = "充电订单号(格式:运营商ID+唯一编号)",
+            example = "123456789201805071630123456",
+            required = true)
+    @JsonProperty("StartChargeSeq")
+    private String StartChargeSeq;
+
+    @Schema(description = "充电设备接口编码",
+            example = "3702120244102_1",
+            required = true)
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    @Schema(description = "设备接口状态(0-离网,1-空闲,2-占用(未充电),3-占用(充电中),4-占用(预约锁定),255-故障)",
+            example = "3",
+            allowableValues = {"0", "1", "2", "3", "4", "255"},
+            required = true)
+    @JsonProperty("ConnectorStatus")
+    private Integer ConnectorStatus;
+
+    @Schema(description = "累计充电电量,单位:kWh",
+            example = "25.68",
+            required = true)
+    @JsonProperty("TotalPower")
+    private BigDecimal TotalPower;
+
+    @Schema(description = "电费,单位:元",
+            example = "15.80",
+            required = true)
+    @JsonProperty("ElecMoney")
+    private BigDecimal ElecMoney;
+
+    @Schema(description = "服务费,单位:元",
+            example = "3.20",
+            required = true)
+    @JsonProperty("SeviceMoney")
+    private BigDecimal SeviceMoney;
+
+    @Schema(description = "总金额,单位:元",
+            example = "19.00",
+            required = true)
+    @JsonProperty("TotalMoney")
+    private BigDecimal TotalMoney;
+
+    @Schema(description = "A相电流,单位:A",
+            example = "0.0")
+    @JsonProperty("CurrentA")
+    private BigDecimal CurrentA;
+
+    @Schema(description = "B相电流,单位:A",
+            example = "0.0")
+    @JsonProperty("CurrentB")
+    private BigDecimal CurrentB;
+
+    @Schema(description = "C相电流,单位:A",
+            example = "0.0")
+    @JsonProperty("CurrentC")
+    private BigDecimal CurrentC;
+
+    @Schema(description = "A相电压,单位:V",
+            example = "0.0")
+    @JsonProperty("VoltageA")
+    private BigDecimal VoltageA;
+
+    @Schema(description = "B相电压,单位:V",
+            example = "0.0")
+    @JsonProperty("VoltageB")
+    private BigDecimal VoltageB;
+
+    @Schema(description = "C相电压,单位:V",
+            example = "0.0")
+    @JsonProperty("VoltageC")
+    private BigDecimal VoltageC;
+
+    @Schema(description = "电池SOC(剩余电量百分比)",
+            example = "0.0")
+    @JsonProperty("Soc")
+    private BigDecimal Soc;
+
+    @Schema(description = "充电开始时间,格式\"yyyy-MM-dd HH:mm:ss\"",
+            example = "2025-12-05 09:40:07")
+    @JsonProperty("StartTime")
+    private String StartTime;
+
+    @Schema(description = "充电结束时间,格式\"yyyy-MM-dd HH:mm:ss\"",
+            example = "2025-12-05 09:41:52")
+    @JsonProperty("EndTime")
+    private String EndTime;
+
+    @Schema(description = "充电明细信息体列表",
+            required = true)
+    @JsonProperty("ChargeDetails")
+    private List<ChargeDetail> ChargeDetails;
+
+    @Data
+    @Accessors(chain = true)
+    @Schema(description = "充电明细信息体")
+    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+    public static class ChargeDetail {
+
+        @Schema(description = "开始时间,格式\"yyyy-MM-dd HH:mm:ss\"",
+                example = "2024-01-15 10:30:00",
+                required = true)
+        @JsonProperty("DetailStartTime")
+        private String DetailStartTime;
+
+        @Schema(description = "结束时间,格式\"yyyy-MM-dd HH:mm:ss\"",
+                example = "2024-01-15 11:30:00",
+                required = true)
+        @JsonProperty("DetailEndTime")
+        private String DetailEndTime;
+
+        @Schema(description = "时段电价,小数点后2位",
+                example = "0.65")
+        @JsonProperty("ElecPrice")
+        private BigDecimal ElecPrice;
+
+        @Schema(description = "时段服务费价格,小数点后2位",
+                example = "0.15")
+        @JsonProperty("SevicePrice")
+        private BigDecimal SevicePrice;
+
+        @Schema(description = "时段充电量,单位:度,小数点后2位",
+                example = "12.34",
+                required = true)
+        @JsonProperty("DetailPower")
+        private BigDecimal DetailPower;
+
+        @Schema(description = "时段电费,小数点后2位",
+                example = "8.02")
+        @JsonProperty("DetailElecMoney")
+        private BigDecimal DetailElecMoney;
+
+        @Schema(description = "时段服务费,小数点后2位",
+                example = "1.85")
+        @JsonProperty("DetailSeviceMoney")
+        private BigDecimal DetailSeviceMoney;
+    }
+}

+ 40 - 0
src/main/java/com/zsElectric/openapi/vo/EquipmentAuthResponseVO.java

@@ -0,0 +1,40 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "设备认证响应VO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class EquipmentAuthResponseVO {
+
+    @Schema(description = "设备认证流水号(格式:运营商ID+唯一编号)",
+            example = "123456789201805071630123456")
+    @JsonProperty("EquipAuthSeq")
+    private String EquipAuthSeq;
+
+    @Schema(description = "充电设备接口编码",
+            example = "3702120244102_1")
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    @Schema(description = "操作结果(0-成功,1-失败)",
+            example = "0",
+            allowableValues = {"0", "1"})
+    @JsonProperty("SuccStat")
+    private Integer SuccStat;
+
+    @Schema(description = "失败原因(0-无,1-此设备尚未插枪,2-设备检测失败,其它-自定义)",
+            example = "0")
+    @JsonProperty("FailReason")
+    private Integer FailReason;
+
+    @Schema(description = "失败原因描述",
+            example = "无错误")
+    @JsonProperty("FailReasonMsg")
+    private String FailReasonMsg;
+}

+ 31 - 0
src/main/java/com/zsElectric/openapi/vo/QueryStationStatusVO.java

@@ -0,0 +1,31 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.zsElectric.openapi.business.entity.ConnectorStatusInfo;
+import com.zsElectric.openapi.business.entity.StationStatusInfo;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class QueryStationStatusVO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty("StationStatusInfos")
+    List<StationStatusInfo> StationStatusInfos;
+
+    @JsonProperty("Total")
+    private Integer Total;
+
+    /**
+     * 支持单个连接器状态推送格式:{"ConnectorStatusInfo":{"ConnectorID":"xxx","Status":2}}
+     */
+    @JsonProperty("ConnectorStatusInfo")
+    private ConnectorStatusInfo ConnectorStatusInfo;
+}

+ 32 - 0
src/main/java/com/zsElectric/openapi/vo/QueryStationsInfoVO.java

@@ -0,0 +1,32 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.zsElectric.openapi.business.entity.StationInfo;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class QueryStationsInfoVO {
+
+    /** 当前页数 - 如果查询页码大于页码总数,返回查询页码数 */
+    @JsonProperty("PageNo")
+    private Integer PageNo;
+
+    /** 页码总数 - 总页数 */
+    @JsonProperty("PageCount")
+    private Integer PageCount;
+
+    /** 总记录条数 - 符合条件的电站总数 */
+    @JsonProperty("ItemSize")
+    private Integer ItemSize;
+
+    /** 充电站信息列表 - 包含充电站的基本信息、服务信息、支付信息、设备信息等 */
+    @JsonProperty("StationInfos")
+    private List<StationInfo> StationInfos;
+}

+ 53 - 0
src/main/java/com/zsElectric/openapi/vo/StartChargingResponseVO.java

@@ -0,0 +1,53 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "启动充电返回VO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class StartChargingResponseVO {
+
+    @Schema(description = "充电订单号(格式:运营商ID+唯一编号)",
+            example = "123456789201805071630123456",
+            required = true)
+    @JsonProperty("StartChargeSeq")
+    private String StartChargeSeq;
+
+    @Schema(description = "充电订单状态(1-启动中,2-充电中,3-停止中,4-已结束,5-未知)",
+            example = "1",
+            allowableValues = {"1", "2", "3", "4", "5"},
+            required = true)
+    @JsonProperty("StartChargeSeqStat")
+    private Integer StartChargeSeqStat;
+
+    @Schema(description = "充电设备接口编码(参见《电动汽车充换电服务信息交换 第2部分:公共信息交换规范》)",
+            example = "3702120244102_1",
+            required = true)
+    @JsonProperty("ConnectorID")
+    private String ConnectorID;
+
+    @Schema(description = "操作结果(0-成功,1-失败)",
+            example = "0",
+            allowableValues = {"0", "1"},
+            required = true)
+    @JsonProperty("SuccStat")
+    private Integer SuccStat;
+
+    @Schema(description = "失败原因(0-无,1-此设备不存在,2-此设备离线,其它-自定义)",
+            example = "0",
+            allowableValues = {"0", "1", "2"},
+            required = true)
+    @JsonProperty("FailReason")
+    private Integer FailReason;
+
+    @Schema(description = "失败原因描述",
+            example = "无错误",
+            required = true)
+    @JsonProperty("FailReasonMsg")
+    private String FailReasonMsg;
+}

+ 47 - 0
src/main/java/com/zsElectric/openapi/vo/StopChargingOperationResponseVO.java

@@ -0,0 +1,47 @@
+package com.zsElectric.openapi.vo;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+@Schema(description = "停止充电操作响应VO")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class StopChargingOperationResponseVO {
+
+    @Schema(description = "充电订单号(格式:运营商ID+唯一编号)",
+            example = "123456789201805071630123456",
+            required = true)
+    @JsonProperty("StartChargeSeq")
+    private String StartChargeSeq;
+
+    @Schema(description = "充电订单状态(1:启动中;2:充电中;3:停止中;4:已结束;5:未知)",
+            example = "4",
+            allowableValues = {"1", "2", "3", "4", "5"},
+            required = true)
+    @JsonProperty("StartChargeSeqStat")
+    private Integer StartChargeSeqStat;
+
+    @Schema(description = "操作结果(0:成功;1:失败)",
+            example = "0",
+            allowableValues = {"0", "1"},
+            required = true)
+    @JsonProperty("SuccStat")
+    private Integer SuccStat;
+
+    @Schema(description = "失败原因(0:无;1:此设备不存在;2:此设备离线;3:设备已停止充电;其它:自定义)",
+            example = "0",
+            allowableValues = {"0", "1", "2", "3"},
+            required = true)
+    @JsonProperty("FailReason")
+    private Integer FailReason;
+
+    @Schema(description = "失败原因描述",
+            example = "停止成功",
+            required = true)
+    @JsonProperty("FailReasonMsg")
+    private String FailReasonMsg;
+}

+ 1 - 1
src/main/resources/sql/init.sql

@@ -3,7 +3,7 @@
 -- ========================================
 
 -- 1. 创建开放平台独立数据库(如果不存在)
-CREATE DATABASE IF NOT EXISTS `zs_electric_openapi` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='zsElectric开放平台数据库';
+CREATE DATABASE IF NOT EXISTS `zs_electric_openapi` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
 
 USE `zs_electric_openapi`;