Project reconfiguration

This commit is contained in:
JiaJu Zhuang
2023-06-22 13:33:42 +08:00
parent b7a333f2cb
commit 099a3e17eb
456 changed files with 5 additions and 5 deletions

View File

@ -0,0 +1,117 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-server-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>chat2db-server-start</artifactId>
<packaging>jar</packaging>
<name>chat2db-server-start</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-server-web-api</artifactId>
</dependency>
<!-- 需要加载的服务 -->
<dependency>
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-server-domain-core</artifactId>
</dependency>
<dependency>
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-server-domain-support</artifactId>
</dependency>
<!-- 日志用logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- 启动数据库 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- 数据库版本管理 -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
<!-- 登录鉴权 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
</dependency>
<!-- 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<finalName>chat2db-server-start</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 重点 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
package ai.chat2db.server.start;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* 启动类
*
* @author Jiaju Zhuang
*/
@SpringBootApplication
@ComponentScan(value = {"ai.chat2db.server"})
@MapperScan("ai.chat2db.server.domain.repository.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,146 @@
package ai.chat2db.server.start.config.config;
import java.io.IOException;
import java.util.Enumeration;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ai.chat2db.server.domain.api.model.User;
import ai.chat2db.server.domain.api.service.UserService;
import ai.chat2db.server.tools.base.constant.SymbolConstant;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import ai.chat2db.server.tools.common.exception.RedirectBusinessException;
import ai.chat2db.server.tools.common.model.Context;
import ai.chat2db.server.tools.common.model.LoginUser;
import ai.chat2db.server.tools.common.util.ContextUtils;
import com.alibaba.fastjson2.JSON;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static ai.chat2db.server.tools.common.enums.ErrorEnum.NEED_LOGGED_IN;
/**
* web项目配置
*
* @author 是仪
*/
@Configuration
@Slf4j
public class DbhubWebMvcConfigurer implements WebMvcConfigurer {
/**
* api前缀
*/
private static final String API_PREFIX = "/api/";
/**
* 全局放行的url
*/
private static final String[] FRONT_PERMIT_ALL = new String[] {"/favicon.ico", "/error", "/static/**", "/api/system"};
@Resource
private UserService userService;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 所有请求尝试加入用户信息
registry.addInterceptor(new AsyncHandlerInterceptor() {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
@NotNull Object handler) {
String userIdString = (String)StpUtil.getLoginIdDefaultNull();
// 未登录
if (!StringUtils.isNumeric(userIdString)) {
// TODO 这个版本默认放开登录 不管用户是否登录 都算登录,下个版本做权限
userIdString = "1";
//return true;
}
// 已经登录 查询用户信息
Long userId = Long.parseLong(userIdString);
User user = userService.query(userId).getData();
if (user == null) {
// 代表用户可能被删除了
return true;
}
ContextUtils.setContext(Context.builder()
.loginUser(LoginUser.builder()
.id(user.getId()).nickName(user.getNickName())
.build())
.build());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
// 移除登录信息
ContextUtils.removeContext();
}
})
.order(1)
.addPathPatterns("/**")
.excludePathPatterns(FRONT_PERMIT_ALL);
// 校验登录信息
registry.addInterceptor(new AsyncHandlerInterceptor() {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
@NotNull Object handler) throws IOException {
Context context = ContextUtils.queryContext();
// 校验登录信息
if (context == null) {
log.info("访问{},{}需要登录", buildHeaderString(request), SaHolder.getRequest().getUrl());
String path = SaHolder.getRequest().getRequestPath();
if (path.startsWith(API_PREFIX)) {
response.getWriter().println(JSON.toJSONString(
ActionResult.fail(NEED_LOGGED_IN.getCode(), NEED_LOGGED_IN.getDescription())));
return false;
//throw new NeedLoggedInBusinessException();
} else {
throw new RedirectBusinessException(
"/login-a#/login?callback=" + SaFoxUtil.joinParam(
SpringMVCUtil.getRequest().getRequestURI(),
SpringMVCUtil.getRequest().getQueryString()));
}
}
return true;
}
})
.order(2)
.addPathPatterns("/**")
// 前端需要放行的链接
.excludePathPatterns(FRONT_PERMIT_ALL)
// -a结尾的统一放行
.excludePathPatterns("/**/*-a")
// _a结尾的统一放行
.excludePathPatterns("/**/*_a");
}
private String buildHeaderString(HttpServletRequest request) {
StringBuilder stringBuilder = new StringBuilder();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headName = headerNames.nextElement();
stringBuilder.append(headName);
stringBuilder.append(SymbolConstant.COLON);
stringBuilder.append(request.getHeader(headName));
stringBuilder.append(SymbolConstant.COMMA);
}
return stringBuilder.toString();
}
}

View File

@ -0,0 +1,33 @@
/**
* alibaba.com Inc.
* Copyright (c) 2004-2023 All Rights Reserved.
*/
package ai.chat2db.server.start.config.config;
import javax.annotation.Resource;
import ai.chat2db.server.domain.support.sql.DbhubContext;
import ai.chat2db.server.domain.support.util.JdbcJarUtils;
import ai.chat2db.server.tools.common.config.AliDbhubProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
/**
* @author jipengfei
* @version : JarDownloadTask.java
*/
@Component
@Slf4j
public class JarDownloadTask implements CommandLineRunner {
@Resource
private AliDbhubProperties aliDbhubProperties;
@Override
public void run(String... args) throws Exception {
DbhubContext.JDBC_JAR_DOWNLOAD_URL_LIST = aliDbhubProperties.getJdbcJarDownLoadUrls();
JdbcJarUtils.asyncDownload(aliDbhubProperties.getJdbcJarDownLoadUrls());
}
}

View File

@ -0,0 +1,49 @@
package ai.chat2db.server.start.config.i18n;
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
/**
* @author moji
* @version Internationalization.java, v 0.1 2022年10月10日 16:21 moji Exp $
* @date 2022/10/10
*/
@Configuration
public class InternationalizationConfig implements WebMvcConfigurer {
@Bean
public AcceptHeaderLocaleResolver localeResolver() {
final AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setDefaultLocale(Locale.CHINA);
return resolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
@Bean("messageSource")
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource =
new ResourceBundleMessageSource();
messageSource.setBasenames("i18n/messages/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}

View File

@ -0,0 +1,41 @@
package ai.chat2db.server.start.config.interceptor;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
/**
* Cors跨域的拦截器任何情况都允许跨域
*
* 通过CorsRegistry策略的跨域 在登录的情况下会有有问题但是本地没有复现可能原因是bean 的加载顺序的问题。
* 临时通过CorsFilter解决后续可以研究下CorsRegistry
*
* @author 是仪
*/
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse)res;
HttpServletRequest request = (HttpServletRequest)req;
response.setHeader("Access-Control-Allow-Origin", request.getHeader(HttpHeaders.ORIGIN));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, DBHUB, uid");
chain.doFilter(req, res);
}
}

View File

@ -0,0 +1,28 @@
package ai.chat2db.server.start.config.listener;
import ai.chat2db.server.start.config.util.SystemUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.stereotype.Component;
/**
* 自定义tomcat参数
*
* @author Jiaju Zhuang
*/
@Component
@Slf4j
public class DbhubTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
@Override
public void customize(Connector connector) {
connector.addLifecycleListener(event -> {
// 接受到关闭事件 直接退出系统,因为有时候不会退出系统
if (LifecycleState.STOPPING == event.getLifecycle().getState()) {
SystemUtils.stop();
}
});
}
}

View File

@ -0,0 +1,23 @@
package ai.chat2db.server.start.config.listener;
import ai.chat2db.server.start.config.util.SystemUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;
/**
* 应用启动失败的监听器
* 应用启动失败了只是停止了tomcat 并没有停止应用 这里停止xia
*
* @author Jiaju Zhuang
*/
@Slf4j
public class FailedEventApplicationListener implements ApplicationListener<ApplicationFailedEvent> {
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
log.error("启动失败,停止应用", event.getException());
SystemUtils.stop();
}
}

View File

@ -0,0 +1,126 @@
package ai.chat2db.server.start.config.listener;
import java.time.Duration;
import ai.chat2db.server.tools.base.enums.SystemEnvironmentEnum;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import com.alibaba.fastjson2.JSON;
import com.dtflys.forest.Forest;
import com.dtflys.forest.utils.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.Assert;
/**
* 用来管理启动
* 防止启动多个
*
* @author zhuangjiaju
* @date 2023/05/08
*/
@Slf4j
public class ManageApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
Integer serverPort = event.getEnvironment().getProperty("server.port", Integer.class);
Assert.notNull(serverPort, "server.port配置信息");
log.info("启动端口为:{}", serverPort);
String environment = event.getEnvironment().getProperty("spring.profiles.active", String.class);
// 尝试访问确认应用是否已经启动
DataResult<String> dataResult;
try {
dataResult = Forest.get("http://127.0.0.1:" + serverPort + "/api/system/get-version-a")
.connectTimeout(Duration.ofMillis(50))
.readTimeout(Duration.ofSeconds(1))
.execute(new TypeReference<>() {});
} catch (Exception e) {
// 抛出异常 代表没有旧的启动 或者旧的不靠谱
log.info("尝试访问旧的应用失败。本异常不重要,正常启动启动都会输出,请忽略。"+ e.getMessage());
// 尝试杀死旧的进程
killOldIfNecessary(environment);
return;
}
if (dataResult == null || BooleanUtils.isNotTrue(dataResult.getSuccess())) {
// 尝试杀死旧的进程
killOldIfNecessary(environment);
return;
}
// 代表旧的进程是可以用的
log.info("当前接口已经存在启动的应用了,本应用不在启动");
System.exit(0);
}
private void killOldIfNecessary(String environment) {
ProcessHandle.allProcesses().forEach(process -> {
String command = process.info().command().orElse(null);
// 不是java应用
boolean isJava = StringUtils.endsWithIgnoreCase(command, "java") || StringUtils.endsWithIgnoreCase(command,
"java.exe");
if (!isJava) {
return;
}
String[] arguments = process.info().arguments().orElse(null);
// 没有参数
if (arguments == null) {
return;
}
// 是否是dbhub
boolean isDbhub = false;
String environmentArgument = null;
for (String argument : arguments) {
if (StringUtils.equals("ali-dbhub-server-start.jar", argument)) {
isDbhub = true;
}
if (StringUtils.startsWith(argument, "-Dspring.profiles.active=")) {
environmentArgument = StringUtils.substringAfter(argument, "-Dspring.profiles.active=");
}
}
// 不是dbhub
if (!isDbhub) {
return;
}
// 判断是否是正式环境
if (StringUtils.equals(SystemEnvironmentEnum.RELEASE.getCode(), environment) && StringUtils.equals(
SystemEnvironmentEnum.RELEASE.getCode(), environmentArgument)) {
log.info("正式环境需要关闭进程");
destroyProcess(process, command, arguments);
return;
}
// 判断是否是测试环境
if (StringUtils.equals(SystemEnvironmentEnum.TEST.getCode(), environment) && StringUtils.equals(
SystemEnvironmentEnum.TEST.getCode(), environmentArgument)) {
log.info("测试环境需要关闭进程");
destroyProcess(process, command, arguments);
return;
}
// 判断是否是本地环境
boolean devDestroy = StringUtils.equals(SystemEnvironmentEnum.DEV.getCode(), environment) && (
environmentArgument == null
|| StringUtils.equals(SystemEnvironmentEnum.DEV.getCode(), environmentArgument));
if (devDestroy) {
log.info("本地环境需要关闭进程");
destroyProcess(process, command, arguments);
}
});
}
private void destroyProcess(ProcessHandle process, String command, String[] arguments) {
log.info("检查到存在需要关闭的进程:{},{}", JSON.toJSONString(command), JSON.toJSONString(arguments));
try {
process.destroy();
} catch (Exception e) {
log.error("结束进程失败", e);
}
}
}

View File

@ -0,0 +1,31 @@
package ai.chat2db.server.start.config.listener.manage;
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serial;
import java.io.Serializable;
/**
* 管理的消息
*
* @author Jiaju Zhuang
*/
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class ManageMessage implements Serializable {
@Serial
private static final long serialVersionUID = EasyToolsConstant.SERIAL_VERSION_UID;
/**
* 消息类型
*
* @see MessageTypeEnum
*/
private MessageTypeEnum messageTypeEnum;
}

View File

@ -0,0 +1,32 @@
package ai.chat2db.server.start.config.listener.manage;
import ai.chat2db.server.tools.base.enums.BaseEnum;
import lombok.Getter;
/**
* 消息类型枚举
*
* @author Jiaju Zhuang
*/
@Getter
public enum MessageTypeEnum implements BaseEnum<String> {
/**
* 检查是否正常运行
*/
HEARTBEAT,
;
@Override
public String getCode() {
return this.name();
}
@Override
public String getDescription() {
return this.name();
}
}

View File

@ -0,0 +1,26 @@
package ai.chat2db.server.start.config.mybatis;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author moji
* @version MyBatisPlusConfig.java, v 0.1 2022年09月29日 17:38 moji Exp $
* @date 2022/09/29
*/
@Configuration
public class MyBatisPlusConfig {
/**
* myBatisPlus 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
}

View File

@ -0,0 +1,44 @@
package ai.chat2db.server.start.config.oauth;
import cn.dev33.satoken.log.SaLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* satoken 日志打印
*
* @author 是仪
*/
@Slf4j
@Component
public class SaLogForSlf4j implements SaLog {
@Override
public void trace(String str, Object... args) {
log.trace(str, args);
}
@Override
public void debug(String str, Object... args) {
log.debug(str, args);
}
@Override
public void info(String str, Object... args) {
log.info(str, args);
}
@Override
public void warn(String str, Object... args) {
log.trace(str, args);
}
@Override
public void error(String str, Object... args) {
log.error(str, args);
}
@Override
public void fatal(String str, Object... args) {
log.error(str, args);
}
}

View File

@ -0,0 +1,21 @@
package ai.chat2db.server.start.config.oauth;
import cn.dev33.satoken.jwt.StpLogicJwtForStateless;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* satoken配置
*
* @author 是仪
*/
@Configuration
public class SaTokenConfigure {
@Bean
public StpLogic ttpLogic() {
// 登录展示用 无状态的 这样不用依赖于与redis之类的
// 后续可以改成ehcahe 存储磁盘?
return new StpLogicJwtForStateless();
}
}

View File

@ -0,0 +1,36 @@
package ai.chat2db.server.start.config.util;
import ai.chat2db.server.domain.support.sql.SSHManager;
import lombok.extern.slf4j.Slf4j;
/**
* 系统工具包
*
* @author Jiaju Zhuang
*/
@Slf4j
public class SystemUtils {
/**
* 停止当前应用
*/
public static void stop() {
new Thread(() -> {
log.info("1秒以后退出应用");
// 1秒以后自动退出应用
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 直接系统退出
log.info("开始退出系统应用");
SSHManager.close();
try {
System.exit(0);
} catch (Exception ignore) {
}
}).start();
}
}

View File

@ -0,0 +1,91 @@
package ai.chat2db.server.start.controller.oauth;
import javax.annotation.Resource;
import ai.chat2db.server.domain.api.model.User;
import ai.chat2db.server.domain.api.service.UserService;
import ai.chat2db.server.start.controller.oauth.request.LoginRequest;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.common.model.LoginUser;
import ai.chat2db.server.tools.common.util.ContextUtils;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaTokenConsts;
import cn.hutool.crypto.digest.DigestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 登录授权服务
*
* @author Jiaju Zhuang
*/
@RestController
@RequestMapping("/oauth")
@Slf4j
public class OauthController {
@Resource
private UserService userService;
/**
* 用户名密码登录
*
* @param request
* @return
*/
@PostMapping("login_a")
public DataResult login(@Validated @RequestBody LoginRequest request) {
// 查询用户
User user = userService.query(request.getUserName()).getData();
if (user == null) {
throw new BusinessException("当前用户不存在。");
}
if (!DigestUtil.bcryptCheck(request.getPassword(), user.getPassword())) {
throw new BusinessException("您输入的密码有误。");
}
StpUtil.login(user.getId());
Object token = SaHolder.getStorage().get(SaTokenConsts.JUST_CREATED_NOT_PREFIX);
return DataResult.of(token);
}
/**
* 登出
*
* @return
*/
@PostMapping("logout_a")
public ActionResult logout() {
StpUtil.logout();
return ActionResult.isSuccess();
}
/**
* user
*
* @return
*/
@GetMapping("user")
public DataResult<LoginUser> user() {
return DataResult.of(ContextUtils.getLoginUser());
}
/**
* user
*
* @return
*/
@GetMapping("user_a")
public DataResult<LoginUser> usera() {
return DataResult.of(ContextUtils.queryLoginUser());
}
}

View File

@ -0,0 +1,31 @@
package ai.chat2db.server.start.controller.oauth.request;
import javax.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* 登录
*
* @author Jiaju Zhuang
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class LoginRequest {
/**
* 用户名
*/
@NotNull(message = "用户名不能为空")
private String userName;
/**
* 密码
*/
@NotNull(message = "密码不能为空")
private String password;
}

View File

@ -0,0 +1,35 @@
package ai.chat2db.server.start.controller.thymeleaf;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* 模板引擎 配置
*
* @author Jiaju Zhuang
*/
@Controller
@Slf4j
@Order(Integer.MIN_VALUE)
public class ThymeleafController {
/**
* 前端的模板设置
*
* @return
*/
@GetMapping(value = {"/", "/web/", "/web/**","login-a"})
public String index() {
return "index";
}
@RequestMapping(value = "/chat.html", method={RequestMethod.GET}, produces="text/html;charset=utf-8")
public String chat(){
return "chat";
}
}

View File

@ -0,0 +1,178 @@
package ai.chat2db.server.start.exception;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import ai.chat2db.server.start.exception.convertor.BindExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.BusinessExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.DefaultExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.ExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.MaxUploadSizeExceededExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.MethodArgumentNotValidExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.MethodArgumentTypeMismatchExceptionConvertor;
import ai.chat2db.server.start.exception.convertor.ParamExceptionConvertor;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.base.excption.SystemException;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import ai.chat2db.server.tools.common.exception.NeedLoggedInBusinessException;
import ai.chat2db.server.tools.common.exception.RedirectBusinessException;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.ModelAndView;
/**
* 拦截Controller异常
*
* @author 是仪
*/
@ControllerAdvice
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class EasyControllerExceptionHandler {
/**
* 所有的异常处理转换器
*/
public static final Map<Class<?>, ExceptionConvertor> EXCEPTION_CONVERTOR_MAP = Maps.newHashMap();
static {
EXCEPTION_CONVERTOR_MAP.put(MethodArgumentNotValidException.class,
new MethodArgumentNotValidExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(BindException.class, new BindExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(BusinessException.class, new BusinessExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(NeedLoggedInBusinessException.class, new BusinessExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(MissingServletRequestParameterException.class, new ParamExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(IllegalArgumentException.class, new ParamExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(MethodArgumentTypeMismatchException.class,
new MethodArgumentTypeMismatchExceptionConvertor());
EXCEPTION_CONVERTOR_MAP.put(MaxUploadSizeExceededException.class,
new MaxUploadSizeExceededExceptionConvertor());
}
/**
* 默认转换器
*/
public static ExceptionConvertor DEFAULT_EXCEPTION_CONVERTOR = new DefaultExceptionConvertor();
/**
* 业务异常
*
* @param request request
* @param exception exception
* @return return
*/
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class, IllegalArgumentException.class,
MissingServletRequestParameterException.class, MethodArgumentTypeMismatchException.class,
BusinessException.class, MaxUploadSizeExceededException.class, ClientAbortException.class,
HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotAcceptableException.class,
MultipartException.class, MissingRequestHeaderException.class, HttpMediaTypeNotSupportedException.class,
NeedLoggedInBusinessException.class})
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public ActionResult handleBusinessException(HttpServletRequest request, Exception exception) {
ActionResult result = convert(exception);
log.info("发生业务异常{}:{}", request.getRequestURI(), result, exception);
return result;
}
/**
* 业务异常
*
* @param request request
* @param exception exception
* @return return
*/
@ExceptionHandler({RedirectBusinessException.class})
public ModelAndView handleModelAndViewBizException(HttpServletRequest request, Exception exception) {
ModelAndView result = translateModelAndView(exception);
log.info("发生ModelAndView业务异常{}:{}", request.getRequestURI(), result, exception);
return result;
}
public ModelAndView translateModelAndView(Throwable exception) {
// 参数异常
if (exception instanceof RedirectBusinessException) {
RedirectBusinessException e = (RedirectBusinessException)exception;
return dealResponseModelAndView(null, e.getMessage(), e.getRedirect(), null, null);
}
// 默认跳首页
return new ModelAndView("redirect:/");
}
private ModelAndView dealResponseModelAndView(String title, String errorMessage, String redirect, String href,
String buttonText) {
// 如果有车重定向信息 则跳转
if (StringUtils.isNotBlank(redirect)) {
return new ModelAndView("redirect:" + redirect);
}
// 默认跳首页
return new ModelAndView("redirect:/");
// 同步请求
//return ModelAndViewUtils.error(title, errorMessage,href,buttonText);
}
/**
* 系统异常
*
* @param request request
* @param exception exception
* @return return
*/
@ExceptionHandler({SystemException.class})
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public ActionResult handleSystemException(HttpServletRequest request, Exception exception) {
ActionResult result = convert(exception);
log.error("发生业务异常{}:{}", request.getRequestURI(), result, exception);
return result;
}
/**
* 未知异常 需要人工介入查看日志
*
* @param request request
* @param exception exception
* @return return
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public ActionResult handledException(HttpServletRequest request, Exception exception) {
ActionResult result = convert(exception);
log.error("发生未知异常{}:{},请求参数:{}", request.getRequestURI(), result,
JSON.toJSONString(request.getParameterMap()),
exception);
return result;
}
public ActionResult convert(Throwable exception) {
ExceptionConvertor exceptionConvertor = EXCEPTION_CONVERTOR_MAP.get(exception.getClass());
if (exceptionConvertor == null) {
exceptionConvertor = DEFAULT_EXCEPTION_CONVERTOR;
}
return exceptionConvertor.convert(exception);
}
}

View File

@ -0,0 +1,20 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.CommonErrorEnum;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import org.springframework.validation.BindException;
/**
* BindException
*
* @author 是仪
*/
public class BindExceptionConvertor implements ExceptionConvertor<BindException> {
@Override
public ActionResult convert(BindException exception) {
String message = ExceptionConvertorUtils.buildMessage(exception.getBindingResult());
return ActionResult.fail(CommonErrorEnum.PARAM_ERROR, message);
}
}

View File

@ -0,0 +1,17 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
/**
* BusinessException
*
* @author 是仪
*/
public class BusinessExceptionConvertor implements ExceptionConvertor<BusinessException> {
@Override
public ActionResult convert(BusinessException exception) {
return ActionResult.fail(exception.getCode(), exception.getMessage());
}
}

View File

@ -0,0 +1,18 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.CommonErrorEnum;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
/**
* 默认的异常处理
* 直接抛出系统异常
*
* @author 是仪
*/
public class DefaultExceptionConvertor implements ExceptionConvertor<Throwable> {
@Override
public ActionResult convert(Throwable exception) {
return ActionResult.fail(CommonErrorEnum.COMMON_SYSTEM_ERROR);
}
}

View File

@ -0,0 +1,19 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
/**
* 异常转换器
*
* @author 是仪
*/
public interface ExceptionConvertor<T extends Throwable> {
/**
* 转换异常
*
* @param exception
* @return
*/
ActionResult convert(T exception);
}

View File

@ -0,0 +1,42 @@
package ai.chat2db.server.start.exception.convertor;
import java.util.List;
import ai.chat2db.server.tools.base.constant.SymbolConstant;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
/**
* 转换工具类
*
* @author 是仪
*/
public class ExceptionConvertorUtils {
/**
* 提取BindingResult中的错误消息
*
* @param result
* @return
*/
public static String buildMessage(BindingResult result) {
List<ObjectError> errors = result.getAllErrors();
if (CollectionUtils.isEmpty(errors)) {
return null;
}
int index = 1;
StringBuilder msg = new StringBuilder();
msg.append("请检查以下信息:");
for (ObjectError e : errors) {
msg.append(index++);
// 得到错误消息
msg.append(SymbolConstant.DOT);
msg.append(e.getDefaultMessage());
msg.append(SymbolConstant.SEMICOLON);
}
return msg.toString();
}
}

View File

@ -0,0 +1,19 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.CommonErrorEnum;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
/**
* MaxUploadSizeExceededException
*
* @author 是仪
*/
public class MaxUploadSizeExceededExceptionConvertor implements ExceptionConvertor<MaxUploadSizeExceededException> {
@Override
public ActionResult convert(MaxUploadSizeExceededException exception) {
return ActionResult.fail(CommonErrorEnum.MAX_UPLOAD_SIZE);
}
}

View File

@ -0,0 +1,20 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.CommonErrorEnum;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
/**
* MethodArgumentNotValidException
*
* @author 是仪
*/
public class MethodArgumentNotValidExceptionConvertor implements ExceptionConvertor<MethodArgumentNotValidException> {
@Override
public ActionResult convert(MethodArgumentNotValidException exception) {
String message = ExceptionConvertorUtils.buildMessage(exception.getBindingResult());
return ActionResult.fail(CommonErrorEnum.PARAM_ERROR, message);
}
}

View File

@ -0,0 +1,20 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.CommonErrorEnum;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
/**
* MethodArgumentTypeMismatchException
*
* @author 是仪
*/
public class MethodArgumentTypeMismatchExceptionConvertor
implements ExceptionConvertor<MethodArgumentTypeMismatchException> {
@Override
public ActionResult convert(MethodArgumentTypeMismatchException exception) {
return ActionResult.fail(CommonErrorEnum.PARAM_ERROR, "请输入正确的数据格式");
}
}

View File

@ -0,0 +1,19 @@
package ai.chat2db.server.start.exception.convertor;
import ai.chat2db.server.tools.base.excption.CommonErrorEnum;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
/**
* 参数异常 目前包括
* ConstraintViolationException
* MissingServletRequestParameterException
* IllegalArgumentException
*
* @author 是仪
*/
public class ParamExceptionConvertor implements ExceptionConvertor<Throwable> {
@Override
public ActionResult convert(Throwable exception) {
return ActionResult.fail(CommonErrorEnum.PARAM_ERROR, exception.getMessage());
}
}

View File

@ -0,0 +1,3 @@
org.springframework.context.ApplicationListener=\
ai.chat2db.server.start.config.listener.ManageApplicationListener,\
ai.chat2db.server.start.config.listener.FailedEventApplicationListener

View File

@ -0,0 +1,18 @@
spring:
datasource:
# 配置自带数据库的相对路径
url: jdbc:h2:~/.chat2db/db/chat2db_dev;MODE=MYSQL
driver-class-name: org.h2.Driver
# 用于数据库表结构版本管理
flyway:
locations: classpath:db/migration/dev
h2:
console:
enabled: true
path: /h2
settings:
trace: true
web-allow-others: true
# 端口号
server:
port: 10821

View File

@ -0,0 +1,11 @@
spring:
datasource:
# 配置自带数据库的相对路径
url: jdbc:h2:~/.chat2db/db/chat2db;MODE=MYSQL
driver-class-name: org.h2.Driver
# 用于数据库表结构版本管理
flyway:
locations: classpath:db/migration/release
# 端口号
server:
port: 10824

View File

@ -0,0 +1,19 @@
spring:
datasource:
# 配置自带数据库的相对路径
url: jdbc:h2:~/.chat2db/db/chat2db_test;MODE=MYSQL
driver-class-name: org.h2.Driver
# 用于数据库表结构版本管理
# 测试的表结构还是用dev的 ,但是有自己单独的库 确保多个客户端运行
flyway:
locations: classpath:db/migration/dev
h2:
console:
enabled: true
path: /h2
settings:
trace: true
web-allow-others: true
# 端口号
server:
port: 10824

View File

@ -0,0 +1,78 @@
spring:
# 默认开发环境
profiles:
active: dev
jpa:
# 展示sql
show-sql: true
messages:
basename: i18n/messages/messages
encoding: UTF-8
fallbackToSystemLocale: true
jmx:
enabled: false
# thymeleaf
thymeleaf:
prefix: classpath:/thymeleaf/
check-template-location: true
suffix: .html
servlet:
content-type: text/html
mode: HTML5
# 静态文件
mvc:
static-path-pattern: /static/**
web:
resources:
static-locations[0]: classpath:/static/
ali:
dbhub:
version: 1.0.0
jdbc-jar-downLoad-urls:
- https://oss-chat2db.alibaba.com/lib/mysql-connector-java-8.0.30.jar
- https://oss-chat2db.alibaba.com/lib/mysql-connector-java-5.1.47.jar
- https://oss-chat2db.alibaba.com/lib/clickhouse-jdbc-0.3.2-patch8-http.jar
- https://oss-chat2db.alibaba.com/lib/mariadb-java-client-3.0.8.jar
- https://oss-chat2db.alibaba.com/lib/mssql-jdbc-11.2.1.jre17.jar
- https://oss-chat2db.alibaba.com/lib/oceanbase-client-2.4.2.jar
- https://oss-chat2db.alibaba.com/lib/postgresql-42.5.1.jar
- https://oss-chat2db.alibaba.com/lib/sqlite-jdbc-3.39.3.0.jar
- https://oss-chat2db.alibaba.com/lib/ojdbc11.jar
- https://oss-chat2db.alibaba.com/lib/ojdbc8-19.3.0.0.jar
- https://oss-chat2db.alibaba.com/lib/orai18n-19.3.0.0.jar
- https://oss-chat2db.alibaba.com/lib/h2-2.1.214.jar
- https://oss-chat2db.alibaba.com/lib/DmJdbcDriver18-8.1.2.141.jar
- https://oss-chat2db.alibaba.com/lib/HikariCP-4.0.3.jar
- https://oss-chat2db.alibaba.com/lib/redis-jdbc-driver-1.3.jar
- https://oss-chat2db.alibaba.com/lib/kingbase8-8.6.0.jar
- https://oss-chat2db.alibaba.com/lib/hive-jdbc-3.1.2-standalone.jar
- https://oss-chat2db.alibaba.com/lib/mongo-jdbc-standalone-1.18.jar
# flywaydb 输出执行sql的日志
logging:
level:
org:
flywaydb: debug
# 登录功能
sa-token:
# token名称 (同时也是cookie名称)
token-name: DBHUB
timeout: 2592000
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: true
# token风格
token-style: uuid
# 是否输出操作日志
is-log: true
# jwt秘钥 部署的时候需要改掉,不然可以随便破解登录
jwt-secret-key: jwt_dbhub
is-write-header: true
chatgpt:
apiKey: sk-xxxx
apiHost: https://api.openai.com/
# 可以选择GPT3或GPT35
version: GPT35
context:
length: 4

View File

@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS `dbhub_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`user_name` varchar(32) NOT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`nick_name` varchar(256) DEFAULT NULL COMMENT '昵称',
`email` varchar(256) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'
;
INSERT INTO `dbhub_user` (`user_name`,`password`,`nick_name`) VALUES ('dbhub','$2a$10$yElafjDHPoPHSaCo6cjJGuWmtXWNVz/cOOOtDg99eNfvUfalzfane','管理员');

View File

@ -0,0 +1,44 @@
CREATE TABLE IF NOT EXISTS `data_source` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`alias` varchar(128) DEFAULT NULL COMMENT '别名',
`url` varchar(1024) DEFAULT NULL COMMENT '连接地址',
`user_name` varchar(128) DEFAULT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`type` varchar(32) DEFAULT NULL COMMENT '数据库类型',
`env_type` varchar(32) DEFAULT NULL COMMENT '环境类型',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'
;
CREATE TABLE IF NOT EXISTS `operation_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`type` varchar(32) NOT NULL COMMENT '数据库类型',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的执行记录表'
;
CREATE TABLE IF NOT EXISTS `operation_saved` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`name` varchar(128) DEFAULT NULL COMMENT '保存名称',
`type` varchar(32) NOT NULL COMMENT '数据库类型',
`status` varchar(32) NOT NULL COMMENT 'ddl语句状态:DRAFT/RELEASE',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`tab_opened` text DEFAULT NULL COMMENT '是否在tab中被打开,y表示打开,n表示未打开',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的保存表'
;

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS `system_config` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`code` varchar(32) NOT NULL COMMENT '配置项编码',
`content` varchar(256) DEFAULT NULL COMMENT '配置项内容',
`summary` varchar(256) DEFAULT NULL COMMENT '配置项说明',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置中心表'
;
create UNIQUE INDEX uk_code on system_config(code) ;

View File

@ -0,0 +1 @@
ALTER TABLE `operation_saved` ADD COLUMN `db_schema_name` varchar(128) NULL COMMENT 'schema名称';

View File

@ -0,0 +1,9 @@
ALTER TABLE `data_source` ADD COLUMN `host` varchar(128) NULL COMMENT 'host地址';
ALTER TABLE `data_source` ADD COLUMN `port` varchar(128) NULL COMMENT '端口';
ALTER TABLE `data_source` ADD COLUMN `ssh` varchar(1024) NULL COMMENT 'ssh配置信息json';
ALTER TABLE `data_source` ADD COLUMN `ssl` varchar(1024) NULL COMMENT 'ssl配置信息json';
ALTER TABLE `data_source` ADD COLUMN `sid` varchar(32) NULL COMMENT 'sid';
ALTER TABLE `data_source` ADD COLUMN `driver` varchar(128) NULL COMMENT '驱动信息';
ALTER TABLE `data_source` ADD COLUMN `jdbc` varchar(128) NULL COMMENT 'jdbc版本';
ALTER TABLE `data_source` ADD COLUMN `extend_info` varchar(4096) NULL COMMENT '自定义扩展字段json';
create INDEX idx_user_id on data_source(user_id) ;

View File

@ -0,0 +1,39 @@
CREATE TABLE IF NOT EXISTS `dashboard` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`name` varchar(128) DEFAULT NULL COMMENT '报表名称',
`description` varchar(128) DEFAULT NULL COMMENT '报表描述',
`schema` text DEFAULT NULL COMMENT '报表布局信息',
`deleted` text DEFAULT NULL COMMENT '是否被删除,y表示删除,n表示未删除',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='自定义报表表'
;
CREATE TABLE IF NOT EXISTS `chart` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`name` varchar(128) DEFAULT NULL COMMENT '图表名称',
`description` varchar(128) DEFAULT NULL COMMENT '图表描述',
`schema` text DEFAULT NULL COMMENT '图表信息',
`data_source_id` bigint(20) unsigned DEFAULT NULL COMMENT '数据源连接ID',
`type` varchar(32) DEFAULT NULL COMMENT '数据库类型',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`deleted` text DEFAULT NULL COMMENT '是否被删除,y表示删除,n表示未删除',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='自定义报表表'
;
CREATE TABLE IF NOT EXISTS `dashboard_chart_relation` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`dashboard_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '报表id',
`chart_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '图表id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='自定义报表表'
;

View File

@ -0,0 +1,70 @@
CREATE TABLE IF NOT EXISTS `data_source` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`alias` varchar(128) DEFAULT NULL COMMENT '别名',
`url` varchar(1024) DEFAULT NULL COMMENT '连接地址',
`user_name` varchar(128) DEFAULT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`type` varchar(32) DEFAULT NULL COMMENT '数据库类型',
`env_type` varchar(32) DEFAULT NULL COMMENT '环境类型',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'
;
CREATE TABLE IF NOT EXISTS `operation_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`type` varchar(32) NOT NULL COMMENT '数据库类型',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的执行记录表'
;
CREATE TABLE IF NOT EXISTS `operation_saved` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`name` varchar(128) DEFAULT NULL COMMENT '保存名称',
`type` varchar(32) NOT NULL COMMENT '数据库类型',
`status` varchar(32) NOT NULL COMMENT 'ddl语句状态:DRAFT/RELEASE',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`tab_opened` text DEFAULT NULL COMMENT '是否在tab中被打开,y表示打开,n表示未打开',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的保存表'
;
CREATE TABLE IF NOT EXISTS `dbhub_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`user_name` varchar(32) NOT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`nick_name` varchar(256) DEFAULT NULL COMMENT '昵称',
`email` varchar(256) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'
;
CREATE TABLE IF NOT EXISTS `system_config` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`code` varchar(32) NOT NULL COMMENT '配置项编码',
`content` varchar(256) DEFAULT NULL COMMENT '配置项内容',
`summary` varchar(256) DEFAULT NULL COMMENT '配置项说明',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置中心表'
;
create UNIQUE INDEX uk_code on system_config(code) ;
INSERT INTO `dbhub_user` (`user_name`,`password`,`nick_name`) VALUES ('dbhub','$2a$10$yElafjDHPoPHSaCo6cjJGuWmtXWNVz/cOOOtDg99eNfvUfalzfane','管理员');

View File

@ -0,0 +1,9 @@
ALTER TABLE `data_source` ADD COLUMN `host` varchar(128) NULL COMMENT 'host地址';
ALTER TABLE `data_source` ADD COLUMN `port` varchar(128) NULL COMMENT '端口';
ALTER TABLE `data_source` ADD COLUMN `ssh` varchar(1024) NULL COMMENT 'ssh配置信息json';
ALTER TABLE `data_source` ADD COLUMN `ssl` varchar(1024) NULL COMMENT 'ssl配置信息json';
ALTER TABLE `data_source` ADD COLUMN `sid` varchar(32) NULL COMMENT 'sid';
ALTER TABLE `data_source` ADD COLUMN `driver` varchar(128) NULL COMMENT '驱动信息';
ALTER TABLE `data_source` ADD COLUMN `jdbc` varchar(128) NULL COMMENT 'jdbc版本';
ALTER TABLE `data_source` ADD COLUMN `extend_info` varchar(4096) NULL COMMENT '自定义扩展字段json';
create INDEX idx_user_id on data_source(user_id) ;

View File

@ -0,0 +1,70 @@
CREATE TABLE IF NOT EXISTS `data_source` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`alias` varchar(128) DEFAULT NULL COMMENT '别名',
`url` varchar(1024) DEFAULT NULL COMMENT '连接地址',
`user_name` varchar(128) DEFAULT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`type` varchar(32) DEFAULT NULL COMMENT '数据库类型',
`env_type` varchar(32) DEFAULT NULL COMMENT '环境类型',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'
;
CREATE TABLE IF NOT EXISTS `operation_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`type` varchar(32) NOT NULL COMMENT '数据库类型',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的执行记录表'
;
CREATE TABLE IF NOT EXISTS `operation_saved` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`data_source_id` bigint(20) unsigned NOT NULL COMMENT '数据源连接ID',
`database_name` varchar(128) DEFAULT NULL COMMENT 'db名称',
`name` varchar(128) DEFAULT NULL COMMENT '保存名称',
`type` varchar(32) NOT NULL COMMENT '数据库类型',
`status` varchar(32) NOT NULL COMMENT 'ddl语句状态:DRAFT/RELEASE',
`ddl` text DEFAULT NULL COMMENT 'ddl内容',
`tab_opened` text DEFAULT NULL COMMENT '是否在tab中被打开,y表示打开,n表示未打开',
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='我的保存表'
;
CREATE TABLE IF NOT EXISTS `dbhub_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`user_name` varchar(32) NOT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`nick_name` varchar(256) DEFAULT NULL COMMENT '昵称',
`email` varchar(256) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据源连接表'
;
CREATE TABLE IF NOT EXISTS `system_config` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`code` varchar(32) NOT NULL COMMENT '配置项编码',
`content` varchar(256) DEFAULT NULL COMMENT '配置项内容',
`summary` varchar(256) DEFAULT NULL COMMENT '配置项说明',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置中心表'
;
create UNIQUE INDEX uk_code on system_config(code) ;
INSERT INTO `dbhub_user` (`user_name`,`password`,`nick_name`) VALUES ('dbhub','$2a$10$yElafjDHPoPHSaCo6cjJGuWmtXWNVz/cOOOtDg99eNfvUfalzfane','管理员');

View File

@ -0,0 +1 @@
ALTER TABLE `operation_saved` ADD COLUMN `db_schema_name` varchar(128) NULL COMMENT 'schema名称';

View File

@ -0,0 +1,9 @@
ALTER TABLE `data_source` ADD COLUMN `host` varchar(128) NULL COMMENT 'host地址';
ALTER TABLE `data_source` ADD COLUMN `port` varchar(128) NULL COMMENT '端口';
ALTER TABLE `data_source` ADD COLUMN `ssh` varchar(1024) NULL COMMENT 'ssh配置信息json';
ALTER TABLE `data_source` ADD COLUMN `ssl` varchar(1024) NULL COMMENT 'ssl配置信息json';
ALTER TABLE `data_source` ADD COLUMN `sid` varchar(32) NULL COMMENT 'sid';
ALTER TABLE `data_source` ADD COLUMN `driver` varchar(128) NULL COMMENT '驱动信息';
ALTER TABLE `data_source` ADD COLUMN `jdbc` varchar(128) NULL COMMENT 'jdbc版本';
ALTER TABLE `data_source` ADD COLUMN `extend_info` varchar(4096) NULL COMMENT '自定义扩展字段json';
create INDEX idx_user_id on data_source(user_id) ;

View File

@ -0,0 +1,8 @@
200=成功
500=內部异常
name=用户名
pwd=密码
COMMON_SYSTEM_ERROR=系统开小差啦,请尝试刷新页面或者联系管理员
CONSOLE_CONNECT_ERROR=控制台链接错误
DATASOURCE_CONNECT_ERROR=数据源连接错误
DATASOURCE_TEST_ERROR=数据源测试连接错误

View File

@ -0,0 +1,8 @@
200=success
500=unexpected exception
name=user name
pwd=password
COMMON_SYSTEM_ERROR=system error, please contact the system admin
CONSOLE_CONNECT_ERROR=create console connection error
DATASOURCE_CONNECT_ERROR=create data source connection error
DATASOURCE_TEST_ERROR=test data source connection error

View File

@ -0,0 +1,8 @@
200=成功
500=內部异常
name=用户名
pwd=密码
COMMON_SYSTEM_ERROR=系统开小差啦,请尝试刷新页面或者联系管理员
CONSOLE_CONNECT_ERROR=控制台链接错误
DATASOURCE_CONNECT_ERROR=数据源连接错误
DATASOURCE_TEST_ERROR=数据源测试连接错误

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<property name="LOG_PATH" value="${user.home}/.chat2db/logs" />
<property name="LOG_FILE" value="${LOG_PATH}/application.log" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<appender name="APPLICATION"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>7</maxHistory>
<maxFileSize>50MB</maxFileSize>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="APPLICATION"/>
</root>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link
rel="icon"
type="image/ico"
sizes="32x32"
href="./static/front/logo.ico"
/>
<title>Chat2DB</title>
<meta
name="description"
content="Chat2DB 是面向开发人员的免费多平台数据库工具。多种数据库一个工具。它用于查询、创建和管理数据库,数据库可以在本地、服务器或云端。支持 MySQL、PostgreSQL、Microsoft SQL Server、Oracle、H2等未来我们会不断完善其他非关系型数据的支持如Redis。"
/>
<meta
name="keywords"
content="数据库chatgptchatDBdatabase后端程序员数据库"
/>
<meta property="og:title" content="Chat2DB"/>
<meta
property="og:description"
content="Chat2DB 是面向开发人员的免费多平台数据库工具。多种数据库一个工具。它用于查询、创建和管理数据库,数据库可以在本地、服务器或云端。支持 MySQL、PostgreSQL、Microsoft SQL Server、Oracle、H2等未来我们会不断完善其他非关系型数据的支持如Redis。"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<link rel="stylesheet" href="./static/front/umi.css"/>
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.5.39
</script>
</head>
<body>
<div id="root"></div>
<script>
console.log("run startServer");
console.log("window===>", window.myAPI);
if (window.myAPI) {
window.myAPI.startServerForSpawn();
}
</script>
<script src="./static/front/umi.js"></script>
</body>
</html>

View File

@ -0,0 +1,22 @@
package ai.chat2db.server.start.test;
import ai.chat2db.server.start.Application;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 本地环境的启动。
* 主要为了读取本地的一些配置 比如日志输出就和其他环境不一样
*
* @author 是仪
*/
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,17 @@
package ai.chat2db.server.start.test.common;
import ai.chat2db.server.start.Application;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 基础测试类
*
* @author Jiaju Zhuang
**/
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public abstract class BaseTest {
}

View File

@ -0,0 +1,242 @@
package ai.chat2db.server.start.test.druid;
import java.util.List;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.PagerUtils;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
import com.alibaba.druid.sql.ast.SQLLimit;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLNotNullConstraint;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.sql.parser.MysqlUtils;
import com.alibaba.druid.sql.parser.SQLParserFeature;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class SqlUtilsTest {
@Test
public void test() {
List<SQLStatement> sqlStatements = SQLUtils.parseStatements("select 1 from test;", DbType.mysql);
log.info("解析sql:{}", sqlStatements);
sqlStatements = SQLUtils.parseStatements("use xxx;select 1 from test;explain select 1 from test", DbType.mysql);
log.info("解析sql:{}", sqlStatements);
sqlStatements = SQLUtils.parseStatements("select 1 from1 test", DbType.mysql);
log.info("解析sql:{}", sqlStatements);
}
@Test
public void test55() {
List<SQLStatement> sqlStatements = SQLUtils.parseStatements("create table test(id int) comment 'xx';",
DbType.mysql);
log.info("解析sql:{}", sqlStatements);
}
@Test
public void test2() {
String sql = "select * from test";
log.info("分页:{} ----- {} --- {}", PagerUtils.count(sql, DbType.mysql),
PagerUtils.limit(sql, DbType.mysql, 1000, 999),
PagerUtils.limit(sql, DbType.mysql, 1000, 999, true));
sql = "select * from test where id=1 limit 100;";
log.info("分页:{} ----- {} --- {}", PagerUtils.count(sql, DbType.mysql),
PagerUtils.limit(sql, DbType.mysql, 1000, 999),
PagerUtils.limit(sql, DbType.mysql, 1000, 999, true));
sql = "select * from test where id=1 limit 100,10;";
log.info("分页:{} ----- {} --- {}", PagerUtils.count(sql, DbType.mysql),
PagerUtils.limit(sql, DbType.mysql, 1000, 999),
PagerUtils.limit(sql, DbType.mysql, 1000, 999, true));
sql = "select * from test where id=1 limit 100,10;";
log.info("分页:{} ----- {} --- {}", PagerUtils.count(sql, DbType.mysql),
PagerUtils.limit(sql, DbType.mysql, 2, 2),
PagerUtils.limit(sql, DbType.mysql, 2, 2, true));
sql = "select * from test union select * from test2";
log.info("分页:{} ----- {} --- {}", PagerUtils.count(sql, DbType.mysql),
PagerUtils.limit(sql, DbType.mysql, 2, 2),
PagerUtils.limit(sql, DbType.mysql, 2, 2, true));
sql = "select * from test union select * from test2";
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, DbType.mysql);
SQLSelectStatement sqlSelectStatement = (SQLSelectStatement)sqlStatement;
log.info("test{}", sqlSelectStatement);
}
@Test
public void test56() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"create table test(id int ,name varchar(32) not null default 'xx' comment 'name',nu int auto_increment,"
+ "index ds(id) ,primary key (id,nu)) "
+ "comment 'xx';",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void test4() {
MySqlCreateTableStatement x = new MySqlCreateTableStatement();
x.setTableName("ff");
x.setComment(new MySqlCharExpr(null));
SQLColumnDefinition c = new SQLColumnDefinition();
x.addColumn(c);
c.setName("name");
SQLDataTypeImpl sqlDataType = new SQLDataTypeImpl();
sqlDataType.setName("varchar(32)");
c.setDataType(sqlDataType);
c.addConstraint(new SQLNotNullConstraint());
c.setComment(new MySqlCharExpr("xname"));
//x.addColumn();
log.info(x.toString());
}
@Test
public void testreaname() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"rename table data_ops_table_test_1667268894825 to data_ops_table_test_166726889482511;",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void testcomment() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"\n"
+ "alter table data_ops_table_test_166726889482511\n"
+ " comment '测试表33';",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void dropindex() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"drop index data_ops_table_test_1667268894825_idx_date on data_ops_table_test_1667268894825;",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void createindex() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"\n"
+ "create index data_ops_table_test_1667268894825_idx_date\n"
+ " on data_ops_table_test_1667268894825 (date desc, id asc)\n"
+ " comment '日期索引';",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void addColumn() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"alter table data_ops_table_test_1667268894825\n"
+ " add column_5 int default de null;",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void change() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"alter table data_ops_table_test_1667268894825\n"
+ " change number number1 bigint null comment '长整型';",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void modify() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"alter table data_ops_table_test_1667268894825\n"
+ " modify number1 bigint null comment '长整型';",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void dropColumn() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"alter table data_ops_table_test_1667268894825\n"
+ " drop column string;",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void dropPrimaryKey() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"ALTER TABLE `ali_dbhub_test`.`data_ops_table_test_1671368857363` \n"
+ "DROP PRIMARY KEY,\n"
+ "ADD PRIMARY KEY (`date`) USING BTREE;",
DbType.mysql);
log.info("解析sql:{}", sqlStatement);
}
@Test
public void coment() {
try {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"comment on index DATA_OPS_TEMPLATE_TEST_1672663574919_idx_date is '日期索引xx';\n",
DbType.h2, SQLParserFeature.PrintSQLWhileParsingFailed);
log.info("解析sql:{}", sqlStatement);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void errro() {
List<SQLStatement> sqlStatementList = SQLUtils.parseStatements(
"alter table data_ops_table_test_1667268894825 drop column string;comment on index "
+ "DATA_OPS_TEMPLATE_TEST_1672663574919_idx_date is '日期索引xx';\n",
DbType.h2, SQLParserFeature.PrintSQLWhileParsingFailed);
log.info("解析sql:{}", sqlStatementList);
}
@Test
public void spilt() {
List<String> list = MysqlUtils.splitAndRemoveComment(
"alter table data_ops_table_test_1667268894825 drop column string;comment on index "
+ "DATA_OPS_TEMPLATE_TEST_1672663574919_idx_date is '日期索引xx;;;';\n", DbType.h2);
log.info("解析sql:{}", JSON.toJSONString(list));
}
@Test
public void creattable() {
List<SQLStatement> sqlStatementList = SQLUtils.parseStatements(
"CREATE TABLE `data_ops_table_test_1673096155228`\n"
+ "\t(\n"
+ "\t `id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '主键自增',\n"
+ "\t `date` datetime(3) not null COMMENT '日期',\n"
+ "\t `number` bigint COMMENT '长整型',\n"
+ "\t `string` VARCHAR(100) default 'DATA' COMMENT '名字',\n"
+ "\t index data_ops_table_test_1673096155228_idx_date (date desc) comment '日期索引',\n"
+ "\t unique data_ops_table_test_1673096155228_uk_number (number) comment '唯一索引',\n"
+ "\t index data_ops_table_test_1673096155228_idx_number_string (number, date) comment '联合索引'\n"
+ "\t) COMMENT ='测试表';", DbType.mysql);
log.info("解析sql:{}", sqlStatementList);
}
@Test
public void testlimit2() {
SQLLimit sqlLimit= SQLUtils.getLimit("select * from t_orderdetail limit 0,1",DbType.mysql);
log.info("解析sql:{}", sqlLimit);
sqlLimit= SQLUtils.getLimit("select * from t_orderdetail",DbType.mysql);
log.info("解析sql:{}", sqlLimit);
}
}

View File

@ -0,0 +1,20 @@
package ai.chat2db.server.start.test.druid;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class SqlUtilsTest2 {
@Test
public void coment() {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(
"comment on index myindex is '日期xxx';\n",
DbType.h2);
log.info("解析sql:{}", sqlStatement);
}
}

View File

@ -0,0 +1,103 @@
package ai.chat2db.server.start.test.mybatis;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import ai.chat2db.server.start.test.common.BaseTest;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
/**
* 生成mybatis 的mapper
*
* @author Jiaju Zhuang
*/
@Slf4j
public class MybatisGeneratorTest extends BaseTest {
@Resource
private DataSource dataSource;
@Test
public void coreGenerator() {
//doGenerator(Lists.newArrayList("data_source"));
//doGenerator(Lists.newArrayList("operation_log"));
//doGenerator(Lists.newArrayList("operation_saved"));
doGenerator(Lists.newArrayList("dbhub_user"));
}
private void doGenerator(List<String> tableList) {
// 当前项目地址 拿到的是ali-dbhub-server-start地址
String outputDir = System.getProperty("user.dir")
+ "/../ali-dbhub-server-domain/ali-dbhub-server-domain-repository/src/main"
+ "/java";
String xmlDir = System.getProperty("user.dir")
+ "/../ali-dbhub-server-domain/ali-dbhub-server-domain-repository/src/main"
+ "/resources/com/alibaba/dbhub/server/domain/repository";
// 不要生成service controller
Map<OutputFile, String> pathInfo = new HashMap<>();
pathInfo.put(OutputFile.service, null);
pathInfo.put(OutputFile.serviceImpl, null);
pathInfo.put(OutputFile.xml, xmlDir);
pathInfo.put(OutputFile.controller, null);
FastAutoGenerator
.create(new DataSourceConfig.Builder(dataSource)
.typeConvert(new MySqlTypeConvert()))
//全局配置
.globalConfig(builder -> {
// 设置作者
builder.author("ali-dbhub")
//执行完毕不打开文件夹
.disableOpenDir()
// 指定输出目录
.outputDir(outputDir);
})
//包配置
.packageConfig(builder -> {
// 设置父包名
builder.parent("ai.chat2db.server.domain.repository")
//生成实体层
.entity("entity")
.pathInfo(pathInfo)
//生成mapper层
.mapper("mapper");
})
//策略配置
.strategyConfig(builder -> {
// 设置需要生成的表名
builder.addInclude(tableList)
//开启实体类配置
.entityBuilder()
.formatFileName("%sDO")
// 覆盖文件
.enableFileOverride()
//.addTableFills(new Column("gmt_create", FieldFill.INSERT)) // 表字段填充
//.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE)) // 表字段填充
//开启lombok
.enableLombok()
.mapperBuilder()
// 覆盖文件
.enableFileOverride()
;
})
//模板配置
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板默认的是Velocity引擎模板
//执行
.execute();
}
}

View File

@ -0,0 +1,47 @@
package ai.chat2db.server.start.test.sql;
import java.sql.Connection;
import java.sql.Statement;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 模板
*
* @author Jiaju Zhuang
*/
@Slf4j
public class DbhubJdbcTemplateTest {
private static JdbcTemplate jdbcTemplate;
@BeforeAll
public static void prepare() throws Exception {
log.info("连接mysql");
}
@Test
@Order(2)
public void test() throws Exception {
jdbcTemplate.execute("use data_ops_test");
Connection connection = jdbcTemplate.getDataSource().getConnection();
Statement statement = connection.createStatement();
boolean execute = statement.execute("select * from test_query");
log.info("execute{}",execute);
statement = connection.createStatement();
execute = statement.execute("INSERT INTO `test_query` (name,date,number) VALUES ('姓名','2022-01-01',123);");
log.info("execute{}",execute);
//Returns:
//true if the first result is a ResultSet object; false if it is an update count or there are no results
statement = connection.createStatement();
execute = statement.execute("show tables");
log.info("execute{}",execute);
}
}

View File

@ -0,0 +1,191 @@
package com.alibaba.druid.sql.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.alibaba.druid.DbType;
public class MysqlUtils extends SQLParserUtils {
public static List<String> sp(String sql, DbType dbType) {
List<String> tables = new ArrayList<>();
Lexer lexer = createLexer(sql, dbType);
lexer.nextToken();
for (; lexer.token != Token.EOF; ) {
switch (lexer.token) {
case SEMI:
System.out.println(lexer.token);
break;
default:
break;
}
lexer.nextToken();
}
return new ArrayList<>(tables);
}
public static List<String> splitAndRemoveComment(String sql, DbType dbType) {
if (dbType == null) {
dbType = DbType.other;
}
boolean containsCommentAndSemi = false;
{
Lexer lexer = createLexer(sql, dbType);
lexer.config(SQLParserFeature.SkipComments, false);
lexer.config(SQLParserFeature.KeepComments, true);
while (lexer.token != Token.EOF) {
if (lexer.token == Token.LINE_COMMENT
|| lexer.token == Token.MULTI_LINE_COMMENT
|| lexer.token == Token.SEMI) {
containsCommentAndSemi = true;
break;
}
lexer.nextToken();
}
if (!containsCommentAndSemi) {
return Collections.singletonList(sql);
}
}
{
Lexer lexer = createLexer(sql, dbType);
lexer.nextToken();
boolean script = false;
if (dbType == DbType.odps && lexer.token == Token.VARIANT) {
script = true;
}
if (script || lexer.identifierEquals("pai") || lexer.identifierEquals("jar")) {
return Collections.singletonList(sql);
}
}
List list = new ArrayList();
Lexer lexer = createLexer(sql, dbType);
lexer.config(SQLParserFeature.SkipComments, false);
lexer.config(SQLParserFeature.KeepComments, true);
boolean set = false, paiOrJar = false;
int start = 0;
Token token = lexer.token;
for (; lexer.token != Token.EOF; ) {
if (token == Token.SEMI) {
int len = lexer.startPos - start;
if (len > 0) {
String lineSql = sql.substring(start, lexer.startPos);
String splitSql = set
? removeLeftComment(lineSql, dbType)
: removeComment(lineSql, dbType
).trim();
if (!splitSql.isEmpty()) {
list.add(splitSql);
}
}
start = lexer.startPos + 1;
set = false;
} else if (token == Token.MULTI_LINE_COMMENT) {
int len = lexer.startPos - start;
if (len > 0) {
String splitSql = removeComment(
sql.substring(start, lexer.startPos),
dbType
).trim();
if (!splitSql.isEmpty()) {
list.add(splitSql);
}
}
lexer.nextToken();
token = lexer.token;
start = lexer.startPos;
continue;
} else if (token == Token.CREATE) {
lexer.nextToken();
if (lexer.token == Token.FUNCTION || lexer.identifierEquals("FUNCTION")) {
lexer.nextToken();
lexer.nextToken();
if (lexer.token == Token.AS) {
lexer.nextToken();
if (lexer.token == Token.LITERAL_CHARS) {
lexer.nextToken();
token = lexer.token;
continue;
}
}
lexer.startPos = sql.length();
break;
}
token = lexer.token;
continue;
} else if (set && token == Token.EQ && dbType == DbType.odps) {
lexer.nextTokenForSet();
token = lexer.token;
continue;
} else if (dbType == DbType.odps
&& lexer.ch != '.'
&& (lexer.identifierEquals("pai") || lexer.identifierEquals("jar"))) {
paiOrJar = true;
}
if (lexer.identifierEquals("USING")) {
lexer.nextToken();
if (lexer.identifierEquals("jar")) {
lexer.nextToken();
}
}
if (lexer.token == Token.SET) {
set = true;
}
if (lexer.identifierEquals("ADD") && (dbType == DbType.hive || dbType == DbType.odps)) {
lexer.nextToken();
if (lexer.identifierEquals("JAR")) {
lexer.nextPath();
}
} else {
lexer.nextToken();
}
token = lexer.token;
}
if (start != sql.length() && token != Token.SEMI) {
int end = lexer.startPos;
if (end > sql.length()) {
end = sql.length();
}
String splitSql = sql.substring(start, end).trim();
if (!paiOrJar) {
splitSql = removeComment(splitSql, dbType).trim();
} else {
if (splitSql.endsWith(";")) {
splitSql = splitSql.substring(0, splitSql.length() - 1).trim();
}
}
if (!splitSql.isEmpty()) {
list.add(splitSql);
}
}
return list;
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>