# 数据库迁移总结

## 迁移版本：000004_create_personal_customer_relations

**执行时间**：2026-01-10
**执行状态**：✅ 成功
**执行耗时**：307ms

---

## 创建的表

### 1. personal_customer_phone（个人客户手机号绑定表）

**用途**：存储个人客户绑定的手机号信息，支持一对多关系

**字段列表**：
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | INTEGER | 主键ID |
| customer_id | INTEGER | 个人客户ID（关联 personal_customer 表） |
| phone | VARCHAR(20) | 手机号 |
| is_primary | BOOLEAN | 是否主手机号（默认 false） |
| verified_at | TIMESTAMP | 验证通过时间 |
| status | INTEGER | 状态（0=禁用 1=启用，默认 1） |
| created_at | TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | 更新时间 |
| deleted_at | TIMESTAMP | 删除时间（软删除） |

**索引**：
- ✅ `idx_personal_customer_phone_customer_phone` - 唯一索引 (customer_id, phone) WHERE deleted_at IS NULL
- ✅ `idx_personal_customer_phone_phone` - 普通索引 (phone)
- ✅ `idx_personal_customer_phone_deleted_at` - 普通索引 (deleted_at)
- ✅ `personal_customer_phone_pkey` - 主键索引 (id)

---

### 2. personal_customer_iccid（个人客户ICCID绑定表）

**用途**：记录个人客户使用过的 ICCID，支持多对多关系

**字段列表**：
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | INTEGER | 主键ID |
| customer_id | INTEGER | 个人客户ID（关联 personal_customer 表） |
| iccid | VARCHAR(20) | ICCID（20位数字） |
| bind_at | TIMESTAMP | 绑定时间 |
| last_used_at | TIMESTAMP | 最后使用时间 |
| status | INTEGER | 状态（0=禁用 1=启用，默认 1） |
| created_at | TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | 更新时间 |
| deleted_at | TIMESTAMP | 删除时间（软删除） |

**索引**：
- ✅ `idx_personal_customer_iccid_customer_iccid` - 唯一索引 (customer_id, iccid) WHERE deleted_at IS NULL
- ✅ `idx_personal_customer_iccid_iccid` - 普通索引 (iccid)
- ✅ `idx_personal_customer_iccid_deleted_at` - 普通索引 (deleted_at)
- ✅ `personal_customer_iccid_pkey` - 主键索引 (id)

---

### 3. personal_customer_device（个人客户设备号绑定表）

**用途**：记录个人客户使用过的设备号/IMEI，支持多对多关系（可选）

**字段列表**：
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | INTEGER | 主键ID |
| customer_id | INTEGER | 个人客户ID（关联 personal_customer 表） |
| device_no | VARCHAR(50) | 设备号/IMEI |
| bind_at | TIMESTAMP | 绑定时间 |
| last_used_at | TIMESTAMP | 最后使用时间 |
| status | INTEGER | 状态（0=禁用 1=启用，默认 1） |
| created_at | TIMESTAMP | 创建时间 |
| updated_at | TIMESTAMP | 更新时间 |
| deleted_at | TIMESTAMP | 删除时间（软删除） |

**索引**：
- ✅ `idx_personal_customer_device_customer_device` - 唯一索引 (customer_id, device_no) WHERE deleted_at IS NULL
- ✅ `idx_personal_customer_device_device_no` - 普通索引 (device_no)
- ✅ `idx_personal_customer_device_deleted_at` - 普通索引 (deleted_at)
- ✅ `personal_customer_device_pkey` - 主键索引 (id)

---

## 数据库设计原则

### 核心设计理念

1. **无外键约束**：所有表之间不建立 Foreign Key 约束，提高灵活性和性能
2. **软删除支持**：所有表都包含 `deleted_at` 字段，支持软删除
3. **唯一性保证**：通过唯一索引（带 WHERE deleted_at IS NULL 条件）保证业务唯一性
4. **查询优化**：为常用查询字段创建普通索引

### 业务模型关系

```
PersonalCustomer (1) ──< (N) PersonalCustomerPhone
PersonalCustomer (N) ──< (M) PersonalCustomerICCID
PersonalCustomer (N) ──< (M) PersonalCustomerDevice
```

- **个人客户 ←→ 手机号**：一对多（一个客户可以绑定多个手机号）
- **个人客户 ←→ ICCID**：多对多（一个客户可以使用多个 ICCID，一个 ICCID 可以被多个客户使用）
- **个人客户 ←→ 设备号**：多对多（一个客户可以使用多个设备，一个设备可以被多个客户使用）

---

## Makefile 命令

为了方便后续的数据库迁移操作，已添加以下 Makefile 命令：

```bash
# 执行所有待执行的迁移（升级数据库）
make migrate-up

# 回滚最后一次迁移（降级数据库）
make migrate-down

# 查看当前迁移版本
make migrate-version

# 创建新的迁移脚本
make migrate-create
```

---

## 迁移验证

### 表验证
```bash
✅ 个人客户相关表列表:
  - personal_customer_device
  - personal_customer_iccid
  - personal_customer_phone
```

### 索引验证
所有表的索引都已成功创建：
- ✅ 主键索引
- ✅ 唯一索引（带软删除过滤）
- ✅ 查询优化索引
- ✅ 软删除索引

---

## 下一步

1. **数据初始化**（可选）
   - 如需初始化测试数据，可以创建 seed 脚本

2. **业务逻辑实现**
   - 已完成：Service 层、Handler 层、路由注册
   - 待完成：单元测试、集成测试

3. **性能监控**
   - 监控索引使用情况
   - 监控查询性能
   - 根据实际使用情况优化索引

---

## 注意事项

⚠️ **生产环境部署前的检查清单**：

1. 确认数据库备份已完成
2. 在预发布环境测试迁移脚本
3. 确认索引创建不会影响现有查询性能
4. 准备回滚方案（已提供 .down.sql 脚本）
5. 评估迁移执行时间，选择合适的维护窗口

---

**迁移完成时间**：2026-01-10
**文档版本**：v1.0
