428 lines
13 KiB
SQL
428 lines
13 KiB
SQL
-- ============================================
|
|
-- 订阅支付系统数据库迁移 SQL
|
|
-- 版本: v2.0.0
|
|
-- 日期: 2025-11-19
|
|
-- ============================================
|
|
|
|
-- ============================================
|
|
-- 第一步: 备份现有数据
|
|
-- ============================================
|
|
|
|
-- 创建备份表
|
|
CREATE TABLE IF NOT EXISTS user_subscriptions_backup AS SELECT * FROM user_subscriptions;
|
|
CREATE TABLE IF NOT EXISTS payment_orders_backup AS SELECT * FROM payment_orders;
|
|
CREATE TABLE IF NOT EXISTS subscription_plans_backup AS SELECT * FROM subscription_plans;
|
|
|
|
-- ============================================
|
|
-- 第二步: 删除旧表(先删除外键依赖的表)
|
|
-- ============================================
|
|
|
|
DROP TABLE IF EXISTS subscription_upgrades; -- 删除升级表,不再使用
|
|
DROP TABLE IF EXISTS promo_code_usage; -- 暂时删除,稍后重建
|
|
DROP TABLE IF EXISTS payment_orders; -- 删除旧订单表
|
|
DROP TABLE IF EXISTS user_subscriptions; -- 删除旧订阅表
|
|
DROP TABLE IF EXISTS subscription_plans; -- 删除旧套餐表
|
|
|
|
-- ============================================
|
|
-- 第三步: 创建新表结构
|
|
-- ============================================
|
|
|
|
-- 1. 订阅套餐表(重构)
|
|
CREATE TABLE subscription_plans (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
plan_code VARCHAR(20) NOT NULL UNIQUE COMMENT '套餐代码: pro, max',
|
|
plan_name VARCHAR(50) NOT NULL COMMENT '套餐名称: Pro专业版, Max旗舰版',
|
|
description TEXT COMMENT '套餐描述',
|
|
features JSON COMMENT '功能列表',
|
|
|
|
-- 价格配置(所有周期价格)
|
|
price_monthly DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '月付价格',
|
|
price_quarterly DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '季付价格(3个月)',
|
|
price_semiannual DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '半年付价格(6个月)',
|
|
price_yearly DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '年付价格(12个月)',
|
|
|
|
-- 状态字段
|
|
is_active BOOLEAN DEFAULT TRUE COMMENT '是否启用',
|
|
display_order INT DEFAULT 0 COMMENT '展示顺序',
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
INDEX idx_plan_code (plan_code),
|
|
INDEX idx_active_order (is_active, display_order)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订阅套餐配置表';
|
|
|
|
|
|
-- 2. 用户订阅记录表(重构)
|
|
CREATE TABLE user_subscriptions (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
user_id INT NOT NULL COMMENT '用户ID',
|
|
subscription_id VARCHAR(32) UNIQUE NOT NULL COMMENT '订阅ID(唯一标识)',
|
|
|
|
-- 订阅基本信息
|
|
plan_code VARCHAR(20) NOT NULL COMMENT '套餐代码: pro, max, free',
|
|
billing_cycle VARCHAR(20) NOT NULL COMMENT '计费周期: monthly, quarterly, semiannual, yearly',
|
|
|
|
-- 订阅时间
|
|
start_date DATETIME NOT NULL COMMENT '订阅开始时间',
|
|
end_date DATETIME NOT NULL COMMENT '订阅结束时间',
|
|
|
|
-- 订阅状态
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT '状态: active(有效), expired(已过期), cancelled(已取消)',
|
|
is_current BOOLEAN DEFAULT FALSE COMMENT '是否为当前生效的订阅',
|
|
|
|
-- 支付信息
|
|
payment_order_id INT COMMENT '关联的支付订单ID',
|
|
paid_amount DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '实际支付金额',
|
|
original_price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '原价',
|
|
discount_amount DECIMAL(10,2) DEFAULT 0 COMMENT '优惠金额',
|
|
|
|
-- 订阅类型
|
|
subscription_type VARCHAR(20) DEFAULT 'new' COMMENT '订阅类型: new(新购), renew(续费)',
|
|
previous_subscription_id VARCHAR(32) COMMENT '上一个订阅ID(续费时记录)',
|
|
|
|
-- 自动续费
|
|
auto_renew BOOLEAN DEFAULT FALSE COMMENT '是否自动续费',
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_subscription_id (subscription_id),
|
|
INDEX idx_user_current (user_id, is_current),
|
|
INDEX idx_status (status),
|
|
INDEX idx_end_date (end_date)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户订阅记录表';
|
|
|
|
|
|
-- 3. 支付订单表(重构)
|
|
CREATE TABLE payment_orders (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
order_no VARCHAR(32) UNIQUE NOT NULL COMMENT '订单号',
|
|
user_id INT NOT NULL COMMENT '用户ID',
|
|
|
|
-- 订阅信息
|
|
plan_code VARCHAR(20) NOT NULL COMMENT '套餐代码',
|
|
billing_cycle VARCHAR(20) NOT NULL COMMENT '计费周期',
|
|
subscription_type VARCHAR(20) DEFAULT 'new' COMMENT '订阅类型: new(新购), renew(续费)',
|
|
|
|
-- 价格信息
|
|
original_price DECIMAL(10,2) NOT NULL COMMENT '原价',
|
|
discount_amount DECIMAL(10,2) DEFAULT 0 COMMENT '优惠金额',
|
|
final_amount DECIMAL(10,2) NOT NULL COMMENT '实付金额',
|
|
|
|
-- 优惠码
|
|
promo_code_id INT COMMENT '优惠码ID',
|
|
promo_code VARCHAR(50) COMMENT '优惠码',
|
|
|
|
-- 支付信息
|
|
payment_method VARCHAR(20) DEFAULT 'wechat' COMMENT '支付方式: wechat, alipay',
|
|
payment_channel VARCHAR(50) COMMENT '支付渠道详情',
|
|
transaction_id VARCHAR(64) COMMENT '第三方交易号',
|
|
qr_code_url TEXT COMMENT '支付二维码URL',
|
|
|
|
-- 订单状态
|
|
status VARCHAR(20) DEFAULT 'pending' COMMENT '状态: pending(待支付), paid(已支付), expired(已过期), cancelled(已取消)',
|
|
|
|
-- 时间信息
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
paid_at TIMESTAMP NULL COMMENT '支付时间',
|
|
expired_at TIMESTAMP NULL COMMENT '过期时间',
|
|
|
|
-- 备注
|
|
remark TEXT COMMENT '备注信息',
|
|
|
|
INDEX idx_order_no (order_no),
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_status (status),
|
|
INDEX idx_created_at (created_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付订单表';
|
|
|
|
|
|
-- 4. 优惠码使用记录表(重建)
|
|
CREATE TABLE promo_code_usage (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
promo_code_id INT NOT NULL,
|
|
user_id INT NOT NULL,
|
|
order_id INT NOT NULL,
|
|
discount_amount DECIMAL(10,2) NOT NULL COMMENT '实际优惠金额',
|
|
used_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
INDEX idx_promo_code (promo_code_id),
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_order_id (order_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠码使用记录表';
|
|
|
|
|
|
-- ============================================
|
|
-- 第四步: 插入初始数据
|
|
-- ============================================
|
|
|
|
-- 插入套餐数据
|
|
INSERT INTO subscription_plans (
|
|
plan_code,
|
|
plan_name,
|
|
description,
|
|
price_monthly,
|
|
price_quarterly,
|
|
price_semiannual,
|
|
price_yearly,
|
|
features,
|
|
display_order,
|
|
is_active
|
|
) VALUES
|
|
(
|
|
'pro',
|
|
'Pro 专业版',
|
|
'为专业投资者打造,解锁高级分析功能',
|
|
299.00,
|
|
799.00,
|
|
1499.00,
|
|
2699.00,
|
|
JSON_ARRAY(
|
|
'新闻信息流',
|
|
'历史事件对比',
|
|
'事件传导链分析(AI)',
|
|
'事件-相关标的分析',
|
|
'相关概念展示',
|
|
'AI复盘功能',
|
|
'企业概览',
|
|
'个股深度分析(AI) - 50家/月',
|
|
'高效数据筛选工具',
|
|
'概念中心(548大概念)',
|
|
'历史时间轴查询 - 100天',
|
|
'涨停板块数据分析',
|
|
'个股涨停分析'
|
|
),
|
|
1,
|
|
TRUE
|
|
),
|
|
(
|
|
'max',
|
|
'Max 旗舰版',
|
|
'旗舰级体验,无限制使用所有功能',
|
|
599.00,
|
|
1599.00,
|
|
2999.00,
|
|
5399.00,
|
|
JSON_ARRAY(
|
|
'全部 Pro 版功能',
|
|
'板块深度分析(AI)',
|
|
'个股深度分析(AI) - 无限制',
|
|
'历史时间轴查询 - 无限制',
|
|
'概念高频更新',
|
|
'优先客服支持',
|
|
'独家功能抢先体验'
|
|
),
|
|
2,
|
|
TRUE
|
|
);
|
|
|
|
|
|
-- ============================================
|
|
-- 第五步: 数据迁移(可选)
|
|
-- ============================================
|
|
|
|
-- 如果需要迁移旧数据,取消以下注释:
|
|
|
|
/*
|
|
-- 迁移旧的用户订阅数据
|
|
INSERT INTO user_subscriptions (
|
|
user_id,
|
|
subscription_id,
|
|
plan_code,
|
|
billing_cycle,
|
|
start_date,
|
|
end_date,
|
|
status,
|
|
is_current,
|
|
paid_amount,
|
|
original_price,
|
|
subscription_type,
|
|
auto_renew,
|
|
created_at
|
|
)
|
|
SELECT
|
|
user_id,
|
|
CONCAT('SUB_', id, '_', UNIX_TIMESTAMP(NOW())), -- 生成订阅ID
|
|
subscription_type, -- 将 subscription_type 映射为 plan_code
|
|
COALESCE(billing_cycle, 'yearly'), -- 默认年付
|
|
COALESCE(start_date, NOW()),
|
|
COALESCE(end_date, DATE_ADD(NOW(), INTERVAL 365 DAY)),
|
|
subscription_status,
|
|
TRUE, -- 设为当前订阅
|
|
0, -- 旧数据没有支付金额,设为0
|
|
0, -- 旧数据没有原价,设为0
|
|
'new', -- 默认为新购
|
|
COALESCE(auto_renewal, FALSE),
|
|
created_at
|
|
FROM user_subscriptions_backup
|
|
WHERE subscription_type IN ('pro', 'max'); -- 只迁移付费用户
|
|
*/
|
|
|
|
-- ============================================
|
|
-- 第六步: 创建免费订阅记录(为所有用户)
|
|
-- ============================================
|
|
|
|
-- 为所有现有用户创建免费订阅记录(如果没有付费订阅)
|
|
/*
|
|
INSERT INTO user_subscriptions (
|
|
user_id,
|
|
subscription_id,
|
|
plan_code,
|
|
billing_cycle,
|
|
start_date,
|
|
end_date,
|
|
status,
|
|
is_current,
|
|
paid_amount,
|
|
original_price,
|
|
subscription_type
|
|
)
|
|
SELECT
|
|
id AS user_id,
|
|
CONCAT('FREE_', id, '_', UNIX_TIMESTAMP(NOW())),
|
|
'free',
|
|
'monthly',
|
|
NOW(),
|
|
'2099-12-31 23:59:59', -- 免费版永久有效
|
|
'active',
|
|
TRUE,
|
|
0,
|
|
0,
|
|
'new'
|
|
FROM user
|
|
WHERE id NOT IN (
|
|
SELECT DISTINCT user_id FROM user_subscriptions WHERE plan_code IN ('pro', 'max')
|
|
);
|
|
*/
|
|
|
|
-- ============================================
|
|
-- 第七步: 验证数据完整性
|
|
-- ============================================
|
|
|
|
-- 检查套餐数据
|
|
SELECT * FROM subscription_plans;
|
|
|
|
-- 检查用户订阅数据
|
|
SELECT
|
|
plan_code,
|
|
COUNT(*) as user_count,
|
|
SUM(CASE WHEN is_current = TRUE THEN 1 ELSE 0 END) as current_count
|
|
FROM user_subscriptions
|
|
GROUP BY plan_code;
|
|
|
|
-- 检查支付订单数据
|
|
SELECT
|
|
status,
|
|
COUNT(*) as order_count,
|
|
SUM(final_amount) as total_amount
|
|
FROM payment_orders
|
|
GROUP BY status;
|
|
|
|
-- ============================================
|
|
-- 第八步: 添加外键约束(可选)
|
|
-- ============================================
|
|
|
|
-- 注意: 只有在确认 users 表存在且数据完整时才执行
|
|
|
|
-- ALTER TABLE user_subscriptions
|
|
-- ADD CONSTRAINT fk_user_subscriptions_user
|
|
-- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
-- ALTER TABLE payment_orders
|
|
-- ADD CONSTRAINT fk_payment_orders_user
|
|
-- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
-- ALTER TABLE payment_orders
|
|
-- ADD CONSTRAINT fk_payment_orders_promo
|
|
-- FOREIGN KEY (promo_code_id) REFERENCES promo_codes(id) ON DELETE SET NULL;
|
|
|
|
-- ALTER TABLE promo_code_usage
|
|
-- ADD CONSTRAINT fk_promo_usage_promo
|
|
-- FOREIGN KEY (promo_code_id) REFERENCES promo_codes(id) ON DELETE CASCADE;
|
|
|
|
-- ALTER TABLE promo_code_usage
|
|
-- ADD CONSTRAINT fk_promo_usage_user
|
|
-- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
|
|
|
-- ALTER TABLE promo_code_usage
|
|
-- ADD CONSTRAINT fk_promo_usage_order
|
|
-- FOREIGN KEY (order_id) REFERENCES payment_orders(id) ON DELETE CASCADE;
|
|
|
|
|
|
-- ============================================
|
|
-- 第九步: 创建测试数据(开发环境)
|
|
-- ============================================
|
|
|
|
-- 插入测试优惠码
|
|
INSERT INTO promo_codes (
|
|
code,
|
|
description,
|
|
discount_type,
|
|
discount_value,
|
|
applicable_plans,
|
|
applicable_cycles,
|
|
max_total_uses,
|
|
max_uses_per_user,
|
|
valid_from,
|
|
valid_until,
|
|
is_active
|
|
) VALUES
|
|
(
|
|
'WELCOME2025',
|
|
'2025新用户专享',
|
|
'percentage',
|
|
20.00,
|
|
NULL, -- 适用所有套餐
|
|
NULL, -- 适用所有周期
|
|
1000,
|
|
1,
|
|
NOW(),
|
|
DATE_ADD(NOW(), INTERVAL 90 DAY),
|
|
TRUE
|
|
),
|
|
(
|
|
'YEAR2025',
|
|
'年付专享',
|
|
'percentage',
|
|
10.00,
|
|
NULL,
|
|
JSON_ARRAY('yearly'), -- 仅适用年付
|
|
500,
|
|
1,
|
|
NOW(),
|
|
DATE_ADD(NOW(), INTERVAL 365 DAY),
|
|
TRUE
|
|
),
|
|
(
|
|
'TESTCODE',
|
|
'测试优惠码 - 固定减100元',
|
|
'fixed_amount',
|
|
100.00,
|
|
NULL,
|
|
NULL,
|
|
100,
|
|
1,
|
|
NOW(),
|
|
DATE_ADD(NOW(), INTERVAL 30 DAY),
|
|
TRUE
|
|
);
|
|
|
|
|
|
-- ============================================
|
|
-- 迁移完成提示
|
|
-- ============================================
|
|
|
|
SELECT '===================================' AS '';
|
|
SELECT '数据库迁移完成!' AS '状态';
|
|
SELECT '===================================' AS '';
|
|
SELECT '请检查以下数据:' AS '提示';
|
|
SELECT '1. subscription_plans 表是否有2条记录 (pro, max)' AS '';
|
|
SELECT '2. user_subscriptions 表数据是否正确' AS '';
|
|
SELECT '3. payment_orders 表结构是否正确' AS '';
|
|
SELECT '4. 备份表 (*_backup) 已创建' AS '';
|
|
SELECT '===================================' AS '';
|
|
SELECT '下一步: 更新后端代码 (app.py, models.py)' AS '';
|
|
SELECT '===================================' AS '';
|