Files
jsowell-charger-web/doc/JCPP项目配合实现Prompt.md
2025-12-31 16:38:27 +08:00

403 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
---
## 需要实现的功能
### 1. 认证接口
**接口路径**`POST /api/auth/login`
**请求格式**
```json
{
"username": "sanbing",
"password": "password123"
}
```
**响应格式**
```json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 1800
}
```
**说明**
- 用于 Web 项目获取访问令牌
- token 有效期 30 分钟
- Web 项目会将 token 缓存在 Redis 中
---
### 2. 充电桩同步接口
**接口路径**`POST /api/sync/piles`
**认证方式**Bearer Token在请求头中添加 `Authorization: Bearer {token}`
**重要说明**
- ✅ 所有充电桩的 `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`
- **需要验证 Authorization 请求头中的 token**
- token 无效时返回 401 Unauthorized
---
### 3. 充电枪同步接口
**接口路径**`POST /api/sync/guns`
**认证方式**Bearer Token在请求头中添加 `Authorization: Bearer {token}`
**重要说明**
- ✅ 所有充电枪的 `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`
- **需要验证 Authorization 请求头中的 token**
- token 无效时返回 401 Unauthorized
---
## 实现建议
### 技术栈
- **框架**Spring Boot
- **数据库**PostgreSQL 17
- **ORM**JPA / MyBatis
- **JSON 处理**Jackson / Gson
### 代码结构建议
```
src/main/java/com/jcpp/
├── controller/
│ ├── AuthController.java # 认证接口 Controller
│ └── SyncController.java # 同步接口 Controller
├── service/
│ ├── AuthService.java # 认证服务接口
│ ├── PileSyncService.java # 充电桩同步服务接口
│ └── GunSyncService.java # 充电枪同步服务接口
├── service/impl/
│ ├── AuthServiceImpl.java
│ ├── PileSyncServiceImpl.java
│ └── GunSyncServiceImpl.java
├── entity/
│ ├── Pile.java # t_pile 实体
│ └── Gun.java # t_gun 实体
├── repository/
│ ├── PileRepository.java
│ └── GunRepository.java
├── dto/
│ ├── LoginRequest.java # 登录请求
│ ├── LoginResponse.java # 登录响应
│ ├── PileSyncDTO.java # 充电桩同步 DTO
│ ├── GunSyncDTO.java # 充电枪同步 DTO
│ ├── SyncRequest.java # 同步请求
│ └── SyncResponse.java # 同步响应
└── security/
└── JwtTokenProvider.java # JWT Token 生成和验证
```
### 关键代码示例
**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. **安全性**
- 添加接口鉴权JWT Token
- 验证请求数据的合法性
- 防止 SQL 注入
- token 有效期 30 分钟
---
## 交付物
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` 的充电站记录
- 如果不存在,需要先创建该充电站记录
---
## 问题反馈
如有任何问题或需要澄清的地方,请及时反馈。