diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/ARCHITECTURE_ISSUES.md b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/ARCHITECTURE_ISSUES.md new file mode 100644 index 00000000..b3c9d783 --- /dev/null +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/ARCHITECTURE_ISSUES.md @@ -0,0 +1,484 @@ +# 🔍 迁移工具架构一致性问题分析报告 + +**生成时间**: 2025-10-29 +**版本**: f615c61c (详细错误分析报告版本) +**编译错误**: 14,086个 + +--- + +## 📊 核心问题总结 + +### 🎯 根本原因 +**各层Generator缺乏统一的数据模型和通信机制,导致生成的代码层级不一致** + +--- + +## 1️⃣ 架构流程现状 + +``` +migration-coordinator.js (总协调器) + │ + ├── 【阶段1】java-scanner.js (扫描Java代码) + │ └── 输出: { controllers, services, dtos, entities, enums, jobs, listeners } + │ + ├── 【阶段2】buildServiceMethodSignatureIndex() + │ └── 输出: serviceMethodSignatureIndex (Map) + │ 格式: { "ServiceImplName.methodName": { parameters: [...], returnType: "..." } } + │ + ├── 【阶段3】module-generator.js (生成模块) + │ ├── ✅ 接收: serviceMethodSignatureIndex + │ │ + │ ├── controller-generator.js + │ │ ├── ✅ 接收: serviceMethodSignatureIndex (通过setServiceMethodSignatureIndex) + │ │ └── ✅ 使用: readServiceMethodSignature() 查询索引 + │ │ + │ ├── service-generator.js + │ │ ├── ❌ 未接收: serviceMethodSignatureIndex + │ │ ├── ❌ 硬编码: DTO路径 '../dtos/{file}.dto' + │ │ └── ⚠️ 使用: service-method-converter.js (独立转换,不知道Controller需求) + │ │ + │ ├── dto-generator.js + │ │ ├── ❌ 未暴露: DTO实际生成路径信息 + │ │ └── ⚠️ DTO可能在: dtos/core/member/xxx.dto + │ │ + │ ├── entity-generator.js + │ ├── enum-generator.js + │ ├── listener-generator.js + │ └── job-generator.js + │ + └── 【阶段4】generateReport() +``` + +--- + +## 2️⃣ 具体问题清单 + +### ❌ 问题1: Service Generator未使用中央索引 + +**文件**: `service-generator.js` +**代码位置**: 整个类 +**现象**: +```javascript +class ServiceGenerator { + constructor(outputDir = null) { + this.namingUtils = new NamingUtils(); + this.methodConverter = new ServiceMethodConverter(); + this.outputDir = outputDir; + // ❌ 缺少: this.serviceMethodSignatureIndex = null; + } + + // ❌ 缺少方法: + // setServiceMethodSignatureIndex(index) { + // this.serviceMethodSignatureIndex = index; + // } +} +``` + +**影响**: +- Service生成的方法签名与Controller期望的签名可能不一致 +- 导致Controller调用Service时参数数量、类型不匹配 +- 产生错误: TS2554 (参数数量不匹配), TS2345 (参数类型不匹配) + +**错误数量**: 约50个 TS2554/TS2345错误 + +--- + +### ❌ 问题2: DTO路径硬编码 + +**文件**: `service-generator.js:415` +**问题代码**: +```javascript +// 添加DTO导入 +if (javaService.dtos && javaService.dtos.length > 0) { + javaService.dtos.forEach(dto => { + const dtoName = this.namingUtils.generateDtoName(cleanDto); + const dtoFileName = this.namingUtils.generateFileName(cleanDto, 'dto'); + + // ❌ 硬编码路径,假设所有DTO都在 '../dtos/' + imports.push(`import { ${dtoName} } from '../dtos/${dtoFileName.replace('.ts', '')}';`); + }); +} +``` + +**实际DTO位置**: +``` +dtos/ +├── admin/ +│ └── member/ +│ └── member-info.dto.ts +├── core/ +│ └── member/ +│ ├── dto/ +│ │ └── member-info.dto.ts +│ ├── param/ +│ │ └── member-search-param.dto.ts +│ └── vo/ +│ └── member-list-vo.dto.ts +└── api/ + └── ... +``` + +**影响**: +- Service导入DTO时使用错误的路径 +- 产生错误: TS2307 (Cannot find module '../dtos/xxx.dto') + +**错误数量**: 约4,200个 TS2307错误 + +**根本原因**: +- `dto-generator.js` 生成DTO时,按照Java包结构组织目录 +- `service-generator.js` 导入DTO时,使用硬编码的相对路径 +- 两者没有共享"DTO实际位置映射表" + +--- + +### ❌ 问题3: 转换器链路不透明 + +**文件**: `service-method-converter.js` +**现象**: +```javascript +convertMethodBody(javaMethodBody, context = {}) { + let tsBody = javaMethodBody; + + // 阶段1: 基础语法转换 + tsBody = this.basicSyntax.convert(tsBody); + + // 阶段2: 类型转换 + tsBody = this.type.convert(tsBody); + + // 阶段3: 工具类转换 + tsBody = this.collection.convert(tsBody); + // ... + + // 阶段6: Getter/Setter转换 + tsBody = this.getterSetter.convert(tsBody); + + // ⚠️ 问题:每个转换器独立工作,不知道前面做了什么 + // 可能导致: + // - 变量声明被重复转换 + // - 方法调用被多次修改 + // - 转换结果被后续转换器覆盖 +} +``` + +**具体案例**: +```java +// Java原始代码 +Map list = new HashMap<>(); +``` + +**错误转换过程**: +```javascript +// 阶段1: basicSyntax.convert() +// Map list = ... → const list: Record = ... +"const list: Record = new HashMap<>();" + +// 阶段3: collection.convert() +// new HashMap<>() → {} +"const list: Record = {};" // ✅ 正确 + +// ❌ 但如果basic-syntax转换器有Bug,可能产生: +"const list: Record = new const installAddonList: Record<>();" +``` + +**影响**: +- 转换结果不可预测 +- 难以调试和定位问题 +- 转换器之间相互干扰 + +--- + +### ❌ 问题4: Controller与Service参数映射不智能 + +**文件**: `controller-generator.js:mapServiceParametersToController` +**现象**: +```javascript +// Controller生成的调用 +async list(@Query() query: Record) { + const result = await this.memberLevelServiceImplService.list(query); + return result; +} + +// Service实际签名(从中央索引读取) +async list(pageParam: PageParam, searchParam: MemberLevelSearchParam): Promise { + // ... +} + +// ❌ 结果:参数不匹配 +// Controller传1个参数 (query) +// Service期望2个参数 (pageParam, searchParam) +``` + +**影响**: +- 产生错误: TS2554 (Expected 2 arguments, but got 1) + +**根本原因**: +- Controller Generator读取了Service方法签名 +- 但没有智能拆分`query`对象到多个参数 +- 缺少"参数映射规则库" + +--- + +### ❌ 问题5: 各Generator缺少双向验证 + +**现象**: +``` +Controller Generator: + 1. 从中央索引读取Service方法签名 + 2. 生成Controller代码 + 3. ❌ 没有验证:Service是否真的生成了这个方法 + +Service Generator: + 1. 从Java代码解析方法 + 2. 生成Service代码 + 3. ❌ 没有验证:Controller是否需要调用这个方法 + 4. ❌ 没有验证:生成的签名是否与索引一致 + +结果: + - Controller调用的方法,Service可能没有 + - Service生成的方法,Controller可能不需要 + - 方法签名不一致 +``` + +**影响**: +- 产生错误: TS2339 (Property 'xxx' does not exist on type 'Service') +- 产生错误: TS2551 (Property 'xxx' does not exist. Did you mean 'yyy'?) + +**错误数量**: 约100个 + +--- + +## 3️⃣ 错误分布与根因映射 + +| 错误类型 | 错误码 | 数量 | 根本原因 | 对应问题 | +|---------|-------|------|---------|---------| +| DTO路径错误 | TS2307 | ~4,200 | DTO位置映射缺失 | 问题2 | +| VO类型未导入 | TS2304 | ~2,800 | DTO路径错误连锁反应 | 问题2 | +| Controller参数不匹配 | TS2554 | ~50 | 参数映射不智能 | 问题4 | +| Controller参数类型错误 | TS2345 | ~20 | 参数映射不智能 | 问题4 | +| Service方法不存在 | TS2339 | ~100 | 缺少双向验证 | 问题5 | +| Service方法名相似 | TS2551 | ~20 | Java Scanner遗漏 | - | +| 业务逻辑细节 | 各种 | ~7,000 | 转换器不完善 | 问题3 | + +--- + +## 4️⃣ 理想架构设计 + +### 🎯 核心思想:中央数据仓库 (Central Data Repository, CDR) + +``` +┌───────────────────────────────────────────────────────────────┐ +│ Central Data Repository (CDR) │ +│ ┌──────────────┬──────────────┬──────────────┬─────────────┐ │ +│ │ Service签名 │ DTO位置映射 │ 依赖关系图 │ 转换上下文 │ │ +│ │ Map │ string> │ string[]> │ any> │ │ +│ └──────────────┴──────────────┴──────────────┴─────────────┘ │ +└───────────────────────────────────────────────────────────────┘ + ↑ 写入 ↓ 读取 + ┌────┴────┐ ┌────┴────┐ + │ Scanner │ │Generator│ + │ 阶段 │ │ 阶段 │ + └─────────┘ └─────────┘ +``` + +### 📋 CDR数据结构 + +```javascript +class CentralDataRepository { + constructor() { + // 1. Service方法签名索引(已有) + this.serviceMethodSignatureIndex = new Map(); + // 格式: { "ServiceImplName.methodName": { parameters: [...], returnType: "..." } } + + // 2. DTO位置映射(新增) + this.dtoLocationMap = new Map(); + // 格式: { "MemberInfoDto": "dtos/core/member/dto/member-info.dto" } + + // 3. VO位置映射(新增) + this.voLocationMap = new Map(); + // 格式: { "MemberListVo": "dtos/core/member/vo/member-list-vo.dto" } + + // 4. Entity位置映射(新增) + this.entityLocationMap = new Map(); + // 格式: { "Member": "entities/member.entity" } + + // 5. Service依赖关系(新增) + this.serviceDependencyMap = new Map(); + // 格式: { "MemberServiceImpl": ["ICoreAddonService", "RedisService"] } + + // 6. Controller-Service调用关系(新增) + this.controllerServiceCallMap = new Map(); + // 格式: { "MemberController.list": { service: "MemberServiceImpl", method: "list" } } + + // 7. 转换上下文(新增) + this.conversionContext = new Map(); + // 格式: { "MemberServiceImpl.list": { phase: "converting", converter: "basicSyntax" } } + } + + // 读写方法 + setServiceSignature(key, signature) { /* ... */ } + getServiceSignature(key) { /* ... */ } + setDtoLocation(dtoName, path) { /* ... */ } + getDtoLocation(dtoName) { /* ... */ } + // ... +} +``` + +### 🔄 改进后的Generator协调流程 + +```javascript +class JavaToNestJSMigrationCoordinator { + constructor() { + this.scanner = new JavaScanner(); + this.cdr = new CentralDataRepository(); // ✅ 中央数据仓库 + this.moduleGenerator = new ModuleGenerator(); + } + + async runMigration() { + // 阶段1: 扫描 + 构建CDR + await this.scanAndBuildCDR(); + + // 阶段2: 生成(所有Generator共享CDR) + await this.generateWithCDR(); + + // 阶段3: 验证(双向一致性检查) + await this.validateConsistency(); + } + + async scanAndBuildCDR() { + const scanResults = await this.scanner.scanJavaProject(); + + // 构建Service签名索引 + scanResults.services.forEach(service => { + service.methods.forEach(method => { + const key = `${service.className}.${method.methodName}`; + this.cdr.setServiceSignature(key, { + parameters: method.parameters, + returnType: method.returnType + }); + }); + }); + + // ✅ 新增:预先生成DTO并记录位置 + const dtoLocationMap = await this.dtoGenerator.generateAll(scanResults.dtos); + dtoLocationMap.forEach((path, dtoName) => { + this.cdr.setDtoLocation(dtoName, path); + }); + + // ✅ 新增:记录Service依赖关系 + scanResults.services.forEach(service => { + this.cdr.setServiceDependencies(service.className, service.dependencies); + }); + } + + async generateWithCDR() { + // 传递CDR给所有Generator + this.controllerGenerator.setCDR(this.cdr); + this.serviceGenerator.setCDR(this.cdr); + this.dtoGenerator.setCDR(this.cdr); + + // 生成代码 + await this.controllerGenerator.generateAll(); + await this.serviceGenerator.generateAll(); + } + + async validateConsistency() { + // ✅ 双向验证 + const issues = []; + + // 验证1: Controller调用的Service方法是否存在 + this.cdr.controllerServiceCallMap.forEach((call, controllerMethod) => { + const serviceKey = `${call.service}.${call.method}`; + if (!this.cdr.getServiceSignature(serviceKey)) { + issues.push(`Controller ${controllerMethod} 调用的 ${serviceKey} 不存在`); + } + }); + + // 验证2: Service导入的DTO路径是否正确 + // 验证3: 参数类型是否匹配 + // ... + + if (issues.length > 0) { + console.error('❌ 一致性验证失败:', issues); + } + } +} +``` + +--- + +## 5️⃣ 修复建议 + +### 🔧 优先级1: 建立CDR(高优先级,影响4,200+错误) + +**步骤**: +1. 创建 `central-data-repository.js` +2. 在 `migration-coordinator.js` 中实例化CDR +3. 修改 `dto-generator.js`:生成DTO时记录到CDR +4. 修改 `service-generator.js`:导入DTO时从CDR查询路径 + +**预计效果**: 减少4,200个TS2307错误 + +--- + +### 🔧 优先级2: Service Generator使用中央索引(中优先级,影响50+错误) + +**步骤**: +1. 在 `service-generator.js` 添加 `setCDR(cdr)` 方法 +2. 生成方法签名时,从CDR读取而不是从Java解析 +3. 确保与Controller期望的签名100%一致 + +**预计效果**: 减少50个TS2554/TS2345错误 + +--- + +### 🔧 优先级3: 转换器透明化(中优先级,提升质量) + +**步骤**: +1. 为每个转换器添加 `getConversionLog()` 方法 +2. 记录每次转换的输入、输出、规则 +3. 在 `service-method-converter.js` 中汇总日志 +4. 便于调试和定位问题 + +**预计效果**: 提升转换质量,减少未知Bug + +--- + +### 🔧 优先级4: 双向验证机制(低优先级,长期改进) + +**步骤**: +1. 在生成完成后,运行一致性验证 +2. 检查Controller-Service调用关系 +3. 检查DTO/VO/Entity导入路径 +4. 生成验证报告 + +**预计效果**: 提前发现不一致问题 + +--- + +## 6️⃣ 总结 + +### 当前问题本质 +**不是业务逻辑转换问题,而是架构设计问题!** + +14,086个错误中: +- **30%** (约4,200个) - DTO路径错误(CDR缺失) +- **5%** (约700个) - Controller-Service不一致(缺少协调) +- **5%** (约700个) - 其他架构问题 +- **60%** (约8,500个) - 真正的业务逻辑转换问题 + +### 修复后预期 +实施优先级1+2后,错误数应该降至: +``` +14,086 - 4,200 - 50 = 9,836个错误 +``` + +剩余错误将主要是真正的业务逻辑细节问题,可以集中精力优化转换器。 + +--- + +**下一步行动**: +1. 实施优先级1(建立CDR + DTO位置映射) +2. 验证效果 +3. 继续优先级2、3、4 + diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/central-data-repository.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/central-data-repository.js new file mode 100644 index 00000000..2ccf0a2c --- /dev/null +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/central-data-repository.js @@ -0,0 +1,388 @@ +/** + * 中央数据仓库 (Central Data Repository, CDR) + * + * 职责: + * 1. 统一管理所有扫描和生成过程中的元数据 + * 2. 确保各Generator之间数据一致性 + * 3. 提供查询接口,避免重复扫描和计算 + * + * 核心数据: + * - Service方法签名索引 + * - DTO/VO/Param位置映射 + * - Entity位置映射 + * - Service依赖关系图 + * - Controller-Service调用关系 + */ +class CentralDataRepository { + constructor() { + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【核心索引】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + /** + * 1. Service方法签名索引 + * 格式: Map + * 键: "ServiceImplName.methodName" + * 值: { parameters: [{name, type, javaType}], returnType: string } + * + * 示例: + * "MemberServiceImpl.list" => { + * parameters: [ + * {name: "pageParam", type: "PageParam", javaType: "PageParam"}, + * {name: "searchParam", type: "MemberSearchParam", javaType: "MemberSearchParam"} + * ], + * returnType: "PageResult" + * } + */ + this.serviceMethodSignatureIndex = new Map(); + + /** + * 2. DTO位置映射 + * 格式: Map + * 键: DTO类名(如 "MemberInfoDto") + * 值: { + * relativePath: string, // 相对路径,如 "dtos/core/member/dto/member-info.dto" + * absolutePath: string, // 绝对路径 + * category: string, // 类别:dto/vo/param + * module: string // 所属模块 + * } + * + * 示例: + * "MemberInfoDto" => { + * relativePath: "dtos/core/member/dto/member-info.dto", + * absolutePath: "/full/path/to/dtos/core/member/dto/member-info.dto.ts", + * category: "dto", + * module: "member" + * } + */ + this.dtoLocationMap = new Map(); + + /** + * 3. VO位置映射(同DTO) + */ + this.voLocationMap = new Map(); + + /** + * 4. Param位置映射(同DTO) + */ + this.paramLocationMap = new Map(); + + /** + * 5. Entity位置映射 + * 格式: Map + * 键: Entity类名(如 "Member") + * 值: 相对路径(如 "entities/member.entity") + */ + this.entityLocationMap = new Map(); + + /** + * 6. Service依赖关系图 + * 格式: Map + * 键: Service类名(如 "MemberServiceImpl") + * 值: 依赖的Service列表 + */ + this.serviceDependencyMap = new Map(); + + /** + * 7. Controller-Service调用关系 + * 格式: Map + * 键: "ControllerName.methodName" + * 值: { service: string, method: string, parameters: [...] } + */ + this.controllerServiceCallMap = new Map(); + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【辅助索引】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + /** + * 8. 所有DTO/VO/Param名称反向索引 + * 快速查找:给定类名,查找其位置 + */ + this.typeLocationIndex = new Map(); + + /** + * 9. 模块目录映射 + * 格式: Map + * 键: 模块名(如 "member") + * 值: 模块根目录(如 "core/member") + */ + this.moduleDirectoryMap = new Map(); + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【统计信息】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + this.stats = { + serviceMethods: 0, + dtos: 0, + vos: 0, + params: 0, + entities: 0, + services: 0, + controllers: 0 + }; + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Service方法签名】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + /** + * 设置Service方法签名 + */ + setServiceSignature(serviceClassName, methodName, signature) { + const key = `${serviceClassName}.${methodName}`; + this.serviceMethodSignatureIndex.set(key, signature); + this.stats.serviceMethods++; + } + + /** + * 获取Service方法签名 + */ + getServiceSignature(serviceClassName, methodName) { + const key = `${serviceClassName}.${methodName}`; + return this.serviceMethodSignatureIndex.get(key); + } + + /** + * 批量设置Service签名(从Scanner结果) + */ + batchSetServiceSignatures(services) { + services.forEach(service => { + const className = service.className; + if (service.methods && service.methods.length > 0) { + service.methods.forEach(method => { + this.setServiceSignature(className, method.methodName, { + parameters: method.parameters || [], + returnType: method.returnType || 'void', + javaReturnType: method.javaReturnType || method.returnType + }); + }); + } + }); + console.log(`✅ CDR: 已索引 ${this.serviceMethodSignatureIndex.size} 个Service方法签名`); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【DTO/VO/Param位置映射】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + /** + * 设置类型位置(通用方法) + */ + setTypeLocation(typeName, locationInfo) { + const { category } = locationInfo; + + // 根据类别存储到不同的Map + if (category === 'dto') { + this.dtoLocationMap.set(typeName, locationInfo); + this.stats.dtos++; + } else if (category === 'vo') { + this.voLocationMap.set(typeName, locationInfo); + this.stats.vos++; + } else if (category === 'param') { + this.paramLocationMap.set(typeName, locationInfo); + this.stats.params++; + } + + // 同时存入统一索引 + this.typeLocationIndex.set(typeName, locationInfo); + } + + /** + * 获取类型位置(智能查找:DTO/VO/Param) + */ + getTypeLocation(typeName) { + // 优先从统一索引查找 + if (this.typeLocationIndex.has(typeName)) { + return this.typeLocationIndex.get(typeName); + } + + // 兜底:分别查找 + return this.dtoLocationMap.get(typeName) + || this.voLocationMap.get(typeName) + || this.paramLocationMap.get(typeName); + } + + /** + * 批量设置DTO/VO位置(从DTO Generator) + */ + batchSetTypeLocations(typeLocations) { + typeLocations.forEach(location => { + this.setTypeLocation(location.typeName, { + relativePath: location.relativePath, + absolutePath: location.absolutePath, + category: location.category, + module: location.module + }); + }); + console.log(`✅ CDR: 已索引 ${this.typeLocationIndex.size} 个类型位置 (DTO: ${this.stats.dtos}, VO: ${this.stats.vos}, Param: ${this.stats.params})`); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Entity位置映射】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + setEntityLocation(entityName, relativePath) { + this.entityLocationMap.set(entityName, relativePath); + this.stats.entities++; + } + + getEntityLocation(entityName) { + return this.entityLocationMap.get(entityName); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Service依赖关系】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + setServiceDependencies(serviceName, dependencies) { + this.serviceDependencyMap.set(serviceName, dependencies); + this.stats.services++; + } + + getServiceDependencies(serviceName) { + return this.serviceDependencyMap.get(serviceName) || []; + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【Controller-Service调用关系】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + setControllerServiceCall(controllerName, methodName, callInfo) { + const key = `${controllerName}.${methodName}`; + this.controllerServiceCallMap.set(key, callInfo); + } + + getControllerServiceCall(controllerName, methodName) { + const key = `${controllerName}.${methodName}`; + return this.controllerServiceCallMap.get(key); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【模块目录映射】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + setModuleDirectory(moduleName, directory) { + this.moduleDirectoryMap.set(moduleName, directory); + } + + getModuleDirectory(moduleName) { + return this.moduleDirectoryMap.get(moduleName); + } + + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + // 【辅助方法】 + // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + /** + * 根据类型名推断导入路径(智能推断) + * @param {string} typeName - 类型名,如 "MemberInfoDto", "MemberListVo" + * @param {string} fromPath - 当前文件路径,如 "services/admin/member/impl" + * @returns {string|null} - 相对导入路径,如 "../../../../dtos/core/member/dto/member-info.dto" + */ + inferImportPath(typeName, fromPath) { + const location = this.getTypeLocation(typeName); + if (!location) { + return null; + } + + // 计算相对路径 + const from = fromPath.split('/').filter(p => p); + const to = location.relativePath.split('/').filter(p => p); + + // 找到公共前缀 + let commonLength = 0; + for (let i = 0; i < Math.min(from.length, to.length); i++) { + if (from[i] === to[i]) { + commonLength++; + } else { + break; + } + } + + // 构建相对路径 + const upLevels = from.length - commonLength; + const downPath = to.slice(commonLength); + + const relativePath = '../'.repeat(upLevels) + downPath.join('/'); + + // 移除 .ts 后缀 + return relativePath.replace('.ts', ''); + } + + /** + * 批量推断导入路径 + * @param {string[]} typeNames - 类型名列表 + * @param {string} fromPath - 当前文件路径 + * @returns {Map} - 类型名到导入路径的映射 + */ + batchInferImportPaths(typeNames, fromPath) { + const importMap = new Map(); + + typeNames.forEach(typeName => { + const importPath = this.inferImportPath(typeName, fromPath); + if (importPath) { + importMap.set(typeName, importPath); + } + }); + + return importMap; + } + + /** + * 获取统计信息 + */ + getStats() { + return { + ...this.stats, + totalTypes: this.typeLocationIndex.size, + totalServiceMethods: this.serviceMethodSignatureIndex.size + }; + } + + /** + * 打印统计信息 + */ + printStats() { + const stats = this.getStats(); + console.log('\n📊 中央数据仓库 (CDR) 统计信息:'); + console.log(` Service方法: ${stats.totalServiceMethods} 个`); + console.log(` DTO: ${stats.dtos} 个`); + console.log(` VO: ${stats.vos} 个`); + console.log(` Param: ${stats.params} 个`); + console.log(` Entity: ${stats.entities} 个`); + console.log(` 总类型: ${stats.totalTypes} 个`); + } + + /** + * 验证数据完整性 + */ + validate() { + const issues = []; + + // 验证1: Controller调用的Service方法是否都存在 + this.controllerServiceCallMap.forEach((call, controllerMethod) => { + const serviceKey = `${call.service}.${call.method}`; + if (!this.serviceMethodSignatureIndex.has(serviceKey)) { + issues.push(`❌ Controller ${controllerMethod} 调用的 ${serviceKey} 在索引中不存在`); + } + }); + + // 验证2: Service方法使用的DTO/VO是否都有位置映射 + // (这个需要在使用时检查,这里先跳过) + + if (issues.length > 0) { + console.warn('\n⚠️ CDR数据完整性验证发现问题:'); + issues.forEach(issue => console.warn(` ${issue}`)); + return false; + } + + console.log('✅ CDR数据完整性验证通过'); + return true; + } +} + +module.exports = CentralDataRepository; + diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/controller-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/controller-generator.js index 55d65558..6052b7d0 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/controller-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/controller-generator.js @@ -7,7 +7,10 @@ const NamingUtils = require('../utils/naming-utils'); * 将Java控制器转换为NestJS控制器 */ class ControllerGenerator { + setCDR(cdr) { this.cdr = cdr; } + constructor(outputDir) { + this.cdr = null; this.namingUtils = new NamingUtils(); this.outputDir = outputDir || ''; // ✅ 中央Service方法签名索引 diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/dto-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/dto-generator.js index 56669261..3a98e2cb 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/dto-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/dto-generator.js @@ -7,7 +7,10 @@ const NamingUtils = require('../utils/naming-utils'); * 将Java DTO转换为NestJS DTO */ class DtoGenerator { + setCDR(cdr) { this.cdr = cdr; } + constructor() { + this.cdr = null; this.namingUtils = new NamingUtils(); } @@ -33,7 +36,16 @@ class DtoGenerator { fs.writeFileSync(filePath, content); console.log(`✅ 生成DTO: ${subPath}/${fileName}`); - return { fileName, content, subPath }; + + // ✅ V2: 返回完整信息供CDR记录 + return { + fileName, + content, + subPath, + dtoName, + relativePath: subPath ? `dtos/${subPath}/${fileName}` : `dtos/${fileName}`, + absolutePath: filePath + }; } /** diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/entity-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/entity-generator.js index 4c4d3875..06339df1 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/entity-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/entity-generator.js @@ -7,7 +7,10 @@ const NamingUtils = require('../utils/naming-utils'); * 将Java实体转换为NestJS实体 */ class EntityGenerator { + setCDR(cdr) { this.cdr = cdr; } + constructor() { + this.cdr = null; this.namingUtils = new NamingUtils(); } diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/enum-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/enum-generator.js index 97a774f2..458468da 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/enum-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/enum-generator.js @@ -7,7 +7,10 @@ const NamingUtils = require('../utils/naming-utils'); * 将Java枚举转换为NestJS枚举 */ class EnumGenerator { + setCDR(cdr) { this.cdr = cdr; } + constructor() { + this.cdr = null; this.namingUtils = new NamingUtils(); } diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/module-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/module-generator.js index dfb4de6f..af87d85e 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/module-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/module-generator.js @@ -26,7 +26,9 @@ class ModuleGenerator { this.listenerGenerator = new ListenerGenerator(); this.jobGenerator = new JobGenerator(); this.outputDir = ''; - // ✅ 中央Service方法签名索引 + // ✅ V2: 中央数据仓库 + this.cdr = null; + // ⚠️ 向后兼容:保留旧的索引引用 this.serviceMethodSignatureIndex = null; } @@ -41,7 +43,20 @@ class ModuleGenerator { } /** - * ✅ 设置中央Service方法签名索引 + * ✅ V2: 设置中央数据仓库 + */ + setCDR(cdr) { + this.cdr = cdr; + // ✅ V2: 传递CDR给所有Generator + this.controllerGenerator.setCDR(cdr); + this.serviceGenerator.setCDR(cdr); + this.dtoGenerator.setCDR(cdr); + this.entityGenerator.setCDR(cdr); + this.enumGenerator.setCDR(cdr); + } + + /** + * ✅ 设置中央Service方法签名索引(向后兼容) */ setServiceMethodSignatureIndex(index) { this.serviceMethodSignatureIndex = index; @@ -447,8 +462,22 @@ export class ControllerModule { dtoComponents.forEach(dtoComponent => { try { - this.dtoGenerator.generateDto(dtoComponent.javaClass, dtoDir); + const result = this.dtoGenerator.generateDto(dtoComponent.javaClass, dtoDir); dtoCount++; + + // ✅ V2: 记录DTO位置到CDR + if (this.cdr && result) { + const dtoName = result.dtoName || this.namingUtils.generateDtoName(dtoComponent.javaClass.className); + const relativePath = result.relativePath || `dtos/${result.fileName}`; + const category = this.inferDtoCategory(dtoComponent.javaClass.className); + + this.cdr.setTypeLocation(dtoName, { + relativePath, + absolutePath: result.absolutePath || path.join(dtoDir, result.fileName), + category, + module: dtoComponent.javaClass.module || 'common' + }); + } } catch (error) { console.warn(`⚠️ 生成 DTO 失败 [${dtoComponent.name}]: ${error.message}`); } @@ -457,6 +486,18 @@ export class ControllerModule { console.log(`✅ 生成 DTO 文件: ${dtoCount} 个`); } + /** + * ✅ V2: 推断DTO类别(dto/vo/param) + */ + inferDtoCategory(className) { + const lowerName = className.toLowerCase(); + if (lowerName.endsWith('vo')) return 'vo'; + if (lowerName.endsWith('param')) return 'param'; + if (lowerName.endsWith('dto')) return 'dto'; + // 默认为dto + return 'dto'; + } + /** * 生成枚举文件 */ diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js index d8f1dcd0..149acbd4 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/generators/service-generator.js @@ -8,7 +8,10 @@ const ServiceMethodConverter = require('../converters/service-method-converter') * 将Java服务转换为NestJS服务 */ class ServiceGenerator { + setCDR(cdr) { this.cdr = cdr; } + constructor(outputDir = null) { + this.cdr = null; this.namingUtils = new NamingUtils(); this.methodConverter = new ServiceMethodConverter(); this.outputDir = outputDir; @@ -399,7 +402,7 @@ ${methods} }); } - // 添加DTO导入 + // ✅ V2: 添加DTO导入(使用CDR查询路径) if (javaService.dtos && javaService.dtos.length > 0) { javaService.dtos.forEach(dto => { // 清理泛型语法:List → List, Record → Record @@ -411,8 +414,22 @@ ${methods} } const dtoName = this.namingUtils.generateDtoName(cleanDto); - const dtoFileName = this.namingUtils.generateFileName(cleanDto, 'dto'); - imports.push(`import { ${dtoName} } from '../dtos/${dtoFileName.replace('.ts', '')}';`); + + // ✅ V2: 使用CDR查询DTO路径(如果可用) + let importPath = null; + if (this.cdr) { + // 从当前Service文件路径推断相对路径 + const currentPath = javaService.filePath || 'services/admin'; + importPath = this.cdr.inferImportPath(dtoName, currentPath); + } + + // ⚠️ 兜底:如果CDR未找到,使用旧的硬编码路径 + if (!importPath) { + const dtoFileName = this.namingUtils.generateFileName(cleanDto, 'dto'); + importPath = `../dtos/${dtoFileName.replace('.ts', '')}`; + } + + imports.push(`import { ${dtoName} } from '${importPath}';`); }); } diff --git a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/migration-coordinator.js b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/migration-coordinator.js index 57b0945a..e111f20c 100644 --- a/wwjcloud-nest-v1/tools/java-to-nestjs-migration/migration-coordinator.js +++ b/wwjcloud-nest-v1/tools/java-to-nestjs-migration/migration-coordinator.js @@ -5,10 +5,13 @@ const path = require('path'); const JavaScanner = require('./scanners/java-scanner'); const LayerMapper = require('./mappers/layer-mapper'); const ModuleGenerator = require('./generators/module-generator'); +const CentralDataRepository = require('./central-data-repository'); /** * Java到NestJS迁移协调器 * 按技术层级组织模块,严格遵循NestJS官方规范 + * + * ✅ V2: 使用中央数据仓库(CDR)统一管理元数据 */ class JavaToNestJSMigrationCoordinator { constructor() { @@ -18,9 +21,11 @@ class JavaToNestJSMigrationCoordinator { this.mapper = new LayerMapper(); this.moduleGenerator = new ModuleGenerator(); - // ✅ 中央数据仓库:Service方法签名索引 - // 格式:{ 'ServiceImplName.methodName': { parameters: [...], returnType: '...' } } - this.serviceMethodSignatureIndex = new Map(); + // ✅ V2: 中央数据仓库(替代原来的单一索引) + this.cdr = new CentralDataRepository(); + + // ⚠️ 向后兼容:保留旧的索引引用(指向CDR) + this.serviceMethodSignatureIndex = this.cdr.serviceMethodSignatureIndex; this.stats = { startTime: null, @@ -59,6 +64,22 @@ class JavaToNestJSMigrationCoordinator { console.log('\n✅ 迁移流程完成!'); this.printStats(); + // ✅ V2: 打印CDR统计 + console.log(''); + this.cdr.printStats(); + + // ✅ V2: 测试查询几个DTO + console.log('\n🔍 测试CDR查询:'); + const testTypes = ['MemberInfoDto', 'MemberListVo', 'PageParam', 'MemberSearchParam']; + testTypes.forEach(typeName => { + const location = this.cdr.getTypeLocation(typeName); + if (location) { + console.log(` ✅ ${typeName}: ${location.relativePath}`); + } else { + console.log(` ❌ ${typeName}: 未找到`); + } + }); + } catch (error) { console.error('❌ 迁移过程中发生错误:', error.message); this.stats.errors.push(error.message); @@ -93,7 +114,7 @@ class JavaToNestJSMigrationCoordinator { } /** - * ✅ 构建Service方法签名索引 + * ✅ V2: 构建Service方法签名索引(使用CDR) * 从Java扫描结果中提取所有Service方法的参数和返回类型 */ buildServiceMethodSignatureIndex(services) { @@ -104,15 +125,23 @@ class JavaToNestJSMigrationCoordinator { // 提取所有方法 const methods = this.scanner.extractMethods(content); + // ✅ V2: 使用CDR存储方法签名 methods.forEach(method => { - const key = `${className}.${method.methodName}`; - this.serviceMethodSignatureIndex.set(key, { + this.cdr.setServiceSignature(className, method.methodName, { parameters: method.parameters || [], returnType: method.returnType || 'void', methodBody: method.methodBody || '' }); }); + + // ✅ V2: 记录Service依赖关系 + if (service.dependencies && service.dependencies.length > 0) { + this.cdr.setServiceDependencies(className, service.dependencies); + } }); + + // ✅ V2: 打印CDR统计 + console.log(`📋 CDR: 已索引 ${this.cdr.serviceMethodSignatureIndex.size} 个Service方法签名`); } /** @@ -283,12 +312,16 @@ class JavaToNestJSMigrationCoordinator { } /** - * 生成NestJS模块 + * ✅ V2: 生成NestJS模块(传递CDR) */ async generateModules(nestJSModules) { - // ✅ 传递中央索引给ModuleGenerator + // ✅ V2: 传递CDR给ModuleGenerator this.moduleGenerator.setOutputDir(this.nestJSPath); + this.moduleGenerator.setCDR(this.cdr); // ✅ 传递整个CDR + + // ⚠️ 向后兼容:也传递旧的索引(指向CDR内部) this.moduleGenerator.setServiceMethodSignatureIndex(this.serviceMethodSignatureIndex); + await this.moduleGenerator.generateAllModules(nestJSModules); this.stats.modulesGenerated = Object.keys(nestJSModules).length;