34 Commits
Author SHA1 Message Date
蓝白 ea26c0fcd7 修复连接泄露问题 2026-02-03 21:36:37 +08:00
蓝白 9d5f0c05cb jdk8 2026-02-03 13:43:34 +08:00
蓝白 9420a17bcc 修复bug 2026-02-03 12:56:29 +08:00
蓝白 3b6a95c731 更新 2026-01-20 19:54:45 +08:00
蓝白 c59192c144 更新版本至4.0 2026-01-18 16:18:50 +08:00
蓝白 299bec29e0 更新版本至4.0 2026-01-18 15:58:26 +08:00
蓝白 86bb9c2f2a 更新版本至4.0 2026-01-18 15:56:05 +08:00
蓝白 121b6653ab 更新版本至4.0 2026-01-18 15:45:01 +08:00
蓝白 9fc3990411 更新描述 2026-01-13 23:03:46 +08:00
蓝白 7a53390946 更新描述 2026-01-04 16:47:29 +08:00
蓝白 2e8a930b16 修复ck链接问题 2025-12-30 14:32:01 +08:00
蓝白 444c288079 更新描述 2025-12-26 13:04:25 +08:00
蓝白 669c8261f5 修复数据库类型图标匹配问题 2025-12-25 22:21:57 +08:00
蓝白 2b752f56e1 更新驱动 2025-12-25 11:20:36 +08:00
蓝白 9b8858999c 优化查询返回字段排序问题 2025-12-25 11:15:11 +08:00
蓝白 548919485c 更新页面 2025-12-24 15:43:18 +08:00
蓝白 464ee5c738 更新驱动 2025-12-24 15:39:43 +08:00
蓝白 623599998f 更新描述 2025-10-27 23:52:50 +08:00
蓝白 37ba418f87 更新描述 2025-10-18 10:51:49 +08:00
蓝白 affcfb9234 更新描述 2025-10-13 16:01:52 +08:00
蓝白 add6362fb8 更新jdk环境 2025-10-01 21:09:57 +08:00
蓝白 ea85ff64e1 更新版本3.5.2 2025-10-01 11:04:35 +08:00
蓝白 f5ff5e4f54 更新版本3.5.2 2025-10-01 11:02:05 +08:00
蓝白 2183f2d2f6 更新版本3.5.2 2025-10-01 10:55:47 +08:00
蓝白 4e477d86c3 更新版本3.5.2 2025-10-01 10:40:07 +08:00
蓝白 284eb0394d 优化页面参数设置和样式图标 2025-10-01 10:39:06 +08:00
蓝白 8e56270cf5 1、接口日志新增响应结果
2、优化接口加载
3、优化异常处理
4、优化日志查询
2025-10-01 10:04:28 +08:00
蓝白 6b0c4bea49 版本更新至3.5.1 2025-09-30 20:05:18 +08:00
蓝白 9edd1c3e00 优化请求参数和优化测试bug 2025-09-30 20:04:53 +08:00
蓝白 3f0cf9af78 优化接口测试和token有效期 2025-09-30 19:51:07 +08:00
蓝白 ad8636ced0 更新描述 2025-09-25 10:47:18 +08:00
蓝白 03486448f1 升级达梦数据库驱动 2025-09-12 22:48:05 +08:00
蓝白 5925263443 更新描述 2025-07-16 12:30:00 +08:00
蓝白 dc7cc0bd79 跳转字段类型 2025-07-16 12:29:20 +08:00
250 changed files with 5207 additions and 1077 deletions

No files matched your search

+1 -1
View File
@@ -45,12 +45,12 @@ buildNumber.properties
####################### svn ##############################
.svn
####################### eclipse ##############################
.project
.settings
bin/
logs/
db/
.classpath
+2 -2
View File
@@ -1,5 +1,5 @@
FROM openjdk:17-jdk-alpine
LABEL version="2.3.2"
FROM eclipse-temurin:21-jdk-alpine
LABEL version="4.0.0"
COPY crabc-admin.jar /app.jar
ENV LANG="en_US.UTF-8"
+68 -48
View File
@@ -3,19 +3,23 @@
crabc由create + abc 组合而来,意在为项目系统构建的通用的接口底座。
# 介绍
**crabc** 是低代码接口开发平台,企业数据服务平台,深度整合SpringBoot和Mybatis实现动态数据源和动态SQL。
支持接入(mysql、oracle、postgresql、sqlserver、达梦、TiDB、es和mongodb)等SQL或/NoSQL数据源,
**crabc** 是接口快速开发平台,企业数据服务平台,sql2Api,深度整合SpringBoot和Mybatis实现动态数据源和动态SQL、函数和存储过程。
支持接入(mysql、oracle、postgresql、sqlserver、达梦、TiDB、es和hive)等SQL或/NoSQL数据源,
在线可视化编写SQL后即可快速生成接口对外提供服务,接口一键上下线。减少通用接口的SQL编写,让开发人员专注更复杂的业务逻辑实现。
支持Mybatis中if等标签语法、数据脱敏、数据转换等功能,集成微服务网关支持接入第三方接口、权限认证、限流、缓存、告警监控和统一日志等一站式API数据服务。
支持Mybatis中if等标签语法、数据脱敏、数据转换、国密加密、协同开发等功能,可集成微服务网关支持接入第三方接口、权限认证、限流、缓存、告警监控和统一日志等一站式API数据服务,
助力于企业数据资产价值升级,成为企业数字化转型的重要推动力。
![img.png](doc/home.png)
## 功能
- **工作台**:可视化编写SQL快速的对外发布成API接口,支持多SQL同时执行、Mybatis标签语法,实现动态SQL和动态标签
- **数据源管理**:动态加载数据库,支持多种SQL/NoSQL数据库。
- **应用列表**:Code认证、签名认证的应用创建管理以及对API调用在线授权。
- **应用列表**:AppKey认证、签名认证的应用创建管理以及对API调用在线授权。
- **接口列表**:查看开发中和已发布的API接口,可进行上下线管理,编辑升级等
- **接口文档**:生成在线接口文档,支持在线测试。
- **接口日志**:查看发布的接口被调用日志列表和请求详情。
## 接口开发流程
![img.png](doc/dev_flow.png)
## 模块
~~~
cn.crabc
@@ -30,10 +34,18 @@ cn.crabc
├──pom.xml // 依赖
~~~
## 数据库脚本
数据库脚本文件可加QQ群文件中获取
## 版本说明
dev分支框架SpringBoot已升级至3.5,jdk需17及以上版本 \
2.x分支框架SpringBoot还是2.x
## 企业版
https://pingapi.cn
## 企业版架构图
## 运行启动
```
1、先执行db/dml.sql脚本,创建库表和初始化数据
@@ -61,7 +73,7 @@ gitee: https://gitee.com/linebyte/crabc
<dependency>
<groupId>cn.crabc</groupId>
<artifactId>crabc-spring-boot-starter</artifactId>
<version>3.5.0</version>
<version>4.0.0</version>
</dependency>
```
在程序启动类中添加下面注解
@@ -72,60 +84,48 @@ gitee: https://gitee.com/linebyte/crabc
## Docker启动
先执行db/dml.sql脚本,创建库表和初始化数据,建议mysql 8.0+版本
X86/AMD架构
```
docker pull crabc/crabc-admin:latest
docker run -p 9377:9377 -e DB_URL='jdbc:mysql://localhost:3306/crabc' -e DB_USER=root -e DB_PWD=root -d --name crabc-admin crabc/crabc-admin:latest
docker run -p 9377:9377 -e DB_URL=jdbc:mysql://localhost:3306/crabc -e DB_USER=root -e DB_PWD=root -d --name crabc-api crabc/crabc-api:latest
```
ARM架构
```
docker run -p 9377:9377 -e DB_URL=jdbc:mysql://localhost:3306/crabc -e DB_USER=root -e DB_PWD=root -d --name crabc-admin crabc/crabc-admin:latest
```
访问地址:http://127.0.0.1:9377
账号密码:admin/admin123
## Windows桌面运行
_## Windows桌面运行
先执行db/dml.sql脚本,创建库表和初始化数据,建议mysql 8.0+版本
```
java -jar crabc-admin.jar --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crabc --spring.datasource.username=root --spring.datasource.password=root
java -jar crabc-api.jar --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crabc --spring.datasource.username=root --spring.datasource.password=root
```
## 企业版和开源版对比
| 功能 | 社区版 | 企业版 |
|--------|------------------------------| --------------------------|
| 架构设计 | 单体 | 微服务 |
| 接口创建模式 | SQL脚本 | SQL脚本、图表、代理转发 |
| 动态标签 | 支持 | 支持 |
| 动态路由 | 不支持 | 支持 |
| 数据库 | SQL:mysql/oracle/postgresql/sqlserver <br>TiDB/OpenGauss/Doris |SQL:mysql/oracle/postgresql/sqlserver/tidb<br>/opengauss/doris/oceanbase/dm/gbase <br> NoSQL:es/mongodb <br> TSDB: TDengine |
| 认证方式 | 无/Code认证/签名认证 | 无/Code认证/签名认证 |
| SQL类型 | 查询SQL | 查询/插入/更新/删除等SQL |
| 结果类型 | json | json |
| 接口日志 | 支持 | 支持 |
| 数据转换 | 不支持 | 支持 |
| 嵌套结构 | 不支持 | 支持 |
| 接口市场 | 不支持 | 支持 |
| 申请审批 | 不支持 | 支持 |
| 接口授权 | 不支持 | 支持 |
| 系统权限 | 不支持 | 支持 |
| 用户管理 | 不支持 | 支持 |
| 数据脱敏 | 不支持 | 支持 |
| 版本管理 | 不支持 | 支持
| 接口缓存 | 不支持 | 支持 |
| 限流熔断 | 不支持 | 限流和熔断 |
| IP黑白名单 | 不支持 | 支持 |
| 监控报表 | 支持 | 支持 |
| 集群模式 | 不支持 | 支持 |
## 效果截图
### 接口开发
![img.png](doc/sql.png)
#### 接口属性
![img.png](doc/detail.png)
#### 请求参数
#### 返回参数
![img.png](doc/param.png)
#### 在线预览
![img_2.png](doc/img_test.png)
#### 接口列表
![img.png](doc/api.png)
#### 接口列表
![img.png](doc/apis.png)
#### 日志管理
![img.png](doc/logs.png)
#### 数据库类型
![img.png](doc/db.png)
### 限流熔断
![img.png](doc/flow.png)
## 交流群
**进群前请先点【Star】谢谢**
QQ群(请先Star):748993036
商用咨询加微信 (请备注crabc)
![img.png](doc/img.png)
## 产品使用登记
以下为部分接入crabc-api的用户 ,<a href="https://gitee.com/linebyte/crabc/issues/IA98CA" target="_blank"> 点击进行登记</a>
@@ -142,12 +142,32 @@ java -jar crabc-admin.jar --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/cr
| 鹭栖技术信息公司 |
| 循脉医疗科技有限公司 |
| 云上贵州大数据有限公司 |
| 巨元瀚洋有限公司 |
## 交流群
**进群前请先点【Star】谢谢**
QQ群:748993036
商用咨询加微信 (请备注crabc)
![img.png](doc/img.png)
## 企业版和开源版对比
| 功能 | 社区版 | 企业版 |
|--------|------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
| 架构设计 | 单体 | 微服务 |
| 接口创建模式 | SQL脚本 | SQL脚本、图表、接口转发 |
| 动态标签 | 支持 | 支持 |
| 动态路由 | 不支持 | 支持 |
| 数据库 | SQL:mysql/oracle/postgresql/sqlserver <br>TiDB/OpenGauss/Doris | SQL:mysql/oracle/postgresql/sqlserver/tidb<br>/opengauss/doris/oceanbase/dm/gbase <br> NoSQL:es/mongodb <br> TSDB: TDengine |
| 认证方式 | 无/Code认证/签名认证 | 无/Code认证/签名认证 |
| SQL类型 | 查询SQL | 查询/插入/更新/删除等SQL |
| 结果类型 | json | json/Excel |
| 接口日志 | 支持 | 支持 |
| 数据模型 | 不支持 | 支持 |
| 协同开发 | 不支持 | 支持 |
| 结果转换 | 不支持 | 支持 |
| 接口市场 | 不支持 | 支持 |
| 申请审批 | 不支持 | 支持 |
| 接口授权 | 不支持 | 支持 |
| 系统权限 | 不支持 | 支持 |
| 用户管理 | 不支持 | 支持 |
| 数据脱敏 | 不支持 | 支持 |
| 版本管理 | 不支持 | 支持
| 接口缓存 | 不支持 | 支持 |
| 限流熔断 | 不支持 | 支持 |
| IP控制 | 不支持 | 支持 |
| 监控报表 | 支持 | 支持 |
| 集群模式 | 不支持 | 支持 |
@@ -5,16 +5,12 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.crabc</groupId>
<artifactId>crabc-boot</artifactId>
<artifactId>crabc-api</artifactId>
<version>${revision}</version>
</parent>
<artifactId>crabc-admin</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.crabc</groupId>
@@ -3,7 +3,7 @@ server:
port: 9377
spring:
application:
name: crabc-admin
name: crabc-api
main:
banner-mode: off
jackson:
@@ -13,7 +13,7 @@ spring:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/crabc?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
password:
# mybatis配置
mybatis:
# 搜索指定包别名
@@ -25,14 +25,4 @@ mybatis:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
pagehelper:
autoRuntimeDialect: true # 开启多数据源分页识别
# es健康检查
management:
health:
elasticsearch:
enabled: false
# 签名认证过期时间
crabc:
auth:
expiresTime: 10
autoRuntimeDialect: true # 开启多数据源分页识别
@@ -3,19 +3,13 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>crabc-boot</artifactId>
<artifactId>crabc-api</artifactId>
<groupId>cn.crabc</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>crabc-core</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -8,9 +8,11 @@ import cn.crabc.core.app.util.ApiThreadLocal;
import cn.crabc.core.app.util.Result;
import cn.crabc.core.datasource.constant.BaseConstant;
import cn.crabc.core.datasource.enums.ErrorStatusEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -34,14 +36,16 @@ public class ApiServiceController {
* @return
*/
@RequestMapping(value = "/**", method = {RequestMethod.GET, RequestMethod.DELETE})
public Result getService(@RequestParam(required = false) Map<String, Object> paramMap) {
public Result getService(@RequestParam(required = false) Map<String, Object> paramMap, HttpServletResponse response) {
ApiInfoDTO api = ApiThreadLocal.get();
if (api == null) {
response.setStatus(400);
return Result.error(ErrorStatusEnum.API_INVALID.getCode(), ErrorStatusEnum.API_INVALID.getMassage());
}
// 参数校验
if (!validateParams(api, paramMap)) {
response.setStatus(400);
return Result.error(ErrorStatusEnum.PARAM_NOT_FOUNT.getCode(), ErrorStatusEnum.PARAM_NOT_FOUNT.getMassage());
}
@@ -56,9 +60,10 @@ public class ApiServiceController {
* @return
*/
@RequestMapping(value = "/**", method = {RequestMethod.POST, RequestMethod.PUT})
public Result postService(@RequestParam(required = false) Map<String, Object> paramMap, @RequestBody(required = false) Object body) {
public Result postService(@RequestParam(required = false) Map<String, Object> paramMap, @RequestBody(required = false) Object body, HttpServletResponse response) {
ApiInfoDTO api = ApiThreadLocal.get();
if (api == null) {
response.setStatus(400);
return Result.error(ErrorStatusEnum.API_INVALID.getCode(), ErrorStatusEnum.API_INVALID.getMassage());
}
@@ -67,6 +72,7 @@ public class ApiServiceController {
// 参数校验
if (!checkParams(api.getRequestParams(), mergedParams)) {
response.setStatus(400);
return Result.error(ErrorStatusEnum.PARAM_NOT_FOUNT.getCode(), ErrorStatusEnum.PARAM_NOT_FOUNT.getMassage());
}
@@ -92,7 +98,7 @@ public class ApiServiceController {
Object value = paramMap.get(paramName);
// 必填参数校验
if (param.getRequired() && !paramMap.containsKey(paramName)) {
if ("Y".equals(param.getRequired()) && !paramMap.containsKey(paramName)) {
return false;
}
@@ -140,7 +146,7 @@ public class ApiServiceController {
* 执行API调用
*/
private Result executeApi(ApiInfoDTO api, Map<String, Object> params) {
Object data = baseDataService.execute(api.getDatasourceId(), api.getDatasourceType(),
Object data = baseDataService.execute(api.getDatasourceId().toString(), api.getDatasourceType(),
api.getSchemaName(), api.getSqlScript(), params);
if (ResultTypeEnum.ONE.getName().equals(api.getResultType()) && data instanceof List) {
@@ -0,0 +1,34 @@
package cn.crabc.core.app.config;
import cn.crabc.core.app.service.system.IBaseApiInfoService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 加载数据
*/
@Component
public class LoadingData implements InitializingBean {
@Autowired
private IBaseApiInfoService iBaseApiInfoService;
/**
* 启动加载
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
iBaseApiInfoService.initApi();
}
/**
* 定时加载
*/
@Scheduled(cron = "${crabc.corn.api:0 0/5 * * * ?}") // 每5分钟全量加载一次
public void task() {
iBaseApiInfoService.initApi();
}
}
@@ -74,11 +74,7 @@ public class ApiTestController {
);
// 处理返回结果
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("code", 0);
resultMap.put("data", formatResultData(data, params.getResultType()));
return Result.success(resultMap);
return Result.success(formatResultData(data, params.getResultType()));
} catch (Exception e) {
return Result.error("测试异常,请检查参数或者SQL是否正常!");
@@ -105,6 +101,7 @@ public class ApiTestController {
} else {
throw new IllegalArgumentException("参数校验失败");
}
paramsMap.put("pageSetup", params.getPageSetup());
} else if (requestParams instanceof List) {
List<Map<String,Object>> paramsList = (List<Map<String, Object>>) requestParams;
for (Map<String, Object> entry : paramsList) {
@@ -114,6 +111,9 @@ public class ApiTestController {
throw new IllegalArgumentException("参数校验失败");
}
}
if (paramsList.isEmpty()) {
paramsMap.put("pageSetup", params.getPageSetup());
}
}
return paramsMap;
}
@@ -4,6 +4,8 @@ import cn.crabc.core.app.entity.BaseUser;
import cn.crabc.core.app.entity.param.UserParam;
import cn.crabc.core.app.service.system.IBaseUserService;
import cn.crabc.core.app.util.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.BeanUtils;
@@ -13,6 +15,7 @@ import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -26,7 +29,8 @@ public class SysUserController {
@Autowired
@Qualifier("dataCache")
Cache<String, Object> caffeineCache;
@Autowired
private ObjectMapper objectMapper;
/**
* 登录
*
@@ -75,6 +79,21 @@ public class SysUserController {
BaseUser userInfo = iBaseUserService.getUserById(Long.parseLong(UserThreadLocal.getUserId()));
return Result.success(userInfo);
}
/**
* 用户菜单
* @return
*/
@GetMapping("/menus")
public Result userMenus(){
// 固定菜单
String menus ="[{\"name\":\"API Hub\",\"checked\":null,\"id\":2,\"parentId\":1,\"open\":null,\"path\":null,\"url\":\"/hub\",\"sort\":2,\"menuType\":1,\"code\":\"Home\",\"hidden\":false,\"permission\":\"/api/box/sys/user/info;/api/box/sys/user/menus;/api/box/sys/message/**;/api/box/sys/user/update/pwd\",\"subMenus\":[{\"name\":\"接口详情\",\"checked\":null,\"id\":7,\"parentId\":2,\"open\":null,\"path\":null,\"url\":\"/doc\",\"sort\":6,\"menuType\":1,\"code\":\"DataCatelogApiDetails\",\"hidden\":true,\"permission\":\"doc\",\"subMenus\":[]}]},{\"name\":\"工作台\",\"checked\":null,\"id\":3,\"parentId\":1,\"open\":null,\"path\":null,\"url\":\"/working\",\"sort\":3,\"menuType\":1,\"code\":\"DataService\",\"hidden\":false,\"permission\":\"/api/box/sys/common/**;/api/box/sys/api/info/**;/api/box/sys/metadata/**;/api/box/sys/test/**;/api/box/sys/group/**;/api/box/sys/user/check/name\",\"subMenus\":[]},{\"name\":\"服务台\",\"checked\":null,\"id\":4,\"parentId\":1,\"open\":null,\"path\":null,\"url\":\"/manage\",\"sort\":4,\"menuType\":1,\"code\":\"Manage\",\"hidden\":false,\"permission\":\"manage\",\"subMenus\":[{\"name\":\"应用管理\",\"checked\":null,\"id\":8,\"parentId\":4,\"open\":null,\"path\":null,\"url\":\"/manage/keys\",\"sort\":1,\"menuType\":1,\"code\":\"Application\",\"hidden\":true,\"permission\":\"/api/box/sys/app/**\",\"subMenus\":[]},{\"name\":\"接口列表\",\"checked\":null,\"id\":9,\"parentId\":4,\"open\":null,\"path\":null,\"url\":\"/manage/apis\",\"sort\":1,\"menuType\":1,\"code\":\"MyApi\",\"hidden\":true,\"permission\":\"/api/box/sys/api/info/**\",\"subMenus\":[]},{\"name\":\"日志管理\",\"checked\":null,\"id\":12,\"parentId\":4,\"open\":null,\"path\":null,\"url\":\"/manage/logs\",\"sort\":1,\"menuType\":1,\"code\":\"UseLog\",\"hidden\":true,\"permission\":\"/api/box/sys/api/log/**\",\"subMenus\":[{\"name\":\"日志列表\",\"checked\":null,\"id\":13,\"parentId\":12,\"open\":null,\"path\":null,\"url\":\"/manage/logs/list\",\"sort\":1,\"menuType\":1,\"code\":\"UseLogList\",\"hidden\":true,\"permission\":\"/api/box/sys/api/log/**\",\"subMenus\":[]},{\"name\":\"日志详情\",\"checked\":null,\"id\":14,\"parentId\":12,\"open\":null,\"path\":null,\"url\":\"/manage/logs/detail\",\"sort\":1,\"menuType\":1,\"code\":\"DetailUesLog\",\"hidden\":true,\"permission\":null,\"subMenus\":[]}]},{\"name\":\"分类管理\",\"checked\":null,\"id\":30,\"parentId\":4,\"open\":null,\"path\":null,\"url\":\"/manage/classification\",\"sort\":1,\"menuType\":1,\"code\":\"Classification\",\"hidden\":true,\"permission\":\"manage:classification\",\"subMenus\":[]},{\"name\":\"数据源\",\"checked\":null,\"id\":31,\"parentId\":4,\"open\":null,\"path\":null,\"url\":\"/manage/datasource\",\"sort\":1,\"menuType\":1,\"code\":\"DataSource\",\"hidden\":true,\"permission\":\"/api/box/sys/datasource/**;/api/box/sys/user/pubKey\",\"subMenus\":[]},{\"name\":\"密码重置\",\"checked\":null,\"id\":32,\"parentId\":4,\"open\":null,\"path\":null,\"url\":\"/manage/edit-pwd\",\"sort\":1,\"menuType\":1,\"code\":\"ChangePassword\",\"hidden\":true,\"permission\":\"/api/box/sys/user/update/pwd\",\"subMenus\":[]}]}]";
try {
List<Map<String, Object>> list = objectMapper.readValue(menus, List.class);
return Result.success(list);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 修改密码
@@ -93,7 +93,7 @@ public class BaseApiInfo extends BaseEntity {
/**
* 数据源Id
*/
private String datasourceId;
private Integer datasourceId;
/**
* 数据源名称
@@ -120,4 +120,7 @@ public class BaseApiInfo extends BaseEntity {
*/
private Long parentId;
/** 分组名称 */
private String groupName;
}
@@ -40,7 +40,7 @@ public class BaseApiParam {
/**
* 是否必填
*/
private Boolean required;
private String required;
/**
* 操作
@@ -70,7 +70,7 @@ public class BaseApiParam {
/**
* 数据源Id
*/
private String datasourceId;
private Integer datasourceId;
/**
* schema
@@ -33,7 +33,7 @@ public class BaseApiSql extends BaseEntity {
/**
* 数据源Id
*/
private String datasourceId;
private Integer datasourceId;
/**
* schema
@@ -1,10 +1,8 @@
package cn.crabc.core.app.entity;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
@@ -16,7 +14,6 @@ import java.util.Date;
@Setter
@Getter
public class BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
@@ -17,6 +17,8 @@ public class ApiTestParam {
private String sqlParams;
private Integer pageSetup;
private String resultType;
private Object requestParams;
@@ -3,7 +3,7 @@ package cn.crabc.core.app.exception;
import cn.crabc.core.app.util.Result;
import cn.crabc.core.datasource.exception.CustomException;
import cn.crabc.core.spi.PluginException;
import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
@@ -79,6 +79,7 @@ public class ExceptionHandler {
@org.springframework.web.bind.annotation.ExceptionHandler(value = HttpMessageNotReadableException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, HttpMessageNotReadableException e) {
e.printStackTrace();
return Result.error(50002, "Body参数异常,请检查Body/json是否正确传参");
}
}
@@ -0,0 +1,47 @@
package cn.crabc.core.app.filter;
import cn.crabc.core.app.util.ApiThreadLocal;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.util.ContentCachingResponseWrapper;
import java.io.IOException;
public class ApiFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
ContentCachingResponseWrapper responseWrapper = null;
try {
// 包装类
if (request.getContentType() == null || request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
BaseRequestWrapper requestWrapper = new BaseRequestWrapper(request);
responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(requestWrapper, responseWrapper);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}finally {
ApiThreadLocal.remove();
if (responseWrapper != null) {
responseWrapper.copyBodyToResponse();
}
}
}
@Override
public void destroy() {
ApiThreadLocal.remove();
Filter.super.destroy();
}
}
@@ -0,0 +1,243 @@
package cn.crabc.core.app.filter;
import cn.crabc.core.app.entity.BaseApiLog;
import cn.crabc.core.app.entity.BaseApp;
import cn.crabc.core.app.entity.dto.ApiInfoDTO;
import cn.crabc.core.app.service.system.IBaseApiLogService;
import cn.crabc.core.app.util.ApiThreadLocal;
import cn.crabc.core.app.util.RequestUtils;
import cn.crabc.core.app.util.Result;
import cn.crabc.core.app.util.SM3Util;
import cn.crabc.core.datasource.enums.ErrorStatusEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.util.ContentCachingResponseWrapper;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* API开放接口鉴权过滤 拦截器
*
* @author yuqf
*/
public class AuthInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(AuthInterceptor.class);
// API开放接口前缀
private static final String API_PRE = "/api/web/";
@Autowired
private IBaseApiLogService iBaseApiLogService;
@Value("${crabc.auth.expiresTime:10}")
private Integer expiresTime;
@Autowired
private ObjectMapper objectMapper;
@Autowired
@Qualifier("apiCache")
private Cache<String, Object> apiCache;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String path = request.getRequestURI();
String method = request.getMethod();
Object apiData = apiCache.getIfPresent(method + "_" + path.replace(API_PRE, ""));
if (apiData == null) {
setErrorResponse(request, response,ErrorStatusEnum.API_INVALID.getCode(),ErrorStatusEnum.API_INVALID.getMassage());
return false;
}
ApiInfoDTO apiInfo = (ApiInfoDTO) apiData;
if (apiInfo.getEnabled() == 0) {
setErrorResponse(request, response,ErrorStatusEnum.API_OFFLINE.getCode(),ErrorStatusEnum.API_OFFLINE.getMassage());
return false;
}
// 存入当前时间,当作是日志的请求时间
apiInfo.setRequestDate(new Date());
apiInfo.setRequestTime(System.currentTimeMillis());
// 放入上下文
ApiThreadLocal.set(apiInfo);
try {
// 应用列表
List<BaseApp> appList = apiInfo.getAppList();
switch (apiInfo.getAuthType().toUpperCase()) {
case "APP_CODE":
return checkAppCode(request, response, appList);
case "APP_KEY":
return checkAppKey(request, response, appList);
case "APP_SECRET":
return checkSM3(request, response, appList);
default:
return true;
}
}catch (Exception e) {
setErrorResponse(request,response,ErrorStatusEnum.API_UN_AUTH.getCode(),ErrorStatusEnum.API_UN_AUTH.getMassage());
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
addLog(request, response, "");
// 清除上下文
ApiThreadLocal.remove();
}
/**
* 异常返回
* @param response
* @param status
* @param message
* @throws Exception
*/
private void setErrorResponse(HttpServletRequest request, HttpServletResponse response, int status, String message)
throws Exception {
// 指定错误码
response.setStatus(400);
response.setContentType("application/json;charset=UTF-8");
Result result = Result.error(status, message);
String json = objectMapper.writeValueAsString(result);
// 日记记录
addLog(request, response, json);
response.getWriter().write(json);
}
/**
* 记录访问日志
*
* @param request
* @param response
*/
private void addLog(HttpServletRequest request, HttpServletResponse response, String msg) throws Exception {
ContentCachingResponseWrapper responseWrapper = null;
BaseApiLog apiLog = new BaseApiLog();
long endTime = System.currentTimeMillis();
ApiInfoDTO apiInfo = ApiThreadLocal.get();
if (apiInfo != null) {
apiLog.setApiId(apiInfo.getApiId());
apiLog.setApiName(apiInfo.getApiName());
apiLog.setApiMethod(apiInfo.getApiMethod());
apiLog.setAuthType(apiInfo.getAuthType());
apiLog.setRequestTime(apiInfo.getRequestDate());
apiLog.setCostTime(endTime - apiInfo.getRequestTime());
}
int status = response.getStatus();
apiLog.setAppName(RequestUtils.getAppKey(request));
apiLog.setApiPath(request.getRequestURI());
apiLog.setRequestIp(RequestUtils.getIp(request));
apiLog.setResponseTime(new Date());
apiLog.setQueryParam(request.getQueryString());
apiLog.setResponseCode(status);
apiLog.setRequestStatus(status == 200 ? "success" : "fail");
try {
if (request instanceof BaseRequestWrapper) {
String requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
apiLog.setRequestBody(requestBody);
}
if (response instanceof ContentCachingResponseWrapper) {
responseWrapper = (ContentCachingResponseWrapper) response;
byte[] content = responseWrapper.getContentAsByteArray();
String responseBody = new String(content, StandardCharsets.UTF_8);
apiLog.setResponseBody(responseBody);
}
if (status == 400) {
apiLog.setResponseBody(msg);
}
iBaseApiLogService.addLog(apiLog);
} catch (Exception e) {
log.error("响应结果转换异常", e);
} finally {
if (responseWrapper != null) {
responseWrapper.copyBodyToResponse();
}
}
}
/**
* 验证接口访问权限APP_CODE
*
* @param request
* @param appList
* @return
*/
private boolean checkAppCode(HttpServletRequest request,HttpServletResponse response, List<BaseApp> appList) throws Exception {
String appCode = RequestUtils.getAppCode(request);
if (appCode == null || appCode.isEmpty()) {
setErrorResponse(request,response,ErrorStatusEnum.API_UN_AUTH.getCode(),ErrorStatusEnum.API_UN_AUTH.getMassage());
return false;
}
boolean check = appList.stream().anyMatch(app -> app.getAppCode().equals(appCode));
if (!check) {
setErrorResponse(request,response,ErrorStatusEnum.API_UN_AUTH.getCode(),ErrorStatusEnum.API_UN_AUTH.getMassage());
}
return check;
}
/**
* 验证接口访问权限APP_KEY
*
* @param request
* @param appList
* @return
*/
private boolean checkAppKey(HttpServletRequest request, HttpServletResponse response, List<BaseApp> appList) throws Exception {
String appKey = RequestUtils.getAppKey(request);
if (appKey == null || appKey.isEmpty()) {
setErrorResponse(request,response,ErrorStatusEnum.SHA_APPKEY_NOT_FOUNT.getCode(),ErrorStatusEnum.SHA_APPKEY_NOT_FOUNT.getMassage());
return false;
}
boolean check = appList.stream().anyMatch(app -> app.getAppKey().equals(appKey));
if (!check) {
setErrorResponse(request,response,ErrorStatusEnum.APP_UN_AUTH.getCode(),ErrorStatusEnum.APP_UN_AUTH.getMassage());
}
return check;
}
/**
* 国密签名认证
*
* @param request
* @param appList
* @return
* @throws Exception
*/
public boolean checkSM3(HttpServletRequest request, HttpServletResponse response, List<BaseApp> appList) throws Exception {
// 认证参数
String sign = Optional.ofNullable(request.getHeader("sign")).orElse(request.getParameter("sign"));
String timeStamp = Optional.ofNullable(request.getHeader("timestamp")).orElse(request.getParameter("timestamp"));
String appKey = Optional.ofNullable(request.getHeader("appkey")).orElse(request.getParameter("appkey"));
if (appKey == null || sign == null || timeStamp == null) {
setErrorResponse(request,response,ErrorStatusEnum.SHA_PARAM_NOT_FOUNT.getCode(), ErrorStatusEnum.PARAM_NOT_FOUNT.getMassage());
return false;
}
// 校验时间戳,超过10分钟失效
long authTime = Long.parseLong(timeStamp);
long nowTime = System.currentTimeMillis() - authTime;
if (nowTime > expiresTime * 60 * 1000) {
setErrorResponse(request,response,ErrorStatusEnum.SHA_TIMESTAMP_EXPIRE.getCode(), ErrorStatusEnum.SHA_TIMESTAMP_EXPIRE.getMassage());
return false;
}
String appSecret = appList.stream()
.filter(app -> app.getAppKey().equals(appKey))
.map(BaseApp::getAppSecret)
.findFirst()
.orElse("");
boolean verify = SM3Util.verify(appSecret + timeStamp, sign);
if (!verify) {
setErrorResponse(request,response,ErrorStatusEnum.API_UN_AUTH.getCode(),ErrorStatusEnum.API_UN_AUTH.getMassage());
}
return verify;
}
}
@@ -1,9 +1,9 @@
package cn.crabc.core.app.filter;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.tomcat.util.http.fileupload.IOUtils;
@@ -5,8 +5,8 @@ import cn.crabc.core.app.util.UserThreadLocal;
import cn.crabc.core.datasource.enums.ErrorStatusEnum;
import cn.crabc.core.datasource.exception.CustomException;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
@@ -18,9 +18,6 @@ import org.springframework.web.servlet.HandlerInterceptor;
*/
public class JwtInterceptor implements HandlerInterceptor {
@Value("${crabc.token.expireTime:36000}")
private long expireTime;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 放行OPTIONS请求
@@ -39,14 +36,6 @@ public class JwtInterceptor implements HandlerInterceptor {
if (claims == null) {
throw new CustomException(ErrorStatusEnum.JWT_LOGIN_EXPIRE.getCode(), ErrorStatusEnum.JWT_UN_AUTH.getMassage());
}
// 校验token是否过期
long expireTime = Long.parseLong(claims.get("expireTime").toString());
long currentTime = System.currentTimeMillis();
if ((currentTime - expireTime) / 1000 > this.expireTime) {
throw new CustomException(ErrorStatusEnum.JWT_LOGIN_EXPIRE.getCode(), ErrorStatusEnum.JWT_LOGIN_EXPIRE.getMassage());
}
// 设置用户信息到线程上下文
UserThreadLocal.set(claims);
return true;
@@ -50,12 +50,7 @@ public class BaseApiInfoServiceImpl implements IBaseApiInfoService {
@Autowired
@Qualifier("apiCache")
Cache<String, Object> apiInfoCache;
@Scheduled(cron = "${crabc.corn.api:0 0/1 * * * ?}")
public void task() {
initApi();
}
@Override
public void initApi() {
updateCache(null);
@@ -71,7 +66,7 @@ public class BaseApiInfoServiceImpl implements IBaseApiInfoService {
@Override
public List<ApiInfoDTO> getApiCache(Long apiId) {
List<ApiInfoDTO> apiInfos = apiInfoMapper.selectApiDetail(apiId);
if (apiInfos.size() == 0) {
if (apiInfos.isEmpty()) {
return apiInfos;
}
// 应用
@@ -62,7 +62,7 @@ public class BaseApiParamServiceImpl implements IBaseApiParamService {
responseCode.setParamDesc("状态码:0 成功");
responseCode.setParamType("Int");
responseCode.setExample("0");
responseCode.setRequired("1");
responseCode.setRequired("Y");
list.add(responseCode);
RequestParamsVO responseMsg = new RequestParamsVO();
@@ -71,7 +71,7 @@ public class BaseApiParamServiceImpl implements IBaseApiParamService {
responseMsg.setParamType("String");
responseMsg.setExample("");
responseMsg.setChildren(null);
responseCode.setRequired("1");
responseCode.setRequired("Y");
list.add(responseMsg);
RequestParamsVO responseData = new RequestParamsVO();
@@ -79,7 +79,7 @@ public class BaseApiParamServiceImpl implements IBaseApiParamService {
responseData.setParamDesc("数据");
responseData.setParamType("Array");
responseData.setExample("");
responseCode.setRequired("1");
responseCode.setRequired("Y");
responseData.setChildren(responseParams);
list.add(responseData);
return list;
@@ -11,15 +11,14 @@ import cn.crabc.core.datasource.driver.DataSourceManager;
import cn.crabc.core.spi.bean.BaseDataSource;
import com.github.pagehelper.PageHelper;
import com.zaxxer.hikari.HikariDataSource;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.Base64;
import java.util.Date;
@@ -61,7 +60,7 @@ public class BaseDataSourceServiceImpl implements IBaseDataSourceService {
public void init() {
List<BaseDataSource> baseDataSources = this.getList();
for (BaseDataSource dataSource : baseDataSources) {
DataSource ds = DataSourceManager.DATA_SOURCE_POOL_JDBC.get(dataSource.getDatasourceId());
DataSource ds = DataSourceManager.DATA_SOURCE_POOL_JDBC.get(dataSource.getDatasourceId().toString());
try {
String jdbcUrl = null;
String username = null;
@@ -113,6 +112,7 @@ public class BaseDataSourceServiceImpl implements IBaseDataSourceService {
dataSource.setClassify("jdbc");
dataSource.setCreateBy(UserThreadLocal.getUserId());
dataSource.setCreateTime(new Date());
dataSource.setUpdateTime(new Date());
// 判断是否是自定义
if ("custom".equals(dataSource.getDatasourceType())){
String dataSourceType = SQLUtil.getDataSourceType(dataSource.getJdbcUrl());
@@ -176,7 +176,7 @@ public class BaseDataSourceServiceImpl implements IBaseDataSourceService {
byte[] decode = Base64.getDecoder().decode(password);
pwd = new String(decode);
}else if (dataSource.getDatasourceId() != null){
BaseDatasource baseDatasource = dataSourceMapper.selectOne(Integer.parseInt(dataSource.getDatasourceId()));
BaseDatasource baseDatasource = dataSourceMapper.selectOne(dataSource.getDatasourceId());
byte[] decode = Base64.getDecoder().decode(baseDatasource.getPassword());
pwd = new String(decode);
}
@@ -1,6 +1,9 @@
package cn.crabc.core.app.util;
import cn.crabc.core.app.entity.dto.ApiInfoDTO;
import cn.crabc.core.app.filter.AuthInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 上下文 当前用户API信息
@@ -8,7 +11,7 @@ import cn.crabc.core.app.entity.dto.ApiInfoDTO;
* @author yuqf
*/
public class ApiThreadLocal {
private static final Logger log = LoggerFactory.getLogger(ApiThreadLocal.class);
private static ThreadLocal<ApiInfoDTO> apiInfo =new ThreadLocal<>();
/**
@@ -16,6 +19,7 @@ public class ApiThreadLocal {
* @param api
*/
public static void set(ApiInfoDTO api){
log.info("--> 设置上下文API信息,{}",api.getApiPath());
apiInfo.set(api);
}
@@ -37,6 +41,7 @@ public class ApiThreadLocal {
* 清除
*/
public static void remove(){
log.info("<-- 清除上下文API信息");
apiInfo.remove();
}
}
@@ -3,8 +3,9 @@ package cn.crabc.core.app.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -17,8 +18,9 @@ import java.util.UUID;
public class JwtUtil {
private static String header = "Authorization";
// 令牌秘钥
private static String secret = "crabcjfakdjfaldjflkadjlafjaldkjlaflkalk";
private static String secret = "KLSDJIWKD27JKZDFJALKJKD82KM90DL1NWMD";
public static final String TOKEN_PREFIX = "bearer ";
public static long expirationTime = 1000L * 60 * 60 * 8; // 8小时过期
/**
* 创建令牌
@@ -27,7 +29,6 @@ public class JwtUtil {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("userName", userName);
claims.put("expireTime", System.currentTimeMillis());
return createToken(claims);
}
@@ -36,6 +37,8 @@ public class JwtUtil {
*/
public static String createToken(Map<String, Object> claims) {
String uuid = UUID.randomUUID().toString();
Date now = new Date();
Date expiration = new Date(now.getTime() + expirationTime);
try {
return Jwts.builder()
.header()
@@ -43,7 +46,7 @@ public class JwtUtil {
.add("alg", "HS256")
.and()
.claims(claims)
// 令牌ID
.expiration(expiration)
.id(uuid)
.signWith(Keys.hmacShaKeyFor(secret.getBytes()))
.compact();
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.HashMap;
@@ -143,6 +143,9 @@ public class RequestUtils {
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if ("0:0:0:0:0:0:0:1".equals(ip) || "localhost".equals(ip)) {
ip ="127.0.0.1";
}
return ip == null ? "" : ip.trim();
}
@@ -1,10 +1,10 @@
package cn.crabc.core.app.util;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.HexFormat;
/**
* 国密SM3加密工具类
@@ -36,7 +36,7 @@ public class SM3Util {
digest.update(bytes, 0, bytes.length);
byte[] result = new byte[digest.getDigestSize()];
digest.doFinal(result, 0);
return HexFormat.of().formatHex(result);
return new String(Hex.encodeHex(result));
}
/**
@@ -330,10 +330,10 @@ public class SQLUtil {
if (reqParams instanceof Map) {
Map<String, Object> paramMap = (Map<String, Object>) reqParams;
for (BaseApiParam param : paramsConfig) {
if (param.getRequired() && !paramMap.containsKey(param.getParamName())) {
if ("Y".equals(param.getRequired()) && !paramMap.containsKey(param.getParamName())) {
// 必传参数没有传,则报错提示
throw new CustomException(52005, "请求参数缺少!");
} else if (!param.getRequired() && !paramMap.containsKey(param.getParamName())) {
} else if ("N".equals(param.getRequired()) && !paramMap.containsKey(param.getParamName())) {
// 非比传参数,去掉SQL中相应的条件
sql = regexSql(sql, param.getParamName());
}
@@ -24,11 +24,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
i.update_time,
i.release_time,
i.show_sql_script,
d.datasource_name
g.group_name
FROM
base_api_info i
left join base_datasource d
on i.datasource_id = d.datasource_id
left join base_group g
on i.group_id = g.group_id
<where>
<if test="apiName !=null and apiName !=''">
(i.api_name like concat('%',#{apiName},'%')
@@ -69,8 +69,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
base_app_api aa on
ai.api_id = aa.api_id
WHERE
ai.enabled = 1
AND ai.api_status = 'release'
ai.api_status = 'release'
<if test="appId != null">
and aa.app_id = #{appId}
</if>
@@ -194,7 +193,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="pageSetup != null">page_setup,</if>
enabled,
create_by,
create_time
create_time,
update_time
)values(
<if test="apiName != null and apiName != ''">#{apiName},</if>
<if test="apiPath != null and apiPath != ''">#{apiPath},</if>
@@ -217,7 +217,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="pageSetup != null">#{pageSetup},</if>
#{enabled},
#{createBy},
#{createTime}
#{createTime},
#{updateTime}
)
</insert>
@@ -263,7 +264,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</update>
<delete id="deleteApiInfo">
DELETE FROM base_api_info where api_status ='edit' and api_id =#{apiId} and create_by =#{userId}
DELETE FROM base_api_info where api_id =#{apiId} and create_by =#{userId}
</delete>
<select id="countApi" resultType="java.lang.Integer">
@@ -33,8 +33,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and request_status =#{result}
</if>
<if test="startTime !='' and endTime !=''">
and request_time &gt;=#{startTime}
and request_time &lt;=#{endTime}
and DATE_FORMAT(request_time,'%Y-%m-%d') &gt;=#{startTime}
and DATE_FORMAT(request_time,'%Y-%m-%d') &lt;=#{endTime}
</if>
</where>
order by log_id desc
@@ -22,6 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectList" resultType="cn.crabc.core.app.entity.BaseApiParam">
SELECT
param_id,
api_id,
param_name,
column_name,
@@ -16,7 +16,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
password,
remarks,
extend,
secret_key
secret_key,
min_idle,
max_active,
connect_timeout,
idle_timeout,
max_lifetime,
keepalive_time
FROM
base_datasource ORDER BY datasource_id DESC
</select>
@@ -33,6 +39,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
'******' as password,
remarks,
extend,
min_idle,
max_active,
connect_timeout,
idle_timeout,
max_lifetime,
keepalive_time,
update_time
FROM
base_datasource
@@ -56,6 +68,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
username,
password,
extend,
min_idle,
max_active,
connect_timeout,
idle_timeout,
max_lifetime,
keepalive_time,
secret_key
FROM
base_datasource WHERE datasource_id=#{datasourceId}
@@ -79,7 +97,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remarks != null and remarks != ''">remarks,</if>
<if test="secretKey != null and secretKey != ''">secret_key,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
create_time
<if test="minIdle != null">min_idle,</if>
<if test="maxActive != null">max_active,</if>
<if test="connectTimeout != null">connect_timeout,</if>
<if test="idleTimeout != null">idle_timeout,</if>
<if test="maxLifetime != null">max_lifetime,</if>
<if test="keepaliveTime != null">keepalive_time,</if>
create_time,
update_time
)values(
<if test="datasourceId != null and datasourceId != ''">#{datasourceId},</if>
<if test="datasourceName != null and datasourceName != ''">#{datasourceName},</if>
@@ -93,7 +118,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remarks != null and remarks != ''">#{remarks},</if>
<if test="secretKey != null and secretKey != ''">#{secretKey},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
#{createTime}
<if test="minIdle != null">#{minIdle},</if>
<if test="maxActive != null">#{maxActive},</if>
<if test="connectTimeout != null">#{connectTimeout},</if>
<if test="idleTimeout != null">#{idleTimeout},</if>
<if test="maxLifetime != null">#{maxLifetime},</if>
<if test="keepaliveTime != null">#{keepaliveTime},</if>
#{createTime},
#{updateTime}
)
</insert>
@@ -110,6 +142,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="classify != null and classify != ''">classify = #{classify},</if>
<if test="secretKey != null and secretKey != ''">secret_key = #{secretKey},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="minIdle != null">min_idle=#{minIdle},</if>
<if test="maxActive != null">max_active=#{maxActive},</if>
<if test="connectTimeout != null">connect_timeout=#{connectTimeout},</if>
<if test="idleTimeout != null">idle_timeout=#{idleTimeout},</if>
<if test="maxLifetime != null">max_lifetime=#{maxLifetime},</if>
<if test="keepaliveTime != null">keepalive_time=#{keepaliveTime},</if>
update_time =#{updateTime},remarks = #{remarks}
</set>
WHERE datasource_id = #{datasourceId}
Loaded 100 of 250 files, more files were not shown because too many files have changed in this diff. Show more