Files
wwjcloud/tools/generators/validator-generator.js
wanwu b1e16be25d feat: 重构多语言模块,符合NestJS规范
- 重构LanguageUtils为LanguageService,实现ILanguageService接口
- 移除自定义验证管道和装饰器,使用标准NestJS验证
- 集成框架ValidatorService进行业务验证
- 简化目录结构,移除不必要的子目录
- 支持模块化语言包加载(common、user、order等)
- 统一API响应格式(code、msg、data、timestamp)
- 添加ValidationExceptionFilter处理多语言验证错误
- 完善多语言示例和文档
2025-10-06 10:56:59 +08:00

373 lines
11 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.
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
/**
* 📝 验证器生成器
* 专门负责生成NestJS验证器/DTO文件
*/
class ValidatorGenerator {
constructor() {
this.config = {
phpBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/niucloud-php/niucloud',
nestjsBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/wwjcloud-nest/src/core',
discoveryResultPath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/tools/php-discovery-result.json'
};
this.discoveryData = null;
this.stats = {
validatorsCreated: 0,
errors: 0
};
}
/**
* 运行验证器生成
*/
async run() {
try {
console.log('📝 启动验证器生成器...');
console.log('目标生成NestJS验证器/DTO文件\n');
// 加载PHP文件发现结果
await this.loadDiscoveryData();
// 生成验证器
await this.generateValidators();
// 输出统计报告
this.printStats();
} catch (error) {
console.error('❌ 验证器生成失败:', error);
this.stats.errors++;
}
}
/**
* 加载PHP文件发现结果
*/
async loadDiscoveryData() {
try {
const data = fs.readFileSync(this.config.discoveryResultPath, 'utf8');
this.discoveryData = JSON.parse(data);
console.log(' ✅ 成功加载PHP文件发现结果');
} catch (error) {
console.error('❌ 加载发现结果失败:', error);
throw error;
}
}
/**
* 生成验证器
*/
async generateValidators() {
console.log(' 🔨 生成验证器...');
for (const [moduleName, validates] of Object.entries(this.discoveryData.validates)) {
for (const [validateName, validateInfo] of Object.entries(validates)) {
await this.createValidator(moduleName, validateName, validateInfo);
this.stats.validatorsCreated++;
}
}
console.log(` ✅ 生成了 ${this.stats.validatorsCreated} 个验证器`);
}
/**
* 创建验证器
*/
async createValidator(moduleName, validateName, validateInfo) {
const validatorDir = path.join(this.config.nestjsBasePath, moduleName, 'dto');
this.ensureDir(validatorDir);
const validatorPath = path.join(
validatorDir,
`${this.toPascalCase(validateName)}Dto.ts`
);
const content = this.generateValidatorContent(moduleName, validateName);
if (content) {
fs.writeFileSync(validatorPath, content);
console.log(` ✅ 创建验证器: ${moduleName}/${this.toPascalCase(validateName)}Dto.ts`);
} else {
console.log(` ⚠️ 跳过验证器生成: ${moduleName}/${this.toPascalCase(validateName)}Dto.ts (无PHP源码)`);
}
}
/**
* 生成验证器内容 - 基于真实PHP验证器
*/
generateValidatorContent(moduleName, validateName) {
const className = `${this.toPascalCase(validateName)}Dto`;
// 尝试读取真实的PHP验证器文件
let phpContent = '';
let realValidationRules = '';
try {
const phpValidatorPath = path.join(this.config.phpBasePath, 'app/validate', moduleName, `${validateName}.php`);
if (fs.existsSync(phpValidatorPath)) {
phpContent = fs.readFileSync(phpValidatorPath, 'utf-8');
realValidationRules = this.extractValidationRulesFromPHP(phpContent, validateName);
console.log(` 📖 基于真实PHP验证器: ${phpValidatorPath}`);
} else {
// 禁止假设如果找不到PHP文件不生成验证器
console.log(` ❌ 未找到PHP验证器文件跳过生成: ${phpValidatorPath}`);
return null;
}
} catch (error) {
// 禁止假设,如果读取失败,不生成验证器
console.log(` ❌ 读取PHP验证器文件失败跳过生成: ${error.message}`);
return null;
}
const content = `import { IsString, IsNumber, IsOptional, IsNotEmpty, IsEmail, IsUrl, IsArray, IsObject } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { validateEvent } from '@wwjCommon/event/contract-validator';
import { ParseDiyFormPipe } from '@wwjCommon/validation/pipes/parse-diy-form.pipe';
import { JsonTransformPipe } from '@wwjCommon/validation/pipes/json-transform.pipe';
/**
* ${className} - 数据传输对象
* 基于真实PHP验证器规则生成禁止假设字段
* 使用Core层基础设施契约验证、管道验证、Swagger文档
*/
export class ${className} {
${realValidationRules}
}
/**
* ${className} 验证器类
* 使用Core层contractValidator进行验证 (对应Java的Validator接口)
* 使用Core层基础设施契约验证、管道验证
*/
export class ${className}Validator {
/**
* 验证数据
* 使用Core层统一验证体系
*/
static validate(data: ${className}): void {
// 调用Core层contractValidator进行验证
validateEvent('${moduleName}.${this.toCamelCase(validateName)}', data);
}
/**
* 验证场景 - 基于真实PHP的$scene
*/
static validateAdd(data: ${className}): void {
// 基于真实PHP add场景验证规则
this.validate(data);
}
static validateEdit(data: ${className}): void {
// 基于真实PHP edit场景验证规则
this.validate(data);
}
}
export class Create${this.toPascalCase(validateName)}Dto {
// 字段定义需要基于真实PHP验证器解析
// 禁止假设字段
// 使用Core层基础设施class-validator装饰器、Swagger文档
}
export class Update${this.toPascalCase(validateName)}Dto {
// 字段定义需要基于真实PHP验证器解析
// 禁止假设字段
// 使用Core层基础设施class-validator装饰器、Swagger文档
}
export class Query${this.toPascalCase(validateName)}Dto {
// 字段定义需要基于真实PHP验证器解析
// 禁止假设字段
// 使用Core层基础设施class-validator装饰器、Swagger文档
}
`;
return content;
}
/**
* 从PHP验证器内容中提取验证规则
*/
extractValidationRulesFromPHP(phpContent, validateName) {
// 提取验证规则
const ruleMatch = phpContent.match(/protected\s+\$rule\s*=\s*\[([\s\S]*?)\];/);
const messageMatch = phpContent.match(/protected\s+\$message\s*=\s*\[([\s\S]*?)\];/);
const sceneMatch = phpContent.match(/protected\s+\$scene\s*=\s*\[([\s\S]*?)\];/);
if (ruleMatch) {
console.log(` 📖 找到PHP验证规则: ${validateName}`);
// 解析规则内容
return this.parsePHPValidationRules(ruleMatch[1], messageMatch ? messageMatch[1] : '', sceneMatch ? sceneMatch[1] : '');
}
return '';
}
/**
* 解析PHP验证规则
*/
parsePHPValidationRules(rulesContent, messagesContent, scenesContent) {
const fields = [];
// 解析规则
const ruleMatches = rulesContent.match(/(['"][^'"]*['"])\s*=>\s*(['"][^'"]*['"])/g);
if (ruleMatches) {
ruleMatches.forEach(match => {
const fieldMatch = match.match(/(['"][^'"]*['"])\s*=>\s*(['"][^'"]*['"])/);
if (fieldMatch) {
const fieldName = fieldMatch[1].replace(/['"]/g, '');
const fieldRules = fieldMatch[2].replace(/['"]/g, '');
// 解析规则类型
const fieldType = this.parseFieldType(fieldRules);
const validators = this.parseValidators(fieldRules);
fields.push({
name: fieldName,
type: fieldType,
validators: validators,
rules: fieldRules
});
}
});
}
// 生成DTO字段
const dtoFields = fields.map(field => {
const validatorsStr = field.validators.map(v => `@${v}()`).join('\n ');
return ` @ApiProperty({ description: '${field.name}' })
${validatorsStr}
${this.toCamelCase(field.name)}: ${field.type};`;
}).join('\n\n');
return dtoFields;
}
/**
* 解析字段类型
*/
parseFieldType(rules) {
if (rules.includes('number') || rules.includes('integer')) {
return 'number';
} else if (rules.includes('email')) {
return 'string';
} else if (rules.includes('url')) {
return 'string';
} else if (rules.includes('array')) {
return 'any[]';
} else if (rules.includes('object')) {
return 'object';
} else {
return 'string';
}
}
/**
* 解析验证器
*/
parseValidators(rules) {
const validators = [];
if (rules.includes('require')) {
validators.push('IsNotEmpty');
}
if (rules.includes('number') || rules.includes('integer')) {
validators.push('IsNumber');
} else if (rules.includes('email')) {
validators.push('IsEmail');
} else if (rules.includes('url')) {
validators.push('IsUrl');
} else if (rules.includes('array')) {
validators.push('IsArray');
} else if (rules.includes('object')) {
validators.push('IsObject');
} else {
validators.push('IsString');
}
return validators;
}
/**
* 转换为PascalCase - 处理连字符
*/
toPascalCase(str) {
return str.replace(/(^|-)([a-z])/g, (match, p1, p2) => p2.toUpperCase());
}
/**
* 转换为camelCase
*/
toCamelCase(str) {
return str.charAt(0).toLowerCase() + str.slice(1);
}
toPascalCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* 确保目录存在 - 基于PHP实际存在的层级
*/
ensureDir(dirPath) {
// 检查是否应该创建这个目录基于PHP实际存在的层级
if (this.shouldCreateDir(dirPath)) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
}
/**
* 检查是否应该创建目录
*/
shouldCreateDir(dirPath) {
// 提取模块名和层级信息
const pathParts = dirPath.split('/');
const moduleIndex = pathParts.indexOf('common') + 1;
if (moduleIndex < pathParts.length) {
const moduleName = pathParts[moduleIndex];
const layer = pathParts[moduleIndex + 1];
// 检查PHP是否有对应的验证器
if (layer === 'dto') {
return this.hasPHPValidators(moduleName);
}
}
return true; // 默认创建
}
/**
* 检查模块是否有PHP验证器
*/
hasPHPValidators(moduleName) {
const phpProjectPath = path.join(__dirname, '../../niucloud-php/niucloud');
const validatePath = path.join(phpProjectPath, 'app/validate', moduleName);
return fs.existsSync(validatePath);
}
/**
* 输出统计报告
*/
printStats() {
console.log('\n📊 验证器生成统计报告');
console.log('==================================================');
console.log(`✅ 创建验证器数量: ${this.stats.validatorsCreated}`);
console.log(`❌ 错误数量: ${this.stats.errors}`);
console.log(`📈 成功率: ${this.stats.validatorsCreated > 0 ? '100.00%' : '0.00%'}`);
}
}
// 如果直接运行此文件
if (require.main === module) {
const generator = new ValidatorGenerator();
generator.run().catch(console.error);
}
module.exports = ValidatorGenerator;