Skip to content

Supabase 设置指南

📋 概述

本指南将帮助你设置 Supabase 后端,用于托管词汇库数据(vocabulary_items)。

方案:简化方案(方案A)

  • 仅在 Supabase 托管 vocabulary_items 表
  • 本地使用缓存(vocabulary_cache)
  • 不实现多设备同步(未来可扩展)

🚀 步骤 1: 注册 Supabase 项目

1.1 创建账号

  1. 访问 https://supabase.com
  2. 点击 "Start your project" 或 "Sign up"
  3. 使用 GitHub 账号登录(推荐)或邮箱注册

1.2 创建项目

  1. 点击 "New Project"
  2. 填写项目信息:
    • Name: reading-vocab-helper(或自定义)
    • Database Password: 生成强密码并保存(后续需要)
    • Region: 选择 Northeast Asia (Seoul)Northeast Asia (Tokyo)(离中国最近)
    • Pricing Plan: 选择 Free(足够使用)
  3. 点击 "Create new project"
  4. 等待 1-2 分钟项目初始化完成

🗄️ 步骤 2: 创建数据库表

2.1 打开 SQL Editor

  1. 在 Supabase Dashboard 左侧菜单点击 "SQL Editor"
  2. 点击 "New query"

2.2 执行建表 SQL

复制以下 SQL 并执行:

sql
-- ============================================================================
-- Supabase 词汇库表结构(版本见 schema.md)
-- 项目:Reading Vocab Helper
-- 更新日期:2026-03-21
-- ============================================================================

-- 1. 创建 vocabulary_items 表(15 列)
CREATE TABLE IF NOT EXISTS vocabulary_items (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  word TEXT UNIQUE NOT NULL,
  ipa_pronunciation TEXT,
  pronunciation_url TEXT,
  primary_cefr_level TEXT,
  pos_definitions JSONB,
  frequency_rank INTEGER,
  source TEXT NOT NULL DEFAULT 'preinstalled',
  last_accessed_at TIMESTAMPTZ,
  etymology TEXT,
  word_forms JSONB,
  audio_local_path TEXT,
  word_family JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. 创建索引
CREATE INDEX IF NOT EXISTS idx_vocabulary_word_lower
  ON vocabulary_items(LOWER(word));
CREATE INDEX IF NOT EXISTS idx_vocabulary_primary_cefr
  ON vocabulary_items(primary_cefr_level);
CREATE INDEX IF NOT EXISTS idx_vocabulary_frequency
  ON vocabulary_items(frequency_rank);
CREATE INDEX IF NOT EXISTS idx_vocabulary_source
  ON vocabulary_items(source);
CREATE INDEX IF NOT EXISTS idx_vocabulary_last_accessed
  ON vocabulary_items(last_accessed_at);

-- 3. 添加注释
COMMENT ON TABLE vocabulary_items IS '词汇库表:存储CEFR词汇及其详细信息';
COMMENT ON COLUMN vocabulary_items.word IS '单词(唯一)';
COMMENT ON COLUMN vocabulary_items.ipa_pronunciation IS 'IPA国际音标';
COMMENT ON COLUMN vocabulary_items.primary_cefr_level IS '主CEFR等级(所有词性中最低)';
COMMENT ON COLUMN vocabulary_items.pos_definitions IS '多词性结构化数据(含 translations)';

-- 4. 创建更新时间触发器
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_vocabulary_items_updated_at
  BEFORE UPDATE ON vocabulary_items
  FOR EACH ROW
  EXECUTE FUNCTION update_updated_at_column();

-- 5. 完成提示
SELECT 'vocabulary_items 表创建完成!' AS status;

2.3 验证表结构

执行以下查询验证:

sql
-- 检查表是否创建成功
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
  AND table_name = 'vocabulary_items';

-- 检查索引是否创建成功
SELECT indexname
FROM pg_indexes
WHERE tablename = 'vocabulary_items';

-- 检查表结构
\d vocabulary_items

📤 步骤 3: 导入词汇数据

方式 1: 使用 Python 脚本导入(推荐)

项目已提供数据导入脚本:scripts/import_vocabulary_to_supabase.py

使用方法

bash
# 1. 安装依赖
pip install supabase pandas

# 2. 配置环境变量
# 创建 .env.supabase 文件(不要提交到 Git)
cat > .env.supabase <<EOF
SUPABASE_URL=https://your-project-id.supabase.co
SUPABASE_SERVICE_KEY=your-service-role-key
EOF

# 3. 运行导入脚本
python scripts/import_vocabulary_to_supabase.py

# 预计耗时:全量预装 18,916 词,约 5-10 分钟

方式 2: 手动 CSV 导入

  1. 导出数据为 CSV

    bash
    python scripts/export_vocabulary_to_csv.py
    # 生成 data/vocabulary_export.csv
  2. 在 Supabase 导入

    • 打开 Supabase Dashboard → Table Editor → vocabulary_items
    • 点击右上角 "Import data" → "CSV"
    • 选择 data/vocabulary_export.csv
    • 点击 "Import"

方式 3: 使用 SQL 批量插入

如果数据量较小(<1000词),可以直接在 SQL Editor 执行:

sql
-- 示例:批量插入(v34 结构)
INSERT INTO vocabulary_items (word, ipa_pronunciation, primary_cefr_level, pos_definitions, source)
VALUES
  ('hello', '/həˈloʊ/', 'A1', '{"interjection":[{"cefr":"A1","definitions":[{"gloss":"used as a greeting"}],"translations":{"zh":"你好"}}]}', 'preinstalled'),
  ('world', '/wɜːrld/', 'A1', '{"noun":[{"cefr":"A1","definitions":[{"gloss":"the earth and all the people"}],"translations":{"zh":"世界"}}]}', 'preinstalled'),
  -- ... 更多数据
ON CONFLICT (word) DO NOTHING;

🔑 步骤 4: 获取 API 凭证

4.1 获取 Project URL 和 API Keys

  1. 在 Supabase Dashboard 点击左下角齿轮图标(Settings)
  2. 点击 "API"
  3. 找到以下信息:
    • Project URL: https://xxxxx.supabase.co
    • Project API keys:
      • anon public key(客户端使用)
      • service_role secret key(仅服务端使用,不要暴露)

4.2 配置到 Flutter 项目

创建 .env 文件(不要提交到 Git):

bash
# 在项目根目录创建 .env
cat > .env <<EOF
SUPABASE_URL=https://your-project-id.supabase.co
SUPABASE_ANON_KEY=your-anon-key-here
EOF

.gitignore 中添加:

.env
.env.supabase

✅ 步骤 5: 验证设置

5.1 测试 API 查询

在 Supabase SQL Editor 执行:

sql
-- 1. 检查数据量
SELECT COUNT(*) AS total_words FROM vocabulary_items;

-- 2. 查看 CEFR 分布
SELECT primary_cefr_level, COUNT(*) AS count
FROM vocabulary_items
GROUP BY primary_cefr_level
ORDER BY primary_cefr_level;

-- 3. 测试查询单词
SELECT * FROM vocabulary_items WHERE word = 'hello';

-- 4. 测试模糊搜索
SELECT word, ipa_pronunciation, primary_cefr_level
FROM vocabulary_items
WHERE word ILIKE 'hello%'
LIMIT 5;

5.2 测试 REST API

在浏览器或 Postman 测试:

bash
# 替换 <your-project-url> 和 <your-anon-key>
curl -X GET \
  'https://<your-project-url>.supabase.co/rest/v1/vocabulary_items?word=eq.hello' \
  -H 'apikey: <your-anon-key>' \
  -H 'Authorization: Bearer <your-anon-key>'

预期返回:

json
[
  {
    "id": "...",
    "word": "hello",
    "ipa_pronunciation": "/həˈloʊ/",
    "primary_cefr_level": "A1",
    "pos_definitions": {"interjection": [{"cefr": "A1", "definitions": [{"gloss": "used as a greeting"}]}]},
    "source": "preinstalled",
    "created_at": "2026-03-21T..."
  }
]

🎯 步骤 6: 性能优化(可选)

6.1 启用 Row Level Security(RLS)

注意:对于只读公共数据,可以暂时不启用 RLS。

sql
-- 启用 RLS
ALTER TABLE vocabulary_items ENABLE ROW LEVEL SECURITY;

-- 创建公开读取策略
CREATE POLICY "Allow public read access"
  ON vocabulary_items
  FOR SELECT
  USING (true);

6.2 配置缓存

Supabase 默认启用 CDN 缓存,无需额外配置。


📊 预期结果

完成以上步骤后,你应该:

  • ✅ Supabase 项目已创建并运行
  • ✅ vocabulary_items 表已创建(包含 3 个索引)
  • ✅ 词汇数据已导入(18,916 词)
  • ✅ API 可正常查询
  • ✅ 客户端凭证已配置

🔧 常见问题

Q1: 导入数据时报错 "conflict"?

原因:单词重复(word 字段有 UNIQUE 约束)

解决:使用 ON CONFLICT (word) DO NOTHINGDO UPDATE

Q2: API 查询返回空?

检查

  1. 数据是否已导入(SELECT COUNT(*))
  2. API Key 是否正确
  3. URL 是否正确(注意 /rest/v1/ 路径)

Q3: 查询性能慢?

优化

  1. 确认索引已创建(idx_vocabulary_word_lower
  2. 使用 ILIKE 搜索时添加 LIMIT
  3. 考虑启用全文搜索(FTS)

Q4: 免费额度不够用?

当前用量估算(1000 活跃用户):

  • 存储:15MB(<<500MB免费)
  • 请求:30,000-60,000次/月(缓存后,<50,000免费)
  • 带宽:20-40MB/月(<<2GB免费)

结论:完全够用,无需付费


📚 参考资料


下一步:完成设置后,运行 flutter pub get 并重启应用,客户端将自动连接到 Supabase。