25 KiB
充电枪管理
**本文档引用文件** - [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java) - [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java) - [GunMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/GunMapper.java) - [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java) - [GunRepository.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/GunRepository.java) - [GunRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/GunRepositoryImpl.java) - [GunCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/GunCreateRequest.java) - [GunUpdateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/GunUpdateRequest.java) - [GunWithStatusResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/GunWithStatusResponse.java) - [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java) - [AttrKeyEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/data/kv/AttrKeyEnum.java) - [GunRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/gun/GunRedisCache.java) - [GunRunStatusEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java)目录
简介
本文档详细描述了充电枪管理功能的实现,包括数据模型、API接口、服务层逻辑、缓存机制和状态管理。系统通过Gun实体管理充电枪,支持创建、查询、更新和删除操作,并通过属性系统实时跟踪充电枪的运行状态。
数据模型
Gun实体
Gun实体是充电枪的核心数据模型,定义了充电枪的基本属性和关系。
classDiagram
class Gun {
+UUID id
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+String gunNo
+String gunName
+String gunCode
+UUID stationId
+UUID pileId
+Integer version
}
图示来源
核心属性说明
- id: 充电枪唯一标识符,UUID类型
- gunNo: 充电枪编号,如"01"、"02"
- gunName: 充电枪名称
- gunCode: 充电枪编码,全局唯一标识
- stationId: 所属充电站ID,与Station实体关联
- pileId: 所属充电桩ID,与Pile实体关联
实体关系
erDiagram
GUN {
uuid id PK
string gunNo
string gunName
string gunCode UK
uuid stationId FK
uuid pileId FK
timestamp createdTime
timestamp updatedTime
integer version
}
PILE {
uuid id PK
string pileCode UK
}
GUN ||--o{ PILE : "属于"
图示来源
实体关系说明
- 1对多关系: 一个充电桩(Pile)可以有多个充电枪(Gun),形成1对多关系
- 外键约束:
pileId字段作为外键关联到Pile实体 - 唯一性约束:
gunCode字段具有唯一性约束,确保全局唯一
Section sources
RESTful API
API概览
GunController提供了完整的RESTful API接口,支持对充电枪的CRUD操作。
graph TD
A[客户端] --> B[POST /api/guns]
A --> C[GET /api/guns/{id}]
A --> D[PUT /api/guns/{id}]
A --> E[DELETE /api/guns/{id}]
A --> F[GET /api/guns]
A --> G[GET /api/guns/status/{gunCode}]
A --> H[GET /api/guns/code/{gunCode}]
B --> I[创建充电枪]
C --> J[查询单个充电枪]
D --> K[更新充电枪]
E --> L[删除充电枪]
F --> M[分页查询充电枪]
G --> N[查询充电枪状态]
H --> O[查询充电枪详情]
图示来源
API详情
创建充电枪 (POST /api/guns)
创建新的充电枪实体。
请求参数
- HTTP方法: POST
- URL: /api/guns
- Content-Type: application/json
请求体 (GunCreateRequest)
{
"gunName": "快充枪1",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string"
}
响应格式
{
"code": 200,
"message": "创建成功",
"data": {
"id": "uuid-string",
"createdTime": "2023-01-01T00:00:00",
"gunName": "快充枪1",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string",
"version": 0
}
}
验证规则
gunName: 必填,不允许XSS攻击gunNo: 必填gunCode: 必填,不允许重复stationId: 必填,有效UUIDpileId: 必填,有效UUID
Section sources
查询充电枪 (GET /api/guns)
分页查询充电枪列表,包含状态信息。
请求参数
- HTTP方法: GET
- URL: /api/guns
- 查询参数:
page: 页码(默认1)size: 每页大小(默认10)gunName: 充电枪名称(可选)pileId: 充电桩ID(可选)
响应格式 (PageResponse)
{
"records": [
{
"id": "uuid-string",
"gunName": "快充枪1",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string",
"stationName": "充电站A",
"pileName": "充电桩1",
"pileCode": "PILE001",
"runStatus": "CHARGING"
}
],
"total": 1,
"totalPages": 1,
"page": 1,
"size": 10
}
分页参数
- records: 当前页的数据列表
- total: 总记录数
- totalPages: 总页数
- page: 当前页码
- size: 每页大小
Section sources
更新充电枪 (PUT /api/guns/{id})
更新指定ID的充电枪信息。
请求参数
- HTTP方法: PUT
- URL: /api/guns/{id}
- 路径参数: id (充电枪UUID)
- 请求体: GunUpdateRequest
请求体 (GunUpdateRequest)
{
"gunName": "快充枪1-更新",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string"
}
响应格式
{
"code": 200,
"message": "更新成功",
"data": {
"id": "uuid-string",
"updatedTime": "2023-01-01T00:00:00",
"gunName": "快充枪1-更新",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string"
}
}
更新规则
- 允许修改所有字段
- 更新时自动设置
updatedTime字段 - 版本号保持不变
Section sources
删除充电枪 (DELETE /api/guns/{id})
删除指定ID的充电枪。
请求参数
- HTTP方法: DELETE
- URL: /api/guns/{id}
- 路径参数: id (充电枪UUID)
响应格式
{
"code": 200,
"message": "删除成功",
"data": null
}
删除逻辑
- 先检查充电枪是否存在
- 执行物理删除操作
- 返回成功或失败状态
Section sources
查询充电枪状态 (GET /api/guns/status/{gunCode})
根据充电枪编码查询其运行状态。
请求参数
- HTTP方法: GET
- URL: /api/guns/status/{gunCode}
- 路径参数: gunCode (充电枪编码)
响应格式
{
"code": 200,
"message": "查询成功",
"data": "CHARGING"
}
状态查询流程
- 根据
gunCode查找充电枪实体 - 通过
AttributeService获取gunRunStatus属性 - 返回状态值
Section sources
查询充电枪详情 (GET /api/guns/code/{gunCode})
根据充电枪编码查询其详细信息,包含状态。
请求参数
- HTTP方法: GET
- URL: /api/guns/code/{gunCode}
- 路径参数: gunCode (充电枪编码)
响应格式
{
"code": 200,
"message": "查询成功",
"data": {
"id": "uuid-string",
"gunName": "快充枪1",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string",
"stationName": "充电站A",
"pileName": "充电桩1",
"pileCode": "PILE001",
"runStatus": "CHARGING"
}
}
详情查询特点
- 返回完整的
GunWithStatusResponse对象 - 包含关联的充电站和充电桩信息
- 包含当前运行状态
Section sources
服务层实现
DefaultGunService架构
DefaultGunService是充电枪业务逻辑的核心实现,协调数据访问和缓存操作。
classDiagram
class DefaultGunService {
-GunMapper gunMapper
-GunRepository gunRepository
-AttributeService attributeService
+createGun(GunCreateRequest) Gun
+findById(UUID) Gun
+updateGun(UUID, GunUpdateRequest) Gun
+deleteGun(UUID) void
+queryGunsWithStatus(GunQueryRequest) PageResponse
+findByPileCodeAndGunNo(String, String) Gun
+findByGunCode(String) Gun
+findGunWithStatusByCode(String) GunWithStatusResponse
+findGunStatus(UUID) String
+saveGunStatusChange(UUID, String, Long) void
+handleGunRunStatus(String, String, GunRunStatus, long) boolean
}
class GunService {
<<interface>>
+createGun(GunCreateRequest) Gun
+findById(UUID) Gun
+updateGun(UUID, GunUpdateRequest) Gun
+deleteGun(UUID) void
+queryGunsWithStatus(GunQueryRequest) PageResponse
+findByPileCodeAndGunNo(String, String) Gun
+findByGunCode(String) Gun
+findGunWithStatusByCode(String) GunWithStatusResponse
+findGunStatus(UUID) String
+saveGunStatusChange(UUID, String, Long) void
+handleGunRunStatus(String, String, GunRunStatus, long) boolean
}
DefaultGunService --|> GunService : 实现
DefaultGunService --> GunMapper : 使用
DefaultGunService --> GunRepository : 使用
DefaultGunService --> AttributeService : 使用
图示来源
核心方法分析
创建充电枪 (createGun)
实现充电枪的创建逻辑。
处理流程
- 检查
gunCode是否已存在 - 构建新的
Gun实体 - 设置默认值(ID、创建时间、版本等)
- 插入数据库
验证逻辑
- 使用
LambdaQueryWrapper检查gunCode唯一性 - 抛出异常阻止重复创建
Section sources
查询充电枪状态 (findGunStatus)
从属性系统中查询充电枪的运行状态。
实现细节
- 使用
AttributeService.find()异步获取属性 - 属性键为
AttrKeyEnum.GUN_RUN_STATUS.getCode() - 返回字符串格式的状态值
异步处理
- 使用
ListenableFuture进行异步调用 - 通过
get()方法阻塞等待结果 - 包含异常处理逻辑
Section sources
保存充电枪状态 (saveGunStatusChange)
将充电枪状态保存到属性系统。
实现细节
- 创建
BaseAttributeKvEntry对象 - 使用
StringDataEntry存储状态值 - 调用
attributeService.save()持久化
时间戳处理
- 使用传入的
ts参数或当前时间 - 支持精确到毫秒的时间戳
Section sources
处理充电枪运行状态 (handleGunRunStatus)
处理从协议层上报的充电枪状态。
处理流程
- 将Proto状态转换为数据库枚举
- 根据
pileCode和gunNo查找充电枪 - 检查状态是否发生变化
- 保存新状态到属性系统
- 判断是否需要更新充电桩状态
状态转换
- 实现Proto状态到数据库枚举的映射
- 支持多种状态类型(空闲、充电中、故障等)
- 未知状态返回null
状态同步
- 检查当前状态避免重复更新
- 记录状态变化日志
- 触发相关业务逻辑
Section sources
缓存机制
缓存架构
系统采用多级缓存策略提升查询性能。
graph TD
A[应用层] --> B[GunService]
B --> C[GunRepository]
C --> D[GunRedisCache]
D --> E[Redis]
C --> F[GunMapper]
F --> G[数据库]
style D fill:#f9f,stroke:#333
style F fill:#bbf,stroke:#333
subgraph "缓存层"
D
E
end
subgraph "持久层"
F
G
end
图示来源
GunRedisCache实现
GunRedisCache是基于Redis的充电枪缓存实现。
classDiagram
class GunRedisCache {
+String cacheName
+CacheSpecsMap cacheSpecsMap
+LettuceConnectionFactory connectionFactory
+JCPPRedisSerializer serializer
+serialize(Gun) byte[]
+deserialize(GunCacheKey, byte[]) Gun
}
class VersionedRedisCache {
<<abstract>>
+get(K, Callable<V>) V
+evict(K) void
+evict(Collection<K>) void
}
GunRedisCache --|> VersionedRedisCache : 继承
GunRedisCache --> Gun : 序列化/反序列化
GunRedisCache --> JacksonUtil : JSON转换
图示来源
核心特性
- 使用
JCPPRedisSerializer进行序列化 - 基于
JacksonUtil实现JSON转换 - 支持版本化缓存
- 配置化缓存策略
序列化实现
serialize(): 使用Jackson将Gun对象转换为字节数组deserialize(): 从字节数组反序列化为Gun对象
Section sources
缓存键设计 (GunCacheKey)
缓存键设计支持多种查询场景。
// 伪代码表示GunCacheKey的设计
public class GunCacheKey {
private UUID gunId; // 基于ID的缓存
private String pileCode; // 基于桩编码的缓存
private String gunNo; // 基于枪编号的缓存
private String gunCode; // 基于枪编码的缓存
// 支持多种构造函数
public GunCacheKey(UUID gunId)
public GunCacheKey(String pileCode, String gunNo)
public GunCacheKey(String gunCode)
}
缓存键类型
- 基于ID:
new GunCacheKey(gunId) - 基于桩编码+枪编号:
new GunCacheKey(pileCode, gunNo) - 基于枪编码:
new GunCacheKey(gunCode)
Section sources
缓存事件处理
通过事件机制维护缓存一致性。
sequenceDiagram
participant Service as DefaultGunService
participant Repository as GunRepositoryImpl
participant Cache as GunRedisCache
participant Event as GunCacheEvictEvent
Service->>Repository : updateGun()
Repository->>Repository : handleEvictEvent()
Repository->>Cache : evict()
Cache->>Redis : 删除缓存
Repository->>Repository : 查询数据库
Repository-->>Service : 返回结果
图示来源
事件处理流程
- 服务层执行更新或删除操作
- 触发
GunCacheEvictEvent事件 GunRepositoryImpl监听并处理事件- 从Redis缓存中删除相关键
- 后续查询将从数据库获取最新数据
缓存失效策略
- 写操作后立即失效缓存
- 支持批量失效多个缓存键
- 保证缓存与数据库的一致性
Section sources
状态管理
运行状态枚举 (GunRunStatusEnum)
定义了充电枪的所有可能运行状态。
classDiagram
class GunRunStatusEnum {
+IDLE
+INSERTED
+CHARGING
+CHARGE_COMPLETE
+DISCHARGE_READY
+DISCHARGING
+DISCHARGE_COMPLETE
+RESERVED
+FAULT
}
图示来源
状态说明
- IDLE: 空闲状态,未连接车辆
- INSERTED: 已插枪,未开始充电
- CHARGING: 正在充电
- CHARGE_COMPLETE: 充电完成
- DISCHARGE_READY: 放电准备
- DISCHARGING: 正在放电
- DISCHARGE_COMPLETE: 放电完成
- RESERVED: 已预约
- FAULT: 故障状态
状态转换
- 通过
switch语句实现Proto状态到枚举的转换 - 未知状态返回null,不进行更新
Section sources
状态获取流程
从协议会话中获取和更新充电枪状态。
sequenceDiagram
participant Protocol as 协议层
participant Service as DefaultGunService
participant Attribute as AttributeService
participant Redis as Redis
participant DB as 数据库
Protocol->>Service : handleGunRunStatus(pileCode, gunNo, protoStatus, ts)
Service->>Service : convertProtoStatusToDbStatus()
Service->>Repository : findByPileCodeAndGunNo(pileCode, gunNo)
Repository->>Redis : 查询缓存
Redis-->>Repository : 返回结果
Repository-->>Service : 返回Gun实体
Service->>Service : findGunStatus(gunId)
Service->>Attribute : find(gunId, GUN_RUN_STATUS)
Attribute->>Redis : 查询属性缓存
Redis-->>Attribute : 返回状态
Attribute-->>Service : 返回当前状态
alt 状态发生变化
Service->>Service : saveGunStatusChange(gunId, status, ts)
Service->>Attribute : save(gunId, attribute)
Attribute->>Redis : 保存属性
Redis-->>Attribute : 确认
Attribute-->>Service : 确认
Service-->>Protocol : 返回true
else 状态未变化
Service-->>Protocol : 返回false
end
图示来源
状态同步机制
- 协议层上报充电枪状态
- 服务层转换状态格式
- 查找对应的充电枪实体
- 获取当前状态进行比较
- 只有状态变化时才更新
- 保存新状态到属性系统
性能优化
- 使用缓存减少数据库查询
- 状态比较避免不必要的更新
- 异步属性服务提高响应速度
Section sources
使用示例
为充电桩配置两把充电枪
展示如何为一个充电桩配置两把充电枪的完整流程。
flowchart TD
Start([开始]) --> CreatePile["创建充电桩"]
CreatePile --> GetPileId["获取充电桩ID"]
GetPileId --> CreateGun1["创建第一把充电枪"]
CreateGun1 --> SetGun1Props["设置第一把枪属性"]
SetGun1Props --> CreateGun2["创建第二把充电枪"]
CreateGun2 --> SetGun2Props["设置第二把枪属性"]
SetGun2Props --> Verify["验证配置结果"]
Verify --> End([完成])
style CreatePile fill:#9f9,stroke:#333
style CreateGun1 fill:#9f9,stroke:#333
style CreateGun2 fill:#9f9,stroke:#333
图示来源
实现步骤
步骤1: 创建充电桩
首先创建一个充电桩实体。
// 伪代码:创建充电桩
Pile pile = pileService.createPile(createRequest);
UUID pileId = pile.getId();
步骤2: 创建第一把充电枪
为充电桩创建第一把充电枪。
// 伪代码:创建第一把充电枪
GunCreateRequest gun1Request = GunCreateRequest.builder()
.gunName("快充枪1")
.gunNo("01")
.gunCode("GUN001")
.stationId(stationId)
.pileId(pileId)
.build();
Gun gun1 = gunService.createGun(gun1Request);
步骤3: 创建第二把充电枪
为同一充电桩创建第二把充电枪。
// 伪代码:创建第二把充电枪
GunCreateRequest gun2Request = GunCreateRequest.builder()
.gunName("快充枪2")
.gunNo("02")
.gunCode("GUN002")
.stationId(stationId)
.pileId(pileId)
.build();
Gun gun2 = gunService.createGun(gun2Request);
步骤4: 验证配置结果
查询充电桩下的所有充电枪。
// 伪代码:验证配置
List<Gun> guns = gunService.findByPileId(pileId);
assert guns.size() == 2;
GunWithStatusResponse gun1Status = gunService.findGunWithStatusByCode("GUN001");
GunWithStatusResponse gun2Status = gunService.findGunWithStatusByCode("GUN002");
关键要点
- 两把充电枪共享同一个
pileId gunNo在同一充电桩下必须唯一gunCode在整个系统中必须唯一- 可以独立管理每把充电枪的状态
Section sources
级联关系与状态同步
级联关系管理
充电枪与充电桩之间的级联关系管理。
erDiagram
STATION ||--o{ PILE : "拥有"
PILE ||--o{ GUN : "拥有"
STATION {
uuid id PK
string stationName
}
PILE {
uuid id PK
string pileCode UK
uuid stationId FK
}
GUN {
uuid id PK
string gunCode UK
uuid pileId FK
string gunNo
}
图示来源
级联操作
- 创建: 充电枪必须关联到存在的充电桩
- 查询: 支持通过充电桩查询所有充电枪
- 更新: 可以修改充电枪所属的充电桩
- 删除: 删除充电桩时级联删除所有充电枪
外键约束
pileId字段确保充电枪必须属于某个充电桩- 数据库层面保证引用完整性
Section sources
状态同步机制
充电枪状态变化时与充电桩的状态同步。
flowchart TD
GunStatusChange["充电枪状态变化"] --> CheckStatus["检查新状态"]
CheckStatus --> |CHARGING| UpdatePile["更新充电桩状态"]
CheckStatus --> |DISCHARGING| UpdatePile["更新充电桩状态"]
CheckStatus --> |FAULT| UpdatePile["更新充电桩状态"]
CheckStatus --> |其他状态| NoUpdate["不更新"]
UpdatePile --> PileService["调用PileService"]
PileService --> UpdateStatus["更新充电桩状态"]
UpdateStatus --> Log["记录日志"]
Log --> End([完成])
style UpdatePile fill:#f96,stroke:#333
style NoUpdate fill:#69f,stroke:#333
图示来源
同步规则
private boolean shouldUpdatePileStatus(GunRunStatusEnum gunStatus) {
return switch (gunStatus) {
case CHARGING, DISCHARGING -> true; // 充电或放电时更新
case FAULT -> true; // 故障时更新
default -> false; // 其他状态不更新
};
}
触发条件
- 充电开始: 充电枪状态变为
CHARGING - 放电开始: 充电枪状态变为
DISCHARGING - 故障发生: 充电枪状态变为
FAULT
同步流程
- 充电枪状态发生变化
- 调用
shouldUpdatePileStatus()判断是否需要同步 - 如果需要,通知
PileService更新充电桩状态 - 更新充电桩的在线状态和运行状态
性能考虑
- 只在必要时触发同步,减少不必要的操作
- 使用异步机制避免阻塞主流程
- 缓存充电桩状态减少数据库查询
Section sources