wiki生成

This commit is contained in:
三丙
2025-10-28 14:39:06 +08:00
parent 26cec08d61
commit fa9524d302
31 changed files with 13636 additions and 1 deletions

View File

@@ -0,0 +1,980 @@
# 充电枪管理
<cite>
**本文档引用文件**
- [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)
</cite>
## 目录
1. [简介](#简介)
2. [数据模型](#数据模型)
3. [RESTful API](#restful-api)
4. [服务层实现](#服务层实现)
5. [缓存机制](#缓存机制)
6. [状态管理](#状态管理)
7. [使用示例](#使用示例)
8. [级联关系与状态同步](#级联关系与状态同步)
## 简介
本文档详细描述了充电枪管理功能的实现包括数据模型、API接口、服务层逻辑、缓存机制和状态管理。系统通过Gun实体管理充电枪支持创建、查询、更新和删除操作并通过属性系统实时跟踪充电枪的运行状态。
## 数据模型
### Gun实体
`Gun`实体是充电枪的核心数据模型,定义了充电枪的基本属性和关系。
```mermaid
classDiagram
class Gun {
+UUID id
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+String gunNo
+String gunName
+String gunCode
+UUID stationId
+UUID pileId
+Integer version
}
```
**图示来源**
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L57)
**核心属性说明**
- **id**: 充电枪唯一标识符UUID类型
- **gunNo**: 充电枪编号,如"01"、"02"
- **gunName**: 充电枪名称
- **gunCode**: 充电枪编码,全局唯一标识
- **stationId**: 所属充电站ID与Station实体关联
- **pileId**: 所属充电桩ID与Pile实体关联
**实体关系**
```mermaid
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 : "属于"
```
**图示来源**
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L57)
**实体关系说明**
- **1对多关系**: 一个充电桩(Pile)可以有多个充电枪(Gun)形成1对多关系
- **外键约束**: `pileId`字段作为外键关联到Pile实体
- **唯一性约束**: `gunCode`字段具有唯一性约束,确保全局唯一
**Section sources**
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L57)
## RESTful API
### API概览
`GunController`提供了完整的RESTful API接口支持对充电枪的CRUD操作。
```mermaid
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[查询充电枪详情]
```
**图示来源**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L1-L116)
### API详情
#### 创建充电枪 (POST /api/guns)
创建新的充电枪实体。
**请求参数**
- **HTTP方法**: POST
- **URL**: /api/guns
- **Content-Type**: application/json
**请求体 (GunCreateRequest)**
```json
{
"gunName": "快充枪1",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string"
}
```
**响应格式**
```json
{
"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`: 必填有效UUID
- `pileId`: 必填有效UUID
**Section sources**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L25-L32)
- [GunCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/GunCreateRequest.java#L1-L37)
#### 查询充电枪 (GET /api/guns)
分页查询充电枪列表,包含状态信息。
**请求参数**
- **HTTP方法**: GET
- **URL**: /api/guns
- **查询参数**:
- `page`: 页码默认1
- `size`: 每页大小默认10
- `gunName`: 充电枪名称(可选)
- `pileId`: 充电桩ID可选
**响应格式 (PageResponse<GunWithStatusResponse>)**
```json
{
"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**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L62-L68)
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java#L1-L44)
- [GunWithStatusResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/GunWithStatusResponse.java#L1-L96)
#### 更新充电枪 (PUT /api/guns/{id})
更新指定ID的充电枪信息。
**请求参数**
- **HTTP方法**: PUT
- **URL**: /api/guns/{id}
- **路径参数**: id (充电枪UUID)
- **请求体**: GunUpdateRequest
**请求体 (GunUpdateRequest)**
```json
{
"gunName": "快充枪1-更新",
"gunNo": "01",
"gunCode": "GUN001",
"stationId": "uuid-string",
"pileId": "uuid-string"
}
```
**响应格式**
```json
{
"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**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L34-L43)
- [GunUpdateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/GunUpdateRequest.java#L1-L36)
#### 删除充电枪 (DELETE /api/guns/{id})
删除指定ID的充电枪。
**请求参数**
- **HTTP方法**: DELETE
- **URL**: /api/guns/{id}
- **路径参数**: id (充电枪UUID)
**响应格式**
```json
{
"code": 200,
"message": "删除成功",
"data": null
}
```
**删除逻辑**
- 先检查充电枪是否存在
- 执行物理删除操作
- 返回成功或失败状态
**Section sources**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L45-L51)
#### 查询充电枪状态 (GET /api/guns/status/{gunCode})
根据充电枪编码查询其运行状态。
**请求参数**
- **HTTP方法**: GET
- **URL**: /api/guns/status/{gunCode}
- **路径参数**: gunCode (充电枪编码)
**响应格式**
```json
{
"code": 200,
"message": "查询成功",
"data": "CHARGING"
}
```
**状态查询流程**
1. 根据`gunCode`查找充电枪实体
2. 通过`AttributeService`获取`gunRunStatus`属性
3. 返回状态值
**Section sources**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L69-L96)
#### 查询充电枪详情 (GET /api/guns/code/{gunCode})
根据充电枪编码查询其详细信息,包含状态。
**请求参数**
- **HTTP方法**: GET
- **URL**: /api/guns/code/{gunCode}
- **路径参数**: gunCode (充电枪编码)
**响应格式**
```json
{
"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**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L98-L115)
## 服务层实现
### DefaultGunService架构
`DefaultGunService`是充电枪业务逻辑的核心实现,协调数据访问和缓存操作。
```mermaid
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 : 使用
```
**图示来源**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L1-L246)
### 核心方法分析
#### 创建充电枪 (createGun)
实现充电枪的创建逻辑。
**处理流程**
1. 检查`gunCode`是否已存在
2. 构建新的`Gun`实体
3. 设置默认值ID、创建时间、版本等
4. 插入数据库
**验证逻辑**
- 使用`LambdaQueryWrapper`检查`gunCode`唯一性
- 抛出异常阻止重复创建
**Section sources**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L48-L75)
#### 查询充电枪状态 (findGunStatus)
从属性系统中查询充电枪的运行状态。
**实现细节**
- 使用`AttributeService.find()`异步获取属性
- 属性键为`AttrKeyEnum.GUN_RUN_STATUS.getCode()`
- 返回字符串格式的状态值
**异步处理**
- 使用`ListenableFuture`进行异步调用
- 通过`get()`方法阻塞等待结果
- 包含异常处理逻辑
**Section sources**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L178-L193)
#### 保存充电枪状态 (saveGunStatusChange)
将充电枪状态保存到属性系统。
**实现细节**
- 创建`BaseAttributeKvEntry`对象
- 使用`StringDataEntry`存储状态值
- 调用`attributeService.save()`持久化
**时间戳处理**
- 使用传入的`ts`参数或当前时间
- 支持精确到毫秒的时间戳
**Section sources**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L195-L208)
#### 处理充电枪运行状态 (handleGunRunStatus)
处理从协议层上报的充电枪状态。
**处理流程**
1. 将Proto状态转换为数据库枚举
2. 根据`pileCode``gunNo`查找充电枪
3. 检查状态是否发生变化
4. 保存新状态到属性系统
5. 判断是否需要更新充电桩状态
**状态转换**
- 实现Proto状态到数据库枚举的映射
- 支持多种状态类型(空闲、充电中、故障等)
- 未知状态返回null
**状态同步**
- 检查当前状态避免重复更新
- 记录状态变化日志
- 触发相关业务逻辑
**Section sources**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L210-L244)
## 缓存机制
### 缓存架构
系统采用多级缓存策略提升查询性能。
```mermaid
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
```
**图示来源**
- [GunRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/GunRepositoryImpl.java#L1-L97)
- [GunRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/gun/GunRedisCache.java#L1-L36)
### GunRedisCache实现
`GunRedisCache`是基于Redis的充电枪缓存实现。
```mermaid
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转换
```
**图示来源**
- [GunRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/gun/GunRedisCache.java#L1-L36)
**核心特性**
- 使用`JCPPRedisSerializer`进行序列化
- 基于`JacksonUtil`实现JSON转换
- 支持版本化缓存
- 配置化缓存策略
**序列化实现**
- `serialize()`: 使用Jackson将Gun对象转换为字节数组
- `deserialize()`: 从字节数组反序列化为Gun对象
**Section sources**
- [GunRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/gun/GunRedisCache.java#L1-L36)
### 缓存键设计 (GunCacheKey)
缓存键设计支持多种查询场景。
```java
// 伪代码表示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**
- [GunRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/GunRepositoryImpl.java#L1-L97)
### 缓存事件处理
通过事件机制维护缓存一致性。
```mermaid
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 : 返回结果
```
**图示来源**
- [GunRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/GunRepositoryImpl.java#L1-L97)
**事件处理流程**
1. 服务层执行更新或删除操作
2. 触发`GunCacheEvictEvent`事件
3. `GunRepositoryImpl`监听并处理事件
4. 从Redis缓存中删除相关键
5. 后续查询将从数据库获取最新数据
**缓存失效策略**
- 写操作后立即失效缓存
- 支持批量失效多个缓存键
- 保证缓存与数据库的一致性
**Section sources**
- [GunRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/GunRepositoryImpl.java#L25-L45)
## 状态管理
### 运行状态枚举 (GunRunStatusEnum)
定义了充电枪的所有可能运行状态。
```mermaid
classDiagram
class GunRunStatusEnum {
+IDLE
+INSERTED
+CHARGING
+CHARGE_COMPLETE
+DISCHARGE_READY
+DISCHARGING
+DISCHARGE_COMPLETE
+RESERVED
+FAULT
}
```
**图示来源**
- [GunRunStatusEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java)
**状态说明**
- **IDLE**: 空闲状态,未连接车辆
- **INSERTED**: 已插枪,未开始充电
- **CHARGING**: 正在充电
- **CHARGE_COMPLETE**: 充电完成
- **DISCHARGE_READY**: 放电准备
- **DISCHARGING**: 正在放电
- **DISCHARGE_COMPLETE**: 放电完成
- **RESERVED**: 已预约
- **FAULT**: 故障状态
**状态转换**
- 通过`switch`语句实现Proto状态到枚举的转换
- 未知状态返回null不进行更新
**Section sources**
- [GunRunStatusEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java)
### 状态获取流程
从协议会话中获取和更新充电枪状态。
```mermaid
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
```
**图示来源**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L210-L244)
**状态同步机制**
1. 协议层上报充电枪状态
2. 服务层转换状态格式
3. 查找对应的充电枪实体
4. 获取当前状态进行比较
5. 只有状态变化时才更新
6. 保存新状态到属性系统
**性能优化**
- 使用缓存减少数据库查询
- 状态比较避免不必要的更新
- 异步属性服务提高响应速度
**Section sources**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L210-L244)
## 使用示例
### 为充电桩配置两把充电枪
展示如何为一个充电桩配置两把充电枪的完整流程。
```mermaid
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
```
**图示来源**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L48-L75)
**实现步骤**
#### 步骤1: 创建充电桩
首先创建一个充电桩实体。
```java
// 伪代码:创建充电桩
Pile pile = pileService.createPile(createRequest);
UUID pileId = pile.getId();
```
#### 步骤2: 创建第一把充电枪
为充电桩创建第一把充电枪。
```java
// 伪代码:创建第一把充电枪
GunCreateRequest gun1Request = GunCreateRequest.builder()
.gunName("快充枪1")
.gunNo("01")
.gunCode("GUN001")
.stationId(stationId)
.pileId(pileId)
.build();
Gun gun1 = gunService.createGun(gun1Request);
```
#### 步骤3: 创建第二把充电枪
为同一充电桩创建第二把充电枪。
```java
// 伪代码:创建第二把充电枪
GunCreateRequest gun2Request = GunCreateRequest.builder()
.gunName("快充枪2")
.gunNo("02")
.gunCode("GUN002")
.stationId(stationId)
.pileId(pileId)
.build();
Gun gun2 = gunService.createGun(gun2Request);
```
#### 步骤4: 验证配置结果
查询充电桩下的所有充电枪。
```java
// 伪代码:验证配置
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**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L48-L75)
- [GunMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/GunMapper.java#L1-L132)
## 级联关系与状态同步
### 级联关系管理
充电枪与充电桩之间的级联关系管理。
```mermaid
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
}
```
**图示来源**
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L57)
**级联操作**
- **创建**: 充电枪必须关联到存在的充电桩
- **查询**: 支持通过充电桩查询所有充电枪
- **更新**: 可以修改充电枪所属的充电桩
- **删除**: 删除充电桩时级联删除所有充电枪
**外键约束**
- `pileId`字段确保充电枪必须属于某个充电桩
- 数据库层面保证引用完整性
**Section sources**
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L57)
### 状态同步机制
充电枪状态变化时与充电桩的状态同步。
```mermaid
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
```
**图示来源**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L233-L244)
**同步规则**
```java
private boolean shouldUpdatePileStatus(GunRunStatusEnum gunStatus) {
return switch (gunStatus) {
case CHARGING, DISCHARGING -> true; // 充电或放电时更新
case FAULT -> true; // 故障时更新
default -> false; // 其他状态不更新
};
}
```
**触发条件**
- **充电开始**: 充电枪状态变为`CHARGING`
- **放电开始**: 充电枪状态变为`DISCHARGING`
- **故障发生**: 充电枪状态变为`FAULT`
**同步流程**
1. 充电枪状态发生变化
2. 调用`shouldUpdatePileStatus()`判断是否需要同步
3. 如果需要,通知`PileService`更新充电桩状态
4. 更新充电桩的在线状态和运行状态
**性能考虑**
- 只在必要时触发同步,减少不必要的操作
- 使用异步机制避免阻塞主流程
- 缓存充电桩状态减少数据库查询
**Section sources**
- [DefaultGunService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultGunService.java#L233-L244)

View File

@@ -0,0 +1,699 @@
# 充电桩管理
<cite>
**本文档引用的文件**
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java)
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java)
- [PileCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/PileCreateRequest.java)
- [PileUpdateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/PileUpdateRequest.java)
- [PileWithStatusResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PileWithStatusResponse.java)
- [DefaultPileService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileService.java)
- [PileRepository.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/PileRepository.java)
- [PileRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/PileRepositoryImpl.java)
- [PileStatusEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileStatusEnum.java)
- [PileTypeEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java)
- [StartChargeDTO.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/dto/StartChargeDTO.java)
- [RestartPileDTO.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/dto/RestartPileDTO.java)
- [DefaultPileProtocolService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java)
- [RpcController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/RpcController.java)
</cite>
## 目录
1. [简介](#简介)
2. [数据模型](#数据模型)
3. [RESTful API](#restful-api)
4. [业务逻辑处理](#业务逻辑处理)
5. [远程控制操作](#远程控制操作)
6. [使用示例](#使用示例)
7. [状态机与实时同步](#状态机与实时同步)
## 简介
本文档详细阐述了充电桩管理功能的实现包括充电桩实体的数据模型、RESTful
API接口、业务逻辑处理以及远程控制操作。系统采用分层架构通过Pile实体表示充电桩PileController提供RESTful
APIDefaultPileService处理业务逻辑PileRepository负责数据持久化DownlinkCallService实现远程控制指令的发送。
## 数据模型
充电桩管理功能的核心是Pile实体它表示一个物理充电桩包含其基本信息、状态和与充电站的关系。
```mermaid
erDiagram
PILE {
uuid id PK
timestamp createdTime
timestamp updatedTime
string pileName
string pileCode UK
string protocol
uuid stationId FK
string brand
string model
string manufacturer
string type
integer version
json additionalInfo
}
STATION {
uuid id PK
string stationName
string stationCode UK
}
PILE ||--o{ STATION : "属于"
```
**图源**
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java#L1-L63)
- [PileTypeEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java#L1-L22)
### Pile实体核心属性
Pile实体定义了充电桩的核心属性这些属性在数据库中持久化存储。
| 属性名 | 类型 | 描述 | 约束 |
|----------------|---------------|----------|--------------|
| id | UUID | 充电桩唯一标识符 | 主键 |
| pileName | String | 充电桩名称 | 非空防XSS攻击 |
| pileCode | String | 充电桩编号 | 唯一非空防XSS攻击 |
| protocol | String | 通信协议类型 | 非空防XSS攻击 |
| stationId | UUID | 所属充电站ID | 外键,非空 |
| brand | String | 品牌 | 防XSS攻击 |
| model | String | 型号 | 防XSS攻击 |
| manufacturer | String | 制造商 | 防XSS攻击 |
| type | PileTypeEnum | 充电桩类型 | 枚举值 |
| createdTime | LocalDateTime | 创建时间 | 自动填充 |
| updatedTime | LocalDateTime | 更新时间 | 自动填充 |
| additionalInfo | JsonNode | 附加信息 | JSON格式 |
| version | Integer | 版本号 | 用于乐观锁 |
**节源**
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java#L1-L63)
### PileStatusEnum状态枚举
充电桩状态枚举定义了充电桩的在线/离线状态,独立于充电枪的工作状态。
```mermaid
stateDiagram-v2
[*] --> OFFLINE
OFFLINE --> ONLINE : 设备登录成功
OFFLINE --> ONLINE : 心跳正常
ONLINE --> OFFLINE : 设备断开连接
ONLINE --> OFFLINE : 心跳超时
OFFLINE --> OFFLINE : 设备未登录
ONLINE --> ONLINE : 能正常通信
```
充电桩状态设计原则:
- 状态独立于充电枪状态,不受枪的工作状态影响
- 只关注设备的网络连接状态和基础可用性
- 状态转换由设备登录、心跳和连接状态决定
**节源**
- [PileStatusEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileStatusEnum.java#L1-L50)
### PileTypeEnum类型枚举
充电桩类型枚举定义了充电桩的类型,目前支持交流和直流两种类型。
```mermaid
classDiagram
class PileTypeEnum {
+AC : PileTypeEnum
+DC : PileTypeEnum
+getValue() String
}
```
| 枚举值 | 描述 |
|-----|-------|
| AC | 交流充电桩 |
| DC | 直流充电桩 |
**节源**
- [PileTypeEnum.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java#L1-L22)
### 与Station的多对一关系
每个充电桩属于一个充电站形成多对一的关系。这种关系通过Pile实体中的stationId字段实现该字段是外键指向Station实体的id。
```mermaid
classDiagram
class Pile {
-UUID id
-String pileName
-String pileCode
-UUID stationId
+getStationId() UUID
}
class Station {
-UUID id
-String stationName
-String stationCode
+getId() UUID
}
Pile --> Station : "属于"
```
**图源**
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java#L1-L63)
## RESTful API
PileController类暴露了充电桩管理的RESTful API提供了对充电桩的增删改查和远程控制操作。
```mermaid
graph TB
subgraph "PileController"
A[POST /piles]
B[GET /piles]
C[PUT /piles/{id}]
D[DELETE /piles/{id}]
E[GET /piles/{id}]
F[POST /piles/{id}/start-charge]
G[POST /piles/{id}/restart]
end
Client --> A
Client --> B
Client --> C
Client --> D
Client --> E
Client --> F
Client --> G
```
**图源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L1-L111)
### 创建充电桩 (POST /piles)
创建新的充电桩,需要提供充电桩的基本信息。
#### 请求
- **方法**: POST
- **路径**: /api/piles
- **认证**: 需要有效的JWT令牌
- **授权**: 需要管理员权限
#### 请求DTO (PileCreateRequest)
```mermaid
classDiagram
class PileCreateRequest {
+pileName : String
+pileCode : String
+protocol : String
+stationId : UUID
+brand : String
+model : String
+manufacturer : String
+type : PileTypeEnum
}
```
| 字段 | 类型 | 必填 | 描述 |
|--------------|--------------|----|-------------|
| pileName | String | 是 | 充电桩名称 |
| pileCode | String | 是 | 充电桩编号 |
| protocol | String | 是 | 通信协议 |
| stationId | UUID | 是 | 所属充电站ID |
| brand | String | 否 | 品牌 |
| model | String | 否 | 型号 |
| manufacturer | String | 否 | 制造商 |
| type | PileTypeEnum | 否 | 充电桩类型默认为DC |
#### 响应
- **成功**: HTTP 200返回创建的Pile实体
- **失败**: HTTP 400参数验证失败HTTP 409充电桩编号已存在
**节源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L25-L34)
- [PileCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/PileCreateRequest.java#L1-L45)
### 分页查询充电桩 (GET /piles)
分页查询充电桩列表,支持多种查询条件。
#### 请求
- **方法**: GET
- **路径**: /api/piles
- **认证**: 需要有效的JWT令牌
- **授权**: 需要查看权限
#### 请求参数 (PileQueryRequest)
| 参数 | 类型 | 描述 |
|--------------|----------------|-------------|
| page | Integer | 页码从1开始 |
| size | Integer | 每页大小 |
| pileName | String | 充电桩名称(模糊查询) |
| pileCode | String | 充电桩编号 |
| protocol | String | 通信协议 |
| stationId | UUID | 所属充电站ID |
| brand | String | 品牌 |
| model | String | 型号 |
| manufacturer | String | 制造商 |
| type | PileTypeEnum | 充电桩类型 |
| status | PileStatusEnum | 充电桩状态 |
#### 响应DTO (PileWithStatusResponse)
```mermaid
classDiagram
class PileWithStatusResponse {
+id : UUID
+createdTime : LocalDateTime
+updatedTime : LocalDateTime
+pileName : String
+pileCode : String
+protocol : String
+stationId : UUID
+brand : String
+model : String
+manufacturer : String
+type : PileTypeEnum
+status : PileStatusEnum
+connectedAt : Long
+disconnectedAt : Long
+lastActiveTime : Long
+gunCount : Long
}
```
响应包含分页信息和充电桩列表,每个充电桩都包含其状态信息。
#### 响应
- **成功**: HTTP 200返回PageResponse<PileWithStatusResponse>
- **失败**: HTTP 400参数验证失败
**节源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L68-L77)
- [PileQueryRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/PileQueryRequest.java#L1-L36)
- [PileWithStatusResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PileWithStatusResponse.java#L1-L116)
### 更新充电桩 (PUT /piles/{id})
更新现有充电桩的信息。
#### 请求
- **方法**: PUT
- **路径**: /api/piles/{id}
- **认证**: 需要有效的JWT令牌
- **授权**: 需要编辑权限
#### 请求DTO (PileUpdateRequest)
```mermaid
classDiagram
class PileUpdateRequest {
+pileName : String
+protocol : String
+brand : String
+model : String
+manufacturer : String
+type : PileTypeEnum
}
```
| 字段 | 类型 | 必填 | 描述 |
|--------------|--------------|----|-------|
| pileName | String | 是 | 充电桩名称 |
| protocol | String | 是 | 通信协议 |
| brand | String | 否 | 品牌 |
| model | String | 否 | 型号 |
| manufacturer | String | 否 | 制造商 |
| type | PileTypeEnum | 否 | 充电桩类型 |
#### 响应
- **成功**: HTTP 200返回更新后的Pile实体
- **失败**: HTTP 404充电桩不存在HTTP 400参数验证失败
**节源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L40-L50)
- [PileUpdateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/PileUpdateRequest.java#L1-L35)
### 删除充电桩 (DELETE /piles/{id})
删除指定ID的充电桩。
#### 请求
- **方法**: DELETE
- **路径**: /api/piles/{id}
- **认证**: 需要有效的JWT令牌
- **授权**: 需要删除权限
#### 响应
- **成功**: HTTP 200返回成功消息
- **失败**: HTTP 404充电桩不存在HTTP 409充电桩下有充电枪
**节源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L52-L60)
## 业务逻辑处理
DefaultPileService类负责处理充电桩管理的核心业务逻辑包括数据持久化、缓存交互和状态管理。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Controller as "PileController"
participant Service as "DefaultPileService"
participant Repository as "PileRepository"
participant Cache as "PileRedisCache"
participant DB as "数据库"
Client->>Controller : POST /piles
Controller->>Service : createPile(request)
Service->>Repository : findPileByCode(code)
Repository->>Cache : get(pileCode)
Cache-->>Repository : null
Repository->>DB : selectByCode(code)
DB-->>Repository : null
Repository-->>Service : null
Service->>DB : insert(pile)
DB-->>Service : success
Service-->>Controller : Pile
Controller-->>Client : 200 OK
```
**图源**
- [DefaultPileService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileService.java#L1-L329)
- [PileRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/PileRepositoryImpl.java#L1-L49)
### DefaultPileService核心功能
DefaultPileService实现了PileService接口处理充电桩的业务逻辑。
| 方法 | 描述 |
|------------------------|----------------|
| createPile | 创建充电桩,检查编号唯一性 |
| findById | 根据ID查找充电桩 |
| findByPileCode | 根据编号查找充电桩 |
| updatePile | 更新充电桩信息 |
| deletePile | 删除充电桩,检查是否有充电枪 |
| queryPilesWithStatus | 分页查询充电桩及状态 |
| updatePileStatus | 更新充电桩状态 |
| handlePileLogin | 处理充电桩登录 |
| handlePileHeartbeat | 处理充电桩心跳 |
| handlePileSessionClose | 处理充电桩会话关闭 |
**节源**
- [DefaultPileService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileService.java#L1-L329)
### 数据持久化 (PileRepository)
PileRepository接口定义了充电桩的数据访问方法PileRepositoryImpl实现了该接口并提供了缓存支持。
```mermaid
classDiagram
class PileRepository {
+findPileByCode(pileCode : String) : Pile
}
class PileRepositoryImpl {
-pileMapper : PileMapper
-cache : PileRedisCache
+findPileByCode(pileCode : String) : Pile
+handleEvictEvent(event : PileCacheEvictEvent) : void
}
class PileMapper {
+selectByCode(pileCode : String) : Pile
+insert(pile : Pile) : int
+updateById(pile : Pile) : int
+deleteById(id : UUID) : int
}
PileRepository <|-- PileRepositoryImpl
PileRepositoryImpl --> PileMapper : "使用"
PileRepositoryImpl --> PileRedisCache : "使用"
```
PileRepositoryImpl通过继承CachedVersionedEntityRepository获得了缓存功能实现了基于Redis的缓存机制。当充电桩数据发生变化时通过@TransactionalEventListener监听事件并清除缓存
**图源**
- [PileRepository.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/PileRepository.java#L1-L16)
- [PileRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/PileRepositoryImpl.java#L1-L49)
### 缓存交互 (PileRedisCache)
系统使用Redis作为缓存存储PileRedisCache负责充电桩数据的缓存操作。PileRepositoryImpl通过cache字段与PileRedisCache交互实现了读取时的缓存命中和写入时的缓存失效。
缓存键使用PileCacheKey支持通过ID和编号两种方式缓存充电桩数据。当充电桩被更新或删除时PileRepositoryImpl会发布PileCacheEvictEvent事件触发缓存清除。
**节源**
- [PileRepositoryImpl.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/repository/impl/PileRepositoryImpl.java#L1-L49)
## 远程控制操作
系统支持对充电桩进行远程控制操作如启动充电、重启等。这些操作通过RPC机制实现由RpcController接收请求调用PileProtocolService执行。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant RpcController as "RpcController"
participant PileProtocolService as "DefaultPileProtocolService"
participant DownlinkCallService as "DownlinkCallService"
participant Protocol as "协议层"
Client->>RpcController : RPC startCharge
RpcController->>RpcController : handleStartCharge()
RpcController->>PileProtocolService : startCharge(dto)
PileProtocolService->>DownlinkCallService : sendDownlinkMessage()
DownlinkCallService->>Protocol : 发送指令
Protocol-->>DownlinkCallService : 发送成功
DownlinkCallService-->>PileProtocolService : 回调
PileProtocolService-->>RpcController : 返回
RpcController-->>Client : 响应
```
**图源**
- [RpcController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/RpcController.java#L107-L145)
- [DefaultPileProtocolService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java#L412-L440)
### 启动充电 (POST /piles/{id}/start-charge)
远程启动充电桩的充电操作。
#### 实现流程
1. RpcController接收启动充电的RPC请求
2. 解析StartChargeDTO参数
3. 调用PileProtocolService.startCharge方法
4. 构造下行消息RemoteStartChargingRequest
5. 通过DownlinkCallService发送到协议层
6. 协议层将指令发送到充电桩
#### 请求DTO (StartChargeDTO)
```mermaid
classDiagram
class StartChargeDTO {
+pileCode : String
+gunNo : String
+limitYuan : BigDecimal
+orderNo : String
+logicalCardNo : String
+physicalCardNo : String
+parallelNo : String
}
```
| 字段 | 类型 | 必填 | 描述 |
|----------------|------------|----|---------|
| pileCode | String | 是 | 充电桩编号 |
| gunNo | String | 是 | 充电枪编号 |
| limitYuan | BigDecimal | 是 | 限制金额(元) |
| orderNo | String | 是 | 订单号 |
| logicalCardNo | String | 否 | 逻辑卡号 |
| physicalCardNo | String | 否 | 物理卡号 |
| parallelNo | String | 否 | 并充序号 |
**节源**
- [StartChargeDTO.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/dto/StartChargeDTO.java#L1-L61)
- [DefaultPileProtocolService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java#L412-L440)
### 重启充电桩 (POST /piles/{id}/restart)
远程重启充电桩。
#### 实现流程
1. RpcController接收重启充电桩的RPC请求
2. 解析RestartPileDTO参数
3. 调用PileProtocolService.restartPile方法
4. 构造下行消息RestartPileRequest
5. 通过DownlinkCallService发送到协议层
6. 协议层将指令发送到充电桩
#### 请求DTO (RestartPileDTO)
```mermaid
classDiagram
class RestartPileDTO {
+pileCode : String
+type : Integer
}
```
| 字段 | 类型 | 必填 | 描述 |
|----------|---------|----|-------|
| pileCode | String | 是 | 充电桩编号 |
| type | Integer | 是 | 重启类型 |
**节源**
- [RestartPileDTO.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/dto/RestartPileDTO.java#L1-L32)
- [DefaultPileProtocolService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java#L506-L531)
## 使用示例
以下示例演示如何为一个充电站添加充电桩并远程启动充电。
### 为充电站添加充电桩
```http
POST /api/piles HTTP/1.1
Content-Type: application/json
Authorization: Bearer <token>
```
响应:
```json
{
"code": 200,
"message": "创建成功",
"data": {
"id": "f1e2d3c4-b5a6-9876-5432-10fedcba9876",
"createdTime": "2024-01-01T10:00:00",
"pileName": "直流快充桩1号",
"pileCode": "DCP001",
"protocol": "LVNENG_V340",
"stationId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"brand": "特来电",
"model": "DC-80KW",
"manufacturer": "特来电新能源有限公司",
"type": "DC"
}
}
```
### 远程启动充电
```http
POST /rpc HTTP/1.1
Content-Type: application/json
Authorization: Bearer <token>
```
**节源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L25-L34)
- [RpcController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/RpcController.java#L107-L145)
## 状态机与实时同步
充电桩状态机管理充电桩的在线/离线状态,通过实时同步机制确保状态的准确性。
```mermaid
stateDiagram-v2
[*] --> OFFLINE
OFFLINE --> ONLINE : login
ONLINE --> OFFLINE : session close
ONLINE --> ONLINE : heartbeat
OFFLINE --> OFFLINE : no activity
note right of ONLINE
状态属性 :
- status : ONLINE
- connectedAt : 时间戳
- lastActiveTime : 时间戳
end note
note right of OFFLINE
状态属性 :
- status : OFFLINE
- disconnectedAt : 时间戳
end note
```
### 状态机实现
充电桩状态机基于三个核心事件实现:登录、心跳和会话关闭。
| 事件 | 当前状态 | 新状态 | 更新的属性 |
|------|--------|---------|-------------------------------------|
| 登录 | 任意 | ONLINE | status, connectedAt, lastActiveTime |
| 心跳 | ONLINE | ONLINE | status, lastActiveTime |
| 会话关闭 | ONLINE | OFFLINE | status, disconnectedAt |
**节源**
- [DefaultPileService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileService.java#L240-L300)
### 实时状态同步
系统通过AttributeService将充电桩状态存储在Redis中实现状态的实时同步。
```mermaid
flowchart TD
A[充电桩登录] --> B[调用handlePileLogin]
B --> C[更新状态属性]
C --> D[保存到Redis]
D --> E[状态同步完成]
F[充电桩心跳] --> G[调用handlePileHeartbeat]
G --> H[更新状态属性]
H --> D
I[会话关闭] --> J[调用handlePileSessionClose]
J --> K[更新状态属性]
K --> D
```
状态属性存储在Redis的哈希结构中键名为`pile:attributes:{pileId}`,字段名为状态属性名,如`STATUS``CONNECTED_AT`
等。这种设计支持高效的状态查询和更新。
**节源**
- [DefaultPileService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileService.java#L240-L300)
```mermaid
flowchart TD
A[充电桩登录] --> B[调用handlePileLogin]
B --> C[更新状态属性]
C --> D[保存到Redis]
D --> E[状态同步完成]
F[充电桩心跳] --> G[调用handlePileHeartbeat]
G --> H[更新状态属性]
H --> D
I[会话关闭] --> J[调用handlePileSessionClose]
J --> K[更新状态属性]
K --> D
```
状态属性存储在Redis的哈希结构中键名为`pile:attributes:{pileId}`,字段名为状态属性名,如`STATUS``CONNECTED_AT`
等。这种设计支持高效的状态查询和更新。
**节源**
- [DefaultPileService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileService.java#L240-L300)

View File

@@ -0,0 +1,444 @@
# 充电站管理
<cite>
**本文档引用的文件**
- [Station.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java)
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java)
- [StationMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java)
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java)
- [PileMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/PileMapper.java)
- [StationCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationCreateRequest.java)
- [StationUpdateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationUpdateRequest.java)
- [StationQueryRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationQueryRequest.java)
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java)
- [schema-init.sql](file://jcpp-app/src/main/resources/sql/schema-init.sql)
</cite>
## 目录
1. [引言](#引言)
2. [数据模型](#数据模型)
3. [RESTful API接口](#restful-api接口)
4. [服务层实现](#服务层实现)
5. [使用示例](#使用示例)
6. [分页查询机制](#分页查询机制)
7. [级联删除逻辑](#级联删除逻辑)
## 引言
充电站管理功能是本系统的核心模块之一负责充电站的全生命周期管理。该功能提供了完整的CRUD操作支持通过RESTful
API对充电站进行创建、查询、更新和删除。系统采用分层架构设计包含控制器层、服务层和数据访问层确保了代码的可维护性和扩展性。充电站实体与充电桩实体之间存在一对多的关系系统在删除充电站时会进行级联检查确保数据完整性。
## 数据模型
### Station实体
`Station`实体是充电站管理的核心数据模型,定义了充电站的基本属性和元数据。该实体映射到数据库中的`t_station`使用MyBatis
Plus框架进行ORM映射。
```mermaid
classDiagram
class Station {
+UUID id
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+String stationName
+String stationCode
+Float longitude
+Float latitude
+String province
+String city
+String county
+String address
+Integer version
}
class Pile {
+UUID id
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+String pileName
+String pileCode
+String protocol
+UUID stationId
+String brand
+String model
+String manufacturer
+PileTypeEnum type
+Integer version
}
Station "1" *-- "0..*" Pile : 包含
```
**Diagram sources**
- [Station.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java#L1-L65)
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java#L1-L64)
#### 核心属性说明
- **id**: 充电站唯一标识符使用UUID类型作为主键
- **createdTime**: 创建时间,记录充电站创建的精确时间戳
- **updatedTime**: 更新时间,每次更新时自动更新
- **stationName**: 充电站名称,必填字段,用于标识充电站
- **stationCode**: 充电站编码,必填字段,具有唯一性约束
- **longitude/latitude**: 地理位置坐标,用于地图展示和定位
- **province/city/county/address**: 地址信息,分别表示省、市、区县和详细地址
- **additionalInfo**: 附加信息使用JSON格式存储扩展属性
- **version**: 版本号,用于乐观锁控制,防止并发更新冲突
**Section sources**
- [Station.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java#L1-L65)
- [schema-init.sql](file://jcpp-app/src/main/resources/sql/schema-init.sql#L34-L58)
## RESTful API接口
### API端点概览
`StationController`提供了完整的RESTful API端点用于管理充电站资源。所有API均遵循REST设计原则使用标准的HTTP方法和状态码。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Controller as "StationController"
participant Service as "DefaultStationService"
participant Mapper as "StationMapper"
Client->>Controller : POST /api/stations
Controller->>Service : createStation(request)
Service->>Mapper : insert(station)
Mapper-->>Service : 返回结果
Service-->>Controller : 返回充电站
Controller-->>Client : 200 OK {station}
Client->>Controller : GET /api/stations
Controller->>Service : getStations(request)
Service->>Mapper : selectPage(wrapper)
Mapper-->>Service : 返回分页结果
Service-->>Controller : 返回PageResponse
Controller-->>Client : 200 OK {PageResponse}
Client->>Controller : PUT /api/stations/{id}
Controller->>Service : updateStation(id, request)
Service->>Mapper : selectById(id)
Mapper-->>Service : 返回充电站
Service->>Mapper : updateById(station)
Mapper-->>Service : 返回结果
Service-->>Controller : 返回充电站
Controller-->>Client : 200 OK {station}
Client->>Controller : DELETE /api/stations/{id}
Controller->>Service : deleteStation(id)
Service->>Mapper : selectById(id)
Mapper-->>Service : 返回充电站
Service->>Mapper : countByStationId(id)
Mapper-->>Service : 返回数量
Service->>Mapper : deleteById(id)
Mapper-->>Service : 返回结果
Service-->>Controller : 返回成功
Controller-->>Client : 200 OK {success}
```
**Diagram sources**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L1-L107)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L1-L247)
### 详细API说明
#### 创建充电站 (POST /api/stations)
创建新的充电站资源。
- **请求参数**: `StationCreateRequest`对象
- **请求体示例**:
```json
{
"stationName": "三丙家专属充电站",
"stationCode": "S20241001001",
"longitude": 120.107933,
"latitude": 30.267013,
"province": "浙江省",
"city": "杭州市",
"county": "西湖区",
"address": "西溪路552-1号"
}
```
- **响应格式**: `ApiResponse<Station>`
- **权限要求**: `AuthorityEnum.STATION_MANAGE`
#### 分页查询充电站 (GET /api/stations)
分页查询充电站列表,支持多种查询条件。
- **请求参数**:
- `page`: 页码默认0
- `size`: 每页大小默认10
- `stationName`: 充电站名称(模糊查询)
- `stationCode`: 充电站编码(模糊查询)
- `province`: 省份(精确查询)
- `city`: 城市(精确查询)
- `sortField`: 排序字段
- `sortOrder`: 排序顺序asc/desc
- **响应格式**: `ApiResponse<PageResponse<Station>>`
- **权限要求**: `AuthorityEnum.STATION_MANAGE`
#### 更新充电站 (PUT /api/stations/{id})
更新指定ID的充电站信息。
- **路径参数**: `id` - 充电站UUID
- **请求参数**: `StationUpdateRequest`对象
- **请求体示例**:
```json
{
"stationName": "三丙家专属充电站(更新)",
"longitude": 120.108000,
"latitude": 30.267100,
"province": "浙江省",
"city": "杭州市",
"county": "西湖区",
"address": "西溪路552-1号(更新)"
}
```
- **响应格式**: `ApiResponse<Station>`
- **权限要求**: `AuthorityEnum.STATION_MANAGE`
#### 删除充电站 (DELETE /api/stations/{id})
删除指定ID的充电站。
- **路径参数**: `id` - 充电站UUID
- **响应格式**: `ApiResponse<Void>`
- **权限要求**: `AuthorityEnum.STATION_MANAGE`
- **业务逻辑**: 删除前会检查该充电站下是否存在充电桩,如果存在则不允许删除
#### 获取充电站选项 (GET /api/stations/options)
获取充电站选项列表,用于下拉选择组件。
- **响应格式**: `ApiResponse<List<StationOption>>`
- **权限要求**: `AuthorityEnum.STATION_MANAGE`
#### 搜索充电站选项 (GET /api/stations/search)
搜索充电站选项,支持关键字搜索和分页。
- **请求参数**:
- `keyword`: 搜索关键字
- `page`: 页码
- `size`: 每页大小
- **响应格式**: `ApiResponse<List<StationOption>>`
- **权限要求**: `AuthorityEnum.STATION_MANAGE`
**Section sources**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L1-L107)
- [StationCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationCreateRequest.java#L1-L44)
- [StationUpdateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationUpdateRequest.java#L1-L40)
- [StationQueryRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationQueryRequest.java#L1-L29)
## 服务层实现
### DefaultStationService
`DefaultStationService`是充电站管理的核心服务实现类,协调`StationMapper``PileMapper`进行数据库操作,并处理业务逻辑。
```mermaid
flowchart TD
A[API请求] --> B{操作类型}
B --> |创建| C[createStation]
B --> |查询| D[getStations]
B --> |更新| E[updateStation]
B --> |删除| F[deleteStation]
C --> G[构建Station实体]
G --> H[设置默认值]
H --> I[调用stationMapper.insert]
D --> J[构建QueryWrapper]
J --> K[添加查询条件]
K --> L[调用stationMapper.selectPage]
L --> M[构建PageResponse]
E --> N[查询现有Station]
N --> O[更新字段]
O --> P[调用stationMapper.updateById]
F --> Q[检查Station存在]
Q --> R[检查充电桩数量]
R --> |有充电桩| S[抛出异常]
R --> |无充电桩| T[调用stationMapper.deleteById]
```
**Diagram sources**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L1-L247)
#### 核心方法说明
- **getStations**: 实现分页查询功能,根据`StationQueryRequest`中的条件构建查询条件,支持按名称、编码、省份、城市等字段进行查询,并支持排序功能
- **getStationById**: 根据ID查询单个充电站信息
- **createStation**: 创建新的充电站自动生成UUID和创建时间初始化版本号为1
- **updateStation**: 更新充电站信息,更新时会自动设置`updatedTime`字段
- **deleteStation**: 删除充电站前会进行完整性检查,确保该充电站下没有关联的充电桩
- **getStationOptions**: 获取充电站选项列表,用于前端下拉选择
- **searchStationOptions**: 支持关键字搜索的充电站选项查询
**Section sources**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L1-L247)
- [StationMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java#L1-L15)
## 使用示例
### 创建充电站完整流程
以下是一个通过API创建新充电站并验证其存在的完整示例
```mermaid
sequenceDiagram
participant User as "用户"
participant Frontend as "前端应用"
participant Backend as "后端服务"
participant Database as "数据库"
User->>Frontend : 填写充电站信息并提交
Frontend->>Backend : POST /api/stations
Backend->>Backend : 验证请求参数
Backend->>Backend : 调用DefaultStationService.createStation
Backend->>Backend : 构建Station实体
Backend->>Database : 执行INSERT语句
Database-->>Backend : 返回插入结果
Backend-->>Frontend : 返回创建的充电站信息
Frontend-->>User : 显示创建成功消息
User->>Frontend : 查询充电站列表
Frontend->>Backend : GET /api/stations
Backend->>Backend : 调用DefaultStationService.getStations
Backend->>Database : 执行SELECT查询
Database-->>Backend : 返回查询结果
Backend-->>Frontend : 返回分页响应
Frontend-->>User : 显示包含新充电站的列表
```
**Diagram sources**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L1-L107)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L1-L247)
#### 示例代码
```java
// 创建充电站请求
StationCreateRequest request = new StationCreateRequest();
request.setStationName("新充电站");
request.setStationCode("NEW001");
request.setLongitude(120.123456f);
request.setLatitude(30.654321f);
request.setProvince("浙江省");
request.setCity("杭州市");
request.setCounty("西湖区");
request.setAddress("文三路168号");
// 调用API创建充电站
ResponseEntity<ApiResponse<Station>> response = restTemplate.postForEntity(
"/api/stations",
request,
new ParameterizedTypeReference<ApiResponse<Station>>() {}
);
// 验证创建结果
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
Station createdStation = response.getBody().getData();
assertThat(createdStation.getStationName()).isEqualTo("新充电站");
// 查询验证
ResponseEntity<ApiResponse<PageResponse<Station>>> queryResponse = restTemplate.getForEntity(
"/api/stations?stationName=新充电站",
new ParameterizedTypeReference<ApiResponse<PageResponse<Station>>>() {}
);
assertThat(queryResponse.getBody().getData().getTotal()).isGreaterThan(0);
```
**Section sources**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L1-L107)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L1-L247)
- [InstallInitializingBean.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/initializing/InstallInitializingBean.java#L218-L233)
## 分页查询机制
### 分页实现原理
系统采用MyBatis Plus的分页插件实现分页查询功能通过`Page`对象和`IPage`接口提供分页支持。
```mermaid
flowchart LR
A[客户端请求] --> B[StationController]
B --> C[DefaultStationService]
C --> D[构建QueryWrapper]
D --> E[创建Page对象]
E --> F[调用stationMapper.selectPage]
F --> G[数据库执行分页查询]
G --> H[返回IPage结果]
H --> I[构建PageResponse]
I --> J[返回客户端]
```
**Diagram sources**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L65-L99)
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java#L1-L43)
#### PageResponse结构
`PageResponse`类封装了分页查询的响应数据,包含以下字段:
- **records**: 当前页的数据记录列表
- **total**: 总记录数
- **page**: 当前页码
- **size**: 每页大小
- **totalPages**: 总页数
分页计算公式:`totalPages = ceil(total / size)`
**Section sources**
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java#L1-L43)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L65-L99)
## 级联删除逻辑
### 删除业务流程
系统在删除充电站时实现了严格的级联检查机制,确保数据完整性。
```mermaid
flowchart TD
A[删除请求] --> B{充电站存在?}
B --> |否| C[抛出ITEM_NOT_FOUND异常]
B --> |是| D{有充电桩?}
D --> |是| E[抛出VERSION_CONFLICT异常]
D --> |否| F[执行删除操作]
F --> G{删除成功?}
G --> |否| H[抛出VERSION_CONFLICT异常]
G --> |是| I[返回成功]
```
**Diagram sources**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L150-L185)
#### 详细逻辑说明
1. **存在性检查**: 首先通过`stationMapper.selectById(id)`检查充电站是否存在,如果不存在则抛出`ITEM_NOT_FOUND`异常
2. **关联检查**: 调用`pileMapper.countByStationId(id)`统计该充电站下的充电桩数量如果数量大于0则抛出`VERSION_CONFLICT`
异常,提示用户先删除所有充电桩
3. **执行删除**: 通过`stationMapper.deleteById(id)`执行删除操作
4. **结果验证**: 检查受影响的行数如果为0则说明删除失败可能是被其他操作删除抛出`VERSION_CONFLICT`异常
这种设计确保了数据的一致性和完整性,防止意外删除包含充电桩的充电站。
**Section sources**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L150-L185)
- [PileMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/PileMapper.java#L60-L65)

View File

@@ -0,0 +1,658 @@
# 设备管理模块
<cite>
**本文档引用的文件**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java)
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java)
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java)
- [Station.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java)
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java)
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java)
- [StationCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationCreateRequest.java)
- [PileCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/PileCreateRequest.java)
- [GunCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/GunCreateRequest.java)
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java)
- [StationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/StationService.java)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java)
- [StationMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java)
- [StationRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/station/StationRedisCache.java)
</cite>
## 目录
1. [引言](#引言)
2. [核心数据模型](#核心数据模型)
3. [RESTful API 接口](#restful-api-接口)
4. [业务服务层设计](#业务服务层设计)
5. [完整使用示例](#完整使用示例)
6. [复杂业务逻辑实现](#复杂业务逻辑实现)
## 引言
设备管理模块是充电站管理系统的核心组成部分负责管理充电站Station、充电桩Pile和充电枪Gun三个关键实体。本模块提供了完整的CRUD操作支持分页查询、级联选择和状态管理等功能。系统采用分层架构设计包括控制器层、服务层、数据访问层和缓存层确保了系统的可维护性和高性能。
## 核心数据模型
### 实体层次关系
设备管理模块中的三个核心实体构成了一个清晰的层次结构一个充电站Station可以包含多个充电桩Pile而每个充电桩又可以连接多个充电枪Gun。这种层级关系通过外键关联实现确保了数据的一致性和完整性。
```mermaid
classDiagram
class Station {
+UUID id
+String stationName
+String stationCode
+Float longitude
+Float latitude
+String province
+String city
+String county
+String address
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+Integer version
}
class Pile {
+UUID id
+String pileName
+String pileCode
+String protocol
+UUID stationId
+String brand
+String model
+String manufacturer
+PileTypeEnum type
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+Integer version
}
class Gun {
+UUID id
+String gunNo
+String gunName
+String gunCode
+UUID stationId
+UUID pileId
+LocalDateTime createdTime
+LocalDateTime updatedTime
+JsonNode additionalInfo
+Integer version
}
Station "1" *-- "0..*" Pile : 包含
Pile "1" *-- "0..*" Gun : 包含
```
**图示来源**
- [Station.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java#L1-L65)
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java#L1-L64)
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L56)
### 数据模型详细说明
#### 充电站Station
充电站实体代表一个物理的充电站点包含站点的基本信息和地理位置数据。每个充电站都有唯一的ID标识通过`stationCode`
字段保证编码的唯一性。实体还包含了省份、城市、区县和详细地址等完整的地理位置信息,便于进行区域管理和查询。
**核心字段说明**
- `id`: 充电站唯一标识符使用UUID类型
- `stationName`: 充电站名称,必填字段,用于显示
- `stationCode`: 充电站编码,必填字段,用于系统识别
- `longitude`/`latitude`: 经纬度坐标,用于地图定位
- `province`/`city`/`county`/`address`: 完整的地址信息
- `version`: 版本号,用于乐观锁控制
**实体来源**
- [Station.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java#L1-L65)
#### 充电桩Pile
充电桩实体代表安装在充电站内的具体充电设备。每个充电桩都归属于一个特定的充电站,通过`stationId`
外键进行关联。充电桩包含了设备的品牌、型号、制造商等信息,并指定了通信协议类型,以便系统能够正确处理不同厂商设备的通信需求。
**核心字段说明**
- `id`: 充电桩唯一标识符
- `pileName`: 充电桩名称
- `pileCode`: 充电桩编码
- `protocol`: 通信协议类型,决定设备的通信方式
- `stationId`: 所属充电站ID建立与Station的关联
- `type`: 充电桩类型(直流/交流)
- `version`: 版本号,用于并发控制
**实体来源**
- [Pile.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java#L1-L64)
#### 充电枪Gun
充电枪实体代表充电桩上的具体充电接口。每个充电枪都归属于一个特定的充电桩,通过`pileId`
外键进行关联。充电枪包含了枪号gunNo和编码gunCode等识别信息这些信息通常与物理设备上的标签对应便于现场管理和维护。
**核心字段说明**
- `id`: 充电枪唯一标识符
- `gunNo`: 充电枪编号,通常为物理标识
- `gunName`: 充电枪名称
- `gunCode`: 充电枪编码,系统唯一标识
- `stationId`: 所属充电站ID冗余字段便于查询
- `pileId`: 所属充电桩ID建立与Pile的关联
- `version`: 版本号,支持并发更新
**实体来源**
- [Gun.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java#L1-L56)
## RESTful API 接口
### 控制器概览
设备管理模块提供了三个主要的REST控制器`StationController``PileController``GunController`
分别对应三个核心实体的管理操作。这些控制器遵循RESTful设计原则使用标准的HTTP方法GET、POST、PUT、DELETE来执行相应的CRUD操作。
```mermaid
graph TD
A[客户端] --> B[StationController]
A --> C[PileController]
A --> D[GunController]
B --> E[StationService]
C --> F[PileService]
D --> G[GunService]
E --> H[StationMapper]
F --> I[PileMapper]
G --> J[GunMapper]
E --> K[StationRedisCache]
F --> L[PileRedisCache]
G --> M[GunRedisCache]
style A fill:#f9f,stroke:#333
style B fill:#bbf,stroke:#333
style C fill:#bbf,stroke:#333
style D fill:#bbf,stroke:#333
```
**图示来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L1-L107)
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L1-L111)
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L1-L115)
### StationController API
#### 创建充电站
创建新的充电站实体。
- **端点**: `POST /api/stations`
- **请求体**: `StationCreateRequest`
- **响应**: `ApiResponse<Station>`
- **权限控制**: 需要管理员权限
**请求DTO结构**
```json
{
"stationName": "string",
"stationCode": "string",
"longitude": 0,
"latitude": 0,
"province": "string",
"city": "string",
"county": "string",
"address": "string"
}
```
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L50-L57)
- [StationCreateRequest.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/request/StationCreateRequest.java#L1-L44)
#### 查询充电站
支持分页查询和条件筛选。
- **端点**: `GET /api/stations`
- **请求参数**: `StationQueryRequest`
- **响应**: `ApiResponse<PageResponse<Station>>`
**分页响应结构**
```json
{
"records": [...],
"total": 100,
"page": 1,
"size": 20,
"totalPages": 5
}
```
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L30-L37)
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java#L1-L43)
#### 更新充电站
根据ID更新充电站信息。
- **端点**: `PUT /api/stations/{id}`
- **请求体**: `StationUpdateRequest`
- **响应**: `ApiResponse<Station>`
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L60-L68)
#### 删除充电站
删除指定ID的充电站。
- **端点**: `DELETE /api/stations/{id}`
- **响应**: `ApiResponse<Void>`
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L71-L77)
#### 获取选项列表
为前端下拉组件提供数据。
- **端点**: `GET /api/stations/options`
- **响应**: `ApiResponse<List<StationOption>>`
**选项响应结构**
```json
[
{
"id": "uuid",
"label": "名称 (编码)",
"stationName": "名称",
"stationCode": "编码"
}
]
```
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L80-L88)
- [StationOption.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/StationOption.java#L1-L39)
### PileController API
#### 创建充电桩
创建新的充电桩实体。
- **端点**: `POST /api/piles`
- **请求体**: `PileCreateRequest`
- **响应**: `ApiResponse<Pile>`
**来源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L35-L42)
#### 查询充电桩状态
获取充电桩的实时运行状态。
- **端点**: `GET /api/piles/status/{pileCode}`
- **响应**: `ApiResponse<String>`
**来源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L90-L111)
### GunController API
#### 创建充电枪
创建新的充电枪实体。
- **端点**: `POST /api/guns`
- **请求体**: `GunCreateRequest`
- **响应**: `ApiResponse<Gun>`
**来源**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L35-L42)
#### 查询充电枪状态
根据枪编码获取充电枪的运行状态。
- **端点**: `GET /api/guns/status/{gunCode}`
- **响应**: `ApiResponse<String>`
**来源**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L80-L100)
#### 按编码查询充电枪
根据枪编码获取充电枪的详细信息。
- **端点**: `GET /api/guns/code/{gunCode}`
- **响应**: `ApiResponse<GunWithStatusResponse>`
**来源**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L103-L115)
## 业务服务层设计
### 服务层架构
业务服务层是设备管理模块的核心,负责协调数据访问层和缓存层的操作。`DefaultStationService``DefaultPileService`
`DefaultGunService`分别实现了对应的接口,提供了具体的业务逻辑处理。
```mermaid
classDiagram
class StationService {
<<interface>>
+getStations(StationQueryRequest) PageResponse~Station~
+getStationById(UUID) Station
+createStation(StationCreateRequest) Station
+updateStation(UUID, StationUpdateRequest) Station
+deleteStation(UUID) void
+getStationOptions() StationOption[]
+searchStationOptions(String, int, int) StationOption[]
}
class DefaultStationService {
-stationMapper StationMapper
-stationRedisCache StationRedisCache
+getStations(StationQueryRequest) PageResponse~Station~
+getStationById(UUID) Station
+createStation(StationCreateRequest) Station
+updateStation(UUID, StationUpdateRequest) Station
+deleteStation(UUID) void
+getStationOptions() StationOption[]
+searchStationOptions(String, int, int) StationOption[]
}
class StationMapper {
<<interface>>
+selectPage(StationQueryRequest) Page~Station~
+selectById(UUID) Station
+insert(Station) int
+updateById(Station) int
+deleteById(UUID) int
+selectOptions() StationOption[]
+searchOptions(String, int, int) StationOption[]
}
class StationRedisCache {
-redisTemplate RedisTemplate
+get(UUID) Station
+put(Station) void
+evict(UUID) void
+evictAll() void
}
StationService <|.. DefaultStationService : 实现
DefaultStationService --> StationMapper : 使用
DefaultStationService --> StationRedisCache : 使用
```
**图示来源**
- [StationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/StationService.java#L1-L67)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java)
- [StationMapper.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java)
- [StationRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/station/StationRedisCache.java)
### DefaultStationService 实现细节
#### 创建充电站流程
`DefaultStationService.createStation()`方法负责创建新的充电站实体。该方法首先验证输入数据的有效性然后生成新的UUID作为实体ID设置创建和更新时间戳最后将实体保存到数据库。
```mermaid
flowchart TD
Start([开始]) --> ValidateInput["验证输入数据"]
ValidateInput --> GenerateId["生成UUID"]
GenerateId --> SetTimestamps["设置创建/更新时间"]
SetTimestamps --> SaveToDB["保存到数据库"]
SaveToDB --> UpdateCache["更新Redis缓存"]
UpdateCache --> ReturnResult["返回结果"]
ReturnResult --> End([结束])
```
**来源**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L50-L70)
#### 查询充电站流程
`DefaultStationService.getStations()`方法实现了分页查询功能。该方法首先尝试从Redis缓存中获取数据如果缓存未命中则从数据库查询并将结果存入缓存以提高后续查询的性能。
```mermaid
flowchart TD
Start([开始]) --> CheckCache["检查Redis缓存"]
CheckCache --> CacheHit{"缓存命中?"}
CacheHit --> |是| ReturnFromCache["从缓存返回"]
CacheHit --> |否| QueryDB["查询数据库"]
QueryDB --> SaveToCache["保存到缓存"]
SaveToCache --> ReturnFromDB["从数据库返回"]
ReturnFromCache --> End([结束])
ReturnFromDB --> End
```
**来源**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L30-L45)
#### 删除充电站流程
`DefaultStationService.deleteStation()`方法实现了充电站的删除操作。该方法不仅删除充电站实体本身,还会级联删除其下属的所有充电桩和充电枪,确保数据的一致性。
```mermaid
flowchart TD
Start([开始]) --> ValidateExistence["验证充电站存在"]
ValidateExistence --> DeleteGuns["删除所有充电枪"]
DeleteGuns --> DeletePiles["删除所有充电桩"]
DeletePiles --> DeleteStation["删除充电站"]
DeleteStation --> EvictCache["清除缓存"]
EvictCache --> End([结束])
```
**来源**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L120-L150)
## 完整使用示例
### 创建包含多个充电桩的充电站
以下示例展示了如何通过API创建一个包含多个充电桩的充电站。
#### 步骤1: 创建充电站
```http
POST /api/stations
Content-Type: application/json
{
"stationName": "",
"stationCode": "ST001",
"longitude": 116.397026,
"latitude": 39.909026,
"province": "",
"city": "",
"county": "",
"address": "1"
}
```
**响应**
```json
{
"code": 200,
"message": "创建成功",
"data": {
"id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"stationName": "高新科技园充电站",
"stationCode": "ST001",
// 其他字段...
}
}
```
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L50-L57)
#### 步骤2: 创建充电桩
使用上一步返回的充电站ID创建两个充电桩。
```http
POST /api/piles
Content-Type: application/json
{
"pileName": "A",
"pileCode": "PL001",
"protocol": "LVNENG_V340",
"stationId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"brand": "绿",
"model": "GN-800",
"manufacturer": "绿",
"type": "DC"
}
```
**来源**
- [PileController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/PileController.java#L35-L42)
#### 步骤3: 创建充电枪
为每个充电桩创建两个充电枪。
```http
POST /api/guns
Content-Type: application/json
{
"gunName": "A1",
"gunNo": "A1",
"gunCode": "GN001",
"stationId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"pileId": "b2c3d4e5-f678-9012-3456-7890abcdef12"
}
```
**来源**
- [GunController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/GunController.java#L35-L42)
#### 步骤4: 验证创建结果
通过查询接口验证整个层级结构是否正确创建。
```http
GET /api/stations/a1b2c3d4-e5f6-7890-1234-567890abcdef
```
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L40-L47)
## 复杂业务逻辑实现
### 分页查询实现
分页查询功能通过`PageResponse`类和MyBatis Plus的分页插件实现。`StationService.getStations()`方法接收`StationQueryRequest`
参数,该参数包含了分页信息(页码和每页大小)以及可能的查询条件。
```mermaid
sequenceDiagram
participant Client as 客户端
participant Controller as StationController
participant Service as DefaultStationService
participant Mapper as StationMapper
participant DB as 数据库
Client->>Controller : GET /api/stations?page=1&size=20
Controller->>Service : getStations(request)
Service->>Service : 从缓存获取或生成缓存键
Service->>Service : 检查缓存是否存在
alt 缓存命中
Service-->>Controller : 返回缓存数据
else 缓存未命中
Service->>Mapper : selectPage(request)
Mapper->>DB : 执行SQL查询
DB-->>Mapper : 返回分页结果
Mapper-->>Service : Page<Station>
Service->>Service : 转换为PageResponse
Service->>Service : 存入缓存
Service-->>Controller : PageResponse<Station>
end
Controller->>Controller : 包装为ApiResponse
Controller-->>Client : 返回JSON响应
```
**来源**
- [StationController.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/controller/StationController.java#L30-L37)
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L30-L45)
- [PageResponse.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/adapter/response/PageResponse.java#L1-L43)
### 级联删除实现
级联删除是设备管理模块中的一个重要功能,确保在删除充电站时,其下属的所有充电桩和充电枪也被正确删除。
```mermaid
flowchart TD
Start([开始]) --> ValidateId["验证ID有效性"]
ValidateId --> CheckExistence["检查充电站是否存在"]
CheckExistence --> |不存在| ReturnError["返回404错误"]
CheckExistence --> |存在| BeginTransaction["开始数据库事务"]
BeginTransaction --> DeleteGuns["删除所有充电枪"]
DeleteGuns --> DeletePiles["删除所有充电桩"]
DeletePiles --> DeleteStation["删除充电站"]
DeleteStation --> ClearCache["清除相关缓存"]
ClearCache --> CommitTransaction["提交事务"]
CommitTransaction --> ReturnSuccess["返回成功"]
ReturnError --> End([结束])
ReturnSuccess --> End
Exception --> RollbackTransaction["回滚事务"]
RollbackTransaction --> ThrowException["抛出异常"]
ThrowException --> End
```
**来源**
- [DefaultStationService.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultStationService.java#L120-L150)
### 缓存管理策略
系统采用了多级缓存策略结合了Redis和本地缓存Caffeine以提高数据访问性能。
#### 缓存键设计
缓存键遵循统一的命名规范,便于管理和监控:
- 充电站缓存键:`station:{id}`
- 充电桩缓存键:`pile:{id}`
- 充电枪缓存键:`gun:{id}`
#### 缓存失效策略
当实体被创建、更新或删除时,相关的缓存会被自动清除:
- 创建:新实体存入缓存
- 更新:更新缓存中的实体
- 删除:从缓存中移除实体
**来源**
- [StationRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/station/StationRedisCache.java)
- [PileRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileRedisCache.java)
- [GunRedisCache.java](file://jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/gun/GunRedisCache.java)