SkillAgentSearch skills...

TurboAPI

基于SpringCloud接口开放平台。服务间通信使用DubboRPC, 接口模块接入AIGC进行功能扩展。使用FreeMarker模板引擎通过接口meta-data实现自动化的文档生成 , SDK代码生成。接口说明文档通过Docusaurus进行文markdown转换与构建。支持异步接口调用

Install / Use

/learn @adorabled4/TurboAPI
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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;

对于发布接口 , 流程是

  1. 确定入参 以及 返回结果 => 添加到 api-common 的 com.dhx.apicommon.model 中 (接口功能, 入参 , 出参设计)
  2. 开发功能 , v** 的接口版本需要区分清楚
  3. 进行功能测试
  4. 调用api-core接口(访问 http://localhost:88/doc.html#/api-core/interface%E6%8E%A7%E5%88%B6%E5%B1%82/publishInterface), 添加 接口元信息
  5. 手动 / 自动 生成SDK 代码以及接口Markdown文档
  6. 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 -> 完成发版

主要强调三个问题 :

  1. ftl 的缩进问题
  2. 接口元信息 (通过上面的表设计已经解决, 这里简单解释)
  3. FreeMarker的基本语法。
  4. 相对路径问题

缩进问题

关于缩进 , 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"}}
}

具体的操作就是

  1. 读取文件
  2. 操作字符串
  3. 写文件(写到新的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这里的配置是有点复杂的

    1. 实现接口 org.apache.dubbo.rpc.Filter
    2. 在resource下添加 META-INF/dubbo 目录下添加 org.apache.dubbo.rpc.Filter 文件
    3. 在文件中 输入 `traceId=com.dhx.apicore.aop.RemoteTraceIdFilter

Related Skills

View on GitHub
GitHub Stars62
CategoryDevelopment
Updated20d ago
Forks10

Languages

Java

Security Score

100/100

Audited on Mar 13, 2026

No findings