400 lines
13 KiB
JavaScript
400 lines
13 KiB
JavaScript
const { Client } = require('pg');
|
||
require('dotenv').config();
|
||
|
||
// 使用DATABASE_URL如果可用,否则回退到个别配置
|
||
const client = new Client(
|
||
process.env.DATABASE_URL || {
|
||
host: process.env.DATABASE_HOST || 'localhost',
|
||
port: process.env.DATABASE_PORT || 5432,
|
||
database: process.env.DATABASE_NAME || 'meteor_db',
|
||
user: process.env.DATABASE_USER || 'postgres',
|
||
password: process.env.DATABASE_PASSWORD || 'password',
|
||
}
|
||
);
|
||
|
||
// 订阅计划数据
|
||
const subscriptionPlans = [
|
||
{
|
||
plan_id: 'basic-monthly',
|
||
name: '基础版(月付)',
|
||
description: '适合个人用户的基础功能',
|
||
price: 9.99,
|
||
currency: 'CNY',
|
||
interval: 'month',
|
||
interval_count: 1,
|
||
stripe_price_id: 'price_basic_monthly',
|
||
features: JSON.stringify([
|
||
'基础数据访问',
|
||
'10个设备连接',
|
||
'基础天气数据',
|
||
'标准技术支持',
|
||
'7天数据保留'
|
||
]),
|
||
is_popular: false,
|
||
is_active: true
|
||
},
|
||
{
|
||
plan_id: 'pro-monthly',
|
||
name: '专业版(月付)',
|
||
description: '适合专业用户和小团队',
|
||
price: 29.99,
|
||
currency: 'CNY',
|
||
interval: 'month',
|
||
interval_count: 1,
|
||
stripe_price_id: 'price_pro_monthly',
|
||
features: JSON.stringify([
|
||
'高级数据分析',
|
||
'无限设备连接',
|
||
'实时数据流',
|
||
'高级天气集成',
|
||
'优先技术支持',
|
||
'30天数据保留',
|
||
'自定义报告',
|
||
'API访问权限'
|
||
]),
|
||
is_popular: true,
|
||
is_active: true
|
||
},
|
||
{
|
||
plan_id: 'pro-yearly',
|
||
name: '专业版(年付)',
|
||
description: '专业版年付,享受20%折扣',
|
||
price: 287.99,
|
||
currency: 'CNY',
|
||
interval: 'year',
|
||
interval_count: 1,
|
||
stripe_price_id: 'price_pro_yearly',
|
||
features: JSON.stringify([
|
||
'高级数据分析',
|
||
'无限设备连接',
|
||
'实时数据流',
|
||
'高级天气集成',
|
||
'优先技术支持',
|
||
'30天数据保留',
|
||
'自定义报告',
|
||
'API访问权限',
|
||
'年付8折优惠'
|
||
]),
|
||
is_popular: false,
|
||
is_active: true
|
||
},
|
||
{
|
||
plan_id: 'enterprise-monthly',
|
||
name: '企业版(月付)',
|
||
description: '适合大型企业和研究机构',
|
||
price: 99.99,
|
||
currency: 'CNY',
|
||
interval: 'month',
|
||
interval_count: 1,
|
||
stripe_price_id: 'price_enterprise_monthly',
|
||
features: JSON.stringify([
|
||
'企业级数据分析',
|
||
'无限设备和用户',
|
||
'实时数据流',
|
||
'完整天气数据',
|
||
'24/7专属支持',
|
||
'365天数据保留',
|
||
'高级自定义报告',
|
||
'完整API访问',
|
||
'数据导出功能',
|
||
'白标定制',
|
||
'SSO单点登录',
|
||
'专属客户经理'
|
||
]),
|
||
is_popular: false,
|
||
is_active: true
|
||
},
|
||
{
|
||
plan_id: 'free-trial',
|
||
name: '免费试用',
|
||
description: '14天免费试用专业版功能',
|
||
price: 0.00,
|
||
currency: 'CNY',
|
||
interval: 'month',
|
||
interval_count: 1,
|
||
stripe_price_id: null,
|
||
features: JSON.stringify([
|
||
'14天试用期',
|
||
'所有专业版功能',
|
||
'最多5个设备',
|
||
'基础技术支持',
|
||
'试用期数据保留'
|
||
]),
|
||
is_popular: false,
|
||
is_active: true
|
||
}
|
||
];
|
||
|
||
// 生成用户订阅数据
|
||
async function generateUserSubscriptions() {
|
||
// 获取所有用户
|
||
const usersResult = await client.query('SELECT id FROM user_profiles ORDER BY id');
|
||
const users = usersResult.rows;
|
||
|
||
if (users.length === 0) {
|
||
console.log('No users found. Skipping user subscription generation.');
|
||
return [];
|
||
}
|
||
|
||
// 获取所有订阅计划
|
||
const plansResult = await client.query('SELECT id, plan_id FROM subscription_plans WHERE is_active = true');
|
||
const plans = plansResult.rows;
|
||
|
||
const subscriptions = [];
|
||
const subscriptionStatuses = ['active', 'canceled', 'past_due', 'trialing'];
|
||
|
||
// 为部分用户创建订阅 (约70%的用户有订阅)
|
||
const usersWithSubscriptions = users.filter(() => Math.random() < 0.7);
|
||
|
||
for (const user of usersWithSubscriptions) {
|
||
const randomPlan = plans[Math.floor(Math.random() * plans.length)];
|
||
const status = subscriptionStatuses[Math.floor(Math.random() * subscriptionStatuses.length)];
|
||
|
||
const now = new Date();
|
||
const currentPeriodStart = new Date(now.getTime() - Math.random() * 30 * 24 * 60 * 60 * 1000); // 过去30天内开始
|
||
const currentPeriodEnd = new Date(currentPeriodStart.getTime() + 30 * 24 * 60 * 60 * 1000); // 30天周期
|
||
|
||
let trialStart = null;
|
||
let trialEnd = null;
|
||
let canceledAt = null;
|
||
|
||
if (status === 'trialing') {
|
||
trialStart = currentPeriodStart;
|
||
trialEnd = new Date(trialStart.getTime() + 14 * 24 * 60 * 60 * 1000); // 14天试用
|
||
}
|
||
|
||
if (status === 'canceled') {
|
||
canceledAt = new Date(currentPeriodStart.getTime() + Math.random() * 20 * 24 * 60 * 60 * 1000);
|
||
}
|
||
|
||
subscriptions.push({
|
||
user_profile_id: user.id,
|
||
subscription_plan_id: randomPlan.id,
|
||
stripe_subscription_id: `sub_${Math.random().toString(36).substr(2, 9)}`, // 模拟Stripe ID
|
||
status: status,
|
||
current_period_start: currentPeriodStart,
|
||
current_period_end: currentPeriodEnd,
|
||
cancel_at_period_end: status === 'canceled' ? true : Math.random() < 0.1,
|
||
canceled_at: canceledAt,
|
||
trial_start: trialStart,
|
||
trial_end: trialEnd
|
||
});
|
||
}
|
||
|
||
return subscriptions;
|
||
}
|
||
|
||
// 生成订阅历史记录
|
||
function generateSubscriptionHistory(subscriptionId, subscriptionData) {
|
||
const history = [];
|
||
const actions = ['created', 'updated', 'renewed', 'payment_failed'];
|
||
|
||
// 创建记录
|
||
history.push({
|
||
user_subscription_id: subscriptionId,
|
||
action: 'created',
|
||
old_status: null,
|
||
new_status: 'active',
|
||
metadata: JSON.stringify({
|
||
created_by: 'system',
|
||
payment_method: 'card'
|
||
})
|
||
});
|
||
|
||
// 添加一些随机历史记录
|
||
const numHistory = Math.floor(Math.random() * 3) + 1; // 1-3条记录
|
||
for (let i = 0; i < numHistory; i++) {
|
||
const action = actions[Math.floor(Math.random() * actions.length)];
|
||
let oldStatus = 'active';
|
||
let newStatus = 'active';
|
||
|
||
if (action === 'payment_failed') {
|
||
oldStatus = 'active';
|
||
newStatus = 'past_due';
|
||
} else if (action === 'renewed') {
|
||
oldStatus = 'past_due';
|
||
newStatus = 'active';
|
||
}
|
||
|
||
history.push({
|
||
user_subscription_id: subscriptionId,
|
||
action: action,
|
||
old_status: oldStatus,
|
||
new_status: newStatus,
|
||
metadata: JSON.stringify({
|
||
timestamp: new Date().toISOString(),
|
||
source: 'stripe_webhook'
|
||
})
|
||
});
|
||
}
|
||
|
||
return history;
|
||
}
|
||
|
||
// 生成支付记录
|
||
function generatePaymentRecords(subscriptionId, subscriptionData) {
|
||
const payments = [];
|
||
const paymentStatuses = ['succeeded', 'failed', 'pending'];
|
||
const paymentMethods = ['card', 'alipay', 'wechat_pay'];
|
||
|
||
// 生成1-5条支付记录
|
||
const numPayments = Math.floor(Math.random() * 5) + 1;
|
||
|
||
for (let i = 0; i < numPayments; i++) {
|
||
const status = paymentStatuses[Math.floor(Math.random() * paymentStatuses.length)];
|
||
const amount = 29.99 + Math.random() * 70; // 随机金额
|
||
|
||
let paidAt = null;
|
||
let failureReason = null;
|
||
|
||
if (status === 'succeeded') {
|
||
paidAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000);
|
||
} else if (status === 'failed') {
|
||
failureReason = ['card_declined', 'insufficient_funds', 'expired_card'][Math.floor(Math.random() * 3)];
|
||
}
|
||
|
||
payments.push({
|
||
user_subscription_id: subscriptionId,
|
||
stripe_payment_intent_id: `pi_${Math.random().toString(36).substr(2, 9)}`,
|
||
amount: amount,
|
||
currency: 'CNY',
|
||
status: status,
|
||
payment_method: paymentMethods[Math.floor(Math.random() * paymentMethods.length)],
|
||
failure_reason: failureReason,
|
||
paid_at: paidAt
|
||
});
|
||
}
|
||
|
||
return payments;
|
||
}
|
||
|
||
async function seedSubscriptionData() {
|
||
try {
|
||
await client.connect();
|
||
console.log('Connected to database');
|
||
|
||
// 清空现有数据
|
||
console.log('Clearing existing subscription data...');
|
||
await client.query('DELETE FROM payment_records');
|
||
await client.query('DELETE FROM subscription_history');
|
||
await client.query('DELETE FROM user_subscriptions');
|
||
await client.query('DELETE FROM subscription_plans');
|
||
|
||
// 插入订阅计划数据
|
||
console.log('Inserting subscription plans...');
|
||
const planIds = [];
|
||
|
||
for (const plan of subscriptionPlans) {
|
||
const query = `
|
||
INSERT INTO subscription_plans (
|
||
plan_id, name, description, price, currency, interval, interval_count,
|
||
stripe_price_id, features, is_popular, is_active
|
||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||
RETURNING id
|
||
`;
|
||
|
||
const values = [
|
||
plan.plan_id, plan.name, plan.description, plan.price, plan.currency,
|
||
plan.interval, plan.interval_count, plan.stripe_price_id, plan.features,
|
||
plan.is_popular, plan.is_active
|
||
];
|
||
|
||
const result = await client.query(query, values);
|
||
const planId = result.rows[0].id;
|
||
planIds.push(planId);
|
||
|
||
console.log(`Inserted subscription plan: ${plan.name} (ID: ${planId})`);
|
||
}
|
||
|
||
// 生成用户订阅数据
|
||
console.log('Generating user subscriptions...');
|
||
const subscriptions = await generateUserSubscriptions();
|
||
|
||
for (const subscription of subscriptions) {
|
||
const query = `
|
||
INSERT INTO user_subscriptions (
|
||
user_profile_id, subscription_plan_id, stripe_subscription_id, status,
|
||
current_period_start, current_period_end, cancel_at_period_end,
|
||
canceled_at, trial_start, trial_end
|
||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||
RETURNING id
|
||
`;
|
||
|
||
const values = [
|
||
subscription.user_profile_id, subscription.subscription_plan_id,
|
||
subscription.stripe_subscription_id, subscription.status,
|
||
subscription.current_period_start, subscription.current_period_end,
|
||
subscription.cancel_at_period_end, subscription.canceled_at,
|
||
subscription.trial_start, subscription.trial_end
|
||
];
|
||
|
||
const result = await client.query(query, values);
|
||
const subscriptionId = result.rows[0].id;
|
||
|
||
// 生成订阅历史记录
|
||
const history = generateSubscriptionHistory(subscriptionId, subscription);
|
||
for (const record of history) {
|
||
const historyQuery = `
|
||
INSERT INTO subscription_history (
|
||
user_subscription_id, action, old_status, new_status, metadata
|
||
) VALUES ($1, $2, $3, $4, $5)
|
||
`;
|
||
|
||
const historyValues = [
|
||
record.user_subscription_id, record.action, record.old_status,
|
||
record.new_status, record.metadata
|
||
];
|
||
|
||
await client.query(historyQuery, historyValues);
|
||
}
|
||
|
||
// 生成支付记录
|
||
const payments = generatePaymentRecords(subscriptionId, subscription);
|
||
for (const payment of payments) {
|
||
const paymentQuery = `
|
||
INSERT INTO payment_records (
|
||
user_subscription_id, stripe_payment_intent_id, amount, currency,
|
||
status, payment_method, failure_reason, paid_at
|
||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||
`;
|
||
|
||
const paymentValues = [
|
||
payment.user_subscription_id, payment.stripe_payment_intent_id,
|
||
payment.amount, payment.currency, payment.status, payment.payment_method,
|
||
payment.failure_reason, payment.paid_at
|
||
];
|
||
|
||
await client.query(paymentQuery, paymentValues);
|
||
}
|
||
|
||
console.log(`Generated subscription for user ${subscription.user_profile_id} with ${history.length} history records and ${payments.length} payment records`);
|
||
}
|
||
|
||
console.log('Subscription data seeding completed successfully!');
|
||
|
||
// 显示统计信息
|
||
const planCount = await client.query('SELECT COUNT(*) FROM subscription_plans');
|
||
const subscriptionCount = await client.query('SELECT COUNT(*) FROM user_subscriptions');
|
||
const historyCount = await client.query('SELECT COUNT(*) FROM subscription_history');
|
||
const paymentCount = await client.query('SELECT COUNT(*) FROM payment_records');
|
||
|
||
console.log(`Total subscription plans: ${planCount.rows[0].count}`);
|
||
console.log(`Total user subscriptions: ${subscriptionCount.rows[0].count}`);
|
||
console.log(`Total history records: ${historyCount.rows[0].count}`);
|
||
console.log(`Total payment records: ${paymentCount.rows[0].count}`);
|
||
|
||
} catch (error) {
|
||
console.error('Error seeding subscription data:', error);
|
||
process.exit(1);
|
||
} finally {
|
||
await client.end();
|
||
console.log('Database connection closed');
|
||
}
|
||
}
|
||
|
||
// 如果直接运行此脚本
|
||
if (require.main === module) {
|
||
seedSubscriptionData();
|
||
}
|
||
|
||
module.exports = { seedSubscriptionData }; |