主题
Supabase 设置指南
📋 概述
本指南将帮助你设置 Supabase 后端,用于托管词汇库数据(vocabulary_items)。
方案:简化方案(方案A)
- 仅在 Supabase 托管 vocabulary_items 表
- 本地使用缓存(vocabulary_cache)
- 不实现多设备同步(未来可扩展)
🚀 步骤 1: 注册 Supabase 项目
1.1 创建账号
- 访问 https://supabase.com
- 点击 "Start your project" 或 "Sign up"
- 使用 GitHub 账号登录(推荐)或邮箱注册
1.2 创建项目
- 点击 "New Project"
- 填写项目信息:
- Name:
reading-vocab-helper(或自定义) - Database Password: 生成强密码并保存(后续需要)
- Region: 选择
Northeast Asia (Seoul)或Northeast Asia (Tokyo)(离中国最近) - Pricing Plan: 选择
Free(足够使用)
- Name:
- 点击 "Create new project"
- 等待 1-2 分钟项目初始化完成
🗄️ 步骤 2: 创建数据库表
2.1 打开 SQL Editor
- 在 Supabase Dashboard 左侧菜单点击 "SQL Editor"
- 点击 "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 导入
导出数据为 CSV:
bashpython scripts/export_vocabulary_to_csv.py # 生成 data/vocabulary_export.csv在 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
- 在 Supabase Dashboard 点击左下角齿轮图标(Settings)
- 点击 "API"
- 找到以下信息:
- Project URL:
https://xxxxx.supabase.co - Project API keys:
anonpublickey(客户端使用)service_rolesecretkey(仅服务端使用,不要暴露)
- Project URL:
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 NOTHING 或 DO UPDATE
Q2: API 查询返回空?
检查:
- 数据是否已导入(SELECT COUNT(*))
- API Key 是否正确
- URL 是否正确(注意
/rest/v1/路径)
Q3: 查询性能慢?
优化:
- 确认索引已创建(
idx_vocabulary_word_lower) - 使用
ILIKE搜索时添加LIMIT - 考虑启用全文搜索(FTS)
Q4: 免费额度不够用?
当前用量估算(1000 活跃用户):
- 存储:15MB(<<500MB免费)
- 请求:30,000-60,000次/月(缓存后,<50,000免费)
- 带宽:20-40MB/月(<<2GB免费)
结论:完全够用,无需付费
📚 参考资料
下一步:完成设置后,运行 flutter pub get 并重启应用,客户端将自动连接到 Supabase。