import { PrismaClient } from '@prisma/client' import * as bcrypt from 'bcrypt' const prisma = new PrismaClient() async function main() { const hashed = await bcrypt.hash('admin123', 10) // ---- 租户 ---- const demoCompany = await prisma.tenant.upsert({ where: { id: 'demo-tenant-001' }, update: {}, create: { id: 'demo-tenant-001', companyName: '示例科技公司', contactName: '张三', contactEmail: 'contact@example.com', plan: 'enterprise', status: 'active', validFrom: new Date('2026-01-01'), validUntil: new Date('2027-01-01'), platformType: 'jubian', platformUrl: 'https://jubian.example.com', features: JSON.stringify({ inquiry: true, imageGen: true, product: true, comparison: true }), usageQuotas: JSON.stringify({ monthlyLeads: 10000, monthlyImages: 500, monthlyProducts: 5000, monthlyComparisons: 200, maxUsers: 50 }), }, }) const farEast = await prisma.tenant.upsert({ where: { id: 'demo-tenant-002' }, update: {}, create: { id: 'demo-tenant-002', companyName: '远东电缆有限公司', contactName: '李四', contactEmail: 'li@fareast-cable.com', plan: 'pro', status: 'active', validFrom: new Date('2026-03-01'), validUntil: new Date('2026-05-31'), platformType: 'custom', platformUrl: 'https://erp.fareast-cable.com', features: JSON.stringify({ inquiry: true, imageGen: false, product: true, comparison: true }), usageQuotas: JSON.stringify({ monthlyLeads: 5000, monthlyImages: 0, monthlyProducts: 2000, monthlyComparisons: 100, maxUsers: 20 }), }, }) const shangshang = await prisma.tenant.upsert({ where: { id: 'demo-tenant-003' }, update: {}, create: { id: 'demo-tenant-003', companyName: '上上电缆集团', contactName: '王五', contactEmail: 'wang@shangshang.com', plan: 'basic', status: 'active', validFrom: new Date('2026-04-01'), validUntil: new Date('2027-04-01'), features: JSON.stringify({ inquiry: true, imageGen: false, product: true, comparison: false }), usageQuotas: JSON.stringify({ monthlyLeads: 1000, monthlyImages: 0, monthlyProducts: 500, monthlyComparisons: 0, maxUsers: 5 }), }, }) const minxing = await prisma.tenant.upsert({ where: { id: 'demo-tenant-004' }, update: {}, create: { id: 'demo-tenant-004', companyName: '民兴电缆科技', contactName: '赵六', contactEmail: 'zhao@minxing.com', plan: 'basic', status: 'expired', validFrom: new Date('2025-01-01'), validUntil: new Date('2026-01-01'), features: JSON.stringify({ inquiry: true, imageGen: false, product: false, comparison: false }), usageQuotas: JSON.stringify({ monthlyLeads: 500, monthlyImages: 0, monthlyProducts: 0, monthlyComparisons: 0, maxUsers: 3 }), }, }) // ---- 用户 ---- await prisma.user.upsert({ where: { email: 'admin@platform.com' }, update: {}, create: { email: 'admin@platform.com', username: 'admin', password: hashed, name: '管理员', role: 'admin', tenantId: demoCompany.id }, }) await prisma.user.upsert({ where: { email: 'user@example.com' }, update: {}, create: { email: 'user@example.com', username: 'user', password: hashed, name: '测试用户', role: 'user', tenantId: demoCompany.id }, }) await prisma.user.upsert({ where: { email: 'li@fareast-cable.com' }, update: {}, create: { email: 'li@fareast-cable.com', username: 'li.fareast', password: hashed, name: '李四', role: 'user', tenantId: farEast.id }, }) await prisma.user.upsert({ where: { email: 'wang@shangshang.com' }, update: {}, create: { email: 'wang@shangshang.com', username: 'wang', password: hashed, name: '王五', role: 'user', tenantId: shangshang.id }, }) await prisma.user.upsert({ where: { email: 'zhao@minxing.com' }, update: {}, create: { email: 'zhao@minxing.com', username: 'zhao', password: hashed, name: '赵六', role: 'user', tenantId: minxing.id }, }) await prisma.rule.createMany({ data: [ { name: '综合评分权重', type: 'scoring', config: JSON.stringify({ weights: [{ field: '预估金额', weight: 30 }, { field: '匹配度', weight: 25 }, { field: '甲方级别', weight: 20 }, { field: '时效性', weight: 15 }, { field: '历史成交', weight: 10 }] }), enabled: true }, { name: '防水材料关键词库', type: 'keyword', config: JSON.stringify({ keywords: ['防水卷材', '防水涂料', '聚氨酯', 'SBS改性沥青', '高分子防水'], category: '防水材料', priority: 8 }), enabled: true }, { name: '电力设备关键词库', type: 'keyword', config: JSON.stringify({ keywords: ['变压器', '断路器', '电缆', '开关柜', 'GIS'], category: '电力设备', priority: 9 }), enabled: true }, { name: '钢材关键词库', type: 'keyword', config: JSON.stringify({ keywords: ['钢板', '钢管', '型钢', '螺纹钢', 'H型钢'], category: '钢材', priority: 7 }), enabled: true }, { name: '甲方分级标准', type: 'client_level', config: JSON.stringify({ levels: [{ name: 'S级', minAmount: 1000000, minCooperations: 3 }, { name: 'A级', minAmount: 500000, minCooperations: 1 }, { name: 'B级', minAmount: 100000 }, { name: 'C级', minAmount: 10000 }, { name: 'D级' }] }), enabled: true }, ], }) await prisma.report.createMany({ data: [ { title: '5月第3周线索数据报告', type: '线索', status: '已完成', config: JSON.stringify({ dateRange: '2024-05-13~2024-05-19', sources: ['中国采购与招标网', '中国电建阳光采购网'], categories: ['电力设备', '钢材', 'IT设备'] }), channels: JSON.stringify(['飞书', '邮箱']) }, { title: '5月询价发布统计', type: '询价', status: '已完成', config: JSON.stringify({ dateRange: '2024-05-01~2024-05-20' }) }, { title: '本周商品上架报告', type: '商品', status: '生成中', config: JSON.stringify({ dateRange: '2024-05-20~2024-05-22' }) }, ], }) await prisma.auditLog.createMany({ data: [ { action: '登录', detail: '用户 admin 登录系统', operator: 'admin', ip: '192.168.1.100' }, { action: '修改规则', detail: '修改了评分权重配置', operator: 'admin', ip: '192.168.1.100' }, { action: '导出报告', detail: '导出了5月第3周线索数据报告', operator: 'admin', ip: '192.168.1.100' }, { action: '删除线索', detail: '删除了2条无效线索', operator: 'admin', ip: '192.168.1.100' }, { action: '批量上架', detail: '批量上架了5个商品', operator: 'admin', ip: '192.168.1.100' }, { action: '修改规则', detail: '新增了钢材品类关键词', operator: 'admin', ip: '192.168.1.100' }, { action: '导出报告', detail: '导出了5月询价发布统计', operator: 'admin', ip: '192.168.1.100' }, { action: '登录', detail: '用户 admin 登录系统', operator: 'admin', ip: '192.168.1.100' }, ], }) await prisma.crawlerConfig.deleteMany() await prisma.crawlerConfig.createMany({ data: [ { name: '中国电建阳光采购网', targetUrl: 'https://bid.powerchina.cn', type: 'bid_powerchina', sourceName: '中电建', status: 'active', schedule: '0 0 8 * * *', totalRuns: 286, successRuns: 275, failedRuns: 11, keywords: '["电缆","防火门","配电箱","钢材"]' }, { name: '新矿集团供应链平台', targetUrl: 'https://xkgroup.kdcloud.com', type: 'bid_xkgroup', sourceName: '新矿集团', scriptPath: 'fetch-xkgroup.py', status: 'active', schedule: '0 5 8 * * *', totalRuns: 142, successRuns: 138, failedRuns: 4, keywords: '["电缆","钢材","阀门","管材"]' }, { name: '中核集团电子采购平台', targetUrl: 'https://www.cnncecp.com', type: 'bid_cnncecp', sourceName: '中核集团', scriptPath: 'fetch-cnncecp.py', status: 'paused', totalRuns: 0, successRuns: 0, failedRuns: 0, keywords: '["电缆","防火门","钢材","阀门"]' }, ], }) await prisma.inquiry.createMany({ data: [ { title: '2024年电力电缆框架采购项目', source: '中国电建阳光采购网', url: 'https://example.com/inquiry/001', budget: '500万元', deadline: '2024-12-31', buyer: '中国电力建设集团', category: '电力设备', status: 'pending', level: 'S', star: 5, region: '北京' }, { title: '钢结构桥梁用钢板采购招标', source: '中国中铁采购电子商务平台', url: 'https://example.com/inquiry/002', budget: '1200万元', deadline: '2024-11-15', buyer: '中铁大桥局', category: '钢材', status: 'published', level: 'A', star: 4, region: '湖北武汉' }, { title: '数据中心服务器及网络设备采购', source: '中国移动采购与招标网', url: 'https://example.com/inquiry/003', budget: '800万元', deadline: '2024-10-20', buyer: '中国移动通信集团', category: 'IT设备', status: 'pending', level: 'S', star: 5, region: '北京' }, ], }) const cableSKUs = [ { name: 'YJV-0.6/1kV 3×4 电力电缆', spec: '0.6/1kV 3×4mm²', unit: '米', category: '电力电缆' }, { name: 'YJV-0.6/1kV 3×6 电力电缆', spec: '0.6/1kV 3×6mm²', unit: '米', category: '电力电缆' }, { name: 'YJV-0.6/1kV 3×10 电力电缆', spec: '0.6/1kV 3×10mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×70 电力电缆', spec: '8.7/15kV 3×70mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×95 电力电缆', spec: '8.7/15kV 3×95mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×120 电力电缆', spec: '8.7/15kV 3×120mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×150 电力电缆', spec: '8.7/15kV 3×150mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×185 电力电缆', spec: '8.7/15kV 3×185mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×240 电力电缆', spec: '8.7/15kV 3×240mm²', unit: '米', category: '电力电缆' }, { name: 'YJV22-8.7/15kV 3×300 电力电缆', spec: '8.7/15kV 3×300mm²', unit: '米', category: '电力电缆' }, { name: 'BV-450/750V 2.5mm² 家装电线', spec: '450/750V 2.5mm²', unit: '米', category: '家装电线' }, { name: 'BV-450/750V 4mm² 家装电线', spec: '450/750V 4mm²', unit: '米', category: '家装电线' }, { name: 'BV-450/750V 6mm² 家装电线', spec: '450/750V 6mm²', unit: '米', category: '家装电线' }, { name: 'KVVP-0.45/0.75kV 14×1.5 控制电缆', spec: '0.45/0.75kV 14×1.5mm²', unit: '米', category: '控制电缆' }, { name: 'RVVP-300/300V 4×0.75 屏蔽软电缆', spec: '300/300V 4×0.75mm²', unit: '米', category: '控制电缆' }, ] const suppliers = [ { name: '远东电缆', brand: '远东电缆' }, { name: '上上电缆', brand: '上上电缆' }, { name: '民兴电缆', brand: '民兴电缆' }, { name: '金杯电缆', brand: '金杯电缆' }, { name: '金龙羽', brand: '金龙羽' }, ] // 价格矩阵:每行是一个商品,每列是对应供应商的价格 // 设计逻辑:每个供应商在不同商品上有价格优势,使得 2/3/4 家组合的最优方案不同 // 远东: 商品0-2最便宜 | 上上: 商品3-5最便宜 | 民兴: 商品6-8最便宜 | 金杯: 商品9-11最便宜 | 金龙羽: 商品12-14最便宜 const priceMatrix: number[][] = [ // SKU 远东 上上 民兴 金杯 金龙羽 /* YJV 3×4 电力电缆 */ [12.00, 12.80, 13.50, 13.20, 14.00], /* YJV 3×6 电力电缆 */ [18.00, 19.20, 20.10, 19.50, 21.00], /* YJV 3×10 电力电缆 */ [27.50, 29.00, 30.50, 29.80, 32.00], /* YJV22 3×70 电力电缆 */ [190.00, 182.00, 195.00, 188.00, 200.00], /* YJV22 3×95 电力电缆 */ [250.00, 238.00, 260.00, 248.00, 265.00], /* YJV22 3×120 电力电缆 */[320.00, 305.00, 330.00, 315.00, 340.00], /* YJV22 3×150 电力电缆 */[410.00, 395.00, 385.00, 400.00, 420.00], /* YJV22 3×185 电力电缆 */[500.00, 480.00, 465.00, 475.00, 510.00], /* YJV22 3×240 电力电缆 */[650.00, 630.00, 615.00, 625.00, 670.00], /* YJV22 3×300 电力电缆 */[810.00, 790.00, 780.00, 770.00, 830.00], /* BV 2.5mm² 家装电线 */ [1.85, 1.80, 1.75, 1.68, 1.90], /* BV 4mm² 家装电线 */ [2.70, 2.60, 2.55, 2.45, 2.80], /* BV 6mm² 家装电线 */ [4.30, 4.15, 4.10, 3.95, 4.50], /* KVVP 控制电缆 */ [40.00, 38.50, 37.00, 36.00, 42.00], /* RVVP 屏蔽软电缆 */ [23.00, 22.00, 21.50, 21.00, 24.00], ] // 每个商品的数量 const quantities = [3000, 2500, 2000, 1500, 1200, 1000, 800, 600, 500, 400, 5000, 4000, 3000, 800, 600] const productData: any[] = [] for (let si = 0; si < suppliers.length; si++) { const s = suppliers[si] for (let pi = 0; pi < cableSKUs.length; pi++) { productData.push({ name: cableSKUs[pi].name, spec: cableSKUs[pi].spec, unit: cableSKUs[pi].unit, category: cableSKUs[pi].category, price: priceMatrix[pi][si], quantity: quantities[pi], status: 'draft', brand: s.brand, supplier: s.name, minOrder: 100, }) } } await prisma.product.deleteMany() await prisma.product.createMany({ data: productData }) // Default system configs const defaultConfigs = [ { key: 'llm_provider', value: 'minimax', group: 'ai', description: '大模型供应商(minimax / openai)' }, { key: 'llm_model', value: 'abab-6.5s-chat', group: 'ai', description: '对话模型名称' }, { key: 'image_provider', value: 'minimax', group: 'ai', description: '图片生成供应商(minimax)' }, { key: 'mmx_group_id', value: 'mg-20260301-abc123', group: 'ai', description: 'MiniMax 分组 ID' }, { key: 'mmx_api_key', value: 'sk-mmx-xxxxxxxxxxxxxxxx', group: 'ai', description: 'MiniMax API 密钥' }, { key: 'jubian_api_url', value: 'https://api.jubian.com/v1', group: 'platform', description: '巨变平台 API 地址' }, { key: 'jubian_api_key', value: 'jb-sk-xxxxxxxxxxxxxxxx', group: 'platform', description: '巨变平台 API 密钥' }, { key: 'custom_api_url', value: '', group: 'platform', description: '自建平台 API 地址' }, { key: 'custom_api_key', value: '', group: 'platform', description: '自建平台 API 密钥' }, { key: 'upload_max_size', value: '20', group: 'system', description: '上传文件大小限制(MB)' }, { key: 'session_timeout', value: '120', group: 'system', description: '会话超时时间(分钟)' }, { key: 'maintenance_mode', value: 'false', group: 'system', description: '系统维护模式(true/false)' }, ] for (const cfg of defaultConfigs) { await prisma.systemConfig.upsert({ where: { key: cfg.key }, update: {}, create: cfg, }) } // Default plan config await prisma.systemConfig.upsert({ where: { key: 'plan_config' }, update: {}, create: { key: 'plan_config', value: JSON.stringify({ basic: { label: '基础版', price: 3000, description: '小型团队起步', features: { inquiry: true, imageGen: false, product: true, comparison: false }, quotas: { monthlyLeads: 100, monthlyImages: 0, monthlyProducts: 100, monthlyComparisons: 0, maxUsers: 5 }, platformCount: 1, supportLevel: '社区支持', }, pro: { label: '专业版', price: 6000, description: '中型企业', features: { inquiry: true, imageGen: true, product: true, comparison: true }, quotas: { monthlyLeads: 500, monthlyImages: 50, monthlyProducts: 500, monthlyComparisons: 100, maxUsers: 20 }, platformCount: 3, supportLevel: '标准支持', }, enterprise: { label: '旗舰版', price: 9000, description: '大型企业/多租户', features: { inquiry: true, imageGen: true, product: true, comparison: true }, quotas: { monthlyLeads: 99999, monthlyImages: 99999, monthlyProducts: 99999, monthlyComparisons: 99999, maxUsers: 99999 }, platformCount: 999, supportLevel: '优先技术支持', }, }), group: 'system', description: '订阅方案配置(JSON)', }, }) console.log('Seed data created') const categories = [ { name: '防火门', keywords: ['防火门', '防火卷帘', '防火窗', '钢质防火', '木质防火'] }, { name: '配电箱', keywords: ['配电箱', '配电柜', '配电盘', '照明配电箱', '动力配电箱', '电表箱'] }, { name: '电线电缆', keywords: ['电线', '电缆', '导线', '电力线', '控制电缆', '光缆', '架空线'] }, { name: '阀门', keywords: ['阀门', '闸阀', '球阀', '蝶阀', '截止阀', '止回阀', '调节阀'] }, { name: '钢材', keywords: ['钢材', '钢管', '钢板', '钢筋', '型钢', '槽钢', '角钢', '工字钢', 'H型钢', '螺纹钢'] }, { name: '涂料', keywords: ['涂料', '油漆', '防腐漆', '防火涂料', '乳胶漆', '防水涂料'] }, { name: '混凝土', keywords: ['混凝土', '商混', '商品砼', '水泥', '砂浆', '砼'] }, { name: '变压器', keywords: ['变压器', '箱变', '干式变', '油浸变', '电力变压器'] }, { name: '水泵', keywords: ['水泵', '离心泵', '潜污泵', '消防泵', '排污泵', '循环泵'] }, { name: '风机', keywords: ['风机', '通风机', '排风机', '轴流风机', '离心风机'] }, { name: '管材', keywords: ['管材', '管道', 'PVC管', 'PE管', '镀锌管', '无缝管', '螺旋管', '给水管'] }, { name: '开关', keywords: ['开关', '断路器', '接触器', '继电器', '熔断器', '隔离开关'] }, { name: '灯具', keywords: ['灯具', '照明灯', '路灯', '防爆灯', 'LED灯', '太阳能灯'] }, { name: '仪表', keywords: ['仪表', '仪器', '传感器', '变送器', '流量计', '压力表', '温度计'] }, { name: '电机', keywords: ['电机', '电动机', '减速机', '减速器', '变频电机'] }, ] for (const cat of categories) { await prisma.productCategory.upsert({ where: { name: cat.name }, update: { keywords: JSON.stringify(cat.keywords) }, create: { name: cat.name, keywords: JSON.stringify(cat.keywords) }, }) } console.log('ProductCategory seed data created') } main() .catch((e) => console.error(e)) .finally(() => prisma.$disconnect())