mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-07-17 04:07:51 +08:00
444 lines
16 KiB
Markdown
444 lines
16 KiB
Markdown
# 充电站管理
|
||
|
||
<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) |