mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-07-22 23:22:32 +08:00
Compare commits
10 Commits
06aab00627
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cace7f692f | ||
|
|
c9749a1816 | ||
|
|
df761d1785 | ||
|
|
6a0e4d6ea7 | ||
|
|
b0c138f704 | ||
|
|
2fa0180a26 | ||
|
|
bf258b6924 | ||
|
|
46df35e4f1 | ||
|
|
17b19ab0da | ||
|
|
aa7a9a10d9 |
241
docs/qcyun停车优惠新站点配置指南.md
Normal file
241
docs/qcyun停车优惠新站点配置指南.md
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# qcyun 停车优惠新站点配置指南
|
||||||
|
|
||||||
|
本文用于配置一个新站点的 qcyun 停车优惠功能。配置完成后,订单在满足充电门槛、时间段、车牌等条件时,会自动向 qcyun 下发停车优惠。
|
||||||
|
|
||||||
|
## 配置目标
|
||||||
|
|
||||||
|
一个站点要能发 qcyun 停车优惠,需要同时具备三类数据:
|
||||||
|
|
||||||
|
1. 停车场平台配置:qcyun 分配的车场、机构、密钥等参数。
|
||||||
|
2. 站点绑定关系:站点绑定到哪一条停车场平台配置。
|
||||||
|
3. 停车优惠规则:该站点满足什么条件后发什么优惠。
|
||||||
|
|
||||||
|
## 一、停车场平台配置
|
||||||
|
|
||||||
|
数据表:`thirdparty_parking_config`
|
||||||
|
|
||||||
|
该表记录停车场平台的接口参数。qcyun 多站点配置的核心是:不同站点可以绑定不同的 `thirdparty_parking_config.id`。
|
||||||
|
|
||||||
|
| 字段 | 是否必填 | qcyun 配置说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `id` | 新增后记录 | 停车场配置主键,后续站点通过 `pile_station_info.parking_id` 绑定它。 |
|
||||||
|
| `parking_name` | 建议填 | 停车场/商户名称;qcyun 发券时如果没有站点名称,会作为 `storeName` 备用值。 |
|
||||||
|
| `platform_type` | 是 | qcyun 固定填 `3`。 |
|
||||||
|
| `secret_key` | 是 | qcyun 分配的签名密钥。 |
|
||||||
|
| `org_id` | 是 | qcyun 分配的机构 ID。 |
|
||||||
|
| `park_id` | 是 | qcyun 分配的车场 ID。 |
|
||||||
|
| `api_url` | 建议填 | qcyun 接口地址;为空时使用配置文件 `parking.qcyuns.url`。 |
|
||||||
|
| `app_id` | qcyun 可不填 | 其他停车平台可能使用,qcyun 发券逻辑当前不使用。 |
|
||||||
|
| `parking_merchant_id` | qcyun 可不填 | 其他停车平台可能使用,qcyun 发券逻辑当前不使用。 |
|
||||||
|
| `coupon_id` | qcyun 可不填 | 其他停车平台可能使用,qcyun 发券逻辑当前不使用。 |
|
||||||
|
| `del_flag` | 是 | 正常数据填 `0`。 |
|
||||||
|
|
||||||
|
生产 qcyun 默认接口地址:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://g.qcyuns.com/park-data-api/data/centre/api/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
测试 qcyun 默认接口地址:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://test-gateway.qcyuns.com/park-data-api/data/centre/api/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into thirdparty_parking_config (
|
||||||
|
parking_name,
|
||||||
|
platform_type,
|
||||||
|
secret_key,
|
||||||
|
org_id,
|
||||||
|
park_id,
|
||||||
|
api_url,
|
||||||
|
create_time,
|
||||||
|
create_by,
|
||||||
|
del_flag
|
||||||
|
) values (
|
||||||
|
'示例站点停车场',
|
||||||
|
'3',
|
||||||
|
'QCYUN_SECRET_KEY',
|
||||||
|
'QCYUN_ORG_ID',
|
||||||
|
'QCYUN_PARK_ID',
|
||||||
|
'https://g.qcyuns.com/park-data-api/data/centre/api/v1',
|
||||||
|
now(),
|
||||||
|
'admin',
|
||||||
|
'0'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
系统现有代码提供停车平台列表查询和站点绑定接口。如果没有停车场配置维护页面,可以通过数据库脚本先写入 `thirdparty_parking_config`,再把生成的 `id` 绑定到站点。
|
||||||
|
|
||||||
|
## 二、站点绑定停车场配置
|
||||||
|
|
||||||
|
数据表:`pile_station_info`
|
||||||
|
|
||||||
|
| 字段 | 是否必填 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `id` | 是 | 站点 ID。 |
|
||||||
|
| `parking_id` | 是 | 绑定 `thirdparty_parking_config.id`。 |
|
||||||
|
|
||||||
|
后台接口:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /pile/station/bindParkingPlatform
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"stationId": "站点ID",
|
||||||
|
"parkingId": "thirdparty_parking_config.id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
等价 SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
update pile_station_info
|
||||||
|
set parking_id = '停车场配置ID'
|
||||||
|
where id = '站点ID';
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:一个站点当前只绑定一个 `parking_id`。如果多个站点使用不同 qcyun 车场,需要分别新增多条 `thirdparty_parking_config`,再让各站点绑定各自的配置。
|
||||||
|
|
||||||
|
## 三、站点停车优惠规则
|
||||||
|
|
||||||
|
数据表:`charge_parking_discount`
|
||||||
|
|
||||||
|
该表按站点保存“达到什么充电条件后,发放什么停车优惠”。
|
||||||
|
|
||||||
|
| 字段 | 是否必填 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `station_id` | 是 | 站点 ID。 |
|
||||||
|
| `parking_platform_id` | 是 | qcyun 固定填 `3`。 |
|
||||||
|
| `condition_type` | 是 | 发券门槛类型:`1` 固定电量;`2` 固定时长。 |
|
||||||
|
| `condition_value` | 是 | 门槛值。`condition_type=1` 时表示充电度数;`condition_type=2` 时表示充电时长,单位分钟。 |
|
||||||
|
| `discount_type` | 是 | 优惠类型:`1` 减时间;`2` 减金额。 |
|
||||||
|
| `discount_value` | 是 | 优惠值。`discount_type=1` 时单位分钟;`discount_type=2` 时单位元,系统发给 qcyun 前会转成分。 |
|
||||||
|
| `start_time` | 是 | 优惠生效开始时间,例如 `00:00:00`。 |
|
||||||
|
| `end_time` | 是 | 优惠生效结束时间,例如 `23:59:00`。支持跨天判断。 |
|
||||||
|
| `del_flag` | 是 | 正常数据填 `0`。 |
|
||||||
|
|
||||||
|
示例:站点充电满 10 度后,减免 120 分钟停车费。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into charge_parking_discount (
|
||||||
|
station_id,
|
||||||
|
parking_platform_id,
|
||||||
|
condition_type,
|
||||||
|
condition_value,
|
||||||
|
discount_type,
|
||||||
|
discount_value,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
create_time,
|
||||||
|
create_by,
|
||||||
|
del_flag
|
||||||
|
) values (
|
||||||
|
'站点ID',
|
||||||
|
3,
|
||||||
|
'1',
|
||||||
|
'10',
|
||||||
|
'1',
|
||||||
|
'120',
|
||||||
|
'00:00:00',
|
||||||
|
'23:59:00',
|
||||||
|
now(),
|
||||||
|
'admin',
|
||||||
|
'0'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:站点充电满 30 分钟后,减免 10 元停车费。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into charge_parking_discount (
|
||||||
|
station_id,
|
||||||
|
parking_platform_id,
|
||||||
|
condition_type,
|
||||||
|
condition_value,
|
||||||
|
discount_type,
|
||||||
|
discount_value,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
create_time,
|
||||||
|
create_by,
|
||||||
|
del_flag
|
||||||
|
) values (
|
||||||
|
'站点ID',
|
||||||
|
3,
|
||||||
|
'2',
|
||||||
|
'30',
|
||||||
|
'2',
|
||||||
|
'10',
|
||||||
|
'00:00:00',
|
||||||
|
'23:59:00',
|
||||||
|
now(),
|
||||||
|
'admin',
|
||||||
|
'0'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
建议同一个 `station_id` 只保留一条 `del_flag = '0'` 的有效优惠规则。后台查询按 `station_id` 取规则,多条有效规则可能导致查询异常或规则不确定。
|
||||||
|
|
||||||
|
## 四、发券生效条件
|
||||||
|
|
||||||
|
订单自动下发 qcyun 停车优惠时,会依次检查:
|
||||||
|
|
||||||
|
1. 订单有车牌号:`order_basic_info.plate_number` 不能为空。
|
||||||
|
2. 站点存在停车优惠规则:`charge_parking_discount.station_id = 订单 station_id` 且 `del_flag = '0'`。
|
||||||
|
3. 订单创建时间在 `start_time` 到 `end_time` 的优惠时间段内。
|
||||||
|
4. 订单充电度数或充电时长达到 `condition_type/condition_value` 配置的门槛。
|
||||||
|
5. 该订单没有发过券:`car_coupon_record.order_code` 不存在有效记录。
|
||||||
|
6. 优惠规则的平台是 qcyun:`parking_platform_id = 3`。
|
||||||
|
7. 站点绑定的停车场配置存在,并且 `platform_type = '3'`。
|
||||||
|
8. qcyun 配置里的 `park_id`、`org_id`、`secret_key` 不为空。
|
||||||
|
|
||||||
|
满足以上条件后,系统会先向 qcyun 查询车辆停车信息,再调用 qcyun 商家减免接口。发券成功后,会写入 `car_coupon_record`。
|
||||||
|
|
||||||
|
## 五、配置完成后的检查清单
|
||||||
|
|
||||||
|
配置新站点后,建议逐项确认:
|
||||||
|
|
||||||
|
| 检查项 | 期望结果 |
|
||||||
|
| --- | --- |
|
||||||
|
| `thirdparty_parking_config` 有 qcyun 配置 | `platform_type = '3'`,`del_flag = '0'`。 |
|
||||||
|
| qcyun 鉴权参数完整 | `park_id`、`org_id`、`secret_key` 均不为空。 |
|
||||||
|
| 站点已绑定停车场配置 | `pile_station_info.parking_id = thirdparty_parking_config.id`。 |
|
||||||
|
| 站点有优惠规则 | `charge_parking_discount.station_id = 站点ID`,`parking_platform_id = 3`,`del_flag = '0'`。 |
|
||||||
|
| 优惠时间正确 | `start_time/end_time` 覆盖订单创建时间。 |
|
||||||
|
| 门槛值正确 | 订单充电度数或时长能达到配置门槛。 |
|
||||||
|
| 订单有车牌 | 否则不会发券。 |
|
||||||
|
| 没有重复发券记录 | 同一订单已有 `car_coupon_record` 时不会重复发券。 |
|
||||||
|
|
||||||
|
## 六、常见问题
|
||||||
|
|
||||||
|
### 1. 已配置但没有发券
|
||||||
|
|
||||||
|
优先检查订单是否有车牌、是否满足充电门槛、订单创建时间是否在优惠时间段内,以及 `charge_parking_discount.parking_platform_id` 是否为 `3`。
|
||||||
|
|
||||||
|
### 2. 日志提示“站点未绑定停车场配置”
|
||||||
|
|
||||||
|
说明 `pile_station_info.parking_id` 为空,或绑定的 `thirdparty_parking_config` 不存在/已删除。
|
||||||
|
|
||||||
|
### 3. 日志提示“站点绑定的停车平台类型不是 qcyun”
|
||||||
|
|
||||||
|
说明 `thirdparty_parking_config.platform_type` 不是 `3`。qcyun 配置必须填 `3`。
|
||||||
|
|
||||||
|
### 4. 日志提示“停车场配置缺少 parkId/orgId/secretKey”
|
||||||
|
|
||||||
|
补齐 `thirdparty_parking_config.park_id`、`org_id`、`secret_key`。
|
||||||
|
|
||||||
|
### 5. 一个 qcyun 停车场能否绑定多个站点
|
||||||
|
|
||||||
|
可以。多个站点的 `pile_station_info.parking_id` 可以绑定同一条 `thirdparty_parking_config.id`。
|
||||||
|
|
||||||
|
### 6. 一个站点能否绑定多个 qcyun 停车场
|
||||||
|
|
||||||
|
当前不支持。一个站点只有一个 `parking_id`,只能绑定一条停车场配置。
|
||||||
3
docs/sql/pile_msg_record_retention_index.sql
Normal file
3
docs/sql/pile_msg_record_retention_index.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `pile_msg_record`
|
||||||
|
ADD KEY `idx_pile_frame_id` (`pile_sn`, `frame_type`, `id`),
|
||||||
|
ADD KEY `idx_pile_frame_transaction` (`pile_sn`, `frame_type`, `transaction_code`);
|
||||||
150
docs/科拓停车充电桩抵扣配置指南.md
Normal file
150
docs/科拓停车充电桩抵扣配置指南.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# 科拓停车充电桩抵扣配置指南
|
||||||
|
|
||||||
|
本文用于配置科拓停车系统 2.5.4 充电桩抵扣接口。配置完成后,订单满足站点停车优惠条件时,系统会向科拓下发 `syncChargePilePay` 抵扣请求。
|
||||||
|
|
||||||
|
## 一、停车场平台配置
|
||||||
|
|
||||||
|
数据表:`thirdparty_parking_config`
|
||||||
|
|
||||||
|
| 字段 | 是否必填 | 科拓配置说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `parking_name` | 建议填 | 停车场/商户名称,便于后台识别。 |
|
||||||
|
| `platform_type` | 是 | 科拓固定填 `4`。 |
|
||||||
|
| `app_id` | 是 | 科拓分配的 `appId`。 |
|
||||||
|
| `secret_key` | 是 | 科拓分配的 `appSercert`,用于生成请求体 `key`。 |
|
||||||
|
| `park_id` | 是 | 科拓分配的车场 ID。 |
|
||||||
|
| `api_url` | 是 | 科拓接口地址。可填接口基址,如 `https://IP:端口`;也可填完整地址 `https://IP:端口/api/wec/SyncChargePilePay`。 |
|
||||||
|
| `org_id` | 否 | qcyun 使用,科拓不用。 |
|
||||||
|
| `del_flag` | 是 | 正常数据填 `0`。 |
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into thirdparty_parking_config (
|
||||||
|
parking_name,
|
||||||
|
platform_type,
|
||||||
|
app_id,
|
||||||
|
secret_key,
|
||||||
|
park_id,
|
||||||
|
api_url,
|
||||||
|
create_time,
|
||||||
|
create_by,
|
||||||
|
del_flag
|
||||||
|
) values (
|
||||||
|
'示例科拓停车场',
|
||||||
|
'4',
|
||||||
|
'10038',
|
||||||
|
'KETUO_APP_SECRET',
|
||||||
|
'592011253',
|
||||||
|
'https://IP:端口',
|
||||||
|
now(),
|
||||||
|
'admin',
|
||||||
|
'0'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 二、站点绑定停车场配置
|
||||||
|
|
||||||
|
数据表:`pile_station_info`
|
||||||
|
|
||||||
|
```sql
|
||||||
|
update pile_station_info
|
||||||
|
set parking_id = 'thirdparty_parking_config.id'
|
||||||
|
where id = '站点ID';
|
||||||
|
```
|
||||||
|
|
||||||
|
后台接口同样可用:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /pile/station/bindParkingPlatform
|
||||||
|
```
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"stationId": "站点ID",
|
||||||
|
"parkingId": "thirdparty_parking_config.id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、站点停车优惠规则
|
||||||
|
|
||||||
|
数据表:`charge_parking_discount`
|
||||||
|
|
||||||
|
| 字段 | 是否必填 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `station_id` | 是 | 站点 ID。 |
|
||||||
|
| `parking_platform_id` | 是 | 科拓固定填 `4`。 |
|
||||||
|
| `condition_type` | 是 | 发券门槛类型:`1` 固定电量;`2` 固定时长。 |
|
||||||
|
| `condition_value` | 是 | 门槛值。电量单位度,时长单位分钟。 |
|
||||||
|
| `discount_type` | 是 | 优惠类型:`1` 减时间;`2` 减金额。 |
|
||||||
|
| `discount_value` | 是 | 优惠值。减时间单位分钟,系统下发科拓时会转成秒;减金额单位元,系统下发科拓时会转成分。 |
|
||||||
|
| `start_time` | 是 | 优惠生效开始时间。 |
|
||||||
|
| `end_time` | 是 | 优惠生效结束时间。 |
|
||||||
|
| `del_flag` | 是 | 正常数据填 `0`。 |
|
||||||
|
|
||||||
|
示例:充电满 10 度,抵扣 120 分钟停车时长。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into charge_parking_discount (
|
||||||
|
station_id,
|
||||||
|
parking_platform_id,
|
||||||
|
condition_type,
|
||||||
|
condition_value,
|
||||||
|
discount_type,
|
||||||
|
discount_value,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
create_time,
|
||||||
|
create_by,
|
||||||
|
del_flag
|
||||||
|
) values (
|
||||||
|
'站点ID',
|
||||||
|
4,
|
||||||
|
'1',
|
||||||
|
'10',
|
||||||
|
'1',
|
||||||
|
'120',
|
||||||
|
'00:00:00',
|
||||||
|
'23:59:00',
|
||||||
|
now(),
|
||||||
|
'admin',
|
||||||
|
'0'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、下发给科拓的数据来源
|
||||||
|
|
||||||
|
| 科拓字段 | 当前系统取值 |
|
||||||
|
| --- | --- |
|
||||||
|
| `orderNo` | `order_basic_info.order_code` |
|
||||||
|
| `plateNo` | `order_basic_info.plate_number` |
|
||||||
|
| `startTime` | `order_basic_info.charge_start_time`,为空时用订单创建时间 |
|
||||||
|
| `endTime` | `order_basic_info.charge_end_time`,为空时用当前时间 |
|
||||||
|
| `stationId` | `order_basic_info.station_id` |
|
||||||
|
| `stationName` | `pile_station_info.station_name` |
|
||||||
|
| `deviceId` | `order_basic_info.pile_sn` |
|
||||||
|
| `deviceName` | `pile_basic_info.name`,为空时用桩 SN |
|
||||||
|
| `spaceNo` | `pile_connector_info.park_no`,为空时用 `0` |
|
||||||
|
| `power` | 实时数据 `chargingDegree`,保留 2 位小数 |
|
||||||
|
| `totalMoney` | 实时数据 `chargingAmount`,元转分 |
|
||||||
|
| `elecMoney` | 当前填 `0` |
|
||||||
|
| `seviceMoney` | 当前填 `totalMoney` |
|
||||||
|
| `freeType` | 固定填 `0`,按 `freeMoney/freeTime` 减免 |
|
||||||
|
| `freeMoney` | 金额优惠时取优惠金额并元转分;时长优惠时为 `0` |
|
||||||
|
| `freeTime` | 时长优惠时取优惠分钟并转秒;金额优惠时为 `0` |
|
||||||
|
|
||||||
|
## 五、生效条件
|
||||||
|
|
||||||
|
沿用现有充电停车优惠链路:
|
||||||
|
|
||||||
|
1. 订单有车牌号。
|
||||||
|
2. 站点存在 `charge_parking_discount` 有效配置。
|
||||||
|
3. 订单创建时间在优惠时间段内。
|
||||||
|
4. 订单实时充电电量或充电时长满足门槛。
|
||||||
|
5. 同一订单未在 `car_coupon_record` 写入过发券记录。
|
||||||
|
6. 优惠规则 `parking_platform_id = 4`。
|
||||||
|
7. 站点绑定的 `thirdparty_parking_config.platform_type = '4'`。
|
||||||
|
8. 科拓配置 `app_id`、`secret_key`、`park_id`、`api_url` 不为空。
|
||||||
|
|
||||||
@@ -535,6 +535,29 @@ public class PersonPileController extends BaseController {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 羽信主板取消预约充电
|
||||||
|
* http://localhost:8080/uniapp/personalPile/yuxin/cancelReserved
|
||||||
|
*/
|
||||||
|
@PostMapping("/yuxin/cancelReserved")
|
||||||
|
public RestApiResponse<?> cancelYuxinReserved(HttpServletRequest request, @RequestBody YuxinReservationChargingDTO dto) {
|
||||||
|
RestApiResponse<?> response = null;
|
||||||
|
try {
|
||||||
|
String memberId = getMemberIdByAuthorization(request);
|
||||||
|
dto.setMemberId(memberId);
|
||||||
|
yuxinReservationChargingService.cancelReservation(dto);
|
||||||
|
response = new RestApiResponse<>();
|
||||||
|
} catch (BusinessException e) {
|
||||||
|
logger.error("羽信取消预约充电error, params:{}", dto, e);
|
||||||
|
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("羽信取消预约充电error, params:{}", dto, e);
|
||||||
|
response = new RestApiResponse<>(ReturnCodeEnum.CODE_UPDATE_RESERVED_STATUS_ERROR);
|
||||||
|
}
|
||||||
|
logger.info("羽信取消预约充电params:{}, result:{}", dto, response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存蓝牙充电记录
|
* 保存蓝牙充电记录
|
||||||
* http://localhost:8080/uniapp/personalPile/saveBluetoothChargingRecord
|
* http://localhost:8080/uniapp/personalPile/saveBluetoothChargingRecord
|
||||||
|
|||||||
@@ -132,13 +132,13 @@ public class MemberBasicInfoController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 获取会员基础信息详细信息
|
* 获取会员基础信息详细信息
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("@ss.hasPermi('member:memberGroup:list')")
|
@PreAuthorize("@ss.hasPermi('member:info:query')")
|
||||||
@GetMapping(value = "/{memberId}")
|
@GetMapping(value = "/{memberId}")
|
||||||
public AjaxResult getInfo(@PathVariable("memberId") String memberId) {
|
public AjaxResult getInfo(@PathVariable("memberId") String memberId) {
|
||||||
return AjaxResult.success(memberBasicInfoService.queryMemberInfoByMemberId(memberId));
|
return AjaxResult.success(memberBasicInfoService.queryMemberInfoByMemberId(memberId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("@ss.hasPermi('member:memberGroup:list')")
|
@PreAuthorize("@ss.hasPermi('member:info:query')")
|
||||||
@GetMapping(value = "/getMemberPersonPileInfo/{memberId}")
|
@GetMapping(value = "/getMemberPersonPileInfo/{memberId}")
|
||||||
public AjaxResult getMemberPersonPileInfo(@PathVariable("memberId") String memberId) {
|
public AjaxResult getMemberPersonPileInfo(@PathVariable("memberId") String memberId) {
|
||||||
return AjaxResult.success(memberBasicInfoService.getMemberPersonPileInfo(memberId));
|
return AjaxResult.success(memberBasicInfoService.getMemberPersonPileInfo(memberId));
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -422,7 +423,81 @@ public class PileStationInfoController extends BaseController {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存停车平台配置
|
||||||
|
*
|
||||||
|
* @param config 停车平台配置
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/saveParkingConfig")
|
||||||
|
public RestApiResponse<?> saveParkingConfig(@RequestBody ThirdpartyParkingConfig config) {
|
||||||
|
logger.info("保存停车平台配置 params:{}", JSON.toJSONString(config));
|
||||||
|
RestApiResponse<?> response = null;
|
||||||
|
try {
|
||||||
|
if (config == null) {
|
||||||
|
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(config.getParkingName())
|
||||||
|
|| StringUtils.isBlank(config.getPlatformType())
|
||||||
|
|| StringUtils.isBlank(config.getSecretKey())
|
||||||
|
|| StringUtils.isBlank(config.getParkId())
|
||||||
|
|| StringUtils.isBlank(config.getApiUrl())) {
|
||||||
|
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||||
|
}
|
||||||
|
if ("4".equals(config.getPlatformType()) && StringUtils.isBlank(config.getAppId())) {
|
||||||
|
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Date now = new Date();
|
||||||
|
if (config.getId() == null) {
|
||||||
|
config.setCreateTime(now);
|
||||||
|
config.setCreateBy(getUsername());
|
||||||
|
if (StringUtils.isBlank(config.getDelFlag())) {
|
||||||
|
config.setDelFlag("0");
|
||||||
|
}
|
||||||
|
parkingConfigService.insertSelective(config);
|
||||||
|
} else {
|
||||||
|
config.setUpdateTime(now);
|
||||||
|
config.setUpdateBy(getUsername());
|
||||||
|
parkingConfigService.updateByPrimaryKeySelective(config);
|
||||||
|
}
|
||||||
|
response = new RestApiResponse<>(config);
|
||||||
|
} catch (BusinessException e) {
|
||||||
|
logger.error("保存停车平台配置 error,", e);
|
||||||
|
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("保存停车平台配置 error", e);
|
||||||
|
response = new RestApiResponse<>(e);
|
||||||
|
}
|
||||||
|
logger.info("保存停车平台配置 result:{}", response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据站点获取已绑定停车平台配置
|
||||||
|
*
|
||||||
|
* @param stationId 站点ID
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/getParkingConfigByStationId/{stationId}")
|
||||||
|
public RestApiResponse<?> getParkingConfigByStationId(@PathVariable("stationId") String stationId) {
|
||||||
|
RestApiResponse<?> response = null;
|
||||||
|
try {
|
||||||
|
if (StringUtils.isBlank(stationId)) {
|
||||||
|
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||||
|
}
|
||||||
|
ThirdpartyParkingConfig config = parkingConfigService.selectByStationId(stationId);
|
||||||
|
response = new RestApiResponse<>(config);
|
||||||
|
} catch (BusinessException e) {
|
||||||
|
logger.error("根据站点获取停车平台配置 error,", e);
|
||||||
|
response = new RestApiResponse<>(e.getCode(), e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("根据站点获取停车平台配置 error", e);
|
||||||
|
response = new RestApiResponse<>(e);
|
||||||
|
}
|
||||||
|
logger.info("根据站点获取停车平台配置 result:{}", response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
// public AjaxResult checkStationAmap
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ spring:
|
|||||||
# redis 配置
|
# redis 配置
|
||||||
redis:
|
redis:
|
||||||
# 地址
|
# 地址
|
||||||
host: 192.168.0.118
|
host: 192.168.0.6
|
||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 6379
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
@@ -36,9 +36,9 @@ spring:
|
|||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
# url: jdbc:mysql://192.168.0.118:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
# url: jdbc:mysql://192.168.0.6:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
# username: jsowell_pre
|
# username: jsowell_pre
|
||||||
url: jdbc:mysql://192.168.0.118:3306/jsowell_prd?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
url: jdbc:mysql://192.168.0.6:3306/jsowell_prd?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
username: jsowell_prd
|
username: jsowell_prd
|
||||||
password: dev@js160829
|
password: dev@js160829
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
@@ -90,7 +90,7 @@ spring:
|
|||||||
|
|
||||||
# rabbitmq配置 dev
|
# rabbitmq配置 dev
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
host: 192.168.0.118
|
host: 192.168.0.6
|
||||||
port: 5672
|
port: 5672
|
||||||
username: admin
|
username: admin
|
||||||
password: admin
|
password: admin
|
||||||
@@ -121,7 +121,7 @@ logging:
|
|||||||
|
|
||||||
# 基础URL前缀
|
# 基础URL前缀
|
||||||
baseurl:
|
baseurl:
|
||||||
prefix: http://192.168.0.118:8080
|
prefix: http://192.168.0.6:8080
|
||||||
|
|
||||||
# Minio配置
|
# Minio配置
|
||||||
minio:
|
minio:
|
||||||
@@ -271,7 +271,7 @@ dubbo:
|
|||||||
name: wcc-server
|
name: wcc-server
|
||||||
qosEnable: false
|
qosEnable: false
|
||||||
registry:
|
registry:
|
||||||
address: nacos://192.168.0.118:8848
|
address: nacos://192.168.0.6:8848
|
||||||
parameters:
|
parameters:
|
||||||
namespace: e328faaf-8516-42d0-817a-7406232b3581
|
namespace: e328faaf-8516-42d0-817a-7406232b3581
|
||||||
username: nacos
|
username: nacos
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public enum ParkingPlatformEnum {
|
|||||||
LU_TONG_YUN_TING_PLATFORM("1", "路通云停停车平台"),
|
LU_TONG_YUN_TING_PLATFORM("1", "路通云停停车平台"),
|
||||||
RUAN_JIE_PLATFORM("2", "软杰停车平台"),
|
RUAN_JIE_PLATFORM("2", "软杰停车平台"),
|
||||||
SHEN_ZHEN_PLATFORM("3", "深圳停车道闸平台"),
|
SHEN_ZHEN_PLATFORM("3", "深圳停车道闸平台"),
|
||||||
|
KE_TUO_PLATFORM("4", "科拓停车平台"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private String code;
|
private String code;
|
||||||
|
|||||||
@@ -7,11 +7,8 @@ import com.jsowell.common.util.Cp56Time2a.Cp56Time2aUtil;
|
|||||||
import com.jsowell.common.util.DateUtils;
|
import com.jsowell.common.util.DateUtils;
|
||||||
import com.jsowell.common.util.YKCUtils;
|
import com.jsowell.common.util.YKCUtils;
|
||||||
import com.jsowell.netty.factory.YKCOperateFactory;
|
import com.jsowell.netty.factory.YKCOperateFactory;
|
||||||
import com.jsowell.pile.dto.SavePileMsgDTO;
|
|
||||||
import com.jsowell.pile.service.PileMsgRecordService;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -27,9 +24,6 @@ import java.util.Date;
|
|||||||
public class TimeCheckSettingResponseHandler extends AbstractYkcHandler {
|
public class TimeCheckSettingResponseHandler extends AbstractYkcHandler {
|
||||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.TIME_CHECK_SETTING_ANSWER_CODE.getBytes());
|
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.TIME_CHECK_SETTING_ANSWER_CODE.getBytes());
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PileMsgRecordService pileMsgRecordService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
YKCOperateFactory.register(type, this);
|
YKCOperateFactory.register(type, this);
|
||||||
@@ -58,15 +52,7 @@ public class TimeCheckSettingResponseHandler extends AbstractYkcHandler {
|
|||||||
Date date = Cp56Time2aUtil.byte2Hdate(currentTimeByteArr);
|
Date date = Cp56Time2aUtil.byte2Hdate(currentTimeByteArr);
|
||||||
String deviceTime = DateUtils.formatDateTime(date);
|
String deviceTime = DateUtils.formatDateTime(date);
|
||||||
String platformTime = DateUtils.getDateTime();
|
String platformTime = DateUtils.getDateTime();
|
||||||
SavePileMsgDTO dto = SavePileMsgDTO.builder()
|
// 对时应答(0x55)仅记录日志,不再保存到 pile_msg_record,避免日志表持续膨胀。
|
||||||
.pileSn(pileSn)
|
|
||||||
.connectorCode("")
|
|
||||||
.transactionCode("")
|
|
||||||
.frameType(type)
|
|
||||||
.jsonMsg("对时设置应答: deviceTime=" + deviceTime + ", platformTime=" + platformTime)
|
|
||||||
.originalMsg(ykcDataProtocol.getHEXString())
|
|
||||||
.build();
|
|
||||||
pileMsgRecordService.save(dto);
|
|
||||||
log.info("[===对时设置充电桩应答===], pileSn:{}, channelId:{}, 充电桩当前时间:{}, 平台当前时间:{}", pileSn, channel.channel().id().asShortText(), deviceTime, platformTime);
|
log.info("[===对时设置充电桩应答===], pileSn:{}, channelId:{}, 充电桩当前时间:{}, 平台当前时间:{}", pileSn, channel.channel().id().asShortText(), deviceTime, platformTime);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class ThirdpartyParkingConfig {
|
|||||||
private String parkingName;
|
private String parkingName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停车平台类型(1-路通云停;2-软杰;3-qcyun)
|
* 停车平台类型(1-路通云停;2-软杰;3-qcyun;4-科拓)
|
||||||
*/
|
*/
|
||||||
private String platformType;
|
private String platformType;
|
||||||
|
|
||||||
|
|||||||
@@ -23,4 +23,13 @@ public interface PileMsgRecordMapper {
|
|||||||
|
|
||||||
List<PileMsgRecord> queryPileFeedList(@Param("pileSn") String pileSn, @Param("frameType") String frameType,
|
List<PileMsgRecord> queryPileFeedList(@Param("pileSn") String pileSn, @Param("frameType") String frameType,
|
||||||
@Param("transactionCode") String transactionCode);
|
@Param("transactionCode") String transactionCode);
|
||||||
|
|
||||||
|
List<PileMsgRecord> queryPileFeedListByJsonTransactionCode(@Param("pileSn") String pileSn, @Param("frameType") String frameType,
|
||||||
|
@Param("transactionCode") String transactionCode);
|
||||||
|
|
||||||
|
Long selectRetentionCutoffId(@Param("pileSn") String pileSn, @Param("frameType") String frameType,
|
||||||
|
@Param("offset") int offset);
|
||||||
|
|
||||||
|
int deleteOldRecordsBeforeId(@Param("pileSn") String pileSn, @Param("frameType") String frameType,
|
||||||
|
@Param("cutoffId") Long cutoffId, @Param("limit") int limit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ import com.jsowell.pile.dto.YuxinReservationChargingDTO;
|
|||||||
public interface YuxinReservationChargingService {
|
public interface YuxinReservationChargingService {
|
||||||
|
|
||||||
void createReservation(YuxinReservationChargingDTO dto);
|
void createReservation(YuxinReservationChargingDTO dto);
|
||||||
|
|
||||||
|
void cancelReservation(YuxinReservationChargingDTO dto);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -828,7 +828,7 @@ public class PileBillingTemplateServiceImpl implements PileBillingTemplateServic
|
|||||||
EchoBillingTemplateVO echoBillingTemplateVO = queryPileBillingTemplateById(Long.parseLong(billingTemplateVO.getTemplateId()));
|
EchoBillingTemplateVO echoBillingTemplateVO = queryPileBillingTemplateById(Long.parseLong(billingTemplateVO.getTemplateId()));
|
||||||
// 时段清单
|
// 时段清单
|
||||||
List<BillingTimeDTO> timeArray = echoBillingTemplateVO.getTimeArray();
|
List<BillingTimeDTO> timeArray = echoBillingTemplateVO.getTimeArray();
|
||||||
log.info("计费模板时段:{}", JSON.toJSONString(timeArray));
|
// log.info("计费模板时段:{}", JSON.toJSONString(timeArray));
|
||||||
|
|
||||||
List<BillingPriceVO> resultList = Lists.newArrayList();
|
List<BillingPriceVO> resultList = Lists.newArrayList();
|
||||||
for (BillingTimeDTO billingTimeDTO : timeArray) {
|
for (BillingTimeDTO billingTimeDTO : timeArray) {
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.jsowell.pile.service.impl;
|
||||||
|
|
||||||
|
import com.jsowell.common.util.StringUtils;
|
||||||
|
import com.jsowell.pile.mapper.PileMsgRecordMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非交易记录报文保留策略:
|
||||||
|
* 每个桩的每个帧类型仅保留最近200条,删除动作放到后台异步分批执行,
|
||||||
|
* 避免在报文写入主链路上同步删除带来额外延迟。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class PileMsgRecordRetentionService {
|
||||||
|
|
||||||
|
private static final String KEEP_FOREVER_FRAME_TYPE = "0x3B";
|
||||||
|
private static final int RETAIN_COUNT = 200;
|
||||||
|
private static final int RETENTION_OFFSET = RETAIN_COUNT - 1;
|
||||||
|
private static final int DELETE_BATCH_SIZE = 500;
|
||||||
|
private static final int MAX_DELETE_BATCHES_PER_RUN = 20;
|
||||||
|
private static final long CLEANUP_DELAY_SECONDS = 30L;
|
||||||
|
private static final long FOLLOW_UP_DELAY_SECONDS = 5L;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PileMsgRecordMapper pileMsgRecordMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("scheduledExecutorService")
|
||||||
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
|
||||||
|
private final Set<String> pendingCleanupKeys = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
public void scheduleRetention(String pileSn, String frameType) {
|
||||||
|
if (StringUtils.isBlank(pileSn) || StringUtils.isBlank(frameType) || KEEP_FOREVER_FRAME_TYPE.equals(frameType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.scheduleRetention(pileSn, frameType, CLEANUP_DELAY_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleRetention(String pileSn, String frameType, long delaySeconds) {
|
||||||
|
String key = buildKey(pileSn, frameType);
|
||||||
|
if (!pendingCleanupKeys.add(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduledExecutorService.schedule(() -> runCleanup(key, pileSn, frameType), delaySeconds, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runCleanup(String key, String pileSn, String frameType) {
|
||||||
|
boolean hasMore = false;
|
||||||
|
try {
|
||||||
|
hasMore = cleanupGroup(pileSn, frameType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("pile_msg_record清理失败, pileSn:{}, frameType:{}", pileSn, frameType, e);
|
||||||
|
} finally {
|
||||||
|
pendingCleanupKeys.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasMore) {
|
||||||
|
this.scheduleRetention(pileSn, frameType, FOLLOW_UP_DELAY_SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cleanupGroup(String pileSn, String frameType) {
|
||||||
|
Long cutoffId = pileMsgRecordMapper.selectRetentionCutoffId(pileSn, frameType, RETENTION_OFFSET);
|
||||||
|
if (cutoffId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalDeleted = 0;
|
||||||
|
for (int i = 0; i < MAX_DELETE_BATCHES_PER_RUN; i++) {
|
||||||
|
int deleted = pileMsgRecordMapper.deleteOldRecordsBeforeId(pileSn, frameType, cutoffId, DELETE_BATCH_SIZE);
|
||||||
|
totalDeleted += deleted;
|
||||||
|
if (deleted < DELETE_BATCH_SIZE) {
|
||||||
|
if (totalDeleted > 0) {
|
||||||
|
log.info("pile_msg_record清理完成, pileSn:{}, frameType:{}, deleted:{}", pileSn, frameType, totalDeleted);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalDeleted > 0) {
|
||||||
|
log.info("pile_msg_record本轮清理完成, pileSn:{}, frameType:{}, deleted:{}, 将继续异步清理",
|
||||||
|
pileSn, frameType, totalDeleted);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildKey(String pileSn, String frameType) {
|
||||||
|
return pileSn + "#" + frameType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import com.jsowell.pile.service.OrderBasicInfoService;
|
|||||||
import com.jsowell.pile.service.PileMsgRecordService;
|
import com.jsowell.pile.service.PileMsgRecordService;
|
||||||
import com.jsowell.pile.vo.web.PileCommunicationLogVO;
|
import com.jsowell.pile.vo.web.PileCommunicationLogVO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -36,6 +37,9 @@ public class PileMsgRecordServiceImpl implements PileMsgRecordService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OrderBasicInfoService orderBasicInfoService;
|
private OrderBasicInfoService orderBasicInfoService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PileMsgRecordRetentionService pileMsgRecordRetentionService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(String pileSn, String connectorCode, String frameType, String jsonMsg, String originalMsg) {
|
public void save(String pileSn, String connectorCode, String frameType, String jsonMsg, String originalMsg) {
|
||||||
PileMsgRecord pileMsgRecord = PileMsgRecord.builder()
|
PileMsgRecord pileMsgRecord = PileMsgRecord.builder()
|
||||||
@@ -46,6 +50,7 @@ public class PileMsgRecordServiceImpl implements PileMsgRecordService {
|
|||||||
.originalMsg(originalMsg)
|
.originalMsg(originalMsg)
|
||||||
.build();
|
.build();
|
||||||
pileMsgRecordMapper.insertSelective(pileMsgRecord);
|
pileMsgRecordMapper.insertSelective(pileMsgRecord);
|
||||||
|
pileMsgRecordRetentionService.scheduleRetention(pileSn, frameType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,6 +90,7 @@ public class PileMsgRecordServiceImpl implements PileMsgRecordService {
|
|||||||
pileMsgRecord.setOriginalMsg(originalMsg);
|
pileMsgRecord.setOriginalMsg(originalMsg);
|
||||||
pileMsgRecord.setCreateTime(DateUtils.getDateTime());
|
pileMsgRecord.setCreateTime(DateUtils.getDateTime());
|
||||||
pileMsgRecordMapper.insertSelective(pileMsgRecord);
|
pileMsgRecordMapper.insertSelective(pileMsgRecord);
|
||||||
|
pileMsgRecordRetentionService.scheduleRetention(pileSn, frameType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
@@ -178,6 +184,9 @@ public class PileMsgRecordServiceImpl implements PileMsgRecordService {
|
|||||||
@Override
|
@Override
|
||||||
public List<PileMsgRecord> queryPileFeedList(String pileSn, String frameType, String transactionCode) {
|
public List<PileMsgRecord> queryPileFeedList(String pileSn, String frameType, String transactionCode) {
|
||||||
List<PileMsgRecord> list = pileMsgRecordMapper.queryPileFeedList(pileSn, frameType, transactionCode);
|
List<PileMsgRecord> list = pileMsgRecordMapper.queryPileFeedList(pileSn, frameType, transactionCode);
|
||||||
|
if (CollectionUtils.isEmpty(list) && StringUtils.isNotBlank(transactionCode)) {
|
||||||
|
list = pileMsgRecordMapper.queryPileFeedListByJsonTransactionCode(pileSn, frameType, transactionCode);
|
||||||
|
}
|
||||||
log.info("queryPileFeedList - pileSn:{}, frameType:{}, transactionCode:{}, list: {}", pileSn, frameType, transactionCode, list);
|
log.info("queryPileFeedList - pileSn:{}, frameType:{}, transactionCode:{}, list: {}", pileSn, frameType, transactionCode, list);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,8 +76,9 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
|
|||||||
YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_START_CHARGING_CODE.getBytes()),
|
YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_START_CHARGING_CODE.getBytes()),
|
||||||
YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_STOP_CHARGING_CODE.getBytes()),
|
YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_STOP_CHARGING_CODE.getBytes()),
|
||||||
YKCUtils.frameType2Str(YKCFrameTypeCode.RESERVATION_CHARGING_SETUP_CODE.getBytes()),
|
YKCUtils.frameType2Str(YKCFrameTypeCode.RESERVATION_CHARGING_SETUP_CODE.getBytes()),
|
||||||
YKCUtils.frameType2Str(YKCFrameTypeCode.YUXIN_RESERVATION_CHARGING_SETUP_CODE.getBytes()),
|
YKCUtils.frameType2Str(YKCFrameTypeCode.YUXIN_RESERVATION_CHARGING_SETUP_CODE.getBytes())
|
||||||
YKCUtils.frameType2Str(YKCFrameTypeCode.TIME_CHECK_SETTING_CODE.getBytes())
|
// 对时帧(0x56)不再保存到 pile_msg_record,避免日志表持续膨胀。
|
||||||
|
// YKCUtils.frameType2Str(YKCFrameTypeCode.TIME_CHECK_SETTING_CODE.getBytes())
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.util.Date;
|
|||||||
public class YuxinReservationChargingServiceImpl implements YuxinReservationChargingService {
|
public class YuxinReservationChargingServiceImpl implements YuxinReservationChargingService {
|
||||||
|
|
||||||
private static final String YUXIN_RESERVATION_TYPE_CREATE = "00";
|
private static final String YUXIN_RESERVATION_TYPE_CREATE = "00";
|
||||||
|
private static final String YUXIN_RESERVATION_TYPE_CANCEL = "01";
|
||||||
private static final BigDecimal YUXIN_ACCOUNT_BALANCE = new BigDecimal("999.99");
|
private static final BigDecimal YUXIN_ACCOUNT_BALANCE = new BigDecimal("999.99");
|
||||||
private static final int TIME_CONTROL_CHARGING_STRATEGY = 1;
|
private static final int TIME_CONTROL_CHARGING_STRATEGY = 1;
|
||||||
private static final int DEFAULT_RESERVATION_TIMEOUT_MINUTES = 0xFF;
|
private static final int DEFAULT_RESERVATION_TIMEOUT_MINUTES = 0xFF;
|
||||||
@@ -50,6 +51,15 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
sendYuxinReservationCommandAndAssertSuccess(dto, YUXIN_RESERVATION_TYPE_CREATE);
|
sendYuxinReservationCommandAndAssertSuccess(dto, YUXIN_RESERVATION_TYPE_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelReservation(YuxinReservationChargingDTO dto) {
|
||||||
|
validateCancelParam(dto);
|
||||||
|
normalizePileInfo(dto);
|
||||||
|
assertYuxinMainboard(dto.getPileSn());
|
||||||
|
|
||||||
|
sendYuxinReservationCommandAndAssertSuccess(dto, YUXIN_RESERVATION_TYPE_CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
private void sendYuxinReservationCommandAndAssertSuccess(YuxinReservationChargingDTO dto,
|
private void sendYuxinReservationCommandAndAssertSuccess(YuxinReservationChargingDTO dto,
|
||||||
String yuxinReservationType) {
|
String yuxinReservationType) {
|
||||||
if (!sendYuxinReservationCommand(dto, yuxinReservationType)) {
|
if (!sendYuxinReservationCommand(dto, yuxinReservationType)) {
|
||||||
@@ -71,7 +81,7 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
|
|
||||||
private YuxinReservationChargingCommand buildYuxinReservationChargingCommand(YuxinReservationChargingDTO dto,
|
private YuxinReservationChargingCommand buildYuxinReservationChargingCommand(YuxinReservationChargingDTO dto,
|
||||||
String yuxinReservationType) {
|
String yuxinReservationType) {
|
||||||
LocalTime startTime = parseSqlTime(dto.getStartTime()).toLocalTime();
|
LocalTime startTime = getCommandStartTime(dto);
|
||||||
return YuxinReservationChargingCommand.builder()
|
return YuxinReservationChargingCommand.builder()
|
||||||
.transactionCode(Constants.ILLEGAL_TRANSACTION_CODE)
|
.transactionCode(Constants.ILLEGAL_TRANSACTION_CODE)
|
||||||
.pileSn(dto.getPileSn())
|
.pileSn(dto.getPileSn())
|
||||||
@@ -82,7 +92,7 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
.chargingParam(getChargingHours(dto))
|
.chargingParam(getChargingHours(dto))
|
||||||
.systemTime(new Date())
|
.systemTime(new Date())
|
||||||
.reservedStartTime(getReservedStartDate(startTime, yuxinReservationType))
|
.reservedStartTime(getReservedStartDate(startTime, yuxinReservationType))
|
||||||
.reservationTimeout(getReservationTimeout(dto))
|
.reservationTimeout(getReservationTimeout(dto, yuxinReservationType))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +106,10 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
return DateUtils.localDateTime2Date(reservedStartDateTime);
|
return DateUtils.localDateTime2Date(reservedStartDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getReservationTimeout(YuxinReservationChargingDTO dto) {
|
private int getReservationTimeout(YuxinReservationChargingDTO dto, String yuxinReservationType) {
|
||||||
|
if (StringUtils.equals(yuxinReservationType, YUXIN_RESERVATION_TYPE_CANCEL)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (dto.getReservationTimeout() != null) {
|
if (dto.getReservationTimeout() != null) {
|
||||||
return Math.min(Math.max(dto.getReservationTimeout(), 0), 0xFF);
|
return Math.min(Math.max(dto.getReservationTimeout(), 0), 0xFF);
|
||||||
}
|
}
|
||||||
@@ -104,6 +117,9 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal getChargingHours(YuxinReservationChargingDTO dto) {
|
private BigDecimal getChargingHours(YuxinReservationChargingDTO dto) {
|
||||||
|
if (StringUtils.isBlank(dto.getStartTime()) || StringUtils.isBlank(dto.getEndTime())) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
LocalTime startTime = parseSqlTime(dto.getStartTime()).toLocalTime();
|
LocalTime startTime = parseSqlTime(dto.getStartTime()).toLocalTime();
|
||||||
LocalTime endTime = parseSqlTime(dto.getEndTime()).toLocalTime();
|
LocalTime endTime = parseSqlTime(dto.getEndTime()).toLocalTime();
|
||||||
long timeout = Duration.between(startTime, endTime).toMinutes();
|
long timeout = Duration.between(startTime, endTime).toMinutes();
|
||||||
@@ -136,6 +152,13 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateCancelParam(YuxinReservationChargingDTO dto) {
|
||||||
|
if (StringUtils.isBlank(dto.getMemberId())
|
||||||
|
|| StringUtils.isBlank(dto.getPileConnectorCode())) {
|
||||||
|
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Time parseSqlTime(String time) {
|
private Time parseSqlTime(String time) {
|
||||||
if (time.length() == 5) {
|
if (time.length() == 5) {
|
||||||
return Time.valueOf(time + ":00");
|
return Time.valueOf(time + ":00");
|
||||||
@@ -143,6 +166,13 @@ public class YuxinReservationChargingServiceImpl implements YuxinReservationChar
|
|||||||
return Time.valueOf(time);
|
return Time.valueOf(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LocalTime getCommandStartTime(YuxinReservationChargingDTO dto) {
|
||||||
|
if (StringUtils.isBlank(dto.getStartTime())) {
|
||||||
|
return LocalTime.now();
|
||||||
|
}
|
||||||
|
return parseSqlTime(dto.getStartTime()).toLocalTime();
|
||||||
|
}
|
||||||
|
|
||||||
private void assertYuxinMainboard(String pileSn) {
|
private void assertYuxinMainboard(String pileSn) {
|
||||||
PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn);
|
PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn);
|
||||||
String programVersion = pileBasicInfo == null ? null : pileBasicInfo.getProgramVersion();
|
String programVersion = pileBasicInfo == null ? null : pileBasicInfo.getProgramVersion();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</resultMap>
|
</resultMap>
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
<!--@mbg.generated-->
|
<!--@mbg.generated-->
|
||||||
id, pile_sn, frame_type, connector_code, pile_connector_code, original_msg, json_msg, create_time
|
id, pile_sn, transaction_code, frame_type, connector_code, pile_connector_code, original_msg, json_msg, create_time
|
||||||
</sql>
|
</sql>
|
||||||
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
|
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
|
||||||
<!--@mbg.generated-->
|
<!--@mbg.generated-->
|
||||||
@@ -154,6 +154,19 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="queryPileFeedList" resultMap="BaseResultMap">
|
<select id="queryPileFeedList" resultMap="BaseResultMap">
|
||||||
|
select
|
||||||
|
<include refid="Base_Column_List" />
|
||||||
|
from pile_msg_record
|
||||||
|
where pile_sn = #{pileSn,jdbcType=VARCHAR}
|
||||||
|
<if test="frameType != null and frameType != ''">
|
||||||
|
and frame_type = #{frameType,jdbcType=VARCHAR}
|
||||||
|
</if>
|
||||||
|
<if test="transactionCode != null and transactionCode != ''" >
|
||||||
|
and transaction_code = #{transactionCode,jdbcType=VARCHAR}
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="queryPileFeedListByJsonTransactionCode" resultMap="BaseResultMap">
|
||||||
select
|
select
|
||||||
<include refid="Base_Column_List" />
|
<include refid="Base_Column_List" />
|
||||||
from pile_msg_record
|
from pile_msg_record
|
||||||
@@ -165,4 +178,23 @@
|
|||||||
and json_msg->>'$.transactionCode' LIKE CONCAT('%', #{transactionCode}, '%')
|
and json_msg->>'$.transactionCode' LIKE CONCAT('%', #{transactionCode}, '%')
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectRetentionCutoffId" resultType="java.lang.Long">
|
||||||
|
select id
|
||||||
|
from pile_msg_record
|
||||||
|
where pile_sn = #{pileSn,jdbcType=VARCHAR}
|
||||||
|
and frame_type = #{frameType,jdbcType=VARCHAR}
|
||||||
|
and frame_type != '0x3B'
|
||||||
|
order by id desc
|
||||||
|
limit #{offset}, 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<delete id="deleteOldRecordsBeforeId">
|
||||||
|
delete from pile_msg_record
|
||||||
|
where pile_sn = #{pileSn,jdbcType=VARCHAR}
|
||||||
|
and frame_type = #{frameType,jdbcType=VARCHAR}
|
||||||
|
and frame_type != '0x3B'
|
||||||
|
and id < #{cutoffId,jdbcType=BIGINT}
|
||||||
|
limit #{limit}
|
||||||
|
</delete>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
#{updateBy,jdbcType=VARCHAR}, #{delFlag,jdbcType=VARCHAR})
|
#{updateBy,jdbcType=VARCHAR}, #{delFlag,jdbcType=VARCHAR})
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<insert id="insertSelective" parameterType="com.jsowell.pile.domain.ThirdpartyParkingConfig">
|
<insert id="insertSelective" parameterType="com.jsowell.pile.domain.ThirdpartyParkingConfig" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
|
||||||
<!--@mbg.generated-->
|
<!--@mbg.generated-->
|
||||||
insert into thirdparty_parking_config
|
insert into thirdparty_parking_config
|
||||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import com.jsowell.common.util.StringUtils;
|
|||||||
import com.jsowell.pile.domain.CarCouponRecord;
|
import com.jsowell.pile.domain.CarCouponRecord;
|
||||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||||
import com.jsowell.pile.domain.PileBasicInfo;
|
import com.jsowell.pile.domain.PileBasicInfo;
|
||||||
|
import com.jsowell.pile.domain.PileConnectorInfo;
|
||||||
|
import com.jsowell.pile.domain.PileStationInfo;
|
||||||
import com.jsowell.pile.domain.ThirdPartyStationRelation;
|
import com.jsowell.pile.domain.ThirdPartyStationRelation;
|
||||||
import com.jsowell.pile.dto.PushRealTimeInfoDTO;
|
import com.jsowell.pile.dto.PushRealTimeInfoDTO;
|
||||||
import com.jsowell.pile.dto.PushStationInfoDTO;
|
import com.jsowell.pile.dto.PushStationInfoDTO;
|
||||||
@@ -34,6 +36,8 @@ import com.jsowell.thirdparty.huawei.HuaweiServiceV2;
|
|||||||
import com.jsowell.thirdparty.lianlian.service.LianLianService;
|
import com.jsowell.thirdparty.lianlian.service.LianLianService;
|
||||||
import com.jsowell.thirdparty.nanrui.service.NRService;
|
import com.jsowell.thirdparty.nanrui.service.NRService;
|
||||||
import com.jsowell.thirdparty.parking.common.bean.QcyunParkCouponDTO;
|
import com.jsowell.thirdparty.parking.common.bean.QcyunParkCouponDTO;
|
||||||
|
import com.jsowell.thirdparty.parking.common.bean.KetuoChargePileDeductionDTO;
|
||||||
|
import com.jsowell.thirdparty.parking.service.KetuoService;
|
||||||
import com.jsowell.thirdparty.parking.service.LTYTService;
|
import com.jsowell.thirdparty.parking.service.LTYTService;
|
||||||
import com.jsowell.thirdparty.parking.service.QcyunsService;
|
import com.jsowell.thirdparty.parking.service.QcyunsService;
|
||||||
import com.jsowell.thirdparty.parking.service.RJService;
|
import com.jsowell.thirdparty.parking.service.RJService;
|
||||||
@@ -54,6 +58,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
@@ -112,6 +117,12 @@ public class CommonService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private QcyunsService qcyunsService;
|
private QcyunsService qcyunsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private KetuoService ketuoService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PileConnectorInfoService pileConnectorInfoService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HuaweiServiceV2 huaweiServiceV2;
|
private HuaweiServiceV2 huaweiServiceV2;
|
||||||
|
|
||||||
@@ -827,6 +838,11 @@ public class CommonService {
|
|||||||
.discountValue(chargeParkingDiscount.getDiscountValue())
|
.discountValue(chargeParkingDiscount.getDiscountValue())
|
||||||
.build();
|
.build();
|
||||||
discountFlag = qcyunsService.issuanceOfParkingTickets(dto);
|
discountFlag = qcyunsService.issuanceOfParkingTickets(dto);
|
||||||
|
} else if (StringUtils.equals(ParkingPlatformEnum.KE_TUO_PLATFORM.getCode(), parkingPlatformId)) {
|
||||||
|
// 科拓--充电桩抵扣
|
||||||
|
KetuoChargePileDeductionDTO dto = buildKetuoChargePileDeductionDTO(
|
||||||
|
realTimeMonitorData, orderBasicInfo, chargeParkingDiscount);
|
||||||
|
discountFlag = ketuoService.syncChargePilePay(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discountFlag) {
|
if (discountFlag) {
|
||||||
@@ -842,6 +858,81 @@ public class CommonService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private KetuoChargePileDeductionDTO buildKetuoChargePileDeductionDTO(RealTimeMonitorData realTimeMonitorData,
|
||||||
|
OrderBasicInfo orderBasicInfo,
|
||||||
|
ChargeParkingDiscountVO chargeParkingDiscount) {
|
||||||
|
PileStationInfo stationInfo = pileStationInfoService.selectPileStationInfoById(Long.parseLong(orderBasicInfo.getStationId()));
|
||||||
|
PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(orderBasicInfo.getPileSn());
|
||||||
|
PileConnectorInfo connectorInfo = getPileConnectorInfo(orderBasicInfo);
|
||||||
|
|
||||||
|
Integer totalMoney = yuanToFen(realTimeMonitorData.getChargingAmount());
|
||||||
|
Integer freeMoney = Constants.TWO.equals(String.valueOf(chargeParkingDiscount.getDiscountType()))
|
||||||
|
? yuanToFen(chargeParkingDiscount.getDiscountValue())
|
||||||
|
: 0;
|
||||||
|
Integer freeTime = Constants.ONE.equals(String.valueOf(chargeParkingDiscount.getDiscountType()))
|
||||||
|
? minutesToSeconds(chargeParkingDiscount.getDiscountValue())
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return KetuoChargePileDeductionDTO.builder()
|
||||||
|
.stationId(orderBasicInfo.getStationId())
|
||||||
|
.orderNo(orderBasicInfo.getOrderCode())
|
||||||
|
.plateNo(orderBasicInfo.getPlateNumber())
|
||||||
|
.startTime(formatDateTime(orderBasicInfo.getChargeStartTime(), orderBasicInfo.getCreateTime()))
|
||||||
|
.endTime(formatDateTime(orderBasicInfo.getChargeEndTime(), new java.util.Date()))
|
||||||
|
.stationName(stationInfo == null ? orderBasicInfo.getStationId() : stationInfo.getStationName())
|
||||||
|
.deviceId(orderBasicInfo.getPileSn())
|
||||||
|
.deviceName(pileBasicInfo == null || StringUtils.isBlank(pileBasicInfo.getName())
|
||||||
|
? orderBasicInfo.getPileSn() : pileBasicInfo.getName())
|
||||||
|
.spaceNo(connectorInfo == null || StringUtils.isBlank(connectorInfo.getParkNo())
|
||||||
|
? "0" : connectorInfo.getParkNo())
|
||||||
|
.power(formatPower(realTimeMonitorData.getChargingDegree()))
|
||||||
|
.elecMoney(0)
|
||||||
|
.seviceMoney(totalMoney)
|
||||||
|
.totalMoney(totalMoney)
|
||||||
|
.freeType(0)
|
||||||
|
.freeMoney(freeMoney)
|
||||||
|
.freeTime(freeTime)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PileConnectorInfo getPileConnectorInfo(OrderBasicInfo orderBasicInfo) {
|
||||||
|
if (StringUtils.isBlank(orderBasicInfo.getPileConnectorCode())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PileConnectorInfo query = new PileConnectorInfo();
|
||||||
|
query.setPileConnectorCode(orderBasicInfo.getPileConnectorCode());
|
||||||
|
List<PileConnectorInfo> connectorInfoList = pileConnectorInfoService.selectPileConnectorInfoList(query);
|
||||||
|
if (CollectionUtils.isNotEmpty(connectorInfoList)) {
|
||||||
|
return connectorInfoList.get(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatDateTime(java.util.Date date, java.util.Date defaultDate) {
|
||||||
|
return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date == null ? defaultDate : date);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatPower(String power) {
|
||||||
|
if (StringUtils.isBlank(power)) {
|
||||||
|
return "0.00";
|
||||||
|
}
|
||||||
|
return new BigDecimal(power).setScale(2, RoundingMode.HALF_UP).toPlainString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer yuanToFen(String amount) {
|
||||||
|
if (StringUtils.isBlank(amount)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return new BigDecimal(amount).multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer minutesToSeconds(String minutes) {
|
||||||
|
if (StringUtils.isBlank(minutes)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return new BigDecimal(minutes).multiply(new BigDecimal("60")).setScale(0, RoundingMode.HALF_UP).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一请求启动充电,目前给华为平台用
|
* 统一请求启动充电,目前给华为平台用
|
||||||
* @param dto
|
* @param dto
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.jsowell.thirdparty.parking.common.bean;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 科拓充电桩抵扣 DTO.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class KetuoChargePileDeductionDTO {
|
||||||
|
private String stationId;
|
||||||
|
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
private String plateNo;
|
||||||
|
|
||||||
|
private String startTime;
|
||||||
|
|
||||||
|
private String endTime;
|
||||||
|
|
||||||
|
private String stationName;
|
||||||
|
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
|
private String spaceNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 充电量,单位度,小数点后 2 位。
|
||||||
|
*/
|
||||||
|
private String power;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电费,单位分。
|
||||||
|
*/
|
||||||
|
private Integer elecMoney;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务费/附加费,单位分。科拓接口字段名为 seviceMoney。
|
||||||
|
*/
|
||||||
|
private Integer seviceMoney;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总费用,单位分。
|
||||||
|
*/
|
||||||
|
private Integer totalMoney;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0: 根据 freeMoney/freeTime 减免; 1: 本地车场配置减免规则。
|
||||||
|
*/
|
||||||
|
private Integer freeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减免停车金额,单位分。
|
||||||
|
*/
|
||||||
|
private Integer freeMoney;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减免停车时长,单位秒。
|
||||||
|
*/
|
||||||
|
private Integer freeTime;
|
||||||
|
}
|
||||||
11
jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/parking/service/KetuoService.java
vendored
Normal file
11
jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/parking/service/KetuoService.java
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package com.jsowell.thirdparty.parking.service;
|
||||||
|
|
||||||
|
import com.jsowell.thirdparty.parking.common.bean.KetuoChargePileDeductionDTO;
|
||||||
|
|
||||||
|
public interface KetuoService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 科拓--充电桩抵扣。
|
||||||
|
*/
|
||||||
|
boolean syncChargePilePay(KetuoChargePileDeductionDTO dto);
|
||||||
|
}
|
||||||
139
jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/parking/service/impl/KetuoServiceImpl.java
vendored
Normal file
139
jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/parking/service/impl/KetuoServiceImpl.java
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package com.jsowell.thirdparty.parking.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.jsowell.common.enums.parkplatform.ParkingPlatformEnum;
|
||||||
|
import com.jsowell.common.util.StringUtils;
|
||||||
|
import com.jsowell.common.util.id.UUID;
|
||||||
|
import com.jsowell.common.util.sign.MD5Util;
|
||||||
|
import com.jsowell.pile.domain.ThirdpartyParkingConfig;
|
||||||
|
import com.jsowell.pile.service.ThirdPartyParkingConfigService;
|
||||||
|
import com.jsowell.thirdparty.parking.common.bean.KetuoChargePileDeductionDTO;
|
||||||
|
import com.jsowell.thirdparty.parking.service.KetuoService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class KetuoServiceImpl implements KetuoService {
|
||||||
|
|
||||||
|
private static final String SERVICE_CODE_SYNC_CHARGE_PILE_PAY = "syncChargePilePay";
|
||||||
|
|
||||||
|
private static final String API_PATH_SYNC_CHARGE_PILE_PAY = "/api/wec/SyncChargePilePay";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ThirdPartyParkingConfigService thirdPartyParkingConfigService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean syncChargePilePay(KetuoChargePileDeductionDTO dto) {
|
||||||
|
ThirdpartyParkingConfig parkingConfig = resolveParkingConfig(dto);
|
||||||
|
if (parkingConfig == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JSONObject requestBody = buildRequestBody(dto, parkingConfig);
|
||||||
|
requestBody.put("key", generateKey(requestBody, parkingConfig.getSecretKey()));
|
||||||
|
|
||||||
|
String requestUrl = getRequestUrl(parkingConfig);
|
||||||
|
log.info("科拓充电桩抵扣 requestUrl:{}, requestBody:{}", requestUrl, requestBody.toJSONString());
|
||||||
|
String result = HttpUtil.post(requestUrl, requestBody.toJSONString());
|
||||||
|
if (StringUtils.isBlank(result)) {
|
||||||
|
log.warn("科拓充电桩抵扣返回为空, requestBody:{}", requestBody.toJSONString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JSONObject response = JSON.parseObject(result);
|
||||||
|
log.info("科拓充电桩抵扣 response:{}", JSON.toJSONString(response));
|
||||||
|
return StringUtils.equals("0", response.getString("resCode"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThirdpartyParkingConfig resolveParkingConfig(KetuoChargePileDeductionDTO dto) {
|
||||||
|
if (dto == null || StringUtils.isBlank(dto.getStationId())) {
|
||||||
|
log.warn("科拓充电桩抵扣失败, stationId为空, dto:{}", JSON.toJSONString(dto));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ThirdpartyParkingConfig parkingConfig = thirdPartyParkingConfigService.selectByStationId(dto.getStationId());
|
||||||
|
if (parkingConfig == null) {
|
||||||
|
log.warn("科拓充电桩抵扣失败, 站点未绑定停车场配置, stationId:{}", dto.getStationId());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!StringUtils.equals(ParkingPlatformEnum.KE_TUO_PLATFORM.getCode(), parkingConfig.getPlatformType())) {
|
||||||
|
log.warn("科拓充电桩抵扣失败, 站点绑定的停车平台类型不是科拓, stationId:{}, parkingConfigId:{}, platformType:{}",
|
||||||
|
dto.getStationId(), parkingConfig.getId(), parkingConfig.getPlatformType());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(parkingConfig.getAppId())
|
||||||
|
|| StringUtils.isBlank(parkingConfig.getSecretKey())
|
||||||
|
|| StringUtils.isBlank(parkingConfig.getParkId())) {
|
||||||
|
log.warn("科拓充电桩抵扣失败, 停车场配置缺少appId/secretKey/parkId, stationId:{}, parkingConfigId:{}",
|
||||||
|
dto.getStationId(), parkingConfig.getId());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(parkingConfig.getApiUrl())) {
|
||||||
|
log.warn("科拓充电桩抵扣失败, 停车场配置缺少apiUrl, stationId:{}, parkingConfigId:{}",
|
||||||
|
dto.getStationId(), parkingConfig.getId());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parkingConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildRequestBody(KetuoChargePileDeductionDTO dto, ThirdpartyParkingConfig parkingConfig) {
|
||||||
|
JSONObject requestBody = new JSONObject();
|
||||||
|
requestBody.put("appId", parkingConfig.getAppId());
|
||||||
|
requestBody.put("parkId", parkingConfig.getParkId());
|
||||||
|
requestBody.put("serviceCode", SERVICE_CODE_SYNC_CHARGE_PILE_PAY);
|
||||||
|
requestBody.put("ts", System.currentTimeMillis());
|
||||||
|
requestBody.put("reqId", UUID.randomUUID().toString().replace("-", ""));
|
||||||
|
requestBody.put("orderNo", dto.getOrderNo());
|
||||||
|
requestBody.put("plateNo", dto.getPlateNo());
|
||||||
|
requestBody.put("startTime", dto.getStartTime());
|
||||||
|
requestBody.put("endTime", dto.getEndTime());
|
||||||
|
requestBody.put("stationId", dto.getStationId());
|
||||||
|
requestBody.put("stationName", dto.getStationName());
|
||||||
|
requestBody.put("deviceId", dto.getDeviceId());
|
||||||
|
requestBody.put("deviceName", dto.getDeviceName());
|
||||||
|
requestBody.put("spaceNo", dto.getSpaceNo());
|
||||||
|
requestBody.put("power", dto.getPower());
|
||||||
|
requestBody.put("elecMoney", dto.getElecMoney());
|
||||||
|
requestBody.put("seviceMoney", dto.getSeviceMoney());
|
||||||
|
requestBody.put("totalMoney", dto.getTotalMoney());
|
||||||
|
requestBody.put("freeType", dto.getFreeType());
|
||||||
|
requestBody.put("freeMoney", dto.getFreeMoney());
|
||||||
|
requestBody.put("freeTime", dto.getFreeTime());
|
||||||
|
return requestBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateKey(JSONObject requestBody, String appSecret) {
|
||||||
|
TreeMap<String, String> params = new TreeMap<>();
|
||||||
|
requestBody.entrySet().stream()
|
||||||
|
.filter(entry -> !"key".equals(entry.getKey()) && !"appId".equals(entry.getKey()))
|
||||||
|
.filter(entry -> entry.getValue() != null)
|
||||||
|
.filter(entry -> !(entry.getValue() instanceof Map) && !(entry.getValue() instanceof Iterable))
|
||||||
|
.filter(entry -> !StringUtils.equals("", entry.getValue().toString()))
|
||||||
|
.forEach(entry -> params.put(entry.getKey(), entry.getValue().toString()));
|
||||||
|
|
||||||
|
StringBuilder signContent = new StringBuilder();
|
||||||
|
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||||
|
if (signContent.length() > 0) {
|
||||||
|
signContent.append("&");
|
||||||
|
}
|
||||||
|
signContent.append(entry.getKey()).append("=").append(entry.getValue());
|
||||||
|
}
|
||||||
|
signContent.append("&").append(appSecret);
|
||||||
|
return MD5Util.MD5Encode(signContent.toString()).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRequestUrl(ThirdpartyParkingConfig parkingConfig) {
|
||||||
|
String apiUrl = parkingConfig.getApiUrl();
|
||||||
|
if (apiUrl.endsWith(API_PATH_SYNC_CHARGE_PILE_PAY)) {
|
||||||
|
return apiUrl;
|
||||||
|
}
|
||||||
|
while (apiUrl.endsWith("/")) {
|
||||||
|
apiUrl = apiUrl.substring(0, apiUrl.length() - 1);
|
||||||
|
}
|
||||||
|
return apiUrl + API_PATH_SYNC_CHARGE_PILE_PAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user