Files

147 lines
4.2 KiB
JavaScript
Raw Permalink Normal View History

/**
* 动态设备字段验证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,
};