Files
jsowell-charger-web/doc/JCPP项目配合实现Prompt.md

403 lines
11 KiB
Markdown
Raw Normal View History

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 使用 intJCPP 使用 uuid
2. **station_id 类型**Web 是 intJCPP 是 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` 的充电站记录
- 如果不存在,需要先创建该充电站记录
---
## 问题反馈
如有任何问题或需要澄清的地方,请及时反馈。