platform-assistant/apps/api/prisma/seed.ts
Sisyphus afac18e843 feat(F002): W26 + W29.x + W30 + W31 平台助手全部改动首次 commit
- W26: NestJS API + Next.js Web + Prisma + SQLite scaffold
- W26: Lead 模型 + Crawler + 询价发布助手 (线索池 + 一键发布)
- W29.x: MCP 集成 (Integration 模型 + connectMcp + publish_rfq 5 工具端到端跑通)
- W30: 3 字段补充 (notes / quotationType / quotationDemand)
- W31 P1: 自动填 enterpriseId / userId (Prisma companyUserId 字段 + get_app_identity tool + lead.service.ts publishArgs)
- W31: detail 详情页 + admin UI 集成管理 (OpenAppListView + AuditLogListView)

关联: ~/Documents/02-平台开发/产品开发/F002-W31-交接文档.md
GIT-AUDIT-REPORT-2026-06-24.md
2026-06-24 11:07:11 +08:00

270 lines
16 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 8 * * 1-5', totalRuns: 286, successRuns: 275, failedRuns: 11, keywords: '["电缆","防火门","配电箱","钢材"]' },
{ name: '新矿集团供应链平台', targetUrl: 'https://xkgroup.kdcloud.com', type: 'bid_xkgroup', sourceName: '新矿集团', scriptPath: 'fetch-xkgroup.py', status: 'active', schedule: '5 8 * * 1-5', 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')
}
main()
.catch((e) => console.error(e))
.finally(() => prisma.$disconnect())