Supabase数据库技能 supabase

本技能提供对Empathy Ledger Supabase数据库的深入理解和查询能力,包括数据库表关系、数据查询模式、外键约束等,适用于需要从Supabase数据库获取数据、调试数据访问问题或实施新功能的场景。

后端开发 0 次安装 0 次浏览 更新于 3/2/2026

Supabase 数据库技能

自信地导航和查询 Empathy Ledger Supabase 数据库。

数据库关系图

┌─────────────────────────────────────────────────────────────────────────────┐
│                              TENANTS (顶级)                            │
│                                    │                                        │
│    ┌───────────────────────────────┼───────────────────────────────┐        │
│    │                               │                               │        │
│    ▼                               ▼                               ▼        │
│ ┌──────────────┐           ┌──────────────┐           ┌──────────────────┐  │
│ │ organisations │◄──────────│   profiles   │──────────►│  tenant_members  │  │
│ └──────────────┘           └──────────────┘           └──────────────────┘  │
│        │                          │                                         │
│        │                          │ is_storyteller                          │
│        ▼                          ▼                                         │
│ ┌──────────────┐           ┌──────────────┐                                 │
│ │   projects   │◄──────────│    stories   │                                 │
│ └──────────────┘           └──────────────┘                                 │
│        │                          │                                         │
│        │                          ├────────────────────┐                    │
│        ▼                          ▼                    ▼                    │
│ ┌──────────────┐           ┌──────────────┐    ┌──────────────┐             │
│ │ transcripts  │           │media_assets  │    │story_distribs│             │
│ └──────────────┘           └──────────────┘    └──────────────┘             │
│        │                          │                    │                    │
│        │                          │                    ▼                    │
│        ▼                          ▼             ┌──────────────┐            │
│ ┌──────────────┐           ┌──────────────┐    │ embed_tokens │            │
│ │ key_quotes[] │           │media_usage   │    └──────────────┘            │
│ │ themes[]     │           │_tracking     │                                 │
│ │ ai_summary   │           └──────────────┘                                 │
│ └──────────────┘                                                            │
└─────────────────────────────────────────────────────────────────────────────┘

完整表清单

Live Supabase: 165 个对象 (153 个表, 7 个视图, 3 个分区, 2 个系统) 迁移定义的: 71 个表 带 TypeScript 类型的: 35 个表

另见: DATABASE_ALIGNMENT_AUDIT.md

⚠️ 架构漂移警告: ~80 个表存在于 Supabase 但没有迁移文件。 使用 npx supabase gen types typescript --local 生成准确的类型。

1. 身份与访问 (12 个表)

用途 有类型
tenants 顶级多租户隔离
profiles 用户账户 (与 auth.users 同步)
organisations 社区团体与等级/政策
organization_members 用户 ↔ 组织成员关系
organization_roles 组织内 RBAC 角色 ⚠️
organization_invitations 待处理邀请 ⚠️
tenant_members 用户 ↔ 租户成员关系
profile_organizations 个人资料-组织联接
profile_locations 用户位置
profile_projects 用户-项目联接
user_sessions 会话跟踪
user_reports 用户报告

2. 项目与上下文 (9 个表)

用途 有类型
projects 故事集合
project_participants 项目成员
project_contexts AI 提取的项目上下文 ⚠️
organization_contexts AI 提取的组织上下文 ⚠️
project_profiles 扩展项目元数据 ⚠️
project_seed_interviews 种子访谈数据 ⚠️
project_analyses 缓存的 AI 分析 ⚠️
seed_interview_templates 访谈模板 ⚠️
development_plans 用户发展计划

3. 故事与内容 (10 个表)

用途 有类型
stories 核心故事内容
transcripts 音频/文本转录
media_assets 图像、视频、音频
media_usage_tracking 媒体访问跟踪
extracted_quotes AI 提取的引用
transcription_jobs 转录队列 ⚠️
media_import_sessions 批量导入跟踪 ⚠️
title_suggestions AI 标题建议 ⚠️
galleries 照片画廊
gallery_photos 画廊项目

4. 分发与联合 (11 个表)

用途 有类型
story_distributions 外部平台跟踪
story_access_tokens 临时分享链接 (可撤销,限时)
embed_tokens 安全嵌入令牌
story_syndication_consent 合作伙伴同意记录 ⚠️
external_applications 合作伙伴应用注册 ⚠️
story_access_log 外部访问日志 ⚠️
webhook_subscriptions 合作伙伴 webhooks ⚠️
webhook_delivery_log Webhook 尝试 ⚠️
consent_change_log 同意审计跟踪 ⚠️
consent_proofs GDPR 同意证明 ⚠️
story_review_invitations 故事讲述者审核链接 ⚠️

5. 合作伙伴门户 (6 个表)

用途 有类型
partner_projects 合作伙伴策划的项目 ⚠️
story_syndication_requests 内容请求 ⚠️
partner_messages 合作伙伴-故事讲述者消息 ⚠️
partner_team_members 合作伙伴团队访问 ⚠️
partner_analytics_daily 合作伙伴分析 ⚠️
partner_message_templates 消息模板 ⚠️

6. 分析与洞察 (17 个表)

用途 有类型
storyteller_analytics 聚合的故事讲述者统计 ⚠️
narrative_themes 平台范围的主题 ⚠️
storyteller_themes 每个故事讲述者的主题 ⚠️
storyteller_quotes 有影响力的引用 ⚠️
storyteller_connections 网络连接 ⚠️
storyteller_demographics 人口统计数据 ⚠️
storyteller_recommendations AI 推荐 ❌ ORPHANED
storyteller_dashboard_config 仪表板偏好设置 ⚠️
storyteller_milestones 成就 ⚠️
storyteller_engagement 参与度指标 ⚠️
storyteller_impact_metrics 影响跟踪 ⚠️
cross_narrative_insights 跨故事洞察 ❌ ORPHANED
cross_sector_insights 行业分析 ⚠️
geographic_impact_patterns 地理模式 ❌ ORPHANED
theme_evolution_tracking 主题趋势 ⚠️
analytics_processing_jobs 分析作业队列 ❌ ORPHANED
platform_analytics 平台范围的统计 ⚠️

7. 参与度跟踪 (2 个表)

用途 有类型
story_engagement_events 每次查看事件 ⚠️
story_engagement_daily 每日汇总 ⚠️

8. AI & 安全 (9 个表)

用途 有类型
ai_usage_events AI 成本/使用跟踪 ⚠️
tenant_ai_policies 每个租户的 AI 限制 ⚠️
ai_agent_registry AI 代理配置 ⚠️
ai_usage_daily 每日 AI 汇总 ⚠️
elder_review_queue 长者审核工作流 ⚠️
moderation_results 审核决策 ⚠️
moderation_appeals 上诉请求 ⚠️
ai_moderation_logs AI 审核日志 ⚠️
ai_safety_logs 安全检查日志 ⚠️

9. 管理 & 系统 (8 个表)

用途 有类型
audit_logs 合规审计跟踪
deletion_requests GDPR 删除队列
activity_log 管理活动反馈 ⚠️
notifications 应用内通知 ⚠️
admin_messages 管理广播 ⚠️
message_recipients 消息传递 ⚠️
ai_analysis_jobs AI 作业队列 ⚠️
platform_stats_cache 缓存的平台统计 ⚠️

10. 世界巡回 (3 个表)

用途 有类型
tour_requests 巡回访问请求 ⚠️
tour_stops 已完成的巡回站点 ⚠️
dream_organizations 目标组织 ⚠️

11. 文化与影响 (5 个表)

用途 有类型
cultural_protocols 文化指南
cultural_tags 文化标签
community_impact_insights 影响时刻
community_impact_metrics 聚合的影响
live_community_narratives 自动生成的叙述
locations 地理位置
events 事件跟踪

12. 附加表 (在 Supabase 中,没有迁移)

这些表存在于 Supabase 但没有任何迁移文件:

用途 有类型
activities 活动跟踪 (52 列!)
outcomes 结果跟踪 (38 列)
annual_reports 年度报告
annual_report_stories 报告-故事链接
report_sections 报告部分
report_templates 报告模板
blog_posts 博客内容
testimonials 用户推荐
services 服务定义
service_impact 服务影响指标
partners 合作伙伴组织
team_members 团队成员档案

13. 照片系统 (仅在 Supabase 中)

用途 有类型
photo_analytics 照片查看跟踪
photo_faces 面部识别数据
photo_galleries 照片画廊
photo_gallery_items 画廊项目
photo_locations 照片位置
photo_memories 照片记忆
photo_organizations 照片组织链接
photo_projects 照片项目链接
photo_storytellers 照片故事讲述者链接
photo_tags 照片标签

14. 遗留/同步表

用途 有类型
empathy_entries 遗留的同理心数据
empathy_sync_log 同步跟踪
syndicated_stories 联合内容
scraped_services 网络刮削数据
scraper_health_metrics 刮削器健康
scraping_metadata 刮削元数据

⚠️ 拼写注释

Supabase 使用 organizations (美国拼写) TypeScript 类型使用 organisations (英国拼写)

查询时使用 Supabase 的拼写。类型可能需要更新。

外键关系

故事表 (中心枢纽)

stories.storyteller_id  → profiles.id       // 谁讲述了这个故事
stories.author_id       → profiles.id       // 谁创作/记录了
stories.project_id      → projects.id       // 属于哪个项目
stories.organization_id → organisations.id  // 哪个组织拥有
stories.tenant_id       → tenants.id        // 租户隔离
stories.featured_media_id → media_assets.id // 封面图片

转录表

transcripts.storyteller_id → profiles.id   // 谁在说话
transcripts.tenant_id      → tenants.id    // 租户隔离
// 注意: 故事可以通过内容或 transcript_id 链接到转录

组织层级

tenants.organization_id        → organisations.id  // 租户的主要组织
organisations.tenant_id        → tenants.id        // 租户所有权
organization_members.profile_id     → profiles.id      // 用户
organization_members.organization_id → organisations.id // 组织

分发链

story_distributions.story_id     → stories.id  // 哪个故事
story_distributions.tenant_id    → tenants.id  // 租户隔离
embed_tokens.story_id            → stories.id  // 哪个故事
embed_tokens.distribution_id     → story_distributions.id  // 父级分发
story_access_tokens.story_id     → stories.id  // 哪个故事 (临时分享链接)
story_access_tokens.created_by   → profiles.id // 谁创建了链接
story_access_tokens.tenant_id    → tenants.id  // 租户隔离

按领域分类的类型文件

领域 类型文件 覆盖的表
用户 src/types/database/user-profile.ts profiles, profile_locations, profile_organizations, user_sessions
组织 src/types/database/organization-tenant.ts organisations, organization_members, tenants, tenant_members
项目 src/types/database/project-management.ts projects, project_participants
内容 src/types/database/content-media.ts stories, transcripts, media_assets, extracted_quotes
分发 src/types/database/story-ownership.ts story_distributions, embed_tokens, audit_logs, deletion_requests
共享控制 src/types/database/story-access-tokens.ts story_access_tokens
文化 src/types/database/cultural-sensitivity.ts cultural_safety_moderation
位置 src/types/database/location-events.ts locations, events
分析 src/types/database/analysis-support.ts transcript_analysis, themes, quotes

Supabase 客户端使用

客户端类型

// 浏览器客户端 (使用 cookie, 遵守 RLS)
import { createSupabaseBrowserClient } from '@/lib/supabase/client'
const supabase = createSupabaseBrowserClient()

// 服务器 SSR 客户端 (用于 API 路由, 服务器组件)
import { createSupabaseServerClient } from '@/lib/supabase/client-ssr'
const supabase = createSupabaseServerClient()

// 服务角色客户端 (绕过 RLS - 仅限管理员!)
import { createSupabaseServiceClient } from '@/lib/supabase/service-role-client'
const supabase = createSupabaseServiceClient()

何时使用每个客户端

客户端 用例 RLS 认证
浏览器 React 组件 用户会话
服务器 SSR API 路由, 服务器组件 用户会话
服务角色 管理操作, 后台作业 服务密钥

常见查询模式

获取带有故事讲述者的故事

const { data } = await supabase
  .from('stories')
  .select(`
    *,
    storyteller:profiles!stories_storyteller_id_fkey(
      id, display_name, profile_image_url
    )
  `)
  .eq('status', 'published')
  .eq('tenant_id', tenantId)

获取带有主题的转录

const { data } = await supabase
  .from('transcripts')
  .select('id, title, themes, key_quotes, ai_summary')
  .not('themes', 'is', null)
  .order('created_at', { ascending: false })

获取带有成员的组织

const { data } = await supabase
  .from('organisations')
  .select(`
    *,
    members:organization_members(
      profile:profiles(id, display_name, profile_image_url),
      role
    )
  `)
  .eq('id', orgId)
  .single()

获取所有关系的故事

const { data } = await supabase
  .from('stories')
  .select(`
    *,
    storyteller:profiles!stories_storyteller_id_fkey(*),
    project:projects(*),
    organization:organisations(*),
    distributions:story_distributions(*),
    featured_media:media_assets(*)
  `)
  .eq('id', storyId)
  .single()

基于主题的故事搜索 (数组重叠)

// 与任何匹配主题的故事
const { data } = await supabase
  .from('stories')
  .select('*')
  .overlaps('ai_themes', ['identity', 'heritage'])

// 与所有主题匹配的故事
const { data } = await supabase
  .from('stories')
  .select('*')
  .contains('ai_themes', ['identity', 'heritage'])

按状态计数

const { count } = await supabase
  .from('stories')
  .select('*', { count: 'exact', head: true })
  .eq('status', 'published')
  .eq('tenant_id', tenantId)

故事访问令牌 (共享控制)

验证令牌并获取故事

// 使用数据库函数进行验证 + 查看计数增加
const { data: validation } = await supabase.rpc('validate_and_increment_token', {
  p_token: 'abc123xyz'
})

if (validation[0]?.is_valid) {
  const { data: story } = await supabase
    .from('stories')
    .select('*, storyteller:profiles(*)')
    .eq('id', validation[0].story_id)
    .single()
}

获取故事的活跃共享链接

const { data: tokens } = await supabase
  .from('story_access_tokens')
  .select('*')
  .eq('story_id', storyId)
  .eq('revoked', false)
  .gt('expires_at', new Date().toISOString())
  .order('created_at', { ascending: false })

创建共享链接

import { nanoid } from 'nanoid'

const token = nanoid(21)
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 天

const { data } = await supabase
  .from('story_access_tokens')
  .insert({
    story_id: storyId,
    token,
    expires_at: expiresAt.toISOString(),
    purpose: 'social-media',
    created_by: userId,
    tenant_id: tenantId
  })
  .select()
  .single()

const shareUrl = `https://empathy-ledger.org/s/${token}`

撤销共享链接

const { error } = await supabase
  .from('story_access_tokens')
  .update({ revoked: true })
  .eq('id', tokenId)
  .eq('story_id', storyId) // 确保用户拥有故事

获取共享分析

// 查看计数, 最多共享的故事
const { data: analytics } = await supabase
  .from('story_access_tokens')
  .select('story_id, view_count, purpose, shared_to')
  .eq('story_id', storyId)
  .order('view_count', { ascending: false })

多租户查询模式

重要: 所有查询始终通过 tenant_id 进行数据隔离。

// 标准查询模式
async function getStories(userId: string) {
  const supabase = createSupabaseServerClient()

  // 1. 获取用户的租户
  const { data: profile } = await supabase
    .from('profiles')
    .select('tenant_id')
    .eq('id', userId)
    .single()

  // 2. 使用租户过滤器查询
  const { data } = await supabase
    .from('stories')
    .select('*')
    .eq('tenant_id', profile.tenant_id)  // 始终包含!
    .eq('status', 'published')

  return data
}

数据库函数

可用的 RPC 函数:

// 计算租户分析
const { data } = await supabase.rpc('calculate_tenant_analytics', {
  tenant_uuid: tenantId
})

// 获取组织统计
const { data } = await supabase.rpc('get_organization_stats', {
  org_id: orgId
})

// 全文搜索引用
const { data } = await supabase.rpc('search_quotes', {
  query: 'wisdom ancestors'
})

// 搜索媒体
const { data } = await supabase.rpc('search_media', {
  query: 'interview video'
})

迁移位置

所有数据库架构在: supabase/migrations/

关键迁移:

  • 20251220093000_multi_org_tenants.sql - 多组织租户结构
  • 20251207_story_ownership_distribution.sql - 分发系统
  • 20251209000000_cultural_safety_moderation_tables.sql - 文化安全
  • 20251210000000_partner_portal_system.sql - 合作伙伴分发

何时使用这个技能

调用时:

  • 需要了解表关系
  • 编写 Supabase 查询
  • 查找正确的类型定义
  • 理解外键约束
  • 调试数据访问问题
  • 实施涉及数据库的新功能

MCP 访问

这个项目配置了 MCP 用于直接访问 Supabase:

只读 (默认):

https://mcp.supabase.com/mcp?project_ref=yvnuayzslukamizrlhwb&read_only=true

带写入访问:

https://mcp.supabase.com/mcp?project_ref=yvnuayzslukamizrlhwb&features=database,docs,debugging,development,functions,branching

可用的 MCP 工具:

  • list_tables - 查看所有表和列
  • execute_sql - 运行 SQL 查询
  • list_migrations - 查看迁移历史
  • generate_typescript_types - 从架构生成类型
  • get_logs - 查看应用日志

参看: SUPABASE_ACCESS_GUIDE.md


触发器: 用户询问数据库表、关系、查询或 “如何从 Supabase 获取 X”