# 充电桩数据同步实现计划

> 创建时间：2025-12-30
> 任务状态：规划中

---

## 一、需求概述

将 Web 项目（MySQL）中的充电桩数据同步到 JCPP 项目（PostgreSQL）中，用于登录鉴权等操作。

### 1.1 数据流向
```
Web 项目 (MySQL) → JCPP 项目 (PostgreSQL)
```

### 1.2 同步方式
- **全量同步**：同步所有充电桩和充电枪数据
- **增量同步**：只同步新增或修改的数据（基于 update_time）

---

## 二、表结构映射分析

### 2.1 充电桩表映射

| Web 字段 (pile_basic_info) | JCPP 字段 (t_pile) | 映射说明 |
|---------------------------|-------------------|---------|
| id (int) | - | Web 主键，不同步 |
| sn (varchar(20)) | pile_code (varchar(255)) | 充电桩编码，唯一标识 |
| name (varchar(32)) | pile_name (varchar(255)) | 充电桩名称 |
| software_protocol (varchar(20)) | protocol (varchar(255)) | 软件协议 |
| station_id (int) | station_id (uuid) | ✅ 统一使用固定值 '88bca8da-cdbf-6587-aecc-75784838c501' |
| model_id (int) | model (varchar(255)) | 型号，需要查询转换 |
| - | brand (varchar(255)) | 品牌，可为空 |
| - | manufacturer (varchar(255)) | 制造商，可为空 |
| business_type (char(5)) | type (varchar(16)) | 经营类型映射 |
| 其他字段 | additional_info (jsonb) | 存储为 JSON |

### 2.2 充电枪表映射

| Web 字段 (pile_connector_info) | JCPP 字段 (t_gun) | 映射说明 |
|-------------------------------|------------------|---------|
| id (int) | - | Web 主键，不同步 |
| pile_connector_code (varchar(20)) | gun_code (varchar(255)) | 充电枪编码，唯一标识 |
| name (varchar(20)) | gun_name (varchar(255)) | 充电枪名称 |
| pile_sn (varchar(20)) | pile_id (uuid) | ⚠️ 需要通过 pile_code 查询 pile_id |
| - | gun_no (varchar(255)) | 枪号，从 pile_connector_code 提取 |
| - | station_id (uuid) | ✅ 统一使用固定值 '88bca8da-cdbf-6587-aecc-75784838c501' |

### 2.3 关键问题

#### 问题1：station_id 类型映射（int → uuid）
**解决方案**：
- ✅ 统一使用固定的 UUID 值：`88bca8da-cdbf-6587-aecc-75784838c501`
- 所有充电桩都使用这个固定的 station_id
- Web 的原始 station_id 保存在 additional_info 中便于追溯

#### 问题2：pile_id 获取
**解决方案**：
- 先同步充电桩，获取返回的 pile_id
- 再同步充电枪时，通过 pile_code 查询 pile_id

#### 问题3：gun_no 提取
**解决方案**：
- pile_connector_code 格式为 "桩号+枪号"（如：20231212000010**01**）
- 提取最后 2 位作为 gun_no

---

## 三、实现方案

### 3.1 Web 项目实现（本项目）

#### 3.1.1 数据传输对象（DTO）

**文件位置**：`jsowell-pile/src/main/java/com/jsowell/pile/jcpp/dto/sync/`

1. **JcppPileSyncDTO** - 充电桩同步数据
```java
{
    "pileCode": "20231212000010",
    "pileName": "1号充电桩",
    "protocol": "yunkuaichongV150",
    "brand": "特来电",
    "model": "AC-7KW",
    "manufacturer": "特来电",
    "type": "OPERATION",  // OPERATION-运营桩, PERSONAL-个人桩
    "additionalInfo": {
        "webPileId": 6844,
        "webStationId": 123,  // 保存 Web 的原始 station_id
        "businessType": "1",
        "secretKey": "abc123",
        "longitude": "116.404",
        "latitude": "39.915",
        "iccid": "89860123456789012345"
    }
}
```

**注意**：所有充电桩的 station_id 统一使用固定值 `88bca8da-cdbf-6587-aecc-75784838c501`

2. **JcppGunSyncDTO** - 充电枪同步数据
```java
{
    "gunCode": "2023121200001001",
    "gunName": "1号枪",
    "gunNo": "01",
    "pileCode": "20231212000010",
    "additionalInfo": {
        "webGunId": 30060,
        "status": "1",
        "parkNo": "A01"
    }
}
```

3. **JcppSyncRequest** - 同步请求
```java
{
    "syncType": "FULL",  // FULL-全量, INCREMENTAL-增量
    "lastSyncTime": "2025-12-30 10:00:00",  // 增量同步时使用
    "piles": [...],
    "guns": [...]
}
```

4. **JcppSyncResponse** - 同步响应
```java
{
    "success": true,
    "message": "同步成功",
    "totalPiles": 100,
    "successPiles": 98,
    "failedPiles": 2,
    "totalGuns": 200,
    "successGuns": 195,
    "failedGuns": 5,
    "errors": [...]
}
```

#### 3.1.2 服务接口

**文件位置**：`jsowell-pile/src/main/java/com/jsowell/pile/jcpp/service/`

1. **IJcppPileSyncService** - 充电桩同步服务接口
```java
public interface IJcppPileSyncService {
    /**
     * 全量同步充电桩数据到 JCPP
     */
    JcppSyncResponse syncAllPiles();

    /**
     * 增量同步充电桩数据到 JCPP
     * @param lastSyncTime 上次同步时间
     */
    JcppSyncResponse syncIncrementalPiles(Date lastSyncTime);

    /**
     * 同步单个充电桩
     */
    boolean syncSinglePile(String pileSn);
}
```

2. **JcppPileSyncServiceImpl** - 实现类
- 查询 Web 数据库
- 转换数据格式
- 调用 JCPP 接口发送数据
- 处理同步结果

#### 3.1.3 Controller 接口

**文件位置**：`jsowell-admin/src/main/java/com/jsowell/web/controller/jcpp/`

**JcppPileSyncController**
```java
@RestController
@RequestMapping("/jcpp/sync")
public class JcppPileSyncController {

    /**
     * 全量同步充电桩数据
     * POST /jcpp/sync/full
     */
    @PostMapping("/full")
    public AjaxResult syncFull();

    /**
     * 增量同步充电桩数据
     * POST /jcpp/sync/incremental
     * @param lastSyncTime 上次同步时间（可选，默认取上次记录）
     */
    @PostMapping("/incremental")
    public AjaxResult syncIncremental(@RequestParam(required = false) Date lastSyncTime);

    /**
     * 同步单个充电桩
     * POST /jcpp/sync/pile/{pileSn}
     */
    @PostMapping("/pile/{pileSn}")
    public AjaxResult syncSinglePile(@PathVariable String pileSn);

    /**
     * 查询同步记录
     * GET /jcpp/sync/records
     */
    @GetMapping("/records")
    public TableDataInfo getSyncRecords();
}
```

#### 3.1.4 同步记录表

**表名**：`jcpp_sync_record`
```sql
CREATE TABLE `jcpp_sync_record` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `sync_type` varchar(20) NOT NULL COMMENT '同步类型（FULL-全量；INCREMENTAL-增量）',
  `sync_status` varchar(20) NOT NULL COMMENT '同步状态（RUNNING-进行中；SUCCESS-成功；FAILED-失败）',
  `start_time` datetime NOT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
  `total_piles` int(11) DEFAULT 0 COMMENT '总充电桩数',
  `success_piles` int(11) DEFAULT 0 COMMENT '成功充电桩数',
  `failed_piles` int(11) DEFAULT 0 COMMENT '失败充电桩数',
  `total_guns` int(11) DEFAULT 0 COMMENT '总充电枪数',
  `success_guns` int(11) DEFAULT 0 COMMENT '成功充电枪数',
  `failed_guns` int(11) DEFAULT 0 COMMENT '失败充电枪数',
  `error_message` text COMMENT '错误信息',
  `create_by` varchar(20) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_sync_type` (`sync_type`),
  KEY `idx_start_time` (`start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='JCPP 充电桩同步记录表';
```

#### 3.1.5 配置项

**配置文件**：`application-{env}.yml`
```yaml
jcpp:
  sync:
    # JCPP 同步接口地址
    api-url: http://jcpp-server:8080/api/sync
    # 批量同步大小
    batch-size: 100
    # 超时时间（秒）
    timeout: 60
    # 是否启用自动增量同步
    auto-sync-enabled: false
    # 自动同步间隔（分钟）
    auto-sync-interval: 30
```

---

### 3.2 JCPP 项目实现（需要配合）

#### 3.2.1 需要实现的接口

**接口1：接收充电桩同步数据**
```
POST /api/sync/piles
Content-Type: application/json

Request Body:
{
    "piles": [
        {
            "pileCode": "20231212000010",
            "pileName": "1号充电桩",
            "protocol": "yunkuaichongV150",
            "brand": "特来电",
            "model": "AC-7KW",
            "manufacturer": "特来电",
            "type": "OPERATION",
            "additionalInfo": {
                "webStationId": 123  // Web 的原始 station_id
            }
        }
    ]
}

Response:
{
    "success": true,
    "message": "同步成功",
    "results": [
        {
            "pileCode": "20231212000010",
            "pileId": "uuid",
            "success": true,
            "message": "创建成功"
        }
    ]
}
```

**注意**：所有充电桩的 station_id 统一使用固定值 `88bca8da-cdbf-6587-aecc-75784838c501`

**接口2：接收充电枪同步数据**
```
POST /api/sync/guns
Content-Type: application/json

Request Body:
{
    "guns": [
        {
            "gunCode": "2023121200001001",
            "gunName": "1号枪",
            "gunNo": "01",
            "pileCode": "20231212000010",
            "additionalInfo": {...}
        }
    ]
}

Response:
{
    "success": true,
    "message": "同步成功",
    "results": [...]
}
```

#### 3.2.2 数据处理逻辑

1. **充电桩同步逻辑**
   - 根据 pile_code 判断是否已存在
   - 存在则更新，不存在则创建
   - station_id 统一使用固定值 `88bca8da-cdbf-6587-aecc-75784838c501`
   - 返回 pile_id (uuid)

2. **充电枪同步逻辑**
   - 根据 gun_code 判断是否已存在
   - 通过 pile_code 查询 pile_id
   - station_id 统一使用固定值 `88bca8da-cdbf-6587-aecc-75784838c501`
   - 存在则更新，不存在则创建

---

## 四、实现步骤

### 阶段一：Web 项目基础实现 ✅

- [x] 1. 创建 DTO 类（JcppPileSyncDTO、JcppGunSyncDTO、JcppSyncRequest、JcppSyncResponse、JcppSyncResult）
- [x] 2. 创建同步记录表（jcpp_sync_record）及 Mapper
- [x] 3. 创建 Service 接口和实现类（IJcppPileSyncService、JcppPileSyncServiceImpl）
- [x] 4. 创建 Controller 接口（JcppPileSyncController）
- [x] 5. 添加配置项（application-sit.yml）

**已完成文件**：
- DTO: `JcppPileSyncDTO.java`, `JcppGunSyncDTO.java`, `JcppSyncRequest.java`, `JcppSyncResponse.java`, `JcppSyncResult.java`
- Domain: `JcppSyncRecord.java`
- Mapper: `JcppSyncRecordMapper.java`, `JcppSyncRecordMapper.xml`
- Service: `IJcppPileSyncService.java`, `JcppPileSyncServiceImpl.java`
- Controller: `JcppPileSyncController.java`
- SQL: `sql/jcpp_sync_record.sql`
- Config: `application-sit.yml` (已添加 jcpp.sync 配置)

### 阶段二：JCPP 项目配合实现 ⏳

- [ ] 1. 实现充电桩同步接口（使用固定 station_id）
- [ ] 2. 实现充电枪同步接口（使用固定 station_id）

### 阶段三：联调测试 ⏳

- [ ] 1. 测试全量同步
- [ ] 2. 测试增量同步
- [ ] 3. 测试单个充电桩同步
- [ ] 4. 测试异常场景
- [ ] 5. 性能测试

### 阶段四：优化完善 ⏳

- [ ] 1. 添加重试机制
- [ ] 2. 添加同步进度查询
- [ ] 3. 添加定时自动同步
- [ ] 4. 完善错误处理和日志

---

## 五、技术要点

### 5.1 数据转换

1. **business_type 映射**
   - Web: "1"-运营桩, "2"-个人桩
   - JCPP: "OPERATION"-运营桩, "PERSONAL"-个人桩

2. **gun_no 提取**
   - pile_connector_code: "2023121200001001"
   - gun_no: "01" (最后2位)

3. **additional_info 构建**
   - 将 Web 的其他字段存储为 JSON
   - 保留原始 ID 便于追溯

### 5.2 性能优化

1. **批量同步**
   - 每批 100 条数据
   - 避免一次性加载所有数据

2. **异步处理**
   - 全量同步使用异步任务
   - 返回任务 ID，可查询进度

3. **增量同步**
   - 基于 update_time 字段
   - 记录上次同步时间

### 5.3 异常处理

1. **网络异常**
   - 重试机制（最多3次）
   - 指数退避策略

2. **数据异常**
   - 记录失败数据
   - 继续处理其他数据

3. **事务处理**
   - JCPP 端保证事务一致性
   - Web 端记录同步状态

---

## 六、注意事项

1. **数据一致性**
   - 充电桩必须先于充电枪同步
   - 确保 pile_code 唯一性

2. **性能考虑**
   - 全量同步可能耗时较长
   - 建议在业务低峰期执行

3. **安全性**
   - 同步接口需要鉴权
   - 敏感数据加密传输

4. **监控告警**
   - 同步失败告警
   - 同步耗时监控

---

## 七、变更记录

| 日期 | 版本 | 修改人 | 修改内容 |
|------|------|--------|----------|
| 2025-12-30 | v1.0 | Claude | 创建文档，制定实现计划 |

---

## 八、相关文档

- [充电桩数据同步需求.md](./充电桩数据同步需求.md)
- [JCPP对接进度文档.md](./JCPP对接进度文档.md)
