267 lines
6.8 KiB
Markdown
267 lines
6.8 KiB
Markdown
|
|
# 为现有摄像头生成 camera_code - 执行指南
|
|||
|
|
|
|||
|
|
**目的**: 为数据库中已存在的摄像头生成唯一的 camera_code 标识符
|
|||
|
|
|
|||
|
|
**执行时间**: 2026-02-13
|
|||
|
|
|
|||
|
|
**重要性**: ⚠️ 必须执行!否则旧摄像头的 camera_code 为空,前端将无法正常使用
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 快速执行(推荐)
|
|||
|
|
|
|||
|
|
### 方法一:MySQL 命令行执行
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1. 进入 MySQL
|
|||
|
|
mysql -u root -p wvp_aiot
|
|||
|
|
|
|||
|
|
# 2. 执行迁移脚本
|
|||
|
|
source C:/workspace/wvp-platform/数据库/aiot/迁移-添加camera_code字段.sql
|
|||
|
|
|
|||
|
|
# 3. 查看执行结果
|
|||
|
|
# 脚本会自动显示验证结果和样本数据
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方法二:直接复制SQL执行
|
|||
|
|
|
|||
|
|
如果 `source` 命令不可用,请复制以下SQL直接在MySQL客户端执行:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 1. 新增 camera_code 列(如果不存在)
|
|||
|
|
ALTER TABLE wvp_stream_proxy
|
|||
|
|
ADD COLUMN IF NOT EXISTS camera_code VARCHAR(64) NULL COMMENT '摄像头全局唯一编码(格式:cam_xxxxxxxxxxxx)';
|
|||
|
|
|
|||
|
|
-- 2. 为现有数据生成 camera_code(基于 ID 生成12位哈希)
|
|||
|
|
UPDATE wvp_stream_proxy
|
|||
|
|
SET camera_code = CONCAT('cam_', SUBSTRING(MD5(CAST(id AS CHAR)), 1, 12))
|
|||
|
|
WHERE camera_code IS NULL OR camera_code = '';
|
|||
|
|
|
|||
|
|
-- 3. 将 camera_code 设为 NOT NULL 并添加唯一索引(如果不存在)
|
|||
|
|
ALTER TABLE wvp_stream_proxy
|
|||
|
|
MODIFY COLUMN camera_code VARCHAR(64) NOT NULL;
|
|||
|
|
|
|||
|
|
-- 添加唯一索引(如果不存在,忽略错误)
|
|||
|
|
ALTER TABLE wvp_stream_proxy
|
|||
|
|
ADD UNIQUE KEY uk_camera_code (camera_code);
|
|||
|
|
|
|||
|
|
-- 4. 验证数据完整性 - 检查重复
|
|||
|
|
SELECT '检查重复的 camera_code' AS check_name, COUNT(*) AS issue_count
|
|||
|
|
FROM (
|
|||
|
|
SELECT camera_code, COUNT(*) AS cnt
|
|||
|
|
FROM wvp_stream_proxy
|
|||
|
|
GROUP BY camera_code
|
|||
|
|
HAVING cnt > 1
|
|||
|
|
) AS duplicates;
|
|||
|
|
|
|||
|
|
-- 5. 验证格式
|
|||
|
|
SELECT '检查格式错误的 camera_code' AS check_name, COUNT(*) AS issue_count
|
|||
|
|
FROM wvp_stream_proxy
|
|||
|
|
WHERE camera_code NOT REGEXP '^cam_[a-f0-9]{12}$';
|
|||
|
|
|
|||
|
|
-- 6. 更新 ROI 表中的 camera_id(从 app/stream 格式改为 camera_code)
|
|||
|
|
UPDATE wvp_ai_roi r
|
|||
|
|
INNER JOIN wvp_stream_proxy sp ON r.camera_id = CONCAT(sp.app, '/', sp.stream)
|
|||
|
|
SET r.camera_id = sp.camera_code;
|
|||
|
|
|
|||
|
|
-- 7. 验证 ROI 更新结果
|
|||
|
|
SELECT '检查未匹配的 ROI' AS check_name, COUNT(*) AS issue_count
|
|||
|
|
FROM wvp_ai_roi r
|
|||
|
|
LEFT JOIN wvp_stream_proxy sp ON r.camera_id = sp.camera_code
|
|||
|
|
WHERE sp.camera_code IS NULL;
|
|||
|
|
|
|||
|
|
-- 8. 显示迁移后的样本数据
|
|||
|
|
SELECT id, camera_code, app, stream, name
|
|||
|
|
FROM wvp_stream_proxy
|
|||
|
|
LIMIT 5;
|
|||
|
|
|
|||
|
|
SELECT roi_id, camera_id, name
|
|||
|
|
FROM wvp_ai_roi
|
|||
|
|
LIMIT 5;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 验证执行结果
|
|||
|
|
|
|||
|
|
执行完成后,应该看到以下输出:
|
|||
|
|
|
|||
|
|
### 1. 重复检查
|
|||
|
|
```
|
|||
|
|
check_name | issue_count
|
|||
|
|
-------------------------------------------
|
|||
|
|
检查重复的 camera_code | 0
|
|||
|
|
```
|
|||
|
|
✅ `issue_count = 0` 表示无重复
|
|||
|
|
|
|||
|
|
### 2. 格式检查
|
|||
|
|
```
|
|||
|
|
check_name | issue_count
|
|||
|
|
------------------------------------------------
|
|||
|
|
检查格式错误的 camera_code | 0
|
|||
|
|
```
|
|||
|
|
✅ `issue_count = 0` 表示格式正确
|
|||
|
|
|
|||
|
|
### 3. ROI 匹配检查
|
|||
|
|
```
|
|||
|
|
check_name | issue_count
|
|||
|
|
---------------------------------------
|
|||
|
|
检查未匹配的 ROI | 0
|
|||
|
|
```
|
|||
|
|
✅ `issue_count = 0` 表示 ROI 关联正确
|
|||
|
|
|
|||
|
|
### 4. 样本数据示例
|
|||
|
|
```sql
|
|||
|
|
-- wvp_stream_proxy 样本
|
|||
|
|
+----+------------------+------+----------+--------+
|
|||
|
|
| id | camera_code | app | stream | name |
|
|||
|
|
+----+------------------+------+----------+--------+
|
|||
|
|
| 1 | cam_c4ca4238a0b9 | live | camera01 | 大堂 |
|
|||
|
|
| 2 | cam_c81e728d9d4c | live | camera02 | 停车场 |
|
|||
|
|
+----+------------------+------+----------+--------+
|
|||
|
|
|
|||
|
|
-- wvp_ai_roi 样本
|
|||
|
|
+--------+------------------+----------+
|
|||
|
|
| roi_id | camera_id | name |
|
|||
|
|
+--------+------------------+----------+
|
|||
|
|
| 1 | cam_c4ca4238a0b9 | ROI区域1 |
|
|||
|
|
| 2 | cam_c81e728d9d4c | ROI区域2 |
|
|||
|
|
+--------+------------------+----------+
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
✅ camera_code 格式为 `cam_xxxxxxxxxxxx`(12位十六进制)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 手动验证(可选)
|
|||
|
|
|
|||
|
|
如果需要手动检查,执行以下SQL:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 1. 检查所有摄像头是否都有 camera_code
|
|||
|
|
SELECT COUNT(*) AS total_cameras,
|
|||
|
|
COUNT(camera_code) AS with_code,
|
|||
|
|
COUNT(*) - COUNT(camera_code) AS missing_code
|
|||
|
|
FROM wvp_stream_proxy;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**期望结果**: `missing_code = 0`
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 2. 检查 camera_code 唯一性
|
|||
|
|
SELECT COUNT(DISTINCT camera_code) AS unique_codes,
|
|||
|
|
COUNT(*) AS total_rows
|
|||
|
|
FROM wvp_stream_proxy;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**期望结果**: `unique_codes = total_rows`
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 3. 检查 ROI 表是否正确关联
|
|||
|
|
SELECT COUNT(*) AS total_rois,
|
|||
|
|
COUNT(sp.camera_code) AS matched_rois,
|
|||
|
|
COUNT(*) - COUNT(sp.camera_code) AS unmatched_rois
|
|||
|
|
FROM wvp_ai_roi r
|
|||
|
|
LEFT JOIN wvp_stream_proxy sp ON r.camera_id = sp.camera_code;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**期望结果**: `unmatched_rois = 0`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚠️ 常见问题
|
|||
|
|
|
|||
|
|
### Q1: 执行时报错 "Duplicate key name 'uk_camera_code'"
|
|||
|
|
|
|||
|
|
**原因**: 索引已存在(可能之前执行过)
|
|||
|
|
|
|||
|
|
**解决**: 忽略此错误,继续执行后续步骤
|
|||
|
|
|
|||
|
|
### Q2: ROI 更新显示 0 rows affected
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- 情况1: 没有 ROI 数据(正常)
|
|||
|
|
- 情况2: camera_id 格式已经是 camera_code(已迁移过)
|
|||
|
|
|
|||
|
|
**验证**: 执行验证SQL检查 `unmatched_rois` 是否为 0
|
|||
|
|
|
|||
|
|
### Q3: 某些摄像头的 camera_code 仍然为空
|
|||
|
|
|
|||
|
|
**原因**: UPDATE 语句条件过滤了这些记录
|
|||
|
|
|
|||
|
|
**解决**: 手动为这些记录生成 camera_code
|
|||
|
|
```sql
|
|||
|
|
UPDATE wvp_stream_proxy
|
|||
|
|
SET camera_code = CONCAT('cam_', SUBSTRING(MD5(CONCAT('manual_', id, '_', NOW())), 1, 12))
|
|||
|
|
WHERE camera_code IS NULL OR camera_code = '';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 迁移前后对比
|
|||
|
|
|
|||
|
|
### 迁移前
|
|||
|
|
```
|
|||
|
|
wvp_stream_proxy:
|
|||
|
|
- app: "live"
|
|||
|
|
- stream: "camera01"
|
|||
|
|
- camera_code: NULL ❌
|
|||
|
|
|
|||
|
|
wvp_ai_roi:
|
|||
|
|
- camera_id: "live/camera01" ❌ (拼接格式,不稳定)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 迁移后
|
|||
|
|
```
|
|||
|
|
wvp_stream_proxy:
|
|||
|
|
- app: "live"
|
|||
|
|
- stream: "camera01"
|
|||
|
|
- camera_code: "cam_c4ca4238a0b9" ✅
|
|||
|
|
|
|||
|
|
wvp_ai_roi:
|
|||
|
|
- camera_id: "cam_c4ca4238a0b9" ✅ (唯一标识符,稳定)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔄 回滚(如果需要)
|
|||
|
|
|
|||
|
|
如果迁移出现问题,可以执行回滚:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 1. 恢复 ROI 表的 camera_id(需要在迁移前备份原始值)
|
|||
|
|
-- 注意:此步骤需要提前备份!
|
|||
|
|
UPDATE wvp_ai_roi r
|
|||
|
|
INNER JOIN wvp_stream_proxy sp ON r.camera_id = sp.camera_code
|
|||
|
|
SET r.camera_id = CONCAT(sp.app, '/', sp.stream);
|
|||
|
|
|
|||
|
|
-- 2. 删除 camera_code 列
|
|||
|
|
ALTER TABLE wvp_stream_proxy DROP COLUMN camera_code;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
⚠️ **重要**: 建议在执行迁移前先备份数据库!
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 备份数据库
|
|||
|
|
mysqldump -u root -p wvp_aiot > wvp_aiot_backup_20260213.sql
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 执行完成标志
|
|||
|
|
|
|||
|
|
当您看到以下结果时,表示迁移成功:
|
|||
|
|
|
|||
|
|
- [x] 所有验证查询的 `issue_count` 都为 0
|
|||
|
|
- [x] 样本数据中的 camera_code 格式为 `cam_xxxxxxxxxxxx`
|
|||
|
|
- [x] wvp_ai_roi 表的 camera_id 已更新为 camera_code
|
|||
|
|
- [x] 前端摄像头管理页面能正常显示"应用名"列
|
|||
|
|
- [x] 新增摄像头时自动生成 camera_code
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**执行人**: _______________
|
|||
|
|
**执行时间**: _______________
|
|||
|
|
**验证结果**: ☐ 通过 ☐ 失败
|
|||
|
|
**备注**: _______________
|