diff --git a/Task-92-Sense%E6%97%A7%E8%AE%BE%E5%A4%87%E8%83%BD%E5%8A%9BJSONB%E5%85%BC%E5%AE%B9%E8%BF%81%E7%A7%BB.-.md b/Task-92-Sense%E6%97%A7%E8%AE%BE%E5%A4%87%E8%83%BD%E5%8A%9BJSONB%E5%85%BC%E5%AE%B9%E8%BF%81%E7%A7%BB.-.md index d6410dc..d661b35 100644 --- a/Task-92-Sense%E6%97%A7%E8%AE%BE%E5%A4%87%E8%83%BD%E5%8A%9BJSONB%E5%85%BC%E5%AE%B9%E8%BF%81%E7%A7%BB.-.md +++ b/Task-92-Sense%E6%97%A7%E8%AE%BE%E5%A4%87%E8%83%BD%E5%8A%9BJSONB%E5%85%BC%E5%AE%B9%E8%BF%81%E7%A7%BB.-.md @@ -1,71 +1,72 @@ -# 92 Sense旧设备能力JSONB兼容迁移 - -- 类型:缺陷 -- 所属 Epic:#7 -- 所属 MVP / 版本:#8 -- 状态:已完成 -- 日期:2026-08-15 -- Gitea 工单:https://git.ilapage.cn/ila/yovision/issues/92 -- Wiki 页面:Task-92-Sense旧设备能力JSONB兼容迁移 -- Wiki revision:见本地镜像头 - -## 背景与目标 - -用户通过 Windows 包启动 Sense 时,设备迁移报 PostgreSQL `SQLSTATE 42804`。只读诊断确认旧数据库的 `sense_devices.capabilities` 为 `text NOT NULL DEFAULT ''::text`,一条旧记录保存受支持的单值能力但不是 JSON;新模型要求 JSONB,GORM AutoMigrate 无法直接转换旧默认值和数据。 - -目标是在不删除设备、不跳过迁移、不修改当前用户数据库的前提下,为旧 schema 提供确定性、可回滚的 JSONB 兼容迁移。 - -## 最终方案 - -在现有 `2026081414000` 设备迁移事务开头执行 PostgreSQL 专用兼容步骤。受影响数据库尚未登记该迁移版本,后置新版本无法越过失败点,因此兼容逻辑必须放在原失败迁移内。 - -兼容步骤只处理既有 text/varchar 列:取得 ACCESS EXCLUSIVE 表锁,读取并验证全部旧值后才开始改变默认值和数据。空值转为 `[]`;六种受支持旧单值转为单元素 JSON 数组;合法字符串数组规范化后保持语义。未知单值、对象、非字符串数组、未知数组元素和超过 16 项的数组会返回不含业务值的错误,整个事务回滚。 - -全部验证通过后删除旧 text 默认值,参数化更新规范 JSON,使用显式 `USING capabilities::jsonb` 转型并设置 `'[]'::jsonb` 默认值,再继续原有 GORM AutoMigrate、菜单、权限和迁移版本登记。非 PostgreSQL、表/列不存在以及已是 json/jsonb 时不执行旧值转换。 - -## 修改文件 - -- `Sense/server/cmd/migrate/migration/version/2026081414000_device.go`:事务内旧 text 能力验证、转换、锁表和 JSONB 默认值处理。 -- `Sense/server/cmd/migrate/migration/version/2026081414000_device_test.go`:纯函数和隔离 PostgreSQL 17 回归,包括完整设备迁移与版本登记。 -- Wiki `Troubleshooting`、`docs/06-troubleshooting.md`:备份、重试、未知旧值和回退说明。 -- `wiki-docs.json`、`docs/task/92-Sense旧设备能力JSONB兼容迁移.md`:任务归档登记和镜像。 - -## 验收结果 - -| 验收标准 | 结果 | -|---|---| -| 旧 text 默认值不再触发 SQLSTATE 42804 | 通过;隔离 PostgreSQL 重现结构成功转为 jsonb | -| 旧单值无损转换为 JSONB 数组 | 通过;`video` 转为单元素数组 | -| 空值与合法数组正确处理 | 通过;空字符串转空数组,合法数组保持顺序与值 | -| 未知值拒绝且无半成品 | 通过;类型、默认值和已验证行均保持旧状态 | -| 新库、jsonb 和重复执行兼容 | 通过;无表跳过、jsonb 重复调用无操作 | -| 全量 Go 和迁移回归通过 | 通过 | -| 当前用户数据库未被修改 | 通过;仅执行只读诊断,写测试使用独立数据库并在结束后删除 | -| Wiki 与归档一致 | 受影响页面定向检查通过 | - -## 测试 - -- `go test ./cmd/migrate/migration/version -run TestCanonicalLegacyCapabilities -count=1 -v`:通过。 -- 隔离 PostgreSQL 17 `TestDeviceCapabilitiesMigrationOnPostgres`:旧单值/空值/合法数组、未知值事务回滚、无表、重复调用、完整设备迁移和 `sys_migration` 登记全部通过。 -- 隔离数据库 `sense92_migration_test` 测试前确认不存在,测试后确认计数为 0。 -- `go test ./...`、`go vet ./...`、`go build ./...`:通过。 -- `go test -race ./cmd/migrate/migration/version -run TestCanonicalLegacyCapabilities -count=1`:通过。 -- `git diff --check`:通过。 -- 受影响 Wiki 定向同步与检查:通过。 -- **未验证部分**:按工单安全边界未在当前用户 `sense` 数据库执行写迁移,也未替换当前 `Sense/dist` 中的待验收 #70 二进制;需先备份,再部署包含 #92 的新包进行最终启动验收。全量 Wiki 检查仍会先发现待验收 PR #89 的 #70 镜像尚未合入 `dev`。 - +# 92 Sense旧设备能力JSONB兼容迁移 + +- 类型:缺陷 +- 所属 Epic:#7 +- 所属 MVP / 版本:#8 +- 状态:已完成 +- 日期:2026-08-15 +- Gitea 工单:https://git.ilapage.cn/ila/yovision/issues/92 +- Wiki 页面:Task-92-Sense旧设备能力JSONB兼容迁移 +- Wiki revision:见本地镜像头 + +## 背景与目标 + +用户通过 Windows 包启动 Sense 时,设备迁移报 PostgreSQL `SQLSTATE 42804`。只读诊断确认旧数据库的 `sense_devices.capabilities` 为 `text NOT NULL DEFAULT ''::text`,一条旧记录保存受支持的单值能力但不是 JSON;新模型要求 JSONB,GORM AutoMigrate 无法直接转换旧默认值和数据。 + +目标是在不删除设备、不跳过迁移、不修改当前用户数据库的前提下,为旧 schema 提供确定性、可回滚的 JSONB 兼容迁移。 + +## 最终方案 + +在现有 `2026081414000` 设备迁移事务开头执行 PostgreSQL 专用兼容步骤。受影响数据库尚未登记该迁移版本,后置新版本无法越过失败点,因此兼容逻辑必须放在原失败迁移内。 + +兼容步骤只处理既有 text/varchar 列:取得 ACCESS EXCLUSIVE 表锁,读取并验证全部旧值后才开始改变默认值和数据。空值转为 `[]`;六种受支持旧单值转为单元素 JSON 数组;合法字符串数组规范化后保持语义。未知单值、对象、非字符串数组、未知数组元素和超过 16 项的数组会返回不含业务值的错误,整个事务回滚。 + +全部验证通过后删除旧 text 默认值,参数化更新规范 JSON,使用显式 `USING capabilities::jsonb` 转型并设置 `'[]'::jsonb` 默认值,再继续原有 GORM AutoMigrate、菜单、权限和迁移版本登记。非 PostgreSQL、表/列不存在以及已是 json/jsonb 时不执行旧值转换。 + +## 修改文件 + +- `Sense/server/cmd/migrate/migration/version/2026081414000_device.go`:事务内旧 text 能力验证、转换、锁表和 JSONB 默认值处理。 +- `Sense/server/cmd/migrate/migration/version/2026081414000_device_test.go`:纯函数和隔离 PostgreSQL 17 回归,包括完整设备迁移与版本登记。 +- Wiki `Troubleshooting`、`docs/06-troubleshooting.md`:备份、重试、未知旧值和回退说明。 +- `wiki-docs.json`、`docs/task/92-Sense旧设备能力JSONB兼容迁移.md`:任务归档登记和镜像。 + +## 验收结果 + +| 验收标准 | 结果 | +|---|---| +| 旧 text 默认值不再触发 SQLSTATE 42804 | 通过;隔离 PostgreSQL 重现结构成功转为 jsonb | +| 旧单值无损转换为 JSONB 数组 | 通过;`video` 转为单元素数组 | +| 空值与合法数组正确处理 | 通过;空字符串转空数组,合法数组保持顺序与值 | +| 未知值拒绝且无半成品 | 通过;类型、默认值和已验证行均保持旧状态 | +| 新库、jsonb 和重复执行兼容 | 通过;无表跳过、jsonb 重复调用无操作 | +| 全量 Go 和迁移回归通过 | 通过 | +| 当前用户数据库未被修改 | 通过;仅执行只读诊断,写测试使用独立数据库并在结束后删除 | +| Wiki 与归档一致 | 受影响页面定向检查通过 | + +## 测试 + +- `go test ./cmd/migrate/migration/version -run TestCanonicalLegacyCapabilities -count=1 -v`:通过。 +- 隔离 PostgreSQL 17 `TestDeviceCapabilitiesMigrationOnPostgres`:旧单值/空值/合法数组、未知值事务回滚、无表、重复调用、完整设备迁移和 `sys_migration` 登记全部通过。 +- 隔离数据库 `sense92_migration_test` 测试前确认不存在,测试后确认计数为 0。 +- `go test ./...`、`go vet ./...`、`go build ./...`:通过。 +- `go test -race ./cmd/migrate/migration/version -run TestCanonicalLegacyCapabilities -count=1`:通过。 +- `git diff --check`:通过。 +- 受影响 Wiki 定向同步与检查:通过。 +- **未验证部分**:按工单安全边界未在当前用户 `sense` 数据库执行写迁移,也未替换当前 `Sense/dist` 中的待验收 #70 二进制;需先备份,再部署包含 #92 的新包进行最终启动验收。全量 Wiki 检查仍会先发现待验收 PR #89 的 #70 镜像尚未合入 `dev`。 + ## 用户验收 - 用户于 2026-08-15 明确确认 `#92 验收通过`。 - 实现 PR #93 已合入 `dev`,合并提交为 `eaa6ae081542b0b9c74cc2d92a9138639fdbe530`。 - #92 已完成;真实数据库备份、交付包重建与启动回归继续在 #70 的交付闭环中执行。 -## 遗留问题 - -- 当前交付包仍含 #92 修复前的 `sense.exe`。#92 合入 `dev` 后需让 #70 交付分支吸收该提交并重新打包,用户备份数据库后再运行迁移。 -- Harness strict 的 #66/#67 既有归档格式问题不属于本工单。 - -## 相关提交 - -- `68b436a` 兼容旧设备能力字段迁移。 -- `fe3badf` 覆盖旧设备完整迁移链。 -- `aee5f45` 记录设备能力迁移排错。 + +## 遗留问题 + +- 当前交付包仍含 #92 修复前的 `sense.exe`。#92 合入 `dev` 后需让 #70 交付分支吸收该提交并重新打包,用户备份数据库后再运行迁移。 +- Harness strict 的 #66/#67 既有归档格式问题不属于本工单。 + +## 相关提交 + +- `68b436a` 兼容旧设备能力字段迁移。 +- `fe3badf` 覆盖旧设备完整迁移链。 +- `aee5f45` 记录设备能力迁移排错。