refactor: 统一代码风格并迁移至 ESLint 新配置

style(backend): 格式化模型文件代码
style(frontend): 调整组件代码格式
chore: 删除旧 ESLint 配置并添加新配置
refactor(backend): 重构模型定义语法
style: 统一箭头函数和对象属性简写
This commit is contained in:
zhang1106
2026-03-27 19:12:16 +08:00
parent 6a8d4144ff
commit 63f0cb570e
166 changed files with 15483 additions and 11170 deletions
+134 -113
View File
@@ -28,73 +28,73 @@ const migrations = [
{
name: 'v2.0 - 网卡和端口表',
description: '创建 network_cards 表,为 device_ports 添加 nic_id 字段',
migrate: migrateV2
migrate: migrateV2,
},
{
name: '用户表 pending 状态',
description: '为用户表添加 pending 状态支持',
migrate: migratePendingStatus
migrate: migratePendingStatus,
},
{
name: '耗材乐观锁',
description: '为 consumables 表添加 version 字段',
migrate: migrateConsumableVersion
migrate: migrateConsumableVersion,
},
{
name: '耗材操作日志表结构',
description: '添加 isEditable、originalLogId 等修改记录字段',
migrate: migrateConsumableLogs
migrate: migrateConsumableLogs,
},
{
name: '耗材日志解耦',
description: '添加 isConsumableDeleted 和 consumableSnapshot 字段',
migrate: migrateConsumableLogDecouple
migrate: migrateConsumableLogDecouple,
},
{
name: '移除日志外键约束',
description: '移除 consumable_logs 表的外键约束,防止级联删除',
migrate: removeConsumableLogFK
migrate: removeConsumableLogFK,
},
{
name: '耗材日志归档表',
description: '创建 consumable_log_archives 归档表',
migrate: migrateConsumableLogArchive
migrate: migrateConsumableLogArchive,
},
{
name: '耗材SN序列号字段',
description: '为 consumables、consumable_records、consumable_logs 添加 snList 字段',
migrate: migrateSnList
migrate: migrateSnList,
},
{
name: '设备型号字段可空',
description: '将 devices 表 model 字段改为可空,支持非必填',
migrate: migrateDeviceModelField
migrate: migrateDeviceModelField,
},
{
name: '设备字段配置同步',
description: '同步前后端字段必填配置',
migrate: migrateDeviceFieldsConfig
migrate: migrateDeviceFieldsConfig,
},
{
name: '设备表字段可空',
description: '将设备表所有字段改为可空,由应用层验证控制',
migrate: migrateDeviceFieldsNullable
migrate: migrateDeviceFieldsNullable,
},
{
name: '暂存设备自定义字段',
description: '为 pending_devices 表添加 customFields 字段,支持自定义字段存储',
migrate: migratePendingDeviceCustomFields
migrate: migratePendingDeviceCustomFields,
},
{
name: '空闲设备与业务关联',
description: '创建 businesses、warehouses、device_business 表,为 devices 添加空闲设备字段',
migrate: migrateIdleDeviceAndBusiness
migrate: migrateIdleDeviceAndBusiness,
},
{
name: '设备字段系统标记',
description: '为 deviceFields 表添加 isSystem 字段,标记系统字段不可删除',
migrate: migrateDeviceFieldsIsSystem
}
migrate: migrateDeviceFieldsIsSystem,
},
];
async function runMigrations() {
@@ -149,25 +149,23 @@ async function runMigrations() {
async function getTableColumns(tableName) {
const dialect = sequelize.getDialect();
if (dialect === 'sqlite') {
const tableInfo = await sequelize.query(
`PRAGMA table_info(${tableName})`,
{ type: sequelize.QueryTypes.SELECT }
);
const tableInfo = await sequelize.query(`PRAGMA table_info(${tableName})`, {
type: sequelize.QueryTypes.SELECT,
});
return tableInfo.map(col => col.name);
} else {
const tableInfo = await sequelize.query(
`SHOW COLUMNS FROM ${tableName}`,
{ type: sequelize.QueryTypes.SELECT }
);
const tableInfo = await sequelize.query(`SHOW COLUMNS FROM ${tableName}`, {
type: sequelize.QueryTypes.SELECT,
});
return tableInfo.map(col => col.Field);
}
}
async function tableExists(tableName) {
const dialect = sequelize.getDialect();
if (dialect === 'sqlite') {
const tables = await sequelize.query(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
@@ -175,22 +173,23 @@ async function tableExists(tableName) {
);
return tables.length > 0;
} else {
const tables = await sequelize.query(
"SHOW TABLES LIKE ?",
{ replacements: [tableName], type: sequelize.QueryTypes.SELECT }
);
const tables = await sequelize.query('SHOW TABLES LIKE ?', {
replacements: [tableName],
type: sequelize.QueryTypes.SELECT,
});
return tables.length > 0;
}
}
async function addColumnIfNotExists(tableName, columnName, columnDef) {
const columns = await getTableColumns(tableName);
if (!columns.includes(columnName)) {
const dialect = sequelize.getDialect();
const sql = dialect === 'sqlite'
? `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`
: `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`;
const sql =
dialect === 'sqlite'
? `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`
: `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`;
await sequelize.query(sql);
console.log(` ${tableName} 表添加 ${columnName} 字段成功`);
} else {
@@ -210,32 +209,32 @@ async function migrateV2() {
id: {
type: sequelize.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
autoIncrement: true,
},
nicId: {
type: sequelize.Sequelize.STRING,
allowNull: false,
unique: true
unique: true,
},
name: {
type: sequelize.Sequelize.STRING,
allowNull: false
allowNull: false,
},
macAddress: {
type: sequelize.Sequelize.STRING
type: sequelize.Sequelize.STRING,
},
ipAddress: {
type: sequelize.Sequelize.STRING
type: sequelize.Sequelize.STRING,
},
deviceId: {
type: sequelize.Sequelize.STRING
type: sequelize.Sequelize.STRING,
},
status: {
type: sequelize.Sequelize.STRING,
defaultValue: 'active'
defaultValue: 'active',
},
createdAt: sequelize.Sequelize.DATE,
updatedAt: sequelize.Sequelize.DATE
updatedAt: sequelize.Sequelize.DATE,
});
}
@@ -276,12 +275,11 @@ async function migrateConsumableLogDecouple() {
async function removeConsumableLogFK() {
const dialect = sequelize.getDialect();
if (dialect === 'sqlite') {
const fks = await sequelize.query(
`PRAGMA foreign_key_list(consumable_logs);`,
{ type: sequelize.QueryTypes.SELECT }
);
const fks = await sequelize.query(`PRAGMA foreign_key_list(consumable_logs);`, {
type: sequelize.QueryTypes.SELECT,
});
if (!fks || fks.length === 0) {
return;
@@ -327,7 +325,9 @@ async function removeConsumableLogFK() {
await sequelize.query(`CREATE INDEX idx_logs_consumable_id ON consumable_logs(consumableId)`);
await sequelize.query(`CREATE INDEX idx_logs_operation_type ON consumable_logs(operationType)`);
await sequelize.query(`CREATE INDEX idx_logs_created_at ON consumable_logs(createdAt)`);
await sequelize.query(`CREATE INDEX idx_logs_is_consumable_deleted ON consumable_logs(isConsumableDeleted)`);
await sequelize.query(
`CREATE INDEX idx_logs_is_consumable_deleted ON consumable_logs(isConsumableDeleted)`
);
}
}
@@ -341,80 +341,86 @@ async function migrateConsumableLogArchive() {
id: {
type: sequelize.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
autoIncrement: true,
},
archiveId: {
type: sequelize.Sequelize.STRING,
allowNull: false,
unique: true
unique: true,
},
consumableId: {
type: sequelize.Sequelize.STRING,
allowNull: false
allowNull: false,
},
consumableName: {
type: sequelize.Sequelize.STRING,
allowNull: false
allowNull: false,
},
consumableSnapshot: {
type: sequelize.Sequelize.TEXT
type: sequelize.Sequelize.TEXT,
},
totalOperations: {
type: sequelize.Sequelize.INTEGER,
defaultValue: 0
defaultValue: 0,
},
firstOperationAt: {
type: sequelize.Sequelize.DATE
type: sequelize.Sequelize.DATE,
},
lastOperationAt: {
type: sequelize.Sequelize.DATE
type: sequelize.Sequelize.DATE,
},
totalInQuantity: {
type: sequelize.Sequelize.INTEGER,
defaultValue: 0
defaultValue: 0,
},
totalOutQuantity: {
type: sequelize.Sequelize.INTEGER,
defaultValue: 0
defaultValue: 0,
},
finalStock: {
type: sequelize.Sequelize.INTEGER,
defaultValue: 0
defaultValue: 0,
},
deletedBy: {
type: sequelize.Sequelize.STRING
type: sequelize.Sequelize.STRING,
},
deletedAt: {
type: sequelize.Sequelize.DATE
type: sequelize.Sequelize.DATE,
},
deleteReason: {
type: sequelize.Sequelize.STRING
type: sequelize.Sequelize.STRING,
},
createdAt: {
type: sequelize.Sequelize.DATE,
allowNull: false,
defaultValue: sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
defaultValue: sequelize.Sequelize.literal('CURRENT_TIMESTAMP'),
},
updatedAt: {
type: sequelize.Sequelize.DATE,
allowNull: false,
defaultValue: sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
}
defaultValue: sequelize.Sequelize.literal('CURRENT_TIMESTAMP'),
},
});
if (dbDialect === 'sqlite') {
await sequelize.query(`CREATE INDEX idx_archive_consumable_id ON consumable_log_archives(consumableId)`);
await sequelize.query(`CREATE INDEX idx_archive_archive_id ON consumable_log_archives(archiveId)`);
await sequelize.query(`CREATE INDEX idx_archive_deleted_at ON consumable_log_archives(deletedAt)`);
await sequelize.query(
`CREATE INDEX idx_archive_consumable_id ON consumable_log_archives(consumableId)`
);
await sequelize.query(
`CREATE INDEX idx_archive_archive_id ON consumable_log_archives(archiveId)`
);
await sequelize.query(
`CREATE INDEX idx_archive_deleted_at ON consumable_log_archives(deletedAt)`
);
}
}
async function migrateSnList() {
const tables = ['consumables', 'consumable_records', 'consumable_logs'];
for (const table of tables) {
if (await tableExists(table)) {
const columnDef = dbDialect === 'sqlite' ? "TEXT DEFAULT '[]'" : "JSON";
const columnDef = dbDialect === 'sqlite' ? "TEXT DEFAULT '[]'" : 'JSON';
await addColumnIfNotExists(table, 'snList', columnDef);
} else {
console.log(` ${table} 表不存在,跳过`);
@@ -429,11 +435,9 @@ async function migrateDeviceModelField() {
}
const dialect = sequelize.getDialect();
if (dialect === 'mysql') {
await sequelize.query(
'ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL'
);
await sequelize.query('ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL');
console.log(' devices 表 model 字段已改为可空');
} else if (dialect === 'sqlite') {
const columns = await getTableColumns('devices');
@@ -441,7 +445,7 @@ async function migrateDeviceModelField() {
console.log(' model_old 字段已存在,跳过迁移');
return;
}
await sequelize.query('ALTER TABLE devices RENAME COLUMN model TO model_old');
await sequelize.query('ALTER TABLE devices ADD COLUMN model VARCHAR(255)');
await sequelize.query('UPDATE devices SET model = model_old');
@@ -452,14 +456,14 @@ async function migrateDeviceModelField() {
async function migrateDeviceFieldsConfig() {
const DeviceField = require('../models/DeviceField');
const updates = [
{ fieldName: 'model', required: false },
{ fieldName: 'powerConsumption', required: true },
{ fieldName: 'purchaseDate', required: false },
{ fieldName: 'warrantyExpiry', required: false },
];
for (const update of updates) {
const field = await DeviceField.findOne({ where: { fieldName: update.fieldName } });
if (field && field.required !== update.required) {
@@ -475,20 +479,20 @@ async function migrateDeviceFieldsConfig() {
async function migrateDeviceFieldsNullable() {
const dialect = sequelize.getDialect();
if (dialect === 'mysql') {
const alterCommands = [
"ALTER TABLE devices MODIFY COLUMN name VARCHAR(255) NULL",
"ALTER TABLE devices MODIFY COLUMN type VARCHAR(255) NULL",
"ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL",
"ALTER TABLE devices MODIFY COLUMN serialNumber VARCHAR(255) NULL",
"ALTER TABLE devices MODIFY COLUMN rackId VARCHAR(255) NULL",
"ALTER TABLE devices MODIFY COLUMN position INTEGER NULL",
"ALTER TABLE devices MODIFY COLUMN height INTEGER NULL",
"ALTER TABLE devices MODIFY COLUMN powerConsumption FLOAT NULL",
"ALTER TABLE devices MODIFY COLUMN customFields JSON NULL"
'ALTER TABLE devices MODIFY COLUMN name VARCHAR(255) NULL',
'ALTER TABLE devices MODIFY COLUMN type VARCHAR(255) NULL',
'ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL',
'ALTER TABLE devices MODIFY COLUMN serialNumber VARCHAR(255) NULL',
'ALTER TABLE devices MODIFY COLUMN rackId VARCHAR(255) NULL',
'ALTER TABLE devices MODIFY COLUMN position INTEGER NULL',
'ALTER TABLE devices MODIFY COLUMN height INTEGER NULL',
'ALTER TABLE devices MODIFY COLUMN powerConsumption FLOAT NULL',
'ALTER TABLE devices MODIFY COLUMN customFields JSON NULL',
];
for (const sql of alterCommands) {
try {
await sequelize.query(sql);
@@ -499,21 +503,20 @@ async function migrateDeviceFieldsNullable() {
}
}
console.log(' devices 表字段已改为可空');
} else if (dialect === 'sqlite') {
const columns = await getTableColumns('devices');
const hasNullableFlag = columns.includes('_nullable_migration_done');
if (hasNullableFlag) {
console.log(' 已完成可空迁移,跳过');
return;
}
await sequelize.query('PRAGMA foreign_keys = OFF');
try {
await sequelize.query('DROP TABLE IF EXISTS devices_new');
await sequelize.query(`
CREATE TABLE devices_new (
deviceId VARCHAR(255) PRIMARY KEY NOT NULL UNIQUE,
@@ -536,7 +539,7 @@ async function migrateDeviceFieldsNullable() {
_nullable_migration_done INTEGER DEFAULT 1
)
`);
await sequelize.query(`
INSERT INTO devices_new (
deviceId, name, type, model, serialNumber, rackId, position, height,
@@ -549,15 +552,15 @@ async function migrateDeviceFieldsNullable() {
description, customFields, createdAt, updatedAt
FROM devices
`);
await sequelize.query('DROP TABLE devices');
await sequelize.query('ALTER TABLE devices_new RENAME TO devices');
await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_status ON devices(status)');
await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_type ON devices(type)');
await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_rackId ON devices(rackId)');
await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_name ON devices(name)');
console.log(' devices 表字段已改为可空');
} finally {
await sequelize.query('PRAGMA foreign_keys = ON');
@@ -571,7 +574,7 @@ async function migratePendingDeviceCustomFields() {
return;
}
const columnDef = dbDialect === 'sqlite' ? "JSON DEFAULT '{}'" : "JSON";
const columnDef = dbDialect === 'sqlite' ? "JSON DEFAULT '{}'" : 'JSON';
await addColumnIfNotExists('pending_devices', 'customFields', columnDef);
}
@@ -605,10 +608,9 @@ async function migrateDeviceFieldsIsSystem() {
} else {
const columns = await getTableColumns('deviceFields');
if (!columns.includes('isSystem')) {
await sequelize.query(
"ALTER TABLE deviceFields ADD COLUMN isSystem BOOLEAN DEFAULT 0",
{ type: sequelize.QueryTypes.RAW }
);
await sequelize.query('ALTER TABLE deviceFields ADD COLUMN isSystem BOOLEAN DEFAULT 0', {
type: sequelize.QueryTypes.RAW,
});
console.log(' deviceFields 表添加 isSystem 字段成功');
} else {
console.log(' deviceFields 表 isSystem 字段已存在,跳过');
@@ -616,16 +618,25 @@ async function migrateDeviceFieldsIsSystem() {
}
const systemFields = [
'deviceId', 'name', 'type', 'model', 'serialNumber',
'rackId', 'position', 'height', 'powerConsumption',
'status', 'purchaseDate', 'warrantyExpiry'
'deviceId',
'name',
'type',
'model',
'serialNumber',
'rackId',
'position',
'height',
'powerConsumption',
'status',
'purchaseDate',
'warrantyExpiry',
];
for (const fieldName of systemFields) {
await sequelize.query(
`UPDATE deviceFields SET isSystem = 1 WHERE fieldName = ?`,
{ replacements: [fieldName], type: sequelize.QueryTypes.RAW }
);
await sequelize.query(`UPDATE deviceFields SET isSystem = 1 WHERE fieldName = ?`, {
replacements: [fieldName],
type: sequelize.QueryTypes.RAW,
});
console.log(` 标记系统字段: ${fieldName}`);
}
@@ -643,14 +654,19 @@ async function migrateIdleDeviceAndBusiness() {
try {
if (!(await tableExists('businesses'))) {
await queryInterface.createTable('businesses', {
businessId: { type: sequelize.Sequelize.STRING, primaryKey: true, allowNull: false, unique: true },
businessId: {
type: sequelize.Sequelize.STRING,
primaryKey: true,
allowNull: false,
unique: true,
},
name: { type: sequelize.Sequelize.STRING, allowNull: false },
description: { type: sequelize.Sequelize.TEXT },
status: { type: sequelize.Sequelize.ENUM('active', 'offline'), defaultValue: 'active' },
offlineDate: { type: sequelize.Sequelize.DATE },
offlineReason: { type: sequelize.Sequelize.STRING },
createdAt: { type: sequelize.Sequelize.DATE, allowNull: false },
updatedAt: { type: sequelize.Sequelize.DATE, allowNull: false }
updatedAt: { type: sequelize.Sequelize.DATE, allowNull: false },
});
console.log(' businesses 表创建成功');
} else {
@@ -659,14 +675,19 @@ async function migrateIdleDeviceAndBusiness() {
if (!(await tableExists('warehouses'))) {
await queryInterface.createTable('warehouses', {
warehouseId: { type: sequelize.Sequelize.STRING, primaryKey: true, allowNull: false, unique: true },
warehouseId: {
type: sequelize.Sequelize.STRING,
primaryKey: true,
allowNull: false,
unique: true,
},
name: { type: sequelize.Sequelize.STRING, allowNull: false },
location: { type: sequelize.Sequelize.STRING },
capacity: { type: sequelize.Sequelize.INTEGER, defaultValue: 100 },
status: { type: sequelize.Sequelize.ENUM('active', 'inactive'), defaultValue: 'active' },
description: { type: sequelize.Sequelize.TEXT },
createdAt: { type: sequelize.Sequelize.DATE, allowNull: false },
updatedAt: { type: sequelize.Sequelize.DATE, allowNull: false }
updatedAt: { type: sequelize.Sequelize.DATE, allowNull: false },
});
console.log(' warehouses 表创建成功');
} else {
@@ -680,7 +701,7 @@ async function migrateIdleDeviceAndBusiness() {
businessId: { type: sequelize.Sequelize.STRING, allowNull: false },
isPrimary: { type: sequelize.Sequelize.BOOLEAN, defaultValue: false },
createdAt: { type: sequelize.Sequelize.DATE, allowNull: false },
updatedAt: { type: sequelize.Sequelize.DATE, allowNull: false }
updatedAt: { type: sequelize.Sequelize.DATE, allowNull: false },
});
console.log(' device_business 表创建成功');
} else {