第一次提交

This commit is contained in:
尹舟 2025-03-04 10:08:30 +08:00
commit 0f6436e8fe
15 changed files with 534 additions and 0 deletions

11
docker-compose.yaml Normal file
View File

@ -0,0 +1,11 @@
version: '3.4'
services:
sql-runner:
build:
context: .
dockerfile: Dockerfile
restart: always
container_name: sql-runner
image: sql-runner:latest
ports:
- "8308:8308"

16
dockerfile Normal file
View File

@ -0,0 +1,16 @@
#ARG instanceid="996sdk-yinqing-registry.cn-hangzhou.cr.aliyuncs.com/default/openjdk"
ARG instanceid="registry.cn-hangzhou.aliyuncs.com/yinzhou_docker_hub/openjdk"
FROM ${instanceid}:8-jdk-alpine
# 将 JAR 文件复制到容器中
COPY ./target/sql_run.jar /opt/sdk/sql_run.jar
# 设置工作目录
WORKDIR /opt/sdk/
# 暴露应用程序的端口
EXPOSE 8308
# 运行应用程序
ENTRYPOINT ["java", "-jar", "sql_run.jar"]

78
pom.xml Normal file
View File

@ -0,0 +1,78 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sdk_996</groupId>
<artifactId>sql_run</artifactId>
<version>1.0</version>
<properties>
<java.version>1.8</java.version> <!-- 使用 Java 8 -->
<spring-boot.version>2.7.18</spring-boot.version> <!-- Spring Boot 2.7.18 -->
<mybatis-plus-boot-starter.version>3.5.9</mybatis-plus-boot-starter.version>
<easy-query.version>2.4.14</easy-query.version>
<postgresql.version>42.6.0</postgresql.version> <!-- Hologres 驱动 -->
<fastjson.version>1.2.58</fastjson.version><!-- 用来包装返回 -->
<lombok.version>1.18.8</lombok.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,13 @@
package sdk_996;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SqlRunApplication {
public static void main(String[] args) {
SpringApplication.run(SqlRunApplication.class, args);
}
}

View File

@ -0,0 +1,93 @@
package sdk_996.bean.cnofig;
import com.alibaba.fastjson.JSONObject;
import org.postgresql.util.PSQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import sdk_996.bean.em.ErrorCode;
import sdk_996.bean.exp.FormatValidationException;
import sdk_996.bean.exp.SqlException;
import sdk_996.bean.vo.ResultVO;
import java.net.SocketTimeoutException;
import java.util.concurrent.CompletionException;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = {FormatValidationException.class})
@ResponseBody
public ResultVO formatValidationException(FormatValidationException ex) {
logger.info("format validation exception: {}", ex);
return ResultVO.error(ErrorCode.PARAM_ERROR);
}
@ExceptionHandler(value = {SqlException.class, CompletionException.class})
@ResponseBody
public ResultVO handleSqlException(SqlException ex) {
Exception cause = ex.getEx();
String key_v = ex.getKey_v();
// 根据具体的异常类型做出响应
if (cause.getCause().getCause() instanceof SocketTimeoutException) {
return handleTimeoutException(key_v);
} else if (cause.getCause() instanceof PSQLException) {
return handleSqlSyntaxException(key_v, cause);
} else {
return handleGenericSqlException(key_v, cause);
}
}
private ResultVO handleTimeoutException(String key_v) {
logger.error("SQL query timeout for key: {}", key_v);
JSONObject errorDetail = createErrorDetail(key_v, ErrorCode.SQL_TIMEOUT.getMessage(), "");
return ResultVO.error_sql(ErrorCode.SQL_TIMEOUT, errorDetail);
}
private ResultVO handleSqlSyntaxException(String sqlKey, Exception cause) {
logger.error("SQL syntax error for key: {}", sqlKey, cause);
JSONObject errorDetail = createErrorDetail(sqlKey, ErrorCode.SQL_ERROR.getMessage(), cause.getMessage());
return ResultVO.error_sql(ErrorCode.SQL_ERROR, errorDetail);
}
private ResultVO handleGenericSqlException(String sqlKey, Exception cause) {
logger.error("SQL execution error for key: {}", sqlKey, cause);
return ResultVO.error(ErrorCode.SQL_EXECUTE_ERROR);
}
private JSONObject createErrorDetail(String sqlKey, String Message, String errorMessage) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("sql_key:sql", sqlKey);
jsonObject.put(Message, errorMessage);
return jsonObject;
}
@ExceptionHandler(value = ArithmeticException.class)
@ResponseBody
public ResultVO handleArithmeticException(ArithmeticException ex) {
logger.error("Arithmetic exception occurred: ", ex);
return ResultVO.error(ErrorCode.CODE_ERROR);
}
@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public ResultVO handleNullPointerException(NullPointerException ex) {
logger.error("NullPointerException occurred: ", ex);
return ResultVO.error(ErrorCode.NULL_POINTER_ERROR);
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultVO handleGeneralException(Exception ex) {
logger.error("General exception occurred: ", ex);
return ResultVO.error(ErrorCode.RUNTIME_EXCEPTION);
}
}

View File

@ -0,0 +1,35 @@
package sdk_996.bean.em;
public enum ErrorCode {
SUCCESS(200, "成功"),
SQL_ERROR(301, "SQL异常,请检查 SQL 语句"),
SQL_TIMEOUT(302, "SQL查询超时异常"),
SQL_EXECUTE_ERROR(303, "SQL执行异常"),
NULL_POINTER_ERROR(304, "空指针异常"),
CODE_ERROR(305, "代码运行异常,请查看报错信息"),
INTERNAL_ERROR(306, "内部异常"),
PARAM_NULL(310, "参数为空"),
PARAM_ERROR(311, "参数有误,检查请求参数后重试."),
DATA_EMPTY(312, "此时间段内无数据"),
RUNTIME_EXCEPTION(500, "服务运行异常");
private final Integer code;
private final String message;
ErrorCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,11 @@
package sdk_996.bean.exp;
public class FormatValidationException extends RuntimeException{
private static final long serialVersionUID = 1L;
private Exception ex;
public FormatValidationException(Exception ex) {
this.ex = ex;
}
}

View File

@ -0,0 +1,18 @@
package sdk_996.bean.exp;
import lombok.Data;
@Data
public class SqlException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String key_v;
private Exception ex;
public SqlException(String key_v, Exception ex) {
this.key_v = key_v;
this.ex = ex;
}
}

View File

@ -0,0 +1,60 @@
package sdk_996.bean.vo;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import sdk_996.bean.em.ErrorCode;
import java.time.LocalDateTime;
@Data
public class ResultVO {
private Integer code;
private Object message;
private Object data;
private LocalDateTime currentTime; // 当前时间字段
public ResultVO() {
this.currentTime = LocalDateTime.now(); // 在构造函数中设置当前时间
this.setCode(200); // 默认成功状态
this.setMessage("成功"); // 默认成功消息
}
// 提供一个静态方法来快速创建成功响应
public static ResultVO success() {
return new ResultVO();
}
// 提供一个静态方法来快速创建带有自定义消息的成功响应
public static ResultVO success(JSONObject data) {
ResultVO resultVO = new ResultVO();
resultVO.setData(data);
return resultVO;
}
// 提供一个静态方法来快速创建带有错误码和消息的响应
public static ResultVO error(ErrorCode errorCode) {
ResultVO resultVO = new ResultVO();
resultVO.setCode(errorCode.getCode());
resultVO.setMessage(errorCode.getMessage());
return resultVO;
}
public static ResultVO error_sql(ErrorCode errorCode, JSONObject msg) {
ResultVO resultVO = new ResultVO();
resultVO.setCode(errorCode.getCode());
resultVO.setMessage(msg);
return resultVO;
}
public static void main(String[] args) {
ResultVO resultVO = ResultVO.error(ErrorCode.SQL_ERROR);
System.out.println(resultVO);
}
@Override
public String toString() {
// 以json格式打印返回
return JSONObject.toJSONString(this);
}
}

View File

@ -0,0 +1,21 @@
package sdk_996.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import sdk_996.bean.vo.ResultVO;
import sdk_996.service.SqlExecuteService;
@RestController
@RequestMapping("/api")
public class SqlExecuteController {
@Autowired
private SqlExecuteService sqlExecuteService;
@PostMapping("/sql-query")
public ResultVO sqlQuery(@RequestBody String sql) {
return ResultVO.success(sqlExecuteService.performCustomQuery(sql));
}
}

View File

@ -0,0 +1,17 @@
package sdk_996.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface SqlExecuteMapper extends BaseMapper<Object> {
@Select("${value}")
List<Map<String, Object>> sqlQuery(String value);
}

View File

@ -0,0 +1,86 @@
package sdk_996.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sdk_996.bean.exp.FormatValidationException;
import sdk_996.mapper.SqlExecuteMapper;
import sdk_996.bean.exp.SqlException;
import com.alibaba.fastjson.JSON;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Service
public class SqlExecuteService {
private static final Logger logger = LoggerFactory.getLogger(SqlExecuteService.class);
@Autowired
private SqlExecuteMapper sqlExecuteMapper;
public JSONObject performCustomQuery(String sqlstr) {
// 返回的json对象
JSONObject returnJsonObject = new JSONObject();
JSONObject sqlJsonObject;
try {
// 将SQL字符串转换为JSON对象
sqlJsonObject = JSONObject.parseObject(sqlstr);
} catch (Exception ex) {
throw new FormatValidationException(ex);
}
// 执行查询操作并处理返回结果
List<JSONObject> jsonObjects = query(sqlJsonObject);
// 遍历jsonObjects将结果加入到返回的jsonObject
for (JSONObject jsonObject : jsonObjects) {
jsonObject.forEach(returnJsonObject::put);
}
return returnJsonObject;
}
private List<JSONObject> query(JSONObject sqlJsonObject) {
// 执行所有 SQL 查询的异步任务
List<CompletableFuture<JSONObject>> futures = sqlJsonObject.keySet().stream()
.map(key -> CompletableFuture.supplyAsync(() -> executeQuery(key, sqlJsonObject.getString(key))))
.collect(Collectors.toList());
// 等待所有任务完成并收集结果
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
private JSONObject executeQuery(String sqlKey, String sqlStr) {
JSONObject result = new JSONObject();
try {
long startTime = System.currentTimeMillis();
// 执行 SQL 查询
List<Map<String, Object>> resultList = sqlExecuteMapper.sqlQuery(sqlStr);
long endTime = System.currentTimeMillis();
logger.info("sql执行时长:{}ms 执行sql:{}", (endTime - startTime), sqlStr);
// 将结果列表转换为 JSONArray
JSONArray jsonArray = JSON.parseArray(JSON.toJSONString(resultList));
// 执行 SQL 查询并转换为 JSON 字符串
result.put(sqlKey, jsonArray);
} catch (Exception ex) {
throw new SqlException(sqlKey + ": " + sqlStr, ex);
}
return result;
}
}

View File

@ -0,0 +1,23 @@
server:
port: 8308
spring:
datasource:
dynamic:
datasource:
master_1:
url: jdbc:postgresql://hgprecn-cn-i7m2ssubq004-cn-hangzhou.hologres.aliyuncs.com:80/sdk_statis_test?socketTimeout=30
username: LTAI5tNSJmvVjrS1cL1YSnU3
password: zAhsbbqXftQwOKbNs966rW4b7XBVgl
driver-class-name: org.postgresql.Driver
hikari:
minimum-idle: 10
maximum-pool-size: 100
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
#logging:
# level:
# com.zaxxer.hikari: DEBUG

View File

@ -0,0 +1,21 @@
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
=`-.____`-.___\_____/___.-`____.-'====
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义日志输出格式 -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{36} - %msg%n"/>
<!-- Console Appender: 将日志输出到控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 设置 root logger 为 INFO 级别,并将日志输出到控制台 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<!-- PostgreSQL 的日志: 设置为 ERROR 级别,避免打印 DEBUG 和 INFO 级别的日志 -->
<logger name="org.postgresql" level="ERROR" />
<!-- MyBatis 的日志: 设置为 INFO 级别,通常设置为 INFO 或 WARN -->
<logger name="org.apache.ibatis" level="INFO" />
<!-- HikariCP 连接池日志: 设置为 ERROR 级别 -->
<logger name="com.zaxxer.hikari" level="ERROR" />
<!-- 你可以针对应用中特定的服务或类设置日志级别 -->
<logger name="sdk_996.service.SqlExecuteService" level="DEBUG"/>
</configuration>