# 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

---

## 需要实现的功能

### 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` 的充电站记录
- 如果不存在，需要先创建该充电站记录

---

## 问题反馈

如有任何问题或需要澄清的地方，请及时反馈。
