Merge branch 'refactor/backend-architecture' into development
This commit is contained in:
9
api/migrations_legacy/README.md
Normal file
9
api/migrations_legacy/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 历史迁移(已归档)
|
||||
|
||||
> **注意**:本目录下的 SQL 文件为架构重构前的历史迁移,**不再使用**。
|
||||
> 当前数据库 schema 由 **Alembic** 管理,请使用 `alembic upgrade head` 进行迁移。
|
||||
|
||||
## 说明
|
||||
|
||||
- 本目录保留仅作历史参考,部署时请勿执行这些 SQL。
|
||||
- 新部署或 schema 变更请使用:`uv run alembic upgrade head`
|
||||
8
api/migrations_legacy/add_chapter_is_active.sql
Normal file
8
api/migrations_legacy/add_chapter_is_active.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- 为 chapters 表添加 is_active 字段
|
||||
-- 用于支持"清除回忆"功能:将章节标记为 disabled 而非物理删除
|
||||
-- 默认值为 TRUE,现有章节全部为 active
|
||||
|
||||
ALTER TABLE chapters ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT TRUE;
|
||||
|
||||
-- 确保现有数据全部为 active
|
||||
UPDATE chapters SET is_active = TRUE WHERE is_active IS NULL;
|
||||
42
api/migrations_legacy/add_chapter_sections.sql
Normal file
42
api/migrations_legacy/add_chapter_sections.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- 章节拆分为 chapter_sections:每段正文 + 配图独立存储,chapters 只保留封面图
|
||||
-- 执行顺序: 1) 本文件 2) python -m scripts.migrate_chapters_to_sections
|
||||
-- 执行方式: psql -U <user> -d <database> -f api/migrations/add_chapter_sections.sql
|
||||
|
||||
-- ========== 1. 新建 chapter_sections 表 ==========
|
||||
CREATE TABLE IF NOT EXISTS chapter_sections (
|
||||
id VARCHAR NOT NULL PRIMARY KEY,
|
||||
chapter_id VARCHAR NOT NULL REFERENCES chapters(id) ON DELETE CASCADE,
|
||||
order_index INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
image JSONB,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_chapter_sections_chapter_id ON chapter_sections(chapter_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_chapter_sections_order ON chapter_sections(chapter_id, order_index);
|
||||
|
||||
-- 若 chapter_sections 已存在且已提前切换到 image_id 结构,重跑迁移时需要临时补回 image 列,
|
||||
-- 以便后续 Python 脚本先完成 JSON -> memoir_images 的数据搬迁。
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'chapter_sections'
|
||||
AND column_name = 'image'
|
||||
) THEN
|
||||
ALTER TABLE chapter_sections ADD COLUMN image JSONB;
|
||||
RAISE NOTICE '已补回 chapter_sections.image,供数据迁移使用';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ========== 2. chapters 表增加 cover_image ==========
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'chapters' AND column_name = 'cover_image') THEN
|
||||
ALTER TABLE chapters ADD COLUMN cover_image JSONB;
|
||||
RAISE NOTICE '已添加 chapters.cover_image';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ========== 3. 回填与删列由脚本 scripts.migrate_chapters_to_sections 完成 ==========
|
||||
19
api/migrations_legacy/add_device_info_column.sql
Normal file
19
api/migrations_legacy/add_device_info_column.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- 添加 refresh_tokens.device_info 列的迁移脚本
|
||||
-- 执行方式: psql -U postgres -d life_echo -f migrations/add_device_info_column.sql
|
||||
-- 或者在 psql 中执行: \i migrations/add_device_info_column.sql
|
||||
|
||||
-- 检查列是否存在,如果不存在则添加
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'refresh_tokens'
|
||||
AND column_name = 'device_info'
|
||||
) THEN
|
||||
ALTER TABLE refresh_tokens ADD COLUMN device_info VARCHAR;
|
||||
RAISE NOTICE '已添加 refresh_tokens.device_info 列';
|
||||
ELSE
|
||||
RAISE NOTICE 'refresh_tokens.device_info 列已存在,跳过';
|
||||
END IF;
|
||||
END $$;
|
||||
32
api/migrations_legacy/add_memoir_images_table.sql
Normal file
32
api/migrations_legacy/add_memoir_images_table.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- 图片独立表:将原先挤在 chapter.cover_image / chapter_sections.image 的 JSON 拆成独立列
|
||||
-- 执行顺序: 1) 本文件 2) python -m api.scripts.run_memoir_images_migration
|
||||
-- 执行方式: psql -U <user> -d <database> -f api/migrations/add_memoir_images_table.sql
|
||||
|
||||
-- ========== 1. 新建 memoir_images 表 ==========
|
||||
CREATE TABLE IF NOT EXISTS memoir_images (
|
||||
id VARCHAR NOT NULL PRIMARY KEY,
|
||||
chapter_id VARCHAR NOT NULL REFERENCES chapters(id) ON DELETE CASCADE,
|
||||
section_id VARCHAR NULL REFERENCES chapter_sections(id) ON DELETE CASCADE,
|
||||
order_index INTEGER NOT NULL DEFAULT 0,
|
||||
placeholder TEXT,
|
||||
description TEXT,
|
||||
status VARCHAR NOT NULL DEFAULT 'pending',
|
||||
prompt TEXT,
|
||||
url TEXT,
|
||||
storage_key TEXT,
|
||||
provider VARCHAR,
|
||||
style VARCHAR,
|
||||
size VARCHAR,
|
||||
error TEXT,
|
||||
retryable BOOLEAN,
|
||||
created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE memoir_images IS '章节配图与封面:section_id 为空表示章节封面,非空表示该 section 的配图';
|
||||
COMMENT ON COLUMN memoir_images.order_index IS '章节内唯一:封面=0,段落配图=1,2,3,...(对应 section.order_index+1)';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_memoir_images_chapter_id ON memoir_images(chapter_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_memoir_images_section_id ON memoir_images(section_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ix_memoir_images_chapter_cover ON memoir_images(chapter_id) WHERE section_id IS NULL;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ix_memoir_images_section_unique ON memoir_images(section_id) WHERE section_id IS NOT NULL;
|
||||
26
api/migrations_legacy/add_orders_table.sql
Normal file
26
api/migrations_legacy/add_orders_table.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- 添加订单表和用户订阅到期时间字段
|
||||
-- 执行时间: 2026-02
|
||||
|
||||
-- 1. 为 users 表添加 subscription_expires_at 字段
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS subscription_expires_at TIMESTAMP WITH TIME ZONE DEFAULT NULL;
|
||||
|
||||
-- 2. 创建 orders 表
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id VARCHAR NOT NULL PRIMARY KEY,
|
||||
user_id VARCHAR NOT NULL REFERENCES users(id),
|
||||
plan_id VARCHAR NOT NULL,
|
||||
plan_name VARCHAR NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
currency VARCHAR DEFAULT 'CNY',
|
||||
payment_method VARCHAR NOT NULL,
|
||||
status VARCHAR DEFAULT 'pending',
|
||||
trade_no VARCHAR,
|
||||
paid_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
expired_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
-- 3. 创建索引
|
||||
CREATE INDEX IF NOT EXISTS ix_orders_user_id ON orders(user_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_orders_trade_no ON orders(trade_no);
|
||||
CREATE INDEX IF NOT EXISTS ix_orders_status ON orders(status);
|
||||
18
api/migrations_legacy/add_section_image_id_fk.sql
Normal file
18
api/migrations_legacy/add_section_image_id_fk.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- section 表用 image_id 关联 memoir_images,不再存 JSON
|
||||
-- 执行顺序:1) 本文件 2) 回填后执行 DROP 语句(或由脚本完成)
|
||||
-- 执行方式: psql -U <user> -d <database> -f api/migrations/add_section_image_id_fk.sql
|
||||
|
||||
-- 1. 添加外键列(可空,无默认)
|
||||
ALTER TABLE chapter_sections
|
||||
ADD COLUMN IF NOT EXISTS image_id VARCHAR REFERENCES memoir_images(id) ON DELETE SET NULL;
|
||||
|
||||
-- 2. 回填:已有 memoir_images 且 section_id 指向本行的,把其 id 写入本行 image_id
|
||||
UPDATE chapter_sections cs
|
||||
SET image_id = sub.id
|
||||
FROM (
|
||||
SELECT id, section_id FROM memoir_images WHERE section_id IS NOT NULL
|
||||
) sub
|
||||
WHERE sub.section_id = cs.id AND cs.image_id IS NULL;
|
||||
|
||||
-- 3. 删除旧的 JSON 列
|
||||
ALTER TABLE chapter_sections DROP COLUMN IF EXISTS image;
|
||||
49
api/migrations_legacy/add_sms_verification.sql
Normal file
49
api/migrations_legacy/add_sms_verification.sql
Normal file
@@ -0,0 +1,49 @@
|
||||
-- 短信验证码功能数据库迁移脚本
|
||||
-- 执行方式: psql -U postgres -d life_echo -f migrations/add_sms_verification.sql
|
||||
|
||||
-- 1. 创建短信验证码表
|
||||
CREATE TABLE IF NOT EXISTS sms_verification_codes (
|
||||
id VARCHAR PRIMARY KEY,
|
||||
phone VARCHAR NOT NULL,
|
||||
code VARCHAR NOT NULL,
|
||||
purpose VARCHAR NOT NULL,
|
||||
is_used BOOLEAN DEFAULT FALSE,
|
||||
is_expired BOOLEAN DEFAULT FALSE,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
verified_at TIMESTAMP WITH TIME ZONE,
|
||||
ip_address VARCHAR
|
||||
);
|
||||
|
||||
-- 2. 创建索引以提高查询性能
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_phone ON sms_verification_codes(phone);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_created_at ON sms_verification_codes(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_purpose ON sms_verification_codes(purpose);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_phone_purpose ON sms_verification_codes(phone, purpose);
|
||||
|
||||
-- 3. 扩展 refresh_tokens 表,添加设备信息字段
|
||||
ALTER TABLE refresh_tokens ADD COLUMN IF NOT EXISTS device_info VARCHAR;
|
||||
|
||||
-- 4. 添加注释
|
||||
COMMENT ON TABLE sms_verification_codes IS '短信验证码表';
|
||||
COMMENT ON COLUMN sms_verification_codes.id IS '主键ID';
|
||||
COMMENT ON COLUMN sms_verification_codes.phone IS '手机号';
|
||||
COMMENT ON COLUMN sms_verification_codes.code IS '6位验证码';
|
||||
COMMENT ON COLUMN sms_verification_codes.purpose IS '用途:register/login/reset_password/change_phone';
|
||||
COMMENT ON COLUMN sms_verification_codes.is_used IS '是否已使用';
|
||||
COMMENT ON COLUMN sms_verification_codes.is_expired IS '是否已过期';
|
||||
COMMENT ON COLUMN sms_verification_codes.expires_at IS '过期时间(5分钟后)';
|
||||
COMMENT ON COLUMN sms_verification_codes.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN sms_verification_codes.verified_at IS '验证时间';
|
||||
COMMENT ON COLUMN sms_verification_codes.ip_address IS '请求IP地址';
|
||||
|
||||
COMMENT ON COLUMN refresh_tokens.device_info IS '设备信息(用于全设备登出)';
|
||||
|
||||
-- 5. 显示迁移完成信息
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '短信验证码功能迁移完成!';
|
||||
RAISE NOTICE '- 已创建 sms_verification_codes 表';
|
||||
RAISE NOTICE '- 已创建相关索引';
|
||||
RAISE NOTICE '- 已扩展 refresh_tokens 表';
|
||||
END $$;
|
||||
7
api/migrations_legacy/add_user_profile_fields.sql
Normal file
7
api/migrations_legacy/add_user_profile_fields.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- 添加用户基础资料字段(出生年份、出生地、成长地、职业)
|
||||
SET lock_timeout = '5s';
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS birth_year INTEGER;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS birth_place VARCHAR;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS grew_up_place VARCHAR;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS occupation VARCHAR;
|
||||
RESET lock_timeout;
|
||||
26
api/migrations_legacy/add_users_subscription_columns.sql
Normal file
26
api/migrations_legacy/add_users_subscription_columns.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- 为 users 表添加订阅相关列(subscription_type, subscription_expires_at)
|
||||
-- 若列已存在则跳过,可重复执行。
|
||||
-- 执行方式: psql -U <user> -d <database> -f api/migrations/add_users_subscription_columns.sql
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'users' AND column_name = 'subscription_type'
|
||||
) THEN
|
||||
ALTER TABLE users ADD COLUMN subscription_type VARCHAR DEFAULT 'free';
|
||||
RAISE NOTICE '已添加 users.subscription_type 列';
|
||||
ELSE
|
||||
RAISE NOTICE 'users.subscription_type 列已存在,跳过';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'users' AND column_name = 'subscription_expires_at'
|
||||
) THEN
|
||||
ALTER TABLE users ADD COLUMN subscription_expires_at TIMESTAMP WITH TIME ZONE DEFAULT NULL;
|
||||
RAISE NOTICE '已添加 users.subscription_expires_at 列';
|
||||
ELSE
|
||||
RAISE NOTICE 'users.subscription_expires_at 列已存在,跳过';
|
||||
END IF;
|
||||
END $$;
|
||||
15
api/migrations_legacy/fix_chapter_order_index.sql
Normal file
15
api/migrations_legacy/fix_chapter_order_index.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- 修复章节 order_index 为 999 的问题
|
||||
-- 原因:STAGE_KEYWORDS 使用简化阶段名(career, belief),
|
||||
-- 但 CHAPTER_ORDER 使用详细分类名(career_early, beliefs),导致查找失败回退到 999
|
||||
|
||||
-- 根据 category 字段修复 order_index
|
||||
UPDATE chapters SET order_index = 0 WHERE order_index = 999 AND category = 'childhood';
|
||||
UPDATE chapters SET order_index = 1 WHERE order_index = 999 AND category = 'education';
|
||||
UPDATE chapters SET order_index = 2 WHERE order_index = 999 AND category = 'career';
|
||||
UPDATE chapters SET order_index = 2 WHERE order_index = 999 AND category = 'career_early';
|
||||
UPDATE chapters SET order_index = 3 WHERE order_index = 999 AND category = 'career_achievement';
|
||||
UPDATE chapters SET order_index = 4 WHERE order_index = 999 AND category = 'career_challenge';
|
||||
UPDATE chapters SET order_index = 5 WHERE order_index = 999 AND category = 'family';
|
||||
UPDATE chapters SET order_index = 6 WHERE order_index = 999 AND category = 'belief';
|
||||
UPDATE chapters SET order_index = 6 WHERE order_index = 999 AND category = 'beliefs';
|
||||
UPDATE chapters SET order_index = 7 WHERE order_index = 999 AND category = 'summary';
|
||||
19
api/migrations_legacy/fix_chapter_order_index_v2.sql
Normal file
19
api/migrations_legacy/fix_chapter_order_index_v2.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- 修正章节排序索引,与 8 分类体系对齐
|
||||
-- childhood=0, education=1, career_early=2, career_achievement=3,
|
||||
-- career_challenge=4, family=5, beliefs=6, summary=7
|
||||
UPDATE chapters SET order_index = 0 WHERE category = 'childhood' AND order_index != 0;
|
||||
UPDATE chapters SET order_index = 1 WHERE category = 'education' AND order_index != 1;
|
||||
UPDATE chapters SET order_index = 2 WHERE category = 'career_early' AND order_index != 2;
|
||||
UPDATE chapters SET order_index = 3 WHERE category = 'career_achievement' AND order_index != 3;
|
||||
UPDATE chapters SET order_index = 4 WHERE category = 'career_challenge' AND order_index != 4;
|
||||
UPDATE chapters SET order_index = 5 WHERE category = 'family' AND order_index != 5;
|
||||
UPDATE chapters SET order_index = 6 WHERE category IN ('belief', 'beliefs') AND order_index != 6;
|
||||
UPDATE chapters SET order_index = 7 WHERE category = 'summary' AND order_index != 7;
|
||||
|
||||
-- 旧的 5-stage "career" 章节归入 career_early
|
||||
UPDATE chapters SET category = 'career_early', order_index = 2
|
||||
WHERE category = 'career';
|
||||
|
||||
-- 旧的 "belief" 统一为 "beliefs"
|
||||
UPDATE chapters SET category = 'beliefs'
|
||||
WHERE category = 'belief';
|
||||
9
api/migrations_legacy/fix_memoir_images_order_index.sql
Normal file
9
api/migrations_legacy/fix_memoir_images_order_index.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- 修复 memoir_images 同一章节内 order_index 重复:封面=0,段落配图=1,2,3,...(section.order_index+1)
|
||||
-- 仅更新有 section_id 的段落配图,封面(section_id 为空)保持 0。
|
||||
-- 执行方式: psql -U <user> -d <database> -f api/migrations/fix_memoir_images_order_index.sql
|
||||
|
||||
UPDATE memoir_images mi
|
||||
SET order_index = cs.order_index + 1
|
||||
FROM chapter_sections cs
|
||||
WHERE mi.section_id IS NOT NULL
|
||||
AND mi.section_id = cs.id;
|
||||
141
api/migrations_legacy/sync_schema_to_models.sql
Normal file
141
api/migrations_legacy/sync_schema_to_models.sql
Normal file
@@ -0,0 +1,141 @@
|
||||
-- 数据库结构同步迁移脚本(与 api/database/models.py 保持一致)
|
||||
-- 幂等:可重复执行,已存在的表/列会跳过。
|
||||
-- 执行方式: psql -U <user> -d <database> -f api/migrations/sync_schema_to_models.sql
|
||||
-- 执行时间: 2026-02
|
||||
|
||||
-- ========== 1. users 表缺失列 ==========
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'users' AND column_name = 'subscription_type') THEN
|
||||
ALTER TABLE users ADD COLUMN subscription_type VARCHAR DEFAULT 'free';
|
||||
RAISE NOTICE '已添加 users.subscription_type';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'users' AND column_name = 'subscription_expires_at') THEN
|
||||
ALTER TABLE users ADD COLUMN subscription_expires_at TIMESTAMP WITH TIME ZONE DEFAULT NULL;
|
||||
RAISE NOTICE '已添加 users.subscription_expires_at';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'users' AND column_name = 'openid') THEN
|
||||
ALTER TABLE users ADD COLUMN openid VARCHAR UNIQUE;
|
||||
RAISE NOTICE '已添加 users.openid';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ========== 2. refresh_tokens 表缺失列 ==========
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'refresh_tokens' AND column_name = 'device_info') THEN
|
||||
ALTER TABLE refresh_tokens ADD COLUMN device_info VARCHAR;
|
||||
RAISE NOTICE '已添加 refresh_tokens.device_info';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ========== 3. conversations 表缺失列 ==========
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'conversations' AND column_name = 'current_topic') THEN
|
||||
ALTER TABLE conversations ADD COLUMN current_topic VARCHAR;
|
||||
RAISE NOTICE '已添加 conversations.current_topic';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'conversations' AND column_name = 'conversation_stage') THEN
|
||||
ALTER TABLE conversations ADD COLUMN conversation_stage VARCHAR;
|
||||
RAISE NOTICE '已添加 conversations.conversation_stage';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'conversations' AND column_name = 'last_message_at') THEN
|
||||
ALTER TABLE conversations ADD COLUMN last_message_at TIMESTAMP WITH TIME ZONE;
|
||||
RAISE NOTICE '已添加 conversations.last_message_at';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
UPDATE conversations
|
||||
SET last_message_at = started_at
|
||||
WHERE last_message_at IS NULL
|
||||
AND started_at IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_conversations_last_message_at ON conversations(last_message_at);
|
||||
|
||||
-- ========== 4. chapters 表缺失列 ==========
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'chapters' AND column_name = 'category') THEN
|
||||
ALTER TABLE chapters ADD COLUMN category VARCHAR;
|
||||
RAISE NOTICE '已添加 chapters.category';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'chapters' AND column_name = 'is_new') THEN
|
||||
ALTER TABLE chapters ADD COLUMN is_new BOOLEAN DEFAULT TRUE;
|
||||
RAISE NOTICE '已添加 chapters.is_new';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'chapters' AND column_name = 'source_segments') THEN
|
||||
ALTER TABLE chapters ADD COLUMN source_segments JSONB;
|
||||
RAISE NOTICE '已添加 chapters.source_segments';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'chapters' AND column_name = 'images') THEN
|
||||
ALTER TABLE chapters ADD COLUMN images JSONB;
|
||||
RAISE NOTICE '已添加 chapters.images';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ========== 5. books 表缺失列 ==========
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'books' AND column_name = 'has_update') THEN
|
||||
ALTER TABLE books ADD COLUMN has_update BOOLEAN DEFAULT FALSE;
|
||||
RAISE NOTICE '已添加 books.has_update';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'books' AND column_name = 'last_update_chapter_id') THEN
|
||||
ALTER TABLE books ADD COLUMN last_update_chapter_id VARCHAR;
|
||||
RAISE NOTICE '已添加 books.last_update_chapter_id';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ========== 6. orders 表(若无则创建) ==========
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id VARCHAR NOT NULL PRIMARY KEY,
|
||||
user_id VARCHAR NOT NULL REFERENCES users(id),
|
||||
plan_id VARCHAR NOT NULL,
|
||||
plan_name VARCHAR NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
currency VARCHAR DEFAULT 'CNY',
|
||||
payment_method VARCHAR NOT NULL,
|
||||
status VARCHAR DEFAULT 'pending',
|
||||
trade_no VARCHAR,
|
||||
paid_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
expired_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_orders_user_id ON orders(user_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_orders_trade_no ON orders(trade_no);
|
||||
CREATE INDEX IF NOT EXISTS ix_orders_status ON orders(status);
|
||||
|
||||
-- ========== 7. sms_verification_codes 表(若无则创建) ==========
|
||||
CREATE TABLE IF NOT EXISTS sms_verification_codes (
|
||||
id VARCHAR PRIMARY KEY,
|
||||
phone VARCHAR NOT NULL,
|
||||
code VARCHAR NOT NULL,
|
||||
purpose VARCHAR NOT NULL,
|
||||
is_used BOOLEAN DEFAULT FALSE,
|
||||
is_expired BOOLEAN DEFAULT FALSE,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
verified_at TIMESTAMP WITH TIME ZONE,
|
||||
ip_address VARCHAR
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_phone ON sms_verification_codes(phone);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_created_at ON sms_verification_codes(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_purpose ON sms_verification_codes(purpose);
|
||||
CREATE INDEX IF NOT EXISTS idx_sms_phone_purpose ON sms_verification_codes(phone, purpose);
|
||||
|
||||
-- ========== 8. memoir_states 表(若无则创建,供 create_all 未执行环境使用) ==========
|
||||
CREATE TABLE IF NOT EXISTS memoir_states (
|
||||
id VARCHAR NOT NULL PRIMARY KEY,
|
||||
user_id VARCHAR NOT NULL UNIQUE REFERENCES users(id),
|
||||
stage_order JSONB DEFAULT '[]'::jsonb,
|
||||
current_stage VARCHAR DEFAULT 'childhood',
|
||||
covered_stages JSONB DEFAULT '[]'::jsonb,
|
||||
slots JSONB NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE 'sync_schema_to_models 迁移执行完成';
|
||||
END $$;
|
||||
Reference in New Issue
Block a user