fix(数据层): 修复数据完整性与仓储一致性问题
## 数据完整性修复 (fix-critical-data-integrity) - 添加 error_translate.go 统一错误转换层 - 修复 nil 输入和 NotFound 错误处理 - 增强仓储层错误一致性 ## 仓储一致性修复 (fix-high-repository-consistency) - Group schema 添加 default_validity_days 字段 - Account schema 添加 proxy edge 关联 - 新增 UsageLog ent schema 定义 - 修复 UpdateBalance/UpdateConcurrency 受影响行数校验 ## 数据卫生修复 (fix-medium-data-hygiene) - UserSubscription 添加软删除支持 (SoftDeleteMixin) - RedeemCode/Setting 添加硬删除策略文档 - account_groups/user_allowed_groups 的 created_at 声明 timestamptz - 停止写入 legacy users.allowed_groups 列 - 新增迁移: 011-014 (索引优化、软删除、孤立数据审计、列清理) ## 测试补充 - 添加 UserSubscription 软删除测试 - 添加迁移回归测试 - 添加 NotFound 错误测试 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
39
backend/migrations/011_remove_duplicate_unique_indexes.sql
Normal file
39
backend/migrations/011_remove_duplicate_unique_indexes.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- 011_remove_duplicate_unique_indexes.sql
|
||||
-- 移除重复的唯一索引
|
||||
-- 这些字段在 ent schema 的 Fields() 中已声明 .Unique(),
|
||||
-- 因此在 Indexes() 中再次声明 index.Fields("x").Unique() 会创建重复索引。
|
||||
-- 本迁移脚本清理这些冗余索引。
|
||||
|
||||
-- 重复索引命名约定(由 Ent 自动生成/历史迁移遗留):
|
||||
-- - 字段级 Unique() 创建的索引名: <table>_<field>_key
|
||||
-- - Indexes() 中的 Unique() 创建的索引名: <table>_<field>
|
||||
-- - 初始化迁移中的非唯一索引: idx_<table>_<field>
|
||||
|
||||
-- 仅当索引存在时才删除(幂等操作)
|
||||
|
||||
-- api_keys 表: key 字段
|
||||
DROP INDEX IF EXISTS apikey_key;
|
||||
DROP INDEX IF EXISTS api_keys_key;
|
||||
DROP INDEX IF EXISTS idx_api_keys_key;
|
||||
|
||||
-- users 表: email 字段
|
||||
DROP INDEX IF EXISTS user_email;
|
||||
DROP INDEX IF EXISTS users_email;
|
||||
DROP INDEX IF EXISTS idx_users_email;
|
||||
|
||||
-- settings 表: key 字段
|
||||
DROP INDEX IF EXISTS settings_key;
|
||||
DROP INDEX IF EXISTS idx_settings_key;
|
||||
|
||||
-- redeem_codes 表: code 字段
|
||||
DROP INDEX IF EXISTS redeemcode_code;
|
||||
DROP INDEX IF EXISTS redeem_codes_code;
|
||||
DROP INDEX IF EXISTS idx_redeem_codes_code;
|
||||
|
||||
-- groups 表: name 字段
|
||||
DROP INDEX IF EXISTS group_name;
|
||||
DROP INDEX IF EXISTS groups_name;
|
||||
DROP INDEX IF EXISTS idx_groups_name;
|
||||
|
||||
-- 注意: 每个字段的唯一约束仍由字段级 Unique() 创建的约束保留,
|
||||
-- 如 api_keys_key_key、users_email_key 等。
|
||||
13
backend/migrations/012_add_user_subscription_soft_delete.sql
Normal file
13
backend/migrations/012_add_user_subscription_soft_delete.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- 012: 为 user_subscriptions 表添加软删除支持
|
||||
-- 任务:fix-medium-data-hygiene 1.1
|
||||
|
||||
-- 添加 deleted_at 字段
|
||||
ALTER TABLE user_subscriptions
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ DEFAULT NULL;
|
||||
|
||||
-- 添加 deleted_at 索引以优化软删除查询
|
||||
CREATE INDEX IF NOT EXISTS usersubscription_deleted_at
|
||||
ON user_subscriptions (deleted_at);
|
||||
|
||||
-- 注释:与其他使用软删除的实体保持一致
|
||||
COMMENT ON COLUMN user_subscriptions.deleted_at IS '软删除时间戳,NULL 表示未删除';
|
||||
32
backend/migrations/013_log_orphan_allowed_groups.sql
Normal file
32
backend/migrations/013_log_orphan_allowed_groups.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- 013: 记录 users.allowed_groups 中的孤立 group_id
|
||||
-- 任务:fix-medium-data-hygiene 3.1
|
||||
--
|
||||
-- 目的:在删除 legacy allowed_groups 列前,记录所有引用了不存在 group 的孤立记录
|
||||
-- 这些记录可用于审计或后续数据修复
|
||||
|
||||
-- 创建审计表存储孤立的 allowed_groups 记录
|
||||
CREATE TABLE IF NOT EXISTS orphan_allowed_groups_audit (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
group_id BIGINT NOT NULL,
|
||||
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (user_id, group_id)
|
||||
);
|
||||
|
||||
-- 记录孤立的 group_id(存在于 users.allowed_groups 但不存在于 groups 表)
|
||||
INSERT INTO orphan_allowed_groups_audit (user_id, group_id)
|
||||
SELECT u.id, x.group_id
|
||||
FROM users u
|
||||
CROSS JOIN LATERAL unnest(u.allowed_groups) AS x(group_id)
|
||||
LEFT JOIN groups g ON g.id = x.group_id
|
||||
WHERE u.allowed_groups IS NOT NULL
|
||||
AND g.id IS NULL
|
||||
ON CONFLICT (user_id, group_id) DO NOTHING;
|
||||
|
||||
-- 添加索引便于查询
|
||||
CREATE INDEX IF NOT EXISTS idx_orphan_allowed_groups_audit_user_id
|
||||
ON orphan_allowed_groups_audit(user_id);
|
||||
|
||||
-- 记录迁移完成信息
|
||||
COMMENT ON TABLE orphan_allowed_groups_audit IS
|
||||
'审计表:记录 users.allowed_groups 中引用的不存在的 group_id,用于数据清理前的审计';
|
||||
15
backend/migrations/014_drop_legacy_allowed_groups.sql
Normal file
15
backend/migrations/014_drop_legacy_allowed_groups.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- 014: 删除 legacy users.allowed_groups 列
|
||||
-- 任务:fix-medium-data-hygiene 3.3
|
||||
--
|
||||
-- 前置条件:
|
||||
-- - 迁移 007 已将数据回填到 user_allowed_groups 联接表
|
||||
-- - 迁移 013 已记录所有孤立的 group_id 到审计表
|
||||
-- - 应用代码已停止写入该列(3.2 完成)
|
||||
--
|
||||
-- 该列现已废弃,所有读写操作均使用 user_allowed_groups 联接表。
|
||||
|
||||
-- 删除 allowed_groups 列
|
||||
ALTER TABLE users DROP COLUMN IF EXISTS allowed_groups;
|
||||
|
||||
-- 添加注释记录删除原因
|
||||
COMMENT ON TABLE users IS '用户表。注:原 allowed_groups BIGINT[] 列已迁移至 user_allowed_groups 联接表';
|
||||
Reference in New Issue
Block a user