- 重构sys模块架构,严格按admin/api/core分层 - 对齐所有sys实体与数据库表结构 - 实现完整的adminapi控制器,匹配PHP/Java契约 - 修复依赖注入问题,确保服务正确注册 - 添加自动迁移工具和契约验证 - 完善多租户支持和审计功能 - 统一命名规范,与PHP业务逻辑保持一致
374 lines
10 KiB
JavaScript
374 lines
10 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
/**
|
||
* PHP与NestJS项目自动映射检查器
|
||
* 检查PHP项目与NestJS项目的模块、控制器、服务等对应关系
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
class AutoMappingChecker {
|
||
constructor() {
|
||
this.projectRoot = process.cwd();
|
||
this.phpPath = path.join(this.projectRoot, 'niucloud-php/niucloud');
|
||
this.nestjsPath = path.join(this.projectRoot, 'wwjcloud/src');
|
||
this.results = {
|
||
modules: [],
|
||
controllers: [],
|
||
services: [],
|
||
models: [],
|
||
summary: {
|
||
total: 0,
|
||
matched: 0,
|
||
missing: 0
|
||
}
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 检查目录是否存在
|
||
*/
|
||
checkDirectories() {
|
||
if (!fs.existsSync(this.phpPath)) {
|
||
console.error('❌ PHP项目路径不存在:', this.phpPath);
|
||
return false;
|
||
}
|
||
if (!fs.existsSync(this.nestjsPath)) {
|
||
console.error('❌ NestJS项目路径不存在:', this.nestjsPath);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取PHP控制器列表
|
||
*/
|
||
getPhpControllers() {
|
||
const controllers = [];
|
||
const adminApiPath = path.join(this.phpPath, 'app/adminapi/controller');
|
||
const apiPath = path.join(this.phpPath, 'app/api/controller');
|
||
|
||
// 扫描管理端控制器
|
||
if (fs.existsSync(adminApiPath)) {
|
||
this.scanPhpControllers(adminApiPath, 'adminapi', controllers);
|
||
}
|
||
|
||
// 扫描前台控制器
|
||
if (fs.existsSync(apiPath)) {
|
||
this.scanPhpControllers(apiPath, 'api', controllers);
|
||
}
|
||
|
||
return controllers;
|
||
}
|
||
|
||
/**
|
||
* 扫描PHP控制器
|
||
*/
|
||
scanPhpControllers(dir, type, controllers) {
|
||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||
|
||
for (const entry of entries) {
|
||
const fullPath = path.join(dir, entry.name);
|
||
|
||
if (entry.isDirectory()) {
|
||
// 递归扫描子目录
|
||
this.scanPhpControllers(fullPath, type, controllers);
|
||
} else if (entry.isFile() && entry.name.endsWith('.php')) {
|
||
const relativePath = path.relative(path.join(this.phpPath, 'app', type, 'controller'), fullPath);
|
||
const modulePath = path.dirname(relativePath);
|
||
const fileName = path.basename(entry.name, '.php');
|
||
|
||
controllers.push({
|
||
type,
|
||
module: modulePath === '.' ? 'root' : modulePath,
|
||
name: fileName,
|
||
phpPath: fullPath,
|
||
relativePath
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取NestJS控制器列表
|
||
*/
|
||
getNestjsControllers() {
|
||
const controllers = [];
|
||
const commonPath = path.join(this.nestjsPath, 'common');
|
||
|
||
if (!fs.existsSync(commonPath)) {
|
||
return controllers;
|
||
}
|
||
|
||
const modules = fs.readdirSync(commonPath, { withFileTypes: true })
|
||
.filter(entry => entry.isDirectory())
|
||
.map(entry => entry.name);
|
||
|
||
for (const module of modules) {
|
||
const modulePath = path.join(commonPath, module);
|
||
|
||
// 检查adminapi控制器
|
||
const adminApiPath = path.join(modulePath, 'controllers/adminapi');
|
||
if (fs.existsSync(adminApiPath)) {
|
||
this.scanNestjsControllers(adminApiPath, 'adminapi', module, controllers);
|
||
}
|
||
|
||
// 检查api控制器
|
||
const apiPath = path.join(modulePath, 'controllers/api');
|
||
if (fs.existsSync(apiPath)) {
|
||
this.scanNestjsControllers(apiPath, 'api', module, controllers);
|
||
}
|
||
}
|
||
|
||
return controllers;
|
||
}
|
||
|
||
/**
|
||
* 扫描NestJS控制器
|
||
*/
|
||
scanNestjsControllers(dir, type, module, controllers) {
|
||
if (!fs.existsSync(dir)) return;
|
||
|
||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||
|
||
for (const entry of entries) {
|
||
if (entry.isFile() && entry.name.endsWith('.controller.ts')) {
|
||
const fileName = path.basename(entry.name, '.controller.ts');
|
||
|
||
controllers.push({
|
||
type,
|
||
module,
|
||
name: fileName,
|
||
nestjsPath: path.join(dir, entry.name)
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查控制器映射
|
||
*/
|
||
checkControllerMapping() {
|
||
const phpControllers = this.getPhpControllers();
|
||
const nestjsControllers = this.getNestjsControllers();
|
||
|
||
console.log('\n📋 控制器映射检查结果:');
|
||
console.log('='.repeat(50));
|
||
|
||
for (const phpController of phpControllers) {
|
||
const matched = nestjsControllers.find(nestjs =>
|
||
nestjs.type === phpController.type &&
|
||
this.normalizeModuleName(nestjs.module) === this.normalizeModuleName(phpController.module) &&
|
||
this.normalizeControllerName(nestjs.name) === this.normalizeControllerName(phpController.name)
|
||
);
|
||
|
||
const status = matched ? '✅' : '❌';
|
||
const moduleDisplay = phpController.module === 'root' ? '/' : phpController.module;
|
||
|
||
console.log(`${status} ${phpController.type}/${moduleDisplay}/${phpController.name}.php`);
|
||
|
||
if (matched) {
|
||
console.log(` → ${matched.module}/${matched.name}.controller.ts`);
|
||
this.results.summary.matched++;
|
||
} else {
|
||
console.log(` → 缺失对应的NestJS控制器`);
|
||
this.results.summary.missing++;
|
||
}
|
||
|
||
this.results.summary.total++;
|
||
this.results.controllers.push({
|
||
php: phpController,
|
||
nestjs: matched,
|
||
matched: !!matched
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 标准化模块名
|
||
*/
|
||
normalizeModuleName(name) {
|
||
if (name === 'root' || name === '.' || name === '/') return '';
|
||
return name.toLowerCase().replace(/[_\-]/g, '');
|
||
}
|
||
|
||
/**
|
||
* 标准化控制器名
|
||
*/
|
||
normalizeControllerName(name) {
|
||
return name.toLowerCase().replace(/[_\-]/g, '');
|
||
}
|
||
|
||
/**
|
||
* 检查服务映射
|
||
*/
|
||
checkServiceMapping() {
|
||
console.log('\n🔧 服务映射检查:');
|
||
console.log('='.repeat(50));
|
||
|
||
const phpServicePath = path.join(this.phpPath, 'app/service');
|
||
const nestjsCommonPath = path.join(this.nestjsPath, 'common');
|
||
|
||
if (!fs.existsSync(phpServicePath)) {
|
||
console.log('❌ PHP服务目录不存在');
|
||
return;
|
||
}
|
||
|
||
if (!fs.existsSync(nestjsCommonPath)) {
|
||
console.log('❌ NestJS通用服务目录不存在');
|
||
return;
|
||
}
|
||
|
||
// 简化的服务检查
|
||
const phpServices = this.getPhpServices(phpServicePath);
|
||
const nestjsServices = this.getNestjsServices(nestjsCommonPath);
|
||
|
||
for (const phpService of phpServices) {
|
||
const matched = nestjsServices.find(nestjs =>
|
||
this.normalizeServiceName(nestjs.name) === this.normalizeServiceName(phpService.name)
|
||
);
|
||
|
||
const status = matched ? '✅' : '❌';
|
||
console.log(`${status} ${phpService.name}.php`);
|
||
|
||
if (matched) {
|
||
console.log(` → ${matched.module}/${matched.name}.service.ts`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取PHP服务列表
|
||
*/
|
||
getPhpServices(dir) {
|
||
const services = [];
|
||
|
||
if (!fs.existsSync(dir)) return services;
|
||
|
||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||
|
||
for (const entry of entries) {
|
||
if (entry.isFile() && entry.name.endsWith('.php')) {
|
||
services.push({
|
||
name: path.basename(entry.name, '.php'),
|
||
path: path.join(dir, entry.name)
|
||
});
|
||
}
|
||
}
|
||
|
||
return services;
|
||
}
|
||
|
||
/**
|
||
* 获取NestJS服务列表
|
||
*/
|
||
getNestjsServices(dir) {
|
||
const services = [];
|
||
|
||
if (!fs.existsSync(dir)) return services;
|
||
|
||
const modules = fs.readdirSync(dir, { withFileTypes: true })
|
||
.filter(entry => entry.isDirectory())
|
||
.map(entry => entry.name);
|
||
|
||
for (const module of modules) {
|
||
const servicesPath = path.join(dir, module, 'services');
|
||
|
||
if (fs.existsSync(servicesPath)) {
|
||
this.scanNestjsServices(servicesPath, module, services);
|
||
}
|
||
}
|
||
|
||
return services;
|
||
}
|
||
|
||
/**
|
||
* 扫描NestJS服务
|
||
*/
|
||
scanNestjsServices(dir, module, services) {
|
||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||
|
||
for (const entry of entries) {
|
||
const fullPath = path.join(dir, entry.name);
|
||
|
||
if (entry.isDirectory()) {
|
||
this.scanNestjsServices(fullPath, module, services);
|
||
} else if (entry.isFile() && entry.name.endsWith('.service.ts')) {
|
||
services.push({
|
||
module,
|
||
name: path.basename(entry.name, '.service.ts'),
|
||
path: fullPath
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 标准化服务名
|
||
*/
|
||
normalizeServiceName(name) {
|
||
return name.toLowerCase().replace(/service$/, '').replace(/[_\-]/g, '');
|
||
}
|
||
|
||
/**
|
||
* 生成统计报告
|
||
*/
|
||
generateSummary() {
|
||
console.log('\n📊 检查统计:');
|
||
console.log('='.repeat(50));
|
||
console.log(`总计检查项: ${this.results.summary.total}`);
|
||
console.log(`匹配成功: ${this.results.summary.matched} (${((this.results.summary.matched / this.results.summary.total) * 100).toFixed(1)}%)`);
|
||
console.log(`缺失项目: ${this.results.summary.missing} (${((this.results.summary.missing / this.results.summary.total) * 100).toFixed(1)}%)`);
|
||
|
||
if (this.results.summary.missing > 0) {
|
||
console.log('\n⚠️ 需要关注的缺失项:');
|
||
const missingItems = this.results.controllers.filter(item => !item.matched);
|
||
|
||
for (const item of missingItems.slice(0, 10)) { // 只显示前10个
|
||
console.log(` - ${item.php.type}/${item.php.module}/${item.php.name}.php`);
|
||
}
|
||
|
||
if (missingItems.length > 10) {
|
||
console.log(` ... 还有 ${missingItems.length - 10} 个缺失项`);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 运行完整检查
|
||
*/
|
||
async run() {
|
||
console.log('🚀 PHP与NestJS项目自动映射检查器');
|
||
console.log('='.repeat(50));
|
||
|
||
if (!this.checkDirectories()) {
|
||
process.exit(1);
|
||
}
|
||
|
||
try {
|
||
this.checkControllerMapping();
|
||
this.checkServiceMapping();
|
||
this.generateSummary();
|
||
|
||
console.log('\n✅ 检查完成!');
|
||
|
||
if (this.results.summary.missing > 0) {
|
||
console.log('\n💡 建议: 根据缺失项创建对应的NestJS文件');
|
||
process.exit(1);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('❌ 检查过程中出现错误:', error.message);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 运行检查器
|
||
if (require.main === module) {
|
||
const checker = new AutoMappingChecker();
|
||
checker.run().catch(console.error);
|
||
}
|
||
|
||
module.exports = AutoMappingChecker; |