Files
social-app/docs/plans/2026-02-26-social-data-model-redesign.md
T

578 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plan: social-app 数据库数据模型重设计(支持社交/事项/自动化)
**Date:** 2026-02-26
**Author:** AI Assistant
**Status:** Draft
## Overview
本方案面向 `social-app` 的下一阶段功能升级,重设计 PostgreSQL 数据模型,统一支持用户专属 agent、好友/群组协作、待处理消息、设置、可订阅且可授权编辑的日程事项、待办联动与自动化定时任务。目标是在 FastAPI + Flutter 协作场景下提供长期稳定的数据基础,降低后续 API 演进和跨端同步复杂度。
## Requirements
### Functional
- [x] 每个用户有专属 agent,且模型可扩展到未来多 agent 能力
- [x] 用户支持好友关系、群组创建与成员管理
- [x] 用户支持 inbox/pending 待处理消息
- [x] 用户支持个性化设置(偏好/隐私/通知)
- [x] 用户支持“绑定日程的事项”,可多人订阅,且仅特定人可修改
- [x] 用户支持待办事项(可由日程事项提取,也可手动创建)
- [x] 用户支持自动化定时任务(循环触发)
### Non-Functional
- [x] 性能:核心读路径(inbox 列表、待办列表、事项列表)P95 < 150ms(单用户典型数据量)
- [x] 安全:权限以后端业务授权为准;数据库层保留 RLS 防御边界
- [x] 一致性:关键写路径(好友状态、权限变更、任务触发)使用事务保障
- [x] 可演进:支持旧表迁移、双写与灰度切换
## Technical Approach
采用“认证域(`auth.users`+ 业务域(`public.*`)”分层建模。保持 `auth.users` 作为身份主键来源,业务表统一引用 `user_id UUID -> auth.users.id`。领域边界拆分为:Identity/Profile、Social Graph、Collaboration(事项/订阅/权限)、Inbox、Todo、Automation。通过“规范化主模型 + 局部物化/冗余快照”平衡一致性与查询性能。
### Key Decisions
| Decision | Rationale |
|----------|-----------|
| 用户与 agent 采用 1:1 主约束 + 可扩展结构 | 当前满足“每用户专属 agent”,未来允许多 agent 形态演进 |
| 好友关系用单表双向规范化表示 | 避免 A-B / B-A 重复,降低去重成本 |
| 事项权限采用 ACL 表而非仅 owner | 满足“仅特定人可修改”的协作场景 |
| 待办采用主表 + 源映射表 | 支持从事项提取、手动创建、去重与追踪来源 |
| 自动化调度采用 `rrule + cron + interval` 三选一 | 同时覆盖日历型循环和工程型间隔任务 |
| inbox 采用事件聚合模型 | 将好友请求/群组邀请/事项变更统一进入待处理中心 |
## A. 设计原则与边界
### 1) 核心实体与聚合边界
- 用户聚合:`profiles`(含 settings JSONB, `user_agents`
- 社交聚合:`friendships`, `groups`, `group_members`
- 协作事项聚合:`schedule_items`, `schedule_item_subscriptions`, `schedule_item_permissions`
- 消息聚合:`inbox_events`, `inbox_receipts`
- 待办聚合:`todos`, `todo_sources`
- 自动化聚合:`automation_jobs`, `automation_schedules`, `automation_runs`
### 2) 一致性分级
- 强一致(同事务):好友关系状态迁移、群组成员角色变更、事项权限写入、定时任务抢占执行
- 最终一致:inbox 衍生、待办同步、提醒派发(允许异步补偿)
### 3) 多租户假设
- 默认假设:单租户产品(同一业务库服务所有用户),以 `user_id` 做数据隔离
- 扩展预留:各核心表可预留 `tenant_id UUID NULL`(需业务确认是否近期引入组织空间)
## B. 领域模型与关系图(文字化)
### 实体与关系
- `auth.users (1) - (1) profiles`settings 作为 JSONB 内嵌)
- `auth.users (1) - (1) user_agents`
- `auth.users (N) - (N) auth.users` 通过 `friendships`
- `auth.users (1) - (N) groups`(创建者)
- `groups (1) - (N) group_members``auth.users (1) - (N) group_members`
- `auth.users (1) - (N) schedule_items`(创建者)
- `schedule_items (1) - (N) schedule_item_subscriptions``auth.users (1) - (N) schedule_item_subscriptions`
- `schedule_items (1) - (N) schedule_item_permissions``auth.users (1) - (N) schedule_item_permissions`
- `inbox_events (1) - (N) inbox_receipts``auth.users (1) - (N) inbox_receipts`
- `auth.users (1) - (N) todos`
- `todos (1) - (N) todo_sources`(一条待办可有多个来源记录,默认 1 条)
- `auth.users (1) - (N) automation_jobs`
- `automation_jobs (1) - (N) automation_schedules`
- `automation_jobs (1) - (N) automation_runs`
### 关键约束
- 唯一性:
- `user_agents.user_id` 唯一
- `friendships(user_low_id, user_high_id)` 唯一
- `group_members(group_id, user_id)` 唯一
- `schedule_item_subscriptions(item_id, subscriber_id)` 唯一
- `schedule_item_permissions(item_id, subject_type, subject_id, permission)` 唯一
- `todo_sources(source_type, source_id, owner_id)` 唯一(防止重复抽取)
- `automation_runs(job_id, idempotency_key)` 唯一
- 外键:统一显式 `ON DELETE` 策略(见下)
- 可空性:权限关键字段、状态字段、外部幂等键默认 `NOT NULL`
- 删除策略:
- 用户删除:大部分 `CASCADE`(用户私有数据);跨用户协作数据优先软删
- 事项删除:对子表 `CASCADE`;待办来源保留历史可改 `SET NULL` + 软删
## C. 数据库表设计(PostgreSQL
以下为推荐主表(方案 1,规范化优先)。字段示例采用 `UUID + timestamptz + enum/text-check`
### 1) 用户与 agent
#### `profiles`(已有,建议补齐)
- PK: `id UUID` (`auth.users.id`)
- 关键字段: `username`, `avatar_url`, `bio`
- **新增 JSONB 字段**:
- `settings JSONB`(用户自定义设置,含 `preferences`, `privacy`, `notification` 三大块)
- `settings_version INTEGER DEFAULT 1`(兼容旧数据的版本字段)
- 时间字段: `created_at`, `updated_at`, `deleted_at`
- 索引:
- `INDEX(username)`(允许重名,仅用于列表查询)
- `GIN(settings)`(支持 JSONB 表达式查询)
- 表达式索引:`(settings->>'notification_enabled')`(按需,对高频查询字段单独建)
- 审计: `created_by`, `updated_by`(可等于 id
- 删除策略: 用户删除时 `CASCADE`
#### `user_agents`
- PK: `id UUID`
- 关键字段:
- `user_id UNIQUE`(每用户专属 agent
- `llm_id UUID NOT NULL`(关联绑定的 LLM 模型)
- `agent_type VARCHAR(20) NOT NULL`(枚举限制:`INTENT_RECOGNITION` | `TASK_EXECUTION` | `RESULT_REPORTING`
- `config JSONB`agent 配置参数)
- `capability_version INTEGER DEFAULT 1`
- 时间字段: `created_at`, `updated_at`, `deleted_at`
- 状态字段: `status``active|paused|migrating`
- 索引:
- `UNIQUE(user_id) WHERE deleted_at IS NULL`
- `INDEX(status)`
- `INDEX(agent_type)`
- `GIN(config)`(按需)
- 审计: `created_by`, `updated_by`
### 2) 社交关系
#### `friendships`
- PK: `id UUID`
- 关键字段:
- `user_low_id`(两者中较小的 UUID
- `user_high_id`(两者中较大的 UUID
- `initiator_id`(发起请求方的 user_id,用于追溯谁主动)
- `status`, `requested_at`, `accepted_at`, `blocked_by`
- 时间字段: `created_at`, `updated_at`
- 状态字段: `status``pending|accepted|blocked|declined|canceled`
- 约束:
- `CHECK(user_low_id < user_high_id)`(强制小值放 low,大值放 high,确保 A→B 和 B→A 是同一行)
- `CHECK(initiator_id IN (user_low_id, user_high_id))`
- `UNIQUE(user_low_id, user_high_id)`
- 索引:
- `INDEX(user_low_id, status)`
- `INDEX(user_high_id, status)`
- 部分索引 `INDEX(status) WHERE status='pending'`
- 审计: `created_by`, `updated_by`
**查询示例**
- 查询用户 A 的所有好友:`SELECT * FROM friendships WHERE user_low_id = A OR user_high_id = A`
#### `groups`
- PK: `id UUID`
- 关键字段: `name`, `description`, `visibility`, `owner_id`
- 时间字段: `created_at`, `updated_at`, `deleted_at`
- 状态字段: `status``active|archived`
- 索引: `INDEX(owner_id, status)`, `INDEX(visibility)`
- 审计: `created_by`, `updated_by`
#### `group_members`
- PK: `id UUID`
- 关键字段:
- `group_id`, `user_id`
- `role`JSONB 数组,权柄组合,如 `["view"]` / `["view", "invite"]` / `["view", "invite", "edit"]`
- `join_source``invited|joined`
- `invited_by`, `joined_at`
- 时间字段: `created_at`, `updated_at`, `removed_at`
- 状态字段: `status``active|muted|removed`
- 约束: `UNIQUE(group_id, user_id)`
- 索引:
- `INDEX(group_id, status)`
- `INDEX(user_id, status)`
- GIN 索引支持权柄查询:`INDEX group_members_role USING GIN(role)`
- 审计: `created_by`, `updated_by`
**权柄说明**
| 权柄 | 含义 |
|------|------|
| `view` | 查看群组信息、成员列表、聊天记录 |
| `invite` | 邀请新成员入群 |
| `edit` | 修改群组信息、管理成员(禁言/移除) |
- 群主(创建者)默认拥有全部权柄:`["view", "invite", "edit"]`
- 权柄可动态变更:服务层使用 `jsonb ||``jsonb -` 原子操作增减权柄
### 3) 用户设置(已合并至 profiles 表)
用户设置采用 JSONB 内嵌方式,渐进式扩展无需改表结构:
```json
{
"version": 1,
"preferences": {
"theme": "dark",
"language": "zh-CN",
"timezone": "Asia/Shanghai"
},
"privacy": {
"profile_visible_to": "friends",
"activity_visible_to": "friends",
"allow_friend_requests": true
},
"notification": {
"enabled": true,
"push_enabled": true,
"email_enabled": false,
"quiet_hours_start": "22:00",
"quiet_hours_end": "08:00"
}
}
```
- 扩展方式:新增字段时递增 `settings_version`,应用层做 schema 兼容
- 索引策略:对高频查询字段(如 `notification.enabled`)使用表达式索引
- 更新方式:服务层使用 JSONB merge 或字段级 UPDATE,避免读-改-写并发问题(建议用 `jsonb_set` 原子操作)
### 4) 事项与订阅/权限
#### `schedule_items`
- PK: `id UUID`
- 关键字段:
- `owner_id`, `title`, `description`
- `start_at`, `end_at`, `timezone`
- `recurrence_rule`(可空)
- `source_type``manual|imported|agent_generated`
- 时间字段: `created_at`, `updated_at`, `deleted_at`
- 状态字段: `status``running|completed|archived`
- 索引:
- `INDEX(owner_id, start_at)`
- `INDEX(status, start_at)`
- `INDEX(updated_at DESC)`
- 审计: `created_by`, `updated_by`
#### `schedule_item_subscriptions`
- PK: `id UUID`
- 关键字段: `item_id`, `subscriber_id`, `notify_level`, `subscription_source`
- 时间字段: `created_at`, `updated_at`, `unsubscribed_at`
- 状态字段: `status``active|paused|unsubscribed`
- 约束: `UNIQUE(item_id, subscriber_id)`
- 索引: `INDEX(subscriber_id, status)`, `INDEX(item_id, status)`
- 审计: `created_by`, `updated_by`
#### `schedule_item_permissions`
- PK: `id UUID`
- 关键字段: `item_id`, `subject_type``user|group|friend_circle`, `subject_id`, `permission`
- 时间字段: `created_at`, `updated_at`, `expires_at`
- 状态字段: `status``active|revoked|expired`
- 约束:
- `UNIQUE(item_id, subject_type, subject_id, permission)`
- `permission` 建议枚举:`view|comment|edit|manage`
- 索引:
- `INDEX(item_id, permission, status)`
- `INDEX(subject_type, subject_id, status)`
- 审计: `granted_by`, `updated_by`
### 5) 待处理消息(Inbox
#### `inbox_events`
- PK: `id UUID`
- 关键字段: `event_type`, `actor_id`, `object_type`, `object_id`, `payload_jsonb`, `dedupe_key`
- 时间字段: `created_at`, `updated_at`
- 状态字段: `status``open|resolved|canceled`
- 约束: `UNIQUE(dedupe_key)`
- 索引: `INDEX(event_type, created_at DESC)`, `GIN(payload_jsonb)`
- 审计: `created_by`, `updated_by`
#### `inbox_receipts`
- PK: `id UUID`
- 关键字段: `event_id`, `recipient_id`, `inbox_state`, `action_required`, `acted_at`
- 时间字段: `created_at`, `updated_at`, `read_at`
- 状态字段: `inbox_state``pending|read|accepted|rejected|dismissed|expired`
- 约束: `UNIQUE(event_id, recipient_id)`
- 索引:
- `INDEX(recipient_id, inbox_state, created_at DESC)`
- 部分索引 `INDEX(recipient_id, created_at DESC) WHERE inbox_state='pending'`
- 审计: `created_by`, `updated_by`
### 6) 待办与来源映射
#### `todos`
- PK: `id UUID`
- 关键字段: `owner_id`, `title`, `description`, `due_at`, `priority`, `origin_type`, `source_hash`
- 时间字段: `created_at`, `updated_at`, `completed_at`, `deleted_at`
- 状态字段: `status``pending|in_progress|done|canceled|archived`
- 索引:
- `INDEX(owner_id, status, due_at)`
- `INDEX(owner_id, updated_at DESC)`
- 部分索引 `INDEX(owner_id, due_at) WHERE status IN ('pending','in_progress')`
- 审计: `created_by`, `updated_by`
#### `todo_sources`
- PK: `id UUID`
- 关键字段: `todo_id`, `owner_id`, `source_type``schedule_item|manual|inbox_event|automation`, `source_id`, `extracted_at`, `sync_mode`
- 时间字段: `created_at`, `updated_at`
- 状态字段: `status``linked|unlinked|stale`
- 约束: `UNIQUE(owner_id, source_type, source_id)`(去重关键)
- 索引:
- `INDEX(todo_id)`
- `INDEX(owner_id, source_type, status)`
- 审计: `created_by`, `updated_by`
### 7) 自动化定时任务
#### `automation_jobs`
- PK: `id UUID`
- 关键字段: `owner_id`, `name`, `job_type`, `target_type`, `target_id`, `params_jsonb`
- 时间字段: `created_at`, `updated_at`, `deleted_at`
- 状态字段: `status``active|paused|disabled`
- 索引: `INDEX(owner_id, status)`, `INDEX(target_type, target_id)`
- 审计: `created_by`, `updated_by`
#### `automation_schedules`
- PK: `id UUID`
- 关键字段:
- `job_id UNIQUE`
- `schedule_type``cron|rrule|interval`
- `cron_expr` / `rrule_text` / `interval_seconds`(三选一)
- `timezone`, `start_at`, `end_at`, `next_run_at`
- 时间字段: `created_at`, `updated_at`
- 状态字段: `status``active|paused|expired|invalid`
- 约束:
- `CHECK` 保证三种表达互斥且至少一项有效
- 索引:
- 部分索引 `INDEX(next_run_at) WHERE status='active'`
- `INDEX(job_id, status)`
- 审计: `created_by`, `updated_by`
#### `automation_runs`
- PK: `id UUID`
- 关键字段: `job_id`, `scheduled_for`, `started_at`, `finished_at`, `attempt`, `max_retries`, `idempotency_key`, `worker_id`, `result_jsonb`, `error_code`
- 时间字段: `created_at`, `updated_at`
- 状态字段: `status``queued|running|succeeded|failed|canceled|dead_letter`
- 约束:
- `UNIQUE(job_id, idempotency_key)`
- `CHECK(attempt <= max_retries + 1)`
- 索引:
- `INDEX(job_id, scheduled_for DESC)`
- `INDEX(status, scheduled_for)`
- 部分索引 `INDEX(status, updated_at) WHERE status IN ('queued','running')`
- 审计: `created_by`, `updated_by`
## D. 权限与协作模型
### 1) 事项编辑权限落表
- 权限决策顺序:
1. `schedule_items.owner_id`(天然 `manage`
2. `schedule_item_permissions` 针对 `subject_type=user` 的显式授权
3. 用户所在群组在 `subject_type=group` 的授权
4. 默认无编辑权限
- 建议在服务层计算“有效权限”,可落缓存字段 `effective_permission`(可选)
### 2) 群组角色与事项关系
- `group_members.role`JSONB 数组,权柄组合 `["view"]` / `["view", "invite"]` / `["view", "invite", "edit"]`
- 群主(创建者)默认拥有全部权柄
- 权柄与事项权限映射:
- 群成员 `view` → 事项 `view`
- 群成员 `invite` → 隐含 `view`
- 群成员 `edit` → 事项 `edit|manage`
- 若事项绑定群组上下文,可增 `schedule_items.context_group_id NULL`
## E. 消息与待办联动
### 1) inbox 关联来源
- `inbox_events.event_type` 建议枚举:
- `friend_request`
- `group_invitation`
- `group_role_changed`
- `schedule_item_changed`
- `schedule_item_permission_granted`
- `automation_run_failed`
- 通过 `object_type/object_id` 直接关联业务对象
### 2) 待办提取与防循环
- 从事项提取待办:
- 先计算 `source_hash = sha256(owner_id + source_type + source_id + title + due_at_bucket)`
-`todo_sources(owner_id, source_type, source_id)` 唯一约束防重复
- 防循环同步:
- `todo_sources.sync_mode``one_way|two_way`
- 当来源为 `schedule_item``two_way` 时,写回需携带 `sync_trace_id`
- 同一 `sync_trace_id` 在同一对象链路只消费一次(应用层幂等)
## F. 定时任务模型
### 1) 循环表达建议
- 推荐优先级:
- 用户型日历任务:`rrule`
- 运维/工程任务:`cron`
- 简单轮询:`interval_seconds`
- 数据层统一在 `automation_schedules`,并通过 `schedule_type` 区分
### 2) 触发记录与重试
- 任务调度器按 `next_run_at` 扫描活跃计划,插入 `automation_runs(status='queued')`
- 执行器抢占:`SELECT ... FOR UPDATE SKIP LOCKED`
- 重试策略:`attempt` 递增,失败后按退避策略更新下一次 `scheduled_for`
### 3) 幂等与并发冲突
- 幂等键:`idempotency_key = sha256(job_id + scheduled_for + logical_partition)`
- 冲突处理:唯一约束冲突时视为重复投递,直接忽略或合并结果
- 并发安全:`automation_jobs.version`(乐观锁)可选
## G. 演进与迁移计划(从旧表到新模型)
### Phase 1: 基础并行建模(8-12 小时)
1. 新建核心表(不删旧表):`user_agents`, `friendships`, `groups`, `group_members`
2.`profiles` 表新增 `settings JSONB`, `settings_version` 字段
3. 建立最小外键和索引,启用 RLS deny-all 策略
4. 提供只写新表的影子接口(内部开关)
### Phase 2: 协作与联动接入(12-16 小时)
1. 新建 `schedule_items*`, `inbox_*`, `todos*`, `automation_*`
2. 编写回填脚本(从旧事项/旧消息结构回填,若不存在则跳过)
3. 开启双写:旧接口写旧表同时写新表,记录双写差异日志
### Phase 3: 读切换与一致性校验(8-12 小时)
1. API 读路径灰度切换到新表(按用户百分比)
2. 每日对账:记录数、状态分布、关键字段哈希比对
3. 指标稳定后停止旧表写入,保留只读回滚窗口
### Phase 4: 收尾与清理(4-8 小时)
1. 下线旧读路径和旧双写逻辑
2. 保留旧表冷备份后归档/删除
3. 固化运行手册与告警阈值
### 回滚策略
- 任意阶段回滚:读切回旧表 + 关闭新表写开关
- 双写阶段故障:保留操作日志,可按时间窗重放补偿
- 最终切换前必须满足:新旧关键查询结果偏差 < 0.1%
## H. 两套方案对比
### 方案 1(推荐):规范化、可维护性优先
- 特点:按领域拆表,ACL 与来源映射独立,严格约束
- 优点:一致性好、权限边界清晰、长期演进成本低
- 缺点:联表较多,初期 API 复杂度较高
### 方案 2:开发效率优先、适度反规范化
- 特点:将部分结构合并为 JSONB(如 `permissions_snapshot`, `todo_origin`
- 优点:开发快、迁移初期改动小
- 缺点:约束弱、查询和审计困难、后续重构成本高
- 注:`user_settings` 已按此思路内嵌至 profiles,其他模块仍建议保持规范化
### 对比矩阵
| 维度 | 方案 1(规范化) | 方案 2(反规范化) |
|------|------------------|--------------------|
| 复杂度 | 中高 | 中 |
| 查询性能 | 读热点需索引与缓存优化 | 单行读取快,复杂筛选慢 |
| 写入成本 | 中(多表事务) | 低到中 |
| 扩展性 | 高 | 中低 |
| 风险 | 中(实施复杂) | 中高(数据质量和权限风险) |
### 推荐结论
- 推荐方案 1:当前业务已包含社交关系、协作权限、跨域联动和自动化调度,若选择反规范化将把复杂性转移到应用层并放大后续维护风险。方案 1 在 FastAPI + PostgreSQL 下更符合长期可维护与可审计目标。
## I. 交付物
### I-1. 可直接进入实现计划的结论性摘要(12 条)
1.`auth.users` 为身份主键,业务表统一 `user_id` 外键。
2. 引入 `user_agents`,通过 `UNIQUE(user_id)` 满足每用户专属 agent。
3. 用户设置内嵌至 `profiles.settings JSONB`,支持渐进式扩展。
4. 好友关系采用标准化双向一行模型,避免重复边。
4. 群组采用 `groups + group_members`,角色内建 owner/admin/member。
5. 事项、订阅、权限三表解耦,支持多人订阅与精细编辑授权。
6. inbox 采用 `events + receipts`,统一承载待处理动作。
7. 待办采用 `todos + todo_sources`,实现来源追踪与去重。
8. 自动化采用 `jobs + schedules + runs`,支持 cron/rrule/interval。
9. 所有关键表补齐状态机字段与审计字段,支持可观测与追责。
10. 索引以“用户维度 + 状态 + 时间”为主,兼顾移动端列表查询。
11. 迁移走“四阶段”:并行建模 -> 双写回填 -> 读切换 -> 清理。
12. 通过幂等键、部分索引和事务边界保障高并发稳定性。
### I-2. 后续 API 设计所需数据契约清单
- 用户/agent
- `UserAgentDTO`: `id,user_id,llm_id,agent_type,status,capability_version,config,updated_at`
- `UserSettingsDTO`(内嵌于 Profile: `settings JSONB, settings_version`
- 好友
- `FriendshipDTO`: `id,user_a,user_b,status,initiator_id,requested_at,accepted_at`
- 状态流转:`pending -> accepted|declined|canceled|blocked`
- 群组
- `GroupDTO`: `id,name,owner_id,visibility,status`
- `GroupMemberDTO`: `group_id,user_id,role,status,joined_at`
- 事项
- `ScheduleItemDTO`: `id,owner_id,title,start_at,end_at,status,timezone,recurrence_rule`
- `ScheduleSubscriptionDTO`: `item_id,subscriber_id,status,notify_level`
- `SchedulePermissionDTO`: `item_id,subject_type,subject_id,permission,status`
- Inbox
- `InboxEventDTO`: `id,event_type,object_type,object_id,payload,status,created_at`
- `InboxReceiptDTO`: `event_id,recipient_id,inbox_state,action_required,read_at,acted_at`
- Todo
- `TodoDTO`: `id,owner_id,title,due_at,status,priority,origin_type`
- `TodoSourceDTO`: `todo_id,source_type,source_id,sync_mode,status`
- 自动化
- `AutomationJobDTO`: `id,owner_id,job_type,target_type,target_id,status`
- `AutomationScheduleDTO`: `job_id,schedule_type,cron_expr,rrule_text,interval_seconds,next_run_at,status`
- `AutomationRunDTO`: `id,job_id,scheduled_for,status,attempt,idempotency_key,error_code`
### I-3. 最小可行迁移 DDL 清单(按优先级)
P0(身份与社交基础)
1. `ALTER TABLE profiles ADD COLUMN settings JSONB DEFAULT '{}'`
2. `ALTER TABLE profiles ADD COLUMN settings_version INTEGER DEFAULT 1`
3. `CREATE INDEX idx_profiles_settings_notification ON profiles ((settings->>'notification_enabled'))`
4. `CREATE TABLE user_agents (...)`
5. `CREATE TABLE friendships (...)`
6. `CREATE TABLE groups (...)`
7. `CREATE TABLE group_members (...)`
8. `CREATE INDEX/UNIQUE/CHECK`friendships 规范化约束)
P1(协作事项)
7. `CREATE TABLE schedule_items (...)`
8. `CREATE TABLE schedule_item_subscriptions (...)`
9. `CREATE TABLE schedule_item_permissions (...)`
10. `CREATE INDEX`owner/status/time + permission 查询)
P2(消息与待办)
11. `CREATE TABLE inbox_events (...)`
12. `CREATE TABLE inbox_receipts (...)`
13. `CREATE TABLE todos (...)`
14. `CREATE TABLE todo_sources (...)`
15. `CREATE UNIQUE INDEX uq_todo_sources_owner_source (...)`
P3(自动化)
16. `CREATE TABLE automation_jobs (...)`
17. `CREATE TABLE automation_schedules (...)`
18. `CREATE TABLE automation_runs (...)`
19. `CREATE UNIQUE INDEX uq_automation_runs_job_idempotency (...)`
20. `CREATE INDEX idx_automation_schedules_next_run_active (...)`
P4(安全与治理)
21. 对新增 `public` 表执行 `ALTER TABLE ... ENABLE ROW LEVEL SECURITY`
22.`anon, authenticated` 创建默认 deny-all `SELECT/INSERT/UPDATE/DELETE` policy
23. 审计字段回填脚本与触发器(如需)
## Dependencies
- [ ] PostgreSQL 扩展:`pgcrypto`UUID/哈希,若已启用可跳过)
- [ ] Alembic 迁移体系(现有)
- [ ] 后端任务执行器(Celery)用于自动化触发与补偿
## Testing Strategy
- **Unit Tests:** 状态流转、权限决策、去重哈希、幂等键生成
- **Integration Tests:** 迁移升级/回滚、双写一致性、RLS policy 基线、并发抢占执行
- **E2E Tests:** 好友请求到 inbox、群组邀请处理、事项订阅变更到待办、自动化任务触发到结果回显
## Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| 旧表结构未知导致回填失败 | High | Medium | 先做 schema 探测脚本,按表存在性分支回填 |
| 双写期间新旧数据不一致 | High | Medium | 引入操作日志 + 对账任务 + 幂等补偿 |
| 权限模型过复杂影响上线进度 | Medium | Medium | 先上线最小权限集(owner/user/group),后续扩展 |
| 自动化任务并发冲突 | Medium | Medium | `SKIP LOCKED` + 幂等键 + 乐观锁版本号 |
| 索引不足导致移动端列表慢 | Medium | Medium | 先覆盖 P0/P1 热路径索引,监控后增量优化 |
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Phase 1 | 8-12 hours |
| Phase 2 | 12-16 hours |
| Phase 3 | 8-12 hours |
| Phase 4 | 4-8 hours |
| **Total** | **32-48 hours** |
## 需业务确认(关键不确定项)
1. ~~`profiles.username` 是否允许重名~~(已确认:允许重名,仅建普通索引)。
2. ~~group_members.role 权柄组合~~(已确认:JSONB 数组 `["view", "invite", "edit"]`)。
3. 是否近期需要“组织/团队”多租户(决定 `tenant_id` 是否立即强制)。
4. 事项是否必须绑定群组上下文(`context_group_id` 是否必需)。
5. 待办与事项同步是否默认双向(推荐默认单向,降低循环风险)。
6. 自动化任务失败重试上限与退避策略(固定/指数)。