iconsulting/scripts/seed.ts

242 lines
8.0 KiB
TypeScript

/**
* 数据库种子脚本
*
* 用法:
* npx ts-node scripts/seed.ts
*
* 或在 docker-compose 中启动时执行
*
* 环境变量:
* DATABASE_URL: 数据库连接字符串
* DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME: 单独配置
*/
import { Client } from 'pg';
import * as bcrypt from 'bcrypt';
// 默认租户 ID
const DEFAULT_TENANT_ID = '00000000-0000-0000-0000-000000000001';
// 数据库连接配置
function getDbConfig() {
if (process.env.DATABASE_URL) {
return { connectionString: process.env.DATABASE_URL };
}
return {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'postgres',
database: process.env.DB_NAME || 'iconsulting',
};
}
async function seed() {
const client = new Client(getDbConfig());
try {
await client.connect();
console.log('Connected to database');
// 1. 检查并创建默认租户
await seedTenant(client);
// 2. 检查并创建默认管理员
await seedAdmin(client);
// 3. 检查并创建默认系统配置
await seedSystemConfigs(client);
// 4. 检查并创建默认服务定价
await seedServicePricing(client);
console.log('Seed completed successfully!');
} catch (error) {
console.error('Seed failed:', error);
process.exit(1);
} finally {
await client.end();
}
}
async function seedTenant(client: Client) {
console.log('Checking tenants table...');
// 检查 tenants 表是否存在
const tableExists = await client.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'tenants'
);
`);
if (!tableExists.rows[0].exists) {
console.log(' tenants table does not exist, skipping');
return;
}
// 检查默认租户是否存在
const tenantExists = await client.query(
'SELECT id FROM tenants WHERE id = $1',
[DEFAULT_TENANT_ID]
);
if (tenantExists.rows.length === 0) {
console.log(' Creating default tenant...');
await client.query(`
INSERT INTO tenants (id, name, slug, status, plan)
VALUES ($1, 'Default Tenant', 'default', 'ACTIVE', 'ENTERPRISE')
`, [DEFAULT_TENANT_ID]);
console.log(' Default tenant created');
} else {
console.log(' Default tenant already exists');
}
}
async function seedAdmin(client: Client) {
console.log('Checking admins table...');
// 检查 admins 表是否存在
const tableExists = await client.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'admins'
);
`);
if (!tableExists.rows[0].exists) {
console.log(' admins table does not exist, skipping');
return;
}
// 检查默认管理员是否存在
const adminExists = await client.query(
"SELECT id FROM admins WHERE username = 'admin'"
);
if (adminExists.rows.length === 0) {
console.log(' Creating default admin...');
// bcrypt hash for 'admin123' with cost 10
const passwordHash = await bcrypt.hash('admin123', 10);
// 检查表结构是否包含 tenant_id 和 is_super_admin
const hasNewColumns = await client.query(`
SELECT column_name FROM information_schema.columns
WHERE table_name = 'admins'
AND column_name IN ('tenant_id', 'is_super_admin')
`);
if (hasNewColumns.rows.length >= 2) {
// 新表结构
await client.query(`
INSERT INTO admins (tenant_id, is_super_admin, username, password_hash, name, role, permissions)
VALUES ($1, TRUE, 'admin', $2, '系统管理员', 'SUPER_ADMIN', '["*"]')
`, [DEFAULT_TENANT_ID, passwordHash]);
} else {
// 旧表结构
await client.query(`
INSERT INTO admins (username, password_hash, name, role, permissions)
VALUES ('admin', $1, '系统管理员', 'SUPER_ADMIN', '["*"]')
`, [passwordHash]);
}
console.log(' Default admin created (username: admin, password: admin123)');
} else {
console.log(' Default admin already exists');
}
}
async function seedSystemConfigs(client: Client) {
console.log('Checking system_configs table...');
// 检查 system_configs 表是否存在
const tableExists = await client.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'system_configs'
);
`);
if (!tableExists.rows[0].exists) {
console.log(' system_configs table does not exist, skipping');
return;
}
const configs = [
{ key: 'system_prompt_identity', value: '"专业、友善、耐心的香港移民顾问"', group: 'PROMPT', desc: '系统身份定位' },
{ key: 'system_prompt_style', value: '"专业但不生硬,用简洁明了的语言解答"', group: 'PROMPT', desc: '对话风格' },
{ key: 'system_prompt_rules', value: '["只回答移民相关问题", "复杂评估建议付费服务", "不做法律承诺"]', group: 'PROMPT', desc: '系统行为规则' },
{ key: 'max_free_messages_per_day', value: '50', group: 'SYSTEM', desc: '每日免费消息数限制' },
{ key: 'max_conversation_context_messages', value: '20', group: 'SYSTEM', desc: '对话上下文最大消息数' },
{ key: 'assessment_price_default', value: '99', group: 'PAYMENT', desc: '默认评估价格(元)' },
{ key: 'payment_timeout_minutes', value: '30', group: 'PAYMENT', desc: '支付超时时间(分钟)' },
{ key: 'sms_rate_limit_per_hour', value: '5', group: 'SYSTEM', desc: '每小时短信发送限制' },
{ key: 'enable_anonymous_chat', value: 'true', group: 'FEATURE', desc: '是否允许匿名用户聊天' },
{ key: 'require_phone_for_payment', value: 'true', group: 'FEATURE', desc: '支付时是否要求手机验证' },
];
for (const config of configs) {
const exists = await client.query(
'SELECT key FROM system_configs WHERE key = $1',
[config.key]
);
if (exists.rows.length === 0) {
await client.query(`
INSERT INTO system_configs (key, value, config_group, description)
VALUES ($1, $2, $3, $4)
`, [config.key, config.value, config.group, config.desc]);
console.log(` Created config: ${config.key}`);
}
}
console.log(' System configs seeded');
}
async function seedServicePricing(client: Client) {
console.log('Checking service_pricing table...');
// 检查 service_pricing 表是否存在
const tableExists = await client.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'service_pricing'
);
`);
if (!tableExists.rows[0].exists) {
console.log(' service_pricing table does not exist, skipping');
return;
}
const pricing = [
{ type: 'ASSESSMENT', category: 'QMAS', price: 99, originalPrice: 199, desc: '优才计划移民资格评估' },
{ type: 'ASSESSMENT', category: 'GEP', price: 99, originalPrice: 199, desc: '专才计划移民资格评估' },
{ type: 'ASSESSMENT', category: 'IANG', price: 79, originalPrice: 149, desc: '留学IANG移民资格评估' },
{ type: 'ASSESSMENT', category: 'TTPS', price: 99, originalPrice: 199, desc: '高才通移民资格评估' },
{ type: 'ASSESSMENT', category: 'CIES', price: 199, originalPrice: 399, desc: '投资移民资格评估' },
{ type: 'ASSESSMENT', category: 'TECHTAS', price: 99, originalPrice: 199, desc: '科技人才移民资格评估' },
];
for (const p of pricing) {
const exists = await client.query(
'SELECT id FROM service_pricing WHERE service_type = $1 AND category = $2',
[p.type, p.category]
);
if (exists.rows.length === 0) {
await client.query(`
INSERT INTO service_pricing (service_type, category, price, original_price, currency, description)
VALUES ($1, $2, $3, $4, 'CNY', $5)
`, [p.type, p.category, p.price, p.originalPrice, p.desc]);
console.log(` Created pricing: ${p.type} - ${p.category}`);
}
}
console.log(' Service pricing seeded');
}
// 运行种子脚本
seed();