Files
yunrui_asset/backend/scripts/seed-example-data.js
T
yi e9bff9beb3
Build and Push Docker Images / Build Backend Image (push) Has been cancelled
Build and Push Docker Images / Build Frontend Image (push) Has been cancelled
Build and Push Docker Images / Notify Build Complete (push) Has been cancelled
Update 2026-06-23 10:40:10
2026-06-23 10:40:10 +08:00

389 lines
14 KiB
JavaScript
Raw 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.
/**
* 示例数据种子脚本
* 为 云睿资产管理系统写入演示数据(幂等,可重复执行)
*
* 本地:node scripts/seed-example-data.js
* Dockerdocker exec yunrui_asset-backend node scripts/seed-example-data.js
*/
require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
const bcrypt = require('bcryptjs');
const { sequelize } = require('../db');
const Room = require('../models/Room');
const Rack = require('../models/Rack');
const Device = require('../models/Device');
const Business = require('../models/Business');
const DeviceBusiness = require('../models/DeviceBusiness');
const Warehouse = require('../models/Warehouse');
const User = require('../models/User');
const Role = require('../models/Role');
const UserRole = require('../models/UserRole');
const ConsumableCategory = require('../models/ConsumableCategory');
const Consumable = require('../models/Consumable');
const Ticket = require('../models/Ticket');
const SEED_MARKER_ROOM = '北京通州一号机房';
const DEMO_ADMIN = { username: 'admin', password: 'admin123', realName: '系统管理员', email: 'admin@example.com' };
const DEFAULT_ROLES = [
{
roleId: 'role_admin',
roleName: '管理员',
roleCode: 'admin',
description: '系统管理员,拥有所有权限',
permissions: ['*'],
status: 'active',
sort: 1,
},
{
roleId: 'role_operator',
roleName: '运维人员',
roleCode: 'operator',
description: '负责日常运维操作',
permissions: ['devices:read', 'devices:write', 'racks:read', 'rooms:read', 'consumables:read', 'consumables:write'],
status: 'active',
sort: 2,
},
{
roleId: 'role_viewer',
roleName: '只读用户',
roleCode: 'viewer',
description: '仅能查看数据',
permissions: ['devices:read', 'racks:read', 'rooms:read', 'consumables:read'],
status: 'active',
sort: 3,
},
];
async function upsertRole(roleData) {
const [role] = await Role.findOrCreate({ where: { roleCode: roleData.roleCode }, defaults: roleData });
if (role.roleId !== roleData.roleId) {
await role.update(roleData);
}
return role;
}
async function ensureAdminUser() {
for (const roleData of DEFAULT_ROLES) {
await upsertRole(roleData);
}
const adminRole = await Role.findOne({ where: { roleCode: 'admin' } });
const hashedPassword = await bcrypt.hash(DEMO_ADMIN.password, 10);
const [admin] = await User.findOrCreate({
where: { username: DEMO_ADMIN.username },
defaults: {
password: hashedPassword,
email: DEMO_ADMIN.email,
realName: DEMO_ADMIN.realName,
status: 'active',
},
});
const existingLink = await UserRole.findOne({ where: { UserId: admin.userId, RoleId: adminRole.roleId } });
if (!existingLink) {
await UserRole.create({ UserId: admin.userId, RoleId: adminRole.roleId });
}
return admin;
}
async function seedData() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
await sequelize.sync();
const marker = await Room.findOne({ where: { name: SEED_MARKER_ROOM } });
if (marker && process.env.SEED_FORCE !== '1') {
console.log(`示例数据已存在(机房: ${SEED_MARKER_ROOM}),跳过写入。`);
console.log('如需强制重新写入,请设置环境变量 SEED_FORCE=1');
const admin = await ensureAdminUser();
console.log(`演示账号: ${DEMO_ADMIN.username} / ${DEMO_ADMIN.password} (${admin.userId})`);
process.exit(0);
}
console.log('开始写入示例数据...\n');
const admin = await ensureAdminUser();
console.log(`✓ 角色与管理员账号 (${DEMO_ADMIN.username} / ${DEMO_ADMIN.password})`);
const [room1] = await Room.findOrCreate({
where: { name: SEED_MARKER_ROOM },
defaults: {
location: '北京市通州区马驹桥',
area: 500,
capacity: 100,
status: 'active',
description: '公司核心数据中心,具备双路供电及精密空调系统。',
gridRows: 8,
gridCols: 12,
},
});
const [room2] = await Room.findOrCreate({
where: { name: '上海浦东二号机房' },
defaults: {
location: '上海市浦东新区张江高科',
area: 320,
capacity: 60,
status: 'active',
description: '华东区域灾备机房,承载核心业务冗余部署。',
gridRows: 6,
gridCols: 10,
},
});
console.log(`✓ 机房: ${room1.name}, ${room2.name}`);
const rackDefs = [
{ rackId: 'RCK_SEED_A01', name: 'A-01', roomId: room1.roomId, height: 42, maxPower: 8000, rowPos: 1, colPos: 1 },
{ rackId: 'RCK_SEED_A02', name: 'A-02', roomId: room1.roomId, height: 42, maxPower: 8000, rowPos: 1, colPos: 2 },
{ rackId: 'RCK_SEED_B01', name: 'B-01', roomId: room1.roomId, height: 42, maxPower: 6000, rowPos: 2, colPos: 1 },
{ rackId: 'RCK_SEED_C01', name: 'C-01', roomId: room2.roomId, height: 42, maxPower: 8000, rowPos: 1, colPos: 1 },
];
const racks = {};
for (const def of rackDefs) {
const [rack] = await Rack.findOrCreate({ where: { rackId: def.rackId }, defaults: def });
racks[def.name] = rack;
}
console.log(`✓ 机柜: ${rackDefs.map(r => r.name).join(', ')}`);
const businessDefs = [
{ businessId: 'BIZ_SEED_001', name: '云端计算核心', description: '负责核心云端计算资源的分配与管理。' },
{ businessId: 'BIZ_SEED_002', name: '企业办公平台', description: 'OA、邮件、协作等企业内部应用。' },
{ businessId: 'BIZ_SEED_003', name: '数据分析服务', description: '大数据分析与报表平台。' },
];
const businesses = {};
for (const def of businessDefs) {
const [biz] = await Business.findOrCreate({ where: { businessId: def.businessId }, defaults: def });
businesses[def.businessId] = biz;
}
console.log(`✓ 业务: ${businessDefs.map(b => b.name).join(', ')}`);
const [warehouse] = await Warehouse.findOrCreate({
where: { name: '通州备件库房' },
defaults: {
location: '北京通州一号机房 B 区',
capacity: 200,
status: 'active',
description: '存放待上架及退役备件设备。',
},
});
console.log(`✓ 库房: ${warehouse.name}`);
const deviceDefs = [
{
deviceId: 'DEV_SEED_001',
name: 'Web-01',
type: 'server',
model: 'Dell PowerEdge R750',
serialNumber: 'SN-DELL-R750-001',
rackId: racks['A-01'].rackId,
position: 1,
height: 2,
powerConsumption: 450,
status: 'online',
ipAddress: '10.10.1.101',
description: '核心 Web 应用服务器',
businessId: 'BIZ_SEED_001',
},
{
deviceId: 'DEV_SEED_002',
name: 'DB-01',
type: 'server',
model: 'Dell PowerEdge R750',
serialNumber: 'SN-DELL-R750-002',
rackId: racks['A-01'].rackId,
position: 3,
height: 2,
powerConsumption: 520,
status: 'online',
ipAddress: '10.10.1.102',
description: 'MySQL 主库',
businessId: 'BIZ_SEED_001',
},
{
deviceId: 'DEV_SEED_003',
name: 'SW-Core-01',
type: 'switch',
model: 'Huawei CE6857',
serialNumber: 'SN-HW-CE6857-001',
rackId: racks['A-01'].rackId,
position: 40,
height: 1,
powerConsumption: 180,
status: 'online',
ipAddress: '10.10.0.1',
description: '核心交换机',
businessId: 'BIZ_SEED_001',
},
{
deviceId: 'DEV_SEED_004',
name: 'OA-App-01',
type: 'server',
model: 'Lenovo SR650',
serialNumber: 'SN-LENOVO-SR650-001',
rackId: racks['A-02'].rackId,
position: 5,
height: 2,
powerConsumption: 380,
status: 'online',
ipAddress: '10.20.1.50',
description: 'OA 应用服务器',
businessId: 'BIZ_SEED_002',
},
{
deviceId: 'DEV_SEED_005',
name: 'BI-Node-01',
type: 'server',
model: 'HPE ProLiant DL380',
serialNumber: 'SN-HPE-DL380-001',
rackId: racks['B-01'].rackId,
position: 10,
height: 2,
powerConsumption: 490,
status: 'maintenance',
ipAddress: '10.30.1.10',
description: '数据分析计算节点',
businessId: 'BIZ_SEED_003',
},
{
deviceId: 'DEV_SEED_006',
name: 'DR-Storage-01',
type: 'storage',
model: 'NetApp FAS2750',
serialNumber: 'SN-NETAPP-FAS2750-001',
rackId: racks['C-01'].rackId,
position: 20,
height: 4,
powerConsumption: 600,
status: 'online',
ipAddress: '10.40.1.20',
description: '灾备存储阵列',
businessId: 'BIZ_SEED_001',
},
{
deviceId: 'DEV_SEED_007',
name: 'Spare-R750',
type: 'server',
model: 'Dell PowerEdge R750',
serialNumber: 'SN-DELL-R750-SPARE',
warehouseId: warehouse.warehouseId,
sourceType: 'warehouse',
height: 2,
powerConsumption: 0,
status: 'offline',
description: '待上架备用服务器',
},
];
const devices = {};
for (const def of deviceDefs) {
const { businessId, ...deviceData } = def;
const [device] = await Device.findOrCreate({ where: { deviceId: def.deviceId }, defaults: deviceData });
devices[def.deviceId] = device;
if (businessId) {
await DeviceBusiness.findOrCreate({
where: { deviceId: def.deviceId, businessId },
defaults: { isPrimary: true },
});
}
}
console.log(`✓ 设备: ${deviceDefs.length} 台(含 1 台库房备件)`);
const consumableCategories = [
{ name: '线缆', description: '网络与电源线缆', sortOrder: 1 },
{ name: '光模块', description: 'SFP/SFP+/QSFP 光模块', sortOrder: 2 },
{ name: '硬盘', description: '企业级硬盘与 SSD', sortOrder: 3 },
];
for (const cat of consumableCategories) {
await ConsumableCategory.findOrCreate({ where: { name: cat.name }, defaults: cat });
}
const consumableDefs = [
{ consumableId: 'CON_SEED_001', name: '六类网线 3m', category: '线缆', unit: '根', currentStock: 120, minStock: 20, unitPrice: 15.0, location: '通州备件库房 A 架' },
{ consumableId: 'CON_SEED_002', name: 'SFP+ 10G 光模块', category: '光模块', unit: '个', currentStock: 32, minStock: 10, unitPrice: 280.0, location: '通州备件库房 B 架' },
{ consumableId: 'CON_SEED_003', name: '1.92TB SSD', category: '硬盘', unit: '块', currentStock: 8, minStock: 5, unitPrice: 1800.0, location: '通州备件库房 C 架' },
];
for (const def of consumableDefs) {
await Consumable.findOrCreate({ where: { consumableId: def.consumableId }, defaults: def });
}
console.log(`✓ 耗材: ${consumableDefs.length} 种`);
const ticketDefs = [
{
ticketId: 'TKT_SEED_001',
title: 'DB-01 磁盘 IO 延迟偏高',
deviceId: 'DEV_SEED_002',
deviceName: 'DB-01',
deviceModel: 'Dell PowerEdge R750',
serialNumber: 'SN-DELL-R750-002',
faultCategory: '性能问题',
priority: 'high',
status: 'in_progress',
description: '数据库主库磁盘 IO 等待时间持续超过 50ms,需排查存储性能。',
reporterId: admin.userId,
reporterName: admin.realName,
assigneeId: admin.userId,
assigneeName: admin.realName,
location: '北京通州一号机房 / A-01',
},
{
ticketId: 'TKT_SEED_002',
title: 'BI-Node-01 计划维护',
deviceId: 'DEV_SEED_005',
deviceName: 'BI-Node-01',
deviceModel: 'HPE ProLiant DL380',
serialNumber: 'SN-HPE-DL380-001',
faultCategory: '例行维护',
priority: 'low',
status: 'pending',
description: '月度固件升级与内存巡检。',
reporterId: admin.userId,
reporterName: admin.realName,
location: '北京通州一号机房 / B-01',
},
{
ticketId: 'TKT_SEED_003',
title: '核心交换机端口异常',
deviceId: 'DEV_SEED_003',
deviceName: 'SW-Core-01',
deviceModel: 'Huawei CE6857',
serialNumber: 'SN-HW-CE6857-001',
faultCategory: '网络故障',
priority: 'critical',
status: 'completed',
description: 'Gi0/0/24 端口频繁 flapping,已更换光模块。',
reporterId: admin.userId,
reporterName: admin.realName,
assigneeId: admin.userId,
assigneeName: admin.realName,
location: '北京通州一号机房 / A-01',
resolution: '更换 SFP+ 光模块后端口恢复正常。',
completionDate: new Date(),
},
];
for (const def of ticketDefs) {
await Ticket.findOrCreate({ where: { ticketId: def.ticketId }, defaults: def });
}
console.log(`✓ 工单: ${ticketDefs.length} 条`);
console.log('\n示例数据写入完成!');
console.log('────────────────────────────────────');
console.log(`登录账号: ${DEMO_ADMIN.username}`);
console.log(`登录密码: ${DEMO_ADMIN.password}`);
console.log(`前端地址: http://localhost:${process.env.FRONTEND_PORT || 12000}`);
console.log('────────────────────────────────────');
process.exit(0);
} catch (error) {
console.error('示例数据写入失败:', error);
process.exit(1);
}
}
seedData();