mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-07-22 23:22:32 +08:00
Compare commits
12 Commits
9fef3bc03f
...
feature-VI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
402f66fb3a | ||
|
|
43beb1cf95 | ||
|
|
dd546e75da | ||
|
|
5ba73abb62 | ||
|
|
a1d3de972b | ||
|
|
682f1b4b71 | ||
|
|
4e774d7d7d | ||
|
|
d3c2865dcc | ||
|
|
d7e8562f42 | ||
|
|
d569f70315 | ||
|
|
45b7908ae6 | ||
|
|
2503fe8258 |
376
docs/plan/2026-06-13-ebike-auto-register-fallback-plan.md
Normal file
376
docs/plan/2026-06-13-ebike-auto-register-fallback-plan.md
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
# Plan: 电单车自动建档兜底优化
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
当前电单车设备正常自动建档依赖 `0x20 设备注册包`。
|
||||||
|
|
||||||
|
实际现场已经发现部分设备只持续上报心跳日志,但没有发送注册包,导致平台能看到设备通信,却在 `pile_basic_info`、`pile_connector_info` 中查不到对应桩号和端口数据。后续扫码、下单、端口状态查询会因为基础数据缺失返回空或报错。
|
||||||
|
|
||||||
|
本方案目标是:在不放大误建脏数据风险的前提下,为“不发送注册包但会上报心跳”的电单车设备补一条安全的自动建档兜底链路。
|
||||||
|
|
||||||
|
## 当前问题
|
||||||
|
|
||||||
|
### 当前自动建档入口
|
||||||
|
|
||||||
|
- `jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/RegistrationHandler.java`
|
||||||
|
- 处理 `0x20 设备注册包`。
|
||||||
|
- 解析 `EBikeMessageCmd20`。
|
||||||
|
- 调用 `pileBasicInfoService.registrationEBikePile(message)`。
|
||||||
|
|
||||||
|
- `jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileBasicInfoServiceImpl.java`
|
||||||
|
- `registrationEBikePile(EBikeMessageCmd20 message)` 会先按 `message.getPhysicalId()` 查询 `pile_basic_info`。
|
||||||
|
- 查到则直接返回。
|
||||||
|
- 查不到则创建 `pile_basic_info`,并按注册包 `portNumber` 创建 `pile_connector_info`。
|
||||||
|
|
||||||
|
### 当前缺口
|
||||||
|
|
||||||
|
- 如果设备不发送 `0x20`,`registrationEBikePile` 不会执行。
|
||||||
|
- `0x21 设备心跳包` 只更新端口状态,不会补建基础数据。
|
||||||
|
- `0x06 端口充电时功率心跳包` 只更新单端口状态和实时数据,不会补建基础数据。
|
||||||
|
- `updateConnectorStatus()` 对不存在的端口执行 update 时只会更新 0 行,不会报错,也不会自动创建。
|
||||||
|
- 当前自动建档 service 内部没有“开始创建 / 已存在 / 创建成功 / 创建失败”的明确日志,只能从 `设备注册包:{}` 间接判断是否进入过注册 handler。
|
||||||
|
|
||||||
|
### 数据可用性问题
|
||||||
|
|
||||||
|
现有自动建档逻辑默认写入:
|
||||||
|
|
||||||
|
- `merchant_id = 1`
|
||||||
|
- `station_id = 2`
|
||||||
|
- `model_id = null`
|
||||||
|
- `software_protocol = 3`
|
||||||
|
|
||||||
|
其中 `model_id = null` 有明显风险:部分查询端口详情的 SQL 会 inner join `pile_model_info`,即使 `pile_basic_info` 有记录,也可能因为没有型号导致详情查不到。
|
||||||
|
|
||||||
|
## 优化目标
|
||||||
|
|
||||||
|
1. 设备即使没有发送 `0x20` 注册包,只要上报 `0x21` 设备心跳包,也能兜底创建桩和端口基础数据。
|
||||||
|
2. 自动创建必须幂等,不能因为高频心跳或并发消息产生重复桩、重复端口。
|
||||||
|
3. 自动创建的数据必须可追溯,能从日志和数据库字段看出来源。
|
||||||
|
4. 自动创建只解决“基础数据不存在”的问题,不绕过站点、型号、计费模板等运营配置要求。
|
||||||
|
5. 优化后要能通过日志确认是否执行了兜底建档、创建了多少端口、是否因已存在而跳过。
|
||||||
|
|
||||||
|
## 优化方案
|
||||||
|
|
||||||
|
### 1. 抽取通用自动建档方法
|
||||||
|
|
||||||
|
在 `PileBasicInfoService` 中新增通用方法:
|
||||||
|
|
||||||
|
```java
|
||||||
|
void ensureEBikePileRegistered(String pileSn, int portNumber, String source);
|
||||||
|
```
|
||||||
|
|
||||||
|
建议含义:
|
||||||
|
|
||||||
|
- `pileSn`:电单车物理 ID 转换后的桩号。
|
||||||
|
- `portNumber`:设备端口总数。
|
||||||
|
- `source`:触发来源,例如 `registration_0x20`、`heartbeat_0x21`。
|
||||||
|
|
||||||
|
将现有 `registrationEBikePile(EBikeMessageCmd20 message)` 改为调用该通用方法:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ensureEBikePileRegistered(
|
||||||
|
message.getPhysicalId() + "",
|
||||||
|
message.getPortNumber(),
|
||||||
|
"registration_0x20"
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 在 0x21 设备心跳包增加兜底建档
|
||||||
|
|
||||||
|
修改 `HeartbeatHandler.supplyProcess()`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
EBikeMessageCmd21 message = new EBikeMessageCmd21(dataProtocol.getBytes());
|
||||||
|
String pileSn = message.getPhysicalId() + "";
|
||||||
|
|
||||||
|
saveLastTimeAndCheckChannel(pileSn, ctx);
|
||||||
|
log.info("设备心跳包:{}", JSON.toJSONString(message));
|
||||||
|
|
||||||
|
pileBasicInfoService.ensureEBikePileRegistered(
|
||||||
|
pileSn,
|
||||||
|
message.getPortNumber(),
|
||||||
|
"heartbeat_0x21"
|
||||||
|
);
|
||||||
|
|
||||||
|
updatePileStatus(message);
|
||||||
|
```
|
||||||
|
|
||||||
|
选择 `0x21` 作为主兜底入口的原因:
|
||||||
|
|
||||||
|
- `0x21` 能证明设备真实在线并正在与平台通信。
|
||||||
|
- `0x21` 包含 `physicalId` 和 `portNumber`,足够创建桩和全量端口。
|
||||||
|
- 相比扫码/下单接口,心跳包更接近设备事实,误建概率更低。
|
||||||
|
|
||||||
|
### 3. 不建议在扫码或下单接口直接兜底创建
|
||||||
|
|
||||||
|
扫码或下单接口通常只能拿到二维码参数或桩号,缺少端口总数、设备在线状态和协议上下文。
|
||||||
|
|
||||||
|
如果在这些入口自动创建,容易出现:
|
||||||
|
|
||||||
|
- 用户扫错码也创建数据。
|
||||||
|
- 伪造请求创建脏桩号。
|
||||||
|
- 端口数量无法判断,只能猜。
|
||||||
|
- 新建数据缺少型号、计费模板、站点归属,仍无法稳定下单。
|
||||||
|
|
||||||
|
因此扫码/下单入口只建议保留“查不到时给出明确错误或提示”,不承担建档。
|
||||||
|
|
||||||
|
### 4. 0x06 功率心跳只做二级兜底
|
||||||
|
|
||||||
|
`0x06` 只有当前端口号,不知道设备总端口数,不适合作为完整建桩依据。
|
||||||
|
|
||||||
|
建议处理策略:
|
||||||
|
|
||||||
|
- 如果 `pile_basic_info` 不存在:只打印 warn 日志,不自动创建完整桩。
|
||||||
|
- 如果 `pile_basic_info` 存在但当前 `pile_connector_code` 不存在:可补建这个单端口,并打印 warn 日志。
|
||||||
|
- 如果端口存在:按现有逻辑更新状态和实时数据。
|
||||||
|
|
||||||
|
这样可以减少充电中单端口状态丢失,但不把 `0x06` 作为主建档来源。
|
||||||
|
|
||||||
|
### 5. 默认归属和型号改为配置化
|
||||||
|
|
||||||
|
新增配置,避免继续在代码里硬编码:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ebike:
|
||||||
|
auto-register:
|
||||||
|
enabled: true
|
||||||
|
merchant-id: 1
|
||||||
|
station-id: 2
|
||||||
|
model-id: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `enabled`:兜底开关,便于灰度和回滚。
|
||||||
|
- `merchant-id`:自动建档默认运营商。
|
||||||
|
- `station-id`:自动建档默认站点,建议使用“待配置设备站点”。
|
||||||
|
- `model-id`:默认电单车型号,必须配置成有效 `pile_model_info.id`。
|
||||||
|
|
||||||
|
不建议让 `model-id` 为空。否则后续查询端口详情时可能因为 inner join 型号表导致仍然查不到。
|
||||||
|
|
||||||
|
### 6. 自动建档数据加可追溯标记
|
||||||
|
|
||||||
|
建议自动创建时设置:
|
||||||
|
|
||||||
|
- `create_by = system`
|
||||||
|
- `remark = 自动建档:<source>; portNumber=<n>`
|
||||||
|
- 端口 `create_by = system`
|
||||||
|
|
||||||
|
如需更清晰,也可以后续增加独立字段,例如 `auto_register_flag`、`auto_register_source`,但这涉及数据库结构变更,本轮可以先用 `remark` 兜住。
|
||||||
|
|
||||||
|
### 7. 幂等和并发控制
|
||||||
|
|
||||||
|
心跳是高频数据,必须防止并发重复插入。
|
||||||
|
|
||||||
|
建议双层保护:
|
||||||
|
|
||||||
|
1. 应用层 Redis 锁:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ebike:auto_register:pile:{pileSn}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 使用 `setnx`。
|
||||||
|
- 锁过期时间建议 10-30 秒。
|
||||||
|
- 拿不到锁时直接跳过或短路查询。
|
||||||
|
|
||||||
|
2. 数据库唯一约束:
|
||||||
|
|
||||||
|
- `pile_basic_info.sn` 建议保证唯一。
|
||||||
|
- `pile_connector_info.pile_connector_code` 建议保证唯一。
|
||||||
|
|
||||||
|
如果当前线上表没有唯一索引,至少在代码里要做到先查再插,并捕获重复键异常;中长期建议补唯一约束。
|
||||||
|
|
||||||
|
### 8. 端口补齐策略
|
||||||
|
|
||||||
|
`ensureEBikePileRegistered` 不只处理“桩不存在”,也要处理“桩存在但端口缺失”。
|
||||||
|
|
||||||
|
建议规则:
|
||||||
|
|
||||||
|
- 桩不存在:创建桩,并创建 `01` 到 `portNumber` 的端口。
|
||||||
|
- 桩存在:查询已有端口,只补缺失端口。
|
||||||
|
- 如果后续心跳 `portNumber` 小于已有端口数:不删除端口,只打印 warn。
|
||||||
|
- 如果后续心跳 `portNumber` 大于已有端口数:补齐新增端口。
|
||||||
|
|
||||||
|
这样可以兼容设备端口数变化,也避免误删历史数据。
|
||||||
|
|
||||||
|
### 9. 补齐日志
|
||||||
|
|
||||||
|
建议在通用建档方法中新增统一日志关键词,便于线上排查:
|
||||||
|
|
||||||
|
```text
|
||||||
|
电单车自动建档-开始, pileSn:{}, portNumber:{}, source:{}
|
||||||
|
电单车自动建档-已存在, pileSn:{}, source:{}
|
||||||
|
电单车自动建档-创建成功, pileSn:{}, connectorCount:{}, source:{}
|
||||||
|
电单车自动建档-补齐端口, pileSn:{}, missingConnectors:{}, source:{}
|
||||||
|
电单车自动建档-跳过, pileSn:{}, reason:{}, source:{}
|
||||||
|
电单车自动建档-失败, pileSn:{}, source:{}
|
||||||
|
```
|
||||||
|
|
||||||
|
同时建议 `updateConnectorStatus()` 在 update 结果为 0 时打印 warn:
|
||||||
|
|
||||||
|
```text
|
||||||
|
更新枪口状态-未匹配到端口, pileConnectorCode:{}, status:{}
|
||||||
|
```
|
||||||
|
|
||||||
|
这样可以快速区分“设备有心跳但端口未建档”和“状态正常更新”。
|
||||||
|
|
||||||
|
## 需要修改的代码
|
||||||
|
|
||||||
|
### `jsowell-pile/src/main/java/com/jsowell/pile/service/PileBasicInfoService.java`
|
||||||
|
|
||||||
|
新增通用兜底建档接口:
|
||||||
|
|
||||||
|
- `ensureEBikePileRegistered(String pileSn, int portNumber, String source)`
|
||||||
|
|
||||||
|
保留现有:
|
||||||
|
|
||||||
|
- `registrationEBikePile(EBikeMessageCmd20 message)`
|
||||||
|
|
||||||
|
### `jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileBasicInfoServiceImpl.java`
|
||||||
|
|
||||||
|
主要改动:
|
||||||
|
|
||||||
|
- 抽取 `ensureEBikePileRegistered`。
|
||||||
|
- `registrationEBikePile` 内部改为调用通用方法。
|
||||||
|
- 增加 Redis 锁。
|
||||||
|
- 增加默认运营商、默认站点、默认型号配置读取。
|
||||||
|
- 支持桩存在时补齐缺失端口。
|
||||||
|
- 增加自动建档日志。
|
||||||
|
- 建档或补端口后清理相关缓存。
|
||||||
|
|
||||||
|
### `jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/RegistrationHandler.java`
|
||||||
|
|
||||||
|
主要改动:
|
||||||
|
|
||||||
|
- 保留原有注册包处理。
|
||||||
|
- 调用通用建档方法时传 `source = registration_0x20`。
|
||||||
|
- 保留现有 `设备注册包:{}` 日志。
|
||||||
|
|
||||||
|
### `jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/HeartbeatHandler.java`
|
||||||
|
|
||||||
|
主要改动:
|
||||||
|
|
||||||
|
- 在 `updatePileStatus(message)` 前调用 `ensureEBikePileRegistered`。
|
||||||
|
- 传 `source = heartbeat_0x21`。
|
||||||
|
- 如果兜底建档失败,需要打印 error,但不要影响设备心跳应答,避免设备因平台异常断链。
|
||||||
|
|
||||||
|
### `jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/PowerHeartbeatHandler.java`
|
||||||
|
|
||||||
|
可选改动:
|
||||||
|
|
||||||
|
- `0x06` 不作为完整建桩入口。
|
||||||
|
- 如果状态更新返回 0,可打印更明确日志。
|
||||||
|
- 如果基础桩存在但端口不存在,可调用端口补齐方法或复用 `ensureEBikePileRegistered` 的端口补齐能力。
|
||||||
|
|
||||||
|
### `jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileConnectorInfoServiceImpl.java`
|
||||||
|
|
||||||
|
建议改动:
|
||||||
|
|
||||||
|
- `updateConnectorStatus()` 获取 mapper update 返回值后,如果为 0,打印 warn。
|
||||||
|
- 保持返回值向上透出,方便 handler 判断是否需要兜底。
|
||||||
|
|
||||||
|
### `jsowell-admin/src/main/resources/application-*.yml`
|
||||||
|
|
||||||
|
建议新增配置:
|
||||||
|
|
||||||
|
- `ebike.auto-register.enabled`
|
||||||
|
- `ebike.auto-register.merchant-id`
|
||||||
|
- `ebike.auto-register.station-id`
|
||||||
|
- `ebike.auto-register.model-id`
|
||||||
|
|
||||||
|
不同环境可以使用不同默认站点和型号。
|
||||||
|
|
||||||
|
## 可能产生的后果
|
||||||
|
|
||||||
|
### 正向影响
|
||||||
|
|
||||||
|
- 不发送注册包但发送设备心跳的电单车,也能自动生成基础桩和端口数据。
|
||||||
|
- 心跳状态更新不再因为端口不存在而长期更新 0 行。
|
||||||
|
- 排查能力增强,可以从日志确认是否执行了自动建档和端口补齐。
|
||||||
|
- 减少“设备在线但后台查不到桩”的问题。
|
||||||
|
|
||||||
|
### 业务风险
|
||||||
|
|
||||||
|
- 自动创建的数据默认归属到配置站点和运营商,可能不是最终真实归属。
|
||||||
|
- 如果默认型号配置错误,可能导致端口类型、详情查询、计费判断异常。
|
||||||
|
- 自动建档不等于可运营;仍需要计费模板、站点开放状态、运营商归属等配置完整。
|
||||||
|
- 未配置设备可能进入后台列表,需要运营人员识别和处理。
|
||||||
|
|
||||||
|
### 数据风险
|
||||||
|
|
||||||
|
- 如果没有唯一约束,高并发心跳下可能重复插入。
|
||||||
|
- 设备上报的 `portNumber` 如果异常,可能创建过多端口。
|
||||||
|
- `0x21` 端口数变化时,只补不删,可能留下历史端口,需要人工核对。
|
||||||
|
|
||||||
|
### 安全风险
|
||||||
|
|
||||||
|
- 任何能连上电单车 netty 端口并伪造合法协议包的设备,都可能触发自动建档。
|
||||||
|
- 需要通过网络准入、桩号范围校验、默认待配置站点、后台审核等方式降低风险。
|
||||||
|
- 如果后续支持设备白名单,自动建档前应先校验白名单。
|
||||||
|
|
||||||
|
### 性能风险
|
||||||
|
|
||||||
|
- 心跳高频进入后,每次都查库会增加压力。
|
||||||
|
- 需要使用 Redis 锁和短期缓存降低重复建档查询。
|
||||||
|
- 日志要控制频率,避免每次心跳都打印“已存在”导致日志量过大。
|
||||||
|
|
||||||
|
## 验收方案
|
||||||
|
|
||||||
|
### 场景 1:新设备只发 0x21,不发 0x20
|
||||||
|
|
||||||
|
预期:
|
||||||
|
|
||||||
|
- 日志出现 `电单车自动建档-开始`。
|
||||||
|
- `pile_basic_info` 生成对应 `sn`。
|
||||||
|
- `pile_connector_info` 生成 `01` 到 `portNumber` 的端口。
|
||||||
|
- 随后的心跳状态能正常更新端口。
|
||||||
|
|
||||||
|
### 场景 2:设备正常发送 0x20
|
||||||
|
|
||||||
|
预期:
|
||||||
|
|
||||||
|
- 原有注册包建档逻辑不受影响。
|
||||||
|
- 日志 source 为 `registration_0x20`。
|
||||||
|
- 重复注册包不会创建重复数据。
|
||||||
|
|
||||||
|
### 场景 3:桩存在但端口缺失
|
||||||
|
|
||||||
|
预期:
|
||||||
|
|
||||||
|
- `ensureEBikePileRegistered` 只补缺失端口。
|
||||||
|
- 已存在端口不重复插入。
|
||||||
|
- 日志打印缺失端口列表。
|
||||||
|
|
||||||
|
### 场景 4:并发心跳
|
||||||
|
|
||||||
|
预期:
|
||||||
|
|
||||||
|
- Redis 锁生效。
|
||||||
|
- 只插入一条桩记录。
|
||||||
|
- 端口不重复。
|
||||||
|
|
||||||
|
### 场景 5:默认配置缺失或关闭自动建档
|
||||||
|
|
||||||
|
预期:
|
||||||
|
|
||||||
|
- `enabled = false` 时不自动创建,只打印跳过原因。
|
||||||
|
- `model-id` 未配置或无效时不创建可运营数据,打印 error 或 warn。
|
||||||
|
|
||||||
|
## 推荐实施顺序
|
||||||
|
|
||||||
|
1. 先补日志:让现有 `0x20`、`0x21`、状态 update 0 行可观测。
|
||||||
|
2. 抽取 `ensureEBikePileRegistered`,让 `0x20` 走新方法。
|
||||||
|
3. 加配置项和 Redis 锁。
|
||||||
|
4. 接入 `0x21` 兜底建档。
|
||||||
|
5. 增加端口补齐逻辑。
|
||||||
|
6. 小范围上线,观察自动建档日志和新增数据。
|
||||||
|
7. 再考虑 `0x06` 单端口补齐。
|
||||||
|
|
||||||
|
## 不建议本轮做的事情
|
||||||
|
|
||||||
|
- 不建议在扫码接口自动创建桩。
|
||||||
|
- 不建议在下单接口自动创建桩。
|
||||||
|
- 不建议 `model_id` 继续默认写 `null`。
|
||||||
|
- 不建议收到端口数变小后自动删除端口。
|
||||||
|
- 不建议没有开关、没有日志、没有锁就直接让心跳自动插库。
|
||||||
BIN
docs/充电桩协议/AP3000第二版-设备与服务器通信协议.docx
Normal file
BIN
docs/充电桩协议/AP3000第二版-设备与服务器通信协议.docx
Normal file
Binary file not shown.
BIN
docs/充电桩协议/AP3000第二版-设备与服务器通信协议v8.6.docx
Normal file
BIN
docs/充电桩协议/AP3000第二版-设备与服务器通信协议v8.6.docx
Normal file
Binary file not shown.
@@ -76,7 +76,7 @@ public class JumpController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
// 进入充电桩详情做一下鉴权
|
// 进入充电桩详情做一下鉴权
|
||||||
String memberId = getMemberIdByAuthorization(request);
|
String memberId = getMemberIdByAuthorization(request);
|
||||||
AppletPileDetailVO vo = pileService.getPileDetailByPileSn(pileSn);
|
AppletPileDetailVO vo = pileService.getPileDetailByPileSn(pileSn, memberId);
|
||||||
addMember2MemberGroup(memberId, vo);
|
addMember2MemberGroup(memberId, vo);
|
||||||
response = new RestApiResponse<>(vo);
|
response = new RestApiResponse<>(vo);
|
||||||
} catch (BusinessException e) {
|
} catch (BusinessException e) {
|
||||||
@@ -114,7 +114,7 @@ public class JumpController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
// 进入充电桩详情做一下鉴权
|
// 进入充电桩详情做一下鉴权
|
||||||
String memberId = getMemberIdByAuthorization(request);
|
String memberId = getMemberIdByAuthorization(request);
|
||||||
AppletPileDetailVO vo = pileService.getPileDetailByPileSn(pileSn);
|
AppletPileDetailVO vo = pileService.getPileDetailByPileSn(pileSn, memberId);
|
||||||
addMember2MemberGroup(memberId, vo);
|
addMember2MemberGroup(memberId, vo);
|
||||||
response = new RestApiResponse<>(vo);
|
response = new RestApiResponse<>(vo);
|
||||||
} catch (BusinessException e) {
|
} catch (BusinessException e) {
|
||||||
@@ -176,7 +176,7 @@ public class JumpController extends BaseController {
|
|||||||
memberId = getMemberIdByAuthorization(request);
|
memberId = getMemberIdByAuthorization(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppletPileDetailVO vo = pileService.getConnectorDetail(pileConnectorCode);
|
AppletPileDetailVO vo = pileService.getConnectorDetail(pileConnectorCode, memberId);
|
||||||
logger.info("查询充电枪口详情, pileConnectorCode:{}, vo:{}", pileConnectorCode, JSON.toJSONString(vo));
|
logger.info("查询充电枪口详情, pileConnectorCode:{}, vo:{}", pileConnectorCode, JSON.toJSONString(vo));
|
||||||
if (StringUtils.isNotBlank(memberId)) {
|
if (StringUtils.isNotBlank(memberId)) {
|
||||||
addMember2MemberGroup(memberId, vo);
|
addMember2MemberGroup(memberId, vo);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class JumpXixiaoController extends BaseController {
|
|||||||
logger.info("User-Agent:{}", request.getHeader("user-agent"));
|
logger.info("User-Agent:{}", request.getHeader("user-agent"));
|
||||||
RestApiResponse<?> response = null;
|
RestApiResponse<?> response = null;
|
||||||
try {
|
try {
|
||||||
AppletPileDetailVO vo = pileService.getPileDetailByPileSn(pileSn);
|
AppletPileDetailVO vo = pileService.getPileDetailByPileSn(pileSn, null);
|
||||||
response = new RestApiResponse<>(vo);
|
response = new RestApiResponse<>(vo);
|
||||||
} catch (BusinessException e) {
|
} catch (BusinessException e) {
|
||||||
logger.warn("app-xcx-h5查询充电桩详情 warn", e);
|
logger.warn("app-xcx-h5查询充电桩详情 warn", e);
|
||||||
@@ -65,7 +65,7 @@ public class JumpXixiaoController extends BaseController {
|
|||||||
logger.info("User-Agent:{}", request.getHeader("user-agent"));
|
logger.info("User-Agent:{}", request.getHeader("user-agent"));
|
||||||
RestApiResponse<?> response = null;
|
RestApiResponse<?> response = null;
|
||||||
try {
|
try {
|
||||||
AppletPileDetailVO vo = pileService.getConnectorDetail(pileConnectorCode);
|
AppletPileDetailVO vo = pileService.getConnectorDetail(pileConnectorCode, null);
|
||||||
response = new RestApiResponse<>(vo);
|
response = new RestApiResponse<>(vo);
|
||||||
} catch (BusinessException e) {
|
} catch (BusinessException e) {
|
||||||
logger.warn("app-xcx-h5查询充电枪口详情 warn param:{}", pileConnectorCode, e);
|
logger.warn("app-xcx-h5查询充电枪口详情 warn param:{}", pileConnectorCode, e);
|
||||||
|
|||||||
@@ -693,6 +693,9 @@ public class MemberService {
|
|||||||
if (StringUtils.isNotBlank(dto.getNickName())) {
|
if (StringUtils.isNotBlank(dto.getNickName())) {
|
||||||
memberBasicInfo.setNickName(dto.getNickName());
|
memberBasicInfo.setNickName(dto.getNickName());
|
||||||
}
|
}
|
||||||
|
if (StringUtils.isNotBlank(dto.getDefaultChargeAmount())) {
|
||||||
|
memberBasicInfo.setDefaultChargeAmount(new BigDecimal(dto.getDefaultChargeAmount()));
|
||||||
|
}
|
||||||
memberBasicInfoService.updateMemberBasicInfo(memberBasicInfo);
|
memberBasicInfoService.updateMemberBasicInfo(memberBasicInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ public class PileService {
|
|||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppletPileDetailVO getPileDetailByPileSn(String param) throws Exception {
|
public AppletPileDetailVO getPileDetailByPileSn(String param, String memberId) throws Exception {
|
||||||
AppletPileDetailVO vo = null;
|
AppletPileDetailVO vo = null;
|
||||||
log.info("查询充电枪口详情-getPileDetailByPileSn, param:{}", param);
|
log.info("查询充电枪口详情-getPileDetailByPileSn, param:{}", param);
|
||||||
if (StringUtils.isBlank(param)) {
|
if (StringUtils.isBlank(param)) {
|
||||||
@@ -253,6 +253,22 @@ public class PileService {
|
|||||||
throw new BusinessException(ReturnCodeEnum.CODE_STATION_IS_NOT_OPEN);
|
throw new BusinessException(ReturnCodeEnum.CODE_STATION_IS_NOT_OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断该桩是否为个人桩
|
||||||
|
if (StringUtils.isNotBlank(memberId)) {
|
||||||
|
List<MemberVO> memberVOS = pileMemberRelationService.selectMemberList(pileSn);
|
||||||
|
if (CollectionUtils.isNotEmpty(memberVOS)) {
|
||||||
|
// 说明为个人桩, 判断会员信息
|
||||||
|
// 将memberId收集成list
|
||||||
|
List<String> memberIdList = memberVOS.stream()
|
||||||
|
.map(MemberVO::getMemberId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
// 如果会员没有绑定该桩,说明没有权限进行操作
|
||||||
|
if (!memberIdList.contains(memberId)) {
|
||||||
|
throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_NOT_HAVE_PILE_PERMISSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 查询充电桩下枪口信息
|
// 查询充电桩下枪口信息
|
||||||
CompletableFuture<List<ConnectorInfoVO>> connectorInfoListFuture = CompletableFuture.supplyAsync(() -> pileConnectorInfoService.selectConnectorInfoList(pileSn), executor);
|
CompletableFuture<List<ConnectorInfoVO>> connectorInfoListFuture = CompletableFuture.supplyAsync(() -> pileConnectorInfoService.selectConnectorInfoList(pileSn), executor);
|
||||||
// log.info("查询充电枪口详情-supplyAsync-selectConnectorInfoList:{}", connectorInfoListFuture);
|
// log.info("查询充电枪口详情-supplyAsync-selectConnectorInfoList:{}", connectorInfoListFuture);
|
||||||
@@ -299,7 +315,7 @@ public class PileService {
|
|||||||
* @throws ExecutionException
|
* @throws ExecutionException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public AppletPileDetailVO getConnectorDetail(String pileConnectorCode) throws Exception {
|
public AppletPileDetailVO getConnectorDetail(String pileConnectorCode, String memberId) throws Exception {
|
||||||
log.info("查询充电枪口详情, pileConnectorCode:{}", pileConnectorCode);
|
log.info("查询充电枪口详情, pileConnectorCode:{}", pileConnectorCode);
|
||||||
PileConnectorDetailVO pileConnectorDetailVO = queryPileConnectorDetail(pileConnectorCode);
|
PileConnectorDetailVO pileConnectorDetailVO = queryPileConnectorDetail(pileConnectorCode);
|
||||||
log.info("查询充电枪口详情, pileConnectorDetailVO:{}", JSON.toJSONString(pileConnectorDetailVO));
|
log.info("查询充电枪口详情, pileConnectorDetailVO:{}", JSON.toJSONString(pileConnectorDetailVO));
|
||||||
@@ -314,7 +330,7 @@ public class PileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
String pileSn = pileConnectorDetailVO.getPileSn();
|
String pileSn = pileConnectorDetailVO.getPileSn();
|
||||||
AppletPileDetailVO resultVO = getPileDetailByPileSn(pileSn);
|
AppletPileDetailVO resultVO = getPileDetailByPileSn(pileSn, memberId);
|
||||||
log.info("查询充电枪口详情getConnectorDetail, pileSn:{}, pileConnectorDetailVO:{}, resultVO:{}", pileSn, JSON.toJSONString(pileConnectorDetailVO), JSON.toJSONString(resultVO));
|
log.info("查询充电枪口详情getConnectorDetail, pileSn:{}, pileConnectorDetailVO:{}, resultVO:{}", pileSn, JSON.toJSONString(pileConnectorDetailVO), JSON.toJSONString(resultVO));
|
||||||
List<ConnectorInfoVO> connectorInfoList = resultVO.getConnectorInfoList();
|
List<ConnectorInfoVO> connectorInfoList = resultVO.getConnectorInfoList();
|
||||||
if (connectorInfoList.size() > 1 && !StringUtils.equals(pileConnectorDetailVO.getChargePortType(), "3")) {
|
if (connectorInfoList.size() > 1 && !StringUtils.equals(pileConnectorDetailVO.getChargePortType(), "3")) {
|
||||||
@@ -332,7 +348,7 @@ public class PileService {
|
|||||||
return resultVO;
|
return resultVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppletPileDetailVO getConnectorDetailV2(String pileConnectorCode) throws Exception {
|
public AppletPileDetailVO getConnectorDetailV2(String pileConnectorCode, String memberId) throws Exception {
|
||||||
log.info("查询充电枪口详情, pileConnectorCode:{}", pileConnectorCode);
|
log.info("查询充电枪口详情, pileConnectorCode:{}", pileConnectorCode);
|
||||||
PileConnectorDetailVO pileConnectorDetailVO = queryPileConnectorDetail(pileConnectorCode);
|
PileConnectorDetailVO pileConnectorDetailVO = queryPileConnectorDetail(pileConnectorCode);
|
||||||
log.info("查询充电枪口详情, pileConnectorDetailVO:{}", JSON.toJSONString(pileConnectorDetailVO));
|
log.info("查询充电枪口详情, pileConnectorDetailVO:{}", JSON.toJSONString(pileConnectorDetailVO));
|
||||||
@@ -347,7 +363,7 @@ public class PileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
String pileSn = pileConnectorDetailVO.getPileSn();
|
String pileSn = pileConnectorDetailVO.getPileSn();
|
||||||
AppletPileDetailVO resultVO = getPileDetailByPileSn(pileSn);
|
AppletPileDetailVO resultVO = getPileDetailByPileSn(pileSn, memberId);
|
||||||
log.info("查询充电枪口详情getConnectorDetail, pileSn:{}, pileConnectorDetailVO:{}, resultVO:{}", pileSn, JSON.toJSONString(pileConnectorDetailVO), JSON.toJSONString(resultVO));
|
log.info("查询充电枪口详情getConnectorDetail, pileSn:{}, pileConnectorDetailVO:{}, resultVO:{}", pileSn, JSON.toJSONString(pileConnectorDetailVO), JSON.toJSONString(resultVO));
|
||||||
List<ConnectorInfoVO> connectorInfoList = resultVO.getConnectorInfoList();
|
List<ConnectorInfoVO> connectorInfoList = resultVO.getConnectorInfoList();
|
||||||
if (connectorInfoList.size() > 1 && !StringUtils.equals(pileConnectorDetailVO.getChargePortType(), "3")) {
|
if (connectorInfoList.size() > 1 && !StringUtils.equals(pileConnectorDetailVO.getChargePortType(), "3")) {
|
||||||
|
|||||||
@@ -103,6 +103,17 @@ pagehelper:
|
|||||||
supportMethodsArguments: true
|
supportMethodsArguments: true
|
||||||
params: count=countSql
|
params: count=countSql
|
||||||
|
|
||||||
|
# 电单车自动建档配置
|
||||||
|
ebike:
|
||||||
|
auto-register:
|
||||||
|
# 设备未上报注册包时,允许通过0x21心跳兜底创建桩和端口基础数据
|
||||||
|
enabled: true
|
||||||
|
# 默认归属信息建议配置为“待配置设备”运营商/站点,后续由后台人工调整
|
||||||
|
merchant-id: 1
|
||||||
|
station-id: 2
|
||||||
|
# 必须按环境配置为有效的电单车型号ID;0表示未配置,仍会创建基础数据但详情查询可能受影响
|
||||||
|
model-id: 0
|
||||||
|
|
||||||
# 防止XSS攻击
|
# 防止XSS攻击
|
||||||
xss:
|
xss:
|
||||||
# 过滤开关
|
# 过滤开关
|
||||||
@@ -202,4 +213,4 @@ sms:
|
|||||||
# 是否启用代理 默认关闭 需手动开启
|
# 是否启用代理 默认关闭 需手动开启
|
||||||
enable: false
|
enable: false
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|||||||
@@ -1,188 +1 @@
|
|||||||
002212025121618122710846946041776041984
|
002212026052916015810906344811847229440
|
||||||
002212025123013543010851954554563244032
|
|
||||||
002212026010104322810852537890272755712
|
|
||||||
002212025112522073210839395053787557888
|
|
||||||
002212025120720413410843722075120734208
|
|
||||||
002212026010918332710855648635420176384
|
|
||||||
002212026012207545810860198996873125888
|
|
||||||
002212026010910374210855528908832141312
|
|
||||||
002212025111709485410836310070675070976
|
|
||||||
002212025112816413110840400174906601472
|
|
||||||
002212026013017454210863246761760948224
|
|
||||||
002212025113022210510841210404460548096
|
|
||||||
002212025111316581810834968580149411840
|
|
||||||
002212026012211165810860249831925686272
|
|
||||||
002212026010200085610852833959422513152
|
|
||||||
002212026012118555110860002926394146816
|
|
||||||
002212025121209334710845365961304027136
|
|
||||||
002212026012017103710859614052845150208
|
|
||||||
002212025120319535010842260512643993600
|
|
||||||
002212025121504451010846380494055161856
|
|
||||||
002212025120119313110841530122060828672
|
|
||||||
002212025122523192310850284773435035648
|
|
||||||
002212026013017394110863245248292532224
|
|
||||||
002212026012705333310861975346717986816
|
|
||||||
002212026012009102510859493208189870080
|
|
||||||
002212026012819171110862545009394896896
|
|
||||||
002212026020113151710863903484339363840
|
|
||||||
002212026010614041510854493722241695744
|
|
||||||
002212025122717154010850918016471597056
|
|
||||||
002212026010415405910853793291316260865
|
|
||||||
002212025120313035610842157357671374848
|
|
||||||
002212025120405501810842410617678213120
|
|
||||||
002212025100710292810821462377338449920
|
|
||||||
002212025090513433510809914813501046784
|
|
||||||
002212025121308145310845708494798307328
|
|
||||||
002212025102800250710828920431341121536
|
|
||||||
002212026011209350810856600323798896640
|
|
||||||
002212025093019381810819063779938525184
|
|
||||||
002212026012914130110862830851761950720
|
|
||||||
002212025102723251810828905377354887168
|
|
||||||
002212026011416122610857425084242272256
|
|
||||||
002212025103123200410830353610908577792
|
|
||||||
002212025092723331010818035721160736768
|
|
||||||
002212025112019113210837538826677252096
|
|
||||||
002212026010819084710855295138321465344
|
|
||||||
002212025102319241310827395154634309632
|
|
||||||
002212025102718533110828836980470480896
|
|
||||||
002212025121811351210847570843389894656
|
|
||||||
002212025112811395410840324268695461888
|
|
||||||
002212026011315270810857051297583755264
|
|
||||||
002212026011508300810857671132671950848
|
|
||||||
002212025121818501810847680341601288192
|
|
||||||
002212025122012235910848307895332904960
|
|
||||||
002212025111214424910834572096455241728
|
|
||||||
002212025101503332310824256769765740544
|
|
||||||
002212025101014122910822605665181626368
|
|
||||||
002212025123111295910852280573656604672
|
|
||||||
002212025122912321410851571464460337152
|
|
||||||
002212025122419071810849858946427277312
|
|
||||||
002212025121507533010846427888914513920
|
|
||||||
002212026011820155110858935895485870080
|
|
||||||
002212025121716382410847284757996490752
|
|
||||||
002212025102410154910827619533163728896
|
|
||||||
002212025111019480710833924154224484352
|
|
||||||
002212026012519531010861466899182305280
|
|
||||||
002212025121912515310847952530791710720
|
|
||||||
002212025091911133110814950481046421504
|
|
||||||
002212026010522171010854255381420228608
|
|
||||||
002212025121013552310844707021574512640
|
|
||||||
002212025091415572210813209974666567680
|
|
||||||
002212025111721214310836484421810593792
|
|
||||||
002212025090119241010808550972619939840
|
|
||||||
002212026012607395310861644753064984576
|
|
||||||
002212025090412412210809536768898379776
|
|
||||||
002212026011921282510859316542855516160
|
|
||||||
002212025092118550910815791429036949504
|
|
||||||
002212025121713242610847235944673849344
|
|
||||||
002212025092920124510818710059610238976
|
|
||||||
002212025101309242410823620330208473088
|
|
||||||
002212025090618454910810353263698137088
|
|
||||||
002212025111818080810836798092730814464
|
|
||||||
002212025110621300210832500248041185280
|
|
||||||
002212025122311324710849382174300438528
|
|
||||||
002212026012722323510862231793603231744
|
|
||||||
002212026010220060610853135235943292928
|
|
||||||
002212025111623214410836152237925052416
|
|
||||||
002212025110913170210833463344691904512
|
|
||||||
002212025112217360210838239567226093568
|
|
||||||
002212025111920191110837193462170267648
|
|
||||||
002212025102816240010829161740781883393
|
|
||||||
002212025101313554710823688625817227264
|
|
||||||
002212025092518534110817240611673935872
|
|
||||||
002212025120423191310842674587056205824
|
|
||||||
002212025102516531010828081920610299904
|
|
||||||
002212025101720434410825240839836921856
|
|
||||||
002212025101918085910825926669907476480
|
|
||||||
002212026012419092410861093500920561664
|
|
||||||
002212025091219531510812544559669997568
|
|
||||||
002212025123008312910851873263692075008
|
|
||||||
002212025100221475910819821189846122496
|
|
||||||
002212025112511232110839232941986975744
|
|
||||||
002212025121919483610848057399154188288
|
|
||||||
002212025092214454610816091056525180928
|
|
||||||
002212026020119090710863992529354534912
|
|
||||||
002212025111515305510835671363391528960
|
|
||||||
002212025112317000010838592887014834176
|
|
||||||
002212026010508493510854052147955040256
|
|
||||||
002212026010518415810854201223866535936
|
|
||||||
002212026011522191510857879786327154688
|
|
||||||
002212025091513024210813528407530708992
|
|
||||||
002212025103018083310829912829399547904
|
|
||||||
002212026011513542310857752731270836224
|
|
||||||
002212025100414103310820430849850728448
|
|
||||||
002212026012714242210862108933408727040
|
|
||||||
002212025121418303510846225829766238208
|
|
||||||
002212025100422201910820554104661979136
|
|
||||||
002212025122216352810849095961576468480
|
|
||||||
002212025091216301410812493470157410304
|
|
||||||
002212025110309523010831237547117752320
|
|
||||||
002212026011414155910857395779063824384
|
|
||||||
002212025110812523510833094804327608320
|
|
||||||
002212025092322552810816576681653223424
|
|
||||||
002212026010117005410852726238882861056
|
|
||||||
002212025091007290010811632489596358656
|
|
||||||
002212025100217134710819752185085255680
|
|
||||||
002212025101118563210823039533252460544
|
|
||||||
002212026010409041510853693449663447040
|
|
||||||
002212025100914052510822241499388157952
|
|
||||||
002212025100916430710822281184114229248
|
|
||||||
002212025122514361110850153105390280704
|
|
||||||
002212025101818103810825564697454292992
|
|
||||||
002212026010711420210854820320350359552
|
|
||||||
002212025111714341610836381885107752960
|
|
||||||
002212025110518584710832099797764382720
|
|
||||||
002212026010311552410853374135496413184
|
|
||||||
002212025122608430110850426617083006976
|
|
||||||
002212025122816133210851264768693276672
|
|
||||||
002212026020314034410864640454610989056
|
|
||||||
002212025120219364610841893827348922368
|
|
||||||
002212025120822291510844111562225205248
|
|
||||||
002212025100711442310821481231221641216
|
|
||||||
002212025100815190010821897628623638528
|
|
||||||
002212025092910324910818564115468087296
|
|
||||||
002212025121418490310846230476820201472
|
|
||||||
002212026010523424310854276910094815232
|
|
||||||
002212025100422130310820552276624748544
|
|
||||||
002212025112109154310837751268539756544
|
|
||||||
002212026011813062010858827804463374336
|
|
||||||
002212025091019372610811815805750501376
|
|
||||||
002212025121311371710845759431650816000
|
|
||||||
002212025092221314710816193237265862656
|
|
||||||
002212025122408410510849701355378061312
|
|
||||||
002212025112221494110838303398304419840
|
|
||||||
002212026011214551110856680867706507264
|
|
||||||
002212025123107215410852218142670508032
|
|
||||||
002212025083119251510808188859560914944
|
|
||||||
002212025092308403010816361524222107648
|
|
||||||
002212025121118084510845133170435940352
|
|
||||||
002212025091209403510812390376895553536
|
|
||||||
002212025112518515910839345842035605504
|
|
||||||
002212025101212002610823297209891024896
|
|
||||||
002212025112618380410839704729997799424
|
|
||||||
002212025091813145810814618655161491456
|
|
||||||
002212026011709375210858412953370951680
|
|
||||||
002212025112704025110839846863925022720
|
|
||||||
002212026011113272010856296374202064896
|
|
||||||
002212025102923231610829629641771081728
|
|
||||||
002212025110408044010831572796683010048
|
|
||||||
002212026012218400210860361332279767040
|
|
||||||
002212026010707244110854755555972276224
|
|
||||||
002212025092900192010818409727185489920
|
|
||||||
002212026012619023510861816557700186112
|
|
||||||
002212025102302461410827144007524765696
|
|
||||||
002212026012705214410861972373845626880
|
|
||||||
002212025121113521510845068621057875968
|
|
||||||
002212025112408354410838828369879498752
|
|
||||||
002212025123120392310852418836375592960
|
|
||||||
002212025122219151210849136159274299392
|
|
||||||
002212025092507595610817076088643436544
|
|
||||||
002212025122413121010849769573971103744
|
|
||||||
002212025121319505510845883657896472576
|
|
||||||
002212025120709385910843555330682667009
|
|
||||||
002212025122113262310848685986643464192
|
|
||||||
002212026011616174010858151177270140928
|
|
||||||
002212025091618584210813980384911839232
|
|
||||||
002212025102011233010826187014639308800
|
|
||||||
002212025121601225210846691971442135040
|
|
||||||
002212025090523352110810063740095320064
|
|
||||||
@@ -199,6 +199,8 @@ public enum ReturnCodeEnum {
|
|||||||
|
|
||||||
CODE_ALREADY_AN_USER("00400024", "您已经是此充电桩用户, 无需再次绑定"),
|
CODE_ALREADY_AN_USER("00400024", "您已经是此充电桩用户, 无需再次绑定"),
|
||||||
|
|
||||||
|
CODE_MEMBER_NOT_HAVE_PILE_PERMISSION("00700004", "您没有此桩的权限!"),
|
||||||
|
|
||||||
/* 个人桩 end */
|
/* 个人桩 end */
|
||||||
|
|
||||||
CODE_THIS_CARNO_HAS_BEEN_BINDING("00500001", "当前车牌号已经绑定,请检查!"),
|
CODE_THIS_CARNO_HAS_BEEN_BINDING("00500001", "当前车牌号已经绑定,请检查!"),
|
||||||
@@ -231,8 +233,7 @@ public enum ReturnCodeEnum {
|
|||||||
|
|
||||||
CODE_THIS_VIN_HAS_BEEN_BINDING("00700002", "该vin已被绑定,请检查!"),
|
CODE_THIS_VIN_HAS_BEEN_BINDING("00700002", "该vin已被绑定,请检查!"),
|
||||||
|
|
||||||
CODE_THIS_VIN_INFO_IS_NULL("007000003", "未查到该vin信息!"),
|
CODE_THIS_VIN_INFO_IS_NULL("007000003", "未查到该vin信息!");
|
||||||
;
|
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
|
|||||||
@@ -118,21 +118,23 @@ public class YouDianProtocolDecoder extends ByteToMessageDecoder {
|
|||||||
ByteBuf frame = null;
|
ByteBuf frame = null;
|
||||||
try {
|
try {
|
||||||
// 检查剩余数据是否足够
|
// 检查剩余数据是否足够
|
||||||
if (buffer.readableBytes() < HEADER_LENGTH_DNY + 1) {
|
if (buffer.readableBytes() < HEADER_LENGTH_DNY + 2) {
|
||||||
buffer.readerIndex(beginReader);
|
buffer.readerIndex(beginReader);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取消息长度
|
// DNY协议长度域为2字节,小端模式。长度不包含包头和长度域本身。
|
||||||
int length = buffer.getUnsignedByte(beginReader + HEADER_LENGTH_DNY);
|
int length = buffer.getUnsignedByte(beginReader + HEADER_LENGTH_DNY)
|
||||||
|
| (buffer.getUnsignedByte(beginReader + HEADER_LENGTH_DNY + 1) << 8);
|
||||||
// log.info("获取消息长度, length:{}", length);
|
// log.info("获取消息长度, length:{}", length);
|
||||||
|
int frameLength = HEADER_LENGTH_DNY + 2 + length;
|
||||||
// 检查剩余数据是否足够
|
// 检查剩余数据是否足够
|
||||||
if (buffer.readableBytes() < HEADER_LENGTH_DNY + 1 + length) {
|
if (buffer.readableBytes() < frameLength) {
|
||||||
buffer.readerIndex(beginReader);
|
buffer.readerIndex(beginReader);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 读取 data 数据
|
// 读取 data 数据
|
||||||
frame = buffer.retainedSlice(beginReader, HEADER_LENGTH_DNY + length + 2);
|
frame = buffer.retainedSlice(beginReader, frameLength);
|
||||||
buffer.readerIndex(beginReader + HEADER_LENGTH_DNY + length + 2);
|
buffer.readerIndex(beginReader + frameLength);
|
||||||
out.add(frame);
|
out.add(frame);
|
||||||
} finally {
|
} finally {
|
||||||
// if (frame != null) {
|
// if (frame != null) {
|
||||||
|
|||||||
@@ -47,9 +47,15 @@ public class HeartbeatHandler extends AbstractEBikeHandler {
|
|||||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||||
// 解析字节数组
|
// 解析字节数组
|
||||||
EBikeMessageCmd21 eBikeMessageCmd21 = new EBikeMessageCmd21(dataProtocol.getBytes());
|
EBikeMessageCmd21 eBikeMessageCmd21 = new EBikeMessageCmd21(dataProtocol.getBytes());
|
||||||
|
String pileSn = eBikeMessageCmd21.getPhysicalId() + "";
|
||||||
// 保存时间
|
// 保存时间
|
||||||
saveLastTimeAndCheckChannel(eBikeMessageCmd21.getPhysicalId() + "", ctx);
|
saveLastTimeAndCheckChannel(pileSn, ctx);
|
||||||
log.info("设备心跳包:{}", JSON.toJSONString(eBikeMessageCmd21));
|
log.info("设备心跳包:{}", JSON.toJSONString(eBikeMessageCmd21));
|
||||||
|
try {
|
||||||
|
pileBasicInfoService.ensureEBikePileRegistered(pileSn, eBikeMessageCmd21.getPortNumber(), "heartbeat_0x21");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("电单车心跳兜底建档失败, pileSn:{}, portNumber:{}", pileSn, eBikeMessageCmd21.getPortNumber(), e);
|
||||||
|
}
|
||||||
// 更新充电桩状态
|
// 更新充电桩状态
|
||||||
updatePileStatus(eBikeMessageCmd21);
|
updatePileStatus(eBikeMessageCmd21);
|
||||||
return getResult(dataProtocol, Constants.zeroByteArray);
|
return getResult(dataProtocol, Constants.zeroByteArray);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.jsowell.netty.handler.electricbicycles;
|
package com.jsowell.netty.handler.electricbicycles;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.jsowell.common.YouDianUtils;
|
||||||
import com.jsowell.common.constant.Constants;
|
import com.jsowell.common.constant.Constants;
|
||||||
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
||||||
|
import com.jsowell.common.util.BytesUtil;
|
||||||
import com.jsowell.common.util.YKCUtils;
|
import com.jsowell.common.util.YKCUtils;
|
||||||
import com.jsowell.netty.factory.EBikeOperateFactory;
|
import com.jsowell.netty.factory.EBikeOperateFactory;
|
||||||
import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
|
import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
|
||||||
@@ -39,12 +41,46 @@ public class RegistrationHandler extends AbstractEBikeHandler {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||||
// 解析字节数组
|
EBikeMessageCmd20 message;
|
||||||
EBikeMessageCmd20 message = new EBikeMessageCmd20(dataProtocol.getBytes());
|
try {
|
||||||
|
// 解析字节数组
|
||||||
|
message = new EBikeMessageCmd20(dataProtocol.getBytes());
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleRegistrationParseError(dataProtocol, ctx, e);
|
||||||
|
return getResult(dataProtocol, Constants.zeroByteArray);
|
||||||
|
}
|
||||||
|
|
||||||
// 保存时间
|
// 保存时间
|
||||||
saveLastTimeAndCheckChannel(message.getPhysicalId() + "", ctx);
|
saveLastTimeAndCheckChannel(message.getPhysicalId() + "", ctx);
|
||||||
log.info("设备注册包:{}", JSON.toJSONString(message));
|
log.info("设备注册包:{}", JSON.toJSONString(message));
|
||||||
pileBasicInfoService.registrationEBikePile(message);
|
try {
|
||||||
|
pileBasicInfoService.registrationEBikePile(message);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("设备注册包自动建档失败, pileSn:{}, portNumber:{}, msg:{}",
|
||||||
|
message.getPhysicalId(), message.getPortNumber(), BytesUtil.binary(dataProtocol.getBytes(), 16), e);
|
||||||
|
}
|
||||||
return getResult(dataProtocol, Constants.zeroByteArray);
|
return getResult(dataProtocol, Constants.zeroByteArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleRegistrationParseError(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx, Exception e) {
|
||||||
|
String pileSn = null;
|
||||||
|
int portNumber = 0;
|
||||||
|
try {
|
||||||
|
pileSn = YouDianUtils.convertToPhysicalId(dataProtocol.getPhysicalId()) + "";
|
||||||
|
byte[] msgBody = dataProtocol.getMsgBody();
|
||||||
|
if (msgBody != null && msgBody.length >= 3) {
|
||||||
|
portNumber = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(msgBody, 2, 1));
|
||||||
|
}
|
||||||
|
saveLastTimeAndCheckChannel(pileSn, ctx);
|
||||||
|
log.error("设备注册包解析失败, pileSn:{}, portNumber:{}, msg:{}, msgBodyLength:{}",
|
||||||
|
pileSn, portNumber, BytesUtil.binary(dataProtocol.getBytes(), 16),
|
||||||
|
msgBody == null ? 0 : msgBody.length, e);
|
||||||
|
if (portNumber > 0) {
|
||||||
|
pileBasicInfoService.ensureEBikePileRegistered(pileSn, portNumber, "registration_0x20_parse_fallback");
|
||||||
|
}
|
||||||
|
} catch (Exception fallbackException) {
|
||||||
|
log.error("设备注册包解析失败后兜底处理失败, pileSn:{}, portNumber:{}, msg:{}",
|
||||||
|
pileSn, portNumber, BytesUtil.binary(dataProtocol.getBytes(), 16), fallbackException);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -346,7 +346,11 @@ public class ConfirmStartChargingRequestHandler extends AbstractYkcHandler {
|
|||||||
}
|
}
|
||||||
dto.setConnectorCode(connectorCode);
|
dto.setConnectorCode(connectorCode);
|
||||||
dto.setTransactionCode(transactionCode);
|
dto.setTransactionCode(transactionCode);
|
||||||
dto.setChargeAmount(new BigDecimal(accountBalance));
|
if (memberWalletVO.getDefaultChargeAmount() != null && memberWalletVO.getDefaultChargeAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
dto.setChargeAmount(memberWalletVO.getDefaultChargeAmount());
|
||||||
|
}else {
|
||||||
|
dto.setChargeAmount(new BigDecimal(accountBalance));
|
||||||
|
}
|
||||||
dto.setPayMode(payMode);
|
dto.setPayMode(payMode);
|
||||||
dto.setStartMode(StringUtils.equals("01", startMode) ? "2" : "5");
|
dto.setStartMode(StringUtils.equals("01", startMode) ? "2" : "5");
|
||||||
dto.setMemberId(memberWalletVO.getMemberId());
|
dto.setMemberId(memberWalletVO.getMemberId());
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ public class MemberBasicInfo extends BaseEntity {
|
|||||||
@Excel(name = "所属运营商")
|
@Excel(name = "所属运营商")
|
||||||
private Long merchantId;
|
private Long merchantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认充电金额
|
||||||
|
*/
|
||||||
|
@Excel(name = "默认充电金额")
|
||||||
|
private BigDecimal defaultChargeAmount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除标识(0-正常;1-删除)
|
* 删除标识(0-正常;1-删除)
|
||||||
*/
|
*/
|
||||||
@@ -111,6 +117,7 @@ public class MemberBasicInfo extends BaseEntity {
|
|||||||
.append("avatarUrl", avatarUrl)
|
.append("avatarUrl", avatarUrl)
|
||||||
.append("mobileNumber", mobileNumber)
|
.append("mobileNumber", mobileNumber)
|
||||||
.append("merchantId", merchantId)
|
.append("merchantId", merchantId)
|
||||||
|
.append("defaultChargeAmount", defaultChargeAmount)
|
||||||
.append("delFlag", delFlag)
|
.append("delFlag", delFlag)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import java.util.Arrays;
|
|||||||
@Builder
|
@Builder
|
||||||
@ToString
|
@ToString
|
||||||
public class AbsEBikeMessage2 {
|
public class AbsEBikeMessage2 {
|
||||||
|
protected static final int DATA_START_INDEX = 12;
|
||||||
|
|
||||||
protected String header; // 包头 (3字节)
|
protected String header; // 包头 (3字节)
|
||||||
protected int msgLength; // 长度 (2字节)
|
protected int msgLength; // 长度 (2字节)
|
||||||
protected int physicalId; // 物理ID (4字节)
|
protected int physicalId; // 物理ID (4字节)
|
||||||
@@ -59,7 +61,7 @@ public class AbsEBikeMessage2 {
|
|||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 1;
|
length = 1;
|
||||||
byte[] commandBytes = BytesUtil.copyBytes(messageBytes, startIndex, length);
|
byte[] commandBytes = BytesUtil.copyBytes(messageBytes, startIndex, length);
|
||||||
this.command = BytesUtil.bcd2StrLittle(commandBytes);
|
this.command = BytesUtil.printHexBinary(commandBytes);
|
||||||
|
|
||||||
// 读取数据, 暂不处理, 交给子类处理
|
// 读取数据, 暂不处理, 交给子类处理
|
||||||
// byte[] dataBytes = BytesUtil.copyBytes(messageBytes, startIndex, length);
|
// byte[] dataBytes = BytesUtil.copyBytes(messageBytes, startIndex, length);
|
||||||
@@ -72,4 +74,26 @@ public class AbsEBikeMessage2 {
|
|||||||
public byte[] getMessageBytes() {
|
public byte[] getMessageBytes() {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected int getBodyLength() {
|
||||||
|
return this.msgLength - 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getDataEndIndex(byte[] messageBytes) {
|
||||||
|
return messageBytes.length - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasDataBytes(byte[] messageBytes, int startIndex, int length) {
|
||||||
|
return length >= 0
|
||||||
|
&& startIndex >= DATA_START_INDEX
|
||||||
|
&& startIndex + length <= getDataEndIndex(messageBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getExtendedDataHex(byte[] messageBytes, int startIndex) {
|
||||||
|
int extendedLength = getDataEndIndex(messageBytes) - startIndex;
|
||||||
|
if (extendedLength <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return BytesUtil.printHexBinary(BytesUtil.copyBytes(messageBytes, startIndex, extendedLength));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class EBikeMessageCmd02 extends AbsEBikeMessage2 {
|
|||||||
/**
|
/**
|
||||||
* 时间戳
|
* 时间戳
|
||||||
*/
|
*/
|
||||||
// private String timestamp;
|
private String timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卡号2字节数
|
* 卡号2字节数
|
||||||
@@ -51,7 +51,7 @@ public class EBikeMessageCmd02 extends AbsEBikeMessage2 {
|
|||||||
public EBikeMessageCmd02(byte[] messageBytes) {
|
public EBikeMessageCmd02(byte[] messageBytes) {
|
||||||
super(messageBytes);
|
super(messageBytes);
|
||||||
|
|
||||||
int startIndex = 12;
|
int startIndex = DATA_START_INDEX;
|
||||||
int length = 4;
|
int length = 4;
|
||||||
this.cardId = BytesUtil.bcd2Str(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
this.cardId = BytesUtil.bcd2Str(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
||||||
|
|
||||||
@@ -67,15 +67,20 @@ public class EBikeMessageCmd02 extends AbsEBikeMessage2 {
|
|||||||
length = 2;
|
length = 2;
|
||||||
this.cardBalance = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
this.cardBalance = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
||||||
|
|
||||||
// length = 4;
|
startIndex += length;
|
||||||
// this.timestamp = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, startIndex, startIndex = startIndex + length)) + "";
|
length = 4;
|
||||||
|
if (hasDataBytes(messageBytes, startIndex, length)) {
|
||||||
if (messageBytes.length > startIndex) {
|
this.timestamp = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 1;
|
}
|
||||||
card2Length = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
|
||||||
|
|
||||||
this.card2Code = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
length = 1;
|
||||||
|
if (hasDataBytes(messageBytes, startIndex, length)) {
|
||||||
|
card2Length = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
if (card2Length > 0 && hasDataBytes(messageBytes, startIndex, card2Length)) {
|
||||||
|
this.card2Code = BytesUtil.printHexBinary(BytesUtil.copyBytes(messageBytes, startIndex, card2Length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class EBikeMessageCmd06 extends AbsEBikeMessage2 {
|
|||||||
/**
|
/**
|
||||||
* 上发指令当时的时间,有时候不准确,该字段属于调试使用,服务器无需关心此字段
|
* 上发指令当时的时间,有时候不准确,该字段属于调试使用,服务器无需关心此字段
|
||||||
*/
|
*/
|
||||||
// private String timestamp;
|
private String timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 占位时长:(充电柜专用,其他设备忽略此字段)表示充满后占用设备的时长,单位为分钟
|
* 占位时长:(充电柜专用,其他设备忽略此字段)表示充满后占用设备的时长,单位为分钟
|
||||||
@@ -108,8 +108,9 @@ public class EBikeMessageCmd06 extends AbsEBikeMessage2 {
|
|||||||
|
|
||||||
public EBikeMessageCmd06(byte[] messageBytes) {
|
public EBikeMessageCmd06(byte[] messageBytes) {
|
||||||
super(messageBytes);
|
super(messageBytes);
|
||||||
|
int dataEndIndex = getDataEndIndex(messageBytes);
|
||||||
|
|
||||||
int startIndex = 12;
|
int startIndex = DATA_START_INDEX;
|
||||||
int length = 1;
|
int length = 1;
|
||||||
this.connectorCode = YouDianUtils.convertPortNumberToString(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)));
|
this.connectorCode = YouDianUtils.convertPortNumberToString(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)));
|
||||||
|
|
||||||
@@ -160,31 +161,63 @@ public class EBikeMessageCmd06 extends AbsEBikeMessage2 {
|
|||||||
|
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 2;
|
length = 2;
|
||||||
this.peakPower = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
if (hasPeakPower(dataEndIndex - startIndex) && hasBytes(messageBytes, startIndex, length)) {
|
||||||
.multiply(new BigDecimal("0.1")).toString();
|
this.peakPower = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
||||||
|
.multiply(new BigDecimal("0.1")).toString();
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
startIndex += length;
|
|
||||||
length = 2;
|
length = 2;
|
||||||
this.voltage = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
.multiply(new BigDecimal("0.1")).toString();
|
this.voltage = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
||||||
|
.multiply(new BigDecimal("0.1")).toString();
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
startIndex += length;
|
|
||||||
length = 2;
|
length = 2;
|
||||||
this.current = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
.multiply(new BigDecimal("0.001")).toString();
|
this.current = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
||||||
|
.multiply(new BigDecimal("0.001")).toString();
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
startIndex += length;
|
|
||||||
length = 1;
|
length = 1;
|
||||||
this.ambientTemperature = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
.subtract(new BigDecimal("65")).toString();
|
this.ambientTemperature = parseTemperature(messageBytes, startIndex, length);
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
startIndex += length;
|
|
||||||
length = 1;
|
length = 1;
|
||||||
this.portTemperature = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
.subtract(new BigDecimal("65")).toString();
|
this.portTemperature = parseTemperature(messageBytes, startIndex, length);
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = 4;
|
||||||
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
|
this.timestamp = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
startIndex += length;
|
|
||||||
length = 2;
|
length = 2;
|
||||||
this.occupancyTime = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
|
this.occupancyTime = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBytes(byte[] messageBytes, int startIndex, int length) {
|
||||||
|
return hasDataBytes(messageBytes, startIndex, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPeakPower(int optionalLength) {
|
||||||
|
return optionalLength == 2 || optionalLength == 8 || optionalLength >= 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseTemperature(byte[] messageBytes, int startIndex, int length) {
|
||||||
|
int value = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
||||||
|
if (value == 0) {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
return String.valueOf(value - 65);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import java.math.BigDecimal;
|
|||||||
@Setter
|
@Setter
|
||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
public class EBikeMessageCmd20 extends AbsEBikeMessage2 {
|
public class EBikeMessageCmd20 extends AbsEBikeMessage2 {
|
||||||
|
private static final BigDecimal VERSION_FACTOR = new BigDecimal("0.01");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 固件版本,如100则表示V1.00版本
|
* 固件版本,如100则表示V1.00版本
|
||||||
*/
|
*/
|
||||||
@@ -42,13 +44,30 @@ public class EBikeMessageCmd20 extends AbsEBikeMessage2 {
|
|||||||
*/
|
*/
|
||||||
private String powerBoardVersion;
|
private String powerBoardVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备分时计费功能:0=不支持,1=支持。旧协议无此字段。
|
||||||
|
*/
|
||||||
|
private Integer deviceSeparateBillingSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TC模式。旧协议无此字段。
|
||||||
|
*/
|
||||||
|
private Integer tcMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展数据:标准注册字段后的原始扩展字段
|
||||||
|
*/
|
||||||
|
private String extendedData;
|
||||||
|
|
||||||
public EBikeMessageCmd20(byte[] messageBytes) {
|
public EBikeMessageCmd20(byte[] messageBytes) {
|
||||||
super(messageBytes);
|
super(messageBytes);
|
||||||
|
if (getBodyLength() < 8) {
|
||||||
|
throw new IllegalArgumentException("Invalid command 0x20 body length: " + getBodyLength());
|
||||||
|
}
|
||||||
|
|
||||||
int startIndex = 12;
|
int startIndex = DATA_START_INDEX;
|
||||||
int length = 2;
|
int length = 2;
|
||||||
this.firmwareVersion = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
this.firmwareVersion = parseVersion(messageBytes, startIndex, length);
|
||||||
.multiply(new BigDecimal("0.1")).toString();
|
|
||||||
|
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 1;
|
length = 1;
|
||||||
@@ -64,10 +83,34 @@ public class EBikeMessageCmd20 extends AbsEBikeMessage2 {
|
|||||||
|
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 1;
|
length = 1;
|
||||||
this.workMode = BytesUtil.bcd2StrLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
this.workMode = BytesUtil.printHexBinary(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
||||||
|
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 2;
|
length = 2;
|
||||||
this.powerBoardVersion = BytesUtil.bcd2StrLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
this.powerBoardVersion = parseVersion(messageBytes, startIndex, length);
|
||||||
|
|
||||||
|
startIndex += length;
|
||||||
|
int extendedStartIndex = startIndex;
|
||||||
|
length = 1;
|
||||||
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
|
this.deviceSeparateBillingSupport = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
||||||
|
startIndex += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = 1;
|
||||||
|
if (hasBytes(messageBytes, startIndex, length)) {
|
||||||
|
this.tcMode = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.extendedData = getExtendedDataHex(messageBytes, extendedStartIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseVersion(byte[] messageBytes, int startIndex, int length) {
|
||||||
|
return new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
||||||
|
.multiply(VERSION_FACTOR).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBytes(byte[] messageBytes, int startIndex, int length) {
|
||||||
|
return hasDataBytes(messageBytes, startIndex, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class EBikeMessageCmd21 extends AbsEBikeMessage2 {
|
|||||||
super(messageBytes);
|
super(messageBytes);
|
||||||
|
|
||||||
// 读取结果
|
// 读取结果
|
||||||
int startIndex = 12;
|
int startIndex = DATA_START_INDEX;
|
||||||
int length = 2;
|
int length = 2;
|
||||||
this.voltage = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
this.voltage = new BigDecimal(BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)))
|
||||||
.multiply(new BigDecimal("0.1")).toString();
|
.multiply(new BigDecimal("0.1")).toString();
|
||||||
@@ -78,14 +78,18 @@ public class EBikeMessageCmd21 extends AbsEBikeMessage2 {
|
|||||||
|
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 1;
|
length = 1;
|
||||||
this.rssi = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
if (hasDataBytes(messageBytes, startIndex, length)) {
|
||||||
|
this.rssi = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length)) + "";
|
||||||
|
}
|
||||||
|
|
||||||
startIndex += length;
|
startIndex += length;
|
||||||
length = 1;
|
length = 1;
|
||||||
int i = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
if (hasDataBytes(messageBytes, startIndex, length)) {
|
||||||
if (i > 65) {
|
int i = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(messageBytes, startIndex, length));
|
||||||
i = i - 65;
|
if (i > 0) {
|
||||||
|
i = i - 65;
|
||||||
|
}
|
||||||
|
this.temperature = i + "";
|
||||||
}
|
}
|
||||||
this.temperature = i + "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import lombok.Builder;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户注册前台参数
|
* 用户注册前台参数
|
||||||
*
|
*
|
||||||
@@ -35,4 +37,9 @@ public class MemberRegisterDTO {
|
|||||||
* 手机号码
|
* 手机号码
|
||||||
*/
|
*/
|
||||||
private String mobileNumber;
|
private String mobileNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认充电启动金额
|
||||||
|
*/
|
||||||
|
private String defaultChargeAmount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,15 @@ public interface PileBasicInfoService {
|
|||||||
* 注册电单车桩
|
* 注册电单车桩
|
||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
void registrationEBikePile(EBikeMessageCmd20 message);
|
void registrationEBikePile(EBikeMessageCmd20 message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保电单车桩基础数据已注册
|
||||||
|
* @param pileSn 桩号
|
||||||
|
* @param portNumber 端口数量
|
||||||
|
* @param source 触发来源
|
||||||
|
*/
|
||||||
|
void ensureEBikePileRegistered(String pileSn, int portNumber, String source);
|
||||||
|
|
||||||
List<PileDetailInfoVO> getPileDetailInfoList(String stationId);
|
List<PileDetailInfoVO> getPileDetailInfoList(String stationId);
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ import java.util.stream.Collectors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class PileBasicInfoServiceImpl implements PileBasicInfoService {
|
public class PileBasicInfoServiceImpl implements PileBasicInfoService {
|
||||||
|
private static final String EBIKE_AUTO_REGISTER_LOCK_KEY = "ebike:auto_register:pile:";
|
||||||
|
|
||||||
|
private static final String EBIKE_AUTO_REGISTER_CHECKED_KEY = "ebike:auto_register:checked:";
|
||||||
|
|
||||||
|
private static final int EBIKE_AUTO_REGISTER_LOCK_EXPIRE_SECONDS = 30;
|
||||||
|
|
||||||
|
private static final int EBIKE_AUTO_REGISTER_CHECKED_EXPIRE_SECONDS = 300;
|
||||||
|
|
||||||
|
private static final int EBIKE_AUTO_REGISTER_MAX_PORT_NUMBER = 64;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PileBasicInfoMapper pileBasicInfoMapper;
|
private PileBasicInfoMapper pileBasicInfoMapper;
|
||||||
|
|
||||||
@@ -94,6 +104,18 @@ public class PileBasicInfoServiceImpl implements PileBasicInfoService {
|
|||||||
@Value("${baseurl.prefix}")
|
@Value("${baseurl.prefix}")
|
||||||
private String BASE_URL_PREFIX;
|
private String BASE_URL_PREFIX;
|
||||||
|
|
||||||
|
@Value("${ebike.auto-register.enabled:true}")
|
||||||
|
private Boolean ebikeAutoRegisterEnabled;
|
||||||
|
|
||||||
|
@Value("${ebike.auto-register.merchant-id:1}")
|
||||||
|
private Long ebikeAutoRegisterMerchantId;
|
||||||
|
|
||||||
|
@Value("${ebike.auto-register.station-id:2}")
|
||||||
|
private Long ebikeAutoRegisterStationId;
|
||||||
|
|
||||||
|
@Value("${ebike.auto-register.model-id:0}")
|
||||||
|
private Long ebikeAutoRegisterModelId;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PileReservationInfoService pileReservationInfoService;
|
private PileReservationInfoService pileReservationInfoService;
|
||||||
|
|
||||||
@@ -1587,53 +1609,144 @@ public class PileBasicInfoServiceImpl implements PileBasicInfoService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void registrationEBikePile(EBikeMessageCmd20 message) {
|
public void registrationEBikePile(EBikeMessageCmd20 message) {
|
||||||
// 根据物理id(桩编号)查询桩信息
|
ensureEBikePileRegistered(message.getPhysicalId() + "", message.getPortNumber(), "registration_0x20");
|
||||||
PileBasicInfo pileBasicInfo = this.selectPileBasicInfoBySN(message.getPhysicalId() + "");
|
}
|
||||||
if (pileBasicInfo != null) {
|
|
||||||
|
/**
|
||||||
|
* 确保电单车桩基础数据已注册。用于注册包和心跳包兜底建档。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void ensureEBikePileRegistered(String pileSn, int portNumber, String source) {
|
||||||
|
if (!Boolean.TRUE.equals(ebikeAutoRegisterEnabled)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<PileBasicInfo> basicInfoList = Lists.newArrayList();
|
if (StringUtils.isBlank(pileSn)) {
|
||||||
List<PileConnectorInfo> connectorInfoList = Lists.newArrayList();
|
log.warn("电单车自动建档-跳过, pileSn:{}, reason:桩号为空, source:{}", pileSn, source);
|
||||||
|
return;
|
||||||
// 组装pile_basic_info表数据
|
}
|
||||||
PileBasicInfo basicInfo = new PileBasicInfo();
|
if (portNumber <= 0 || portNumber > EBIKE_AUTO_REGISTER_MAX_PORT_NUMBER) {
|
||||||
// 桩编号
|
log.warn("电单车自动建档-跳过, pileSn:{}, portNumber:{}, reason:端口数量异常, source:{}",
|
||||||
String sn = message.getPhysicalId() + "";
|
pileSn, portNumber, source);
|
||||||
basicInfo.setSn(sn);
|
return;
|
||||||
basicInfo.setBusinessType(Constants.ONE); // 经营类型 1-运营桩;2-个人桩
|
|
||||||
basicInfo.setSoftwareProtocol(Constants.THREE); // 软件协议
|
|
||||||
basicInfo.setMerchantId(Long.valueOf("1")); // 运营商id 默认1
|
|
||||||
basicInfo.setStationId(Long.valueOf("2")); // 站点id
|
|
||||||
basicInfo.setModelId(null); // 型号id
|
|
||||||
basicInfo.setProductionDate(new Date()); // 生产日期
|
|
||||||
basicInfo.setLicenceId(null); // TODO 证书编号
|
|
||||||
basicInfo.setSimId(null); // TODO sim卡
|
|
||||||
basicInfo.setRemark(null); // 备注
|
|
||||||
basicInfo.setCreateBy(Constants.SYSTEM); // 创建人
|
|
||||||
basicInfo.setDelFlag(DelFlagEnum.NORMAL.getValue()); // 删除标识
|
|
||||||
basicInfoList.add(basicInfo);
|
|
||||||
|
|
||||||
int portNumber = message.getPortNumber();
|
|
||||||
PileConnectorInfo connectorInfo;
|
|
||||||
for (int i = 1; i < portNumber + 1; i++) {
|
|
||||||
// 组装pile_connector_info表数据
|
|
||||||
connectorInfo = new PileConnectorInfo();
|
|
||||||
connectorInfo.setPileSn(sn); // sn号
|
|
||||||
String connectorCode = String.format("%1$02d", i);
|
|
||||||
connectorInfo.setPileConnectorCode(sn + connectorCode); // 枪口号
|
|
||||||
connectorInfo.setStatus(Constants.ZERO); //状态,默认 0-离网
|
|
||||||
connectorInfo.setCreateBy(Constants.SYSTEM); // 创建人
|
|
||||||
connectorInfo.setDelFlag(DelFlagEnum.NORMAL.getValue()); // 删除标识
|
|
||||||
connectorInfoList.add(connectorInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量入库
|
String checkedKey = EBIKE_AUTO_REGISTER_CHECKED_KEY + pileSn;
|
||||||
PileTransactionDTO transactionDTO = PileTransactionDTO.builder()
|
String checked = redisCache.getCacheObject(checkedKey);
|
||||||
.pileBasicInfoList(basicInfoList)
|
if (StringUtils.equals(Constants.ONE, checked)) {
|
||||||
.pileConnectorInfoList(connectorInfoList)
|
return;
|
||||||
.build();
|
}
|
||||||
pileTransactionService.doCreatePileTransaction(transactionDTO);
|
|
||||||
}
|
String lockKey = EBIKE_AUTO_REGISTER_LOCK_KEY + pileSn;
|
||||||
|
Boolean locked = redisCache.setnx(lockKey, source, EBIKE_AUTO_REGISTER_LOCK_EXPIRE_SECONDS);
|
||||||
|
if (!Boolean.TRUE.equals(locked)) {
|
||||||
|
log.debug("电单车自动建档-跳过, pileSn:{}, reason:未获取到锁, source:{}", pileSn, source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
checked = redisCache.getCacheObject(checkedKey);
|
||||||
|
if (StringUtils.equals(Constants.ONE, checked)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("电单车自动建档-开始, pileSn:{}, portNumber:{}, source:{}", pileSn, portNumber, source);
|
||||||
|
|
||||||
|
PileBasicInfo pileBasicInfo = this.selectPileBasicInfoBySN(pileSn);
|
||||||
|
List<PileConnectorInfo> connectorInfoList = pileConnectorInfoService.selectPileConnectorInfoList(pileSn);
|
||||||
|
List<PileConnectorInfo> missingConnectorInfoList = buildMissingEBikeConnectorInfoList(pileSn, portNumber, connectorInfoList);
|
||||||
|
|
||||||
|
if (pileBasicInfo == null) {
|
||||||
|
PileBasicInfo basicInfo = buildAutoRegisterEBikeBasicInfo(pileSn, portNumber, source);
|
||||||
|
PileTransactionDTO transactionDTO = PileTransactionDTO.builder()
|
||||||
|
.pileBasicInfoList(Lists.newArrayList(basicInfo))
|
||||||
|
.pileConnectorInfoList(missingConnectorInfoList)
|
||||||
|
.build();
|
||||||
|
pileTransactionService.doCreatePileTransaction(transactionDTO);
|
||||||
|
cleanEBikeAutoRegisterCache(pileSn, basicInfo.getStationId());
|
||||||
|
log.info("电单车自动建档-创建成功, pileSn:{}, connectorCount:{}, source:{}",
|
||||||
|
pileSn, missingConnectorInfoList.size(), source);
|
||||||
|
} else {
|
||||||
|
if (CollectionUtils.isNotEmpty(missingConnectorInfoList)) {
|
||||||
|
pileConnectorInfoService.batchInsertConnectorInfo(missingConnectorInfoList);
|
||||||
|
cleanEBikeAutoRegisterCache(pileSn, pileBasicInfo.getStationId());
|
||||||
|
log.info("电单车自动建档-补齐端口, pileSn:{}, missingConnectors:{}, source:{}",
|
||||||
|
pileSn, missingConnectorInfoList.stream().map(PileConnectorInfo::getPileConnectorCode).collect(Collectors.toList()), source);
|
||||||
|
} else {
|
||||||
|
log.info("电单车自动建档-已存在, pileSn:{}, portNumber:{}, source:{}", pileSn, portNumber, source);
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(connectorInfoList) && connectorInfoList.size() > portNumber) {
|
||||||
|
log.warn("电单车自动建档-端口数量变小, pileSn:{}, dbConnectorCount:{}, reportedPortNumber:{}, source:{}",
|
||||||
|
pileSn, connectorInfoList.size(), portNumber, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redisCache.setCacheObject(checkedKey, Constants.ONE, EBIKE_AUTO_REGISTER_CHECKED_EXPIRE_SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("电单车自动建档-失败, pileSn:{}, portNumber:{}, source:{}", pileSn, portNumber, source, e);
|
||||||
|
if (e instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) e;
|
||||||
|
}
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
redisCache.deleteObject(lockKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PileBasicInfo buildAutoRegisterEBikeBasicInfo(String pileSn, int portNumber, String source) {
|
||||||
|
PileBasicInfo basicInfo = new PileBasicInfo();
|
||||||
|
basicInfo.setSn(pileSn);
|
||||||
|
basicInfo.setBusinessType(Constants.ONE);
|
||||||
|
basicInfo.setSoftwareProtocol(Constants.THREE);
|
||||||
|
basicInfo.setMerchantId(ebikeAutoRegisterMerchantId);
|
||||||
|
basicInfo.setStationId(ebikeAutoRegisterStationId);
|
||||||
|
if (ebikeAutoRegisterModelId != null && ebikeAutoRegisterModelId > 0) {
|
||||||
|
basicInfo.setModelId(ebikeAutoRegisterModelId);
|
||||||
|
} else {
|
||||||
|
log.warn("电单车自动建档-modelId未配置或无效, pileSn:{}, configuredModelId:{}, source:{}",
|
||||||
|
pileSn, ebikeAutoRegisterModelId, source);
|
||||||
|
}
|
||||||
|
basicInfo.setProductionDate(new Date());
|
||||||
|
basicInfo.setCreateBy(Constants.SYSTEM);
|
||||||
|
basicInfo.setDelFlag(DelFlagEnum.NORMAL.getValue());
|
||||||
|
basicInfo.setRemark("自动建档:" + source + "; portNumber=" + portNumber);
|
||||||
|
return basicInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PileConnectorInfo> buildMissingEBikeConnectorInfoList(String pileSn, int portNumber, List<PileConnectorInfo> existingConnectorInfoList) {
|
||||||
|
Set<String> existingConnectorCodeSet = CollectionUtils.isEmpty(existingConnectorInfoList)
|
||||||
|
? Collections.emptySet()
|
||||||
|
: existingConnectorInfoList.stream()
|
||||||
|
.map(PileConnectorInfo::getPileConnectorCode)
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
List<PileConnectorInfo> connectorInfoList = Lists.newArrayList();
|
||||||
|
for (int i = 1; i < portNumber + 1; i++) {
|
||||||
|
String connectorCode = String.format("%1$02d", i);
|
||||||
|
String pileConnectorCode = pileSn + connectorCode;
|
||||||
|
if (existingConnectorCodeSet.contains(pileConnectorCode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PileConnectorInfo connectorInfo = new PileConnectorInfo();
|
||||||
|
connectorInfo.setPileSn(pileSn);
|
||||||
|
connectorInfo.setPileConnectorCode(pileConnectorCode);
|
||||||
|
connectorInfo.setStatus(Constants.ZERO);
|
||||||
|
connectorInfo.setCreateBy(Constants.SYSTEM);
|
||||||
|
connectorInfo.setDelFlag(DelFlagEnum.NORMAL.getValue());
|
||||||
|
connectorInfoList.add(connectorInfo);
|
||||||
|
}
|
||||||
|
return connectorInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanEBikeAutoRegisterCache(String pileSn, Long stationId) {
|
||||||
|
List<String> keys = Lists.newArrayList();
|
||||||
|
keys.add(CacheConstants.SELECT_PILE_BASIC_INFO_BY_SN + pileSn);
|
||||||
|
keys.add(CacheConstants.SELECT_PILE_CONNECTOR_INFO_LIST + pileSn);
|
||||||
|
keys.add(CacheConstants.PILE_DETAIL_KEY + pileSn);
|
||||||
|
if (stationId != null) {
|
||||||
|
keys.add(CacheConstants.GET_PILE_LIST_BY_STATION_ID + stationId);
|
||||||
|
}
|
||||||
|
redisCache.deleteObject(keys);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public java.math.BigDecimal getMonthlyAvgElectricity(String monthStartDate, String monthEndDate) {
|
public java.math.BigDecimal getMonthlyAvgElectricity(String monthStartDate, String monthEndDate) {
|
||||||
|
|||||||
@@ -777,6 +777,10 @@ public class PileConnectorInfoServiceImpl implements PileConnectorInfoService {
|
|||||||
String pileSn = pileConnectorCode.substring(0, pileConnectorCode.length() - 2);
|
String pileSn = pileConnectorCode.substring(0, pileConnectorCode.length() - 2);
|
||||||
// 只修改一个枪口的状态
|
// 只修改一个枪口的状态
|
||||||
num = pileConnectorInfoMapper.updateConnectorStatus(pileConnectorCode, status);
|
num = pileConnectorInfoMapper.updateConnectorStatus(pileConnectorCode, status);
|
||||||
|
if (num <= 0) {
|
||||||
|
log.warn("更新枪口状态-更新结果为0, pileConnectorCode:{}, status:{}, 状态描述:{}",
|
||||||
|
pileConnectorCode, status, PileConnectorDataBaseStatusEnum.getStatusDescription(status));
|
||||||
|
}
|
||||||
deleteRedisByPileSnOrPileConnectorCode(pileSn, pileConnectorCode);
|
deleteRedisByPileSnOrPileConnectorCode(pileSn, pileConnectorCode);
|
||||||
redisCache.setCacheObject(redisKey, status, CacheConstants.cache_expire_time_6m);
|
redisCache.setCacheObject(redisKey, status, CacheConstants.cache_expire_time_6m);
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ public class ConfirmStartChargingMemberVO {
|
|||||||
*/
|
*/
|
||||||
private String plateNumber;
|
private String plateNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认充电金额
|
||||||
|
*/
|
||||||
|
private BigDecimal defaultChargeAmount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 本金金额
|
* 本金金额
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ public class MemberVO {
|
|||||||
*/
|
*/
|
||||||
private String merchantId;
|
private String merchantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认充电金额
|
||||||
|
*/
|
||||||
|
private BigDecimal defaultChargeAmount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 运营商名称
|
* 运营商名称
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<result property="avatarUrl" column="avatar_url" />
|
<result property="avatarUrl" column="avatar_url" />
|
||||||
<result property="mobileNumber" column="mobile_number" />
|
<result property="mobileNumber" column="mobile_number" />
|
||||||
<result property="merchantId" column="merchant_id" />
|
<result property="merchantId" column="merchant_id" />
|
||||||
|
<result property="defaultChargeAmount" column="default_charge_amount"/>
|
||||||
<result property="remark" column="remark" />
|
<result property="remark" column="remark" />
|
||||||
<result property="createTime" column="create_time" />
|
<result property="createTime" column="create_time" />
|
||||||
<result property="createBy" column="create_by" />
|
<result property="createBy" column="create_by" />
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
<!--@mbg.generated PileBillingTemplate-->
|
<!--@mbg.generated PileBillingTemplate-->
|
||||||
id, member_id, open_id, buyer_id, license_plate_number, nick_name,logic_card, physics_card, status, avatar_url,
|
id, member_id, open_id, buyer_id, license_plate_number, nick_name,logic_card, physics_card, status, avatar_url,
|
||||||
mobile_number, merchant_id, remark,
|
mobile_number, merchant_id, default_charge_amount, remark,
|
||||||
create_time, create_by, update_time, update_by, del_flag
|
create_time, create_by, update_time, update_by, del_flag
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<if test="avatarUrl != null">avatar_url,</if>
|
<if test="avatarUrl != null">avatar_url,</if>
|
||||||
<if test="mobileNumber != null">mobile_number,</if>
|
<if test="mobileNumber != null">mobile_number,</if>
|
||||||
<if test="merchantId != null">merchant_id,</if>
|
<if test="merchantId != null">merchant_id,</if>
|
||||||
|
<if test="defaultChargeAmount != null">default_charge_amount,</if>
|
||||||
<if test="remark != null">remark,</if>
|
<if test="remark != null">remark,</if>
|
||||||
<if test="createTime != null">create_time,</if>
|
<if test="createTime != null">create_time,</if>
|
||||||
<if test="createBy != null">create_by,</if>
|
<if test="createBy != null">create_by,</if>
|
||||||
@@ -88,6 +90,7 @@
|
|||||||
<if test="avatarUrl != null">#{avatarUrl},</if>
|
<if test="avatarUrl != null">#{avatarUrl},</if>
|
||||||
<if test="mobileNumber != null">#{mobileNumber},</if>
|
<if test="mobileNumber != null">#{mobileNumber},</if>
|
||||||
<if test="merchantId != null">#{merchantId},</if>
|
<if test="merchantId != null">#{merchantId},</if>
|
||||||
|
<if test="defaultChargeAmount != null">#{defaultChargeAmount},</if>
|
||||||
<if test="remark != null">#{remark},</if>
|
<if test="remark != null">#{remark},</if>
|
||||||
<if test="createTime != null">#{createTime},</if>
|
<if test="createTime != null">#{createTime},</if>
|
||||||
<if test="createBy != null">#{createBy},</if>
|
<if test="createBy != null">#{createBy},</if>
|
||||||
@@ -110,6 +113,7 @@
|
|||||||
<if test="avatarUrl != null">avatar_url = #{avatarUrl},</if>
|
<if test="avatarUrl != null">avatar_url = #{avatarUrl},</if>
|
||||||
<if test="mobileNumber != null">mobile_number = #{mobileNumber},</if>
|
<if test="mobileNumber != null">mobile_number = #{mobileNumber},</if>
|
||||||
<if test="merchantId != null">merchant_id = #{merchantId},</if>
|
<if test="merchantId != null">merchant_id = #{merchantId},</if>
|
||||||
|
<if test="defaultChargeAmount != null">default_charge_amount = #{defaultChargeAmount},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="createTime != null">create_time = #{createTime},</if>
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
<if test="createBy != null">create_by = #{createBy},</if>
|
<if test="createBy != null">create_by = #{createBy},</if>
|
||||||
@@ -201,6 +205,7 @@
|
|||||||
t1.avatar_url as avatarUrl,
|
t1.avatar_url as avatarUrl,
|
||||||
t1.mobile_number as mobileNumber,
|
t1.mobile_number as mobileNumber,
|
||||||
t1.merchant_id as merchantId,
|
t1.merchant_id as merchantId,
|
||||||
|
t1.default_charge_amount as defaultChargeAmount,
|
||||||
t1.create_time as createTime
|
t1.create_time as createTime
|
||||||
<!--t2.principal_balance as principalBalance,
|
<!--t2.principal_balance as principalBalance,
|
||||||
t2.gift_balance as giftBalance-->
|
t2.gift_balance as giftBalance-->
|
||||||
@@ -327,6 +332,7 @@
|
|||||||
<select id="queryMemberInfoByVinCode" resultType="com.jsowell.pile.vo.base.ConfirmStartChargingMemberVO">
|
<select id="queryMemberInfoByVinCode" resultType="com.jsowell.pile.vo.base.ConfirmStartChargingMemberVO">
|
||||||
select
|
select
|
||||||
t1.member_id as memberId,
|
t1.member_id as memberId,
|
||||||
|
t1.default_charge_amount as defaultChargeAmount,
|
||||||
t2.license_plate_number as plateNumber,
|
t2.license_plate_number as plateNumber,
|
||||||
t3.principal_balance as principalBalance,
|
t3.principal_balance as principalBalance,
|
||||||
t3.gift_balance as giftBalance
|
t3.gift_balance as giftBalance
|
||||||
|
|||||||
Reference in New Issue
Block a user