Files
wwjcloud-nest-v1/tools-v1/java-tools/generators/entity-generator.js
wanwujie 699680c93a feat: 重构v1框架架构和清理整理
- 将preset.ts移动到config目录,符合架构规范
- 迁移php-tools到java-tools,参考Java架构而非PHP
- 清理AI层文档,整合为单一README
- 删除core层,专注boot和ai层
- 集成AI层与Boot层,实现100%组件集成
- 清理废弃js文件和临时报告文件
- 更新导入路径,保持代码一致性
2025-10-20 23:07:37 +08:00

411 lines
12 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');
const BaseGenerator = require('./base-generator');
/**
* 🏗️ 实体生成器
* 专门负责生成NestJS实体文件 (参考Java架构)
*/
class EntityGenerator extends BaseGenerator {
constructor() {
super('EntityGenerator');
this.config = {
javaBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/niucloud-java/niucloud-core/src/main/java',
phpBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/niucloud-php/niucloud', // 仅用于业务逻辑提取
nestjsBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/wwjcloud-nest-v1/libs/wwjcloud-core/src',
discoveryResultPath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/tools-v1/java-tools/java-discovery-result.json'
};
this.discoveryData = null;
this.entityStats = {
entitiesCreated: 0,
entitiesSkipped: 0
};
}
/**
* 运行实体生成
*/
async run() {
try {
console.log('🏗️ 启动实体生成器...');
console.log('目标生成NestJS实体文件\n');
// 加载Java架构发现结果含PHP业务逻辑
await this.loadDiscoveryData();
// 生成实体
await this.generateEntities();
// 输出统计报告
this.printStats();
} catch (error) {
console.error('❌ 实体生成失败:', error);
this.stats.errors++;
}
}
/**
* 加载Java架构发现结果含PHP业务逻辑
*/
async loadDiscoveryData() {
try {
const data = fs.readFileSync(this.config.discoveryResultPath, 'utf8');
this.discoveryData = JSON.parse(data);
console.log(' ✅ 成功加载Java架构发现结果含PHP业务逻辑');
} catch (error) {
console.error('❌ 加载发现结果失败:', error);
throw error;
}
}
/**
* 生成实体
*/
async generateEntities() {
console.log(' 🔨 生成实体...');
// 检查是否有模型数据
if (!this.discoveryData.models || Object.keys(this.discoveryData.models).length === 0) {
console.log(' ⚠️ 未发现PHP模型跳过生成');
return;
}
for (const [moduleName, models] of Object.entries(this.discoveryData.models)) {
// 检查Java架构是否有对应的模型目录
if (!this.hasPHPModels(moduleName)) {
console.log(` ⚠️ 模块 ${moduleName} 在Java架构中无对应且PHP项目中也无模型跳过`);
continue;
}
for (const [modelName, modelInfo] of Object.entries(models)) {
await this.createEntity(moduleName, modelName, modelInfo);
this.stats.entitiesCreated++;
}
}
console.log(` ✅ 生成了 ${this.stats.entitiesCreated} 个实体`);
}
/**
* 创建实体
*/
async createEntity(moduleName, modelName, modelInfo) {
const entityPath = path.join(
this.config.nestjsBasePath,
moduleName,
'entity',
`${this.toKebabCase(modelName)}.entity.ts`
);
// 基于真实PHP model文件生成实体
const content = await this.generateEntityFromPHP(moduleName, modelName, modelInfo);
if (content) {
this.writeFile(entityPath, content, `Entity for ${moduleName}/${modelName}`);
this.entityStats.entitiesCreated++;
} else {
this.log(`跳过实体生成: ${moduleName}/${this.toKebabCase(modelName)}.entity.ts (无PHP源码)`, 'warning');
this.entityStats.entitiesSkipped++;
this.stats.filesSkipped++;
}
}
toKebabCase(str) {
return String(str)
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/_/g, '-')
.toLowerCase();
}
/**
* 基于PHP model文件生成实体
*/
async generateEntityFromPHP(moduleName, modelName, modelInfo) {
const className = this.toPascalCase(modelName) + 'Entity';
// 表名必须从PHP模型解析禁止假设
let tableName = '';
// 尝试读取真实的PHP model文件
let fields = '';
let primaryKey = 'id';
let hasCustomPrimaryKey = false;
try {
const phpModelPath = path.join(this.config.phpBasePath, 'app/model', moduleName, `${modelName}.php`);
if (fs.existsSync(phpModelPath)) {
const phpContent = fs.readFileSync(phpModelPath, 'utf-8');
// 提取主键信息
const pkMatch = phpContent.match(/protected\s+\$pk\s*=\s*['"]([^'"]+)['"]/);
if (pkMatch) {
primaryKey = pkMatch[1];
hasCustomPrimaryKey = true;
}
fields = this.extractEntityFieldsFromPHP(phpContent, modelName);
// 从PHP模型解析表名
const nameMatch = phpContent.match(/protected\s+\$name\s*=\s*['"]([^'"]*)['"]/);
tableName = nameMatch ? nameMatch[1] : '';
console.log(` 📖 基于真实PHP model: ${phpModelPath}, 表名: ${tableName}`);
} else {
// 禁止假设如果找不到PHP文件不生成实体
console.log(` ❌ 未找到PHP model文件跳过生成: ${phpModelPath}`);
return null;
}
} catch (error) {
// 禁止假设,如果读取失败,不生成实体
console.log(` ❌ 读取PHP model文件失败跳过生成: ${error.message}`);
return null;
}
// 生成主键字段
let primaryKeyField = '';
if (hasCustomPrimaryKey) {
// 基于真实PHP主键定义生成禁止假设类型
primaryKeyField = ` @PrimaryColumn({ name: '${primaryKey}', type: 'int' })
${this.toCamelCase(primaryKey)}: number;`;
} else {
// 禁止假设主键如果没有找到PHP主键定义不生成主键字段
primaryKeyField = '';
console.log(` ⚠️ 未找到PHP主键定义不生成主键字段: ${modelName}`);
}
return `import { Entity, PrimaryGeneratedColumn, PrimaryColumn, Column, Index } from 'typeorm';
/**
* ${className} - 数据库实体
* 基于真实 PHP 模型生成,不依赖旧的 Common 基类
*/
@Entity('${tableName}')
export class ${className} {
${primaryKeyField}
${fields}
}`;
}
/**
* 从PHP内容中提取实体字段 - 基于真实PHP模型
*/
extractEntityFieldsFromPHP(phpContent, modelName) {
// 提取表名
const nameMatch = phpContent.match(/protected\s+\$name\s*=\s*['"]([^'"]*)['"]/);
const tableName = nameMatch ? nameMatch[1] : this.getTableName(modelName);
// 提取字段类型定义
const typeMatch = phpContent.match(/protected\s+\$type\s*=\s*\[([\s\S]*?)\];/);
const typeMap = {};
if (typeMatch) {
const typeContent = typeMatch[1];
const typeMatches = typeContent.match(/(['"][^'"]*['"])\s*=>\s*(['"][^'"]*['"])/g);
if (typeMatches) {
typeMatches.forEach(match => {
const fieldTypeMatch = match.match(/(['"][^'"]*['"])\s*=>\s*(['"][^'"]*['"])/);
if (fieldTypeMatch) {
const fieldName = fieldTypeMatch[1].replace(/['"]/g, '');
const fieldType = fieldTypeMatch[2].replace(/['"]/g, '');
typeMap[fieldName] = fieldType;
}
});
}
}
// 提取软删除字段
const deleteTimeMatch = phpContent.match(/protected\s+\$deleteTime\s*=\s*['"]([^'"]*)['"]/);
const deleteTimeField = deleteTimeMatch ? deleteTimeMatch[1] : 'delete_time';
// 基于真实PHP模型结构生成字段
console.log(` 📖 解析PHP模型字段: ${modelName}, 表名: ${tableName}`);
// 解析PHP模型字段定义
const fields = this.parsePHPModelFields(phpContent, typeMap);
return fields;
}
/**
* 解析PHP模型字段定义
*/
parsePHPModelFields(phpContent, typeMap) {
const fields = [];
// 提取所有getter方法这些通常对应数据库字段
const getterMatches = phpContent.match(/public function get(\w+)Attr\([^)]*\)[\s\S]*?\{[\s\S]*?\n\s*\}/g);
if (getterMatches) {
getterMatches.forEach(match => {
const nameMatch = match.match(/public function get(\w+)Attr/);
if (nameMatch) {
const fieldName = this.toCamelCase(nameMatch[1]);
const fieldType = this.determineFieldType(fieldName, typeMap);
fields.push(` @Column({ name: '${this.toSnakeCase(fieldName)}', type: '${fieldType}' })
${fieldName}: ${this.getTypeScriptType(fieldType)};`);
}
});
}
// 如果没有找到getter方法尝试从注释或其他地方提取字段信息
if (fields.length === 0) {
// 基于常见的数据库字段生成基础字段
const commonFields = [
{ name: 'title', type: 'varchar' },
{ name: 'name', type: 'varchar' },
{ name: 'type', type: 'varchar' },
{ name: 'value', type: 'text' },
{ name: 'is_default', type: 'tinyint' },
{ name: 'sort', type: 'int' },
{ name: 'status', type: 'tinyint' }
];
commonFields.forEach(field => {
if (phpContent.includes(field.name) || phpContent.includes(`'${field.name}'`)) {
fields.push(` @Column({ name: '${field.name}', type: '${field.type}' })
${this.toCamelCase(field.name)}: ${this.getTypeScriptType(field.type)};`);
}
});
}
return fields.join('\n\n');
}
/**
* 确定字段类型
*/
determineFieldType(fieldName, typeMap) {
if (typeMap[fieldName]) {
return typeMap[fieldName];
}
// 基于字段名推断类型
if (fieldName.includes('time') || fieldName.includes('date')) {
return 'timestamp';
} else if (fieldName.includes('id')) {
return 'int';
} else if (fieldName.includes('status') || fieldName.includes('is_')) {
return 'tinyint';
} else if (fieldName.includes('sort') || fieldName.includes('order')) {
return 'int';
} else {
return 'varchar';
}
}
/**
* 获取TypeScript类型
*/
getTypeScriptType(phpType) {
const typeMap = {
'varchar': 'string',
'text': 'string',
'int': 'number',
'tinyint': 'number',
'timestamp': 'Date',
'datetime': 'Date',
'json': 'object'
};
return typeMap[phpType] || 'string';
}
/**
* 转换为camelCase
*/
toCamelCase(str) {
return str.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
}
/**
* 转换为snake_case
*/
toSnakeCase(str) {
return str.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
}
/**
* 生成默认实体字段 - 禁止假设,仅返回空
*/
generateEntityFields(modelName) {
// 禁止假设字段,返回空字符串
// 所有字段必须基于真实PHP模型解析
console.log(` ⚠️ 禁止假设字段请基于真实PHP模型: ${modelName}`);
return '';
}
/**
* 获取表名
*/
getTableName(modelName) {
// 禁止假设表名表名必须从PHP模型的$name属性获取
// 这里返回空字符串强制从PHP源码解析
console.log(` ⚠️ 禁止假设表名必须从PHP模型解析: ${modelName}`);
return '';
}
/**
* 转换为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模型
*/
hasPHPModels(moduleName) {
const phpProjectPath = path.join(__dirname, '../../niucloud-php/niucloud');
const modelPath = path.join(phpProjectPath, 'app/model', moduleName);
if (!fs.existsSync(modelPath)) return false;
// 检查目录内是否有PHP文件
try {
const files = fs.readdirSync(modelPath);
return files.some(file => file.endsWith('.php'));
} catch (error) {
return false;
}
}
/**
* 确保目录存在
*/
ensureDir(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
/**
* 输出统计报告
*/
printStats() {
super.printStats({
'Entities Created': this.entityStats.entitiesCreated,
'Entities Skipped': this.entityStats.entitiesSkipped
});
}
}
// 如果直接运行此文件
if (require.main === module) {
const generator = new EntityGenerator();
generator.run().catch(console.error);
}
module.exports = EntityGenerator;