2025-12-30 15:59:34 +08:00
|
|
|
|
# JCPP 项目充电桩数据同步接口实现 Prompt
|
|
|
|
|
|
|
|
|
|
|
|
> 请将以下内容复制给 JCPP 项目的 Claude Code 执行
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 任务概述
|
|
|
|
|
|
|
|
|
|
|
|
需要在 JCPP 项目中实现充电桩数据同步接口,用于接收来自 Web 项目的充电桩和充电枪数据。
|
|
|
|
|
|
|
|
|
|
|
|
## 背景说明
|
|
|
|
|
|
|
|
|
|
|
|
Web 项目(MySQL)中维护了充电桩的主数据,现在需要将这些数据同步到 JCPP 项目(PostgreSQL)中,用于登录鉴权等操作。
|
|
|
|
|
|
|
|
|
|
|
|
### 数据流向
|
|
|
|
|
|
```
|
|
|
|
|
|
Web 项目 (MySQL) → HTTP API → JCPP 项目 (PostgreSQL)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 关键差异
|
|
|
|
|
|
1. **主键类型**:Web 使用 int,JCPP 使用 uuid
|
|
|
|
|
|
2. **station_id 类型**:Web 是 int,JCPP 是 uuid(需要映射)
|
|
|
|
|
|
3. **字段名称**:sn → pile_code, name → pile_name
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 需要实现的功能
|
|
|
|
|
|
|
2025-12-31 16:38:27 +08:00
|
|
|
|
### 1. 认证接口
|
|
|
|
|
|
|
|
|
|
|
|
**接口路径**:`POST /api/auth/login`
|
|
|
|
|
|
|
|
|
|
|
|
**请求格式**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"username": "sanbing",
|
|
|
|
|
|
"password": "password123"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**响应格式**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
|
|
|
|
"expiresIn": 1800
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**说明**:
|
|
|
|
|
|
- 用于 Web 项目获取访问令牌
|
|
|
|
|
|
- token 有效期 30 分钟
|
|
|
|
|
|
- Web 项目会将 token 缓存在 Redis 中
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 充电桩同步接口
|
2025-12-30 15:59:34 +08:00
|
|
|
|
|
|
|
|
|
|
**接口路径**:`POST /api/sync/piles`
|
|
|
|
|
|
|
2025-12-31 16:38:27 +08:00
|
|
|
|
**认证方式**:Bearer Token(在请求头中添加 `Authorization: Bearer {token}`)
|
|
|
|
|
|
|
2025-12-30 15:59:34 +08:00
|
|
|
|
**重要说明**:
|
|
|
|
|
|
- ✅ 所有充电桩的 `station_id` 统一使用固定值:`88bca8da-cdbf-6587-aecc-75784838c501`
|
|
|
|
|
|
- Web 项目的原始 station_id 保存在 `additionalInfo.webStationId` 中便于追溯
|
|
|
|
|
|
|
|
|
|
|
|
**请求格式**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"piles": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"pileCode": "20231212000010",
|
|
|
|
|
|
"pileName": "1号充电桩",
|
|
|
|
|
|
"protocol": "yunkuaichongV150",
|
|
|
|
|
|
"brand": "特来电",
|
|
|
|
|
|
"model": "AC-7KW",
|
|
|
|
|
|
"manufacturer": "特来电",
|
|
|
|
|
|
"type": "OPERATION",
|
|
|
|
|
|
"additionalInfo": {
|
|
|
|
|
|
"webPileId": 6844,
|
|
|
|
|
|
"webStationId": 123,
|
|
|
|
|
|
"businessType": "1",
|
|
|
|
|
|
"secretKey": "abc123",
|
|
|
|
|
|
"longitude": "116.404",
|
|
|
|
|
|
"latitude": "39.915",
|
|
|
|
|
|
"iccid": "89860123456789012345"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**字段说明**:
|
|
|
|
|
|
- `pileCode`:充电桩编码(对应 Web 的 sn),唯一标识
|
|
|
|
|
|
- `pileName`:充电桩名称(对应 Web 的 name)
|
|
|
|
|
|
- `protocol`:软件协议(对应 Web 的 software_protocol)
|
|
|
|
|
|
- `brand`:品牌(可为空)
|
|
|
|
|
|
- `model`:型号(可为空)
|
|
|
|
|
|
- `manufacturer`:制造商(可为空)
|
|
|
|
|
|
- `type`:类型,枚举值:
|
|
|
|
|
|
- `OPERATION`:运营桩(对应 Web 的 business_type = "1")
|
|
|
|
|
|
- `PERSONAL`:个人桩(对应 Web 的 business_type = "2")
|
|
|
|
|
|
- `additionalInfo`:附加信息(JSON 格式),包含:
|
|
|
|
|
|
- `webPileId`:Web 项目的充电桩 ID
|
|
|
|
|
|
- `webStationId`:Web 项目的充电站 ID(原始 int 值)
|
|
|
|
|
|
- 其他 Web 项目的字段
|
|
|
|
|
|
|
|
|
|
|
|
**响应格式**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "同步成功",
|
|
|
|
|
|
"results": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"pileCode": "20231212000010",
|
|
|
|
|
|
"pileId": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "创建成功"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**处理逻辑**:
|
|
|
|
|
|
1. 遍历 `piles` 数组
|
|
|
|
|
|
2. 对于每个充电桩:
|
|
|
|
|
|
- 根据 `pileCode` 查询 `t_pile` 表,判断是否已存在
|
|
|
|
|
|
- **station_id 统一使用固定值**:`88bca8da-cdbf-6587-aecc-75784838c501`
|
|
|
|
|
|
- 如果充电桩已存在,执行 UPDATE 操作
|
|
|
|
|
|
- 如果充电桩不存在,执行 INSERT 操作(生成新的 uuid)
|
|
|
|
|
|
- 将 `additionalInfo` 存储为 jsonb 类型
|
|
|
|
|
|
3. 返回每个充电桩的处理结果
|
|
|
|
|
|
|
|
|
|
|
|
**注意事项**:
|
|
|
|
|
|
- 使用事务保证数据一致性
|
|
|
|
|
|
- pile_code 必须唯一(已有唯一索引)
|
|
|
|
|
|
- 所有充电桩的 station_id 都使用固定值 `88bca8da-cdbf-6587-aecc-75784838c501`
|
2025-12-31 16:38:27 +08:00
|
|
|
|
- **需要验证 Authorization 请求头中的 token**
|
|
|
|
|
|
- token 无效时返回 401 Unauthorized
|
2025-12-30 15:59:34 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2025-12-31 16:38:27 +08:00
|
|
|
|
### 3. 充电枪同步接口
|
2025-12-30 15:59:34 +08:00
|
|
|
|
|
|
|
|
|
|
**接口路径**:`POST /api/sync/guns`
|
|
|
|
|
|
|
2025-12-31 16:38:27 +08:00
|
|
|
|
**认证方式**:Bearer Token(在请求头中添加 `Authorization: Bearer {token}`)
|
|
|
|
|
|
|
2025-12-30 15:59:34 +08:00
|
|
|
|
**重要说明**:
|
|
|
|
|
|
- ✅ 所有充电枪的 `station_id` 统一使用固定值:`88bca8da-cdbf-6587-aecc-75784838c501`
|
|
|
|
|
|
|
|
|
|
|
|
**请求格式**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"guns": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"gunCode": "2023121200001001",
|
|
|
|
|
|
"gunName": "1号枪",
|
|
|
|
|
|
"gunNo": "01",
|
|
|
|
|
|
"pileCode": "20231212000010",
|
|
|
|
|
|
"additionalInfo": {
|
|
|
|
|
|
"webGunId": 30060,
|
|
|
|
|
|
"status": "1",
|
|
|
|
|
|
"parkNo": "A01"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**字段说明**:
|
|
|
|
|
|
- `gunCode`:充电枪编码(对应 Web 的 pile_connector_code),唯一标识
|
|
|
|
|
|
- `gunName`:充电枪名称(对应 Web 的 name)
|
|
|
|
|
|
- `gunNo`:枪号(从 gunCode 提取最后 2 位)
|
|
|
|
|
|
- `pileCode`:所属充电桩编码(对应 Web 的 pile_sn)
|
|
|
|
|
|
- `additionalInfo`:附加信息(JSON 格式),包含 Web 项目的其他字段
|
|
|
|
|
|
|
|
|
|
|
|
**响应格式**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "同步成功",
|
|
|
|
|
|
"results": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"gunCode": "2023121200001001",
|
|
|
|
|
|
"gunId": "660e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"message": "创建成功"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**处理逻辑**:
|
|
|
|
|
|
1. 遍历 `guns` 数组
|
|
|
|
|
|
2. 对于每个充电枪:
|
|
|
|
|
|
- 根据 `gunCode` 查询 `t_gun` 表,判断是否已存在
|
|
|
|
|
|
- 根据 `pileCode` 查询 `t_pile` 表,获取 `pile_id` (uuid)
|
|
|
|
|
|
- 如果充电桩不存在,记录错误并跳过该充电枪
|
|
|
|
|
|
- **station_id 统一使用固定值**:`88bca8da-cdbf-6587-aecc-75784838c501`
|
|
|
|
|
|
- 如果充电枪已存在,执行 UPDATE 操作
|
|
|
|
|
|
- 如果充电枪不存在,执行 INSERT 操作(生成新的 uuid)
|
|
|
|
|
|
3. 返回每个充电枪的处理结果
|
|
|
|
|
|
|
|
|
|
|
|
**注意事项**:
|
|
|
|
|
|
- 充电桩必须先于充电枪同步
|
|
|
|
|
|
- gun_code 必须唯一(已有唯一索引)
|
|
|
|
|
|
- (pile_id, gun_no) 组合必须唯一(已有唯一索引)
|
|
|
|
|
|
- 所有充电枪的 station_id 都使用固定值 `88bca8da-cdbf-6587-aecc-75784838c501`
|
2025-12-31 16:38:27 +08:00
|
|
|
|
- **需要验证 Authorization 请求头中的 token**
|
|
|
|
|
|
- token 无效时返回 401 Unauthorized
|
2025-12-30 15:59:34 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 实现建议
|
|
|
|
|
|
|
|
|
|
|
|
### 技术栈
|
|
|
|
|
|
- **框架**:Spring Boot
|
|
|
|
|
|
- **数据库**:PostgreSQL 17
|
|
|
|
|
|
- **ORM**:JPA / MyBatis
|
|
|
|
|
|
- **JSON 处理**:Jackson / Gson
|
|
|
|
|
|
|
|
|
|
|
|
### 代码结构建议
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/main/java/com/jcpp/
|
|
|
|
|
|
├── controller/
|
2025-12-31 16:38:27 +08:00
|
|
|
|
│ ├── AuthController.java # 认证接口 Controller
|
2025-12-30 15:59:34 +08:00
|
|
|
|
│ └── SyncController.java # 同步接口 Controller
|
|
|
|
|
|
├── service/
|
2025-12-31 16:38:27 +08:00
|
|
|
|
│ ├── AuthService.java # 认证服务接口
|
2025-12-30 15:59:34 +08:00
|
|
|
|
│ ├── PileSyncService.java # 充电桩同步服务接口
|
|
|
|
|
|
│ └── GunSyncService.java # 充电枪同步服务接口
|
|
|
|
|
|
├── service/impl/
|
2025-12-31 16:38:27 +08:00
|
|
|
|
│ ├── AuthServiceImpl.java
|
2025-12-30 15:59:34 +08:00
|
|
|
|
│ ├── PileSyncServiceImpl.java
|
|
|
|
|
|
│ └── GunSyncServiceImpl.java
|
|
|
|
|
|
├── entity/
|
|
|
|
|
|
│ ├── Pile.java # t_pile 实体
|
|
|
|
|
|
│ └── Gun.java # t_gun 实体
|
|
|
|
|
|
├── repository/
|
|
|
|
|
|
│ ├── PileRepository.java
|
|
|
|
|
|
│ └── GunRepository.java
|
2025-12-31 16:38:27 +08:00
|
|
|
|
├── dto/
|
|
|
|
|
|
│ ├── LoginRequest.java # 登录请求
|
|
|
|
|
|
│ ├── LoginResponse.java # 登录响应
|
|
|
|
|
|
│ ├── PileSyncDTO.java # 充电桩同步 DTO
|
|
|
|
|
|
│ ├── GunSyncDTO.java # 充电枪同步 DTO
|
|
|
|
|
|
│ ├── SyncRequest.java # 同步请求
|
|
|
|
|
|
│ └── SyncResponse.java # 同步响应
|
|
|
|
|
|
└── security/
|
|
|
|
|
|
└── JwtTokenProvider.java # JWT Token 生成和验证
|
2025-12-30 15:59:34 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 关键代码示例
|
|
|
|
|
|
|
|
|
|
|
|
**PileSyncService 接口**:
|
|
|
|
|
|
```java
|
|
|
|
|
|
public interface PileSyncService {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 同步充电桩数据
|
|
|
|
|
|
* @param piles 充电桩列表
|
|
|
|
|
|
* @return 同步结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
SyncResponse syncPiles(List<PileSyncDTO> piles);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**处理逻辑伪代码**:
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Transactional
|
|
|
|
|
|
public SyncResponse syncPiles(List<PileSyncDTO> piles) {
|
|
|
|
|
|
List<SyncResult> results = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
// 固定的 station_id
|
|
|
|
|
|
UUID FIXED_STATION_ID = UUID.fromString("88bca8da-cdbf-6587-aecc-75784838c501");
|
|
|
|
|
|
|
|
|
|
|
|
for (PileSyncDTO dto : piles) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 1. 查询充电桩是否存在
|
|
|
|
|
|
Pile existingPile = pileRepository.findByPileCode(dto.getPileCode());
|
|
|
|
|
|
|
|
|
|
|
|
if (existingPile != null) {
|
|
|
|
|
|
// 更新
|
|
|
|
|
|
existingPile.setPileName(dto.getPileName());
|
|
|
|
|
|
existingPile.setProtocol(dto.getProtocol());
|
|
|
|
|
|
existingPile.setStationId(FIXED_STATION_ID); // 使用固定值
|
|
|
|
|
|
existingPile.setBrand(dto.getBrand());
|
|
|
|
|
|
existingPile.setModel(dto.getModel());
|
|
|
|
|
|
existingPile.setManufacturer(dto.getManufacturer());
|
|
|
|
|
|
existingPile.setType(dto.getType());
|
|
|
|
|
|
existingPile.setAdditionalInfo(dto.getAdditionalInfo());
|
|
|
|
|
|
existingPile.setUpdatedTime(new Date());
|
|
|
|
|
|
pileRepository.save(existingPile);
|
|
|
|
|
|
results.add(SyncResult.success(dto.getPileCode(), existingPile.getId(), "更新成功"));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 创建
|
|
|
|
|
|
Pile newPile = new Pile();
|
|
|
|
|
|
newPile.setId(UUID.randomUUID());
|
|
|
|
|
|
newPile.setPileCode(dto.getPileCode());
|
|
|
|
|
|
newPile.setPileName(dto.getPileName());
|
|
|
|
|
|
newPile.setProtocol(dto.getProtocol());
|
|
|
|
|
|
newPile.setStationId(FIXED_STATION_ID); // 使用固定值
|
|
|
|
|
|
newPile.setBrand(dto.getBrand());
|
|
|
|
|
|
newPile.setModel(dto.getModel());
|
|
|
|
|
|
newPile.setManufacturer(dto.getManufacturer());
|
|
|
|
|
|
newPile.setType(dto.getType());
|
|
|
|
|
|
newPile.setAdditionalInfo(dto.getAdditionalInfo());
|
|
|
|
|
|
newPile.setCreatedTime(new Date());
|
|
|
|
|
|
pileRepository.save(newPile);
|
|
|
|
|
|
results.add(SyncResult.success(dto.getPileCode(), newPile.getId(), "创建成功"));
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
results.add(SyncResult.fail(dto.getPileCode(), e.getMessage()));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return SyncResponse.build(results);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 测试建议
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 单元测试
|
|
|
|
|
|
- 测试 station_id 映射查询
|
|
|
|
|
|
- 测试充电桩创建
|
|
|
|
|
|
- 测试充电桩更新
|
|
|
|
|
|
- 测试充电枪创建
|
|
|
|
|
|
- 测试充电枪更新
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 集成测试
|
|
|
|
|
|
- 测试完整的同步流程
|
|
|
|
|
|
- 测试异常场景(映射不存在、充电桩不存在等)
|
|
|
|
|
|
- 测试并发同步
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 性能测试
|
|
|
|
|
|
- 测试批量同步性能(100、500、1000 条数据)
|
|
|
|
|
|
- 测试数据库连接池配置
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 注意事项
|
|
|
|
|
|
|
|
|
|
|
|
1. **事务处理**
|
|
|
|
|
|
- 每批数据使用一个事务
|
|
|
|
|
|
- 单个充电桩失败不影响其他充电桩
|
|
|
|
|
|
|
|
|
|
|
|
2. **错误处理**
|
|
|
|
|
|
- 记录详细的错误信息
|
|
|
|
|
|
- 返回明确的错误原因
|
|
|
|
|
|
|
|
|
|
|
|
3. **日志记录**
|
|
|
|
|
|
- 记录同步开始和结束时间
|
|
|
|
|
|
- 记录成功和失败的数量
|
|
|
|
|
|
- 记录详细的错误日志
|
|
|
|
|
|
|
|
|
|
|
|
4. **性能优化**
|
|
|
|
|
|
- 使用批量查询减少数据库访问
|
|
|
|
|
|
- 合理设置数据库连接池大小
|
|
|
|
|
|
- 考虑使用缓存优化映射查询
|
|
|
|
|
|
|
|
|
|
|
|
5. **安全性**
|
2025-12-31 16:38:27 +08:00
|
|
|
|
- 添加接口鉴权(JWT Token)
|
2025-12-30 15:59:34 +08:00
|
|
|
|
- 验证请求数据的合法性
|
|
|
|
|
|
- 防止 SQL 注入
|
2025-12-31 16:38:27 +08:00
|
|
|
|
- token 有效期 30 分钟
|
2025-12-30 15:59:34 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 交付物
|
|
|
|
|
|
|
|
|
|
|
|
1. **代码**
|
|
|
|
|
|
- Controller、Service、Repository、Entity、DTO 等完整代码
|
|
|
|
|
|
- 单元测试代码
|
|
|
|
|
|
|
|
|
|
|
|
2. **接口文档**
|
|
|
|
|
|
- Swagger / OpenAPI 文档
|
|
|
|
|
|
- 接口调用示例
|
|
|
|
|
|
|
|
|
|
|
|
3. **部署说明**
|
|
|
|
|
|
- 配置项说明
|
|
|
|
|
|
- 部署步骤
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 联调准备
|
|
|
|
|
|
|
|
|
|
|
|
完成实现后,请提供:
|
|
|
|
|
|
1. 接口地址(如:http://jcpp-server:8080/api/sync)
|
|
|
|
|
|
2. 测试账号(如果需要鉴权)
|
|
|
|
|
|
3. 测试数据示例
|
|
|
|
|
|
|
|
|
|
|
|
**重要提醒**:
|
|
|
|
|
|
- 确保 JCPP 数据库中已存在 station_id 为 `88bca8da-cdbf-6587-aecc-75784838c501` 的充电站记录
|
|
|
|
|
|
- 如果不存在,需要先创建该充电站记录
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 问题反馈
|
|
|
|
|
|
|
|
|
|
|
|
如有任何问题或需要澄清的地方,请及时反馈。
|