Parcourir la source

feat(thirdParty): 支持渠道方启动、停止充电及查询订单

- 新增第三方启动充电请求和响应数据模型
- 新增第三方停止充电请求和响应数据模型
- 新增第三方查询充电订单列表及详情请求和响应数据模型
- thirdPartyController增加启动充电、停止充电、查询订单接口及日志记录
- thirdPartyTokenService接口及实现新增启动充电、停止充电、查询渠道订单列表和详情方法
- 充电订单实体、表单、视图等添加运维商ID字段支持渠道方运营商区分
- 充电订单服务实现中渠道方订单设置运营商ID
- 数据大盘新增抵扣券购买金额统计,包括今日及累计金额
- 修正平台运营商ID与密钥校验逻辑,提高安全性和返回信息准确性
- 部分分页请求响应数据类型由Integer改为Long,统一数据类型
- 优化并补充接口参数注释及代码结构规范
wzq il y a 3 jours
Parent
commit
83a01a022a
28 fichiers modifiés avec 2187 ajouts et 62 suppressions
  1. 979 0
      doc/第三方接入API文档.md
  2. 5 0
      src/main/java/com/zsElectric/boot/business/model/entity/ChargeOrderInfo.java
  3. 4 0
      src/main/java/com/zsElectric/boot/business/model/form/applet/AppInvokeChargeForm.java
  4. 1 1
      src/main/java/com/zsElectric/boot/business/model/vo/ChargeOrderInfoVO.java
  5. 7 0
      src/main/java/com/zsElectric/boot/business/service/impl/ChargeOrderInfoServiceImpl.java
  6. 4 5
      src/main/java/com/zsElectric/boot/charging/controller/LinkDataController.java
  7. 3 3
      src/main/java/com/zsElectric/boot/common/util/electric/queryToken/ThirdPartyJwtAuthFilter.java
  8. 6 2
      src/main/java/com/zsElectric/boot/system/mapper/DataBoardMapper.java
  9. 2 2
      src/main/java/com/zsElectric/boot/system/model/vo/DataBoardRealTimeVO.java
  10. 1 1
      src/main/java/com/zsElectric/boot/system/model/vo/DataBoardTodayVO.java
  11. 12 3
      src/main/java/com/zsElectric/boot/system/service/impl/DataBoardServiceImpl.java
  12. 36 0
      src/main/java/com/zsElectric/boot/thirdParty/controller/ThirdPartyController.java
  13. 25 0
      src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderDetailRequestData.java
  14. 159 0
      src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderDetailResponseData.java
  15. 49 0
      src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderListRequestData.java
  16. 182 0
      src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderListResponseData.java
  17. 6 0
      src/main/java/com/zsElectric/boot/thirdParty/model/ChargeStationListRequestData.java
  18. 3 3
      src/main/java/com/zsElectric/boot/thirdParty/model/ChargeStationListResponseData.java
  19. 56 0
      src/main/java/com/zsElectric/boot/thirdParty/model/InvokeChargeRequestData.java
  20. 72 0
      src/main/java/com/zsElectric/boot/thirdParty/model/InvokeChargeResponseData.java
  21. 2 2
      src/main/java/com/zsElectric/boot/thirdParty/model/RechargeLevelPageRequestData.java
  22. 3 3
      src/main/java/com/zsElectric/boot/thirdParty/model/RechargeLevelPageResponseData.java
  23. 25 0
      src/main/java/com/zsElectric/boot/thirdParty/model/StopChargeRequestData.java
  24. 72 0
      src/main/java/com/zsElectric/boot/thirdParty/model/StopChargeResponseData.java
  25. 36 0
      src/main/java/com/zsElectric/boot/thirdParty/service/ThirdPartyTokenService.java
  26. 401 4
      src/main/java/com/zsElectric/boot/thirdParty/service/impl/ThirdPartyTokenServiceImpl.java
  27. 36 31
      src/main/resources/mapper/system/DataBoardMapper.xml
  28. 0 2
      src/test/java/com/zsElectric/boot/thirdParty/service/ThirdPartyTokenServiceTest.java

+ 979 - 0
doc/第三方接入API文档.md

@@ -0,0 +1,979 @@
+# 中数电动第三方接入API文档
+
+## 目录
+
+- [1. 平台认证](#1-平台认证)
+  - [1.1 运营商ID](#11-运营商id)
+  - [1.2 请求格式要求](#12-请求格式要求)
+  - [1.3 加密说明](#13-加密说明)
+  - [1.4 ret 返回值说明](#14-ret-返回值说明)
+- [2. 获取Token](#2-获取token)
+- [3. 充值档位信息分页列表](#3-充值档位信息分页列表)
+- [4. 获取用户信息](#4-获取用户信息)
+- [5. 充点券购买](#5-充点券购买)
+- [6. 获取充电站列表](#6-获取充电站列表)
+- [7. 获取充电站详情与充电终端列表](#7-获取充电站详情与充电终端列表)
+- [8. 获取充电终端详情](#8-获取充电终端详情)
+- [9. 启动充电](#9-启动充电)
+- [10. 停止充电](#10-停止充电)
+- [11. 查询充电订单列表](#11-查询充电订单列表)
+- [12. 查询充电订单详情](#12-查询充电订单详情)
+- [附录:配置模板](#附录配置模板)
+
+---
+
+## 1. 平台认证
+
+### 1.1 运营商ID
+
+运营商ID是平台分配给对应渠道方的唯一代码(长度9位,由数字及大小写字符组成)。
+
+### 1.2 请求格式要求
+
+查询接口和回调接口均需要满足以下格式要求:
+
+- 消息头需配置 `Content-Type` 为 `application/json`; `Authorization` 为 `Bearer token`(平台认证接口用于获取token)
+- 入参消息体应由运营商标识(operatorId)、参数内容(data)、时间戳(timeStamp)、自增序列(seq)和数字签名(sig)组成
+
+**入参示例:**
+
+```json
+{
+  "operatorId": "123456789",
+  "data": "mYvffpNoFf4E/ZTC1tOw41TC5OlkEobfAYCm5N8hEusaLUaUIqOrXtdbMrSck0DSmfM7mRuOGMoCQzH0nWPGuw==",
+  "timeStamp": "20180120165755",
+  "seq": "0001",
+  "sig": "D2D584A14F3F284445DF85D0E8C0697C"
+}
+```
+
+**出参示例:**
+
+```json
+{
+  "ret": 0,
+  "msg": "",
+  "data": "uxeKP0ezR5yL8xSg4/ZCDh/N91/u86NXFxd2DrwZVW8zCPYcpl59Twz/yQZ3RaO4rDDrGmkvQignmNEJ+k4PGxdmIC+4fpJ8rU6osSobY+AeA0uueuQ5+eQiWBL6p6v5XMMm91brtK8yfFELYUWQzVcxABnAwK/+dyxtUhqLIxUpkwTEU/4ktN40df9IzzlLO5uvUknPGYu9yL0pp5w9vdRxmA1RiiTDNCysz6klr9bunGV3VJa2qpLcgeZMf/oG",
+  "sig": "58E52010C7DEE87FE183B0AFA5B2BE30"
+}
+```
+
+### 1.3 加密说明
+
+- `data` 里的内容为接口的实际入参和出参,均需要密文传输
+- 加解密方法为 AES 128位加解密,加解密模式为 CBC,填充模式为 PKCS5Padding
+- 签名(sig)采用 HMAC-MD5算法,采用MD5作为散列函数,通过签名密钥(sigSecret)对整个消息主体各参数的值拼接后进行加密
+- 入参拼接顺序为: `operatorId + data + timeStamp + seq`
+- 出参拼接顺序为: `ret + msg + data`
+- ⚠️ 注意:参数签名必须大写
+
+### 1.4 ret 返回值说明
+
+| 返回码 | 说明 |
+|--------|------|
+| 0 | 请求成功 |
+| 500 | 系统错误 |
+| 4001 | 签名错误 |
+| 4002 | token错误 |
+| 4003 | 参数不合法,缺少必需的参数:operatorId、sig、timeStamp、data、seq |
+| 4004 | 请求的业务参数不合法,各接口定义自己的必须参数 |
+
+---
+
+## 2. 获取Token
+
+### 2.1 接口描述
+
+- **接口名称:** query_token
+- **接口说明:** Token作为全局唯一凭证,调用各接口时均需要使用
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_token`
+
+### 2.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| operatorId | 运营商标识 | String | 请求方唯一编号 | 是 |
+| operatorSecret | 运营商密钥 | String | 被调用方分配的唯一识别密钥 | 是 |
+
+### 2.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "operatorId": "123456789",
+  "operatorSecret": "1234567890abcdef"
+}
+```
+
+### 2.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| operatorId | 运营商标识 | String | 运营商唯一标识 |
+| resultStatus | 操作结果标识 | Integer | 0-成功,1-失败 |
+| accessToken | 访问令牌 | String | token令牌 |
+| tokenExpirationTime | token有效期 | Integer | 有效期,单位秒 |
+| failReason | 失败原因 | Integer | 0-无,1-operatorId无效 |
+
+### 2.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "operatorId": "123456789",
+  "resultStatus": 0,
+  "accessToken": "kVnStIRknCCOBdXgJrmu8rb3pcccifKW2NcQrysKKIzb7iQBxkkjeq3WoGdjgrIL",
+  "tokenExpirationTime": 7200,
+  "failReason": 0
+}
+```
+
+---
+
+## 3. 充值档位信息分页列表
+
+### 3.1 接口描述
+
+- **接口名称:** query_recharge_level_page
+- **接口说明:** 第三方获取充值档位分页数据
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_recharge_level_page`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 3.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|------|------|----------|
+| pageNum | 页码 | Long | 页码,默认1 | 否 |
+| pageSize | 每页记录数 | Long | 每页记录数,默认10 | 否 |
+
+### 3.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10
+}
+```
+
+### 3.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型    | 描述 |
+|----------|----------|---------|------|
+| total | 总记录数 | Long    | 总记录数 |
+| pageNum | 当前页码 | Long    | 当前页码 |
+| pageSize | 每页记录数 | Long    | 每页记录数 |
+| pages | 总页数 | Long    | 总页数 |
+| records | 数据列表 | Array   | 充值档位列表 |
+
+**records 数组元素:**
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| id | 档位ID | Long | 档位ID |
+| name | 档位名称 | String | 充值档位名称 |
+| money | 充值金额 | BigDecimal | 充值金额 |
+| status | 状态 | Integer | 0-不可用,1-可用 |
+| tips | 充值提示 | String | 充值提示 |
+
+### 3.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "total": 5,
+  "pageNum": 1,
+  "pageSize": 10,
+  "pages": 1,
+  "records": [
+    {
+      "id": 1,
+      "name": "10元档位",
+      "money": 10.00,
+      "status": 1,
+      "tips": "此券可以用于抵扣充电费用"
+    }
+  ]
+}
+```
+
+---
+
+## 4. 获取用户信息
+
+### 4.1 接口描述
+
+- **接口名称:** query_user_info
+- **接口说明:** 第三方根据手机号获取用户信息
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_user_info`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 4.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| phone | 手机号 | String | 用户手机号 | 是 |
+
+### 4.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "phone": "13800138000"
+}
+```
+
+### 4.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| isNewUser | 是否新用户 | Integer | 0-否,1-是 |
+| userInfo | 用户信息 | Object | 用户基本信息 |
+| accountInfo | 账户信息 | Object | 用户账户信息 |
+
+**userInfo 对象:**
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| userId | 用户ID | Long | 用户ID |
+| nickName | 昵称 | String | 用户昵称 |
+| phone | 手机号 | String | 用户手机号 |
+| openid | 微信openid | String | 微信openid |
+
+**accountInfo 对象:**
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| accountId | 账户ID | Long | 账户ID |
+| balance | 可用抵用券余额 | BigDecimal | 可用抵用券余额 |
+| redeemBalance | 兑换余额 | BigDecimal | 兑换余额 |
+| integral | 积分 | BigDecimal | 积分 |
+
+### 4.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "isNewUser": 0,
+  "userInfo": {
+    "userId": 10001,
+    "nickName": "张三",
+    "phone": "13800138000",
+    "openid": "oXyZ123456"
+  },
+  "accountInfo": {
+    "accountId": 20001,
+    "balance": 100.00,
+    "redeemBalance": 50.00,
+    "integral": 500.00
+  }
+}
+```
+
+---
+
+## 5. 充点券购买
+
+### 5.1 接口描述
+
+- **接口名称:** charge_order_pay
+- **接口说明:** 第三方充点券购买
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/charge_order_pay`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 5.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| levelId | 充值档位ID | Long | 充值档位ID | 是 |
+| userId | 用户ID | Long | 用户ID | 是 |
+| phone | 手机号 | String | 用户手机号 | 是 |
+| orderNo | 订单号 | String | 第三方订单号 | 是 |
+| payTime | 支付时间 | String | 支付时间 | 是 |
+| totalMoney | 总金额 | String | 充值总金额 | 是 |
+
+### 5.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "levelId": 1,
+  "userId": 10001,
+  "phone": "13800138000",
+  "orderNo": "TP2024031600001",
+  "payTime": "2024-03-16 10:30:00",
+  "totalMoney": "100.00"
+}
+```
+
+### 5.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| result | 充值结果 | Integer | 0-成功,1-失败 |
+| message | 结果消息 | String | 结果描述 |
+| userId | 用户ID | Long | 用户ID |
+| rechargeMoney | 充值金额 | BigDecimal | 实际充值金额 |
+| balanceAfter | 充值后余额 | BigDecimal | 充值后账户余额 |
+| orderNo | 第三方订单号 | String | 第三方订单号 |
+
+### 5.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "result": 0,
+  "message": "充值成功",
+  "userId": 10001,
+  "rechargeMoney": 100.00,
+  "balanceAfter": 200.00,
+  "orderNo": "TP2024031600001"
+}
+```
+
+---
+
+## 6. 获取充电站列表
+
+### 6.1 接口描述
+
+- **接口名称:** query_charge_station_list
+- **接口说明:** 第三方获取充电站列表
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_charge_station_list`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 6.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型       | 描述 | 是否必填 |
+|----------|----------|------------|------|----------|
+| pageNum | 页码 | Long       | 页码,默认1 | 否 |
+| pageSize | 每页记录数 | Long       | 每页记录数,默认10 | 否 |
+| sortType | 请求类别 | Integer    | 1-离我最近、2-空闲最多、3-电费最低 | 否 |
+| longitude | 经度 | BigDecimal | 用户当前位置经度 | 否 |
+| latitude | 纬度 | BigDecimal | 用户当前位置纬度 | 否 |
+
+### 6.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10,
+  "sortType": 1,
+  "longitude": 116.404,
+  "latitude": 39.915
+}
+```
+
+### 6.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型  | 描述 |
+|----------|----------|-------|------|
+| total | 总记录数 | Long  | 总记录数 |
+| pageNum | 当前页码 | Long  | 当前页码 |
+| pageSize | 每页大小 | Long  | 每页大小 |
+| pages | 总页数 | Long  | 总页数 |
+| list | 站点列表 | Array | 充电站列表 |
+
+**list 数组元素:**
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| stationId | 站点ID | Long | 充电站ID |
+| stationName | 站点名称 | String | 充电站名称 |
+| tips | 提示语 | String | 站点提示信息 |
+| distance | 距离 | BigDecimal | 距离(km) |
+| fastCharging | 快充 | String | 格式:空闲/总数 |
+| slowCharging | 慢充 | String | 格式:空闲/总数 |
+| peakValue | 当前峰值 | String | 当前峰值 |
+| peakTime | 峰时段时间 | String | 峰时段时间 |
+| periodFlag | 时段标志 | Integer | 1-尖,2-峰,3-平,4-谷 |
+| platformPrice | 平台价 | BigDecimal | 平台价格 |
+
+### 6.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "total": 50,
+  "pageNum": 1,
+  "pageSize": 10,
+  "pages": 5,
+  "list": [
+    {
+      "stationId": 1,
+      "stationName": "XX充电站",
+      "tips": "充电减免2小时停车费",
+      "distance": 1.5,
+      "fastCharging": "5/10",
+      "slowCharging": "3/8",
+      "peakValue": "1.2元/度",
+      "peakTime": "10:00-13:00",
+      "periodFlag": 2,
+      "platformPrice": 1.20
+    }
+  ]
+}
+```
+
+---
+
+## 7. 获取充电站详情与充电终端列表
+
+### 7.1 接口描述
+
+- **接口名称:** query_charge_station_detail
+- **接口说明:** 第三方获取充电站详情与充电设备列表
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_charge_station_detail`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 7.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| stationId | 充电站ID | Long | 充电站ID | 是 |
+| longitude | 经度 | BigDecimal | 用户当前位置经度 | 否 |
+| latitude | 纬度 | BigDecimal | 用户当前位置纬度 | 否 |
+
+### 7.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "stationId": 1,
+  "longitude": 116.404,
+  "latitude": 39.915
+}
+```
+
+### 7.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| stationId | 站点ID | Long | 充电站ID |
+| stationName | 站点名称 | String | 充电站名称 |
+| tips | 提示语 | String | 标签提示信息 |
+| distance | 距离 | BigDecimal | 距离(km) |
+| address | 详细地址 | String | 充电站详细地址 |
+| longitude | 经度 | BigDecimal | 站点经度 |
+| latitude | 纬度 | BigDecimal | 站点纬度 |
+| pictures | 站点图片 | String | 站点图片列表(JSON数组) |
+| currentPrice | 当前价 | BigDecimal | 当前价(元/度) |
+| currentPeriod | 当前时段 | String | 当前时段描述 |
+| originalPrice | 原价 | BigDecimal | 原价/划线价(元/度) |
+| periodFlag | 时段标志 | Integer | 1-尖,2-峰,3-平,4-谷 |
+| idleCount | 空闲终端数 | Integer | 空闲终端数量 |
+| occupiedCount | 占用终端数 | Integer | 占用终端数量 |
+| offlineCount | 离线终端数 | Integer | 离线终端数量 |
+| businessHours | 营业时间 | String | 营业时间 |
+| serviceProvider | 服务提供方 | String | 服务提供方名称 |
+| invoiceProvider | 发票提供方 | String | 发票提供方名称 |
+| customerServiceHotline | 客服热线 | String | 客服电话 |
+| connectorList | 充电终端列表 | Array | 充电终端信息列表 |
+
+**connectorList 数组元素:**
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| connectorId | 充电终端ID | Long | 充电终端ID |
+| connectorName | 终端名称 | String | 终端名称 |
+| equipmentType | 终端分类 | String | 终端分类描述 |
+| connectorCode | 终端编码 | String | 终端编码 |
+| status | 状态 | Integer | 0-离线,1-空闲,2-占用 |
+| statusName | 状态名称 | String | 状态描述 |
+
+### 7.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "stationId": 1,
+  "stationName": "XX充电站",
+  "tips": "充电减免2小时停车费,超出部分按每小时3元计费",
+  "distance": 1.5,
+  "address": "北京市朝阳区XX路XX号",
+  "longitude": 116.404,
+  "latitude": 39.915,
+  "pictures": "[\"http://xxx.com/1.jpg\"]",
+  "currentPrice": 1.20,
+  "currentPeriod": "峰10:00-13:00",
+  "originalPrice": 1.50,
+  "periodFlag": 2,
+  "idleCount": 5,
+  "occupiedCount": 3,
+  "offlineCount": 2,
+  "businessHours": "周一至周日 00:00-24:00",
+  "serviceProvider": "XX充电服务",
+  "invoiceProvider": "XX科技有限公司",
+  "customerServiceHotline": "400-XXX-XXXX",
+  "connectorList": [
+    {
+      "connectorId": 101,
+      "connectorName": "101号直流充电桩",
+      "equipmentType": "直流设备",
+      "connectorCode": "CONN001",
+      "status": 1,
+      "statusName": "空闲"
+    }
+  ]
+}
+```
+
+---
+
+## 8. 获取充电终端详情
+
+### 8.1 接口描述
+
+- **接口名称:** query_charge_device_detail
+- **接口说明:** 第三方获取充电终端详情
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_charge_device_detail`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 8.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| id | 设备主键ID | Long | 设备主键ID | 否 |
+| equipmentId | 设备编码 | String | 设备编码 | 否 |
+
+> 注:id 和 equipmentId 至少传一个
+
+### 8.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "id": 101,
+  "equipmentId": "EQ001"
+}
+```
+
+### 8.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| connectorId | 充电设备接口ID | Long | 充电设备接口ID |
+| connectorCode | 充电设备接口编码 | String | 充电设备接口编码 |
+| connectorName | 充电设备接口名称 | String | 充电设备接口名称 |
+| stationId | 充电站ID | Long | 所属充电站ID |
+| stationName | 充电站名称 | String | 所属充电站名称 |
+| stationAddress | 充电站地址 | String | 所属充电站地址 |
+| equipmentId | 设备ID | Long | 设备ID |
+| equipmentCode | 设备编码 | String | 设备编码 |
+| equipmentName | 设备名称 | String | 设备名称 |
+| equipmentType | 设备类型 | Integer | 1-直流设备,2-交流设备,3-交直流一体设备,4-无线设备,5-其他 |
+| equipmentTypeName | 设备类型名称 | String | 设备类型描述 |
+| parkNo | 车位号 | String | 车位号 |
+| status | 终端状态 | Integer | 0-离网,1-空闲,2-占用(未充电),3-占用(充电中),4-占用(预约锁定),255-故障 |
+| statusName | 终端状态名称 | String | 终端状态描述 |
+| connectorType | 接口类型 | Integer | 1-家用插座,2-交流接口插座,3-交流接口插头,4-直流接口枪头,5-无线充电座,6-其他 |
+| connectorTypeName | 接口类型名称 | String | 接口类型描述 |
+| voltageUpperLimits | 额定电压上限 | Integer | 单位V |
+| voltageLowerLimits | 额定电压下限 | Integer | 单位V |
+| current | 额定电流 | Integer | 单位A |
+| power | 额定功率 | BigDecimal | 单位kW |
+| nationalStandard | 国家标准 | Integer | 1-2011,2-2015 |
+| nationalStandardName | 国家标准名称 | String | 国家标准描述 |
+| currentPrice | 常规价格 | BigDecimal | 元/度(电价+服务费+常规运营费+增值费用) |
+| periodFlag | 时段标志 | Integer | 1-尖,2-峰,3-平,4-谷 |
+| currentPeriodDesc | 当前时段描述 | String | 当前时段描述 |
+| parkingTips | 停车费说明 | String | 提示语/停车费说明 |
+
+### 8.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "connectorId": 101,
+  "connectorCode": "CONN001",
+  "connectorName": "101号充电口",
+  "stationId": 1,
+  "stationName": "XX充电站",
+  "stationAddress": "北京市朝阳区XX路XX号",
+  "equipmentId": 10,
+  "equipmentCode": "EQ001",
+  "equipmentName": "1号直流充电桩",
+  "equipmentType": 1,
+  "equipmentTypeName": "直流设备",
+  "parkNo": "A001",
+  "status": 1,
+  "statusName": "空闲",
+  "connectorType": 4,
+  "connectorTypeName": "直流接口枪头",
+  "voltageUpperLimits": 750,
+  "voltageLowerLimits": 200,
+  "current": 250,
+  "power": 120.00,
+  "nationalStandard": 2,
+  "nationalStandardName": "2015",
+  "currentPrice": 1.20,
+  "periodFlag": 2,
+  "currentPeriodDesc": "峰时段 10:00-13:00",
+  "parkingTips": "充电减免2小时停车费"
+}
+```
+
+---
+
+## 9. 启动充电
+
+### 9.1 接口描述
+
+- **接口名称:** invoke_charge
+- **接口说明:** 第三方启动充电
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/invoke_charge`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 9.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| equipmentId | 充电桩编号 | String | 充电桩编号 | 是 |
+| stationId | 第三方充电站ID | String | 充电站ID | 是 |
+| connectorId | 充电设备接口编码 | String | 充电设备接口编码 | 是 |
+| channelOrderNo | 渠道方订单编号 | String | 第三方平台订单编号 | 是 |
+| channelUserPhone | 渠道方用户手机号 | String | 用户手机号 | 是 |
+| channelPreAmt | 渠道方预支付金额 | BigDecimal | 预支付金额 | 是 |
+
+### 9.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "equipmentId": "EQ001",
+  "stationId": "1",
+  "connectorId": "CONN001",
+  "channelOrderNo": "CH2024031600001",
+  "channelUserPhone": "13800138000",
+  "channelPreAmt": 100.00
+}
+```
+
+### 9.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| result | 启动结果 | Integer | 0-成功,1-失败 |
+| message | 结果消息 | String | 结果描述 |
+| chargeOrderId | 充电订单ID | Long | 平台充电订单ID |
+| chargeOrderNo | 充电订单编号 | String | 平台充电订单编号 |
+| status | 充电状态 | Integer | 0-待启动,1-充电中,2-结算中,3-已完成,5-未成功充电 |
+
+### 9.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "result": 0,
+  "message": "启动充电成功",
+  "chargeOrderId": 100001,
+  "chargeOrderNo": "CD2024031600001",
+  "status": 1
+}
+```
+
+---
+
+## 10. 停止充电
+
+### 10.1 接口描述
+
+- **接口名称:** stop_charge
+- **接口说明:** 第三方停止充电
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/stop_charge`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 10.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| chargeOrderNo | 充电订单编号 | String | 平台充电订单编号 | 是 |
+
+### 10.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "chargeOrderNo": "CD2024031600001"
+}
+```
+
+### 10.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| result | 停止结果 | Integer | 0-成功,1-失败 |
+| message | 结果消息 | String | 结果描述 |
+| chargeOrderId | 充电订单ID | Long | 平台充电订单ID |
+| chargeOrderNo | 充电订单编号 | String | 平台充电订单编号 |
+| status | 充电状态 | Integer | 0-待启动,1-充电中,2-结算中,3-已完成,5-未成功充电 |
+
+### 10.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "result": 0,
+  "message": "停止充电成功",
+  "chargeOrderId": 100001,
+  "chargeOrderNo": "CD2024031600001",
+  "status": 2
+}
+```
+
+---
+
+## 11. 查询充电订单列表
+
+### 11.1 接口描述
+
+- **接口名称:** query_charge_order_list
+- **接口说明:** 第三方查询充电订单列表(只查询渠道类型订单)
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_charge_order_list`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 11.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型   | 描述 | 是否必填 |
+|----------|----------|--------|------|----------|
+| operatorId | 运营商ID | String | 运营商唯一标识 | 是 |
+| userId | 用户ID | Long   | 用户ID | 否 |
+| chargeOrderNo | 充电订单号 | String | 充电订单号 | 否 |
+| pageNo | 页码 | Long   | 页码,默认1 | 否 |
+| pageSize | 每页记录数 | Long   | 每页记录数,默认10 | 否 |
+
+### 11.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "operatorId": "123456789",
+  "userId": 10001,
+  "pageNo": 1,
+  "pageSize": 10
+}
+```
+
+### 11.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型    | 描述 |
+|----------|----------|---------|------|
+| total | 总记录数 | Long    | 总记录数 |
+| pageNo | 当前页码 | Long    | 当前页码 |
+| pageSize | 每页记录数 | Long    | 每页记录数 |
+| pages | 总页数 | Long    | 总页数 |
+| records | 数据列表 | Array   | 订单列表 |
+
+**records 数组元素:**
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| id | 订单ID | Long | 订单ID |
+| userId | 用户ID | Long | 用户ID |
+| chargeOrderNo | 充电订单号 | String | 充电订单号 |
+| equipmentId | 充电桩编号 | String | 充电桩编号 |
+| connectorId | 充电设备接口编码 | String | 充电设备接口编码 |
+| stationName | 充电站名称 | String | 充电站名称 |
+| connectorName | 充电设备接口名称 | String | 充电设备接口名称 |
+| startTime | 充电开始时间 | String | 充电开始时间 |
+| endTime | 充电结束时间 | String | 充电结束时间 |
+| chargeTime | 充电时长 | Integer | 充电时长(秒) |
+| totalCharge | 实际充电度数 | BigDecimal | 单位:kW/h |
+| status | 订单状态 | Integer | 0-待启动,1-充电中,2-结算中,3-已完成,5-未成功充电 |
+| preAmt | 预充值金额 | BigDecimal | 预充值金额 |
+| phoneNum | 手机号 | String | 用户手机号 |
+| plateNum | 车牌号 | String | 车牌号 |
+| stopType | 停止类型 | Integer | 1-主动停止,2-充满停止,3-余额不足停止,4-电桩按钮停止 |
+| stopReason | 停止原因 | String | 停止原因描述 |
+| createTime | 创建时间 | String | 订单创建时间 |
+
+### 11.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "total": 100,
+  "pageNo": 1,
+  "pageSize": 10,
+  "pages": 10,
+  "records": [
+    {
+      "id": 100001,
+      "userId": 10001,
+      "chargeOrderNo": "CD2024031600001",
+      "equipmentId": "EQ001",
+      "connectorId": "CONN001",
+      "stationName": "XX充电站",
+      "connectorName": "101号充电口",
+      "startTime": "2024-03-16 10:00:00",
+      "endTime": "2024-03-16 11:30:00",
+      "chargeTime": 5400,
+      "totalCharge": 30.50,
+      "status": 3,
+      "preAmt": 100.00,
+      "phoneNum": "13800138000",
+      "plateNum": "京A12345",
+      "stopType": 2,
+      "stopReason": "充满停止",
+      "createTime": "2024-03-16 10:00:00"
+    }
+  ]
+}
+```
+
+---
+
+## 12. 查询充电订单详情
+
+### 12.1 接口描述
+
+- **接口名称:** query_charge_order_info
+- **接口说明:** 第三方查询充电订单详情(只查询渠道类型订单)
+- **请求格式:** JSON
+- **请求方式:** POST
+- **接口路径:** `/third-party/v1/query_charge_order_info`
+- **认证方式:** 需要在Header中携带 `Authorization: Bearer {accessToken}`
+
+### 12.2 输入参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 | 是否必填 |
+|----------|----------|----------|------|----------|
+| chargeOrderNo | 充电订单号 | String | 充电订单号 | 是 |
+
+### 12.3 请求示例
+
+**data加密前:**
+
+```json
+{
+  "chargeOrderNo": "CD2024031600001"
+}
+```
+
+### 12.4 返回参数(data解密后)
+
+| 参数名称 | 参数定义 | 参数类型 | 描述 |
+|----------|----------|----------|------|
+| result | 查询结果 | Integer | 0-成功,1-失败 |
+| message | 失败原因 | String | 失败原因描述 |
+| id | 订单ID | Long | 订单ID |
+| userId | 用户ID | Long | 用户ID |
+| chargeOrderNo | 充电订单号 | String | 充电订单号 |
+| equipmentId | 充电桩编号 | String | 充电桩编号 |
+| connectorId | 充电设备接口编码 | String | 充电设备接口编码 |
+| stationName | 充电站名称 | String | 充电站名称 |
+| connectorName | 充电设备接口名称 | String | 充电设备接口名称 |
+| startTime | 充电开始时间 | String | 充电开始时间 |
+| endTime | 充电结束时间 | String | 充电结束时间 |
+| chargeTime | 充电时长 | Integer | 充电时长(秒) |
+| totalCharge | 实际充电度数 | BigDecimal | 单位:kW/h |
+| status | 订单状态 | Integer | 0-待启动,1-充电中,2-结算中,3-已完成,5-未成功充电 |
+| preAmt | 预充值金额 | BigDecimal | 预充值金额 |
+| phoneNum | 手机号 | String | 用户手机号 |
+| plateNum | 车牌号 | String | 车牌号 |
+| stopType | 停止类型 | Integer | 1-主动停止,2-充满停止,3-余额不足停止,4-电桩按钮停止 |
+| stopReason | 停止原因 | String | 停止原因描述 |
+| createTime | 创建时间 | String | 订单创建时间 |
+
+### 12.5 返回示例
+
+**data解密后:**
+
+```json
+{
+  "result": 0,
+  "message": "",
+  "id": 100001,
+  "userId": 10001,
+  "chargeOrderNo": "CD2024031600001",
+  "equipmentId": "EQ001",
+  "connectorId": "CONN001",
+  "stationName": "XX充电站",
+  "connectorName": "101号充电口",
+  "startTime": "2024-03-16 10:00:00",
+  "endTime": "2024-03-16 11:30:00",
+  "chargeTime": 5400,
+  "totalCharge": 30.50,
+  "status": 3,
+  "preAmt": 100.00,
+  "phoneNum": "13800138000",
+  "plateNum": "京A12345",
+  "stopType": 2,
+  "stopReason": "充满停止",
+  "createTime": "2024-03-16 10:00:00"
+}
+```
+
+---
+
+## 附录:配置模板
+
+对接需要配置以下参数,由平台给第三方平台提供这些参数信息:
+
+| 参数名称 | 参数定义 | 参数说明 | 是否必填 | 备注                              |
+|----------|----------|----------|----------|---------------------------------|
+| operatorId | 运营商ID | 平台分配给对应渠道方的唯一代码(长度9位,由数字及大小写字符组成) | 是 | 用于标识运营商身份                       |
+| apiBaseUrl | 接口地址 | 平台提供的API接口基础地址 | 是 | 例如: https://cd.admin.zswlgz.com |
+| operatorSecret | 运营商密钥 | 用于获取Token的密钥,由对方平台分配 | 是 | 用于申请认证                          |
+| sigSecret | 签名密钥 | 用于生成和验证消息签名的密钥 | 是 | 用于HMAC-MD5签名算法                  |
+| dataSecret | 数据密钥 | 用于AES加密和解密数据的密钥 | 是 | 16位密钥,用于data字段加解密               |
+| dataSecretIV | 数据加密向量 | AES加密算法使用的初始化向量(IV) | 是 | 16字节向量,用于CBC模式加密                |
+
+**配置示例:**
+
+```json
+{
+  "operatorId": "123456789",
+  "apiBaseUrl": "https://cd.admin.zswlgz.com",
+  "operatorSecret": "your_operator_secret_key",
+  "sigSecret": "your_sign_secret_key",
+  "dataSecret": "your_data_encryption_key",
+  "dataSecretIV": "your_data_iv_vector"
+}
+```
+
+> ⚠️ **重要提示:**
+> - 以上所有参数由平台提供给第三方平台
+> - 密钥信息需要严格保密,不得泄露
+> - 配置参数需要双方确认无误后再进行接口对接

+ 5 - 0
src/main/java/com/zsElectric/boot/business/model/entity/ChargeOrderInfo.java

@@ -2,6 +2,7 @@ package com.zsElectric.boot.business.model.entity;
 
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.Version;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.zsElectric.boot.common.base.BaseEntity;
 import lombok.Getter;
 import lombok.Setter;
@@ -46,6 +47,10 @@ public class ChargeOrderInfo extends BaseEntity {
      * 第三方充电订单号
      */
     private String startChargeSeq;
+    /**
+     * 运营商ID
+     */
+    private String operatorId;
     /**
      * 充电设备接口编码
      */

+ 4 - 0
src/main/java/com/zsElectric/boot/business/model/form/applet/AppInvokeChargeForm.java

@@ -1,5 +1,6 @@
 package com.zsElectric.boot.business.model.form.applet;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
 import lombok.Getter;
@@ -20,6 +21,9 @@ public class AppInvokeChargeForm implements Serializable {
     @Schema(description = "订单类型 0-平台 1-企业 2-渠道方")
     private Integer orderType;
 
+    @JsonProperty("运营商ID")
+    private String operatorId;
+
     @Schema(description = "充电桩编号")
     @NotBlank(message = "充电桩编号不能为空")
     private String equipmentId;

+ 1 - 1
src/main/java/com/zsElectric/boot/business/model/vo/ChargeOrderInfoVO.java

@@ -28,7 +28,7 @@ public class ChargeOrderInfoVO implements Serializable {
     private Long id;
     @Schema(description = "用户ID")
     private Long userId;
-    @Schema(description = "订单类型 1 个人订单 2 集团订单")
+    @Schema(description = "订单类型 0-平台 1-企业订单 2-渠道订单")
     private Integer orderType;
     @Schema(description = "充电桩编号")
     private String equipmentId;

+ 7 - 0
src/main/java/com/zsElectric/boot/business/service/impl/ChargeOrderInfoServiceImpl.java

@@ -243,6 +243,11 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
             BigDecimal preAmt = appletHomeService.calculateAvailableChargingAmount(formData.getConnectorId());
             chargeOrderInfo.setPreAmt(preAmt);
 
+            //渠道方订单设置运营商ID
+            if (Objects.equals(formData.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)){
+                chargeOrderInfo.setOperatorId(formData.getOperatorId());
+            }
+
             //判断用户是否绑定企业
             if (ObjectUtil.isNotEmpty(userInfo.getFirmId())) {
                 FirmInfo firmInfo = firmInfoMapper.selectById(userInfo.getFirmId());
@@ -329,6 +334,8 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
         }
         //预支付金额
         chargeOrderInfo.setPreAmt(formData.getChannelPreAmt());
+        //渠道方订单设置运营商ID
+        chargeOrderInfo.setOperatorId(formData.getOperatorId());
 
         //启动充电
         StartChargingRequestDTO requestDTO = new StartChargingRequestDTO();

+ 4 - 5
src/main/java/com/zsElectric/boot/charging/controller/LinkDataController.java

@@ -1,7 +1,5 @@
 package com.zsElectric.boot.charging.controller;
 
-import com.fasterxml.jackson.databind.MapperFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.zsElectric.boot.charging.service.ChargingReceptionService;
 import com.zsElectric.boot.common.annotation.ApiRateLimit;
@@ -81,12 +79,13 @@ public class LinkDataController {
             }
 
             //判断运营商ID与密钥是否正确
-            if (!queryTokenRequestParms.getOperatorID().equals(ConnectivityConstants.PLATFORM_OPERATOR_ID) && !queryTokenRequestParms.getOperatorSecret().equals(ConnectivityConstants.PLATFORM_OPERATOR_SECRET)) {
-                responseParmsEntity .setRet(4004);
+            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.setData("");
                 responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
                         ConnectivityConstants.SIG_SECRET));
+                return responseParmsEntity;
             }
 
             //redis获取token,不存在则创建

+ 3 - 3
src/main/java/com/zsElectric/boot/common/util/electric/queryToken/ThirdPartyJwtAuthFilter.java

@@ -61,7 +61,7 @@ public class ThirdPartyJwtAuthFilter extends OncePerRequestFilter {
                 log.error("Token缺失,请求URI: {}", requestUri);
                 // Token缺失,通过AuthenticationEntryPoint返回统一错误格式
                 authenticationEntryPoint.commence(request, response, 
-                    new AuthenticationServiceException("Missing or invalid Bearer token"));
+                    new AuthenticationServiceException("Missing or invalid Bearer token(缺少Authorization头)"));
                 return; // 重要:直接返回,不再执行过滤链后续操作
             }
             
@@ -84,13 +84,13 @@ public class ThirdPartyJwtAuthFilter extends OncePerRequestFilter {
                     log.error("Token验证失败,Token: {}...", token.substring(0, Math.min(20, token.length())));
                     // Token无效
                     authenticationEntryPoint.commence(request, response, 
-                        new AuthenticationServiceException("Invalid token"));
+                        new AuthenticationServiceException("Invalid token(token无效)"));
                     return;
                 }
             } catch (Exception e) { // 捕获JWT解析等特定异常
                 log.error("Token验证异常: {}", e.getMessage(), e);
                 authenticationEntryPoint.commence(request, response, 
-                    new AuthenticationServiceException("Token validation failed: " + e.getMessage()));
+                    new AuthenticationServiceException("Token validation failed(token验证失败): " + e.getMessage()));
                 return;
             }
         } else {

+ 6 - 2
src/main/java/com/zsElectric/boot/system/mapper/DataBoardMapper.java

@@ -148,9 +148,9 @@ public interface DataBoardMapper {
     BigDecimal selectTodayFirstOrderAmount(@Param("todayStart") String todayStart, @Param("todayEnd") String todayEnd);
 
     /**
-     * 获取累计首单金额
+     * 获取累计首单立减金额
      *
-     * @return 累计首单金额
+     * @return 累计首单立减金额
      */
     BigDecimal selectTotalFirstOrderAmount();
 
@@ -168,4 +168,8 @@ public interface DataBoardMapper {
      */
     List<ChurnUserExportDTO> selectChurnUserList(@Param("earlyStart") String earlyStart, @Param("earlyEnd") String earlyEnd,
                                                  @Param("recentStart") String recentStart, @Param("recentEnd") String recentEnd);
+
+    BigDecimal selectTotalDiscountPurchaseAmount();
+
+    BigDecimal selectTodayDiscountPurchaseAmount(@Param("todayStart") String todayStart, @Param("todayEnd") String todayEnd);
 }

+ 2 - 2
src/main/java/com/zsElectric/boot/system/model/vo/DataBoardRealTimeVO.java

@@ -21,8 +21,8 @@ public class DataBoardRealTimeVO {
     @Schema(description = "总充电金额")
     private BigDecimal totalChargeAmount;
 
-    @Schema(description = "总抵扣券金额")
-    private BigDecimal totalDiscountAmount;
+    @Schema(description = "总抵扣券购买金额")
+    private BigDecimal totalDiscountPurchaseAmount;
 
     @Schema(description = "总服务费金额")
     private BigDecimal totalServiceFeeAmount;

+ 1 - 1
src/main/java/com/zsElectric/boot/system/model/vo/DataBoardTodayVO.java

@@ -22,7 +22,7 @@ public class DataBoardTodayVO {
     private BigDecimal todayChargeAmount;
 
     @Schema(description = "今日抵扣券金额")
-    private BigDecimal todayDiscountAmount;
+    private BigDecimal todayDiscountPurchaseAmount;
 
     @Schema(description = "今日服务费金额")
     private BigDecimal todayServiceFeeAmount;

+ 12 - 3
src/main/java/com/zsElectric/boot/system/service/impl/DataBoardServiceImpl.java

@@ -44,11 +44,16 @@ public class DataBoardServiceImpl implements DataBoardService {
         if (realTimeVO == null) {
             realTimeVO = new DataBoardRealTimeVO();
         }
+
+        // 获取抵扣券购买金额
+        BigDecimal totalDiscountPurchaseAmount = dataBoardMapper.selectTotalDiscountPurchaseAmount();
+        realTimeVO.setTotalDiscountPurchaseAmount(totalDiscountPurchaseAmount != null ? totalDiscountPurchaseAmount : BigDecimal.ZERO);
+
         // 获取累计退款金额
         BigDecimal totalRefundAmount = dataBoardMapper.selectTotalRefundAmount();
         realTimeVO.setTotalRefundAmount(totalRefundAmount != null ? totalRefundAmount : BigDecimal.ZERO);
 
-        // 获取累计首单金额
+        // 获取累计首单立减金额
         BigDecimal totalFirstOrderAmount = dataBoardMapper.selectTotalFirstOrderAmount();
         realTimeVO.setTotalFirstOrderAmount(totalFirstOrderAmount != null ? totalFirstOrderAmount : BigDecimal.ZERO);
 
@@ -68,6 +73,10 @@ public class DataBoardServiceImpl implements DataBoardService {
             todayVO = new DataBoardTodayVO();
         }
 
+        // 获取抵扣券购买金额
+        BigDecimal todayDiscountPurchaseAmount = dataBoardMapper.selectTodayDiscountPurchaseAmount(todayStart, todayEnd);
+        todayVO.setTodayDiscountPurchaseAmount(todayDiscountPurchaseAmount != null ? todayDiscountPurchaseAmount : BigDecimal.ZERO);
+
         // 获取今日退款金额
         BigDecimal todayRefundAmount = dataBoardMapper.selectTodayRefundAmount(todayStart, todayEnd);
         todayVO.setTodayRefundAmount(todayRefundAmount != null ? todayRefundAmount : BigDecimal.ZERO);
@@ -440,7 +449,7 @@ public class DataBoardServiceImpl implements DataBoardService {
     private void ensureRealTimeDataNotNull(DataBoardRealTimeVO vo) {
         if (vo.getTotalChargePower() == null) vo.setTotalChargePower(BigDecimal.ZERO);
         if (vo.getTotalChargeAmount() == null) vo.setTotalChargeAmount(BigDecimal.ZERO);
-        if (vo.getTotalDiscountAmount() == null) vo.setTotalDiscountAmount(BigDecimal.ZERO);
+        if (vo.getTotalDiscountPurchaseAmount() == null) vo.setTotalDiscountPurchaseAmount(BigDecimal.ZERO);
         if (vo.getTotalServiceFeeAmount() == null) vo.setTotalServiceFeeAmount(BigDecimal.ZERO);
         if (vo.getTotalRefundAmount() == null) vo.setTotalRefundAmount(BigDecimal.ZERO);
         if (vo.getTotalActualPayAmount() == null) vo.setTotalActualPayAmount(BigDecimal.ZERO);
@@ -456,7 +465,7 @@ public class DataBoardServiceImpl implements DataBoardService {
     private void ensureTodayDataNotNull(DataBoardTodayVO vo) {
         if (vo.getTodayChargePower() == null) vo.setTodayChargePower(BigDecimal.ZERO);
         if (vo.getTodayChargeAmount() == null) vo.setTodayChargeAmount(BigDecimal.ZERO);
-        if (vo.getTodayDiscountAmount() == null) vo.setTodayDiscountAmount(BigDecimal.ZERO);
+        if (vo.getTodayDiscountPurchaseAmount() == null) vo.setTodayDiscountPurchaseAmount(BigDecimal.ZERO);
         if (vo.getTodayServiceFeeAmount() == null) vo.setTodayServiceFeeAmount(BigDecimal.ZERO);
         if (vo.getTodayRefundAmount() == null) vo.setTodayRefundAmount(BigDecimal.ZERO);
         if (vo.getTodayActualPayAmount() == null) vo.setTodayActualPayAmount(BigDecimal.ZERO);

+ 36 - 0
src/main/java/com/zsElectric/boot/thirdParty/controller/ThirdPartyController.java

@@ -109,4 +109,40 @@ public class ThirdPartyController {
             @Parameter(description = "访问令牌,格式: Bearer {accessToken}") @RequestHeader(value = "Authorization", required = false) String authorization) {
         return thirdPartyTokenService.queryChargeDeviceDetail(request, authorization);
     }
+
+    @Operation(summary = "启动充电", description = "第三方启动充电,需要在Header中携带Authorization")
+    @PostMapping("/invoke_charge")
+    @Log(value = "第三方启动充电", module = LogModuleEnum.OTHER, params = true, result = true)
+    public ThirdPartyResponse invokeCharge(
+            @RequestBody ThirdPartyRequest request,
+            @Parameter(description = "访问令牌,格式: Bearer {accessToken}") @RequestHeader(value = "Authorization", required = false) String authorization) {
+        return thirdPartyTokenService.invokeCharge(request, authorization);
+    }
+
+    @Operation(summary = "停止充电", description = "第三方停止充电,需要在Header中携带Authorization")
+    @PostMapping("/stop_charge")
+    @Log(value = "第三方停止充电", module = LogModuleEnum.OTHER, params = true, result = true)
+    public ThirdPartyResponse stopCharge(
+            @RequestBody ThirdPartyRequest request,
+            @Parameter(description = "访问令牌,格式: Bearer {accessToken}") @RequestHeader(value = "Authorization", required = false) String authorization) {
+        return thirdPartyTokenService.stopCharge(request, authorization);
+    }
+
+    @Operation(summary = "查询充电订单列表", description = "第三方查询充电订单列表,需要在Header中携带Authorization")
+    @PostMapping("/query_charge_order_list")
+    @Log(value = "第三方查询充电订单列表", module = LogModuleEnum.OTHER, params = true, result = true)
+    public ThirdPartyResponse queryChargeOrderList(
+            @RequestBody ThirdPartyRequest request,
+            @Parameter(description = "访问令牌,格式: Bearer {accessToken}") @RequestHeader(value = "Authorization", required = false) String authorization) {
+        return thirdPartyTokenService.queryChargeOrderList(request, authorization);
+    }
+
+    @Operation(summary = "查询充电订单详情", description = "第三方查询充电订单详情,需要在Header中携带Authorization")
+    @PostMapping("/query_charge_order_info")
+    @Log(value = "第三方查询充电订单详情", module = LogModuleEnum.OTHER, params = true, result = true)
+    public ThirdPartyResponse queryChargeOrderInfo(
+            @RequestBody ThirdPartyRequest request,
+            @Parameter(description = "访问令牌,格式: Bearer {accessToken}") @RequestHeader(value = "Authorization", required = false) String authorization) {
+        return thirdPartyTokenService.queryChargeOrderInfo(request, authorization);
+    }
 }

+ 25 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderDetailRequestData.java

@@ -0,0 +1,25 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 充电订单详情查询请求参数 (data解密后的内容)
+ *
+ * @author wzq
+ */
+@Data
+public class ChargeOrderDetailRequestData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 充电订单号(必传)
+     */
+    @JsonProperty("chargeOrderNo")
+    private String chargeOrderNo;
+}

+ 159 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderDetailResponseData.java

@@ -0,0 +1,159 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 充电订单详情查询响应数据
+ *
+ * @author wzq
+ */
+@Data
+public class ChargeOrderDetailResponseData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 查询结果 0-成功 1-失败
+     */
+    @JsonProperty("result")
+    private Integer result;
+
+    /**
+     * 失败原因
+     */
+    @JsonProperty("message")
+    private String message;
+
+    /**
+     * 订单ID
+     */
+    @JsonProperty("id")
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    @JsonProperty("userId")
+    private Long userId;
+
+    /**
+     * 充电订单号
+     */
+    @JsonProperty("chargeOrderNo")
+    private String chargeOrderNo;
+
+    /**
+     * 充电桩编号
+     */
+    @JsonProperty("equipmentId")
+    private String equipmentId;
+
+    /**
+     * 充电设备接口编码
+     */
+    @JsonProperty("connectorId")
+    private String connectorId;
+
+    /**
+     * 充电站名称
+     */
+    @JsonProperty("stationName")
+    private String stationName;
+
+    /**
+     * 充电设备接口名称
+     */
+    @JsonProperty("connectorName")
+    private String connectorName;
+
+    /**
+     * 充电开始时间
+     */
+    @JsonProperty("startTime")
+    private String startTime;
+
+    /**
+     * 充电结束时间
+     */
+    @JsonProperty("endTime")
+    private String endTime;
+
+    /**
+     * 充电时长(秒)
+     */
+    @JsonProperty("chargeTime")
+    private Integer chargeTime;
+
+    /**
+     * 实际充电度数(单位:kw/h)
+     */
+    @JsonProperty("totalCharge")
+    private BigDecimal totalCharge;
+
+    /**
+     * 订单状态 0待启动 1充电中 2结算中 3已完成 5未成功充电
+     */
+    @JsonProperty("status")
+    private Integer status;
+
+    /**
+     * 预充值金额
+     */
+    @JsonProperty("preAmt")
+    private BigDecimal preAmt;
+
+    /**
+     * 手机号
+     */
+    @JsonProperty("phoneNum")
+    private String phoneNum;
+
+    /**
+     * 车牌号
+     */
+    @JsonProperty("plateNum")
+    private String plateNum;
+
+    /**
+     * 停止类型 1主动停止 2充满停止 3余额不足停止 4电桩按钮停止
+     */
+    @JsonProperty("stopType")
+    private Integer stopType;
+
+    /**
+     * 停止原因
+     */
+    @JsonProperty("stopReason")
+    private String stopReason;
+
+    /**
+     * 创建时间
+     */
+    @JsonProperty("createTime")
+    private String createTime;
+
+    /**
+     * 构建成功响应
+     */
+    public static ChargeOrderDetailResponseData success() {
+        ChargeOrderDetailResponseData data = new ChargeOrderDetailResponseData();
+        data.setResult(0);
+        return data;
+    }
+
+    /**
+     * 构建失败响应
+     */
+    public static ChargeOrderDetailResponseData fail(String message) {
+        ChargeOrderDetailResponseData data = new ChargeOrderDetailResponseData();
+        data.setResult(1);
+        data.setMessage(message);
+        return data;
+    }
+}

+ 49 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderListRequestData.java

@@ -0,0 +1,49 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 充电订单列表分页查询请求参数 (data解密后的内容)
+ *
+ * @author wzq
+ */
+@Data
+public class ChargeOrderListRequestData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商ID
+     */
+    @JsonProperty("operatorId")
+    private String operatorId;
+
+    /**
+     * 用户ID(可选)
+     */
+    @JsonProperty("userId")
+    private Long userId;
+
+    /**
+     * 充电订单号(可选)
+     */
+    @JsonProperty("chargeOrderNo")
+    private String chargeOrderNo;
+
+    /**
+     * 页码(默认1)
+     */
+    @JsonProperty("pageNo")
+    private Long pageNo = 1L;
+
+    /**
+     * 每页记录数(默认10)
+     */
+    @JsonProperty("pageSize")
+    private Long pageSize = 10L;
+}

+ 182 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/ChargeOrderListResponseData.java

@@ -0,0 +1,182 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 充电订单列表分页查询响应数据
+ *
+ * @author wzq
+ */
+@Data
+public class ChargeOrderListResponseData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 总记录数
+     */
+    @JsonProperty("total")
+    private Long total;
+
+    /**
+     * 当前页码
+     */
+    @JsonProperty("pageNo")
+    private Long pageNo;
+
+    /**
+     * 每页记录数
+     */
+    @JsonProperty("pageSize")
+    private Long pageSize;
+
+    /**
+     * 总页数
+     */
+    @JsonProperty("pages")
+    private Long pages;
+
+    /**
+     * 数据列表
+     */
+    @JsonProperty("records")
+    private List<ChargeOrderItem> records;
+
+    /**
+     * 充电订单明细项
+     */
+    @Data
+    public static class ChargeOrderItem implements Serializable {
+
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 订单ID
+         */
+        @JsonProperty("id")
+        private Long id;
+
+        /**
+         * 用户ID
+         */
+        @JsonProperty("userId")
+        private Long userId;
+
+        /**
+         * 充电订单号
+         */
+        @JsonProperty("chargeOrderNo")
+        private String chargeOrderNo;
+
+        /**
+         * 充电桩编号
+         */
+        @JsonProperty("equipmentId")
+        private String equipmentId;
+
+        /**
+         * 充电设备接口编码
+         */
+        @JsonProperty("connectorId")
+        private String connectorId;
+
+        /**
+         * 充电站名称
+         */
+        @JsonProperty("stationName")
+        private String stationName;
+
+        /**
+         * 充电设备接口名称
+         */
+        @JsonProperty("connectorName")
+        private String connectorName;
+
+        /**
+         * 充电开始时间
+         */
+        @JsonProperty("startTime")
+        private String startTime;
+
+        /**
+         * 充电结束时间
+         */
+        @JsonProperty("endTime")
+        private String endTime;
+
+        /**
+         * 充电时长(秒)
+         */
+        @JsonProperty("chargeTime")
+        private Integer chargeTime;
+
+        /**
+         * 实际充电度数(单位:kw/h)
+         */
+        @JsonProperty("totalCharge")
+        private BigDecimal totalCharge;
+
+        /**
+         * 订单状态 0待启动 1充电中 2结算中 3已完成 5未成功充电
+         */
+        @JsonProperty("status")
+        private Integer status;
+
+        /**
+         * 预充值金额
+         */
+        @JsonProperty("preAmt")
+        private BigDecimal preAmt;
+
+        /**
+         * 手机号
+         */
+        @JsonProperty("phoneNum")
+        private String phoneNum;
+
+        /**
+         * 车牌号
+         */
+        @JsonProperty("plateNum")
+        private String plateNum;
+
+        /**
+         * 停止类型 1主动停止 2充满停止 3余额不足停止 4电桩按钮停止
+         */
+        @JsonProperty("stopType")
+        private Integer stopType;
+
+        /**
+         * 停止原因
+         */
+        @JsonProperty("stopReason")
+        private String stopReason;
+
+        /**
+         * 创建时间
+         */
+        @JsonProperty("createTime")
+        private String createTime;
+    }
+
+    /**
+     * 构建响应数据
+     */
+    public static ChargeOrderListResponseData of(Long total, Long pageNo, Long pageSize, Long pages, List<ChargeOrderItem> records) {
+        ChargeOrderListResponseData data = new ChargeOrderListResponseData();
+        data.setTotal(total);
+        data.setPageNo(pageNo);
+        data.setPageSize(pageSize);
+        data.setPages(pages);
+        data.setRecords(records);
+        return data;
+    }
+}

+ 6 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/ChargeStationListRequestData.java

@@ -36,4 +36,10 @@ public class ChargeStationListRequestData extends BasePageQuery implements Seria
      */
     @Schema(description = "纬度")
     private BigDecimal latitude;
+
+    @Schema(description = "页码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long pageNum = 1L;
+
+    @Schema(description = "每页记录数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Long pageSize = 10L;
 }

+ 3 - 3
src/main/java/com/zsElectric/boot/thirdParty/model/ChargeStationListResponseData.java

@@ -25,12 +25,12 @@ public class ChargeStationListResponseData implements Serializable {
     /**
      * 当前页码
      */
-    private Integer pageNum;
+    private Long pageNum;
 
     /**
      * 每页大小
      */
-    private Integer pageSize;
+    private Long pageSize;
 
     /**
      * 总页数
@@ -84,7 +84,7 @@ public class ChargeStationListResponseData implements Serializable {
     /**
      * 构建成功响应
      */
-    public static ChargeStationListResponseData of(Long total, Integer pageNum, Integer pageSize, Long pages, List<StationItem> list) {
+    public static ChargeStationListResponseData of(Long total, Long pageNum, Long pageSize, Long pages, List<StationItem> list) {
         ChargeStationListResponseData data = new ChargeStationListResponseData();
         data.setTotal(total);
         data.setPageNum(pageNum);

+ 56 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/InvokeChargeRequestData.java

@@ -0,0 +1,56 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 启动充电请求参数 (data加密前的内容)
+ *
+ * @author wzq
+ */
+@Data
+public class InvokeChargeRequestData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 充电桩编号
+     */
+    @JsonProperty("equipmentId")
+    private String equipmentId;
+
+    /**
+     * 第三方充电站ID
+     */
+    @JsonProperty("stationId")
+    private String stationId;
+
+    /**
+     * 充电设备接口编码
+     */
+    @JsonProperty("connectorId")
+    private String connectorId;
+
+    /**
+     * 渠道方订单编号
+     */
+    @JsonProperty("channelOrderNo")
+    private String channelOrderNo;
+
+    /**
+     * 渠道方用户手机号
+     */
+    @JsonProperty("channelUserPhone")
+    private String channelUserPhone;
+
+    /**
+     * 渠道方预支付金额
+     */
+    @JsonProperty("channelPreAmt")
+    private BigDecimal channelPreAmt;
+}

+ 72 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/InvokeChargeResponseData.java

@@ -0,0 +1,72 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 启动充电响应参数 (data加密前的内容)
+ *
+ * @author wzq
+ */
+@Data
+public class InvokeChargeResponseData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 启动结果 (0-成功 1-失败)
+     */
+    @JsonProperty("result")
+    private Integer result;
+
+    /**
+     * 结果消息
+     */
+    @JsonProperty("message")
+    private String message;
+
+    /**
+     * 充电订单ID
+     */
+    @JsonProperty("chargeOrderId")
+    private Long chargeOrderId;
+
+    /**
+     * 充电订单编号
+     */
+    @JsonProperty("chargeOrderNo")
+    private String chargeOrderNo;
+
+    /**
+     * 充电状态 (0-待启动 1-充电中 2-结算中 3-已完成 5-未成功充电)
+     */
+    @JsonProperty("status")
+    private Integer status;
+
+    /**
+     * 创建成功响应数据
+     */
+    public static InvokeChargeResponseData success(Long chargeOrderId, String chargeOrderNo, Integer status) {
+        InvokeChargeResponseData data = new InvokeChargeResponseData();
+        data.setResult(0);
+        data.setMessage("启动充电成功");
+        data.setChargeOrderId(chargeOrderId);
+        data.setChargeOrderNo(chargeOrderNo);
+        data.setStatus(status);
+        return data;
+    }
+
+    /**
+     * 创建失败响应数据
+     */
+    public static InvokeChargeResponseData fail(String message) {
+        InvokeChargeResponseData data = new InvokeChargeResponseData();
+        data.setResult(1);
+        data.setMessage(message);
+        return data;
+    }
+}

+ 2 - 2
src/main/java/com/zsElectric/boot/thirdParty/model/RechargeLevelPageRequestData.java

@@ -21,11 +21,11 @@ public class RechargeLevelPageRequestData implements Serializable {
      * 页码(默认1)
      */
     @JsonProperty("pageNum")
-    private Integer pageNum = 1;
+    private Long pageNum = 1L;
 
     /**
      * 每页记录数(默认10)
      */
     @JsonProperty("pageSize")
-    private Integer pageSize = 10;
+    private Long pageSize = 10L;
 }

+ 3 - 3
src/main/java/com/zsElectric/boot/thirdParty/model/RechargeLevelPageResponseData.java

@@ -29,13 +29,13 @@ public class RechargeLevelPageResponseData implements Serializable {
      * 当前页码
      */
     @JsonProperty("pageNum")
-    private Integer pageNum;
+    private Long pageNum;
 
     /**
      * 每页记录数
      */
     @JsonProperty("pageSize")
-    private Integer pageSize;
+    private Long pageSize;
 
     /**
      * 总页数
@@ -92,7 +92,7 @@ public class RechargeLevelPageResponseData implements Serializable {
     /**
      * 创建成功响应数据
      */
-    public static RechargeLevelPageResponseData of(Long total, Integer pageNum, Integer pageSize, Long pages, List<RechargeLevelItem> records) {
+    public static RechargeLevelPageResponseData of(Long total, Long pageNum, Long pageSize, Long pages, List<RechargeLevelItem> records) {
         RechargeLevelPageResponseData data = new RechargeLevelPageResponseData();
         data.setTotal(total);
         data.setPageNum(pageNum);

+ 25 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/StopChargeRequestData.java

@@ -0,0 +1,25 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 停止充电请求参数 (data加密前的内容)
+ *
+ * @author wzq
+ */
+@Data
+public class StopChargeRequestData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 充电订单编号
+     */
+    @JsonProperty("chargeOrderNo")
+    private String chargeOrderNo;
+}

+ 72 - 0
src/main/java/com/zsElectric/boot/thirdParty/model/StopChargeResponseData.java

@@ -0,0 +1,72 @@
+package com.zsElectric.boot.thirdParty.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 停止充电响应参数 (data加密前的内容)
+ *
+ * @author wzq
+ */
+@Data
+public class StopChargeResponseData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 停止结果 (0-成功 1-失败)
+     */
+    @JsonProperty("result")
+    private Integer result;
+
+    /**
+     * 结果消息
+     */
+    @JsonProperty("message")
+    private String message;
+
+    /**
+     * 充电订单ID
+     */
+    @JsonProperty("chargeOrderId")
+    private Long chargeOrderId;
+
+    /**
+     * 充电订单编号
+     */
+    @JsonProperty("chargeOrderNo")
+    private String chargeOrderNo;
+
+    /**
+     * 充电状态 (0-待启动 1-充电中 2-结算中 3-已完成 5-未成功充电)
+     */
+    @JsonProperty("status")
+    private Integer status;
+
+    /**
+     * 创建成功响应数据
+     */
+    public static StopChargeResponseData success(Long chargeOrderId, String chargeOrderNo, Integer status) {
+        StopChargeResponseData data = new StopChargeResponseData();
+        data.setResult(0);
+        data.setMessage("停止充电成功");
+        data.setChargeOrderId(chargeOrderId);
+        data.setChargeOrderNo(chargeOrderNo);
+        data.setStatus(status);
+        return data;
+    }
+
+    /**
+     * 创建失败响应数据
+     */
+    public static StopChargeResponseData fail(String message) {
+        StopChargeResponseData data = new StopChargeResponseData();
+        data.setResult(1);
+        data.setMessage(message);
+        return data;
+    }
+}

+ 36 - 0
src/main/java/com/zsElectric/boot/thirdParty/service/ThirdPartyTokenService.java

@@ -57,4 +57,40 @@ public interface ThirdPartyTokenService {
      * @return 响应结果
      */
     ThirdPartyResponse queryChargeDeviceDetail(ThirdPartyRequest request, String authorization);
+
+    /**
+     * 启动充电
+     *
+     * @param request       第三方请求参数
+     * @param authorization 请求头中的Authorization
+     * @return 响应结果
+     */
+    ThirdPartyResponse invokeCharge(ThirdPartyRequest request, String authorization);
+
+    /**
+     * 停止充电
+     *
+     * @param request       第三方请求参数
+     * @param authorization 请求头中的Authorization
+     * @return 响应结果
+     */
+    ThirdPartyResponse stopCharge(ThirdPartyRequest request, String authorization);
+
+    /**
+     * 查询充电订单列表(只查询渠道类型订单)
+     *
+     * @param request       第三方请求参数
+     * @param authorization 请求头中的Authorization
+     * @return 响应结果
+     */
+    ThirdPartyResponse queryChargeOrderList(ThirdPartyRequest request, String authorization);
+
+    /**
+     * 查询充电订单详情(只查询渠道类型订单)
+     *
+     * @param request       第三方请求参数
+     * @param authorization 请求头中的Authorization
+     * @return 响应结果
+     */
+    ThirdPartyResponse queryChargeOrderInfo(ThirdPartyRequest request, String authorization);
 }

+ 401 - 4
src/main/java/com/zsElectric/boot/thirdParty/service/impl/ThirdPartyTokenServiceImpl.java

@@ -29,6 +29,10 @@ import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentInfo;
 import com.zsElectric.boot.charging.entity.ThirdPartyStationInfo;
 import com.zsElectric.boot.business.service.UserInfoService;
 import com.zsElectric.boot.business.service.UserAccountService;
+import com.zsElectric.boot.business.service.ChargeOrderInfoService;
+import com.zsElectric.boot.business.model.form.applet.AppInvokeChargeForm;
+import com.zsElectric.boot.business.model.form.applet.AppStopChargeForm;
+import com.zsElectric.boot.business.model.vo.applet.AppChargeVO;
 import com.zsElectric.boot.common.constant.SystemConstants;
 import com.zsElectric.boot.common.util.AESCryptoUtils;
 import com.zsElectric.boot.common.util.HmacMD5Util;
@@ -70,6 +74,7 @@ public class ThirdPartyTokenServiceImpl implements ThirdPartyTokenService {
     private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper;
     private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper;
     private final ThirdPartyConnectorInfoMapper thirdPartyConnectorInfoMapper;
+    private final ChargeOrderInfoService chargeOrderInfoService;
     private final ObjectMapper objectMapper = new ObjectMapper();
 
     /**
@@ -207,8 +212,8 @@ public class ThirdPartyTokenServiceImpl implements ThirdPartyTokenService {
 
             RechargeLevelPageResponseData responseData = RechargeLevelPageResponseData.of(
                     pageResult.getTotal(),
-                    (int) pageResult.getCurrent(),
-                    (int) pageResult.getSize(),
+                    pageResult.getCurrent(),
+                    pageResult.getSize(),
                     pageResult.getPages(),
                     records
             );
@@ -704,8 +709,8 @@ public class ThirdPartyTokenServiceImpl implements ThirdPartyTokenService {
 
             ChargeStationListResponseData responseData = ChargeStationListResponseData.of(
                     pageResult.getTotal(),
-                    (int) pageResult.getCurrent(),
-                    (int) pageResult.getSize(),
+                    pageResult.getCurrent(),
+                    pageResult.getSize(),
                     pageResult.getPages(),
                     records
             );
@@ -1069,4 +1074,396 @@ public class ThirdPartyTokenServiceImpl implements ThirdPartyTokenService {
         return detail;
     }
 
+    @Override
+    public ThirdPartyResponse invokeCharge(ThirdPartyRequest request, String authorization) {
+        try {
+            // 1. 参数校验
+            if (!validateRequestParams(request)) {
+                return buildErrorResponse(4003, "参数不合法,缺少必需的参数", null);
+            }
+
+            // 2. 通过operatorId查询第三方配置信息
+            ThirdPartyInfo thirdPartyInfo = getThirdPartyInfoByOperatorId(request.getOperatorId());
+            if (thirdPartyInfo == null) {
+                log.warn("运营商不存在, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4004, "运营商不存在", null);
+            }
+
+            // 3. 验证签名
+            String signContent = request.getOperatorId() + request.getData() + request.getTimeStamp() + request.getSeq();
+            if (!HmacMD5Util.verify(signContent, thirdPartyInfo.getSigSecret(), request.getSig())) {
+                log.warn("签名验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4001, "签名错误", thirdPartyInfo);
+            }
+
+            // 4. 从Header中解析并验证Token
+            String accessToken = parseAccessToken(authorization);
+            if (StrUtil.isBlank(accessToken)) {
+                log.warn("Token为空或格式错误, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+            if (!validateAccessToken(accessToken, request.getOperatorId())) {
+                log.warn("Token验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+
+            // 5. 解密并解析业务参数
+            String decryptedData = AESCryptoUtils.decrypt(
+                    request.getData(),
+                    thirdPartyInfo.getDataSecret(),
+                    thirdPartyInfo.getDataSecretIV()
+            );
+            log.info("启动充电-解密后的请求数据: {}", decryptedData);
+
+            InvokeChargeRequestData invokeRequest = objectMapper.readValue(decryptedData, InvokeChargeRequestData.class);
+            if (invokeRequest == null || StrUtil.isBlank(invokeRequest.getEquipmentId()) 
+                    || StrUtil.isBlank(invokeRequest.getStationId()) || StrUtil.isBlank(invokeRequest.getConnectorId())) {
+                return buildErrorResponse(4003, "请求的业务参数不合法", thirdPartyInfo);
+            }
+
+            // 6. 构建启动充电表单
+            AppInvokeChargeForm formData = new AppInvokeChargeForm();
+            formData.setOrderType(2); // 渠道方订单
+            formData.setEquipmentId(invokeRequest.getEquipmentId());
+            formData.setStationId(invokeRequest.getStationId());
+            formData.setConnectorId(invokeRequest.getConnectorId());
+            formData.setChannelOrderNo(invokeRequest.getChannelOrderNo());
+            formData.setChannelUserPhone(invokeRequest.getChannelUserPhone());
+            formData.setChannelPreAmt(invokeRequest.getChannelPreAmt());
+
+            // 7. 调用启动充电服务
+            AppChargeVO chargeVO = chargeOrderInfoService.invokeCharge(formData);
+
+            // 8. 构建响应数据
+            InvokeChargeResponseData responseData = InvokeChargeResponseData.success(
+                    chargeVO.getChargeOrderId(),
+                    chargeVO.getChargeOrderNo(),
+                    chargeVO.getStatus()
+            );
+            log.info("启动充电成功, chargeOrderNo: {}", chargeVO.getChargeOrderNo());
+
+            return buildSuccessResponse(responseData, thirdPartyInfo);
+
+        } catch (Exception e) {
+            log.error("处理invoke_charge请求异常", e);
+            return buildErrorResponse(500, "系统错误: " + e.getMessage(), null);
+        }
+    }
+
+    @Override
+    public ThirdPartyResponse stopCharge(ThirdPartyRequest request, String authorization) {
+        try {
+            // 1. 参数校验
+            if (!validateRequestParams(request)) {
+                return buildErrorResponse(4003, "参数不合法,缺少必需的参数", null);
+            }
+
+            // 2. 通过operatorId查询第三方配置信息
+            ThirdPartyInfo thirdPartyInfo = getThirdPartyInfoByOperatorId(request.getOperatorId());
+            if (thirdPartyInfo == null) {
+                log.warn("运营商不存在, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4004, "运营商不存在", null);
+            }
+
+            // 3. 验证签名
+            String signContent = request.getOperatorId() + request.getData() + request.getTimeStamp() + request.getSeq();
+            if (!HmacMD5Util.verify(signContent, thirdPartyInfo.getSigSecret(), request.getSig())) {
+                log.warn("签名验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4001, "签名错误", thirdPartyInfo);
+            }
+
+            // 4. 从Header中解析并验证Token
+            String accessToken = parseAccessToken(authorization);
+            if (StrUtil.isBlank(accessToken)) {
+                log.warn("Token为空或格式错误, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+            if (!validateAccessToken(accessToken, request.getOperatorId())) {
+                log.warn("Token验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+
+            // 5. 解密并解析业务参数
+            String decryptedData = AESCryptoUtils.decrypt(
+                    request.getData(),
+                    thirdPartyInfo.getDataSecret(),
+                    thirdPartyInfo.getDataSecretIV()
+            );
+            log.info("停止充电-解密后的请求数据: {}", decryptedData);
+
+            StopChargeRequestData stopRequest = objectMapper.readValue(decryptedData, StopChargeRequestData.class);
+            if (stopRequest == null || StrUtil.isBlank(stopRequest.getChargeOrderNo())) {
+                return buildErrorResponse(4003, "请求的业务参数不合法", thirdPartyInfo);
+            }
+
+            // 6. 构建停止充电表单
+            AppStopChargeForm formData = new AppStopChargeForm();
+            formData.setChargeOrderNo(stopRequest.getChargeOrderNo());
+
+            // 7. 调用停止充电服务
+            AppChargeVO chargeVO = chargeOrderInfoService.stopCharge(formData);
+
+            // 8. 构建响应数据
+            StopChargeResponseData responseData = StopChargeResponseData.success(
+                    chargeVO.getChargeOrderId(),
+                    chargeVO.getChargeOrderNo(),
+                    chargeVO.getStatus()
+            );
+            log.info("停止充电成功, chargeOrderNo: {}", chargeVO.getChargeOrderNo());
+
+            return buildSuccessResponse(responseData, thirdPartyInfo);
+
+        } catch (Exception e) {
+            log.error("处理stop_charge请求异常", e);
+            return buildErrorResponse(500, "系统错误: " + e.getMessage(), null);
+        }
+    }
+
+    @Override
+    public ThirdPartyResponse queryChargeOrderList(ThirdPartyRequest request, String authorization) {
+        try {
+            // 1. 参数校验
+            if (!validateRequestParams(request)) {
+                return buildErrorResponse(4003, "参数不合法,缺少必需的参数", null);
+            }
+
+            // 2. 通过operatorId查询第三方配置信息
+            ThirdPartyInfo thirdPartyInfo = getThirdPartyInfoByOperatorId(request.getOperatorId());
+            if (thirdPartyInfo == null) {
+                log.warn("运营商不存在, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4004, "运营商不存在", null);
+            }
+
+            // 3. 验证签名
+            String signContent = request.getOperatorId() + request.getData() + request.getTimeStamp() + request.getSeq();
+            if (!HmacMD5Util.verify(signContent, thirdPartyInfo.getSigSecret(), request.getSig())) {
+                log.warn("签名验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4001, "签名错误", thirdPartyInfo);
+            }
+
+            // 4. 从Header中解析并验证Token
+            String accessToken = parseAccessToken(authorization);
+            if (StrUtil.isBlank(accessToken)) {
+                log.warn("Token为空或格式错误, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+            if (!validateAccessToken(accessToken, request.getOperatorId())) {
+                log.warn("Token验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+
+            // 5. 解密并解析业务参数
+            String decryptedData = AESCryptoUtils.decrypt(
+                    request.getData(),
+                    thirdPartyInfo.getDataSecret(),
+                    thirdPartyInfo.getDataSecretIV()
+            );
+            log.info("查询充电订单列表-解密后的请求数据: {}", decryptedData);
+
+            ChargeOrderListRequestData listRequest = objectMapper.readValue(decryptedData, ChargeOrderListRequestData.class);
+            if (listRequest == null) {
+                listRequest = new ChargeOrderListRequestData();
+            }
+
+            // 6. 构建查询条件(只查询渠道类型订单 order_type=2)
+            LambdaQueryWrapper<com.zsElectric.boot.business.model.entity.ChargeOrderInfo> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getOrderType, SystemConstants.CHARGE_ORDER_TYPE_CHANNEL);
+            queryWrapper.eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getOperatorId, listRequest.getOperatorId());
+            queryWrapper.eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getIsDeleted, 0);
+
+            // 可选条件:userId
+            if (listRequest.getUserId() != null) {
+                queryWrapper.eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getUserId, listRequest.getUserId());
+            }
+            // 可选条件:chargeOrderNo
+            if (StrUtil.isNotBlank(listRequest.getChargeOrderNo())) {
+                queryWrapper.like(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getChargeOrderNo, listRequest.getChargeOrderNo());
+            }
+
+            // 按创建时间倒序
+            queryWrapper.orderByDesc(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getCreateTime);
+
+            // 7. 分页查询
+            long pageNo = listRequest.getPageNo() != null && listRequest.getPageNo() > 0 ? listRequest.getPageNo() : 1L;
+            long pageSize = listRequest.getPageSize() != null && listRequest.getPageSize() > 0 ? listRequest.getPageSize() : 10L;
+            Page<com.zsElectric.boot.business.model.entity.ChargeOrderInfo> page = new Page<>(pageNo, pageSize);
+
+            IPage<com.zsElectric.boot.business.model.entity.ChargeOrderInfo> pageResult = chargeOrderInfoService.page(page, queryWrapper);
+
+            // 8. 转换为响应数据
+            List<ChargeOrderListResponseData.ChargeOrderItem> records = pageResult.getRecords().stream()
+                    .map(this::convertToChargeOrderItem)
+                    .collect(Collectors.toList());
+
+            ChargeOrderListResponseData responseData = ChargeOrderListResponseData.of(
+                    pageResult.getTotal(),
+                    pageResult.getCurrent(),
+                    pageResult.getSize(),
+                    pageResult.getPages(),
+                    records
+            );
+
+            log.info("查询充电订单列表成功, operatorId: {}, total: {}", request.getOperatorId(), pageResult.getTotal());
+            return buildSuccessResponse(responseData, thirdPartyInfo);
+
+        } catch (Exception e) {
+            log.error("处理query_charge_order_list请求异常", e);
+            return buildErrorResponse(500, "系统错误: " + e.getMessage(), null);
+        }
+    }
+
+    /**
+     * 将ChargeOrderInfo转换为ChargeOrderItem
+     */
+    private ChargeOrderListResponseData.ChargeOrderItem convertToChargeOrderItem(com.zsElectric.boot.business.model.entity.ChargeOrderInfo order) {
+        ChargeOrderListResponseData.ChargeOrderItem item = new ChargeOrderListResponseData.ChargeOrderItem();
+        item.setId(order.getId());
+        item.setUserId(order.getUserId());
+        item.setChargeOrderNo(order.getChargeOrderNo());
+        item.setEquipmentId(order.getEquipmentId());
+        item.setConnectorId(order.getConnectorId());
+        item.setStartTime(order.getStartTime());
+        item.setEndTime(order.getEndTime());
+        item.setChargeTime(order.getChargeTime());
+        item.setTotalCharge(order.getTotalCharge());
+        item.setStatus(order.getStatus());
+        item.setPreAmt(order.getPreAmt());
+        item.setPhoneNum(order.getPhoneNum());
+        item.setPlateNum(order.getPlateNum());
+        item.setStopType(order.getStopType());
+        item.setStopReason(order.getStopReason());
+        item.setCreateTime(order.getCreateTime() != null ? order.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null);
+
+        // 查询充电站名称
+        if (order.getThirdPartyStationId() != null) {
+            ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectById(order.getThirdPartyStationId());
+            if (stationInfo != null) {
+                item.setStationName(stationInfo.getStationName());
+            }
+        }
+
+        // 查询充电设备接口名称
+        if (StrUtil.isNotBlank(order.getConnectorId())) {
+            ThirdPartyConnectorInfo connectorInfo = thirdPartyConnectorInfoMapper.selectOne(
+                    Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
+                            .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId())
+                            .last("LIMIT 1")
+            );
+            if (connectorInfo != null) {
+                item.setConnectorName(connectorInfo.getConnectorName());
+            }
+        }
+
+        return item;
+    }
+
+    @Override
+    public ThirdPartyResponse queryChargeOrderInfo(ThirdPartyRequest request, String authorization) {
+        try {
+            // 1. 参数校验
+            if (!validateRequestParams(request)) {
+                return buildErrorResponse(4003, "参数不合法,缺少必需的参数", null);
+            }
+
+            // 2. 通过operatorId查询第三方配置信息
+            ThirdPartyInfo thirdPartyInfo = getThirdPartyInfoByOperatorId(request.getOperatorId());
+            if (thirdPartyInfo == null) {
+                log.warn("运营商不存在, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4004, "运营商不存在", null);
+            }
+
+            // 3. 验证签名
+            String signContent = request.getOperatorId() + request.getData() + request.getTimeStamp() + request.getSeq();
+            if (!HmacMD5Util.verify(signContent, thirdPartyInfo.getSigSecret(), request.getSig())) {
+                log.warn("签名验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4001, "签名错误", thirdPartyInfo);
+            }
+
+            // 4. 从Header中解析并验证Token
+            String accessToken = parseAccessToken(authorization);
+            if (StrUtil.isBlank(accessToken)) {
+                log.warn("Token为空或格式错误, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+            if (!validateAccessToken(accessToken, request.getOperatorId())) {
+                log.warn("Token验证失败, operatorId: {}", request.getOperatorId());
+                return buildErrorResponse(4002, "token错误", thirdPartyInfo);
+            }
+
+            // 5. 解密并解析业务参数
+            String decryptedData = AESCryptoUtils.decrypt(
+                    request.getData(),
+                    thirdPartyInfo.getDataSecret(),
+                    thirdPartyInfo.getDataSecretIV()
+            );
+            log.info("查询充电订单详情-解密后的请求数据: {}", decryptedData);
+
+            ChargeOrderDetailRequestData detailRequest = objectMapper.readValue(decryptedData, ChargeOrderDetailRequestData.class);
+            if (detailRequest == null || StrUtil.isBlank(detailRequest.getChargeOrderNo())) {
+                return buildErrorResponse(4003, "请求的业务参数不合法,chargeOrderNo不能为空", thirdPartyInfo);
+            }
+
+            // 6. 查询订单(只查询渠道类型订单 order_type=2)
+            com.zsElectric.boot.business.model.entity.ChargeOrderInfo order = chargeOrderInfoService.getOne(
+                    Wrappers.<com.zsElectric.boot.business.model.entity.ChargeOrderInfo>lambdaQuery()
+                            .eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getChargeOrderNo, detailRequest.getChargeOrderNo())
+                            .eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getOrderType, SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)
+                            .eq(com.zsElectric.boot.business.model.entity.ChargeOrderInfo::getIsDeleted, 0)
+                            .last("LIMIT 1")
+            );
+
+            if (order == null) {
+                log.warn("充电订单不存在或不是渠道订单, chargeOrderNo: {}", detailRequest.getChargeOrderNo());
+                ChargeOrderDetailResponseData failData = ChargeOrderDetailResponseData.fail("充电订单不存在或不是渠道订单");
+                return buildSuccessResponse(failData, thirdPartyInfo);
+            }
+
+            // 7. 构建响应数据
+            ChargeOrderDetailResponseData responseData = ChargeOrderDetailResponseData.success();
+            responseData.setId(order.getId());
+            responseData.setUserId(order.getUserId());
+            responseData.setChargeOrderNo(order.getChargeOrderNo());
+            responseData.setEquipmentId(order.getEquipmentId());
+            responseData.setConnectorId(order.getConnectorId());
+            responseData.setStartTime(order.getStartTime());
+            responseData.setEndTime(order.getEndTime());
+            responseData.setChargeTime(order.getChargeTime());
+            responseData.setTotalCharge(order.getTotalCharge());
+            responseData.setStatus(order.getStatus());
+            responseData.setPreAmt(order.getPreAmt());
+            responseData.setPhoneNum(order.getPhoneNum());
+            responseData.setPlateNum(order.getPlateNum());
+            responseData.setStopType(order.getStopType());
+            responseData.setStopReason(order.getStopReason());
+            responseData.setCreateTime(order.getCreateTime() != null ? order.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null);
+
+            // 查询充电站名称
+            if (order.getThirdPartyStationId() != null) {
+                ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectById(order.getThirdPartyStationId());
+                if (stationInfo != null) {
+                    responseData.setStationName(stationInfo.getStationName());
+                }
+            }
+
+            // 查询充电设备接口名称
+            if (StrUtil.isNotBlank(order.getConnectorId())) {
+                ThirdPartyConnectorInfo connectorInfo = thirdPartyConnectorInfoMapper.selectOne(
+                        Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
+                                .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId())
+                                .last("LIMIT 1")
+                );
+                if (connectorInfo != null) {
+                    responseData.setConnectorName(connectorInfo.getConnectorName());
+                }
+            }
+
+            log.info("查询充电订单详情成功, chargeOrderNo: {}", detailRequest.getChargeOrderNo());
+            return buildSuccessResponse(responseData, thirdPartyInfo);
+
+        } catch (Exception e) {
+            log.error("处理query_charge_order_info请求异常", e);
+            return buildErrorResponse(500, "系统错误: " + e.getMessage(), null);
+        }
+    }
+
 }

+ 36 - 31
src/main/resources/mapper/system/DataBoardMapper.xml

@@ -7,7 +7,6 @@
         SELECT
             COALESCE(SUM(total_charge), 0) AS totalChargePower,
             COALESCE(SUM(third_party_total_cost), 0) AS totalChargeAmount,
-            COALESCE(SUM(discount_money), 0) AS totalDiscountAmount,
             COALESCE(SUM(real_service_cost), 0) AS totalServiceFeeAmount,
             COALESCE(SUM(real_cost), 0) AS totalActualPayAmount,
             COALESCE(SUM(coupon_price), 0) AS totalCouponAmount,
@@ -22,7 +21,6 @@
         SELECT
             COALESCE(SUM(total_charge), 0) AS todayChargePower,
             COALESCE(SUM(third_party_total_cost), 0) AS todayChargeAmount,
-            COALESCE(SUM(discount_money), 0) AS todayDiscountAmount,
             COALESCE(SUM(real_service_cost), 0) AS todayServiceFeeAmount,
             COALESCE(SUM(real_cost), 0) AS todayActualPayAmount,
             COALESCE(SUM(coupon_price), 0) AS todayCouponAmount,
@@ -41,11 +39,21 @@
         AND success_time BETWEEN #{todayStart} AND #{todayEnd}
     </select>
 
-    <!-- 获取累计退款金额 -->
+    <!-- 获取累计退款金额(兼容1.0和2.0版本数据) -->
     <select id="selectTotalRefundAmount" resultType="java.math.BigDecimal">
-        SELECT COALESCE(SUM(amount), 0)
-        FROM c_user_refunds_order_info
-        WHERE status = 'SUCCESS' AND is_deleted = 0
+        SELECT
+            -- 1.0版本退款(2026-01-05之前)
+            (SELECT COALESCE(SUM(refund_money), 0)
+             FROM c_user_order_info
+             WHERE is_deleted = 0 AND refund_money > 0
+             AND refund_time &lt; '2026-01-05 00:00:00')
+            +
+            -- 2.0版本退款(2025-12-31及之后)
+            (SELECT COALESCE(SUM(amount), 0)
+             FROM c_user_refunds_order_info
+             WHERE status = 'SUCCESS' AND is_deleted = 0
+             AND success_time >= '2025-12-31 00:00:00')
+        AS totalRefundAmount
     </select>
 
     <!-- 按小时统计充电度数 -->
@@ -158,35 +166,21 @@
         WHERE recent.user_id IS NULL
     </select>
 
-    <!-- 获取今日首单金额 -->
+    <!-- 获取今日首单立减金额 -->
     <select id="selectTodayFirstOrderAmount" resultType="java.math.BigDecimal">
-        SELECT COALESCE(SUM(first_order.real_cost), 0)
-        FROM (
-            SELECT user_id, MIN(create_time) AS first_time
-            FROM c_charge_order_info
-            WHERE status = 3 AND is_deleted = 0
-            GROUP BY user_id
-        ) first_order_time
-        INNER JOIN c_charge_order_info first_order
-            ON first_order.user_id = first_order_time.user_id
-            AND first_order.create_time = first_order_time.first_time
-        WHERE first_order.status = 3 AND first_order.is_deleted = 0
-        AND DATE(first_order.create_time) BETWEEN #{todayStart} AND #{todayEnd}
+        SELECT
+            COALESCE(SUM(discount_money), 0) AS todayFirstOrderAmount
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND create_time BETWEEN #{todayStart} AND #{todayEnd}
     </select>
 
-    <!-- 获取累计首单金额 -->
+    <!-- 获取累计首单立减金额 -->
     <select id="selectTotalFirstOrderAmount" resultType="java.math.BigDecimal">
-        SELECT COALESCE(SUM(first_order.real_cost), 0)
-        FROM (
-            SELECT user_id, MIN(create_time) AS first_time
-            FROM c_charge_order_info
-            WHERE status = 3 AND is_deleted = 0
-            GROUP BY user_id
-        ) first_order_time
-        INNER JOIN c_charge_order_info first_order
-            ON first_order.user_id = first_order_time.user_id
-            AND first_order.create_time = first_order_time.first_time
-        WHERE first_order.status = 3 AND first_order.is_deleted = 0
+        SELECT
+            COALESCE(SUM(discount_money), 0) AS totalFirstOrderAmount
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
     </select>
 
     <!-- 获取流失用户列表(包含用户信息和最后一次充电信息) -->
@@ -228,5 +222,16 @@
         AND u.is_deleted = 0
         ORDER BY last_order.create_time DESC
     </select>
+    <select id="selectTotalDiscountPurchaseAmount" resultType="java.math.BigDecimal">
+        SELECT
+            COALESCE(SUM(pay_money),0) as totalDiscountPurchaseAmount
+        FROM `c_user_order_info` WHERE order_status > 1 AND order_status <![CDATA[  !=  ]]> 3
+    </select>
+    <select id="selectTodayDiscountPurchaseAmount" resultType="java.math.BigDecimal">
+        SELECT
+            COALESCE(SUM(pay_money),0) as totalDiscountPurchaseAmount
+        FROM `c_user_order_info` WHERE order_status > 1 AND order_status <![CDATA[  !=  ]]> 3
+        AND pay_time BETWEEN #{todayStart} AND #{todayEnd}
+    </select>
 
 </mapper>

+ 0 - 2
src/test/java/com/zsElectric/boot/thirdParty/service/ThirdPartyTokenServiceTest.java

@@ -247,7 +247,6 @@ class ThirdPartyTokenServiceTest {
 
         ThirdPartyRequest request = buildRequestWithTokenData(tokenData);
         when(thirdPartyInfoMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(mockThirdPartyInfo);
-        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
 
         ThirdPartyResponse response = thirdPartyTokenService.queryToken(request);
 
@@ -273,7 +272,6 @@ class ThirdPartyTokenServiceTest {
 
         ThirdPartyRequest request = buildRequestWithTokenData(tokenData);
         when(thirdPartyInfoMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(mockThirdPartyInfo);
-        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
 
         ThirdPartyResponse response = thirdPartyTokenService.queryToken(request);