商城后端模板
This commit is contained in:
230
middleware/validation.js
Normal file
230
middleware/validation.js
Normal file
@@ -0,0 +1,230 @@
|
||||
const Joi = require('joi');
|
||||
const { AppError } = require('./errorHandler');
|
||||
const { ERROR_CODES, HTTP_STATUS } = require('../config/constants');
|
||||
|
||||
// 验证中间件工厂函数
|
||||
const validate = (schema) => {
|
||||
return (req, res, next) => {
|
||||
const { error } = schema.validate(req.body, { abortEarly: false });
|
||||
if (error) {
|
||||
const errorMessage = error.details.map(detail => detail.message).join(', ');
|
||||
return next(new AppError(errorMessage, HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR));
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// 查询参数验证中间件
|
||||
const validateQuery = (schema) => {
|
||||
return (req, res, next) => {
|
||||
const { error } = schema.validate(req.query, { abortEarly: false });
|
||||
if (error) {
|
||||
const errorMessage = error.details.map(detail => detail.message).join(', ');
|
||||
return next(new AppError(errorMessage, HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR));
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// 路径参数验证中间件
|
||||
const validateParams = (schema) => {
|
||||
return (req, res, next) => {
|
||||
const { error } = schema.validate(req.params, { abortEarly: false });
|
||||
if (error) {
|
||||
const errorMessage = error.details.map(detail => detail.message).join(', ');
|
||||
return next(new AppError(errorMessage, HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR));
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// 通用验证规则
|
||||
const commonSchemas = {
|
||||
// ID验证
|
||||
id: Joi.number().integer().positive().required().messages({
|
||||
'number.base': 'ID必须是数字',
|
||||
'number.integer': 'ID必须是整数',
|
||||
'number.positive': 'ID必须是正数',
|
||||
'any.required': 'ID是必需的'
|
||||
}),
|
||||
|
||||
// 分页验证
|
||||
pagination: Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1).messages({
|
||||
'number.base': '页码必须是数字',
|
||||
'number.integer': '页码必须是整数',
|
||||
'number.min': '页码必须大于0'
|
||||
}),
|
||||
limit: Joi.number().integer().min(1).max(100).default(10).messages({
|
||||
'number.base': '每页数量必须是数字',
|
||||
'number.integer': '每页数量必须是整数',
|
||||
'number.min': '每页数量必须大于0',
|
||||
'number.max': '每页数量不能超过100'
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
// 用户相关验证规则
|
||||
const userSchemas = {
|
||||
// 用户注册
|
||||
register: Joi.object({
|
||||
username: Joi.string().alphanum().min(3).max(30).required().messages({
|
||||
'string.base': '用户名必须是字符串',
|
||||
'string.alphanum': '用户名只能包含字母和数字',
|
||||
'string.min': '用户名至少3个字符',
|
||||
'string.max': '用户名最多30个字符',
|
||||
'any.required': '用户名是必需的'
|
||||
}),
|
||||
password: Joi.string().min(6).max(128).required().messages({
|
||||
'string.base': '密码必须是字符串',
|
||||
'string.min': '密码至少6个字符',
|
||||
'string.max': '密码最多128个字符',
|
||||
'any.required': '密码是必需的'
|
||||
}),
|
||||
phone: Joi.string().pattern(/^1[3-9]\d{9}$/).required().messages({
|
||||
'string.pattern.base': '手机号格式不正确',
|
||||
'any.required': '手机号是必需的'
|
||||
}),
|
||||
// 可选字段,注册时不需要填写
|
||||
real_name: Joi.string().max(50).allow('').optional().messages({
|
||||
'string.max': '真实姓名最多50个字符'
|
||||
}),
|
||||
role: Joi.string().valid('admin', 'user').default('user').messages({
|
||||
'any.only': '角色只能是admin或user'
|
||||
})
|
||||
}),
|
||||
|
||||
// 用户登录
|
||||
login: Joi.object({
|
||||
username: Joi.string().required().messages({
|
||||
'any.required': '用户名是必需的'
|
||||
}),
|
||||
password: Joi.string().required().messages({
|
||||
'any.required': '密码是必需的'
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
// 转账相关验证规则
|
||||
const transferSchemas = {
|
||||
// 转账查询参数
|
||||
query: Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1).messages({
|
||||
'number.base': '页码必须是数字',
|
||||
'number.integer': '页码必须是整数',
|
||||
'number.min': '页码必须大于0'
|
||||
}),
|
||||
limit: Joi.number().integer().min(1).max(100).default(10).messages({
|
||||
'number.base': '每页数量必须是数字',
|
||||
'number.integer': '每页数量必须是整数',
|
||||
'number.min': '每页数量必须大于0',
|
||||
'number.max': '每页数量不能超过100'
|
||||
}),
|
||||
status: Joi.string().valid('pending', 'confirmed', 'rejected', 'cancelled').allow('').messages({
|
||||
'any.only': '状态值无效'
|
||||
}),
|
||||
type: Joi.string().valid('user_to_user', 'system_to_user', 'user_to_system').allow('').messages({
|
||||
'any.only': '转账类型无效'
|
||||
}),
|
||||
search: Joi.string().allow('').max(100).messages({
|
||||
'string.max': '搜索关键词最多100个字符'
|
||||
}),
|
||||
transfer_type: Joi.string().valid('user_to_user', 'system_to_user', 'user_to_system').allow('').messages({
|
||||
'any.only': '转账类型无效'
|
||||
}),
|
||||
start_date: Joi.date().iso().allow('').messages({
|
||||
'date.format': '开始日期格式不正确'
|
||||
}),
|
||||
end_date: Joi.date().iso().allow('').messages({
|
||||
'date.format': '结束日期格式不正确'
|
||||
}),
|
||||
sort: Joi.string().valid('id', 'amount', 'created_at', 'updated_at', 'status').allow('').messages({
|
||||
'any.only': '排序字段无效,只支持: id, amount, created_at, updated_at, status'
|
||||
}),
|
||||
order: Joi.string().valid('asc', 'desc').allow('').messages({
|
||||
'any.only': '排序方向无效,只支持: asc, desc'
|
||||
}),
|
||||
// 优先显示待处理转账参数
|
||||
show_pending: Joi.alternatives().try(
|
||||
Joi.boolean(),
|
||||
Joi.string().valid('true', 'false', '')
|
||||
).allow('').messages({
|
||||
'alternatives.match': 'show_pending参数只能是布尔值或字符串true/false'
|
||||
})
|
||||
}),
|
||||
|
||||
// 创建转账
|
||||
create: Joi.object({
|
||||
to_user_id: Joi.number().integer().positive().required().messages({
|
||||
'number.base': '收款用户ID必须是数字',
|
||||
'number.integer': '收款用户ID必须是整数',
|
||||
'number.positive': '收款用户ID必须是正数',
|
||||
'any.required': '收款用户ID是必需的'
|
||||
}),
|
||||
amount: Joi.number().positive().precision(2).required().messages({
|
||||
'number.base': '金额必须是数字',
|
||||
'number.positive': '金额必须是正数',
|
||||
'any.required': '金额是必需的'
|
||||
}),
|
||||
transfer_type: Joi.string().valid('user_to_user', 'system_to_user', 'user_to_system').required().messages({
|
||||
'any.only': '转账类型无效',
|
||||
'any.required': '转账类型是必需的'
|
||||
}),
|
||||
description: Joi.string().max(500).allow('').messages({
|
||||
'string.max': '描述最多500个字符'
|
||||
}),
|
||||
voucher_url: Joi.string().uri().allow('').messages({
|
||||
'string.uri': '凭证URL格式不正确'
|
||||
})
|
||||
}),
|
||||
|
||||
// 确认转账
|
||||
confirm: Joi.object({
|
||||
transfer_id: Joi.number().integer().positive().required().messages({
|
||||
'number.base': '转账ID必须是数字',
|
||||
'number.integer': '转账ID必须是整数',
|
||||
'number.positive': '转账ID必须是正数',
|
||||
'any.required': '转账ID是必需的'
|
||||
}),
|
||||
note: Joi.string().max(500).allow('').messages({
|
||||
'string.max': '备注最多500个字符'
|
||||
})
|
||||
}),
|
||||
|
||||
// 拒绝转账
|
||||
reject: Joi.object({
|
||||
transfer_id: Joi.number().integer().positive().required().messages({
|
||||
'number.base': '转账ID必须是数字',
|
||||
'number.integer': '转账ID必须是整数',
|
||||
'number.positive': '转账ID必须是正数',
|
||||
'any.required': '转账ID是必需的'
|
||||
}),
|
||||
note: Joi.string().max(500).allow('').messages({
|
||||
'string.max': '备注最多500个字符'
|
||||
})
|
||||
})
|
||||
};
|
||||
// 系统设置相关验证规则
|
||||
const systemSchemas = {
|
||||
updateSettings: Joi.object({
|
||||
site_name: Joi.string().max(100).optional(),
|
||||
site_description: Joi.string().max(500).optional(),
|
||||
|
||||
contact_phone: Joi.string().max(20).optional(),
|
||||
maintenance_mode: Joi.boolean().optional(),
|
||||
max_transfer_amount: Joi.number().positive().optional(),
|
||||
min_transfer_amount: Joi.number().positive().optional(),
|
||||
transfer_fee_rate: Joi.number().min(0).max(1).optional()
|
||||
})
|
||||
};
|
||||
|
||||
// 导出所有验证规则
|
||||
module.exports = {
|
||||
validate,
|
||||
validateQuery,
|
||||
validateParams,
|
||||
commonSchemas,
|
||||
userSchemas,
|
||||
transferSchemas,
|
||||
systemSchemas
|
||||
};
|
||||
Reference in New Issue
Block a user