import { PrismaClient } from '@prisma/client'; import * as bcrypt from 'bcryptjs'; const prisma = new PrismaClient(); async function main() { const tenant = await prisma.tenant.upsert({ where: { code: 'default' }, update: {}, create: { name: '默认租户', code: 'default' }, }); const china = await prisma.region.upsert({ where: { code: '100000' }, update: {}, create: { code: '100000', name: '全国', level: 0, longitude: 104.195397, latitude: 35.86166 }, }); const province = await prisma.region.upsert({ where: { code: '110000' }, update: {}, create: { code: '110000', name: '北京市', level: 1, parentId: china.id, longitude: 116.407526, latitude: 39.90403 }, }); const city = await prisma.region.upsert({ where: { code: '110100' }, update: {}, create: { code: '110100', name: '北京市', level: 2, parentId: province.id, longitude: 116.407526, latitude: 39.90403 }, }); const district = await prisma.region.upsert({ where: { code: '110101' }, update: {}, create: { code: '110101', name: '东城区', level: 3, parentId: city.id, longitude: 116.416357, latitude: 39.928353 }, }); const shanghai = await prisma.region.upsert({ where: { code: '310000' }, update: {}, create: { code: '310000', name: '上海市', level: 1, parentId: china.id, longitude: 121.473701, latitude: 31.230416 }, }); const shanghaiCity = await prisma.region.upsert({ where: { code: '310100' }, update: {}, create: { code: '310100', name: '上海市', level: 2, parentId: shanghai.id, longitude: 121.473701, latitude: 31.230416 }, }); const pudong = await prisma.region.upsert({ where: { code: '310115' }, update: {}, create: { code: '310115', name: '浦东新区', level: 3, parentId: shanghaiCity.id, longitude: 121.544346, latitude: 31.221461 }, }); const guangdong = await prisma.region.upsert({ where: { code: '440000' }, update: {}, create: { code: '440000', name: '广东省', level: 1, parentId: china.id, longitude: 113.266887, latitude: 23.133306 }, }); const guangzhou = await prisma.region.upsert({ where: { code: '440100' }, update: {}, create: { code: '440100', name: '广州市', level: 2, parentId: guangdong.id, longitude: 113.264385, latitude: 23.129112 }, }); const tianhe = await prisma.region.upsert({ where: { code: '440106' }, update: {}, create: { code: '440106', name: '天河区', level: 3, parentId: guangzhou.id, longitude: 113.361597, latitude: 23.124817 }, }); const zhejiang = await prisma.region.upsert({ where: { code: '330000' }, update: {}, create: { code: '330000', name: '浙江省', level: 1, parentId: china.id, longitude: 120.152575, latitude: 30.266619 }, }); const hangzhou = await prisma.region.upsert({ where: { code: '330100' }, update: {}, create: { code: '330100', name: '杭州市', level: 2, parentId: zhejiang.id, longitude: 120.15507, latitude: 30.274085 }, }); const xihu = await prisma.region.upsert({ where: { code: '330106' }, update: {}, create: { code: '330106', name: '西湖区', level: 3, parentId: hangzhou.id, longitude: 120.130396, latitude: 30.259242 }, }); const permissions = [ ['tenants:manage', '租户管理', 'MENU', '/system/tenants'], ['dashboard:view', '数据看板', 'MENU', '/dashboard'], ['map:view', '地图监控', 'MENU', '/map'], ['lamps:manage', '路灯管理', 'MENU', '/lamps'], ['alarms:manage', '报警管理', 'MENU', '/alarms'], ['system:manage', '系统管理', 'MENU', '/system/users'], ] as const; for (const [code, name, type, path] of permissions) { await prisma.permission.upsert({ where: { code }, update: {}, create: { code, name, type, path }, }); } let platformRole = await prisma.role.findFirst({ where: { tenantId: null, code: 'platform_super_admin' } }); if (!platformRole) { platformRole = await prisma.role.create({ data: { name: '平台超级管理员', code: 'platform_super_admin', description: '仅用于平台租户管理,不在页面中配置', }, }); } const tenantManagePermission = await prisma.permission.findUnique({ where: { code: 'tenants:manage' } }); if (tenantManagePermission) { await prisma.rolePermission.createMany({ data: [{ roleId: platformRole.id, permissionId: tenantManagePermission.id }], skipDuplicates: true, }); } let platformAdmin = await prisma.user.findFirst({ where: { tenantId: null, username: 'admin' } }); if (!platformAdmin) { platformAdmin = await prisma.user.create({ data: { username: 'admin', passwordHash: await bcrypt.hash('admin123456', 10), name: '平台超级管理员', roles: { create: [{ roleId: platformRole.id }] }, }, }); } let role = await prisma.role.findFirst({ where: { tenantId: tenant.id, code: { in: ['tenant_admin', 'super_admin'] } } }); if (role) { role = await prisma.role.update({ where: { id: role.id }, data: { name: '租户管理员', code: 'tenant_admin', description: '租户初始化管理员角色' }, }); } else { role = await prisma.role.create({ data: { name: '租户管理员', code: 'tenant_admin', description: '租户初始化管理员角色', tenantId: tenant.id }, }); } const allPermissions = await prisma.permission.findMany({ where: { code: { not: 'tenants:manage' } } }); await prisma.rolePermission.createMany({ data: allPermissions.map((permission) => ({ roleId: role.id, permissionId: permission.id })), skipDuplicates: true, }); await prisma.roleRegionScope.createMany({ data: [{ roleId: role.id, regionId: china.id }], skipDuplicates: true, }); let admin = await prisma.user.findFirst({ where: { tenantId: tenant.id, username: 'admin' } }); if (admin) { admin = await prisma.user.update({ where: { id: admin.id }, data: { name: '系统管理员' }, }); } else { admin = await prisma.user.create({ data: { username: 'admin', passwordHash: await bcrypt.hash('admin123456', 10), name: '系统管理员', tenantId: tenant.id, roles: { create: [{ roleId: role.id }] }, regionScopes: { create: [{ regionId: china.id }] }, }, }); } const sampleLamps = [ ['BJ-DC-0001', '东城区智慧路灯 A01', district.id, '北京市东城区东长安街', 116.416357, 39.928353, '单灯', 'NORMAL', 'ONLINE'], ['BJ-DC-0002', '东城区智慧路灯 A02', district.id, '北京市东城区王府井大街', 116.411568, 39.908823, '单灯', 'FAULT', 'ONLINE'], ['BJ-DC-0003', '东城区智慧路灯 A03', district.id, '北京市东城区朝阳门内大街', 116.433235, 39.924463, '单灯', 'MAINTENANCE', 'OFFLINE'], ['SH-PD-0001', '浦东新区智慧路灯 P01', pudong.id, '上海市浦东新区世纪大道', 121.544346, 31.221461, '单灯', 'NORMAL', 'ONLINE'], ['SH-PD-0002', '浦东新区智慧路灯 P02', pudong.id, '上海市浦东新区陆家嘴环路', 121.502833, 31.239101, '单灯', 'FAULT', 'ONLINE'], ['GZ-TH-0001', '天河区智慧路灯 T01', tianhe.id, '广州市天河区体育西路', 113.321478, 23.132191, '单灯', 'NORMAL', 'ONLINE'], ['GZ-TH-0002', '天河区智慧路灯 T02', tianhe.id, '广州市天河区珠江新城', 113.32459, 23.119962, '单灯', 'OFFLINE', 'OFFLINE'], ['HZ-XH-0001', '西湖区智慧路灯 X01', xihu.id, '杭州市西湖区文三路', 120.130396, 30.279242, '单灯', 'NORMAL', 'ONLINE'], ['HZ-XH-0002', '西湖区智慧路灯 X02', xihu.id, '杭州市西湖区曙光路', 120.14321, 30.26311, '单灯', 'FAULT', 'ONLINE'], ] as const; const lampMap = new Map(); for (const [code, name, regionId, address, longitude, latitude, deviceType, status, onlineStatus] of sampleLamps) { const lamp = await prisma.streetLamp.upsert({ where: { code }, update: { name, regionId, address, longitude, latitude, deviceType, status, onlineStatus }, create: { code, name, tenantId: tenant.id, regionId, address, longitude, latitude, deviceType, status, onlineStatus, lastReportedAt: onlineStatus === 'ONLINE' ? new Date(Date.now() - Math.random() * 1000 * 60 * 45) : undefined, }, }); lampMap.set(code, { id: lamp.id, regionId: lamp.regionId }); } const sampleAlarms = [ ['sample-alarm-001', 'BJ-DC-0001', 'POWER_FAILURE', '路灯断电报警', '设备供电异常,请尽快排查配电箱。', 'HIGH', 'OPEN', 18], ['sample-alarm-002', 'BJ-DC-0002', 'LAMP_FAULT', '灯具故障报警', '灯具电流异常,疑似光源损坏。', 'MEDIUM', 'PROCESSING', 65], ['sample-alarm-003', 'SH-PD-0002', 'DOOR_OPEN', '灯杆箱门开启', '箱门异常开启,存在安全风险。', 'CRITICAL', 'OPEN', 12], ['sample-alarm-004', 'GZ-TH-0002', 'OFFLINE', '设备离线报警', '设备超过 30 分钟未上报数据。', 'HIGH', 'OPEN', 42], ['sample-alarm-005', 'HZ-XH-0002', 'VOLTAGE_ABNORMAL', '电压异常报警', '输入电压波动超过阈值。', 'MEDIUM', 'PROCESSING', 95], ['sample-alarm-006', 'SH-PD-0001', 'BRIGHTNESS_LOW', '亮度不足报警', '夜间亮度低于策略要求。', 'LOW', 'RESOLVED', 240], ] as const; for (const [sourceAlarmId, lampCode, type, title, description, level, status, minutesAgo] of sampleAlarms) { const lamp = lampMap.get(lampCode); if (!lamp) { continue; } await prisma.alarm.upsert({ where: { sourceAlarmId }, update: { status }, create: { sourceAlarmId, tenantId: tenant.id, lampId: lamp.id, regionId: lamp.regionId, type, title, description, level, status, occurredAt: new Date(Date.now() - minutesAgo * 60 * 1000), recoveredAt: status === 'RESOLVED' ? new Date(Date.now() - 60 * 60 * 1000) : undefined, events: { create: [{ eventType: 'CREATED', payload: { seededBy: platformAdmin.username, tenantAdmin: admin.username } }] }, }, }); } } main() .then(async () => prisma.$disconnect()) .catch(async (error) => { console.error(error); await prisma.$disconnect(); process.exit(1); });