# 数据库软删除 (Soft Delete) 统一标准规范 ## 1. 核心目标 - **数据保留**:防止误操作导致的数据永久丢失,支持操作审计。 - **级联安全**:通过逻辑链路同步软删关联数据,避免孤儿数据。 - **透明过滤**:利用 RLS(行级安全策略)或统一过滤口径,使业务层查询默认排除已标记删除的记录。 ## 2. 字段规范 所有需要支持软删除的业务表必须统一包含以下字段: | 字段名 | 类型 | 说明 | 索引建议 | | :------------ | :------------ | :-------------------------------------- | :--------------- | | `deleted_at` | `TIMESTAMPTZ` | 删除时间戳。非 NULL 表示已删除。 | **必须建立索引** | | `deleted_by` | `UUID` | 执行删除操作的用户 ID (`ak_users.id`)。 | 建议索引 | | `restored_at` | `TIMESTAMPTZ` | (可选) 最近一次恢复的时间戳。 | - | | `restored_by` | `UUID` | (可选) 最近一次恢复的操作人。 | - | ## 3. RLS 自动过滤口径 为了确保查询透明性,必须在 `20_rls/` 策略中统一加入过滤条件: ```sql -- 示例策略:仅允许查询未删除的记录 CREATE POLICY select_active_records ON public.your_table FOR SELECT TO authenticated USING (deleted_at IS NULL); ``` ## 4. RPC 重构准则 删除接口必须从 `DELETE FROM ...` 改为 `UPDATE ... SET deleted_at = now()`: ### 4.1 基础删除模板 ```sql CREATE OR REPLACE FUNCTION public.rpc_admin_soft_delete_item(p_id UUID) RETURNS BOOLEAN SECURITY DEFINER SET search_path = public AS $$ BEGIN -- 1. 鉴权 (示例) IF NOT EXISTS ( SELECT 1 FROM public.ak_users WHERE auth_id = auth.uid() AND role = 'admin' ) THEN RAISE EXCEPTION 'Permission denied'; END IF; -- 2. 执行软删 UPDATE public.your_table SET deleted_at = now(), deleted_by = auth.uid() WHERE id = p_id AND deleted_at IS NULL; RETURN FOUND; END; $$ LANGUAGE plpgsql; ``` ### 4.2 级联软删除规范 若存在级联依赖(如:软删“商品分类”时需同步软删其下的“商品”),必须在 RPC 内部通过事务或同步 UPDATE 处理: ```sql -- 级联链路示例 UPDATE public.ml_products SET deleted_at = now(), deleted_by = auth.uid() WHERE category_id = p_category_id AND deleted_at IS NULL; ``` ## 5. UI 交互适配 - **默认视图**:列表页面默认仅展示 `deleted_at IS NULL` 的数据。 - **回收站 (可选)**:若业务需要,可提供“回收站”视图(过滤 `deleted_at IS NOT NULL`)及“恢复”功能。 - **二次确认**:点击“删除”按钮时,必须弹出提示明确告知数据将进入逻辑删除状态。 --- ## 6. 改造优先级 1. **第一梯队**:基础资产(商品、分类、文章、分销商)。 2. **第二梯队**:配置类(运费模板、通知模板、DIY 页面)。 3. **第三梯队**:日志/交互类(评论、收藏、地址)。