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方法体
|
* 1. 提取Java方法体
|
||||||
* 2. Java语法 → TypeScript语法转换
|
* 2. 调用各个专业转换器进行转换
|
||||||
* 3. Java工具类 → NestJS Boot层映射
|
* 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 {
|
class ServiceMethodConverter {
|
||||||
constructor() {
|
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】基础语法转换
|
// 【阶段1】基础语法转换
|
||||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
tsBody = this.convertBasicSyntax(tsBody);
|
tsBody = this.basicSyntax.convert(tsBody);
|
||||||
|
|
||||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
// 【阶段2】类型转换
|
// 【阶段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');
|
tsBody = tsBody.split('\n').map(line => ' ' + line).join('\n');
|
||||||
|
|
||||||
return tsBody;
|
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
|
* 分析需要的imports
|
||||||
*
|
*
|
||||||
@@ -342,23 +137,15 @@ class ServiceMethodConverter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// NestJS异常
|
// NestJS异常
|
||||||
if (convertedBody.includes('BadRequestException')) {
|
const nestjsImports = this.exception.analyzeImports(convertedBody);
|
||||||
imports.nestjs.add('BadRequestException');
|
nestjsImports.forEach(imp => imports.nestjs.add(imp));
|
||||||
}
|
|
||||||
if (convertedBody.includes('UnauthorizedException')) {
|
|
||||||
imports.nestjs.add('UnauthorizedException');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node.js模块
|
// Node.js模块
|
||||||
if (convertedBody.includes('fs.')) {
|
const nodeModules = this.file.analyzeNodeModules(convertedBody);
|
||||||
imports.nodeModules.add('fs');
|
nodeModules.forEach(mod => imports.nodeModules.add(mod));
|
||||||
}
|
|
||||||
if (convertedBody.includes('path.')) {
|
|
||||||
imports.nodeModules.add('path');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigService
|
// ConfigService
|
||||||
if (convertedBody.includes('this.config.')) {
|
if (this.config.needsConfigService(convertedBody)) {
|
||||||
imports.boot.add('ConfigService');
|
imports.boot.add('ConfigService');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,4 +158,3 @@ class ServiceMethodConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = 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-listener.service";
|
||||||
export * from "./infra/events/event-bus";
|
export * from "./infra/events/event-bus";
|
||||||
export * from "./infra/common/result";
|
export * from "./infra/common/result";
|
||||||
|
export { ConfigService } from "@nestjs/config";
|
||||||
|
|||||||
@@ -358,14 +358,14 @@ export class AuthSiteServiceImplService {
|
|||||||
*/
|
*/
|
||||||
async getSiteGroupAppList(...args: any[]): Promise<any> {
|
async getSiteGroupAppList(...args: any[]): Promise<any> {
|
||||||
const siteGroupAppList: string[] = getSiteGroupApps();
|
const siteGroupAppList: string[] = getSiteGroupApps();
|
||||||
if (CollectionUtils.isEmpty(siteGroupAppList)){
|
if (CollectionUtils.isEmpty(siteGroupAppList)){
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
const addonList: Addon[] = addonMapper.selectList(new LambdaQueryWrapper<Addon>()
|
const addonList: Addon[] = addonMapper.selectList(new LambdaQueryWrapper<Addon>()
|
||||||
.eq(Addon::getStatus, AddonStatusEnum.ON.getCode())
|
.eq(Addon::getStatus, AddonStatusEnum.ON.getCode())
|
||||||
.eq(Addon::getType, AddonActionEnum.APP.getCode())
|
.eq(Addon::getType, AddonActionEnum.APP.getCode())
|
||||||
.in(Addon::getKey, siteGroupAppList));
|
.in(Addon::getKey, siteGroupAppList));
|
||||||
|
|
||||||
return processAddonList(addonList);
|
return processAddonList(addonList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user