refactor: 重构转换器为模块化架构
✅ 新增转换器目录结构: converters/ ├── index.js (统一导出) ├── service-method-converter.js (主协调器) ├── syntax/ (语法转换) │ ├── basic-syntax.converter.js │ ├── type.converter.js │ └── exception.converter.js ├── utils/ (工具类转换) │ ├── config.converter.js │ ├── file.converter.js │ ├── string.converter.js │ ├── collection.converter.js │ ├── json.converter.js │ └── object.converter.js ├── mybatis/ (MyBatis转换) │ ├── query-wrapper.converter.js │ ├── mapper.converter.js │ └── pagination.converter.js ├── method/ (方法调用转换) │ ├── getter-setter.converter.js │ ├── method-call.converter.js │ └── stream-api.converter.js └── post-processor.js (后处理) ✅ 优势: - 单一职责:每个转换器只负责一种转换 - 易于维护:清晰的模块化结构 - 易于扩展:新增转换器只需添加新文件 - 易于测试:每个转换器可独立测试 📋 下一步: service-generator.js已自动兼容(不需要修改)
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 转换器模块导出
|
||||
*
|
||||
* 提供统一的转换器导出接口
|
||||
*/
|
||||
|
||||
// 主转换器
|
||||
const ServiceMethodConverter = require('./service-method-converter');
|
||||
|
||||
// 语法转换器
|
||||
const BasicSyntaxConverter = require('./syntax/basic-syntax.converter');
|
||||
const TypeConverter = require('./syntax/type.converter');
|
||||
const ExceptionConverter = require('./syntax/exception.converter');
|
||||
|
||||
// 工具类转换器
|
||||
const ConfigConverter = require('./utils/config.converter');
|
||||
const FileConverter = require('./utils/file.converter');
|
||||
const StringConverter = require('./utils/string.converter');
|
||||
const CollectionConverter = require('./utils/collection.converter');
|
||||
const JsonConverter = require('./utils/json.converter');
|
||||
const ObjectConverter = require('./utils/object.converter');
|
||||
|
||||
// MyBatis转换器
|
||||
const QueryWrapperConverter = require('./mybatis/query-wrapper.converter');
|
||||
const MapperConverter = require('./mybatis/mapper.converter');
|
||||
const PaginationConverter = require('./mybatis/pagination.converter');
|
||||
|
||||
// 方法调用转换器
|
||||
const GetterSetterConverter = require('./method/getter-setter.converter');
|
||||
const MethodCallConverter = require('./method/method-call.converter');
|
||||
const StreamApiConverter = require('./method/stream-api.converter');
|
||||
|
||||
// 后处理器
|
||||
const PostProcessor = require('./post-processor');
|
||||
|
||||
module.exports = {
|
||||
// 主转换器(推荐使用)
|
||||
ServiceMethodConverter,
|
||||
|
||||
// 子转换器(按需使用)
|
||||
BasicSyntaxConverter,
|
||||
TypeConverter,
|
||||
ExceptionConverter,
|
||||
|
||||
ConfigConverter,
|
||||
FileConverter,
|
||||
StringConverter,
|
||||
CollectionConverter,
|
||||
JsonConverter,
|
||||
ObjectConverter,
|
||||
|
||||
QueryWrapperConverter,
|
||||
MapperConverter,
|
||||
PaginationConverter,
|
||||
|
||||
GetterSetterConverter,
|
||||
MethodCallConverter,
|
||||
StreamApiConverter,
|
||||
|
||||
PostProcessor
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Getter/Setter转换器
|
||||
*
|
||||
* Java getter/setter → TypeScript属性访问
|
||||
*/
|
||||
class GetterSetterConverter {
|
||||
/**
|
||||
* 转换getter/setter
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. obj.getXxx() → obj.xxx (只转换简单的getter)
|
||||
// 但保留常见的业务方法如 getList(), getConfig()等
|
||||
const commonMethods = ['getList', 'getConfig', 'getPage', 'getTotal', 'getData', 'getSearch', 'getLimit'];
|
||||
|
||||
// 先处理需要保留的方法(不转换)
|
||||
// 其他的 getXxx() 转换为属性访问
|
||||
tsCode = tsCode.replace(/(\w+)\.get(\w+)\(\)/g, (match, obj, prop) => {
|
||||
const methodName = `get${prop}`;
|
||||
if (commonMethods.includes(methodName)) {
|
||||
return match; // 保留不转换
|
||||
}
|
||||
// 转换为属性访问:obj.xxx
|
||||
return `${obj}.${prop.charAt(0).toLowerCase() + prop.slice(1)}`;
|
||||
});
|
||||
|
||||
// 2. obj.setXxx(value) → obj.xxx = value
|
||||
tsCode = tsCode.replace(/(\w+)\.set(\w+)\(([^)]+)\)/g, (match, obj, prop, value) => {
|
||||
return `${obj}.${prop.charAt(0).toLowerCase() + prop.slice(1)} = ${value}`;
|
||||
});
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GetterSetterConverter;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 方法调用转换器
|
||||
*
|
||||
* Java方法调用 → TypeScript方法调用
|
||||
*/
|
||||
class MethodCallConverter {
|
||||
/**
|
||||
* 转换方法调用
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. list.add(item) → list.push(item)
|
||||
tsCode = tsCode.replace(/(\w+)\.add\(/g, '$1.push(');
|
||||
|
||||
// 2. list.size() → list.length
|
||||
tsCode = tsCode.replace(/(\w+)\.size\(\)/g, '$1.length');
|
||||
|
||||
// 3. map.put(key, value) → map[key] = value 或 map.set(key, value)
|
||||
// 这里简化为直接赋值
|
||||
tsCode = tsCode.replace(/(\w+)\.put\(([^,]+),\s*([^)]+)\)/g, '$1[$2] = $3');
|
||||
|
||||
// 4. map.get(key) → map[key] 或 map.get(key)
|
||||
tsCode = tsCode.replace(/(\w+)\.get\(([^)]+)\)/g, '$1[$2]');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MethodCallConverter;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Stream API转换器
|
||||
*
|
||||
* Java Stream API → TypeScript数组方法
|
||||
*/
|
||||
class StreamApiConverter {
|
||||
/**
|
||||
* 转换Stream API
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. list.stream().filter(x -> x.condition).collect(Collectors.toList())
|
||||
// → list.filter(x => x.condition)
|
||||
tsCode = tsCode.replace(/(\w+)\.stream\(\)\.filter\(([^)]+)\)\.collect\(Collectors\.toList\(\)\)/g, '$1.filter($2)');
|
||||
|
||||
// 2. list.stream().map(x -> x.transform).collect(Collectors.toList())
|
||||
// → list.map(x => x.transform)
|
||||
tsCode = tsCode.replace(/(\w+)\.stream\(\)\.map\(([^)]+)\)\.collect\(Collectors\.toList\(\)\)/g, '$1.map($2)');
|
||||
|
||||
// 3. list.stream() → list (简化)
|
||||
tsCode = tsCode.replace(/(\w+)\.stream\(\)/g, '$1');
|
||||
|
||||
// 4. Collectors.toList() → (移除,因为TypeScript数组方法默认返回数组)
|
||||
tsCode = tsCode.replace(/\.collect\(Collectors\.toList\(\)\)/g, '');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StreamApiConverter;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Mapper转换器
|
||||
*
|
||||
* MyBatis Mapper → TypeORM Repository
|
||||
*/
|
||||
class MapperConverter {
|
||||
/**
|
||||
* 转换Mapper调用
|
||||
*/
|
||||
convert(javaCode, context = {}) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// xxxMapper.selectOne() → this.xxxRepository.findOne()
|
||||
tsCode = tsCode.replace(/(\w+Mapper)\.selectOne\(/g, (match, mapperName) => {
|
||||
const repoName = mapperName.replace('Mapper', 'Repository');
|
||||
const camelRepoName = this.toCamelCase(repoName);
|
||||
return `this.${camelRepoName}.findOne(`;
|
||||
});
|
||||
|
||||
// xxxMapper.selectList() → this.xxxRepository.find()
|
||||
tsCode = tsCode.replace(/(\w+Mapper)\.selectList\(/g, (match, mapperName) => {
|
||||
const repoName = mapperName.replace('Mapper', 'Repository');
|
||||
const camelRepoName = this.toCamelCase(repoName);
|
||||
return `this.${camelRepoName}.find(`;
|
||||
});
|
||||
|
||||
// xxxMapper.insert() → this.xxxRepository.save()
|
||||
tsCode = tsCode.replace(/(\w+Mapper)\.insert\(/g, (match, mapperName) => {
|
||||
const repoName = mapperName.replace('Mapper', 'Repository');
|
||||
const camelRepoName = this.toCamelCase(repoName);
|
||||
return `this.${camelRepoName}.save(`;
|
||||
});
|
||||
|
||||
// xxxMapper.update() → this.xxxRepository.save()
|
||||
tsCode = tsCode.replace(/(\w+Mapper)\.update\(/g, (match, mapperName) => {
|
||||
const repoName = mapperName.replace('Mapper', 'Repository');
|
||||
const camelRepoName = this.toCamelCase(repoName);
|
||||
return `this.${camelRepoName}.save(`;
|
||||
});
|
||||
|
||||
// xxxMapper.delete() → this.xxxRepository.delete()
|
||||
tsCode = tsCode.replace(/(\w+Mapper)\.delete\(/g, (match, mapperName) => {
|
||||
const repoName = mapperName.replace('Mapper', 'Repository');
|
||||
const camelRepoName = this.toCamelCase(repoName);
|
||||
return `this.${camelRepoName}.delete(`;
|
||||
});
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为camelCase
|
||||
*/
|
||||
toCamelCase(str) {
|
||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MapperConverter;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 分页转换器
|
||||
*
|
||||
* MyBatis分页 → TypeORM分页
|
||||
*/
|
||||
class PaginationConverter {
|
||||
/**
|
||||
* 转换分页
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// IPage<Entity> → TODO: 需要手动处理
|
||||
tsCode = tsCode.replace(/IPage<(\w+)>/g, '/* TODO: Paginated<$1> */any');
|
||||
|
||||
// new Page<Entity>(page, limit) → TODO
|
||||
tsCode = tsCode.replace(/new\s+Page<(\w+)>\(([^,]+),\s*([^)]+)\)/g, '/* TODO: { page: $2, limit: $3 } */');
|
||||
|
||||
// selectPage() → find() with pagination
|
||||
tsCode = tsCode.replace(/\.selectPage\(/g, '/* TODO: .find() with take/skip */');
|
||||
|
||||
// PageResult.build() → new PageResult()
|
||||
tsCode = tsCode.replace(/PageResult\.build\(([^,]+),\s*([^,]+),\s*([^)]+)\)/g, 'new PageResult({ currentPage: $1, perPage: $2, total: $3 })');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PaginationConverter;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* QueryWrapper转换器
|
||||
*
|
||||
* MyBatis QueryWrapper → TypeORM QueryBuilder
|
||||
*/
|
||||
class QueryWrapperConverter {
|
||||
/**
|
||||
* 转换QueryWrapper
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. new QueryWrapper<Entity>() → 需要转换为TypeORM查询
|
||||
// 暂时标记为TODO,因为需要更复杂的上下文
|
||||
tsCode = tsCode.replace(/new\s+QueryWrapper<(\w+)>\(\)/g, '/* TODO: TypeORM Query for $1 */{}');
|
||||
|
||||
// 2. queryWrapper.eq("field", value) → TODO
|
||||
tsCode = tsCode.replace(/(\w+)\.eq\(([^,]+),\s*([^)]+)\)/g, '/* TODO: .where("$2 = :value", { value: $3 }) */$1');
|
||||
|
||||
// 3. queryWrapper.like("field", value) → TODO
|
||||
tsCode = tsCode.replace(/(\w+)\.like\(([^,]+),\s*([^)]+)\)/g, '/* TODO: .where("$2 LIKE :value", { value: `%${$3}%` }) */$1');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = QueryWrapperConverter;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 后处理器
|
||||
*
|
||||
* 修复转换后的常见语法错误
|
||||
*/
|
||||
class PostProcessor {
|
||||
/**
|
||||
* 后处理清理
|
||||
*/
|
||||
process(tsCode) {
|
||||
let result = tsCode;
|
||||
|
||||
// 1. 修复 this.fs. 和 this.path. → fs. 和 path.
|
||||
result = result.replace(/this\.fs\./g, 'fs.');
|
||||
result = result.replace(/this\.path\./g, 'path.');
|
||||
|
||||
// 2. 修复逻辑运算符优先级问题
|
||||
// if (!this.config.get('xxx') === "yyy") → if (this.config.get('xxx') !== "yyy")
|
||||
result = result.replace(/if\s*\(\s*!this\.config\.get\([^)]+\)\s*===\s*([^)]+)\)/g, (match) => {
|
||||
const configCall = match.match(/this\.config\.get\([^)]+\)/)[0];
|
||||
const value = match.match(/===\s*([^)]+)\)/)[1].trim();
|
||||
return `if (${configCall} !== ${value})`;
|
||||
});
|
||||
|
||||
// 3. 修复 !xxx === "yyy" → xxx !== "yyy" (通用)
|
||||
result = result.replace(/!\s*([a-zA-Z_$.()[\]]+)\s*===\s*([^;)\n]+)/g, '$1 !== $2');
|
||||
|
||||
// 4. 修复复杂表达式的 .exists()
|
||||
// this.config.get('xxx' + yyy).exists() → fs.existsSync(this.config.get('xxx' + yyy))
|
||||
result = result.replace(/(this\.config\.get\([^)]+\))\.exists\(\)/g, 'fs.existsSync($1)');
|
||||
|
||||
// 5. 修复逗号表达式:if (file, "info.json".exists())
|
||||
result = result.replace(/if\s*\(\s*(\w+),\s*"([^"]+)"\.exists\(\s*\)\s*\)/g, 'if (fs.existsSync(path.join($1, "$2")))');
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PostProcessor;
|
||||
|
||||
@@ -1,18 +1,60 @@
|
||||
const NamingUtils = require('../utils/naming-utils');
|
||||
|
||||
/**
|
||||
* Service方法体转换器
|
||||
* Service方法体转换器(主协调器)
|
||||
*
|
||||
* 职责:将Java Service方法体转换为TypeScript
|
||||
* 职责:协调各个子转换器,将Java Service方法体转换为TypeScript
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 提取Java方法体
|
||||
* 2. Java语法 → TypeScript语法转换
|
||||
* 3. Java工具类 → NestJS Boot层映射
|
||||
* 2. 调用各个专业转换器进行转换
|
||||
* 3. 后处理清理
|
||||
* 4. 分析需要的imports
|
||||
*/
|
||||
|
||||
// 导入各个转换器
|
||||
const BasicSyntaxConverter = require('./syntax/basic-syntax.converter');
|
||||
const TypeConverter = require('./syntax/type.converter');
|
||||
const ExceptionConverter = require('./syntax/exception.converter');
|
||||
|
||||
const ConfigConverter = require('./utils/config.converter');
|
||||
const FileConverter = require('./utils/file.converter');
|
||||
const StringConverter = require('./utils/string.converter');
|
||||
const CollectionConverter = require('./utils/collection.converter');
|
||||
const JsonConverter = require('./utils/json.converter');
|
||||
const ObjectConverter = require('./utils/object.converter');
|
||||
|
||||
const QueryWrapperConverter = require('./mybatis/query-wrapper.converter');
|
||||
const MapperConverter = require('./mybatis/mapper.converter');
|
||||
const PaginationConverter = require('./mybatis/pagination.converter');
|
||||
|
||||
const GetterSetterConverter = require('./method/getter-setter.converter');
|
||||
const MethodCallConverter = require('./method/method-call.converter');
|
||||
const StreamApiConverter = require('./method/stream-api.converter');
|
||||
|
||||
const PostProcessor = require('./post-processor');
|
||||
|
||||
class ServiceMethodConverter {
|
||||
constructor() {
|
||||
this.namingUtils = new NamingUtils();
|
||||
// 初始化所有转换器
|
||||
this.basicSyntax = new BasicSyntaxConverter();
|
||||
this.type = new TypeConverter();
|
||||
this.exception = new ExceptionConverter();
|
||||
|
||||
this.config = new ConfigConverter();
|
||||
this.file = new FileConverter();
|
||||
this.string = new StringConverter();
|
||||
this.collection = new CollectionConverter();
|
||||
this.json = new JsonConverter();
|
||||
this.object = new ObjectConverter();
|
||||
|
||||
this.queryWrapper = new QueryWrapperConverter();
|
||||
this.mapper = new MapperConverter();
|
||||
this.pagination = new PaginationConverter();
|
||||
|
||||
this.getterSetter = new GetterSetterConverter();
|
||||
this.methodCall = new MethodCallConverter();
|
||||
this.streamApi = new StreamApiConverter();
|
||||
|
||||
this.postProcessor = new PostProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,302 +74,55 @@ class ServiceMethodConverter {
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段1】基础语法转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertBasicSyntax(tsBody);
|
||||
tsBody = this.basicSyntax.convert(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段2】类型转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertTypes(tsBody);
|
||||
tsBody = this.type.convert(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段3】工具类映射(Java → NestJS Boot层)
|
||||
// 【阶段3】工具类转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertUtilityClasses(tsBody);
|
||||
tsBody = this.config.convert(tsBody);
|
||||
tsBody = this.file.convert(tsBody);
|
||||
tsBody = this.string.convert(tsBody);
|
||||
tsBody = this.collection.convert(tsBody);
|
||||
tsBody = this.json.convert(tsBody);
|
||||
tsBody = this.object.convert(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段4】Mapper/Service调用转换
|
||||
// 【阶段4】MyBatis → TypeORM转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertDependencyCalls(tsBody, context);
|
||||
tsBody = this.queryWrapper.convert(tsBody);
|
||||
tsBody = this.mapper.convert(tsBody, context);
|
||||
tsBody = this.pagination.convert(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段5】后处理清理
|
||||
// 【阶段5】方法调用转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.postProcessCleanup(tsBody);
|
||||
tsBody = this.streamApi.convert(tsBody);
|
||||
tsBody = this.methodCall.convert(tsBody);
|
||||
tsBody = this.getterSetter.convert(tsBody); // 最后转换getter/setter
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段6】添加缩进
|
||||
// 【阶段6】异常处理转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.exception.convert(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段7】后处理清理
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.postProcessor.process(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段8】添加缩进
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = tsBody.split('\n').map(line => ' ' + line).join('\n');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段5】后处理清理
|
||||
* 修复转换后的常见语法错误
|
||||
*/
|
||||
postProcessCleanup(tsBody) {
|
||||
// 1. 修复 this.fs. 和 this.path. → fs. 和 path.
|
||||
tsBody = tsBody.replace(/this\.fs\./g, 'fs.');
|
||||
tsBody = tsBody.replace(/this\.path\./g, 'path.');
|
||||
|
||||
// 2. 修复逻辑运算符优先级问题
|
||||
// if (!this.config.get('xxx') === "yyy") → if (this.config.get('xxx') !== "yyy")
|
||||
tsBody = tsBody.replace(/if\s*\(\s*!this\.config\.get\([^)]+\)\s*===\s*([^)]+)\)/g, (match) => {
|
||||
// 提取内容
|
||||
const configCall = match.match(/this\.config\.get\([^)]+\)/)[0];
|
||||
const value = match.match(/===\s*([^)]+)\)/)[1].trim();
|
||||
return `if (${configCall} !== ${value})`;
|
||||
});
|
||||
|
||||
// 3. 修复 !xxx === "yyy" → xxx !== "yyy" (通用)
|
||||
tsBody = tsBody.replace(/!\s*([a-zA-Z_$.()[\]]+)\s*===\s*([^;)\n]+)/g, '$1 !== $2');
|
||||
|
||||
// 4. 修复复杂表达式的 .exists()
|
||||
// this.config.get('xxx' + yyy).exists() → fs.existsSync(this.config.get('xxx' + yyy))
|
||||
tsBody = tsBody.replace(/(this\.config\.get\([^)]+\))\.exists\(\)/g, 'fs.existsSync($1)');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段1】基础语法转换
|
||||
*/
|
||||
convertBasicSyntax(javaBody) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// 1. 变量声明:Type varName = value; → const varName: Type = value;
|
||||
tsBody = tsBody.replace(
|
||||
/(\b(?:Integer|Long|String|Boolean|Double|Float|BigDecimal|Object|List<[^>]+>|Map<[^>]+>|[\w<>,\s]+))\s+(\w+)\s*=\s*([^;]+);/g,
|
||||
(match, type, varName, value) => {
|
||||
const tsType = this.mapJavaTypeToTypeScript(type);
|
||||
return `const ${varName}: ${tsType} = ${value};`;
|
||||
}
|
||||
);
|
||||
|
||||
// 2. for-each循环:for (Type item : collection) → for (const item of collection)
|
||||
tsBody = tsBody.replace(/for\s*\(\s*[\w<>,\s]+\s+(\w+)\s*:\s*([^)]+)\)/g, 'for (const $1 of $2)');
|
||||
|
||||
// 3. Java实例化:new ArrayList<>() → []
|
||||
tsBody = tsBody.replace(/new\s+ArrayList<[^>]*>\(\)/g, '[]');
|
||||
tsBody = tsBody.replace(/new\s+LinkedList<[^>]*>\(\)/g, '[]');
|
||||
tsBody = tsBody.replace(/new\s+HashMap<[^>]*>\(\)/g, '{}');
|
||||
tsBody = tsBody.replace(/new\s+HashSet<[^>]*>\(\)/g, 'new Set()');
|
||||
|
||||
// 4. Lambda表达式:item -> expression → item => expression
|
||||
tsBody = tsBody.replace(/(\w+)\s*->\s*/g, '$1 => ');
|
||||
tsBody = tsBody.replace(/\(([^)]+)\)\s*->\s*/g, '($1) => ');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段2】类型转换
|
||||
*/
|
||||
convertTypes(javaBody) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// 1. 基础类型
|
||||
tsBody = tsBody.replace(/\bInteger\b/g, 'number');
|
||||
tsBody = tsBody.replace(/\bLong\b/g, 'number');
|
||||
tsBody = tsBody.replace(/\bDouble\b/g, 'number');
|
||||
tsBody = tsBody.replace(/\bFloat\b/g, 'number');
|
||||
|
||||
// 2. Java对象类型
|
||||
tsBody = tsBody.replace(/\bJSONObject\b/g, 'Record<string, any>');
|
||||
tsBody = tsBody.replace(/\bId\b/g, 'number');
|
||||
|
||||
// 3. Java类型转换语法:(Type) value → value
|
||||
tsBody = tsBody.replace(/\(\s*(?:int|long|double|float|String|Integer|Long|Double|Float|number)\s*\)\s*/g, '');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段3】工具类映射
|
||||
*/
|
||||
convertUtilityClasses(javaBody) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【配置访问】WebAppEnvs / GlobalConfig → ConfigService
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// WebAppEnvs.get().property.exists() → fs.existsSync(this.config.get('property'))
|
||||
// 必须在WebAppEnvs.get().property之前处理
|
||||
tsBody = tsBody.replace(/WebAppEnvs\.get\(\)\.(\w+)\.exists\(\)/g, (match, prop) => {
|
||||
return `fs.existsSync(this.config.get('${prop}'))`;
|
||||
});
|
||||
|
||||
// WebAppEnvs.get().property → this.config.get('property')
|
||||
tsBody = tsBody.replace(/WebAppEnvs\.get\(\)\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
tsBody = tsBody.replace(/WebAppEnvs\.get\(\)/g, 'this.config');
|
||||
|
||||
// GlobalConfig.property.exists() → fs.existsSync(this.config.get('property'))
|
||||
// 必须在GlobalConfig.property之前处理
|
||||
tsBody = tsBody.replace(/GlobalConfig\.(\w+)\.exists\(\)/g, (match, prop) => {
|
||||
return `fs.existsSync(this.config.get('${prop}'))`;
|
||||
});
|
||||
|
||||
// GlobalConfig.property → this.config.get('property')
|
||||
tsBody = tsBody.replace(/GlobalConfig\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【文件操作】Java File API → Node.js fs/path
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// FileUtils工具类
|
||||
tsBody = tsBody.replace(/FileUtils\.cleanDirectory\(([^)]+)\)/g, 'fs.rmSync($1, { recursive: true, force: true })');
|
||||
tsBody = tsBody.replace(/FileUtils\.copyFile\(([^,]+),\s*([^)]+)\)/g, 'fs.copyFileSync($1, $2)');
|
||||
tsBody = tsBody.replace(/FileUtils\.deleteDirectory\(([^)]+)\)/g, 'fs.rmSync($1, { recursive: true, force: true })');
|
||||
tsBody = tsBody.replace(/FileUtils\.readFileToString\(([^)]+)\)/g, 'fs.readFileSync($1, \'utf-8\')');
|
||||
tsBody = tsBody.replace(/FileUtils\.writeStringToFile\(([^,]+),\s*([^)]+)\)/g, 'fs.writeFileSync($1, $2, \'utf-8\')');
|
||||
|
||||
// File API
|
||||
tsBody = tsBody.replace(/new\s+File\(([^)]+)\)/g, '$1');
|
||||
tsBody = tsBody.replace(/(\w+)\.exists\(\)/g, 'fs.existsSync($1)');
|
||||
tsBody = tsBody.replace(/(\w+)\.isDirectory\(\)/g, 'fs.lstatSync($1).isDirectory()');
|
||||
tsBody = tsBody.replace(/(\w+)\.getName\(\)/g, 'path.basename($1)');
|
||||
tsBody = tsBody.replace(/(\w+)\.listFiles\(\)/g, 'fs.readdirSync($1)');
|
||||
tsBody = tsBody.replace(/(\w+)\.getPath\(\)/g, '$1');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【字符串方法】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// .equals() → ===
|
||||
tsBody = tsBody.replace(/([^!;,\s]+)\.equals\(([^)]+)\)/g, '$1 === $2');
|
||||
|
||||
// .equalsIgnoreCase() → .toLowerCase() === xxx.toLowerCase()
|
||||
tsBody = tsBody.replace(/([a-zA-Z_$][\w$.()]+)\.equalsIgnoreCase\(([^)]+)\)/g, '$1.toLowerCase() === $2.toLowerCase()');
|
||||
|
||||
// .contains() → .includes()
|
||||
tsBody = tsBody.replace(/\.contains\(/g, '.includes(');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【集合判空】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// CollectionUtil.isEmpty(list) → !list || list.length === 0
|
||||
tsBody = tsBody.replace(/(?:CollectionUtil|CollUtil)\.isEmpty\(([^)]+)\)/g, '(!$1 || $1.length === 0)');
|
||||
tsBody = tsBody.replace(/(?:CollectionUtil|CollUtil)\.isNotEmpty\(([^)]+)\)/g, '($1 && $1.length > 0)');
|
||||
|
||||
// list.isEmpty() → list.length === 0
|
||||
tsBody = tsBody.replace(/([a-zA-Z_$][\w$]*)\.isEmpty\(\)/g, '$1.length === 0');
|
||||
tsBody = tsBody.replace(/!([a-zA-Z_$][\w$]*)\.isEmpty\(\)/g, '$1.length > 0');
|
||||
|
||||
// StringUtils.isEmpty(str) → !str || str.trim() === ''
|
||||
tsBody = tsBody.replace(/StringUtils\.isEmpty\(([^)]+)\)/g, '(!$1 || $1.trim() === \'\')');
|
||||
tsBody = tsBody.replace(/StringUtils\.isNotEmpty\(([^)]+)\)/g, '($1 && $1.trim() !== \'\')');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【异常处理】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// try-catch语句:catch (Exception e) → catch (error)
|
||||
tsBody = tsBody.replace(/catch\s*\(\s*(?:Exception|RuntimeException|Throwable)\s+(\w+)\s*\)/g, 'catch ($1)');
|
||||
|
||||
// 异常方法调用:e.getMessage() → e.message
|
||||
tsBody = tsBody.replace(/(\w+)\.getMessage\(\)/g, '$1.message');
|
||||
|
||||
// 异常抛出
|
||||
tsBody = tsBody.replace(/throw\s+new\s+CommonException\(/g, 'throw new BadRequestException(');
|
||||
tsBody = tsBody.replace(/throw\s+new\s+AuthException\(/g, 'throw new UnauthorizedException(');
|
||||
tsBody = tsBody.replace(/throw\s+new\s+RuntimeException\(/g, 'throw new Error(');
|
||||
tsBody = tsBody.replace(/throw\s+new\s+Exception\(/g, 'throw new Error(');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【其他】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// System.out.println → console.log
|
||||
tsBody = tsBody.replace(/System\.out\.println\(/g, 'console.log(');
|
||||
tsBody = tsBody.replace(/System\.err\.println\(/g, 'console.error(');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段4】Mapper/Service调用转换
|
||||
*/
|
||||
convertDependencyCalls(javaBody, context) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// Mapper调用:xxxMapper.method() → this.xxxRepository.method()
|
||||
if (context.dependencies) {
|
||||
context.dependencies.forEach(dep => {
|
||||
if (dep.includes('Mapper')) {
|
||||
const mapperName = dep;
|
||||
const repoName = this.namingUtils.toCamelCase(dep.replace('Mapper', 'Repository'));
|
||||
tsBody = tsBody.replace(new RegExp(`${mapperName}\\.`, 'g'), `this.${repoName}.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Service调用:xxxService.method() → this.xxxService.method()
|
||||
if (context.dependencies) {
|
||||
context.dependencies.forEach(dep => {
|
||||
if (dep.includes('Service') && !dep.includes('ServiceImpl')) {
|
||||
const serviceName = this.namingUtils.toCamelCase(dep);
|
||||
tsBody = tsBody.replace(new RegExp(`${dep}\\.`, 'g'), `this.${serviceName}.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射Java类型到TypeScript
|
||||
*/
|
||||
mapJavaTypeToTypeScript(javaType) {
|
||||
// 处理泛型
|
||||
if (javaType.includes('<')) {
|
||||
const genericMatch = javaType.match(/^(\w+)<(.+)>$/);
|
||||
if (genericMatch) {
|
||||
const baseType = genericMatch[1];
|
||||
const genericType = genericMatch[2];
|
||||
|
||||
if (baseType === 'List' || baseType === 'ArrayList' || baseType === 'LinkedList') {
|
||||
return `${this.mapJavaTypeToTypeScript(genericType)}[]`;
|
||||
} else if (baseType === 'Map' || baseType === 'HashMap') {
|
||||
return 'Record<string, any>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typeMap = {
|
||||
'Integer': 'number',
|
||||
'int': 'number',
|
||||
'Long': 'number',
|
||||
'long': 'number',
|
||||
'Double': 'number',
|
||||
'double': 'number',
|
||||
'Float': 'number',
|
||||
'float': 'number',
|
||||
'BigDecimal': 'number',
|
||||
'String': 'string',
|
||||
'Boolean': 'boolean',
|
||||
'boolean': 'boolean',
|
||||
'Object': 'any',
|
||||
'void': 'void',
|
||||
'File': 'string',
|
||||
'JSONObject': 'Record<string, any>',
|
||||
'Id': 'number',
|
||||
'Date': 'Date',
|
||||
'LocalDateTime': 'Date',
|
||||
'LocalDate': 'Date',
|
||||
'List': 'any[]',
|
||||
'Map': 'Record<string, any>'
|
||||
};
|
||||
|
||||
return typeMap[javaType] || javaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析需要的imports
|
||||
*
|
||||
@@ -342,23 +137,15 @@ class ServiceMethodConverter {
|
||||
};
|
||||
|
||||
// NestJS异常
|
||||
if (convertedBody.includes('BadRequestException')) {
|
||||
imports.nestjs.add('BadRequestException');
|
||||
}
|
||||
if (convertedBody.includes('UnauthorizedException')) {
|
||||
imports.nestjs.add('UnauthorizedException');
|
||||
}
|
||||
const nestjsImports = this.exception.analyzeImports(convertedBody);
|
||||
nestjsImports.forEach(imp => imports.nestjs.add(imp));
|
||||
|
||||
// Node.js模块
|
||||
if (convertedBody.includes('fs.')) {
|
||||
imports.nodeModules.add('fs');
|
||||
}
|
||||
if (convertedBody.includes('path.')) {
|
||||
imports.nodeModules.add('path');
|
||||
}
|
||||
const nodeModules = this.file.analyzeNodeModules(convertedBody);
|
||||
nodeModules.forEach(mod => imports.nodeModules.add(mod));
|
||||
|
||||
// ConfigService
|
||||
if (convertedBody.includes('this.config.')) {
|
||||
if (this.config.needsConfigService(convertedBody)) {
|
||||
imports.boot.add('ConfigService');
|
||||
}
|
||||
|
||||
@@ -371,4 +158,3 @@ class ServiceMethodConverter {
|
||||
}
|
||||
|
||||
module.exports = ServiceMethodConverter;
|
||||
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
const NamingUtils = require('../utils/naming-utils');
|
||||
|
||||
/**
|
||||
* Service方法体转换器
|
||||
*
|
||||
* 职责:将Java Service方法体转换为TypeScript
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 提取Java方法体
|
||||
* 2. Java语法 → TypeScript语法转换
|
||||
* 3. Java工具类 → NestJS Boot层映射
|
||||
*/
|
||||
class ServiceMethodConverter {
|
||||
constructor() {
|
||||
this.namingUtils = new NamingUtils();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换Java方法体为TypeScript
|
||||
*
|
||||
* @param {string} javaMethodBody - Java方法体代码
|
||||
* @param {object} context - 上下文信息(Service类信息、依赖等)
|
||||
* @returns {string} 转换后的TypeScript方法体
|
||||
*/
|
||||
convertMethodBody(javaMethodBody, context = {}) {
|
||||
if (!javaMethodBody || javaMethodBody.trim() === '') {
|
||||
return ' // TODO: 实现业务逻辑\n return null;';
|
||||
}
|
||||
|
||||
let tsBody = javaMethodBody;
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段1】基础语法转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertBasicSyntax(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段2】类型转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertTypes(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段3】工具类映射(Java → NestJS Boot层)
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertUtilityClasses(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段4】Mapper/Service调用转换
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.convertDependencyCalls(tsBody, context);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段5】后处理清理
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = this.postProcessCleanup(tsBody);
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【阶段6】添加缩进
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsBody = tsBody.split('\n').map(line => ' ' + line).join('\n');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段5】后处理清理
|
||||
* 修复转换后的常见语法错误
|
||||
*/
|
||||
postProcessCleanup(tsBody) {
|
||||
// 1. 修复 this.fs. 和 this.path. → fs. 和 path.
|
||||
tsBody = tsBody.replace(/this\.fs\./g, 'fs.');
|
||||
tsBody = tsBody.replace(/this\.path\./g, 'path.');
|
||||
|
||||
// 2. 修复逻辑运算符优先级问题
|
||||
// if (!this.config.get('xxx') === "yyy") → if (this.config.get('xxx') !== "yyy")
|
||||
tsBody = tsBody.replace(/if\s*\(\s*!this\.config\.get\([^)]+\)\s*===\s*([^)]+)\)/g, (match) => {
|
||||
// 提取内容
|
||||
const configCall = match.match(/this\.config\.get\([^)]+\)/)[0];
|
||||
const value = match.match(/===\s*([^)]+)\)/)[1].trim();
|
||||
return `if (${configCall} !== ${value})`;
|
||||
});
|
||||
|
||||
// 3. 修复 !xxx === "yyy" → xxx !== "yyy" (通用)
|
||||
tsBody = tsBody.replace(/!\s*([a-zA-Z_$.()[\]]+)\s*===\s*([^;)\n]+)/g, '$1 !== $2');
|
||||
|
||||
// 4. 修复复杂表达式的 .exists()
|
||||
// this.config.get('xxx' + yyy).exists() → fs.existsSync(this.config.get('xxx' + yyy))
|
||||
tsBody = tsBody.replace(/(this\.config\.get\([^)]+\))\.exists\(\)/g, 'fs.existsSync($1)');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段1】基础语法转换
|
||||
*/
|
||||
convertBasicSyntax(javaBody) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// 1. 变量声明:Type varName = value; → const varName: Type = value;
|
||||
tsBody = tsBody.replace(
|
||||
/(\b(?:Integer|Long|String|Boolean|Double|Float|BigDecimal|Object|List<[^>]+>|Map<[^>]+>|[\w<>,\s]+))\s+(\w+)\s*=\s*([^;]+);/g,
|
||||
(match, type, varName, value) => {
|
||||
const tsType = this.mapJavaTypeToTypeScript(type);
|
||||
return `const ${varName}: ${tsType} = ${value};`;
|
||||
}
|
||||
);
|
||||
|
||||
// 2. for-each循环:for (Type item : collection) → for (const item of collection)
|
||||
tsBody = tsBody.replace(/for\s*\(\s*[\w<>,\s]+\s+(\w+)\s*:\s*([^)]+)\)/g, 'for (const $1 of $2)');
|
||||
|
||||
// 3. Java实例化:new ArrayList<>() → []
|
||||
tsBody = tsBody.replace(/new\s+ArrayList<[^>]*>\(\)/g, '[]');
|
||||
tsBody = tsBody.replace(/new\s+LinkedList<[^>]*>\(\)/g, '[]');
|
||||
tsBody = tsBody.replace(/new\s+HashMap<[^>]*>\(\)/g, '{}');
|
||||
tsBody = tsBody.replace(/new\s+HashSet<[^>]*>\(\)/g, 'new Set()');
|
||||
|
||||
// 4. Lambda表达式:item -> expression → item => expression
|
||||
tsBody = tsBody.replace(/(\w+)\s*->\s*/g, '$1 => ');
|
||||
tsBody = tsBody.replace(/\(([^)]+)\)\s*->\s*/g, '($1) => ');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段2】类型转换
|
||||
*/
|
||||
convertTypes(javaBody) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// 1. 基础类型
|
||||
tsBody = tsBody.replace(/\bInteger\b/g, 'number');
|
||||
tsBody = tsBody.replace(/\bLong\b/g, 'number');
|
||||
tsBody = tsBody.replace(/\bDouble\b/g, 'number');
|
||||
tsBody = tsBody.replace(/\bFloat\b/g, 'number');
|
||||
|
||||
// 2. Java对象类型
|
||||
tsBody = tsBody.replace(/\bJSONObject\b/g, 'Record<string, any>');
|
||||
tsBody = tsBody.replace(/\bId\b/g, 'number');
|
||||
|
||||
// 3. Java类型转换语法:(Type) value → value
|
||||
tsBody = tsBody.replace(/\(\s*(?:int|long|double|float|String|Integer|Long|Double|Float|number)\s*\)\s*/g, '');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段3】工具类映射
|
||||
*/
|
||||
convertUtilityClasses(javaBody) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【配置访问】WebAppEnvs / GlobalConfig → ConfigService
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// WebAppEnvs.get().property.exists() → fs.existsSync(this.config.get('property'))
|
||||
// 必须在WebAppEnvs.get().property之前处理
|
||||
tsBody = tsBody.replace(/WebAppEnvs\.get\(\)\.(\w+)\.exists\(\)/g, (match, prop) => {
|
||||
return `fs.existsSync(this.config.get('${prop}'))`;
|
||||
});
|
||||
|
||||
// WebAppEnvs.get().property → this.config.get('property')
|
||||
tsBody = tsBody.replace(/WebAppEnvs\.get\(\)\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
tsBody = tsBody.replace(/WebAppEnvs\.get\(\)/g, 'this.config');
|
||||
|
||||
// GlobalConfig.property.exists() → fs.existsSync(this.config.get('property'))
|
||||
// 必须在GlobalConfig.property之前处理
|
||||
tsBody = tsBody.replace(/GlobalConfig\.(\w+)\.exists\(\)/g, (match, prop) => {
|
||||
return `fs.existsSync(this.config.get('${prop}'))`;
|
||||
});
|
||||
|
||||
// GlobalConfig.property → this.config.get('property')
|
||||
tsBody = tsBody.replace(/GlobalConfig\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【文件操作】Java File API → Node.js fs/path
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// FileUtils工具类
|
||||
tsBody = tsBody.replace(/FileUtils\.cleanDirectory\(([^)]+)\)/g, 'fs.rmSync($1, { recursive: true, force: true })');
|
||||
tsBody = tsBody.replace(/FileUtils\.copyFile\(([^,]+),\s*([^)]+)\)/g, 'fs.copyFileSync($1, $2)');
|
||||
tsBody = tsBody.replace(/FileUtils\.deleteDirectory\(([^)]+)\)/g, 'fs.rmSync($1, { recursive: true, force: true })');
|
||||
tsBody = tsBody.replace(/FileUtils\.readFileToString\(([^)]+)\)/g, 'fs.readFileSync($1, \'utf-8\')');
|
||||
tsBody = tsBody.replace(/FileUtils\.writeStringToFile\(([^,]+),\s*([^)]+)\)/g, 'fs.writeFileSync($1, $2, \'utf-8\')');
|
||||
|
||||
// File API
|
||||
tsBody = tsBody.replace(/new\s+File\(([^)]+)\)/g, '$1');
|
||||
tsBody = tsBody.replace(/(\w+)\.exists\(\)/g, 'fs.existsSync($1)');
|
||||
tsBody = tsBody.replace(/(\w+)\.isDirectory\(\)/g, 'fs.lstatSync($1).isDirectory()');
|
||||
tsBody = tsBody.replace(/(\w+)\.getName\(\)/g, 'path.basename($1)');
|
||||
tsBody = tsBody.replace(/(\w+)\.listFiles\(\)/g, 'fs.readdirSync($1)');
|
||||
tsBody = tsBody.replace(/(\w+)\.getPath\(\)/g, '$1');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【字符串方法】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// .equals() → ===
|
||||
tsBody = tsBody.replace(/([^!;,\s]+)\.equals\(([^)]+)\)/g, '$1 === $2');
|
||||
|
||||
// .equalsIgnoreCase() → .toLowerCase() === xxx.toLowerCase()
|
||||
tsBody = tsBody.replace(/([a-zA-Z_$][\w$.()]+)\.equalsIgnoreCase\(([^)]+)\)/g, '$1.toLowerCase() === $2.toLowerCase()');
|
||||
|
||||
// .contains() → .includes()
|
||||
tsBody = tsBody.replace(/\.contains\(/g, '.includes(');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【集合判空】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// CollectionUtil.isEmpty(list) → !list || list.length === 0
|
||||
tsBody = tsBody.replace(/(?:CollectionUtil|CollUtil)\.isEmpty\(([^)]+)\)/g, '(!$1 || $1.length === 0)');
|
||||
tsBody = tsBody.replace(/(?:CollectionUtil|CollUtil)\.isNotEmpty\(([^)]+)\)/g, '($1 && $1.length > 0)');
|
||||
|
||||
// list.isEmpty() → list.length === 0
|
||||
tsBody = tsBody.replace(/([a-zA-Z_$][\w$]*)\.isEmpty\(\)/g, '$1.length === 0');
|
||||
tsBody = tsBody.replace(/!([a-zA-Z_$][\w$]*)\.isEmpty\(\)/g, '$1.length > 0');
|
||||
|
||||
// StringUtils.isEmpty(str) → !str || str.trim() === ''
|
||||
tsBody = tsBody.replace(/StringUtils\.isEmpty\(([^)]+)\)/g, '(!$1 || $1.trim() === \'\')');
|
||||
tsBody = tsBody.replace(/StringUtils\.isNotEmpty\(([^)]+)\)/g, '($1 && $1.trim() !== \'\')');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【异常处理】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// try-catch语句:catch (Exception e) → catch (error)
|
||||
tsBody = tsBody.replace(/catch\s*\(\s*(?:Exception|RuntimeException|Throwable)\s+(\w+)\s*\)/g, 'catch ($1)');
|
||||
|
||||
// 异常方法调用:e.getMessage() → e.message
|
||||
tsBody = tsBody.replace(/(\w+)\.getMessage\(\)/g, '$1.message');
|
||||
|
||||
// 异常抛出
|
||||
tsBody = tsBody.replace(/throw\s+new\s+CommonException\(/g, 'throw new BadRequestException(');
|
||||
tsBody = tsBody.replace(/throw\s+new\s+AuthException\(/g, 'throw new UnauthorizedException(');
|
||||
tsBody = tsBody.replace(/throw\s+new\s+RuntimeException\(/g, 'throw new Error(');
|
||||
tsBody = tsBody.replace(/throw\s+new\s+Exception\(/g, 'throw new Error(');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【其他】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// System.out.println → console.log
|
||||
tsBody = tsBody.replace(/System\.out\.println\(/g, 'console.log(');
|
||||
tsBody = tsBody.replace(/System\.err\.println\(/g, 'console.error(');
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【阶段4】Mapper/Service调用转换
|
||||
*/
|
||||
convertDependencyCalls(javaBody, context) {
|
||||
let tsBody = javaBody;
|
||||
|
||||
// Mapper调用:xxxMapper.method() → this.xxxRepository.method()
|
||||
if (context.dependencies) {
|
||||
context.dependencies.forEach(dep => {
|
||||
if (dep.includes('Mapper')) {
|
||||
const mapperName = dep;
|
||||
const repoName = this.namingUtils.toCamelCase(dep.replace('Mapper', 'Repository'));
|
||||
tsBody = tsBody.replace(new RegExp(`${mapperName}\\.`, 'g'), `this.${repoName}.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Service调用:xxxService.method() → this.xxxService.method()
|
||||
if (context.dependencies) {
|
||||
context.dependencies.forEach(dep => {
|
||||
if (dep.includes('Service') && !dep.includes('ServiceImpl')) {
|
||||
const serviceName = this.namingUtils.toCamelCase(dep);
|
||||
tsBody = tsBody.replace(new RegExp(`${dep}\\.`, 'g'), `this.${serviceName}.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return tsBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射Java类型到TypeScript
|
||||
*/
|
||||
mapJavaTypeToTypeScript(javaType) {
|
||||
// 处理泛型
|
||||
if (javaType.includes('<')) {
|
||||
const genericMatch = javaType.match(/^(\w+)<(.+)>$/);
|
||||
if (genericMatch) {
|
||||
const baseType = genericMatch[1];
|
||||
const genericType = genericMatch[2];
|
||||
|
||||
if (baseType === 'List' || baseType === 'ArrayList' || baseType === 'LinkedList') {
|
||||
return `${this.mapJavaTypeToTypeScript(genericType)}[]`;
|
||||
} else if (baseType === 'Map' || baseType === 'HashMap') {
|
||||
return 'Record<string, any>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typeMap = {
|
||||
'Integer': 'number',
|
||||
'int': 'number',
|
||||
'Long': 'number',
|
||||
'long': 'number',
|
||||
'Double': 'number',
|
||||
'double': 'number',
|
||||
'Float': 'number',
|
||||
'float': 'number',
|
||||
'BigDecimal': 'number',
|
||||
'String': 'string',
|
||||
'Boolean': 'boolean',
|
||||
'boolean': 'boolean',
|
||||
'Object': 'any',
|
||||
'void': 'void',
|
||||
'File': 'string',
|
||||
'JSONObject': 'Record<string, any>',
|
||||
'Id': 'number',
|
||||
'Date': 'Date',
|
||||
'LocalDateTime': 'Date',
|
||||
'LocalDate': 'Date',
|
||||
'List': 'any[]',
|
||||
'Map': 'Record<string, any>'
|
||||
};
|
||||
|
||||
return typeMap[javaType] || javaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析需要的imports
|
||||
*
|
||||
* @param {string} convertedBody - 转换后的TypeScript代码
|
||||
* @returns {object} 需要导入的模块
|
||||
*/
|
||||
analyzeImports(convertedBody) {
|
||||
const imports = {
|
||||
nestjs: new Set(),
|
||||
boot: new Set(),
|
||||
nodeModules: new Set()
|
||||
};
|
||||
|
||||
// NestJS异常
|
||||
if (convertedBody.includes('BadRequestException')) {
|
||||
imports.nestjs.add('BadRequestException');
|
||||
}
|
||||
if (convertedBody.includes('UnauthorizedException')) {
|
||||
imports.nestjs.add('UnauthorizedException');
|
||||
}
|
||||
|
||||
// Node.js模块
|
||||
if (convertedBody.includes('fs.')) {
|
||||
imports.nodeModules.add('fs');
|
||||
}
|
||||
if (convertedBody.includes('path.')) {
|
||||
imports.nodeModules.add('path');
|
||||
}
|
||||
|
||||
// ConfigService
|
||||
if (convertedBody.includes('this.config.')) {
|
||||
imports.boot.add('ConfigService');
|
||||
}
|
||||
|
||||
return {
|
||||
nestjs: Array.from(imports.nestjs),
|
||||
boot: Array.from(imports.boot),
|
||||
nodeModules: Array.from(imports.nodeModules)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ServiceMethodConverter;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 基础语法转换器
|
||||
*
|
||||
* 负责Java基础语法到TypeScript的转换:
|
||||
* - 变量声明
|
||||
* - 循环语句
|
||||
* - Lambda表达式
|
||||
* - 集合实例化
|
||||
*/
|
||||
class BasicSyntaxConverter {
|
||||
/**
|
||||
* 转换基础语法
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. 变量声明:Type varName = value; → const varName: Type = value;
|
||||
tsCode = tsCode.replace(
|
||||
/(\b(?:Integer|Long|String|Boolean|Double|Float|BigDecimal|Object|List<[^>]+>|Map<[^>]+>|[\w<>,\s]+))\s+(\w+)\s*=\s*([^;]+);/g,
|
||||
(match, type, varName, value) => {
|
||||
const tsType = this.mapJavaTypeToTypeScript(type);
|
||||
return `const ${varName}: ${tsType} = ${value};`;
|
||||
}
|
||||
);
|
||||
|
||||
// 2. for-each循环:for (Type item : collection) → for (const item of collection)
|
||||
tsCode = tsCode.replace(/for\s*\(\s*[\w<>,\s]+\s+(\w+)\s*:\s*([^)]+)\)/g, 'for (const $1 of $2)');
|
||||
|
||||
// 3. Lambda表达式:item -> expression → item => expression
|
||||
tsCode = tsCode.replace(/(\w+)\s*->\s*/g, '$1 => ');
|
||||
tsCode = tsCode.replace(/\(([^)]+)\)\s*->\s*/g, '($1) => ');
|
||||
|
||||
// 4. Java实例化:new ArrayList<>() → []
|
||||
tsCode = tsCode.replace(/new\s+ArrayList<[^>]*>\(\)/g, '[]');
|
||||
tsCode = tsCode.replace(/new\s+LinkedList<[^>]*>\(\)/g, '[]');
|
||||
tsCode = tsCode.replace(/new\s+HashMap<[^>]*>\(\)/g, '{}');
|
||||
tsCode = tsCode.replace(/new\s+HashSet<[^>]*>\(\)/g, 'new Set()');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射Java类型到TypeScript
|
||||
*/
|
||||
mapJavaTypeToTypeScript(javaType) {
|
||||
// 处理泛型
|
||||
if (javaType.includes('<')) {
|
||||
const genericMatch = javaType.match(/^(\w+)<(.+)>$/);
|
||||
if (genericMatch) {
|
||||
const baseType = genericMatch[1];
|
||||
const genericType = genericMatch[2];
|
||||
|
||||
if (baseType === 'List' || baseType === 'ArrayList' || baseType === 'LinkedList') {
|
||||
return `${this.mapJavaTypeToTypeScript(genericType)}[]`;
|
||||
} else if (baseType === 'Map' || baseType === 'HashMap') {
|
||||
return 'Record<string, any>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typeMap = {
|
||||
'Integer': 'number',
|
||||
'int': 'number',
|
||||
'Long': 'number',
|
||||
'long': 'number',
|
||||
'Double': 'number',
|
||||
'double': 'number',
|
||||
'Float': 'number',
|
||||
'float': 'number',
|
||||
'BigDecimal': 'number',
|
||||
'String': 'string',
|
||||
'Boolean': 'boolean',
|
||||
'boolean': 'boolean',
|
||||
'Object': 'any',
|
||||
'void': 'void',
|
||||
'File': 'string',
|
||||
'JSONObject': 'Record<string, any>',
|
||||
'Id': 'number',
|
||||
'Date': 'Date',
|
||||
'LocalDateTime': 'Date',
|
||||
'LocalDate': 'Date',
|
||||
'List': 'any[]',
|
||||
'Map': 'Record<string, any>'
|
||||
};
|
||||
|
||||
return typeMap[javaType] || javaType;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BasicSyntaxConverter;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 异常处理转换器
|
||||
*
|
||||
* 负责Java异常到TypeScript/NestJS异常的转换
|
||||
*/
|
||||
class ExceptionConverter {
|
||||
/**
|
||||
* 转换异常处理
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. try-catch语句:catch (Exception e) → catch (e)
|
||||
tsCode = tsCode.replace(/catch\s*\(\s*(?:Exception|RuntimeException|Throwable|IOException|SQLException)\s+(\w+)\s*\)/g, 'catch ($1)');
|
||||
|
||||
// 2. 异常方法调用:e.getMessage() → e.message
|
||||
tsCode = tsCode.replace(/(\w+)\.getMessage\(\)/g, '$1.message');
|
||||
tsCode = tsCode.replace(/(\w+)\.printStackTrace\(\)/g, 'console.error($1)');
|
||||
|
||||
// 3. 异常抛出
|
||||
tsCode = tsCode.replace(/throw\s+new\s+CommonException\(/g, 'throw new BadRequestException(');
|
||||
tsCode = tsCode.replace(/throw\s+new\s+AuthException\(/g, 'throw new UnauthorizedException(');
|
||||
tsCode = tsCode.replace(/throw\s+new\s+RuntimeException\(/g, 'throw new Error(');
|
||||
tsCode = tsCode.replace(/throw\s+new\s+Exception\(/g, 'throw new Error(');
|
||||
tsCode = tsCode.replace(/throw\s+new\s+IOException\(/g, 'throw new Error(');
|
||||
tsCode = tsCode.replace(/throw\s+new\s+SQLException\(/g, 'throw new Error(');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析需要的异常imports
|
||||
*/
|
||||
analyzeImports(tsCode) {
|
||||
const imports = new Set();
|
||||
|
||||
if (tsCode.includes('BadRequestException')) {
|
||||
imports.add('BadRequestException');
|
||||
}
|
||||
if (tsCode.includes('UnauthorizedException')) {
|
||||
imports.add('UnauthorizedException');
|
||||
}
|
||||
|
||||
return Array.from(imports);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExceptionConverter;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 类型转换器
|
||||
*
|
||||
* 负责Java类型到TypeScript类型的转换
|
||||
*/
|
||||
class TypeConverter {
|
||||
/**
|
||||
* 转换类型
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. 基础类型
|
||||
tsCode = tsCode.replace(/\bInteger\b/g, 'number');
|
||||
tsCode = tsCode.replace(/\bLong\b/g, 'number');
|
||||
tsCode = tsCode.replace(/\bDouble\b/g, 'number');
|
||||
tsCode = tsCode.replace(/\bFloat\b/g, 'number');
|
||||
|
||||
// 2. Java对象类型
|
||||
tsCode = tsCode.replace(/\bJSONObject\b/g, 'Record<string, any>');
|
||||
tsCode = tsCode.replace(/\bId\b/g, 'number');
|
||||
|
||||
// 3. Java类型转换语法:(Type) value → value
|
||||
tsCode = tsCode.replace(/\(\s*(?:int|long|double|float|String|Integer|Long|Double|Float|number)\s*\)\s*/g, '');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TypeConverter;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 集合判空转换器
|
||||
*
|
||||
* Java集合判空方法 → TypeScript数组判空
|
||||
*/
|
||||
class CollectionConverter {
|
||||
/**
|
||||
* 转换集合判空
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. CollectionUtil.isEmpty/isNotEmpty
|
||||
tsCode = tsCode.replace(/(?:CollectionUtil|CollUtil)\.isEmpty\(([^)]+)\)/g, '(!$1 || $1.length === 0)');
|
||||
tsCode = tsCode.replace(/(?:CollectionUtil|CollUtil)\.isNotEmpty\(([^)]+)\)/g, '($1 && $1.length > 0)');
|
||||
|
||||
// 2. list.isEmpty() → list.length === 0
|
||||
// 支持链式调用:obj.getConfig().isEmpty()
|
||||
tsCode = tsCode.replace(/([a-zA-Z_$][\w$.]*)\.isEmpty\(\)/g, '$1.length === 0');
|
||||
tsCode = tsCode.replace(/!([a-zA-Z_$][\w$.]*)\.isEmpty\(\)/g, '$1.length > 0');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CollectionConverter;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 配置访问转换器
|
||||
*
|
||||
* WebAppEnvs / GlobalConfig → ConfigService
|
||||
*/
|
||||
class ConfigConverter {
|
||||
/**
|
||||
* 转换配置访问
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. WebAppEnvs.get().property.exists() → fs.existsSync(this.config.get('property'))
|
||||
tsCode = tsCode.replace(/WebAppEnvs\.get\(\)\.(\w+)\.exists\(\)/g, (match, prop) => {
|
||||
return `fs.existsSync(this.config.get('${prop}'))`;
|
||||
});
|
||||
|
||||
// 2. WebAppEnvs.get().property → this.config.get('property')
|
||||
tsCode = tsCode.replace(/WebAppEnvs\.get\(\)\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
|
||||
// 3. WebAppEnvs.get() → this.config
|
||||
tsCode = tsCode.replace(/WebAppEnvs\.get\(\)/g, 'this.config');
|
||||
|
||||
// 4. WebAppEnvs.staticProperty → this.config.get('staticProperty')
|
||||
tsCode = tsCode.replace(/WebAppEnvs\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
|
||||
// 5. GlobalConfig.property.exists() → fs.existsSync(this.config.get('property'))
|
||||
tsCode = tsCode.replace(/GlobalConfig\.(\w+)\.exists\(\)/g, (match, prop) => {
|
||||
return `fs.existsSync(this.config.get('${prop}'))`;
|
||||
});
|
||||
|
||||
// 6. GlobalConfig.property → this.config.get('property')
|
||||
tsCode = tsCode.replace(/GlobalConfig\.(\w+)/g, (match, prop) => {
|
||||
return `this.config.get('${prop}')`;
|
||||
});
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析是否需要ConfigService
|
||||
*/
|
||||
needsConfigService(tsCode) {
|
||||
return tsCode.includes('this.config.');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConfigConverter;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 文件操作转换器
|
||||
*
|
||||
* Java File API / FileUtils → Node.js fs/path
|
||||
*/
|
||||
class FileConverter {
|
||||
/**
|
||||
* 转换文件操作
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【FileUtils工具类】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsCode = tsCode.replace(/FileUtils\.cleanDirectory\(([^)]+)\)/g, 'fs.rmSync($1, { recursive: true, force: true })');
|
||||
tsCode = tsCode.replace(/FileUtils\.copyFile\(([^,]+),\s*([^)]+)\)/g, 'fs.copyFileSync($1, $2)');
|
||||
tsCode = tsCode.replace(/FileUtils\.deleteDirectory\(([^)]+)\)/g, 'fs.rmSync($1, { recursive: true, force: true })');
|
||||
tsCode = tsCode.replace(/FileUtils\.readFileToString\(([^)]+)\)/g, 'fs.readFileSync($1, \'utf-8\')');
|
||||
tsCode = tsCode.replace(/FileUtils\.writeStringToFile\(([^,]+),\s*([^)]+)\)/g, 'fs.writeFileSync($1, $2, \'utf-8\')');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【File API】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
tsCode = tsCode.replace(/new\s+File\(([^)]+)\)/g, '$1');
|
||||
tsCode = tsCode.replace(/(\w+)\.exists\(\)/g, 'fs.existsSync($1)');
|
||||
tsCode = tsCode.replace(/(\w+)\.isDirectory\(\)/g, 'fs.lstatSync($1).isDirectory()');
|
||||
tsCode = tsCode.replace(/(\w+)\.getName\(\)/g, 'path.basename($1)');
|
||||
tsCode = tsCode.replace(/(\w+)\.listFiles\(\)/g, 'fs.readdirSync($1)');
|
||||
tsCode = tsCode.replace(/(\w+)\.getPath\(\)/g, '$1');
|
||||
tsCode = tsCode.replace(/(\w+)\.getAbsolutePath\(\)/g, 'path.resolve($1)');
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 【Java NIO API】
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// Files.list(Paths.get(x)) → fs.readdirSync(x).map(...)
|
||||
tsCode = tsCode.replace(/Files\.list\(Paths\.get\(([^)]+)\)\)/g, 'fs.readdirSync($1)');
|
||||
|
||||
// Paths.get(x) → x
|
||||
tsCode = tsCode.replace(/Paths\.get\(([^)]+)\)/g, '$1');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析是否需要Node.js模块
|
||||
*/
|
||||
analyzeNodeModules(tsCode) {
|
||||
const modules = new Set();
|
||||
|
||||
if (tsCode.includes('fs.')) {
|
||||
modules.add('fs');
|
||||
}
|
||||
if (tsCode.includes('path.')) {
|
||||
modules.add('path');
|
||||
}
|
||||
|
||||
return Array.from(modules);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FileConverter;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* JSON工具类转换器
|
||||
*
|
||||
* JSONUtil / JSON相关 → TypeScript JSON / JsonUtils
|
||||
*/
|
||||
class JsonConverter {
|
||||
/**
|
||||
* 转换JSON工具类
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// JSONUtil.parseObj() → JSON.parse()
|
||||
tsCode = tsCode.replace(/JSONUtil\.parseObj\(([^)]+)\)/g, 'JSON.parse($1)');
|
||||
|
||||
// JSONUtil.toBean() → 需要手动处理,暂时保留TODO
|
||||
tsCode = tsCode.replace(/JSONUtil\.toBean\(([^,]+),\s*([^)]+)\.class\)/g, '/* TODO: JSONUtil.toBean($1, $2) */Object.assign(new $2(), JSON.parse(JSON.stringify($1)))');
|
||||
|
||||
// JSONObject → Record<string, any>
|
||||
tsCode = tsCode.replace(/JSONObject/g, 'Record<string, any>');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JsonConverter;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 对象工具类转换器
|
||||
*
|
||||
* ObjectUtil / BeanUtils等 → TypeScript对象操作
|
||||
*/
|
||||
class ObjectConverter {
|
||||
/**
|
||||
* 转换对象工具类
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. ObjectUtil.isNotEmpty() → value && ...
|
||||
tsCode = tsCode.replace(/ObjectUtil\.isNotEmpty\(([^)]+)\)/g, '($1 && Object.keys($1).length > 0)');
|
||||
tsCode = tsCode.replace(/ObjectUtil\.isEmpty\(([^)]+)\)/g, '(!$1 || Object.keys($1).length === 0)');
|
||||
|
||||
// 2. ObjectUtil.defaultIfNull(a, b) → a ?? b
|
||||
tsCode = tsCode.replace(/ObjectUtil\.defaultIfNull\(([^,]+),\s*([^)]+)\)/g, '$1 ?? $2');
|
||||
|
||||
// 3. BeanUtils.copyProperties(src, dest) → Object.assign(dest, src)
|
||||
tsCode = tsCode.replace(/BeanUtils\.copyProperties\(([^,]+),\s*([^)]+)\)/g, 'Object.assign($2, $1)');
|
||||
|
||||
// 4. System.out.println() → console.log()
|
||||
tsCode = tsCode.replace(/System\.out\.println\(/g, 'console.log(');
|
||||
tsCode = tsCode.replace(/System\.err\.println\(/g, 'console.error(');
|
||||
|
||||
// 5. System.currentTimeMillis() → Date.now()
|
||||
tsCode = tsCode.replace(/System\.currentTimeMillis\(\)/g, 'Date.now()');
|
||||
|
||||
// 6. Assert.notNull() → if (!x) throw new Error()
|
||||
tsCode = tsCode.replace(/Assert\.notNull\(([^,]+),\s*([^)]+)\)/g, 'if (!$1) throw new BadRequestException($2)');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ObjectConverter;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 字符串方法转换器
|
||||
*
|
||||
* Java String方法 → TypeScript String方法
|
||||
*/
|
||||
class StringConverter {
|
||||
/**
|
||||
* 转换字符串方法
|
||||
*/
|
||||
convert(javaCode) {
|
||||
let tsCode = javaCode;
|
||||
|
||||
// 1. .equals() → ===
|
||||
// 支持方法调用结果:path.basename(child).equals("sql")
|
||||
tsCode = tsCode.replace(/([a-zA-Z_$][\w$.()]+)\.equals\(([^)]+)\)/g, '$1 === $2');
|
||||
|
||||
// 2. .equalsIgnoreCase() → .toLowerCase() === xxx.toLowerCase()
|
||||
tsCode = tsCode.replace(/([a-zA-Z_$][\w$.()]+)\.equalsIgnoreCase\(([^)]+)\)/g, '$1.toLowerCase() === $2.toLowerCase()');
|
||||
|
||||
// 3. .contains() → .includes()
|
||||
tsCode = tsCode.replace(/\.contains\(/g, '.includes(');
|
||||
|
||||
// 4. StringUtils.isEmpty/isNotEmpty
|
||||
tsCode = tsCode.replace(/StringUtils\.isEmpty\(([^)]+)\)/g, '(!$1 || $1.trim() === \'\')');
|
||||
tsCode = tsCode.replace(/StringUtils\.isNotEmpty\(([^)]+)\)/g, '($1 && $1.trim() !== \'\')');
|
||||
|
||||
// 5. String.valueOf() → String()
|
||||
tsCode = tsCode.replace(/String\.valueOf\(([^)]+)\)/g, 'String($1)');
|
||||
|
||||
return tsCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StringConverter;
|
||||
|
||||
@@ -39,3 +39,4 @@ export * from "./infra/queue/job-scheduler.service";
|
||||
export * from "./infra/events/event-listener.service";
|
||||
export * from "./infra/events/event-bus";
|
||||
export * from "./infra/common/result";
|
||||
export { ConfigService } from "@nestjs/config";
|
||||
|
||||
@@ -358,14 +358,14 @@ export class AuthSiteServiceImplService {
|
||||
*/
|
||||
async getSiteGroupAppList(...args: any[]): Promise<any> {
|
||||
const siteGroupAppList: string[] = getSiteGroupApps();
|
||||
if (CollectionUtils.isEmpty(siteGroupAppList)){
|
||||
if (CollectionUtils.isEmpty(siteGroupAppList)){
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
const addonList: Addon[] = addonMapper.selectList(new LambdaQueryWrapper<Addon>()
|
||||
.eq(Addon::getStatus, AddonStatusEnum.ON.getCode())
|
||||
.eq(Addon::getType, AddonActionEnum.APP.getCode())
|
||||
.in(Addon::getKey, siteGroupAppList));
|
||||
.eq(Addon::getStatus, AddonStatusEnum.ON.getCode())
|
||||
.eq(Addon::getType, AddonActionEnum.APP.getCode())
|
||||
.in(Addon::getKey, siteGroupAppList));
|
||||
|
||||
return processAddonList(addonList);
|
||||
return processAddonList(addonList);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user