1. 新增动态设备校验Schema生成器,支持从数据库读取字段配置生成校验规则 2. 重构设备增改接口,使用动态校验替代硬编码Schema 3. 调整前端设备表单,适配字段配置并锁定核心系统字段 4. 修复前后端默认字段配置不一致问题 5. 新增字段管理页面防护,锁定核心字段的必填/可见配置
147 lines
4.2 KiB
JavaScript
147 lines
4.2 KiB
JavaScript
/**
|
|
* 动态设备字段验证Schema生成器
|
|
* 根据数据库DeviceField表的配置动态生成Joi验证Schema
|
|
* 系统核心字段(name/serialNumber/position/height)强制锁定必填
|
|
*/
|
|
|
|
const Joi = require('joi');
|
|
const DeviceField = require('../models/DeviceField');
|
|
|
|
const DEVICE_TYPES = ['server', 'switch', 'router', 'storage', 'other'];
|
|
const DEVICE_STATUS = ['running', 'maintenance', 'offline', 'fault', 'idle'];
|
|
|
|
// 强制锁定必填的字段(不受字段管理配置影响)
|
|
const FORCE_REQUIRED_FIELDS = ['name', 'serialNumber', 'position', 'height'];
|
|
|
|
/**
|
|
* 根据字段配置生成单个字段的Joi校验器
|
|
* @param {Object} field - DeviceField数据库记录
|
|
* @param {boolean} isUpdate - 是否为更新模式
|
|
* @returns {Joi.AnySchema} 该字段对应的Joi校验器
|
|
*/
|
|
function buildFieldValidator(field, isUpdate) {
|
|
const { fieldName, required: fieldRequired } = field;
|
|
// 系统核心字段强制必填
|
|
const isRequired = FORCE_REQUIRED_FIELDS.includes(fieldName) || fieldRequired;
|
|
|
|
let validator;
|
|
|
|
switch (fieldName) {
|
|
case 'name':
|
|
validator = Joi.string().max(100).messages({
|
|
'string.empty': '设备名称不能为空',
|
|
'string.max': '设备名称不能超过100个字符',
|
|
'any.required': '设备名称是必填字段',
|
|
});
|
|
if (isRequired && !isUpdate) validator = validator.required();
|
|
else validator = validator.allow('', null);
|
|
break;
|
|
|
|
case 'type':
|
|
validator = Joi.string().valid(...DEVICE_TYPES).messages({
|
|
'any.only': `设备类型必须是以下之一: ${DEVICE_TYPES.join(', ')}`,
|
|
'any.required': '设备类型是必填字段',
|
|
});
|
|
if (isRequired && !isUpdate) validator = validator.required();
|
|
break;
|
|
|
|
case 'serialNumber':
|
|
validator = Joi.string().max(100).messages({
|
|
'string.empty': '序列号不能为空',
|
|
'string.max': '序列号不能超过100个字符',
|
|
'any.required': '序列号是必填字段',
|
|
});
|
|
if (isRequired && !isUpdate) validator = validator.required();
|
|
else validator = validator.allow('', null);
|
|
break;
|
|
|
|
case 'rackId':
|
|
validator = Joi.string().allow('', null).max(50);
|
|
break;
|
|
|
|
case 'position':
|
|
validator = Joi.number().integer().min(1).max(100).allow(null);
|
|
break;
|
|
|
|
case 'height':
|
|
validator = Joi.number().integer().min(1).max(50).allow(null);
|
|
break;
|
|
|
|
case 'powerConsumption':
|
|
validator = Joi.number().min(0).max(100000).allow(null);
|
|
break;
|
|
|
|
case 'status':
|
|
validator = Joi.string().valid(...DEVICE_STATUS).default('offline');
|
|
break;
|
|
|
|
case 'model':
|
|
validator = Joi.string().allow('', null).max(100);
|
|
break;
|
|
|
|
case 'ipAddress':
|
|
validator = Joi.string().allow('', null).max(50);
|
|
break;
|
|
|
|
case 'description':
|
|
validator = Joi.string().allow('', null).max(500);
|
|
break;
|
|
|
|
case 'purchaseDate':
|
|
case 'warrantyExpiry':
|
|
validator = Joi.date().allow(null);
|
|
break;
|
|
|
|
case 'brand':
|
|
validator = Joi.string().allow('', null).max(100);
|
|
break;
|
|
|
|
default:
|
|
// 自定义字段走宽松校验
|
|
validator = Joi.any().allow(null);
|
|
}
|
|
|
|
return validator;
|
|
}
|
|
|
|
/**
|
|
* 动态生成设备创建/更新的Joi Schema
|
|
* 每次请求时从DeviceField表读取最新配置
|
|
* @param {boolean} isUpdate - 是否为更新模式
|
|
* @returns {Promise<Joi.ObjectSchema>} 动态生成的Joi Schema
|
|
*/
|
|
async function createDeviceSchema(isUpdate = false) {
|
|
// 查询所有字段配置
|
|
const fieldConfigs = await DeviceField.findAll({
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
const schemaMap = {};
|
|
|
|
// 根据字段配置动态生成每个字段的校验器
|
|
fieldConfigs.forEach(field => {
|
|
schemaMap[field.fieldName] = buildFieldValidator(field, isUpdate);
|
|
});
|
|
|
|
// 补充customFields(不在DeviceField表中)
|
|
schemaMap.customFields = Joi.object().allow(null);
|
|
|
|
let schema = Joi.object(schemaMap);
|
|
|
|
// 更新模式要求至少传一个字段
|
|
if (isUpdate) {
|
|
schema = schema.min(1).messages({
|
|
'object.min': '至少需要提供一个字段进行更新',
|
|
});
|
|
}
|
|
|
|
return schema;
|
|
}
|
|
|
|
module.exports = {
|
|
createDeviceSchema,
|
|
DEVICE_TYPES,
|
|
DEVICE_STATUS,
|
|
FORCE_REQUIRED_FIELDS,
|
|
};
|