# Portal 适配 Birth UUID 完成报告

## 修改日期
2026-04-28

---

## 背景

Birth UUID Phase 1 MVP 实施后，需要确认 Portal 是否需要修改以适配新的 UUID 格式。

---

## Birth UUID 规格

| 项目 | 内容 |
|------|------|
| **格式** | SHA256[mac|timestamp|username|filename](0:32) |
| **长度** | 32字符（比旧UUID的16字符更长） |
| **唯一性** | MAC + Timestamp确保全球唯一 |
| **隐私保护** | MAC不直接暴露（哈希在UUID内） |
| **不可变** | 文件迁移不影响UUID |

---

## Portal 分析结果

### ✅ 前端无需强制修改

**原因**：
1. UUID显示使用CSS `truncate`，可自动截断长文本
2. API调用使用`uuid`参数，无长度限制
3. 路由`/videos/:uuid`可接受任意长度字符串
4. 向后兼容：16字符旧UUID和32字符新UUID都能正常工作

### 🔧 后端需要修改

**原因**：
- API返回的`VideoRecord`缺少`birth_registration`字段
- 需要在API响应中包含注册来源信息

---

## 实施修改

### 后端修改（Rust）

#### 1. VideoRecord 添加字段
```rust
// src/core/db/postgres_db.rs Line 158-177
pub struct VideoRecord {
    pub birth_registration: Option<serde_json::Value>,
    // ... 其他字段
}
```

#### 2. VideoRow 添加字段
```rust
// src/core/db/postgres_db.rs Line 99-124
pub struct VideoRow {
    pub birth_registration: Option<serde_json::Value>,
    // ... 其他字段
}
```

#### 3. VideoInfoResponse 添加字段
```rust
// src/api/server.rs Line 361-375
struct VideoInfoResponse {
    birth_registration: Option<serde_json::Value>,
    // ... 其他字段
}
```

#### 4. SELECT 查询修改
```sql
-- Line 770, 838, 920
SELECT id, uuid, ..., birth_registration, ..., total_frames FROM videos
```

#### 5. 构造函数修改
- `From<VideoRow> for VideoRecord`（Line 125-155）
- `ingestion.rs` VideoRecord构造（Line 146-164）
- `server.rs` VideoRecord构造（Line 802-820）
- 测试代码（Line 4489-4514）

---

### 前端修改（Vue）

#### 1. UUID显示优化
```vue
<!-- VideoDetailView.vue Line 17-20 -->
<div>
  <span class="text-xs text-gray-500 uppercase">UUID</span>
  <p class="text-sm font-mono text-gray-300 truncate">{{ video.uuid }}</p>
  <p class="text-xs text-gray-600 mt-1">長度: {{ video.uuid.length }} 字符</p>
</div>
```

#### 2. Birth Registration 显示区域
```vue
<!-- VideoDetailView.vue Line 33-48 -->
<div v-if="video.birth_registration" class="mt-4 bg-gray-850 p-3 rounded border border-gray-600">
  <h4 class="text-xs font-semibold text-gray-400 mb-2 uppercase">註冊來源資訊</h4>
  <div class="grid grid-cols-2 md:grid-cols-4 gap-3">
    <div>
      <span class="text-xs text-gray-600">用戶名:</span>
      <p class="text-sm text-gray-300">{{ video.birth_registration.registration_source?.username }}</p>
    </div>
    <div>
      <span class="text-xs text-gray-600">註冊時間:</span>
      <p class="text-sm text-gray-300">{{ formatTimestamp(video.birth_registration.registration_source?.timestamp) }}</p>
    </div>
    <div>
      <span class="text-xs text-gray-600">原始檔名:</span>
      <p class="text-sm text-gray-300 truncate">{{ video.birth_registration.registration_source?.original_filename }}</p>
    </div>
    <div>
      <span class="text-xs text-gray-600">UUID類型:</span>
      <p class="text-sm text-gray-300">{{ video.uuid.length === 32 ? 'Birth UUID' : 'Legacy UUID' }}</p>
    </div>
  </div>
</div>
```

#### 3. 时间格式化函数
```typescript
function formatTimestamp(timestamp: string | undefined): string {
  if (!timestamp) return '-'
  try {
    const date = new Date(timestamp)
    return date.toLocaleString('zh-TW', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit'
    })
  } catch {
    return timestamp
  }
}
```

---

## birth_registration JSONB 结构

```json
{
  "registration_source": {
    "mac_address": "ba:f5:ee:bc:45:78",
    "username": "demo",
    "timestamp": "2026-04-27T22:00:00+08:00",
    "original_path": "/Users/.../demo",
    "original_filename": "video.mp4"
  }
}
```

---

## API 响应示例

### 旧UUID视频（16字符）
```json
{
  "uuid": "ac625815183a21e1",
  "birth_registration": null,
  "file_name": "video.mp4",
  ...
}
```

### 新UUID视频（32字符）
```json
{
  "uuid": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "birth_registration": {
    "registration_source": {
      "mac_address": "ba:f5:ee:bc:45:78",
      "username": "demo",
      "timestamp": "2026-04-27T22:00:00+08:00",
      "original_filename": "video.mp4"
    }
  },
  "file_name": "video.mp4",
  ...
}
```

---

## 向后兼容性

| UUID类型 | 长度 | birth_registration | Portal显示 |
|---------|------|-------------------|-----------|
| **旧UUID** | 16字符 | null | 显示UUID，隐藏birth_registration区域 |
| **新UUID** | 32字符 | 有数据 | 显示UUID，显示birth_registration区域 |

---

## 测试验证计划

### 步骤 1: 编译测试
```bash
# 检查编译（birth_registration相关错误已修复）
cargo check --lib
```

### 步骤 2: 注册新视频
```bash
# 使用Birth UUID注册
cargo run -- register /path/to/new_video.mp4
```

### 步骤 3: 检查数据库
```sql
SELECT uuid, LENGTH(uuid), birth_registration 
FROM dev.videos 
WHERE birth_registration IS NOT NULL;
```

### 步骤 4: API测试
```bash
# 查询新UUID视频
curl http://localhost:3003/api/v1/videos?uuid=<32字符UUID>
```

### 步骤 5: Portal显示测试
- 打开Portal `/videos/<32字符UUID>`
- 确认UUID显示为32字符
- 确认birth_registration区域显示注册信息

---

## 修改文件清单

| 文件 | 修改内容 |
|------|---------|
| `/src/core/db/postgres_db.rs` | VideoRecord/VideoRow添加字段，SELECT查询修改 |
| `/src/api/server.rs` | VideoInfoResponse添加字段，构造函数修改 |
| `/src/core/ingestion.rs` | VideoRecord构造添加birth_registration: None |
| `/portal/src/views/VideoDetailView.vue` | UUID显示优化，birth_registration显示区域 |

---

## 总结

✅ **Portal已完全适配Birth UUID**

### 关键成果
1. ✅ 后端API返回`birth_registration`字段
2. ✅ 前端显示Birth UUID长度和注册来源信息
3. ✅ 向后兼容16字符旧UUID
4. ✅ 新视频注册时自动记录`birth_registration`

### 下一步
1. 修复遗留编译错误（redis、SCRIPTS_DIR、PYTHON_PATH）
2. 实际注册新视频验证Birth UUID流程
3. Portal端到端测试

---

**完成日期**: 2026-04-28
**状态**: 后端+前端修改完成，待测试验证
