meteor_detection_system/meteor-web-backend/migrations/1754714300000_create-subscription-tables.js

220 lines
5.7 KiB
JavaScript

/**
* 用户订阅管理相关表
*/
export const up = (pgm) => {
// 1. 订阅计划表
pgm.createTable('subscription_plans', {
id: { type: 'serial', primaryKey: true },
plan_id: {
type: 'varchar(100)',
unique: true,
notNull: true,
comment: '计划唯一标识符'
},
name: {
type: 'varchar(255)',
notNull: true,
comment: '计划名称'
},
description: {
type: 'text',
comment: '计划描述'
},
price: {
type: 'decimal(10,2)',
notNull: true,
comment: '价格'
},
currency: {
type: 'varchar(3)',
notNull: true,
default: 'CNY',
comment: '货币类型'
},
interval: {
type: 'varchar(50)',
notNull: true,
comment: 'month, year, week'
},
interval_count: {
type: 'integer',
notNull: true,
default: 1,
comment: '间隔数量'
},
stripe_price_id: {
type: 'varchar(255)',
comment: 'Stripe价格ID'
},
features: {
type: 'jsonb',
comment: '功能列表JSON'
},
is_popular: {
type: 'boolean',
default: false,
comment: '是否为推荐计划'
},
is_active: {
type: 'boolean',
notNull: true,
default: true,
comment: '是否启用'
},
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('current_timestamp') },
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('current_timestamp') }
});
// 2. 用户订阅表
pgm.createTable('user_subscriptions', {
id: { type: 'serial', primaryKey: true },
user_profile_id: {
type: 'uuid',
notNull: true,
references: 'user_profiles(id)',
onDelete: 'CASCADE'
},
subscription_plan_id: {
type: 'integer',
notNull: true,
references: 'subscription_plans(id)',
onDelete: 'RESTRICT'
},
stripe_subscription_id: {
type: 'varchar(255)',
unique: true,
comment: 'Stripe订阅ID'
},
status: {
type: 'varchar(50)',
notNull: true,
comment: 'active, canceled, past_due, trialing, incomplete'
},
current_period_start: {
type: 'timestamptz',
notNull: true,
comment: '当前周期开始时间'
},
current_period_end: {
type: 'timestamptz',
notNull: true,
comment: '当前周期结束时间'
},
cancel_at_period_end: {
type: 'boolean',
default: false,
comment: '是否在周期结束时取消'
},
canceled_at: {
type: 'timestamptz',
comment: '取消时间'
},
trial_start: {
type: 'timestamptz',
comment: '试用开始时间'
},
trial_end: {
type: 'timestamptz',
comment: '试用结束时间'
},
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('current_timestamp') },
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('current_timestamp') }
});
// 3. 订阅历史记录表
pgm.createTable('subscription_history', {
id: { type: 'serial', primaryKey: true },
user_subscription_id: {
type: 'integer',
notNull: true,
references: 'user_subscriptions(id)',
onDelete: 'CASCADE'
},
action: {
type: 'varchar(100)',
notNull: true,
comment: 'created, updated, canceled, renewed, payment_failed'
},
old_status: {
type: 'varchar(50)',
comment: '原状态'
},
new_status: {
type: 'varchar(50)',
comment: '新状态'
},
metadata: {
type: 'jsonb',
comment: '额外信息JSON'
},
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('current_timestamp') }
});
// 4. 支付记录表
pgm.createTable('payment_records', {
id: { type: 'serial', primaryKey: true },
user_subscription_id: {
type: 'integer',
notNull: true,
references: 'user_subscriptions(id)',
onDelete: 'CASCADE'
},
stripe_payment_intent_id: {
type: 'varchar(255)',
unique: true,
comment: 'Stripe支付意向ID'
},
amount: {
type: 'decimal(10,2)',
notNull: true,
comment: '支付金额'
},
currency: {
type: 'varchar(3)',
notNull: true,
comment: '货币类型'
},
status: {
type: 'varchar(50)',
notNull: true,
comment: 'succeeded, failed, pending, canceled'
},
payment_method: {
type: 'varchar(100)',
comment: '支付方式'
},
failure_reason: {
type: 'varchar(255)',
comment: '失败原因'
},
paid_at: {
type: 'timestamptz',
comment: '支付成功时间'
},
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('current_timestamp') }
});
// 添加索引
pgm.createIndex('subscription_plans', 'plan_id');
pgm.createIndex('subscription_plans', 'is_active');
pgm.createIndex('user_subscriptions', 'user_profile_id');
pgm.createIndex('user_subscriptions', 'status');
pgm.createIndex('user_subscriptions', 'stripe_subscription_id');
pgm.createIndex('subscription_history', 'user_subscription_id');
pgm.createIndex('payment_records', 'user_subscription_id');
pgm.createIndex('payment_records', 'status');
// 添加唯一约束:一个用户同时只能有一个活跃订阅
pgm.addConstraint('user_subscriptions', 'unique_active_subscription', {
unique: ['user_profile_id'],
where: "status IN ('active', 'trialing')"
});
};
export const down = (pgm) => {
pgm.dropTable('payment_records');
pgm.dropTable('subscription_history');
pgm.dropTable('user_subscriptions');
pgm.dropTable('subscription_plans');
};