- 重构sys模块架构,严格按admin/api/core分层 - 对齐所有sys实体与数据库表结构 - 实现完整的adminapi控制器,匹配PHP/Java契约 - 修复依赖注入问题,确保服务正确注册 - 添加自动迁移工具和契约验证 - 完善多租户支持和审计功能 - 统一命名规范,与PHP业务逻辑保持一致
342 lines
9.2 KiB
JavaScript
342 lines
9.2 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* NestJS项目结构验证器
|
|
* 检查项目目录结构、分层规范、命名规范等
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
class StructureValidator {
|
|
constructor() {
|
|
this.projectRoot = process.cwd();
|
|
this.srcRoot = path.join(this.projectRoot, 'wwjcloud', 'src');
|
|
this.commonRoot = path.join(this.srcRoot, 'common');
|
|
this.issues = [];
|
|
this.stats = {
|
|
modules: 0,
|
|
controllers: 0,
|
|
services: 0,
|
|
entities: 0,
|
|
dtos: 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 添加问题记录
|
|
*/
|
|
addIssue(type, message, path = '') {
|
|
this.issues.push({
|
|
type,
|
|
message,
|
|
path,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 检查基础目录结构
|
|
*/
|
|
checkBaseStructure() {
|
|
console.log('🏗️ 检查基础目录结构...');
|
|
|
|
const requiredDirs = [
|
|
'wwjcloud/src',
|
|
'wwjcloud/src/common',
|
|
'wwjcloud/src/config',
|
|
'wwjcloud/src/core',
|
|
'wwjcloud/src/vendor'
|
|
];
|
|
|
|
for (const dir of requiredDirs) {
|
|
const fullPath = path.join(this.projectRoot, dir);
|
|
if (!fs.existsSync(fullPath)) {
|
|
this.addIssue('structure', `缺少必需目录: ${dir}`, fullPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查模块结构
|
|
*/
|
|
checkModuleStructure() {
|
|
console.log('📦 检查模块结构...');
|
|
|
|
if (!fs.existsSync(this.commonRoot)) {
|
|
this.addIssue('structure', 'common目录不存在', this.commonRoot);
|
|
return;
|
|
}
|
|
|
|
const modules = fs.readdirSync(this.commonRoot, { withFileTypes: true })
|
|
.filter(entry => entry.isDirectory())
|
|
.map(entry => entry.name);
|
|
|
|
this.stats.modules = modules.length;
|
|
|
|
for (const moduleName of modules) {
|
|
this.validateModule(moduleName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证单个模块
|
|
*/
|
|
validateModule(moduleName) {
|
|
const modulePath = path.join(this.commonRoot, moduleName);
|
|
const moduleFile = path.join(modulePath, `${moduleName}.module.ts`);
|
|
|
|
// 检查模块文件
|
|
if (!fs.existsSync(moduleFile)) {
|
|
this.addIssue('module', `缺少模块文件: ${moduleName}.module.ts`, moduleFile);
|
|
}
|
|
|
|
// 检查标准目录结构
|
|
const expectedDirs = ['controllers', 'services', 'entities', 'dto'];
|
|
const optionalDirs = ['guards', 'decorators', 'interfaces', 'enums'];
|
|
|
|
for (const dir of expectedDirs) {
|
|
const dirPath = path.join(modulePath, dir);
|
|
if (!fs.existsSync(dirPath)) {
|
|
this.addIssue('structure', `模块 ${moduleName} 缺少 ${dir} 目录`, dirPath);
|
|
} else {
|
|
this.validateModuleDirectory(moduleName, dir, dirPath);
|
|
}
|
|
}
|
|
|
|
// 检查控制器分层
|
|
this.checkControllerLayers(moduleName, modulePath);
|
|
|
|
// 检查服务分层
|
|
this.checkServiceLayers(moduleName, modulePath);
|
|
}
|
|
|
|
/**
|
|
* 验证模块目录
|
|
*/
|
|
validateModuleDirectory(moduleName, dirType, dirPath) {
|
|
const files = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
|
|
for (const file of files) {
|
|
if (file.isFile() && file.name.endsWith('.ts')) {
|
|
this.validateFileName(moduleName, dirType, file.name, path.join(dirPath, file.name));
|
|
|
|
// 统计文件数量
|
|
if (dirType === 'controllers') this.stats.controllers++;
|
|
else if (dirType === 'services') this.stats.services++;
|
|
else if (dirType === 'entities') this.stats.entities++;
|
|
else if (dirType === 'dto') this.stats.dtos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证文件命名
|
|
*/
|
|
validateFileName(moduleName, dirType, fileName, filePath) {
|
|
const expectedPatterns = {
|
|
controllers: /^[a-z][a-zA-Z0-9]*\.controller\.ts$/,
|
|
services: /^[a-z][a-zA-Z0-9]*\.service\.ts$/,
|
|
entities: /^[a-z][a-zA-Z0-9]*\.entity\.ts$/,
|
|
dto: /^[A-Z][a-zA-Z0-9]*Dto\.ts$/
|
|
};
|
|
|
|
const pattern = expectedPatterns[dirType];
|
|
if (pattern && !pattern.test(fileName)) {
|
|
this.addIssue('naming',
|
|
`文件命名不符合规范: ${fileName} (应符合 ${pattern})`,
|
|
filePath
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查控制器分层
|
|
*/
|
|
checkControllerLayers(moduleName, modulePath) {
|
|
const controllersPath = path.join(modulePath, 'controllers');
|
|
if (!fs.existsSync(controllersPath)) return;
|
|
|
|
const expectedLayers = ['adminapi', 'api'];
|
|
let hasLayers = false;
|
|
|
|
for (const layer of expectedLayers) {
|
|
const layerPath = path.join(controllersPath, layer);
|
|
if (fs.existsSync(layerPath)) {
|
|
hasLayers = true;
|
|
this.validateLayerFiles(moduleName, 'controllers', layer, layerPath);
|
|
}
|
|
}
|
|
|
|
// 检查是否有直接在controllers目录下的文件
|
|
const directFiles = fs.readdirSync(controllersPath, { withFileTypes: true })
|
|
.filter(entry => entry.isFile() && entry.name.endsWith('.controller.ts'));
|
|
|
|
if (directFiles.length > 0 && hasLayers) {
|
|
this.addIssue('structure',
|
|
`模块 ${moduleName} 的控制器既有分层又有直接文件,建议统一结构`,
|
|
controllersPath
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查服务分层
|
|
*/
|
|
checkServiceLayers(moduleName, modulePath) {
|
|
const servicesPath = path.join(modulePath, 'services');
|
|
if (!fs.existsSync(servicesPath)) return;
|
|
|
|
const expectedLayers = ['admin', 'api', 'core'];
|
|
|
|
for (const layer of expectedLayers) {
|
|
const layerPath = path.join(servicesPath, layer);
|
|
if (fs.existsSync(layerPath)) {
|
|
this.validateLayerFiles(moduleName, 'services', layer, layerPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证分层文件
|
|
*/
|
|
validateLayerFiles(moduleName, dirType, layer, layerPath) {
|
|
const files = fs.readdirSync(layerPath, { withFileTypes: true })
|
|
.filter(entry => entry.isFile() && entry.name.endsWith('.ts'));
|
|
|
|
if (files.length === 0) {
|
|
this.addIssue('structure',
|
|
`模块 ${moduleName} 的 ${dirType}/${layer} 目录为空`,
|
|
layerPath
|
|
);
|
|
}
|
|
|
|
for (const file of files) {
|
|
this.validateFileName(moduleName, dirType, file.name, path.join(layerPath, file.name));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查依赖关系
|
|
*/
|
|
checkDependencies() {
|
|
console.log('🔗 检查依赖关系...');
|
|
|
|
// 这里可以添加更复杂的依赖关系检查
|
|
// 例如检查循环依赖、不当的跨层依赖等
|
|
}
|
|
|
|
/**
|
|
* 检查代码质量
|
|
*/
|
|
checkCodeQuality() {
|
|
console.log('✨ 检查代码质量...');
|
|
|
|
// 检查是否有空的类或方法
|
|
// 检查是否有TODO注释
|
|
// 检查是否有硬编码值等
|
|
}
|
|
|
|
/**
|
|
* 生成报告
|
|
*/
|
|
generateReport() {
|
|
console.log('\n📊 验证报告');
|
|
console.log('='.repeat(50));
|
|
|
|
// 统计信息
|
|
console.log('📈 项目统计:');
|
|
console.log(` 模块数量: ${this.stats.modules}`);
|
|
console.log(` 控制器数量: ${this.stats.controllers}`);
|
|
console.log(` 服务数量: ${this.stats.services}`);
|
|
console.log(` 实体数量: ${this.stats.entities}`);
|
|
console.log(` DTO数量: ${this.stats.dtos}`);
|
|
|
|
// 问题分类统计
|
|
const issuesByType = this.issues.reduce((acc, issue) => {
|
|
acc[issue.type] = (acc[issue.type] || 0) + 1;
|
|
return acc;
|
|
}, {});
|
|
|
|
console.log('\n🚨 问题统计:');
|
|
if (Object.keys(issuesByType).length === 0) {
|
|
console.log(' ✅ 未发现问题');
|
|
} else {
|
|
for (const [type, count] of Object.entries(issuesByType)) {
|
|
console.log(` ${type}: ${count} 个问题`);
|
|
}
|
|
}
|
|
|
|
// 详细问题列表
|
|
if (this.issues.length > 0) {
|
|
console.log('\n📋 详细问题列表:');
|
|
|
|
const groupedIssues = this.issues.reduce((acc, issue) => {
|
|
if (!acc[issue.type]) acc[issue.type] = [];
|
|
acc[issue.type].push(issue);
|
|
return acc;
|
|
}, {});
|
|
|
|
for (const [type, issues] of Object.entries(groupedIssues)) {
|
|
console.log(`\n${type.toUpperCase()} 问题:`);
|
|
for (const issue of issues) {
|
|
console.log(` ❌ ${issue.message}`);
|
|
if (issue.path) {
|
|
console.log(` 路径: ${issue.path}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 建议
|
|
console.log('\n💡 改进建议:');
|
|
if (this.issues.length === 0) {
|
|
console.log(' 🎉 项目结构良好,继续保持!');
|
|
} else {
|
|
console.log(' 1. 优先解决结构性问题');
|
|
console.log(' 2. 统一命名规范');
|
|
console.log(' 3. 完善缺失的文件和目录');
|
|
console.log(' 4. 定期运行此工具进行检查');
|
|
}
|
|
|
|
return this.issues.length === 0;
|
|
}
|
|
|
|
/**
|
|
* 运行验证
|
|
*/
|
|
async run() {
|
|
console.log('🔍 NestJS项目结构验证器');
|
|
console.log('='.repeat(50));
|
|
|
|
try {
|
|
this.checkBaseStructure();
|
|
this.checkModuleStructure();
|
|
this.checkDependencies();
|
|
this.checkCodeQuality();
|
|
|
|
const isValid = this.generateReport();
|
|
|
|
console.log('\n' + '='.repeat(50));
|
|
if (isValid) {
|
|
console.log('✅ 验证通过!项目结构符合规范。');
|
|
process.exit(0);
|
|
} else {
|
|
console.log('❌ 验证失败!发现结构问题,请查看上述报告。');
|
|
process.exit(1);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ 验证过程中出现错误:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 运行验证器
|
|
if (require.main === module) {
|
|
const validator = new StructureValidator();
|
|
validator.run().catch(console.error);
|
|
}
|
|
|
|
module.exports = StructureValidator; |