TurboAPI
基于SpringCloud接口开放平台。服务间通信使用DubboRPC, 接口模块接入AIGC进行功能扩展。使用FreeMarker模板引擎通过接口meta-data实现自动化的文档生成 , SDK代码生成。接口说明文档通过Docusaurus进行文markdown转换与构建。支持异步接口调用
Install / Use
/learn @adorabled4/TurboAPIREADME
TurboAPI
项目简介
「TurboAPI」是一款高效、可靠和安全的接口开放平台,为广大用户提供高质量、可靠、安全的接口服务,帮助用户轻松实现各种功能和数据交互,提高工作效率和用户体验。
本项目为前后端分离项目,前端主要采用TypeScript、React、Antd Pro等主流开发框架。后端采用Spring Cloud SpringBoot 作为业务框架。通过Springcloud Gateway作为全局网关实现流量控制、负载均衡以及路由管理,使用Mybatis-plus作为持久层技术。使用Apache Dubbo做高性能远程服务调用。同时Nacos作为注册中心,完成服务注册与发现,通过各模块主要功能以及业务进行模块的合理划分。通过全链路日志以及回调配置, 支持异步接口调用 , 同时通过FreeMarker模板引擎进行自动化SDK代码以及接口文档生成 , 并通过Github Action完成SDK发版与接口文档部署。
主要开源组件与版本
| item | version | | ---------------- | -------------- | | JDK | 17 | | SpringBoot | 2.7.9 | | SpringCloud | 2021.0.7.0 | | Apache Dubbo | 3.1.6 | | OpenAPI | 3 | | MySQL | 5.7 | | Redis | 6.2 | | Mybatis-Plus | 3.5.2 | | Nacos | 2.2.1 | | FreeMarker | 2.3.2 |
关于版本说明 , 项目开发时使用是JDK17, 同时完美支持JDK1.8
实测 JDK17下启动速度效果更好, 建议使用JDK17
项目架构图:

主要功能说明
发布接口
接口表根据易变信息分成了两个表 : interface_info 以及 interface_variable_info
结构如下:
前者主要是接口的基本信息, 后者主要包含 示例 , 请求元信息 以及SDK生成需要的信息。
-- ----------------------------
-- Table structure for interface_info
-- ----------------------------
DROP TABLE IF EXISTS `interface_info`;
CREATE TABLE `interface_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '接口id',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '接口名称',
`cost` int(11) NULL DEFAULT 1 COMMENT '消耗硬币数',
`description` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口描述',
`image_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口图片',
`category_bit_map` bigint(20) NOT NULL COMMENT '接口分类',
`status` tinyint(4) NULL DEFAULT NULL COMMENT '接口状态',
`is_async` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否是异步API',
`doc_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接口文档地址',
`request_method` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求方式',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint(4) NULL DEFAULT 0 COMMENT '逻辑删除字段',
PRIMARY KEY (`id`, `category_bit_map`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for interface_variable_info
-- ----------------------------
DROP TABLE IF EXISTS `interface_variable_info`;
CREATE TABLE `interface_variable_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '接口ID',
`request_param` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求参数',
`request_headers` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求头',
`request_example` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求示例',
`response_example` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '响应示例',
`total_call_count` bigint(20) NULL DEFAULT 0 COMMENT '总调用次数',
`call_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调用路径',
`sdk_method_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'SDK中的方法名称',
`model_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '响应结果对应的实体类的名称',
`sdk_param_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'SDK中的参数名称',
`service_address` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '服务地址',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint(4) NULL DEFAULT 0 COMMENT '逻辑删除字段',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
对于发布接口 , 流程是
- 确定入参 以及 返回结果 => 添加到 api-common 的
com.dhx.apicommon.model中 (接口功能, 入参 , 出参设计) - 开发功能 , v** 的接口版本需要区分清楚
- 进行功能测试
- 调用api-core接口(访问
http://localhost:88/doc.html#/api-core/interface%E6%8E%A7%E5%88%B6%E5%B1%82/publishInterface), 添加 接口元信息 - 手动 / 自动 生成SDK 代码以及接口Markdown文档
- GithubAction 执行CI/CD , 完成SDK发版
发布接口的示例参数如下
{
"name": "在线判题API",
"description": "判题API,输入代码以及输入(input), 返回程序运行结果以及消耗内存,运行时间等信息。(测试期间,目前仅支持Java)",
"imageUrl": "http://dhx-blog.oss-cn-beijing.aliyuncs.com/dhx/oj.png",
"categories": ["数据智能","应用开发","限时免费"],
"status": "可用",
"mockCategoryEnum": "数据智能",
"docUrl": "https://turboapi-doc.dhx.icu/docs/v2/在线判题API",
"requestMethod": "POST",
"requestParam": "{`code`:`string`,`input`:`string[]`,`language`:`string`}",
"requestHeaders": "content-type:application/json",
"callPath": "api/v3/judge/java",
"serviceAddress": "http://turboapi.dhx.icu",
"isAigc": false,
"sdkMethodName": "callOJ",
"sdkParamName": "com.dhx.apicommon.model.v2.param.OJParam",
"version": "v2",
"modelName": "java.lang.Object",
"isAsync": true
}
SDK代码生成
详细内容可以参考 : TurboAPI-SDK自动生成技术与代码实践
大致的流程上是 : 监听接口元数据 -> 触发hook -> 执行代码生成 -> 更新到代码仓库 -> 自动化测试 -> CI/CD -> 完成发版
主要强调三个问题 :
- ftl 的缩进问题
- 接口元信息 (通过上面的表设计已经解决, 这里简单解释)
- FreeMarker的基本语法。
- 相对路径问题
缩进问题
关于缩进 , ftl文件中是不能包含任何的空格以及制表符的(除了 标签 携带的制表符)
这就会导致生成的代码没有任何格式, 可读性极差。
比如:
public class ${className} {
public ${api.modelName} ${api.methodName}() {
try {
String nowTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
String result = HttpRequest.get(SERVER_HOST + "${callPath}").addHeaders(getHeaderMap()).execute().body();
BaseResponse baseResponse = JSONUtil.toBean(result, BaseResponse.class);
if (baseResponse.getCode() == 200) {
String dataStr = JSONUtil.toJsonStr(baseResponse.getData());
if (dataStr == null || dataStr.equals("")) {
log.error("\u001B[31m" + e.getClass() + "\u001B[0m: " + "[HxApiClient] 调用接口失败 --" + baseResponse.toString());
}
${basePackage}.model.${api.modelName} obj = JSONUtil.toBean(dataStr, ${basePackage}.model.${api.modelName}.class);
return obj;
} else {
throw new BusinessException(baseResponse.getCode(), baseResponse.getMessage());
}
} catch (IORuntimeException e) {
log.error("\u001B[31m" + e.getClass() + "\u001B[0m: " + "[HxApiClient] 访问服务器失败 --" + e.getMessage());
} catch (RuntimeException e) {
log.error("\u001B[31m" + e.getClass() + "\u001B[0m: " + "[HxApiClient] 调用接口失败 --" + e.getMessage());
}
}
}
解决办法是准备一个FileUtil, 对ftl文件进行格式化处理, 生成对应的制表符即可。
public class ${className} {
${"\t"}@Slf4j
${"\t"}public ${basePackage}.model.${modelName} ${methodName}() {
${"\t"}${"\t"}try {
${"\t"}${"\t"}${"\t"}String nowTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
${"\t"}${"\t"}${"\t"}String result = HttpRequest.get(SERVER_HOST + "${callPath}").addHeaders(getHeaderMap()).execute().body();
${"\t"}${"\t"}${"\t"}BaseResponse baseResponse = JSONUtil.toBean(result, BaseResponse.class);
${"\t"}${"\t"}${"\t"}if (baseResponse.getCode() == 200) {
${"\t"}${"\t"}${"\t"}${"\t"}String dataStr = JSONUtil.toJsonStr(baseResponse.getData());
${"\t"}${"\t"}${"\t"}${"\t"}if (dataStr == null || dataStr.equals("")) {
${"\t"}${"\t"}${"\t"}${"\t"}${"\t"}log.error("\u001B[31m" + e.getClass() + "\u001B[0m: " + "[HxApiClient] 调用接口失败 --" + baseResponse.toString());
${"\t"}${"\t"}${"\t"}${"\t"}}
${"\t"}${"\t"}${"\t"}${"\t"}${basePackage}.model.${modelName} obj = JSONUtil.toBean(dataStr, ${basePackage}.model.${modelName}.class);
${"\t"}${"\t"}${"\t"}${"\t"}return obj;
${"\t"}${"\t"}${"\t"}} else {
${"\t"}${"\t"}${"\t"}${"\t"}throw new BusinessException(baseResponse.getCode(), baseResponse.getMessage());
${"\t"}${"\t"}${"\t"}}
${"\t"}${"\t"}} catch (IORuntimeException e) {
${"\t"}${"\t"}${"\t"}log.error("\u001B[31m" + e.getClass() + "\u001B[0m: " + "[HxApiClient] 访问服务器失败 --" + e.getMessage());
${"\t"}${"\t"}} catch (RuntimeException e) {
${"\t"}${"\t"}${"\t"}log.error("\u001B[31m" + e.getClass() + "\u001B[0m: " + "[HxApiClient] 调用接口失败 --" + e.getMessage());
${"\t"}${"\t"}}
${"\t"}}
}
具体的操作就是
- 读取文件
- 操作字符串
- 写文件(写到新的ftl文件中)
这里的debug还是非常痛苦的, 好在顺利完成了。
接口元信息
为了简化SDK代码的生成, 统一通过设定类来规定一个接口的入参, 具体可以参考com.dhx.apicommon.model.vXXX下的类
在 接口信息表中, 我们通过写入全类名 , 即可完成顺利完成代码的生成
比如
public com.dhx.common.model.v1.Poet1 getWeather1(com.dhx.common.model.v1.query.PoetQuery param) {
}
meta-data维护
关于接口的meta-data, 有
- basePackage
- className(ApiClient)
- apis:
- name
- description
- callPath
- serviceAddress
- requestParam
- requestMethod
- requestHeaders
- sdkMethodName
- sdkParamName
- imageUrl
- version
- requestExample
- responseExample
- docUrl
- status
- categories
FreeMarker基本语法
掌握基本的if-else , list 以及 判空 操作即可
全链路日志
对于分布式系统或者是微服务项目, 常常会出现多个服务之间互相调用的情况, 对于单体项目的日志, 我们是难以进行错误排查的
全链路日志是指记录了一个请求/事务在整个系统中经过的所有组件、服务和节点的日志信息。
这包括了请求在系统中的调用、处理、传输、响应等环节的详细日志。
全链路日志可以帮助定位分布式系统中的故障或问题,通过查看全链路日志,可以清晰地了解请求在系统中的流转情况,从而更容易发现故障发生的位置和原因。
-
对于请求 : 从网关路由到某个模块, 此时需要网关创建
trace_id, 然后通过配置日志文件打印相关内容 -
对于RPC : 需要通过RpcContext来传播相关的
trace_id, 注意Dubbo这里的配置是有点复杂的- 实现接口
org.apache.dubbo.rpc.Filter - 在resource下添加
META-INF/dubbo目录下添加org.apache.dubbo.rpc.Filter文件 - 在文件中 输入 `traceId=com.dhx.apicore.aop.RemoteTraceIdFilter
- 实现接口
Related Skills
node-connect
345.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
prose
345.9kOpenProse VM skill pack. Activate on any `prose` command, .prose files, or OpenProse mentions; orchestrates multi-agent workflows.
frontend-design
106.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
