This commit is contained in:
Jane
2023-12-26 11:02:39 +08:00
commit 03cc177018
519 changed files with 54893 additions and 0 deletions

28
salpa-system/pom.xml Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>salpa</artifactId>
<groupId>com.salpa</groupId>
<version>3.8.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>salpa-system</artifactId>
<description>
system系统模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.salpa</groupId>
<artifactId>salpa-common</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="salpa-common" />
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-security:2.5.14" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.5.14" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.5.14" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.14" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.5.14" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.36" level="project" />
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-config:5.5.8" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.5.8" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.5.8" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.5.8" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.1" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.14" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:4.0.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.2.2" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.9" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.7" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-autoconfigure:1.4.1" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.3.0" level="project" />
<orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.5.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.63" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.2.3.Final" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.6.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.6" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.12.6" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.fastjson2:fastjson2:2.0.8" level="project" />
<orderEntry type="library" name="Maven: commons-io:commons-io:2.11.0" level="project" />
<orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-compress:1.21" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:4.1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi:4.1.2" level="project" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:SparseBitSet:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:4.1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:3.1.0" level="project" />
<orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" />
<orderEntry type="library" name="Maven: com.deepoove:poi-tl:1.10.0" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.36" level="project" />
<orderEntry type="library" name="Maven: com.deepoove:poi-ooxml-schemas-extra:4.1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-transcoder:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-anim:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-css:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-ext:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-parser:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-svg-dom:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-awt-util:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:xmlgraphics-commons:2.6" level="project" />
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.0.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-bridge:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-script:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-dom:1.14" level="project" />
<orderEntry type="library" name="Maven: xalan:xalan:2.7.2" level="project" />
<orderEntry type="library" name="Maven: xalan:serializer:2.7.2" level="project" />
<orderEntry type="library" name="Maven: xml-apis:xml-apis:1.4.01" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-gvt:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-shared-resources:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-svggen:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-util:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-constants:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-i18n:1.14" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-xml:1.14" level="project" />
<orderEntry type="library" name="Maven: xml-apis:xml-apis-ext:1.3.04" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-codec:1.14" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" />
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
<orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
<orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.24" level="project" />
<orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.4.2.Final" level="project" />
<orderEntry type="library" name="Maven: net.sourceforge.plantuml:plantuml:1.2022.6" level="project" />
<orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.31" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.14" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.5.11" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.5.11" level="project" />
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.5.11" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.3.20" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.3.20" level="project" />
<orderEntry type="library" name="Maven: io.lettuce:lettuce-core:6.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.4.18" level="project" />
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" />
<orderEntry type="library" name="Maven: eu.bitwalker:UserAgentUtils:1.21" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" />
</component>
</module>

View File

@@ -0,0 +1,103 @@
package com.salpa.model.domain;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* 项目模型信息管理表 project_model_info
*
* @author zhuff
*/
public class ProjectModelInfo implements Serializable {
private static final long serialVersionUID = 1L;
/** ID */
private Integer projectId;
/** 项目名称 */
private String projectName;
/** 项目描述 */
private String projectDescription;
/** 项目json文件存储 */
private String jsonFile;
/** 删除标识 */
private boolean deleted;
/** 创建时间 */
private Timestamp createAt;
/*业务字段*/
/*组员*/
private Long[] userIds;
private String[] userNames;
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectDescription() {
return projectDescription;
}
public void setProjectDescription(String projectDescription) {
this.projectDescription = projectDescription;
}
public String getJsonFile() {
return jsonFile;
}
public void setJsonFile(String jsonFile) {
this.jsonFile = jsonFile;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public Timestamp getCreateAt() {
return createAt;
}
public void setCreateAt(Timestamp createAt) {
this.createAt = createAt;
}
public Long[] getUserIds() {
return userIds;
}
public void setUserIds(Long[] userIds) {
this.userIds = userIds;
}
public String[] getUserNames() {
return userNames;
}
public void setUserNames(String[] userNames) {
this.userNames = userNames;
}
}

View File

@@ -0,0 +1,37 @@
package com.salpa.model.mapper;
import com.salpa.model.domain.ProjectModelInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @author zhuff
*/
@Mapper
public interface ModelMapper {
String getProjectIds(Long userId);
List<ProjectModelInfo> getList(@Param("list") List<String> list,@Param("projectName") String projectName);
int checkProjectNameUnique(String projectName);
int addProject(ProjectModelInfo projectModelInfo);
void updateProjectIds(@Param("projectIds") String projectIds, @Param("userId") Long userId);
int deleteProject(Integer projectId);
int updateProject(ProjectModelInfo projectModelInfo);
ProjectModelInfo getById(Integer projectId);
List<Map<String, Object>> getUserList();
int updateJsonFile(ProjectModelInfo projectModelInfo);
void addProjectIds(@Param("projectIds") String projectIds, @Param("userId") Long userId);
}

View File

@@ -0,0 +1,34 @@
package com.salpa.model.service;
import com.salpa.common.core.domain.AjaxResult;
import com.salpa.model.domain.ProjectModelInfo;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @author zhuff
*/
public interface ModelService {
List<ProjectModelInfo> getList(Integer pageNum,Integer pageSize,String projectName);
String checkProjectNameUnique(String projectName);
int addProject(ProjectModelInfo projectModelInfo) throws IOException;
int deleteProject(Integer projectId);
int updateProject(ProjectModelInfo projectModelInfo);
ProjectModelInfo getById(Integer projectId);
List<Map<String, Object>> getUserList();
int updateJsonFile(ProjectModelInfo projectModelInfo);
AjaxResult connectDataSource(Map map);
AjaxResult executeSql(Map map);
}

View File

@@ -0,0 +1,326 @@
package com.salpa.model.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.PageHelper;
import com.salpa.common.constant.UserConstants;
import com.salpa.common.core.domain.AjaxResult;
import com.salpa.common.core.domain.entity.SysUser;
import com.salpa.common.utils.SecurityUtils;
import com.salpa.model.domain.ProjectModelInfo;
import com.salpa.model.mapper.ModelMapper;
import com.salpa.model.service.ModelService;
import com.salpa.system.domain.SysUserRole;
import com.salpa.system.mapper.SysUserMapper;
import com.salpa.system.mapper.SysUserRoleMapper;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;
@Service
public class ModelServiceImpl implements ModelService {
@Autowired
private ModelMapper modelMappper;
@Autowired
private SysUserRoleMapper sysUserRoleMapper;
@Autowired
private SysUserMapper sysUserMapper;
/**
* 获取当前用户项目模型列表
* @author zhuff
* @return
*/
@Override
public List<ProjectModelInfo> getList(Integer pageNum,Integer pageSize,String projectName) {
SysUser user = SecurityUtils.getLoginUser().getUser();
List<String> projectList = new ArrayList<>();
if (!user.isAdmin()){
/*获取当前用户所对应项目id*/
String projectIds = modelMappper.getProjectIds(user.getUserId());
if (projectIds != null && projectIds != "") {
projectList = Arrays.asList(projectIds.split(","));
}
}
PageHelper.startPage(pageNum,pageSize);
List<ProjectModelInfo> list = modelMappper.getList(projectList,projectName);
/*添加项目人员*/
for (ProjectModelInfo projectModelInfo : list) {
List<SysUserRole> userRole = sysUserRoleMapper.getUserRole();
List<Long> userList = new ArrayList<>();
List<String> userNameList = new ArrayList<>();
for (SysUserRole sysUserRole : userRole) {
String projectIds = sysUserRole.getProjectIds();
if (projectIds!=null){
String[] split = projectIds.split(",");
for (String projectId : split) {
if (projectId.equals(projectModelInfo.getProjectId().toString())){
userList.add(sysUserRole.getUserId());
userNameList.add(sysUserMapper.selectUserById(sysUserRole.getUserId()).getUserName());
}
}
}
}
projectModelInfo.setUserIds(userList.toArray(new Long[userList.size()]));
projectModelInfo.setUserNames(userNameList.toArray(new String[userNameList.size()]));
}
return list;
}
/**
* 校验项目模型名称是否唯一
* @author zhuff
* @param projectName
* @return
*/
@Override
public String checkProjectNameUnique(String projectName) {
int count = modelMappper.checkProjectNameUnique(projectName);
if (count > 0)
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
/**
* 新增项目模型
* @author zhuff
* @param projectModelInfo
* @return
*/
@Override
public int addProject(ProjectModelInfo projectModelInfo) throws IOException {
InputStream inputStream = new ClassPathResource("empty.json").getInputStream();
String json = IOUtils.toString(inputStream, "UTF-8");
Map map = JSON.parseObject(json);
map.put("name",projectModelInfo.getProjectName());
map.put("describe",projectModelInfo.getProjectDescription());
projectModelInfo.setJsonFile(JSONObject.toJSONString(map));
/*新增项目模型*/
int i = modelMappper.addProject(projectModelInfo);
/*获取新增项目模型的id*/
Integer projectId = projectModelInfo.getProjectId();
/*为用户添加项目权限*/
Long[] userIds = projectModelInfo.getUserIds();
for (Long userId : userIds) {
/*获取已有项目权限*/
String projectIds = modelMappper.getProjectIds(userId);
if (projectIds != null && !"".equals(projectIds)){
projectIds += ","+ projectId;
modelMappper.updateProjectIds(projectIds,userId);
} else {
if ("".equals(projectIds)) {
projectIds = projectId.toString();
modelMappper.updateProjectIds(projectIds,userId);
} else {
projectIds = projectId.toString();
modelMappper.addProjectIds(projectIds,userId);
}
}
}
return i;
}
/**
* 删除项目模型
* @author zhuff
* @param projectId
* @return
*/
@Override
public int deleteProject(Integer projectId) {
return modelMappper.deleteProject(projectId);
}
/**
* 修改项目模型
* @author zhuff
* @param projectModelInfo
* @return
*/
@Override
public int updateProject(ProjectModelInfo projectModelInfo) {
/*读取json,修改名称及描述*/
ProjectModelInfo byId = modelMappper.getById(projectModelInfo.getProjectId());
Map map = JSON.parseObject(byId.getJsonFile());
map.put("name",projectModelInfo.getProjectName());
map.put("describe",projectModelInfo.getProjectDescription());
projectModelInfo.setJsonFile(JSONObject.toJSONString(map));
/*修改项目模型*/
int i = modelMappper.updateProject(projectModelInfo);
/*删除该项目原有人员*/
List<SysUserRole> userRole = sysUserRoleMapper.getUserRole();
for (SysUserRole sysUserRole : userRole) {
List<String> projectList = new ArrayList<>(Arrays.asList(sysUserRole.getProjectIds().split(",")));
if (projectList.size()>0){
// 获取迭代器
Iterator<String> it = projectList.iterator();
while(it.hasNext()){
String str = it.next();
if(projectModelInfo.getProjectId().toString().equals(str)){
it.remove();
}
}
}
String projectIds = "";
if (projectList.size()>0){
for (String project : projectList) {
if (projectIds == ""){
projectIds = project;
} else {
projectIds += "," + project;
}
}
}
modelMappper.updateProjectIds(projectIds,sysUserRole.getUserId());
}
/*添加项目现有人员*/
Long[] userIds = projectModelInfo.getUserIds();
for (Long userId : userIds) {
/*获取已有项目权限*/
String projectIds = modelMappper.getProjectIds(userId);
if (projectIds != null && !"".equals(projectIds)){
projectIds += ","+ projectModelInfo.getProjectId();
modelMappper.updateProjectIds(projectIds,userId);
} else {
if ("".equals(projectIds)) {
projectIds = projectModelInfo.getProjectId().toString();
modelMappper.updateProjectIds(projectIds,userId);
} else {
projectIds = projectModelInfo.getProjectId().toString();
modelMappper.addProjectIds(projectIds,userId);
}
}
}
return i;
}
/**
* 根据id获取项目模型
* @author zhuff
* @param projectId
* @return
*/
@Override
public ProjectModelInfo getById(Integer projectId) {
ProjectModelInfo projectModelInfo = modelMappper.getById(projectId);
List<SysUserRole> userRole = sysUserRoleMapper.getUserRole();
List<Long> userList = new ArrayList<>();
for (SysUserRole sysUserRole : userRole) {
String projectIds = sysUserRole.getProjectIds();
if (projectIds!=null){
String[] split = projectIds.split(",");
for (String project : split) {
if (project.equals(projectModelInfo.getProjectId().toString())){
userList.add(sysUserRole.getUserId());
}
}
}
}
projectModelInfo.setUserIds(userList.toArray(new Long[userList.size()]));
return projectModelInfo;
}
/**
* 获取所有用户
* @return
*/
@Override
public List<Map<String, Object>> getUserList() {
return modelMappper.getUserList();
}
/**
* 更新jsonFile
* @param projectModelInfo
* @return
*/
@Override
public int updateJsonFile(ProjectModelInfo projectModelInfo) {
return modelMappper.updateJsonFile(projectModelInfo);
}
/**
* 测试连接数据库
* @param map
* @return
*/
@Override
public AjaxResult connectDataSource(Map map) {
Properties info = new Properties();
info.put("user" ,map.get("username"));
info.put("password" ,map.get("password"));
try {
Class.forName(map.get("driverName").toString());
System.out.println("驱动加载成功");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return AjaxResult.error("驱动加载失败");
}
try {
DriverManager.getConnection(map.get("url").toString(), info);
return AjaxResult.success("数据库连接成功");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return AjaxResult.error("数据库连接失败");
}
}
/**
* 执行ddl语句
* @param map
* @return
*/
@Override
public AjaxResult executeSql(Map map) {
Properties info = new Properties();
info.put("user" ,map.get("username"));
info.put("password" ,map.get("password"));
try {
Class.forName(map.get("driverName").toString());
System.out.println("驱动加载成功");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("驱动加载失败");
}
try {
Connection connection = DriverManager.getConnection(map.get("url").toString(), info);
String[] tableTypes = {"TABLE"};
ResultSet tablesResult = connection.getMetaData()
.getTables(connection.getCatalog(), connection.getSchema(), null, tableTypes);
while (tablesResult.next()) {
return AjaxResult.error("该数据库已存在数据表,不能再次执行");
}
Statement statement = connection.createStatement();
String[] sqls = map.get("sql").toString().split(";");
for (String sql : sqls) {
if (!sql.equals("\n\n") && !sql.equals("\n")) {
statement.executeUpdate(sql);
}
}
statement.close();
tablesResult.close();
connection.close();
return AjaxResult.success("执行成功");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return AjaxResult.error(e.getMessage());
}
}
}

View File

@@ -0,0 +1,15 @@
package com.salpa.online.service;
import com.salpa.common.core.domain.AjaxResult;
import com.salpa.subject.domain.MonitorDataSource;
import java.util.List;
public interface OnlineService {
List<MonitorDataSource> getDataSource();
AjaxResult connectDataSource(String databaseName);
AjaxResult testExecute(String databaseName, String script);
}

View File

@@ -0,0 +1,116 @@
package com.salpa.online.service.impl;
import com.salpa.common.core.domain.AjaxResult;
import com.salpa.online.service.OnlineService;
import com.salpa.subject.domain.MonitorDataSource;
import com.salpa.subject.mapper.DataSourcePropertyMapper;
import com.salpa.subject.mapper.DatabaseMapper;
import com.salpa.subject.service.ProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.*;
import java.util.*;
@Service
public class OnlineServiceImpl implements OnlineService {
@Autowired
private DatabaseMapper databaseMapper;
@Autowired
private ProjectService projectService;
@Autowired
private DataSourcePropertyMapper dataSourcePropertyMapper;
/**
* 获取数据源
* @return
*/
@Override
public List<MonitorDataSource> getDataSource() {
List<MonitorDataSource> list = databaseMapper.selectDataSourceList();
return list;
}
/**
* 连接数据库
* @param databaseName
* @return
*/
@Override
public AjaxResult connectDataSource(String databaseName) {
MonitorDataSource monitorDataSource = databaseMapper.selectByDatabaseName(databaseName);
List<Map<String,Object>> properties = dataSourcePropertyMapper.selectPropertyBySourceId(monitorDataSource.getId());
Connection connection = projectService.create(monitorDataSource, properties);
List<String> tables = new ArrayList<>();
if (connection == null) {
return AjaxResult.error("连接失败");
} else {
try {
String[] tableTypes = {"TABLE"};
ResultSet tablesResult = connection.getMetaData()
.getTables(databaseName, monitorDataSource.getSchemaName(), null, tableTypes);
while (tablesResult.next()) {
tables.add(tablesResult.getString("TABLE_NAME"));
}
tablesResult.close();//关闭tablesResult对象
connection.close();//关闭Connection对象
} catch (Exception throwAbles) {
throwAbles.printStackTrace();
return AjaxResult.error(throwAbles.getMessage());
}
}
return AjaxResult.success(tables);
}
/**
* 执行sql语句
* @param databaseName
* @param script
* @return
*/
@Override
public AjaxResult testExecute(String databaseName, String script) {
Map<String, Map<String,Object>> columns = new LinkedHashMap();
MonitorDataSource monitorDataSource = databaseMapper.selectByDatabaseName(databaseName);
List<Map<String,Object>> properties = dataSourcePropertyMapper.selectPropertyBySourceId(monitorDataSource.getId());
Map<String,Object> reslutMap = new LinkedHashMap<>();
List<Map<String, Object>> rows = new ArrayList<>();
try {
Connection connection = projectService.create(monitorDataSource, properties);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(script);
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 0; i < columnCount; i++) {
Map<String,Object> map = new HashMap<>();
map.put("name",metaData.getColumnName(i+1));
map.put("type",metaData.getColumnTypeName(i+1));
columns.put(metaData.getColumnName(i+1),map);
}
while(resultSet.next()){
Map<String, Object> row = new LinkedHashMap<>();
for (int i = 0; i < columnCount; i++) {
Object object = resultSet.getObject(metaData.getColumnLabel(i+1));
if (metaData.getColumnTypeName(i+1).equals("DATETIME") || metaData.getColumnTypeName(i+1).equals("TIMESTAMP") || metaData.getColumnTypeName(i+1).equals("DATE")) {
row.put(metaData.getColumnName(i+1),resultSet.getString(metaData.getColumnLabel(i+1)));
} else {
row.put(metaData.getColumnName(i+1),object);
}
}
rows.add(row);
}
resultSet.close();//关闭ResultSet对象
statement.close();//关闭Statement对象
connection.close();//关闭Connection对象
reslutMap.put("columns",columns);
reslutMap.put("rows",rows);
} catch (SQLException throwAbles) {
throwAbles.printStackTrace();
return AjaxResult.error(throwAbles.getMessage());
}
return AjaxResult.success(reslutMap);
}
}

View File

@@ -0,0 +1,134 @@
package com.salpa.subject.domain;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 数据库表 monitor_data_source
*
* @author zhuff
*/
public class MonitorDataSource {
/** id */
private Integer id;
/** 项目id */
private Integer projectId;
/** 数据库名称 */
private String databaseName;
/** schema名称 */
private String schemaName;
/** 数据库类型 */
private String databaseType;
/** 数据库路径 */
private String url;
/** 登录名 */
private String username;
/** 密码 */
private String password;
/** 修改时间 */
private LocalDateTime updateAt;
/** 创建时间 */
private LocalDateTime createAt;
/** 连接参数 */
private Map[] properties;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getDatabaseName() {
return databaseName;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public String getSchemaName() {
return schemaName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
public String getDatabaseType() {
return databaseType;
}
public void setDatabaseType(String databaseType) {
this.databaseType = databaseType;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public LocalDateTime getUpdateAt() {
return updateAt;
}
public void setUpdateAt(LocalDateTime updateAt) {
this.updateAt = updateAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public Map[] getProperties() {
return properties;
}
public void setProperties(Map[] properties) {
this.properties = properties;
}
}

View File

@@ -0,0 +1,67 @@
package com.salpa.subject.domain;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* 数据库参数表 monitor_data_source_property
*
* @author zhuff
*/
public class MonitorDataSourceProperty {
/** ID */
private Integer id;
/** 数据库ID */
private Integer dataSourceId;
/** key值 */
private String dataSourceKey;
/** value值 */
private String dataSourceValue;
/** 项目ID */
private LocalDateTime createAt;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getDataSourceId() {
return dataSourceId;
}
public void setDataSourceId(Integer dataSourceId) {
this.dataSourceId = dataSourceId;
}
public String getDataSourceKey() {
return dataSourceKey;
}
public void setDataSourceKey(String dataSourceKey) {
this.dataSourceKey = dataSourceKey;
}
public String getDataSourceValue() {
return dataSourceValue;
}
public void setDataSourceValue(String dataSourceValue) {
this.dataSourceValue = dataSourceValue;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,127 @@
package com.salpa.subject.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
/**
* 数据库文档表
* @author zhuff
*/
public class MonitorDatabaseDocument {
private Integer id;
private Integer projectId;
private String databaseName;
private String schemaName;
private String productName;
private String productVersion;
private Long documentVersion;
private boolean archive;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime createAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime updateAt;
private List<MonitorTableDocument> tables;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getDatabaseName() {
return databaseName;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public String getSchemaName() {
return schemaName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductVersion() {
return productVersion;
}
public void setProductVersion(String productVersion) {
this.productVersion = productVersion;
}
public Long getDocumentVersion() {
return documentVersion;
}
public void setDocumentVersion(Long documentVersion) {
this.documentVersion = documentVersion;
}
public boolean isArchive() {
return archive;
}
public void setArchive(boolean archive) {
this.archive = archive;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public LocalDateTime getUpdateAt() {
return updateAt;
}
public void setUpdateAt(LocalDateTime updateAt) {
this.updateAt = updateAt;
}
public List<MonitorTableDocument> getTables() {
return tables;
}
public void setTables(List<MonitorTableDocument> tables) {
this.tables = tables;
}
}

View File

@@ -0,0 +1,166 @@
package com.salpa.subject.domain;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* 数据库类型表 monitor_database_type
*
* @author zhuff
*/
public class MonitorDatabaseType {
/** id */
private Integer id;
/** 数据库类型 */
private String databaseType;
/** 图像 */
private String icon;
/** 描述 */
private String description;
/** jdbc文件网络地址 */
private String jdbcDriverFileUrl;
/** jdbc文件路径 */
private String jdbcDriverFilePath;
/** jdbc名称 */
private String jdbcDriverClassName;
/** jdbcProtocol */
private String jdbcProtocol;
/** urlPattern */
private String urlPattern;
/** 删除标识 */
private boolean deleted;
/** 删除token */
private Integer deletedToken;
/** 修改时间 */
private LocalDateTime updateAt;
/** 创建时间 */
private LocalDateTime createAt;
/*业务字段,项目数量*/
private Integer projectCount;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDatabaseType() {
return databaseType;
}
public void setDatabaseType(String databaseType) {
this.databaseType = databaseType;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getJdbcDriverFileUrl() {
return jdbcDriverFileUrl;
}
public void setJdbcDriverFileUrl(String jdbcDriverFileUrl) {
this.jdbcDriverFileUrl = jdbcDriverFileUrl;
}
public String getJdbcDriverFilePath() {
return jdbcDriverFilePath;
}
public void setJdbcDriverFilePath(String jdbcDriverFilePath) {
this.jdbcDriverFilePath = jdbcDriverFilePath;
}
public String getJdbcDriverClassName() {
return jdbcDriverClassName;
}
public void setJdbcDriverClassName(String jdbcDriverClassName) {
this.jdbcDriverClassName = jdbcDriverClassName;
}
public String getJdbcProtocol() {
return jdbcProtocol;
}
public void setJdbcProtocol(String jdbcProtocol) {
this.jdbcProtocol = jdbcProtocol;
}
public String getUrlPattern() {
return urlPattern;
}
public void setUrlPattern(String urlPattern) {
this.urlPattern = urlPattern;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public Integer getDeletedToken() {
return deletedToken;
}
public void setDeletedToken(Integer deletedToken) {
this.deletedToken = deletedToken;
}
public LocalDateTime getUpdateAt() {
return updateAt;
}
public void setUpdateAt(LocalDateTime updateAt) {
this.updateAt = updateAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public Integer getProjectCount() {
return projectCount;
}
public void setProjectCount(Integer projectCount) {
this.projectCount = projectCount;
}
}

View File

@@ -0,0 +1,90 @@
package com.salpa.subject.domain;
import java.time.LocalDateTime;
/**
* 描述表 monitor_document_description
* @author rhl
*/
public class MonitorDocumentDescription {
/** ID */
private Integer id;
/** 内容 */
private String content;
/** 项目ID */
private Integer projectId;
/** 表名 */
private String tableName;
/** 列表项 */
private String columnName;
/** 用户ID */
private Long updateBy;
/** 更新时间 */
private LocalDateTime updateAt;
/** 创建时间 */
private LocalDateTime createAt;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public Long getUpdateBy() {
return updateBy;
}
public void setUpdateBy(Long updateBy) {
this.updateBy = updateBy;
}
public LocalDateTime getUpdateAt() {
return updateAt;
}
public void setUpdateAt(LocalDateTime updateAt) {
this.updateAt = updateAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,135 @@
package com.salpa.subject.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
/**
* 数据库表 monitor_document_discussion
* @author hzl
*/
public class MonitorDocumentDiscussion {
/** id */
private Integer id;
/** 评论内容content */
private String content;
/** 用户id user_id */
private Long userId;
/** 项目id project_id */
private Integer projectId;
/** 表名称 table_name */
private String tableName;
/** 列名称 column_name */
private String columnName;
/** 创建时间 create_at */
/*时间格式化*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime createAt;
/** 当前记录起始索引 */
private Integer pageNum;
/** 每页显示记录数 */
private Integer pageSize;
/** 用户名称 */
private String userName;
/** 头像地址 */
private String avatar;
/** 登陆人 */
private String loginUser;
public String getLoginUser() {
return loginUser;
}
public void setLoginUser(String loginUser) {
this.loginUser = loginUser;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Integer getId() {
return id;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,100 @@
package com.salpa.subject.domain;
import com.salpa.subject.domain.data.DocumentTemplatePropertyType;
import java.time.LocalDateTime;
/**
* monitor_document_template_property 文档模板属性
*
* @author zhuff
*/
public class MonitorDocumentTemplateProperty {
private Integer id;
private String key;
private String value;
private String defaultValue;
private DocumentTemplatePropertyType type;
private LocalDateTime createAt;
public MonitorDocumentTemplateProperty() {}
public MonitorDocumentTemplateProperty(MonitorDocumentTemplateProperty value) {
this.id = value.id;
this.key = value.key;
this.value = value.value;
this.defaultValue = value.defaultValue;
this.type = value.type;
this.createAt = value.createAt;
}
public MonitorDocumentTemplateProperty(
Integer id,
String key,
String value,
String defaultValue,
DocumentTemplatePropertyType type,
LocalDateTime createAt
) {
this.id = id;
this.key = key;
this.value = value;
this.defaultValue = defaultValue;
this.type = type;
this.createAt = createAt;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public DocumentTemplatePropertyType getType() {
return type;
}
public void setType(DocumentTemplatePropertyType type) {
this.type = type;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,138 @@
package com.salpa.subject.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* 日志表 monitor_operation_log
*
* @author zhuff
*/
@Builder
public class MonitorOperationLog {
/*id*/
private Long id;
/*用户id*/
private Long operatorUserId;
/*用户名*/
private String operatorUsername;
/*用户昵称*/
private String operatorNickname;
/*所属模块*/
private String operationModule;
/*方法名*/
private String operationCode;
/*日志名称*/
private String operationName;
/*结果*/
private String operationResponse;
/*操作是否成功*/
private boolean success;
/*涉及项目*/
private Integer involvedProjectId;
/*创建时间*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime createAt;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getOperatorUserId() {
return operatorUserId;
}
public void setOperatorUserId(Long operatorUserId) {
this.operatorUserId = operatorUserId;
}
public String getOperatorUsername() {
return operatorUsername;
}
public void setOperatorUsername(String operatorUsername) {
this.operatorUsername = operatorUsername;
}
public String getOperatorNickname() {
return operatorNickname;
}
public void setOperatorNickname(String operatorNickname) {
this.operatorNickname = operatorNickname;
}
public String getOperationModule() {
return operationModule;
}
public void setOperationModule(String operationModule) {
this.operationModule = operationModule;
}
public String getOperationCode() {
return operationCode;
}
public void setOperationCode(String operationCode) {
this.operationCode = operationCode;
}
public String getOperationName() {
return operationName;
}
public void setOperationName(String operationName) {
this.operationName = operationName;
}
public String getOperationResponse() {
return operationResponse;
}
public void setOperationResponse(String operationResponse) {
this.operationResponse = operationResponse;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public Integer getInvolvedProjectId() {
return involvedProjectId;
}
public void setInvolvedProjectId(Integer involvedProjectId) {
this.involvedProjectId = involvedProjectId;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,108 @@
package com.salpa.subject.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.salpa.subject.domain.vo.ProjectSyncRule;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* 项目实体
*
* @author zhuff
*/
public class MonitorProject implements Serializable {
private static final long serialVersionUID = 1L;
/** 项目ID */
private Integer id;
/** 项目名称 */
private String projectName;
/** 项目名称 */
private String projectDescription;
/** 主题id */
private Integer subjectId;
/** 删除标识 */
private boolean deleted;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime createAt;
/** 数据库 */
private MonitorDataSource monitorDataSource;
/** 数据库高级设置 */
private ProjectSyncRule projectSyncRule;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectDescription() {
return projectDescription;
}
public void setProjectDescription(String projectDescription) {
this.projectDescription = projectDescription;
}
public Integer getSubjectId() {
return subjectId;
}
public void setSubjectId(Integer subjectId) {
this.subjectId = subjectId;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public MonitorDataSource getMonitorDataSource() {
return monitorDataSource;
}
public void setMonitorDataSource(MonitorDataSource monitorDataSource) {
this.monitorDataSource = monitorDataSource;
}
public ProjectSyncRule getProjectSyncRule() {
return projectSyncRule;
}
public void setProjectSyncRule(ProjectSyncRule projectSyncRule) {
this.projectSyncRule = projectSyncRule;
}
}

View File

@@ -0,0 +1,118 @@
package com.salpa.subject.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Arrays;
/**
* 数据库配置表 monitor_project_sync_rule
*
* @author zhuff
*/
public class MonitorProjectSyncRule implements Serializable {
private static final long serialVersionUID = 1L;
/** ID */
private Integer id;
/** 项目ID */
private Integer projectId;
/** 忽略表名称 */
private String ignoreTableNameRegexArray;
/** 忽略字段名称 */
private String ignoreColumnNameRegexArray;
/** 是否定时同步 */
private boolean autoSync;
/** cron表达式 */
private String autoSyncCron;
/** 修改时间 */
private LocalDateTime updateAt;
/** 创建时间 */
private LocalDateTime createAt;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getIgnoreTableNameRegexArray() {
return ignoreTableNameRegexArray;
}
public void setIgnoreTableNameRegexArray(String ignoreTableNameRegexArray) {
this.ignoreTableNameRegexArray = ignoreTableNameRegexArray;
}
public String getIgnoreColumnNameRegexArray() {
return ignoreColumnNameRegexArray;
}
public void setIgnoreColumnNameRegexArray(String ignoreColumnNameRegexArray) {
this.ignoreColumnNameRegexArray = ignoreColumnNameRegexArray;
}
public boolean isAutoSync() {
return autoSync;
}
public void setAutoSync(boolean autoSync) {
this.autoSync = autoSync;
}
public String getAutoSyncCron() {
return autoSyncCron;
}
public void setAutoSyncCron(String autoSyncCron) {
this.autoSyncCron = autoSyncCron;
}
public LocalDateTime getUpdateAt() {
return updateAt;
}
public void setUpdateAt(LocalDateTime updateAt) {
this.updateAt = updateAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
@Override
public String toString() {
return "MonitorProjectSyncRule{" +
"id=" + id +
", projectId=" + projectId +
", ignoreTableNameRegexArray='" + ignoreTableNameRegexArray + '\'' +
", ignoreColumnNameRegexArray='" + ignoreColumnNameRegexArray + '\'' +
", autoSync=" + autoSync +
", autoSyncCron='" + autoSyncCron + '\'' +
", updateAt=" + updateAt +
", createAt=" + createAt +
'}';
}
}

View File

@@ -0,0 +1,106 @@
package com.salpa.subject.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 数据库配置表 monitor_project_sync_task
*
* @author zhuff
*/
public class MonitorProjectSyncTask {
/** ID */
private Integer id;
/** 项目ID */
private Integer projectId;
/** 用户id */
private Long userId;
/** 状态 */
private String status;
/** 同步结果 */
private String result;
/** 同步时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private Date runAt;
/** 修改时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime updateAt;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime createAt;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Date getRunAt() {
return runAt;
}
public void setRunAt(Date runAt) {
this.runAt = runAt;
}
public LocalDateTime getUpdateAt() {
return updateAt;
}
public void setUpdateAt(LocalDateTime updateAt) {
this.updateAt = updateAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,82 @@
package com.salpa.subject.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* 主题管理表 monitor_subject
*
* @author zhuff
*/
public class MonitorSubject implements Serializable {
private static final long serialVersionUID = 1L;
/** ID */
private Integer id;
/** 主题名称 */
private String subjectName;
/** 主题描述 */
private String subjectDescription;
/** 删除标识 */
private boolean deleted;
/** 创建时间 */
private LocalDateTime createAt;
/** 形目数量 */
private Integer projectCount;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSubjectName() {
return subjectName;
}
public void setSubjectName(String subjectName) {
this.subjectName = subjectName;
}
public String getSubjectDescription() {
return subjectDescription;
}
public void setSubjectDescription(String subjectDescription) {
this.subjectDescription = subjectDescription;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public Integer getProjectCount() {
return projectCount;
}
public void setProjectCount(Integer projectCount) {
this.projectCount = projectCount;
}
}

View File

@@ -0,0 +1,206 @@
package com.salpa.subject.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* monitor_table_column_document
* @author zhuff
*/
public class MonitorTableColumnDocument implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private String name;
private String type;
private Integer dataType;
private String comment;
private String defaultValue;
private Integer size;
private Integer decimalDigits;
private Boolean primaryKey;
private String nullable;
private String autoIncrement;
private LocalDateTime createAt;
public MonitorTableColumnDocument() {}
public MonitorTableColumnDocument(MonitorTableColumnDocument value) {
this.id = value.id;
this.tableDocumentId = value.tableDocumentId;
this.databaseDocumentId = value.databaseDocumentId;
this.name = value.name;
this.type = value.type;
this.dataType = value.dataType;
this.comment = value.comment;
this.defaultValue = value.defaultValue;
this.size = value.size;
this.decimalDigits = value.decimalDigits;
this.primaryKey = value.primaryKey;
this.nullable = value.nullable;
this.autoIncrement = value.autoIncrement;
this.createAt = value.createAt;
}
public MonitorTableColumnDocument(
Integer id,
Integer tableDocumentId,
Integer databaseDocumentId,
String name,
String type,
Integer dataType,
String comment,
String defaultValue,
Integer size,
Integer decimalDigits,
Boolean primaryKey,
String nullable,
String autoIncrement,
LocalDateTime createAt
) {
this.id = id;
this.tableDocumentId = tableDocumentId;
this.databaseDocumentId = databaseDocumentId;
this.name = name;
this.type = type;
this.dataType = dataType;
this.comment = comment;
this.defaultValue = defaultValue;
this.size = size;
this.decimalDigits = decimalDigits;
this.primaryKey = primaryKey;
this.nullable = nullable;
this.autoIncrement = autoIncrement;
this.createAt = createAt;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getTableDocumentId() {
return tableDocumentId;
}
public void setTableDocumentId(Integer tableDocumentId) {
this.tableDocumentId = tableDocumentId;
}
public Integer getDatabaseDocumentId() {
return databaseDocumentId;
}
public void setDatabaseDocumentId(Integer databaseDocumentId) {
this.databaseDocumentId = databaseDocumentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getDataType() {
return dataType;
}
public void setDataType(Integer dataType) {
this.dataType = dataType;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public Integer getDecimalDigits() {
return decimalDigits;
}
public void setDecimalDigits(Integer decimalDigits) {
this.decimalDigits = decimalDigits;
}
public Boolean getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(Boolean primaryKey) {
this.primaryKey = primaryKey;
}
public String getNullable() {
return nullable;
}
public void setNullable(String nullable) {
this.nullable = nullable;
}
public String getAutoIncrement() {
return autoIncrement;
}
public void setAutoIncrement(String autoIncrement) {
this.autoIncrement = autoIncrement;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,173 @@
package com.salpa.subject.domain;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class MonitorTableDocument {
private Integer id;
private Integer databaseDocumentId;
private String name;
private String type;
private String comment;
private LocalDateTime createAt;
public MonitorTableDocument() {};
public MonitorTableDocument(MonitorTableDocument value) {
this.id = value.id;
this.databaseDocumentId = value.databaseDocumentId;
this.name = value.name;
this.type = value.type;
this.comment = value.comment;
this.createAt = value.createAt;
}
public MonitorTableDocument(
Integer id,
Integer databaseDocumentId,
String name,
String type,
String comment,
LocalDateTime createAt
) {
this.id = id;
this.databaseDocumentId = databaseDocumentId;
this.name = name;
this.type = type;
this.comment = comment;
this.createAt = createAt;
}
/*描述*/
private String description;
/*评论条数*/
private Integer discussionCount;
private List<MonitorTableColumnDocument> columns;
private List<MonitorTableIndexDocument> indexes;
private List<MonitorTableForeignKeyDocument> foreignKeys;
private List<MonitorTableTriggerDocument> triggers;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getDatabaseDocumentId() {
return databaseDocumentId;
}
public void setDatabaseDocumentId(Integer databaseDocumentId) {
this.databaseDocumentId = databaseDocumentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getDiscussionCount() {
return discussionCount;
}
public void setDiscussionCount(Integer discussionCount) {
this.discussionCount = discussionCount;
}
public List<MonitorTableColumnDocument> getColumns() {
return columns;
}
public void setColumns(List<MonitorTableColumnDocument> columns) {
this.columns = columns;
}
public List<MonitorTableIndexDocument> getIndexes() {
return indexes;
}
public void setIndexes(List<MonitorTableIndexDocument> indexes) {
this.indexes = indexes;
}
public List<MonitorTableForeignKeyDocument> getForeignKeys() {
return foreignKeys;
}
public void setForeignKeys(List<MonitorTableForeignKeyDocument> foreignKeys) {
this.foreignKeys = foreignKeys;
}
public List<MonitorTableTriggerDocument> getTriggers() {
return triggers;
}
public void setTriggers(List<MonitorTableTriggerDocument> triggers) {
this.triggers = triggers;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("TableDocument (");
sb.append(id);
sb.append(", ").append(databaseDocumentId);
sb.append(", ").append(name);
sb.append(", ").append(type);
sb.append(", ").append(comment);
sb.append(", ").append(createAt);
sb.append(")");
return sb.toString();
}
}

View File

@@ -0,0 +1,193 @@
package com.salpa.subject.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* monitor_table_foreign_key_document
* @author zhuff
*/
public class MonitorTableForeignKeyDocument implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private Integer keySeq;
private String fkName;
private String fkTableName;
private String fkColumnName;
private String pkName;
private String pkTableName;
private String pkColumnName;
private String updateRule;
private String deleteRule;
private LocalDateTime createAt;
public MonitorTableForeignKeyDocument() {}
public MonitorTableForeignKeyDocument(MonitorTableForeignKeyDocument value) {
this.id = value.id;
this.tableDocumentId = value.tableDocumentId;
this.databaseDocumentId = value.databaseDocumentId;
this.keySeq = value.keySeq;
this.fkName = value.fkName;
this.fkTableName = value.fkTableName;
this.fkColumnName = value.fkColumnName;
this.pkName = value.pkName;
this.pkTableName = value.pkTableName;
this.pkColumnName = value.pkColumnName;
this.updateRule = value.updateRule;
this.deleteRule = value.deleteRule;
this.createAt = value.createAt;
}
public MonitorTableForeignKeyDocument(
Integer id,
Integer tableDocumentId,
Integer databaseDocumentId,
Integer keySeq,
String fkName,
String fkTableName,
String fkColumnName,
String pkName,
String pkTableName,
String pkColumnName,
String updateRule,
String deleteRule,
LocalDateTime createAt
) {
this.id = id;
this.tableDocumentId = tableDocumentId;
this.databaseDocumentId = databaseDocumentId;
this.keySeq = keySeq;
this.fkName = fkName;
this.fkTableName = fkTableName;
this.fkColumnName = fkColumnName;
this.pkName = pkName;
this.pkTableName = pkTableName;
this.pkColumnName = pkColumnName;
this.updateRule = updateRule;
this.deleteRule = deleteRule;
this.createAt = createAt;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getTableDocumentId() {
return tableDocumentId;
}
public void setTableDocumentId(Integer tableDocumentId) {
this.tableDocumentId = tableDocumentId;
}
public Integer getDatabaseDocumentId() {
return databaseDocumentId;
}
public void setDatabaseDocumentId(Integer databaseDocumentId) {
this.databaseDocumentId = databaseDocumentId;
}
public Integer getKeySeq() {
return keySeq;
}
public void setKeySeq(Integer keySeq) {
this.keySeq = keySeq;
}
public String getFkName() {
return fkName;
}
public void setFkName(String fkName) {
this.fkName = fkName;
}
public String getFkTableName() {
return fkTableName;
}
public void setFkTableName(String fkTableName) {
this.fkTableName = fkTableName;
}
public String getFkColumnName() {
return fkColumnName;
}
public void setFkColumnName(String fkColumnName) {
this.fkColumnName = fkColumnName;
}
public String getPkName() {
return pkName;
}
public void setPkName(String pkName) {
this.pkName = pkName;
}
public String getPkTableName() {
return pkTableName;
}
public void setPkTableName(String pkTableName) {
this.pkTableName = pkTableName;
}
public String getPkColumnName() {
return pkColumnName;
}
public void setPkColumnName(String pkColumnName) {
this.pkColumnName = pkColumnName;
}
public String getUpdateRule() {
return updateRule;
}
public void setUpdateRule(String updateRule) {
this.updateRule = updateRule;
}
public String getDeleteRule() {
return deleteRule;
}
public void setDeleteRule(String deleteRule) {
this.deleteRule = deleteRule;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,127 @@
package com.salpa.subject.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* @author zhuff
* monitor_table_index_document
*/
public class MonitorTableIndexDocument implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private String name;
private Boolean unique;
private String columnNameArray;
private LocalDateTime createAt;
/*业务字段*/
private Object columnNameArrays;
public MonitorTableIndexDocument() {}
public MonitorTableIndexDocument(MonitorTableIndexDocument value) {
this.id = value.id;
this.tableDocumentId = value.tableDocumentId;
this.databaseDocumentId = value.databaseDocumentId;
this.name = value.name;
this.unique = value.unique;
this.columnNameArray = value.columnNameArray;
this.createAt = value.createAt;
}
public MonitorTableIndexDocument(
Integer id,
Integer tableDocumentId,
Integer databaseDocumentId,
String name,
Boolean unique,
String columnNameArray,
LocalDateTime createAt
) {
this.id = id;
this.tableDocumentId = tableDocumentId;
this.databaseDocumentId = databaseDocumentId;
this.name = name;
this.unique = unique;
this.columnNameArray = columnNameArray;
this.createAt = createAt;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getTableDocumentId() {
return tableDocumentId;
}
public void setTableDocumentId(Integer tableDocumentId) {
this.tableDocumentId = tableDocumentId;
}
public Integer getDatabaseDocumentId() {
return databaseDocumentId;
}
public void setDatabaseDocumentId(Integer databaseDocumentId) {
this.databaseDocumentId = databaseDocumentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getUnique() {
return unique;
}
public void setUnique(Boolean unique) {
this.unique = unique;
}
public String getColumnNameArray() {
return columnNameArray;
}
public void setColumnNameArray(String columnNameArray) {
this.columnNameArray = columnNameArray;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
public Object getColumnNameArrays() {
return columnNameArrays;
}
public void setColumnNameArrays(Object columnNameArrays) {
this.columnNameArrays = columnNameArrays;
}
}

View File

@@ -0,0 +1,141 @@
package com.salpa.subject.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* monitor_table_trigger_document
* @author zhuff
*/
public class MonitorTableTriggerDocument implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private String timing;
private String manipulation;
private String statement;
private String triggerCreateAt;
private LocalDateTime createAt;
public MonitorTableTriggerDocument() {}
public MonitorTableTriggerDocument(MonitorTableTriggerDocument value) {
this.id = value.id;
this.name = value.name;
this.tableDocumentId = value.tableDocumentId;
this.databaseDocumentId = value.databaseDocumentId;
this.timing = value.timing;
this.manipulation = value.manipulation;
this.statement = value.statement;
this.triggerCreateAt = value.triggerCreateAt;
this.createAt = value.createAt;
}
public MonitorTableTriggerDocument(
Integer id,
String name,
Integer tableDocumentId,
Integer databaseDocumentId,
String timing,
String manipulation,
String statement,
String triggerCreateAt,
LocalDateTime createAt
) {
this.id = id;
this.name = name;
this.tableDocumentId = tableDocumentId;
this.databaseDocumentId = databaseDocumentId;
this.timing = timing;
this.manipulation = manipulation;
this.statement = statement;
this.triggerCreateAt = triggerCreateAt;
this.createAt = createAt;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getTableDocumentId() {
return tableDocumentId;
}
public void setTableDocumentId(Integer tableDocumentId) {
this.tableDocumentId = tableDocumentId;
}
public Integer getDatabaseDocumentId() {
return databaseDocumentId;
}
public void setDatabaseDocumentId(Integer databaseDocumentId) {
this.databaseDocumentId = databaseDocumentId;
}
public String getTiming() {
return timing;
}
public void setTiming(String timing) {
this.timing = timing;
}
public String getManipulation() {
return manipulation;
}
public void setManipulation(String manipulation) {
this.manipulation = manipulation;
}
public String getStatement() {
return statement;
}
public void setStatement(String statement) {
this.statement = statement;
}
public String getTriggerCreateAt() {
return triggerCreateAt;
}
public void setTriggerCreateAt(String triggerCreateAt) {
this.triggerCreateAt = triggerCreateAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
public void setCreateAt(LocalDateTime createAt) {
this.createAt = createAt;
}
}

View File

@@ -0,0 +1,40 @@
package com.salpa.subject.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 数据库类型表 monitor_waening
*
* @author zff
*/
@Data
public class MonitorWarning {
/** ID */
private Integer id;
/** 告警名称 */
private String warningName;
/** 所属主题模型 */
private String warningSubject;
/** 所属数据库 */
private String warningDataSource;
/** 告警时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date warningTime;
/** 告警内容 */
private String warningContent;
/** 告警状态 */
private Integer warningStatus;
/** 告警处理人 */
private String handlingUser;
}

View File

@@ -0,0 +1,81 @@
package com.salpa.subject.domain.converter;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.salpa.common.core.domain.JsonData;
import com.salpa.subject.domain.data.DatabaseDocumentResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Component
public class JsonConverter {
@Autowired
private ObjectMapper objectMapper;
public List<String> fromJson(String data) {
if (data == null) {
return Collections.emptyList();
} else {
return Arrays.asList(data.split(","));
}
}
public JSONObject toJson(List<String> array) {
String json = objToJson(array);
return JSONObject.parseObject(json);
}
public JSONObject toJson(DatabaseDocumentResponse response) {
String json = objToJson(response);
return JSONObject.parseObject(json);
}
public JSONObject toJson(JsonData<Object> data) {
String json = objToJson(data);
return JSONObject.parseObject(json);
}
public DatabaseDocumentResponse of(JSONObject json) {
try {
if (json == null) {
return null;
}
return objectMapper.readValue(json.toString().getBytes(StandardCharsets.UTF_8), DatabaseDocumentResponse.class);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
public JsonData toJsonData(JSONObject json) {
try {
if (json == null) {
return null;
}
return objectMapper.readValue(json.toString().getBytes(StandardCharsets.UTF_8), JsonData.class);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
public JSONObject objToJsonData(Object obj) {
String json = objToJson(obj);
return JSONObject.parseObject(json);
}
private String objToJson(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@@ -0,0 +1,36 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ColumnMeta {
private String name;
private String comment;
private String type;
private Integer dataType;
/**
* if default value is empty string, will be converted to ''.
*/
private String defaultValue;
private Integer size;
private Integer decimalDigits;
private String nullable;
private String autoIncrement;
private Boolean isPrimaryKey;
}

View File

@@ -0,0 +1,37 @@
package com.salpa.subject.domain.data;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DatabaseDocumentResponse {
private Integer id;
private String databaseName;
private String schemaName;
private String productName;
private String productVersion;
private Integer documentVersion;
@Builder.Default
private List<TableDocumentResponse> tables = new ArrayList<>();
private LocalDateTime createAt;
private DiffType diffType;
}

View File

@@ -0,0 +1,59 @@
package com.salpa.subject.domain.data;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.salpa.subject.domain.MonitorTableColumnDocument;
import com.salpa.subject.domain.diff.DiffAble;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
public class DatabaseDocumentSimpleResponse {
private Integer id;
private String projectName;
private String databaseName;
private String schemaName;
private String productName;
private String productVersion;
private Long documentVersion;
private List<TableData> tables = new ArrayList<>();
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT +8")
private LocalDateTime createAt;
private DiffType diffType;
@Data
public static class TableData implements DiffAble<TableData> {
private Integer id;
private String name;
private String type;
private String comment;
private List<MonitorTableColumnDocument> columns = new ArrayList<>();
private Integer discussionCount;
private String description;
private DiffType diffType;
private TableData original;
}
}

View File

@@ -0,0 +1,50 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DatabaseMeta {
/**
* product_name
*/
private String productName;
/**
* product_version
*/
private String productVersion;
/**
* driver_name
*/
private String driverName;
/**
* driver_version
*/
private String driverVersion;
/**
* database_name
*/
private String databaseName;
/**
* schema_name
*/
private String schemaName;
@Builder.Default
private List<TableMeta> tables = Collections.emptyList();
}

View File

@@ -0,0 +1,58 @@
package com.salpa.subject.domain.data;
import com.salpa.subject.domain.provider.MetaProviders;
import com.salpa.subject.domain.provider.condition.Condition;
import com.salpa.subject.domain.render.Render;
import com.salpa.subject.domain.render.RenderConfig;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Getter
public class Databasir {
private final DatabasirConfig config;
public Optional<DatabaseMeta> get(Connection connection, String databaseName, String schemaName) {
// pre compile regex
List<Pattern> ignoreTableColumnPatterns = config.getIgnoreTableColumnNameRegex().stream()
.map(Pattern::compile)
.collect(Collectors.toList());
List<Pattern> ignoreTableNamePatterns = config.getIgnoreTableNameRegex().stream()
.map(Pattern::compile)
.collect(Collectors.toList());
Condition condition = Condition.builder()
.databaseName(databaseName)
.schemaName(schemaName)
.ignoreTableNamePatterns(ignoreTableNamePatterns)
.ignoreTableColumnNamePatterns(ignoreTableColumnPatterns)
.build();
return MetaProviders
.of(connection)
.select(connection, condition);
}
public void renderAsMarkdown(DatabaseMeta meta, OutputStream out) throws IOException {
renderAsMarkdown(new RenderConfig(), meta, out);
}
public void renderAsMarkdown(RenderConfig config, DatabaseMeta meta, OutputStream stream) throws IOException {
Render.markdownRender(config).rendering(meta, stream);
}
public static Databasir of() {
return of(new DatabasirConfig());
}
public static Databasir of(DatabasirConfig config) {
return new Databasir(config);
}
}

View File

@@ -0,0 +1,26 @@
package com.salpa.subject.domain.data;
import lombok.Getter;
import lombok.Setter;
import java.util.Collection;
import java.util.HashSet;
@Getter
@Setter
public class DatabasirConfig {
private Collection<String> ignoreTableNameRegex = new HashSet<>();
private Collection<String> ignoreTableColumnNameRegex = new HashSet<>();
public DatabasirConfig ignoreTable(String tableNameRegex) {
ignoreTableNameRegex.add(tableNameRegex);
return this;
}
public DatabasirConfig ignoreColumn(String columnNameRegex) {
ignoreTableColumnNameRegex.add(columnNameRegex);
return this;
}
}

View File

@@ -0,0 +1,47 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DocumentTemplatePropertiesResponse {
@Builder.Default
private List<DocumentTemplatePropertyResponse> tableFieldNameProperties = Collections.emptyList();
@Builder.Default
private List<DocumentTemplatePropertyResponse> columnFieldNameProperties = Collections.emptyList();
@Builder.Default
private List<DocumentTemplatePropertyResponse> indexFieldNameProperties = Collections.emptyList();
@Builder.Default
private List<DocumentTemplatePropertyResponse> triggerFieldNameProperties = Collections.emptyList();
@Builder.Default
private List<DocumentTemplatePropertyResponse> foreignKeyFieldNameProperties = Collections.emptyList();
@Data
public static class DocumentTemplatePropertyResponse {
private String key;
private String value;
private String defaultValue;
private DocumentTemplatePropertyType type;
private LocalDateTime createAt;
}
}

View File

@@ -0,0 +1,7 @@
package com.salpa.subject.domain.data;
public enum DocumentTemplatePropertyType {
TABLE_FIELD_NAME, INDEX_FIELD_NAME, COLUMN_FIELD_NAME, TRIGGER_FIELD_NAME, FOREIGN_KEY_FIELD_NAME;
}

View File

@@ -0,0 +1,32 @@
package com.salpa.subject.domain.data;
import com.salpa.subject.domain.diff.data.RootDiff;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Optional;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DocumentUpdated {
private RootDiff diff;
private Long newVersion;
private Long oldVersion;
private Integer projectId;
private Integer databaseDocumentId;
public Optional<Long> getOldVersion() {
return Optional.ofNullable(oldVersion);
}
public Optional<RootDiff> getDiff() {
return Optional.ofNullable(diff);
}
}

View File

@@ -0,0 +1,43 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ForeignKeyMeta {
private Integer keySeq;
/**
* may null
*/
private String pkName;
private String pkTableName;
private String pkColumnName;
/**
* may null
*/
private String fkName;
private String fkTableName;
private String fkColumnName;
/**
* NO_ACTION \ CASCADE \ SET_NULL \ SET_DEFAULT
*/
private String updateRule;
/**
* NO_ACTION \ CASCADE \ SET_NULL \ SET_DEFAULT
*/
private String deleteRule;
}

View File

@@ -0,0 +1,23 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IndexMeta {
private String name;
@Builder.Default
private List<String> columnNames = Collections.emptyList();
private Boolean isUniqueKey;
}

View File

@@ -0,0 +1,10 @@
package com.salpa.subject.domain.data;
public enum ProjectSyncTaskStatus {
NEW,
RUNNING,
FINISHED,
FAILED,
CANCELED
}

View File

@@ -0,0 +1,170 @@
package com.salpa.subject.domain.data;
import com.salpa.subject.domain.diff.DiffAble;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TableDocumentResponse {
private Integer id;
private Integer databaseDocumentId;
private String name;
private String type;
private String comment;
private Integer discussionCount;
private String description;
@Builder.Default
private List<ColumnDocumentResponse> columns = new ArrayList<>();
@Builder.Default
private List<IndexDocumentResponse> indexes = new ArrayList<>();
@Builder.Default
private List<ForeignKeyDocumentResponse> foreignKeys = new ArrayList<>();
@Builder.Default
private List<TriggerDocumentResponse> triggers = new ArrayList<>();
private LocalDateTime createAt;
private DiffType diffType;
private TableDocumentResponse original;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ColumnDocumentResponse implements DiffAble<ColumnDocumentResponse> {
private Integer id;
private String name;
private String type;
private Integer size;
private Integer decimalDigits;
private String comment;
private String description;
private Boolean isPrimaryKey;
private String nullable;
private String autoIncrement;
private String defaultValue;
private LocalDateTime createAt;
private DiffType diffType;
private Integer discussionCount;
private ColumnDocumentResponse original;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class IndexDocumentResponse implements DiffAble<IndexDocumentResponse> {
private Integer id;
private String name;
private Boolean isUnique;
@Builder.Default
private List<String> columnNames = new ArrayList<>();
private String columnNameArray;
private LocalDateTime createAt;
private DiffType diffType;
private IndexDocumentResponse original;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ForeignKeyDocumentResponse implements DiffAble<ForeignKeyDocumentResponse> {
private Integer id;
private String fkName;
private String fkTableName;
private String fkColumnName;
private Integer keySeq;
private String pkName;
private String pkTableName;
private String pkColumnName;
private String updateRule;
private String deleteRule;
private LocalDateTime createAt;
private DiffType diffType;
private ForeignKeyDocumentResponse original;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class TriggerDocumentResponse implements DiffAble<TriggerDocumentResponse> {
private Integer id;
private String name;
private String timing;
private String manipulation;
private String statement;
private String triggerCreateAt;
private LocalDateTime createAt;
private DiffType diffType;
private TriggerDocumentResponse original;
}
}

View File

@@ -0,0 +1,34 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TableMeta {
private String name;
private String type;
private String comment;
@Builder.Default
private List<ColumnMeta> columns = Collections.emptyList();
@Builder.Default
private List<TriggerMeta> triggers = Collections.emptyList();
@Builder.Default
private List<IndexMeta> indexes = Collections.emptyList();
@Builder.Default
private List<ForeignKeyMeta> foreignKeys = Collections.emptyList();
}

View File

@@ -0,0 +1,32 @@
package com.salpa.subject.domain.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* now: only support mysql, postgresql.
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TriggerMeta {
private String name;
/**
* example: BEFORE, AFTER
*/
private String timing;
/**
* example: INSERT, UPDATE
*/
private String manipulation;
private String statement;
private String createAt;
}

View File

@@ -0,0 +1,82 @@
package com.salpa.subject.domain.diff;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Slf4j
public class BaseTypeFieldEqualFunction implements BiFunction<Object, Object, Boolean> {
private final List<String> ignoreFields;
@Override
public Boolean apply(Object that, Object other) {
if (Objects.equals(that, other)) {
return true;
}
if (that == null || other == null) {
return false;
}
try {
BeanInfo thatBean = Introspector.getBeanInfo(that.getClass());
BeanInfo otherBean = Introspector.getBeanInfo(other.getClass());
Map<String, PropertyDescriptor> otherBeanPropertyMap = Arrays.stream(otherBean.getPropertyDescriptors())
.collect(Collectors.toMap(PropertyDescriptor::getName, p -> p));
for (PropertyDescriptor thatProperty : thatBean.getPropertyDescriptors()) {
if (thatProperty.getReadMethod() == null || thatProperty.getWriteMethod() == null) {
continue;
}
if (ignoreFields.contains(thatProperty.getName())) {
continue;
}
if (!otherBeanPropertyMap.containsKey(thatProperty.getName())) {
return false;
}
if (Collection.class.isAssignableFrom(thatProperty.getPropertyType())) {
Collection thatValue = (Collection) thatProperty.getReadMethod().invoke(that);
Collection otherValue = (Collection) otherBeanPropertyMap.get(thatProperty.getName())
.getReadMethod().invoke(other);
return handleCollection(thatValue, otherValue);
}
if (!thatProperty.getPropertyType().isPrimitive()) {
Object thatValue = thatProperty.getReadMethod().invoke(that);
Object otherValue = otherBeanPropertyMap.get(thatProperty.getName()).getReadMethod().invoke(other);
if (!apply(thatValue, otherValue)) {
return false;
}
}
Object thatValue = thatProperty.getReadMethod().invoke(that);
Object otherValue = otherBeanPropertyMap.get(thatProperty.getName()).getReadMethod().invoke(other);
if (!Objects.equals(thatValue, otherValue)) {
return false;
}
}
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
log.error("Error comparing objects", e);
throw new RuntimeException(e);
}
return true;
}
private boolean handleCollection(Collection<Object> that, Collection<Object> other) {
if (that.size() != other.size()) {
return false;
}
for (Object thatObj : that) {
boolean anyMatch = other.stream().anyMatch(otherObj -> this.apply(thatObj, otherObj));
if (!anyMatch) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,42 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ColumnDocDiff implements DiffAble<ColumnDocDiff> {
private Integer id;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private String name;
private String type;
private Integer dataType;
private String comment;
private String defaultValue;
private Integer size;
private Integer decimalDigits;
private Boolean isPrimaryKey;
private String nullable;
private String autoIncrement;
private LocalDateTime createAt;
private DiffType diffType;
private ColumnDocDiff original;
}

View File

@@ -0,0 +1,32 @@
package com.salpa.subject.domain.diff;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@Data
public class DatabaseDocDiff {
private Integer id;
private Integer projectId;
private String databaseName;
private String schemaName;
private String productName;
private String productVersion;
private Long version;
private List<TableDocDiff> tables = Collections.emptyList();
private LocalDateTime updateAt;
private LocalDateTime createAt;
}

View File

@@ -0,0 +1,10 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.diff.data.DiffType;
public interface DiffAble<T> {
void setDiffType(DiffType diffType);
void setOriginal(T t);
}

View File

@@ -0,0 +1,32 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.data.DatabaseDocumentSimpleResponse;
import com.salpa.subject.domain.diff.data.DiffType;
import java.util.List;
public class DiffTypePredictor {
public static DiffType predict(List<DatabaseDocumentSimpleResponse.TableData> result) {
long changedItemSize = result.stream()
.filter(item -> !item.getDiffType().isNone())
.count();
long addedItemSize = result.stream()
.filter(item -> !item.getDiffType().isNone())
.filter(item -> item.getDiffType().isAdded())
.count();
long removedItemSize = result.stream()
.filter(item -> !item.getDiffType().isNone())
.filter(item -> item.getDiffType().isRemoved())
.count();
if (changedItemSize > 0 && addedItemSize == changedItemSize) {
return DiffType.ADDED;
} else if (changedItemSize > 0 && removedItemSize == changedItemSize) {
return DiffType.REMOVED;
} else {
return result.stream()
.anyMatch(t -> t.getDiffType() != DiffType.NONE) ? DiffType.MODIFIED : DiffType.NONE;
}
}
}

View File

@@ -0,0 +1,17 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.data.DatabaseMeta;
import com.salpa.subject.domain.diff.data.RootDiff;
import com.salpa.subject.domain.diff.processor.DatabaseDiffProcessor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Diffs {
private static final DatabaseDiffProcessor databaseDiffProcessor = new DatabaseDiffProcessor();
public static RootDiff diff(DatabaseMeta original, DatabaseMeta current) {
return databaseDiffProcessor.process(original, current);
}
}

View File

@@ -0,0 +1,230 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
@Slf4j
public class DocumentDiffChecker {
private static final List<String> IGNORE_FIELDS = List.of(
"id",
"class",
"tableDocumentId",
"databaseDocumentId",
"createAt",
"updateAt",
"diffType",
"original"
);
public List<TableDocDiff> diff(List<TableDocDiff> original, List<TableDocDiff> current) {
Map<String, TableDocDiff> originalTablesMap = toMap(original, TableDocDiff::getName);
Map<String, TableDocDiff> currentTablesMap = toMap(current, TableDocDiff::getName);
// added items
var added = added(originalTablesMap, currentTablesMap)
.stream()
.map(curr -> {
List<ColumnDocDiff> columnDiffs =
doDiff(Collections.emptyList(), curr.getColumns(), ColumnDocDiff::getName);
columnDiffs.sort(Comparator.comparingInt(c -> c.getId()));
List<IndexDocDiff> indexDiffs =
doDiff(Collections.emptyList(), curr.getIndexes(), IndexDocDiff::getName);
List<TriggerDocDiff> triggerDiffs =
doDiff(Collections.emptyList(), curr.getTriggers(), TriggerDocDiff::getName);
List<ForeignKeyDocDiff> fkDiffs =
doDiff(Collections.emptyList(),
curr.getForeignKeys(),
fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq());
return TableDocDiff.builder()
.id(curr.getId())
.diffType(DiffType.ADDED)
.name(curr.getName())
.comment(curr.getComment())
.type(curr.getType())
.createAt(curr.getCreateAt())
.columns(columnDiffs)
.indexes(indexDiffs)
.triggers(triggerDiffs)
.foreignKeys(fkDiffs)
.build();
})
.collect(Collectors.toList());
// removed items
var removed = removed(originalTablesMap, currentTablesMap)
.stream()
.map(old -> {
List<ColumnDocDiff> columnDiffs =
doDiff(old.getColumns(), Collections.emptyList(), ColumnDocDiff::getName);
columnDiffs.sort(Comparator.comparingInt(c -> c.getId()));
List<IndexDocDiff> indexDiffs =
doDiff(old.getIndexes(), Collections.emptyList(), IndexDocDiff::getName);
List<TriggerDocDiff> triggerDiffs =
doDiff(old.getTriggers(), Collections.emptyList(), TriggerDocDiff::getName);
List<ForeignKeyDocDiff> fkDiffs =
doDiff(old.getForeignKeys(),
Collections.emptyList(),
fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq());
return TableDocDiff.builder()
.id(old.getId())
.diffType(DiffType.REMOVED)
.name(old.getName())
.comment(old.getComment())
.type(old.getType())
.createAt(old.getCreateAt())
.columns(columnDiffs)
.indexes(indexDiffs)
.triggers(triggerDiffs)
.foreignKeys(fkDiffs)
.build();
})
.collect(Collectors.toList());
// unchanged or modified items
List<TableDocDiff> sameOrModified = currentTablesMap.entrySet()
.stream()
.filter(entry -> originalTablesMap.containsKey(entry.getKey()))
.map(entry -> {
String tableName = entry.getKey();
TableDocDiff currentTable = entry.getValue();
TableDocDiff originalTable = originalTablesMap.get(tableName);
List<ColumnDocDiff> columnDiffs =
doDiff(originalTable.getColumns(), currentTable.getColumns(), ColumnDocDiff::getName);
columnDiffs.sort(Comparator.comparingInt(c -> c.getId()));
List<IndexDocDiff> indexDiffs =
doDiff(originalTable.getIndexes(), currentTable.getIndexes(), IndexDocDiff::getName);
List<TriggerDocDiff> triggerDiffs =
doDiff(originalTable.getTriggers(), currentTable.getTriggers(), TriggerDocDiff::getName);
List<ForeignKeyDocDiff> fkDiffs =
doDiff(originalTable.getForeignKeys(),
currentTable.getForeignKeys(),
fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq());
BaseTypeFieldEqualFunction eq = new BaseTypeFieldEqualFunction(IGNORE_FIELDS);
DiffType diffType = eq.apply(currentTable, originalTable) ? DiffType.NONE : DiffType.MODIFIED;
// workaround for diffType = NONE
if (diffType == DiffType.NONE) {
originalTable = null;
}
return TableDocDiff.builder()
.id(currentTable.getId())
.diffType(diffType)
.original(originalTable)
.name(currentTable.getName())
.comment(currentTable.getComment())
.type(currentTable.getType())
.createAt(currentTable.getCreateAt())
.columns(columnDiffs)
.indexes(indexDiffs)
.triggers(triggerDiffs)
.foreignKeys(fkDiffs)
.build();
})
.collect(Collectors.toList());
List<TableDocDiff> all = new ArrayList<>(16);
all.addAll(sameOrModified);
all.addAll(added);
all.addAll(removed);
return all;
}
private <T extends DiffAble<T>> List<T> doDiff(List<T> original, List<T> current, Function<T, String> idMapping) {
Map<String, T> originalMap = toMap(original, idMapping);
Map<String, T> currentMap = toMap(current, idMapping);
List<T> added = added(originalMap, currentMap);
List<T> removed = removed(originalMap, currentMap);
List<T> modified = modified(originalMap, currentMap);
List<T> same = same(originalMap, currentMap);
List<T> results = new ArrayList<>();
results.addAll(same);
results.addAll(added);
results.addAll(modified);
results.addAll(removed);
return results;
}
private <T> Map<String, T> toMap(List<T> content, Function<T, String> idMapping) {
return content
.stream()
.collect(Collectors.toMap(idMapping, Function.identity(), (a, b) -> {
log.warn("Duplicate key, origin = {}, current = {}", a, b);
return a;
}));
}
private <T extends DiffAble<T>> List<T> added(Map<String, T> originalMapById,
Map<String, T> currentMapById) {
return currentMapById.entrySet()
.stream()
.filter(entry -> !originalMapById.containsKey(entry.getKey()))
.map(Map.Entry::getValue)
.map(value -> {
value.setDiffType(DiffType.ADDED);
return value;
})
.collect(Collectors.toList());
}
private <T extends DiffAble<T>> List<T> removed(Map<String, T> originalMapById,
Map<String, T> currentMapById) {
return originalMapById.entrySet()
.stream()
.filter(entry -> !currentMapById.containsKey(entry.getKey()))
.map(Map.Entry::getValue)
.map(value -> {
value.setDiffType(DiffType.REMOVED);
return value;
})
.collect(Collectors.toList());
}
private <T extends DiffAble<T>> List<T> modified(Map<String, T> originalMapById,
Map<String, T> currentMapById) {
BaseTypeFieldEqualFunction eq = new BaseTypeFieldEqualFunction(IGNORE_FIELDS);
return modified(originalMapById, currentMapById, eq);
}
private <T extends DiffAble<T>> List<T> modified(Map<String, T> originalMapById,
Map<String, T> currentMapById,
BiFunction<Object, Object, Boolean> sameFunction) {
return currentMapById.entrySet()
.stream()
.filter(entry -> originalMapById.containsKey(entry.getKey()))
.filter(entry -> !sameFunction.apply(entry.getValue(), originalMapById.get(entry.getKey())))
.map(entry -> {
T value = entry.getValue();
value.setDiffType(DiffType.MODIFIED);
value.setOriginal(originalMapById.get(entry.getKey()));
return value;
})
.collect(Collectors.toList());
}
private <T extends DiffAble<T>> List<T> same(Map<String, T> originalMapById,
Map<String, T> currentMapById) {
BaseTypeFieldEqualFunction eq = new BaseTypeFieldEqualFunction(IGNORE_FIELDS);
return same(originalMapById, currentMapById, eq);
}
private <T extends DiffAble<T>> List<T> same(Map<String, T> originalMapById,
Map<String, T> currentMapById,
BiFunction<Object, Object, Boolean> sameFunction) {
return currentMapById.entrySet()
.stream()
.filter(entry -> originalMapById.containsKey(entry.getKey()))
.filter(entry -> sameFunction.apply(entry.getValue(), originalMapById.get(entry.getKey())))
.map(entry -> {
T value = entry.getValue();
value.setDiffType(DiffType.NONE);
return value;
})
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,40 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ForeignKeyDocDiff implements DiffAble<ForeignKeyDocDiff> {
private Integer id;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private Integer keySeq;
private String fkName;
private String fkTableName;
private String fkColumnName;
private String pkName;
private String pkTableName;
private String pkColumnName;
private String updateRule;
private String deleteRule;
private LocalDateTime createAt;
private DiffType diffType;
private ForeignKeyDocDiff original;
}

View File

@@ -0,0 +1,29 @@
package com.salpa.subject.domain.diff;
import com.alibaba.fastjson2.JSON;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class IndexDocDiff implements DiffAble<IndexDocDiff> {
private Integer id;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private String name;
private Boolean isUnique;
private String columnNameArray;
private LocalDateTime createAt;
private DiffType diffType;
private IndexDocDiff original;
}

View File

@@ -0,0 +1,41 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@Data
@Builder
public class TableDocDiff implements DiffAble<TableDocDiff> {
private Integer id;
private String name;
private String type;
private String comment;
@Builder.Default
private List<ColumnDocDiff> columns = Collections.emptyList();
@Builder.Default
private List<IndexDocDiff> indexes = Collections.emptyList();
@Builder.Default
private List<TriggerDocDiff> triggers = Collections.emptyList();
@Builder.Default
private List<ForeignKeyDocDiff> foreignKeys = Collections.emptyList();
private LocalDateTime createAt;
private DiffType diffType;
private TableDocDiff original;
}

View File

@@ -0,0 +1,32 @@
package com.salpa.subject.domain.diff;
import com.salpa.subject.domain.diff.data.DiffType;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class TriggerDocDiff implements DiffAble<TriggerDocDiff> {
private Integer id;
private String name;
private Integer tableDocumentId;
private Integer databaseDocumentId;
private String timing;
private String manipulation;
private String statement;
private String triggerCreateAt;
private LocalDateTime createAt;
private DiffType diffType;
private TriggerDocDiff original;
}

View File

@@ -0,0 +1,11 @@
package com.salpa.subject.domain.diff.data;
public interface Diff {
DiffType getDiffType();
Object getOriginal();
Object getCurrent();
}

View File

@@ -0,0 +1,21 @@
package com.salpa.subject.domain.diff.data;
public enum DiffType {
NONE, ADDED, REMOVED, MODIFIED;
public static boolean isModified(DiffType type) {
return type != null && type != NONE;
}
public boolean isAdded() {
return this == ADDED;
}
public boolean isRemoved() {
return this == REMOVED;
}
public boolean isNone() {
return this == NONE;
}
}

View File

@@ -0,0 +1,24 @@
package com.salpa.subject.domain.diff.data;
import lombok.Builder;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@Builder
public class FieldDiff implements Diff {
private String fieldName;
private DiffType diffType;
private Object original;
private Object current;
@Builder.Default
private List<FieldDiff> fields = new ArrayList<>();
}

View File

@@ -0,0 +1,15 @@
package com.salpa.subject.domain.diff.data;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class RootDiff {
private DiffType diffType;
private List<FieldDiff> fields = new ArrayList<>();
}

View File

@@ -0,0 +1,15 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.diff.data.FieldDiff;
import java.util.List;
public class ColumnDiffProcessor implements DiffProcessor<ColumnMeta> {
@Override
public FieldDiff process(String fieldName, List<ColumnMeta> original, List<ColumnMeta> current) {
return diffTableField(original, current, fieldName, ColumnMeta::getName);
}
}

View File

@@ -0,0 +1,81 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.data.DatabaseMeta;
import com.salpa.subject.domain.diff.data.DiffType;
import com.salpa.subject.domain.diff.data.FieldDiff;
import com.salpa.subject.domain.diff.data.RootDiff;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@Slf4j
public class DatabaseDiffProcessor {
private final TableDiffProcessor tableDiffProcessor = new TableDiffProcessor();
private static final DatabaseMeta EMPTY = DatabaseMeta.builder().build();
public RootDiff process(DatabaseMeta original, DatabaseMeta current) {
DiffType diffType = null;
if (original == null && current != null) {
diffType = DiffType.ADDED;
}
if (original != null && current == null) {
diffType = DiffType.REMOVED;
}
List<FieldDiff> fields = diffDatabaseFields(
Objects.requireNonNullElse(original, EMPTY),
Objects.requireNonNullElse(current, EMPTY)
);
boolean isModified = fields.stream().anyMatch(f -> DiffType.isModified(f.getDiffType()));
if (diffType == null) {
diffType = isModified ? DiffType.MODIFIED : DiffType.NONE;
}
RootDiff diff = new RootDiff();
diff.setFields(fields);
diff.setDiffType(diffType);
return diff;
}
private List<FieldDiff> diffDatabaseFields(DatabaseMeta original, DatabaseMeta current) {
Class<DatabaseMeta> clazz = DatabaseMeta.class;
Field[] fields = clazz.getDeclaredFields();
List<FieldDiff> diffs = new ArrayList<>(32);
// ignore tables diff
Arrays.stream(fields)
.filter(field -> !Objects.equals(field.getName(), "tables"))
.forEach(field -> {
try {
field.setAccessible(true);
Object originalValue = original == null ? null : field.get(original);
Object currentValue = current == null ? null : field.get(current);
if (!Objects.equals(originalValue, currentValue)) {
DiffType diffType;
if (originalValue == null) {
diffType = DiffType.ADDED;
} else if (currentValue == null) {
diffType = DiffType.REMOVED;
} else {
diffType = DiffType.MODIFIED;
}
diffs.add(FieldDiff.builder()
.diffType(diffType)
.fieldName(field.getName())
.original(originalValue)
.current(currentValue)
.build());
}
} catch (IllegalAccessException e) {
log.error("diff field failed", e);
}
});
FieldDiff tablesField = tableDiffProcessor.process("tables", original.getTables(), current.getTables());
diffs.add(tablesField);
return diffs;
}
}

View File

@@ -0,0 +1,101 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.diff.data.DiffType;
import com.salpa.subject.domain.diff.data.FieldDiff;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
public interface DiffProcessor<T> {
Logger log = LoggerFactory.getLogger(DiffProcessor.class);
FieldDiff process(String fieldName, List<T> original, List<T> current);
default FieldDiff diffTableField(List<T> original,
List<T> current,
String fieldName,
Function<T, String> identity) {
Map<String, T> originalMap = toMap(original, identity);
Map<String, T> currentMap = toMap(current, identity);
List<FieldDiff> columnFieldDiffs = new ArrayList<>(32);
// removed
List<FieldDiff> removedFields = originalRemovedField(originalMap, currentMap);
columnFieldDiffs.addAll(removedFields);
// added
List<FieldDiff> addedFields = currentAddedField(originalMap, currentMap);
columnFieldDiffs.addAll(addedFields);
// modified
List<FieldDiff> modifiedFields = modifiedField(originalMap, currentMap);
columnFieldDiffs.addAll(modifiedFields);
return FieldDiff.builder()
.fieldName(fieldName)
.diffType(columnFieldDiffs.isEmpty() ? DiffType.NONE : DiffType.MODIFIED)
.fields(columnFieldDiffs)
.build();
}
default Map<String, T> toMap(List<T> content, Function<T, String> idMapping) {
return content
.stream()
.collect(Collectors.toMap(idMapping, Function.identity(), (a, b) -> {
log.warn("Duplicate key, origin = {}, current = {}", a, b);
return a;
}));
}
default List<FieldDiff> originalRemovedField(Map<String, T> originalMapById,
Map<String, T> currentMapById) {
return originalMapById.entrySet()
.stream()
.filter(entry -> !currentMapById.containsKey(entry.getKey()))
.map(entry -> FieldDiff.builder()
.fieldName(entry.getKey())
.original(entry.getValue())
.diffType(DiffType.REMOVED)
.build())
.collect(Collectors.toList());
}
default List<FieldDiff> currentAddedField(Map<String, T> originalMapById,
Map<String, T> currentMapById) {
return currentMapById.entrySet()
.stream()
.filter(entry -> !originalMapById.containsKey(entry.getKey()))
.map(entry -> FieldDiff.builder()
.fieldName(entry.getKey())
.current(entry.getValue())
.diffType(DiffType.ADDED)
.build())
.collect(Collectors.toList());
}
default List<FieldDiff> modifiedField(Map<String, T> original,
Map<String, T> current) {
List<FieldDiff> diff = new ArrayList<>();
original.entrySet()
.stream()
.filter(entry -> current.containsKey(entry.getKey()))
.forEach(entry -> {
T originalValue = entry.getValue();
T currentValue = current.get(entry.getKey());
if (!Objects.equals(originalValue, currentValue)) {
FieldDiff fieldDiff = FieldDiff.builder()
.fieldName(entry.getKey())
.original(originalValue)
.current(currentValue)
.diffType(DiffType.MODIFIED)
.build();
diff.add(fieldDiff);
}
});
return diff;
}
}

View File

@@ -0,0 +1,19 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.data.ForeignKeyMeta;
import com.salpa.subject.domain.diff.data.FieldDiff;
import java.util.List;
public class ForeignKeyDiffProcessor implements DiffProcessor<ForeignKeyMeta> {
@Override
public FieldDiff process(String fieldName, List<ForeignKeyMeta> original, List<ForeignKeyMeta> current) {
return diffTableField(
original,
current,
"foreignKeys",
fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq());
}
}

View File

@@ -0,0 +1,15 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.data.IndexMeta;
import com.salpa.subject.domain.diff.data.FieldDiff;
import java.util.List;
public class IndexDiffProcessor implements DiffProcessor<IndexMeta> {
@Override
public FieldDiff process(String fieldName, List<IndexMeta> original, List<IndexMeta> current) {
return diffTableField(original, current, fieldName, IndexMeta::getName);
}
}

View File

@@ -0,0 +1,158 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.data.TableMeta;
import com.salpa.subject.domain.diff.data.DiffType;
import com.salpa.subject.domain.diff.data.FieldDiff;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
public class TableDiffProcessor implements DiffProcessor<TableMeta> {
private final IndexDiffProcessor indexDiffProcessor = new IndexDiffProcessor();
private final ColumnDiffProcessor columnDiffProcessor = new ColumnDiffProcessor();
private final TriggerDiffProcessor triggerDiffProcessor = new TriggerDiffProcessor();
private final ForeignKeyDiffProcessor foreignKeyDiffProcessor = new ForeignKeyDiffProcessor();
private static final TableMeta EMPTY = new TableMeta();
@Override
public FieldDiff process(String fieldName, List<TableMeta> original, List<TableMeta> current) {
// diff tables field
Map<String, TableMeta> originalMap = toMap(original, TableMeta::getName);
Map<String, TableMeta> currentMap = toMap(current, TableMeta::getName);
List<FieldDiff> tables = new ArrayList<>();
List<TableMeta> added = added(originalMap, currentMap);
List<TableMeta> removed = removed(originalMap, currentMap);
// added
List<FieldDiff> addedFields = added.stream()
.map(table -> diffTableField(EMPTY, table))
.collect(Collectors.toList());
tables.addAll(addedFields);
// removed
List<FieldDiff> removedFields = removed.stream()
.map(table -> diffTableField(table, EMPTY))
.collect(Collectors.toList());
tables.addAll(removedFields);
// modified
List<FieldDiff> modified = originalMap.entrySet()
.stream()
.filter(entry -> currentMap.containsKey(entry.getKey()))
.filter(entry -> !Objects.equals(entry.getValue(), currentMap.get(entry.getKey())))
.map(entry -> {
TableMeta originalValue = entry.getValue();
TableMeta currentValue = currentMap.get(entry.getKey());
return diffTableField(originalValue, currentValue);
})
.collect(Collectors.toList());
tables.addAll(modified);
DiffType tablesDiffType;
if (!modified.isEmpty()) {
tablesDiffType = DiffType.MODIFIED;
} else if (!addedFields.isEmpty()) {
tablesDiffType = DiffType.ADDED;
} else if (!removedFields.isEmpty()) {
tablesDiffType = DiffType.REMOVED;
} else {
tablesDiffType = DiffType.NONE;
}
FieldDiff tablesField = FieldDiff.builder()
.diffType(tablesDiffType)
.fieldName(fieldName)
.fields(tables)
.build();
return tablesField;
}
private List<TableMeta> added(Map<String, TableMeta> originalMap,
Map<String, TableMeta> currentMap) {
return currentMap.entrySet()
.stream()
.filter(entry -> !originalMap.containsKey(entry.getKey()))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
}
private List<TableMeta> removed(Map<String, TableMeta> originalMap,
Map<String, TableMeta> currentMap) {
return originalMap.entrySet()
.stream()
.filter(entry -> !currentMap.containsKey(entry.getKey()))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
}
private FieldDiff diffTableField(TableMeta original, TableMeta current) {
FieldDiff columns =
columnDiffProcessor.process("columns", original.getColumns(), current.getColumns());
FieldDiff indexes =
indexDiffProcessor.process("indexes", original.getIndexes(), current.getIndexes());
FieldDiff triggers =
triggerDiffProcessor.process("triggers", original.getTriggers(), current.getTriggers());
FieldDiff foreignKeys =
foreignKeyDiffProcessor.process("foreignKeys", original.getForeignKeys(), current.getForeignKeys());
List<FieldDiff> otherFields = fields(original, current);
List<FieldDiff> fields = new ArrayList<>();
fields.add(columns);
fields.add(indexes);
fields.add(foreignKeys);
fields.add(triggers);
fields.addAll(otherFields);
DiffType diffType;
if (original == EMPTY) {
diffType = DiffType.ADDED;
} else if (current == EMPTY) {
diffType = DiffType.REMOVED;
} else {
diffType = DiffType.MODIFIED;
}
return FieldDiff.builder()
.diffType(diffType)
.fieldName(original == EMPTY ? current.getName() : original.getName())
.original(current == EMPTY ? original : null)
.current(original == EMPTY ? current : null)
.fields(fields)
.build();
}
private List<FieldDiff> fields(TableMeta original, TableMeta current) {
List<FieldDiff> fields = new ArrayList<>();
// ignore tables diff
Class<TableMeta> clazz = TableMeta.class;
List<String> ignoredFields = List.of("columns", "indexes", "triggers", "foreignKeys");
Arrays.stream(clazz.getDeclaredFields())
.filter(field -> !ignoredFields.contains(field.getName()))
.forEach(field -> {
try {
field.setAccessible(true);
Object originalValue = original == null ? null : field.get(original);
Object currentValue = current == null ? null : field.get(current);
if (!Objects.equals(originalValue, currentValue)) {
DiffType diffType;
if (originalValue == null) {
diffType = DiffType.ADDED;
} else if (currentValue == null) {
diffType = DiffType.REMOVED;
} else {
diffType = DiffType.MODIFIED;
}
fields.add(FieldDiff.builder()
.diffType(diffType)
.fieldName(field.getName())
.original(originalValue)
.current(currentValue)
.build());
}
} catch (IllegalAccessException e) {
log.error("diff field failed", e);
}
});
return fields;
}
}

View File

@@ -0,0 +1,15 @@
package com.salpa.subject.domain.diff.processor;
import com.salpa.subject.domain.data.TriggerMeta;
import com.salpa.subject.domain.diff.data.FieldDiff;
import java.util.List;
public class TriggerDiffProcessor implements DiffProcessor<TriggerMeta> {
@Override
public FieldDiff process(String fieldName, List<TriggerMeta> original, List<TriggerMeta> current) {
return diffTableField(original, current, fieldName, TriggerMeta::getName);
}
}

View File

@@ -0,0 +1,72 @@
package com.salpa.subject.domain.error;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public enum DomainErrors implements MetaopsErrors {
REFRESH_TOKEN_EXPIRED("X_0001", "refresh token expired"),
INVALID_REFRESH_TOKEN_OPERATION("X_0002", "invalid refresh token operation"),
NETWORK_ERROR("X_0003", "网络似乎不稳定,请稍后再试"),
INVALID_ACCESS_TOKEN("X_0004", "无效的 access token"),
NOT_SUPPORT_DATABASE_TYPE("A_10000", "不支持的数据库类型, 请检查项目配置"),
PROJECT_NOT_FOUND("A_10001", "项目不存在"),
DATABASE_META_NOT_FOUND("A_10002", "获取数据库信息失败"),
CONNECT_DATABASE_FAILED("A_10003", "连接数据库失败,请检查连接配置"),
GROUP_OWNER_MUST_NOT_BE_EMPTY("A_10004", "请至少指定一个分组组长"),
PASSWORD_MUST_NOT_BE_BLANK("A_10005", "密码不能为空"),
USERNAME_OR_EMAIL_DUPLICATE("A_10006", "用户名或邮箱已存在"),
USER_ROLE_DUPLICATE("A_10007", "用户角色已存在"),
PROJECT_NAME_DUPLICATE("A_10008", "项目名称已被占用"),
CANNOT_UPDATE_SELF_ROLE("A_10009", "无法对自己执行角色变更的操作"),
UPDATE_PASSWORD_CONFIRM_FAILED("A_10010", "两次密码输入不一致"),
ORIGIN_PASSWORD_NOT_CORRECT("A_10011", "原密码不正确"),
INVALID_CRON_EXPRESSION("A_10012", "不合法的 cron 表达式"),
REGISTRATION_ID_DUPLICATE("A_10013", "应用注册 ID 不能重复"),
REGISTRATION_ID_NOT_FOUND("A_10014", "应用 ID 不存在"),
MISS_REQUIRED_PARAMETERS("A_10015", "缺少必填参数"),
DATABASE_TYPE_NAME_DUPLICATE("A_10016", "数据库类型名已存在"),
MUST_NOT_MODIFY_SYSTEM_DEFAULT_DATABASE_TYPE("A_10017", "禁止修改系统默认数据库类型"),
DOWNLOAD_DRIVER_ERROR("A_10018", "驱动下载失败"),
INVALID_DATABASE_TYPE_URL_PATTERN("A_10019", "不合法的 url pattern"),
DOCUMENT_VERSION_IS_INVALID("A_10020", "文档版本不合法"),
CANNOT_UPDATE_SELF_ENABLED_STATUS("A_10021", "无法对自己执行启用禁用操作"),
MOCK_DATA_SCRIPT_MUST_NOT_BE_BLANK("A_10022", "脚本内容不能为空"),
TABLE_META_NOT_FOUND("A_10023", "不存在的数据库表"),
DEPENDENT_COLUMN_NAME_MUST_NOT_BE_BLANK("A_10024", "必须指定依赖的字段"),
DEPENDENT_REF_MUST_NOT_BE_BLANK("A_10025", "请选择关联表和字段"),
MUST_NOT_REF_SELF("A_10026", "不能引用自身"),
CIRCLE_REFERENCE("A_10027", "检查到循环引用"),
DUPLICATE_COLUMN("A_10028", "重复的列"),
INVALID_MOCK_DATA_SCRIPT("A_10029", "不合法的表达式"),
CANNOT_DELETE_SELF("A_10030", "无法对自己执行删除账号操作"),
DRIVER_CLASS_NOT_FOUND("A_10031", "获取驱动类名失败"),
DATABASE_DOCUMENT_DUPLICATE_KEY("A_10032", "文档版本重复"),
UPLOAD_DRIVER_FILE_ERROR("A_10033", "上传失败,请检查后重新上传"),
DRIVER_URL_AND_PATH_MUST_NOT_BE_ALL_BLANK("A_10034", "请填写下载驱动的地址或手动上传驱动文件"),
LOAD_DRIVER_FAILED("A_10045", "驱动加载失败,请检查后重试")
;
private final String errCode;
private final String errMessage;
public MetaopsException exception() {
return new MetaopsException(this);
}
public MetaopsException exception(Throwable origin) {
return new MetaopsException(this, origin);
}
public MetaopsException exception(String message, Throwable origin) {
return new MetaopsException(this, message, origin);
}
public MetaopsException exception(String s) {
return exception(s, null);
}
}

View File

@@ -0,0 +1,8 @@
package com.salpa.subject.domain.error;
public interface MetaopsErrors {
String getErrCode();
String getErrMessage();
}

View File

@@ -0,0 +1,56 @@
package com.salpa.subject.domain.error;
import lombok.Getter;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class MetaopsException extends RuntimeException {
@Getter
private MetaopsErrors errorCodeMessage;
@Getter
private String errCode;
@Getter
private String errMessage;
/**
* @param errorCodeMessage 错误信息
*/
public MetaopsException(MetaopsErrors errorCodeMessage) {
super(errorCodeMessage.getErrMessage());
this.errorCodeMessage = errorCodeMessage;
this.errCode = errorCodeMessage.getErrCode();
this.errMessage = errorCodeMessage.getErrMessage();
}
/**
* @param errorCodeMessage 错误信息
* @param overrideMessage 覆盖 message
*/
public MetaopsException(MetaopsErrors errorCodeMessage, String overrideMessage) {
super(overrideMessage);
this.errorCodeMessage = errorCodeMessage;
this.errCode = errorCodeMessage.getErrCode();
this.errMessage = overrideMessage;
}
/**
* @param errorCodeMessage 错误信息
* @param cause root cause
*/
public MetaopsException(MetaopsErrors errorCodeMessage, Throwable cause) {
super(errorCodeMessage.getErrMessage(), cause);
this.errorCodeMessage = errorCodeMessage;
this.errCode = errorCodeMessage.getErrCode();
this.errMessage = errorCodeMessage.getErrMessage();
}
public MetaopsException(MetaopsErrors errorCodeMessage, String overrideMessage, Throwable cause) {
super(overrideMessage, cause);
this.errorCodeMessage = errorCodeMessage;
this.errCode = errorCodeMessage.getErrCode();
this.errMessage = overrideMessage;
}
}

View File

@@ -0,0 +1,29 @@
package com.salpa.subject.domain.generator;
import com.salpa.subject.domain.data.DatabaseDocumentResponse;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import org.springframework.stereotype.Component;
import java.io.OutputStream;
@Component
public interface DocumentFileGenerator {
boolean support(DocumentFileType type);
void generate(DocumentFileGenerateContext context, OutputStream outputStream);
@Getter
@Builder
class DocumentFileGenerateContext {
@NonNull
private DocumentFileType documentFileType;
@NonNull
private DatabaseDocumentResponse databaseDocument;
}
}

View File

@@ -0,0 +1,23 @@
package com.salpa.subject.domain.generator;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum DocumentFileType {
MARKDOWN("md", "Markdown"),
PLANT_UML_ER_SVG("svg", "UML SVG"),
PLANT_UML_ER_PNG("png", "UML PNG"),
WORD("docx", "Word"),
;
private String fileExtension;
private String name;
}

View File

@@ -0,0 +1,243 @@
package com.salpa.subject.domain.generator;
import com.salpa.common.exception.SystemException;
import com.salpa.common.utils.StringUtils;
import com.salpa.subject.domain.data.DatabaseDocumentResponse;
import com.salpa.subject.domain.data.DocumentTemplatePropertiesResponse;
import com.salpa.subject.domain.data.TableDocumentResponse;
import com.salpa.subject.domain.render.markdown.MarkdownBuilder;
import com.salpa.subject.service.DocumentTemplateService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
@Slf4j
@RequiredArgsConstructor
public class MarkdownDocumentFileGenerator implements DocumentFileGenerator {
@Autowired(required=false)
private DocumentTemplateService documentTemplateService;
@Override
public boolean support(DocumentFileType type) {
return type == DocumentFileType.MARKDOWN;
}
@Override
public void generate(DocumentFileGenerateContext context, OutputStream outputStream) {
DocumentTemplatePropertiesResponse templateProperties = documentTemplateService.getAllProperties();
String fileName = context.getDatabaseDocument().getDatabaseName() + "-" + UUID.randomUUID().toString();
String data = markdownData(context, templateProperties);
Path tempFile = null;
try {
tempFile = Files.createTempFile(fileName, ".md");
Path path = Files.writeString(tempFile, data, StandardCharsets.UTF_8);
byte[] bytes = Files.readAllBytes(path);
outputStream.write(bytes);
} catch (IOException e) {
if (tempFile != null) {
try {
Files.deleteIfExists(tempFile);
} catch (IOException ex) {
log.warn("delete temp file error", ex);
}
}
throw new SystemException("System error");
}
}
private String markdownData(DocumentFileGenerateContext context,
DocumentTemplatePropertiesResponse properties) {
DatabaseDocumentResponse doc = context.getDatabaseDocument();
MarkdownBuilder builder = MarkdownBuilder.builder();
builder.primaryTitle(doc.getDatabaseName());
// overview
overviewBuild(builder, doc);
// field map by table name
Map<String, String> columnTitleMap = properties.getColumnFieldNameProperties()
.stream()
.collect(Collectors.toMap(d -> d.getKey(),
d -> Objects.requireNonNullElse(d.getValue(), d.getDefaultValue())));
Map<String, String> indexTitleMap = properties.getIndexFieldNameProperties()
.stream()
.collect(Collectors.toMap(d -> d.getKey(),
d -> Objects.requireNonNullElse(d.getValue(), d.getDefaultValue())));
Map<String, String> triggerTitleMap = properties.getTriggerFieldNameProperties()
.stream()
.collect(Collectors.toMap(d -> d.getKey(),
d -> Objects.requireNonNullElse(d.getValue(), d.getDefaultValue())));
Map<String, String> foreignKeyTitleMap = properties.getForeignKeyFieldNameProperties()
.stream()
.collect(Collectors.toMap(d -> d.getKey(),
d -> Objects.requireNonNullElse(d.getValue(), d.getDefaultValue())));
// table document build
doc.getTables().forEach(table -> {
if (StringUtils.isNotBlank(table.getComment())) {
builder.secondTitle(table.getName() + " /\\*" + table.getComment() + "\\*/");
} else {
builder.secondTitle(table.getName());
}
columnBuild(builder, table, columnTitleMap);
indexBuild(builder, table, indexTitleMap);
foreignKeyBuild(builder, table, foreignKeyTitleMap);
triggerBuild(builder, table, triggerTitleMap);
});
return builder.build();
}
private void overviewBuild(MarkdownBuilder builder, DatabaseDocumentResponse doc) {
builder.secondTitle("overview");
List<List<String>> overviewContent = new ArrayList<>();
for (int i = 0; i < doc.getTables().size(); i++) {
TableDocumentResponse table = doc.getTables().get(i);
List<String> row = List.of((i + 1) + "",
Objects.requireNonNullElse(table.getName(), ""),
Objects.requireNonNullElse(table.getType(), ""),
Objects.requireNonNullElse(table.getComment(), ""));
overviewContent.add(row);
}
builder.table(List.of("", "表名", "类型", "备注"), overviewContent);
}
private void columnBuild(MarkdownBuilder builder,
TableDocumentResponse table,
Map<String, String> titleMap) {
Function<TableDocumentResponse.ColumnDocumentResponse, String>
columnDefaultValueMapping = column -> {
if (Objects.equals(column.getNullable(), "YES")) {
return Objects.requireNonNullElse(column.getDefaultValue(), "null");
} else {
return Objects.requireNonNullElse(column.getDefaultValue(), "");
}
};
builder.thirdTitle("Columns");
List<List<String>> columnContent = new ArrayList<>();
for (int i = 0; i < table.getColumns().size(); i++) {
var column = table.getColumns().get(i);
String type;
if (column.getDecimalDigits() == null
|| Objects.requireNonNullElse(column.getDecimalDigits(), 0) == 0) {
type = column.getType() + "(" + column.getSize() + ")";
} else {
type = column.getType() + "(" + column.getSize() + "," + column.getDecimalDigits() + ")";
}
columnContent.add(List.of((i + 1) + "",
column.getName(),
type,
column.getIsPrimaryKey() ? "YES" : "NO",
column.getNullable(),
column.getAutoIncrement(),
columnDefaultValueMapping.apply(column),
Objects.requireNonNullElse(column.getComment(), "")));
}
builder.table(
List.of(
"",
titleMap.getOrDefault("name", "name"),
titleMap.getOrDefault("type", "type"),
titleMap.getOrDefault("isPrimaryKey", "isPrimaryKey"),
titleMap.getOrDefault("nullable", "nullable"),
titleMap.getOrDefault("autoIncrement", "autoIncrement"),
titleMap.getOrDefault("defaultValue", "defaultValue"),
titleMap.getOrDefault("comment", "comment")
),
columnContent
);
}
private void indexBuild(MarkdownBuilder builder,
TableDocumentResponse table,
Map<String, String> titleMap) {
builder.thirdTitle("Indexes");
List<List<String>> indexContent = new ArrayList<>();
for (int i = 0; i < table.getIndexes().size(); i++) {
var index = table.getIndexes().get(i);
String columnNames = String.join(", ", index.getColumnNames());
String isUnique = index.getIsUnique() ? "YES" : "NO";
indexContent.add(List.of((i + 1) + "", index.getName(), isUnique, columnNames));
}
builder.table(
List.of(
"",
titleMap.getOrDefault("name", "name"),
titleMap.getOrDefault("isUnique", "isUnique"),
titleMap.getOrDefault("columnNames", "columnNames")
),
indexContent
);
}
private void foreignKeyBuild(MarkdownBuilder builder,
TableDocumentResponse table,
Map<String, String> titleMap) {
if (!table.getForeignKeys().isEmpty()) {
List<List<String>> foreignKeys = new ArrayList<>();
builder.thirdTitle("Foreign Keys");
for (int i = 0; i < table.getForeignKeys().size(); i++) {
TableDocumentResponse.ForeignKeyDocumentResponse fk = table.getForeignKeys().get(i);
List<String> item = List.of(
(i + 1) + "",
Objects.requireNonNullElse(fk.getFkName(), ""), fk.getFkColumnName(),
Objects.requireNonNullElse(fk.getPkName(), ""), fk.getPkTableName(),
fk.getPkColumnName(),
fk.getUpdateRule(), fk.getDeleteRule()
);
foreignKeys.add(item);
}
builder.table(
List.of(
"",
titleMap.getOrDefault("fkName", "fkName"),
titleMap.getOrDefault("fkColumnName", "fkColumnName"),
titleMap.getOrDefault("pkName", "pkName"),
titleMap.getOrDefault("pkTableName", "pkTableName"),
titleMap.getOrDefault("pkColumnName", "pkColumnName"),
titleMap.getOrDefault("updateRule", "updateRule"),
titleMap.getOrDefault("deleteRule", "deleteRule")
),
foreignKeys
);
}
}
private void triggerBuild(MarkdownBuilder builder,
TableDocumentResponse table,
Map<String, String> titleMap) {
if (!table.getTriggers().isEmpty()) {
List<List<String>> triggerContent = new ArrayList<>();
for (int i = 0; i < table.getTriggers().size(); i++) {
var trigger = table.getTriggers().get(i);
triggerContent.add(
List.of(
(i + 1) + "",
Objects.requireNonNullElse(trigger.getName(), ""),
Objects.requireNonNullElse(trigger.getTiming(), ""),
Objects.requireNonNullElse(trigger.getManipulation(), ""),
Objects.requireNonNullElse(trigger.getStatement(), "")
));
}
builder.thirdTitle("Triggers");
builder.table(
List.of(
"",
titleMap.getOrDefault("name", "name"),
titleMap.getOrDefault("timing", "timing"),
titleMap.getOrDefault("manipulation", "manipulation"),
titleMap.getOrDefault("statement", "statement")
),
triggerContent
);
}
}
}

View File

@@ -0,0 +1,81 @@
package com.salpa.subject.domain.generator;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.HackLoopTableRenderPolicy;
import com.salpa.common.exception.SystemException;
import com.salpa.subject.domain.data.TableDocumentResponse;
import com.salpa.subject.domain.vo.ColumnVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@Slf4j
@RequiredArgsConstructor
public class WordFileGenerator implements DocumentFileGenerator {
@Override
public boolean support(DocumentFileType type) {
return type == DocumentFileType.WORD;
}
@Override
public void generate(DocumentFileGenerateContext context, OutputStream outputStream) {
try {
Map<String, Object> data = new HashMap<>();
data.put("databaseName",context.getDatabaseDocument().getDatabaseName());
List<Map<String,Object>> tList=new ArrayList<>();
List<Map<String, Object>> tables = new ArrayList<>();
List<TableDocumentResponse> tables1 = context.getDatabaseDocument().getTables();
for (TableDocumentResponse tableDocumentResponse : tables1) {
Map<String, Object> table = new HashMap<>();
table.put("tableName",tableDocumentResponse.getName());
table.put("tableType",tableDocumentResponse.getType());
table.put("tableComment",tableDocumentResponse.getComment());
tables.add(table);
List<TableDocumentResponse.ColumnDocumentResponse> columns1 = tableDocumentResponse.getColumns();
List<ColumnVo> columns = new ArrayList<>();
Map<String,Object> tMap = new HashMap<>();
for (TableDocumentResponse.ColumnDocumentResponse column : columns1) {
ColumnVo columnVo = new ColumnVo();
columnVo.setName(column.getName());
columnVo.setType(column.getType() + "(" + column.getSize() + ")");
if (column.getIsPrimaryKey()) {
columnVo.setPrimaryKey("YES");
} else {
columnVo.setPrimaryKey("NO");
}
columnVo.setNullable(column.getNullable());
columnVo.setAutoIncrement(column.getAutoIncrement());
columnVo.setDefaultValue(column.getDefaultValue());
columnVo.setComment(column.getComment());
columns.add(columnVo);
}
tMap.put("tableName",tableDocumentResponse.getName());
tMap.put("columns",columns);
tList.add(tMap);
}
data.put("tables",tables);
data.put("tableColumns",tList);
InputStream inputStream = new ClassPathResource("word.docx").getInputStream();
HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();
Configure config = Configure.newBuilder().bind("tables", policy).bind("columns",policy).build();
XWPFTemplate template = XWPFTemplate.compile(inputStream,config).render(data);
template.write(outputStream);
} catch (IOException e) {
throw new SystemException("System error");
}
}
}

View File

@@ -0,0 +1,153 @@
package com.salpa.subject.domain.generator.plantuml;
import com.salpa.subject.domain.data.DatabaseDocumentResponse;
import com.salpa.subject.domain.data.TableDocumentResponse;
import com.salpa.subject.domain.generator.DocumentFileGenerator;
import com.salpa.common.exception.SystemException;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
public abstract class BasePlantUmlFileGenerator implements DocumentFileGenerator {
public static final String LINE = "\r\n";
@Override
public void generate(DocumentFileGenerateContext context, OutputStream outputStream) {
String dsl = new ErDsl(context).toDsl();
try {
new SourceStringReader(dsl).outputImage(outputStream, fileFormatOption());
} catch (IOException e) {
log.error("export plantuml error", e);
throw new SystemException("System error");
}
}
protected abstract FileFormatOption fileFormatOption();
public class ErDsl {
private final DocumentFileGenerateContext context;
private Set<String> foreignKeyRelations = new HashSet<>(16);
private Set<String> tables = new HashSet<>(16);
public ErDsl(DocumentFileGenerateContext context) {
this.context = context;
Set<String> tables = context.getDatabaseDocument()
.getTables()
.stream()
.map(TableDocumentResponse::getName)
.collect(Collectors.toSet());
this.tables = tables;
}
public String toDsl() {
DatabaseDocumentResponse databaseDocument = context.getDatabaseDocument();
StringBuilder dslBuilder = new StringBuilder(1024);
// use smetana engine
dslBuilder.append("@startuml").append(LINE)
.append("!pragma layout smetana").append(LINE);
// configuration
dslBuilder.append("' hide the spot").append(LINE);
dslBuilder.append("hide circle").append(LINE);
//分辨率像素
dslBuilder.append("skinparam dpi 48").append(LINE);
dslBuilder.append("scale 8192 width").append(LINE);
dslBuilder.append("scale 8192 height").append(LINE);
// entities
String entities = databaseDocument.getTables()
.stream()
.map(table -> toErDsl(table))
.collect(Collectors.joining(LINE));
dslBuilder.append(entities);
// relation
dslBuilder.append(LINE);
String relations = foreignKeyRelations.stream()
.collect(Collectors.joining(LINE));
dslBuilder.append(relations);
dslBuilder.append(LINE);
dslBuilder.append("@enduml");
if (log.isDebugEnabled()) {
log.debug("------------------------------");
log.debug(dslBuilder.toString());
log.debug("------------------------------");
}
return dslBuilder.toString();
}
private String toErDsl(TableDocumentResponse table) {
StringBuilder dslBuilder = new StringBuilder(1024);
dslBuilder.append("entity ").append(table.getName())
.append(" {");
table.getColumns()
.stream()
.filter(TableDocumentResponse.ColumnDocumentResponse::getIsPrimaryKey)
.forEach(primaryCol -> {
dslBuilder.append(LINE);
dslBuilder.append("*")
.append(primaryCol.getName())
.append(" : ")
.append(primaryCol.getType())
.append("(")
.append(primaryCol.getSize())
.append(")")
.append(" <PK> ");
dslBuilder.append(LINE);
});
table.getColumns()
.stream()
.filter(col -> !col.getIsPrimaryKey())
.forEach(col -> {
dslBuilder.append(LINE);
if ("NO".equalsIgnoreCase(col.getNullable())) {
dslBuilder.append("*");
}
dslBuilder.append(col.getName())
.append(" : ")
.append(col.getType())
.append("(")
.append(col.getSize())
.append(")");
if (col.getComment() != null && !"".equals(col.getComment().trim())) {
dslBuilder.append(" /* ").append(col.getComment()).append(" */");
}
dslBuilder.append(LINE);
});
dslBuilder.append("}");
dslBuilder.append(LINE);
table.getForeignKeys()
.stream()
.filter(fk -> tables.contains(fk.getFkTableName()) && tables.contains(fk.getPkTableName()))
.forEach(fk -> {
String fkTableName = fk.getFkTableName();
String fkColumnName = fk.getFkColumnName();
String pkTableName = fk.getPkTableName();
String pkColumnName = fk.getPkColumnName();
StringBuilder relationBuilder = new StringBuilder();
relationBuilder.append(fkTableName).append("::").append(fkColumnName)
.append(" --> ")
.append(pkTableName).append("::").append(pkColumnName)
.append(" : ")
.append(Objects.requireNonNullElse(fk.getFkName(), ""));
foreignKeyRelations.add(relationBuilder.toString());
});
return dslBuilder.toString();
}
}
}

View File

@@ -0,0 +1,21 @@
package com.salpa.subject.domain.generator.plantuml;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class PlantUmlErSvgFileGenerator extends BasePlantUmlFileGenerator {
@Override
public boolean support(com.salpa.subject.domain.generator.DocumentFileType type) {
return type == com.salpa.subject.domain.generator.DocumentFileType.PLANT_UML_ER_SVG;
}
@Override
protected FileFormatOption fileFormatOption() {
return new FileFormatOption(FileFormat.SVG);
}
}

View File

@@ -0,0 +1,22 @@
package com.salpa.subject.domain.generator.plantuml;
import com.salpa.subject.domain.generator.DocumentFileType;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class PlantUmlPngFileGenerator extends BasePlantUmlFileGenerator {
@Override
public boolean support(DocumentFileType type) {
return type == DocumentFileType.PLANT_UML_ER_PNG;
}
@Override
protected FileFormatOption fileFormatOption() {
return new FileFormatOption(FileFormat.PNG);
}
}

View File

@@ -0,0 +1,82 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.TriggerMeta;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@Slf4j
public abstract class AbstractSqlTriggerMetaProvider implements TriggerMetaProvider {
@Override
public List<TriggerMeta> selectTriggers(Connection connection, TableCondition condition) {
String sql = sql(condition);
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
ResultSet results = preparedStatement.executeQuery();
List<TriggerMeta> triggers = new ArrayList<>();
while (results.next()) {
String name = getTriggerName(results);
String statement = getStatement(results);
String timing = getTiming(results);
String manipulation = getManipulation(results);
String created = getCreateAt(results);
TriggerMeta meta = TriggerMeta.builder()
.name(name)
.manipulation(manipulation)
.timing(timing)
.statement(statement)
.createAt(created)
.build();
triggers.add(meta);
}
return triggers;
} catch (SQLException e) {
log.warn("get trigger meta failed", e);
return Collections.emptyList();
} finally {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
// ignore
}
}
}
}
protected abstract String sql(TableCondition condition);
protected String getTriggerName(ResultSet results) throws SQLException {
return Objects.requireNonNullElse(results.getString("TRIGGER_NAME"), "");
}
protected String getStatement(ResultSet results) throws SQLException {
return results.getString("ACTION_STATEMENT");
}
protected String getTiming(ResultSet results) throws SQLException {
return results.getString("ACTION_TIMING");
}
protected String getManipulation(ResultSet results) throws SQLException {
return results.getString("EVENT_MANIPULATION");
}
protected String getCreateAt(ResultSet results) throws SQLException {
String created = results.getString("CREATED");
if (created == null) {
return "unknown";
}
return created;
}
}

View File

@@ -0,0 +1,14 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.provider.condition.TableCondition;
import java.sql.Connection;
import java.util.List;
public interface ColumnMetaProvider {
List<ColumnMeta> selectColumns(Connection connection, TableCondition condition);
}

View File

@@ -0,0 +1,14 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.DatabaseMeta;
import com.salpa.subject.domain.provider.condition.Condition;
import java.sql.Connection;
import java.util.Optional;
public interface DatabaseMetaProvider {
Optional<DatabaseMeta> select(Connection connection, Condition condition);
}

View File

@@ -0,0 +1,14 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.ForeignKeyMeta;
import com.salpa.subject.domain.provider.condition.TableCondition;
import java.sql.Connection;
import java.util.List;
public interface ForeignKeyMetaProvider {
List<ForeignKeyMeta> selectForeignKeys(Connection connection, TableCondition condition);
}

View File

@@ -0,0 +1,14 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.IndexMeta;
import com.salpa.subject.domain.provider.condition.TableCondition;
import java.sql.Connection;
import java.util.List;
public interface IndexMetaProvider {
List<IndexMeta> selectIndexes(Connection connection, TableCondition condition);
}

View File

@@ -0,0 +1,130 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.provider.jdbc.*;
import com.salpa.subject.domain.provider.maria.MariaTriggerMetaProvider;
import com.salpa.subject.domain.provider.mysql.MysqlTableTriggerMetaProvider;
import com.salpa.subject.domain.provider.oracle.OracleTriggerMetaProvider;
import com.salpa.subject.domain.provider.postgresql.PostgresqlTriggerMetaProvider;
import com.salpa.subject.domain.provider.sqlserver.SqlServerColumnMetaProvider;
import com.salpa.subject.domain.provider.sqlserver.SqlServerTableMetaProvider;
import com.salpa.subject.domain.provider.sqlserver.SqlServerTriggerMetaProvider;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
@Slf4j
public class MetaProviders {
public static DatabaseMetaProvider jdbc() {
var columnMetaProvider = new JdbcColumnMetaProvider();
var foreignKeyMetaProvider = new JdbcForeignKeyMetaProvider();
var indexMetaProvider = new JdbcIndexMetaProvider();
var triggerMetaProvider = new JdbcTriggerMetaProvider();
var tableMetaProvider = new JdbcTableMetaProvider(
columnMetaProvider,
indexMetaProvider,
triggerMetaProvider,
foreignKeyMetaProvider
);
return new JdbcDatabaseMetaProvider(tableMetaProvider);
}
public static DatabaseMetaProvider of(Connection connection) {
String url;
try {
DatabaseMetaData metaData = connection.getMetaData();
url = metaData.getURL();
} catch (SQLException e) {
log.warn("failed to get connect url, {}, fallback to jdbc provider", e.getMessage());
return jdbc();
}
if (url.contains(":sqlserver:")) {
return sqlServer();
}
if (url.contains(":mysql:")) {
return mysql();
}
if (url.contains(":postgresql:") || url.contains(":pgsql:")) {
return postgresql();
}
if (url.contains(":mariadb:")) {
return mariaDB();
}
if (url.contains(":oracle:")) {
return oracle();
}
return jdbc();
}
private static DatabaseMetaProvider mysql() {
var columnMetaProvider = new JdbcColumnMetaProvider();
var foreignKeyMetaProvider = new JdbcForeignKeyMetaProvider();
var indexMetaProvider = new JdbcIndexMetaProvider();
var triggerMetaProvider = new MysqlTableTriggerMetaProvider();
var tableMetaProvider = new JdbcTableMetaProvider(
columnMetaProvider,
indexMetaProvider,
triggerMetaProvider,
foreignKeyMetaProvider
);
return new JdbcDatabaseMetaProvider(tableMetaProvider);
}
private static DatabaseMetaProvider sqlServer() {
var columnMetaProvider = new SqlServerColumnMetaProvider();
var foreignKeyMetaProvider = new JdbcForeignKeyMetaProvider();
var indexMetaProvider = new JdbcIndexMetaProvider();
var triggerMetaProvider = new SqlServerTriggerMetaProvider();
var tableMetaProvider = new SqlServerTableMetaProvider(
columnMetaProvider,
indexMetaProvider,
foreignKeyMetaProvider,
triggerMetaProvider
);
return new JdbcDatabaseMetaProvider(tableMetaProvider);
}
private static DatabaseMetaProvider postgresql() {
var columnMetaProvider = new JdbcColumnMetaProvider();
var foreignKeyMetaProvider = new JdbcForeignKeyMetaProvider();
var indexMetaProvider = new JdbcIndexMetaProvider();
var triggerMetaProvider = new PostgresqlTriggerMetaProvider();
var tableMetaProvider = new JdbcTableMetaProvider(
columnMetaProvider,
indexMetaProvider,
triggerMetaProvider,
foreignKeyMetaProvider
);
return new JdbcDatabaseMetaProvider(tableMetaProvider);
}
private static DatabaseMetaProvider mariaDB() {
var columnMetaProvider = new JdbcColumnMetaProvider();
var foreignKeyMetaProvider = new JdbcForeignKeyMetaProvider();
var indexMetaProvider = new JdbcIndexMetaProvider();
var triggerMetaProvider = new MariaTriggerMetaProvider();
var tableMetaProvider = new JdbcTableMetaProvider(
columnMetaProvider,
indexMetaProvider,
triggerMetaProvider,
foreignKeyMetaProvider
);
return new JdbcDatabaseMetaProvider(tableMetaProvider);
}
private static DatabaseMetaProvider oracle() {
var columnMetaProvider = new JdbcColumnMetaProvider();
var foreignKeyMetaProvider = new JdbcForeignKeyMetaProvider();
var indexMetaProvider = new JdbcIndexMetaProvider();
var triggerMetaProvider = new OracleTriggerMetaProvider();
var tableMetaProvider = new JdbcTableMetaProvider(
columnMetaProvider,
indexMetaProvider,
triggerMetaProvider,
foreignKeyMetaProvider
);
return new JdbcDatabaseMetaProvider(tableMetaProvider);
}
}

View File

@@ -0,0 +1,14 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.TableMeta;
import com.salpa.subject.domain.provider.condition.Condition;
import java.sql.Connection;
import java.util.List;
public interface TableMetaProvider {
List<TableMeta> selectTables(Connection connection, Condition condition);
}

View File

@@ -0,0 +1,14 @@
package com.salpa.subject.domain.provider;
import com.salpa.subject.domain.data.TriggerMeta;
import com.salpa.subject.domain.provider.condition.TableCondition;
import java.sql.Connection;
import java.util.List;
public interface TriggerMetaProvider {
List<TriggerMeta> selectTriggers(Connection connection, TableCondition condition);
}

View File

@@ -0,0 +1,36 @@
package com.salpa.subject.domain.provider.condition;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
@SuperBuilder
@Getter
public class Condition {
@NonNull
private String databaseName;
private String schemaName;
@Builder.Default
@Builder.ObtainVia(method = "ignoreTableNameRegexes")
private Collection<Pattern> ignoreTableNamePatterns = Collections.emptyList();
@Builder.Default
@Builder.ObtainVia(method = "ignoreTableColumnNameRegexes")
private Collection<Pattern> ignoreTableColumnNamePatterns = Collections.emptyList();
public boolean tableIsIgnored(String tableName) {
return ignoreTableNamePatterns.stream().anyMatch(regex -> regex.matcher(tableName).matches());
}
public boolean columnIsIgnored(String column) {
return ignoreTableColumnNamePatterns.stream().anyMatch(regex -> regex.matcher(column).matches());
}
}

View File

@@ -0,0 +1,24 @@
package com.salpa.subject.domain.provider.condition;
import lombok.Getter;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;
@SuperBuilder
@Getter
public class TableCondition extends Condition {
@NonNull
private String tableName;
public static TableCondition of(Condition condition, String tableName) {
return TableCondition.builder()
.databaseName(condition.getDatabaseName())
.tableName(tableName)
.schemaName(condition.getSchemaName())
.ignoreTableNamePatterns(condition.getIgnoreTableNamePatterns())
.ignoreTableColumnNamePatterns(condition.getIgnoreTableColumnNamePatterns())
.build();
}
}

View File

@@ -0,0 +1,129 @@
package com.salpa.subject.domain.provider.jdbc;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.provider.ColumnMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
public class JdbcColumnMetaProvider implements ColumnMetaProvider {
@Override
public List<ColumnMeta> selectColumns(Connection connection, TableCondition tableCondition) {
try {
return doSelect(connection, tableCondition);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
private List<ColumnMeta> doSelect(Connection connection, TableCondition tableCondition) throws SQLException {
List<ColumnMeta> columnDocs = new ArrayList<>();
String databaseName = tableCondition.getDatabaseName();
String tableName = tableCondition.getTableName();
List<String> primaryKeyColumns = selectPrimaryKeyColumns(connection.getMetaData(), tableCondition);
ResultSet columnsResult;
try {
columnsResult = connection.getMetaData()
.getColumns(databaseName, tableCondition.getSchemaName(), tableName, null);
} catch (SQLException e) {
log.warn("warn: ignore columns in " + databaseName + "." + tableName + ", error: " + e.getMessage());
return columnDocs;
}
try {
while (columnsResult.next()) {
String columnName = columnsResult.getString("COLUMN_NAME");
if (tableCondition.columnIsIgnored(columnName)) {
if (log.isWarnEnabled()) {
log.warn("ignore column: " + columnName);
}
} else {
String defaultValue = columnsResult.getString("COLUMN_DEF");
String isNullable = columnsResult.getString("IS_NULLABLE");
if (isNullable.trim().equals("")) {
isNullable = "UNKNOWN";
}
if (defaultValue != null && defaultValue.trim().equals("")) {
defaultValue = "'" + defaultValue + "'";
}
Integer decimalDigits;
if (columnsResult.getObject("DECIMAL_DIGITS") == null) {
decimalDigits = null;
} else {
decimalDigits = columnsResult.getInt("DECIMAL_DIGITS");
}
Integer columnSize = columnsResult.getInt("COLUMN_SIZE");
String columnType = columnsResult.getString("TYPE_NAME");
String columnComment = columnsResult.getString("REMARKS");
int dataType = columnsResult.getInt("DATA_TYPE");
String autoIncrement = retrieveAutoIncrement(columnsResult);
ColumnMeta columnMeta = ColumnMeta.builder()
.name(columnName)
.dataType(dataType)
.type(columnType)
.size(columnSize)
.decimalDigits(decimalDigits)
.nullable(isNullable)
.autoIncrement(autoIncrement)
.comment(columnComment)
.defaultValue(defaultValue)
.isPrimaryKey(primaryKeyColumns.contains(columnName))
.build();
columnDocs.add(columnMeta);
}
}
} finally {
columnsResult.close();
}
return columnDocs;
}
private String retrieveAutoIncrement(ResultSet columnsResult) {
try {
return retrieveAutoIncrement(columnsResult, "IS_AUTOINCREMENT");
} catch (SQLException e) {
log.warn("get is_autoincrement failed, fallback to is_auto_increment, error: " + e.getMessage());
try {
// hive jdbc driver doesn't support is_autoincrement, fallback to is_auto_increment
return retrieveAutoIncrement(columnsResult, "is_auto_increment");
} catch (SQLException ex) {
log.warn("get is_auto_increment failed, error: " + ex.getMessage());
return "UNKNOWN";
}
}
}
private String retrieveAutoIncrement(ResultSet columnsResult, String columnName) throws SQLException {
String isAutoIncrement = columnsResult.getString(columnName);
if (isAutoIncrement.trim().equals("")) {
return "UNKNOWN";
}
return isAutoIncrement;
}
private List<String> selectPrimaryKeyColumns(DatabaseMetaData meta,
TableCondition tableCondition) throws SQLException {
ResultSet result = meta.getPrimaryKeys(tableCondition.getDatabaseName(),
tableCondition.getSchemaName(), tableCondition.getTableName());
try {
List<String> columns = new ArrayList<>();
while (result.next()) {
String columnName = result.getString("COLUMN_NAME");
columns.add(columnName);
}
return columns;
} finally {
result.close();
}
}
}

View File

@@ -0,0 +1,64 @@
package com.salpa.subject.domain.provider.jdbc;
import com.salpa.subject.domain.data.DatabaseMeta;
import com.salpa.subject.domain.data.TableMeta;
import com.salpa.subject.domain.provider.DatabaseMetaProvider;
import com.salpa.subject.domain.provider.TableMetaProvider;
import com.salpa.subject.domain.provider.condition.Condition;
import lombok.RequiredArgsConstructor;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@RequiredArgsConstructor
public class JdbcDatabaseMetaProvider implements DatabaseMetaProvider {
private final TableMetaProvider tableMetaProvider;
@Override
public Optional<DatabaseMeta> select(Connection connection, Condition condition) {
try {
DatabaseMetaData metaData = connection.getMetaData();
ResultSet catalogs = metaData.getCatalogs();
while (catalogs.next()) {
String catalogName = catalogs.getString("TABLE_CAT");
if (Objects.equals(condition.getDatabaseName(), catalogName)) {
List<TableMeta> tableDocs = tableMetaProvider.selectTables(connection, condition);
DatabaseMeta meta = DatabaseMeta.builder()
.productName(metaData.getDatabaseProductName())
.productVersion(metaData.getDatabaseProductVersion())
.databaseName(catalogName)
.schemaName(condition.getSchemaName())
.tables(tableDocs)
.build();
return Optional.of(meta);
}
}
ResultSet schemas = metaData.getSchemas();
while (schemas.next()) {
String schemaName = schemas.getString("TABLE_SCHEM");
if (Objects.equals(condition.getSchemaName(), schemaName)) {
List<TableMeta> tableDocs = tableMetaProvider.selectTables(connection, condition);
DatabaseMeta meta = DatabaseMeta.builder()
.productName(metaData.getDatabaseProductName())
.productVersion(metaData.getDatabaseProductVersion())
.databaseName(condition.getDatabaseName())
.schemaName(condition.getSchemaName())
.tables(tableDocs)
.build();
return Optional.of(meta);
}
}
return Optional.empty();
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -0,0 +1,92 @@
package com.salpa.subject.domain.provider.jdbc;
import com.salpa.subject.domain.data.ForeignKeyMeta;
import com.salpa.subject.domain.provider.ForeignKeyMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class JdbcForeignKeyMetaProvider implements ForeignKeyMetaProvider {
@Override
public List<ForeignKeyMeta> selectForeignKeys(Connection connection, TableCondition condition) {
String databaseName = condition.getDatabaseName();
String schemaName = condition.getSchemaName();
String tableName = condition.getTableName();
List<ForeignKeyMeta> foreignKeys = new ArrayList<>();
ResultSet keyResult = null;
try {
keyResult = connection.getMetaData().getImportedKeys(databaseName, schemaName, tableName);
while (keyResult.next()) {
String fkTableName = keyResult.getString("FKTABLE_NAME");
String fkColumnName = keyResult.getString("FKCOLUMN_NAME");
String fkName = keyResult.getString("FK_NAME");
String pkTableName = keyResult.getString("PKTABLE_NAME");
String pkColumnName = keyResult.getString("PKCOLUMN_NAME");
String pkName = keyResult.getString("PK_NAME");
int updateRule = keyResult.getInt("UPDATE_RULE");
int keySeq = keyResult.getInt("KEY_SEQ");
int deleteRule = keyResult.getInt("DELETE_RULE");
ForeignKeyMeta meta = ForeignKeyMeta.builder()
.keySeq(keySeq)
.fkTableName(fkTableName)
.fkColumnName(fkColumnName)
.fkName(fkName)
.pkTableName(pkTableName)
.pkColumnName(pkColumnName)
.pkName(pkName)
.updateRule(updateRuleConvert(updateRule))
.deleteRule(deleteRuleConvert(deleteRule))
.build();
foreignKeys.add(meta);
}
} catch (SQLException e) {
log.warn("warn: ignore foreign keys in " + databaseName + "." + tableName + ", " + e.getMessage());
} finally {
try {
if (keyResult != null) {
keyResult.close();
}
} catch (SQLException e) {
log.warn("warn: close key result error " + databaseName + "." + tableName + ", " + e.getMessage());
}
}
return foreignKeys;
}
private String updateRuleConvert(int updateRule) {
return doMapping(updateRule, "update");
}
private String deleteRuleConvert(int deleteRule) {
return doMapping(deleteRule, "delete");
}
private String doMapping(int rule, String type) {
if (rule == DatabaseMetaData.importedKeyCascade) {
return "CASCADE";
}
if (rule == DatabaseMetaData.importedKeyRestrict) {
return "CASCADE";
}
if (rule == DatabaseMetaData.importedKeyNoAction) {
return "RESTRICT";
}
if (rule == DatabaseMetaData.importedKeySetNull) {
return "SET_NULL";
}
if (rule == DatabaseMetaData.importedKeySetDefault) {
return "SET_DEFAULT";
}
log.warn("can not map foreign key " + type + " rule = " + rule);
return "";
}
}

View File

@@ -0,0 +1,66 @@
package com.salpa.subject.domain.provider.jdbc;
import com.salpa.subject.domain.data.IndexMeta;
import com.salpa.subject.domain.provider.IndexMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
@Slf4j
public class JdbcIndexMetaProvider implements IndexMetaProvider {
@Override
public List<IndexMeta> selectIndexes(Connection connection, TableCondition condition) {
try {
return doCreateIndexDocs(connection, condition);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
private List<IndexMeta> doCreateIndexDocs(Connection connection, TableCondition condition)
throws SQLException {
String databaseName = condition.getDatabaseName();
String tableName = condition.getTableName();
List<IndexMeta> indexMetas = new ArrayList<>();
ResultSet indexResults;
try {
indexResults = connection.getMetaData()
.getIndexInfo(databaseName, condition.getSchemaName(), tableName, false, false);
} catch (SQLException e) {
log.warn("warn: ignore " + databaseName + "." + tableName + ", error=" + e.getMessage());
return indexMetas;
}
Map<String, IndexMeta> metaGroupByName = new HashMap<>();
try {
while (indexResults.next()) {
Boolean nonUnique = indexResults.getBoolean("NON_UNIQUE");
String indexName = indexResults.getString("INDEX_NAME");
String columnName = indexResults.getString("COLUMN_NAME");
if (indexName == null) {
continue;
}
if (metaGroupByName.containsKey(indexName)) {
metaGroupByName.get(indexName).getColumnNames().add(columnName);
} else {
List<String> columns = new ArrayList<>();
columns.add(columnName);
IndexMeta indexMeta = IndexMeta.builder()
.name(indexName)
.columnNames(columns)
.isUniqueKey(Objects.equals(nonUnique, false))
.build();
metaGroupByName.put(indexName, indexMeta);
}
}
} finally {
indexResults.close();
}
return new ArrayList<>(metaGroupByName.values());
}
}

View File

@@ -0,0 +1,81 @@
package com.salpa.subject.domain.provider.jdbc;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.data.TableMeta;
import com.salpa.subject.domain.provider.*;
import com.salpa.subject.domain.provider.condition.Condition;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
@Slf4j
public class JdbcTableMetaProvider implements TableMetaProvider {
private final ColumnMetaProvider columnMetaProvider;
private final IndexMetaProvider indexMetaProvider;
private final TriggerMetaProvider triggerMetaProvider;
private final ForeignKeyMetaProvider foreignKeyMetaProvider;
@Override
public List<TableMeta> selectTables(Connection connection, Condition condition) {
try {
return doSelect(connection, condition);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
private List<TableMeta> doSelect(Connection connection, Condition condition) throws SQLException {
List<TableMeta> tableMetas = new ArrayList<>();
String databaseName = condition.getDatabaseName();
String[] tableTypes = {"TABLE", "VIEW"};
ResultSet tablesResult = connection.getMetaData()
.getTables(databaseName, condition.getSchemaName(), null, tableTypes);
try {
while (tablesResult.next()) {
String tableName = tablesResult.getString("TABLE_NAME");
if (condition.tableIsIgnored(tableName)) {
if (log.isWarnEnabled()) {
log.warn("ignored table: " + databaseName + "." + tableName);
}
} else {
String tableType = tablesResult.getString("TABLE_TYPE");
String tableComment = tablesResult.getString("REMARKS");
TableCondition tableCondition = TableCondition.of(condition, tableName);
List<ColumnMeta> columns = columnMetaProvider.selectColumns(connection, tableCondition);
if (columns.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("ignored table: " + databaseName + "." + tableName
+ ", caused by get empty columns");
}
continue;
}
TableMeta tableMeta = TableMeta.builder()
.name(tableName)
.type(tableType)
.comment(tableComment)
.columns(columns)
.foreignKeys(foreignKeyMetaProvider.selectForeignKeys(connection, tableCondition))
.indexes(indexMetaProvider.selectIndexes(connection, tableCondition))
.triggers(triggerMetaProvider.selectTriggers(connection, tableCondition))
.build();
tableMetas.add(tableMeta);
}
}
} finally {
tablesResult.close();
}
return tableMetas;
}
}

View File

@@ -0,0 +1,19 @@
package com.salpa.subject.domain.provider.jdbc;
import com.salpa.subject.domain.data.TriggerMeta;
import com.salpa.subject.domain.provider.TriggerMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import java.sql.Connection;
import java.util.Collections;
import java.util.List;
public class JdbcTriggerMetaProvider implements TriggerMetaProvider {
@Override
public List<TriggerMeta> selectTriggers(Connection connection, TableCondition condition) {
// note: jdbc not support get triggers
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,26 @@
package com.salpa.subject.domain.provider.maria;
import com.salpa.subject.domain.provider.AbstractSqlTriggerMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MariaTriggerMetaProvider extends AbstractSqlTriggerMetaProvider {
@Override
protected String sql(TableCondition condition) {
String sql = "SELECT \n"
+ " TRIGGER_NAME,\n"
+ " TRIGGER_SCHEMA,\n"
+ " TRIGGER_CATALOG,\n"
+ " EVENT_OBJECT_CATALOG,\n"
+ " EVENT_OBJECT_SCHEMA,\n"
+ " EVENT_OBJECT_TABLE,\n"
+ " ACTION_STATEMENT,\n"
+ " ACTION_TIMING,\n"
+ " EVENT_MANIPULATION,\n"
+ " CREATED\n"
+ "FROM information_schema.triggers "
+ "WHERE TRIGGER_SCHEMA = '%s' AND EVENT_OBJECT_TABLE = '%s'";
return String.format(sql, condition.getDatabaseName(), condition.getTableName());
}
}

View File

@@ -0,0 +1,34 @@
package com.salpa.subject.domain.provider.mysql;
import com.salpa.subject.domain.provider.AbstractSqlTriggerMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MysqlTableTriggerMetaProvider extends AbstractSqlTriggerMetaProvider {
@Override
protected String sql(TableCondition condition) {
String sql = "SELECT TRIGGER_CATALOG,\n"
+ " TRIGGER_SCHEMA,\n"
+ " TRIGGER_NAME,\n"
+ " EVENT_MANIPULATION,\n"
+ " EVENT_OBJECT_CATALOG,\n"
+ " EVENT_OBJECT_SCHEMA,\n"
+ " EVENT_OBJECT_TABLE,\n"
+ " ACTION_ORDER,\n"
+ " ACTION_CONDITION,\n"
+ " ACTION_STATEMENT,\n"
+ " ACTION_ORIENTATION,\n"
+ " ACTION_TIMING,\n"
+ " ACTION_REFERENCE_OLD_TABLE,\n"
+ " ACTION_REFERENCE_NEW_TABLE,\n"
+ " ACTION_REFERENCE_OLD_ROW,\n"
+ " ACTION_REFERENCE_NEW_ROW,\n"
+ " CREATED,\n"
+ " SQL_MODE,\n"
+ " DEFINER\n "
+ "FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA = '%s' AND EVENT_OBJECT_TABLE = '%s'";
return String.format(sql, condition.getDatabaseName(), condition.getTableName());
}
}

View File

@@ -0,0 +1,44 @@
package com.salpa.subject.domain.provider.oracle;
import com.salpa.subject.domain.provider.AbstractSqlTriggerMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;
@Slf4j
public class OracleTriggerMetaProvider extends AbstractSqlTriggerMetaProvider {
@Override
protected String sql(TableCondition condition) {
String sql = "SELECT trig.table_owner AS schema_name,\n"
+ " trig.table_name,\n"
+ " trig.owner AS trigger_schema_name,\n"
+ " trig.trigger_name as TRIGGER_NAME,\n"
+ " trig.trigger_type AS ACTION_TIMING,\n"
+ " trig.triggering_event as EVENT_MANIPULATION,\n"
+ " trig.status,\n"
+ " trig.trigger_body AS ACTION_STATEMENT \n"
+ "FROM sys.all_triggers trig\n"
+ " INNER JOIN sys.all_tables tab ON trig.table_owner = tab.owner\n"
+ " AND trig.table_name = tab.table_name\n"
+ "WHERE trig.base_object_type = 'TABLE' AND trig.owner = '%s' AND trig.TABLE_NAME = '%s'";
return String.format(sql, condition.getSchemaName(), condition.getTableName());
}
@Override
protected String getTriggerName(ResultSet results) throws SQLException {
String status = results.getString("status");
String name = Objects.requireNonNullElse(results.getString("trigger_name"), "")
+ " ("
+ status
+ ")";
return name;
}
@Override
protected String getCreateAt(ResultSet results) throws SQLException {
return "unknown";
}
}

View File

@@ -0,0 +1,25 @@
package com.salpa.subject.domain.provider.postgresql;
import com.salpa.subject.domain.provider.AbstractSqlTriggerMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PostgresqlTriggerMetaProvider extends AbstractSqlTriggerMetaProvider {
@Override
protected String sql(TableCondition condition) {
String sql = "SELECT trigger_name AS TRIGGER_NAME,\n"
+ " action_timing AS ACTION_TIMING,\n"
+ " event_manipulation AS EVENT_MANIPULATION,\n"
+ " action_statement AS ACTION_STATEMENT,\n"
+ " created AS CREATED,\n"
+ " trigger_catalog AS trigger_catalog,\n"
+ " TRIGGER_SCHEMA AS trigger_schema,\n"
+ " event_object_catalog AS target_catalog,\n"
+ " event_object_schema AS target_schema,\n"
+ " event_object_table AS target_table_name\n"
+ "FROM information_schema.triggers "
+ "WHERE trigger_catalog = '%s' AND trigger_schema = '%s' AND event_object_table = '%s';";
return String.format(sql, condition.getDatabaseName(), condition.getSchemaName(), condition.getTableName());
}
}

View File

@@ -0,0 +1,78 @@
package com.salpa.subject.domain.provider.sqlserver;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.provider.ColumnMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import com.salpa.subject.domain.provider.jdbc.JdbcColumnMetaProvider;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class SqlServerColumnMetaProvider implements ColumnMetaProvider {
private final ColumnMetaProvider columnMetaProvider = new JdbcColumnMetaProvider();
@Override
public List<ColumnMeta> selectColumns(Connection connection, TableCondition condition) {
Map<String, String> columnRemarksMap = getColumnRemarks(connection, condition);
return columnMetaProvider.selectColumns(connection, condition)
.stream()
.map(column -> {
String remark = columnRemarksMap.getOrDefault(column.getName(), "");
column.setComment(remark);
return column;
})
.collect(Collectors.toList());
}
private Map<String, String> getColumnRemarks(Connection connection,
TableCondition condition) {
String sql = "SELECT col.name AS COLUMN_NAME,\n"
+ " ep.value AS REMARKS\n"
+ "FROM sys.tables AS tab\n"
+ " INNER JOIN sys.columns AS col\n"
+ " ON tab.object_id = col.object_id\n"
+ " LEFT JOIN sys.extended_properties AS ep "
+ "ON ep.major_id = col.object_id AND ep.minor_id = col.column_id\n"
+ "WHERE tab.name LIKE ?\n"
+ " AND SCHEMA_NAME(tab.schema_id) LIKE ?\n"
+ "ORDER BY tab.name, column_id;";
Map<String, String> map = new HashMap<>();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, condition.getTableName());
preparedStatement.setString(2, condition.getSchemaName());
ResultSet result = preparedStatement.executeQuery();
while (result.next()) {
String name = result.getString("COLUMN_NAME");
String remarks = result.getString("REMARKS");
if (name == null || remarks == null) {
continue;
} else {
map.put(name, remarks);
}
}
} catch (SQLException e) {
log.error("", e);
} finally {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
// ignore
}
}
}
return map;
}
}

View File

@@ -0,0 +1,146 @@
package com.salpa.subject.domain.provider.sqlserver;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.data.TableMeta;
import com.salpa.subject.domain.provider.*;
import com.salpa.subject.domain.provider.condition.Condition;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
@RequiredArgsConstructor
@Slf4j
public class SqlServerTableMetaProvider implements TableMetaProvider {
private final ColumnMetaProvider columnMetaProvider;
private final IndexMetaProvider indexMetaProvider;
private final ForeignKeyMetaProvider foreignKeyMetaProvider;
private final TriggerMetaProvider triggerMetaProvider;
@Override
public List<TableMeta> selectTables(Connection connection, Condition condition) {
return doSelect(connection, condition);
}
private List<TableMeta> doSelect(Connection connection, Condition condition) {
List<TableMeta> tableMetas = new ArrayList<>();
String databaseName = condition.getDatabaseName();
Map<String, String> tableNameAndComment = tableNameAndCommentMap(connection, condition);
ResultSet tablesResult = null;
try {
tablesResult = connection.getMetaData()
.getTables(databaseName, condition.getSchemaName(), null, new String[]{"TABLE"});
while (tablesResult.next()) {
String tableName = tablesResult.getString("TABLE_NAME");
if (condition.tableIsIgnored(tableName)) {
if (log.isDebugEnabled()) {
log.debug("ignored table: " + databaseName + "." + tableName);
}
} else {
String tableType = tablesResult.getString("TABLE_TYPE");
String tableComment = tableNameAndComment.getOrDefault(tableName, "");
TableCondition tableCondition = TableCondition.of(condition, tableName);
List<ColumnMeta> columns = columnMetaProvider.selectColumns(connection, tableCondition);
if (columns.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("ignored table: " + databaseName + "." + tableName
+ ", caused by get empty columns");
}
continue;
}
TableMeta tableMeta = TableMeta.builder()
.name(tableName)
.type(tableType)
.comment(tableComment)
.columns(columns)
.foreignKeys(foreignKeyMetaProvider.selectForeignKeys(connection, tableCondition))
.indexes(indexMetaProvider.selectIndexes(connection, tableCondition))
.triggers(triggerMetaProvider.selectTriggers(connection, tableCondition))
.build();
tableMetas.add(tableMeta);
}
}
} catch (SQLException e) {
log.error("select tables error", e);
throw new IllegalStateException(e);
} finally {
if (tablesResult != null) {
try {
tablesResult.close();
} catch (SQLException e) {
// ignore
}
}
}
return tableMetas;
}
public Map<String, String> tableNameAndCommentMap(Connection connection, Condition condition) {
String sql = "SELECT sys.objects.name AS TABLE_NAME,\n"
+ " sys.objects.type_desc AS TABLE_TYPE,\n"
+ " CAST(extended_properties.value AS NVARCHAR(500)) AS REMARKS\n"
+ "FROM sys.objects\n"
+ " LEFT JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id\n"
+ " LEFT JOIN sys.extended_properties "
+ "ON sys.objects.object_id = sys.extended_properties.major_id\n"
+ "WHERE (type = 'U' OR type = 'V')\n"
+ " AND sys.extended_properties.minor_id = 0\n"
+ " AND sys.schemas.name LIKE ?;\n";
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, condition.getSchemaName());
ResultSet results = preparedStatement.executeQuery();
return tableNameAndCommentMap(results, connection, condition);
} catch (SQLException e) {
log.warn("get table meta failed {}", e.getMessage());
if (log.isDebugEnabled()) {
log.debug("get table meta failed", e);
}
return Collections.emptyMap();
} finally {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
// ignore
}
}
}
}
private Map<String, String> tableNameAndCommentMap(ResultSet tablesResult,
Connection connection,
Condition condition) throws SQLException {
Map<String, String> tableNameAndCommentMap = new HashMap<>();
try {
while (tablesResult.next()) {
String tableName = tablesResult.getString("TABLE_NAME");
if (condition.tableIsIgnored(tableName)) {
if (log.isDebugEnabled()) {
log.debug("ignored table: " + condition.getSchemaName() + "." + tableName);
}
} else {
String tableComment = tablesResult.getString("REMARKS");
if (!Objects.isNull(tableComment)) {
tableNameAndCommentMap.put(tableName, tableComment);
}
}
}
} finally {
tablesResult.close();
}
return tableNameAndCommentMap;
}
}

View File

@@ -0,0 +1,45 @@
package com.salpa.subject.domain.provider.sqlserver;
import com.salpa.subject.domain.provider.AbstractSqlTriggerMetaProvider;
import com.salpa.subject.domain.provider.condition.TableCondition;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SqlServerTriggerMetaProvider extends AbstractSqlTriggerMetaProvider {
@Override
protected String sql(TableCondition condition) {
String sql = "SELECT SCHEMA_NAME(tab.schema_id) + '.' + tab.name AS table_name,\n"
+ " trig.name AS TRIGGER_NAME,\n"
+ " trig.create_date AS CREATED,\n"
+ " CASE\n"
+ " WHEN is_instead_of_trigger = 1 THEN 'Instead of'\n"
+ " ELSE 'After' END AS ACTION_TIMING,\n"
+ " (CASE\n"
+ " WHEN OBJECTPROPERTY(trig.object_id, 'ExecIsUpdateTrigger') = 1\n"
+ " THEN 'Update '\n"
+ " ELSE '' END\n"
+ " + CASE\n"
+ " WHEN OBJECTPROPERTY(trig.object_id, 'ExecIsDeleteTrigger') = 1\n"
+ " THEN 'Delete '\n"
+ " ELSE '' END\n"
+ " + CASE\n"
+ " WHEN OBJECTPROPERTY(trig.object_id, 'ExecIsInsertTrigger') = 1\n"
+ " THEN 'Insert '\n"
+ " ELSE '' END\n"
+ " ) AS EVENT_MANIPULATION,\n"
+ " CASE\n"
+ " WHEN trig.[type] = 'TA' THEN 'Assembly (CLR) trigger'\n"
+ " WHEN trig.[type] = 'TR' THEN 'SQL trigger'\n"
+ " ELSE '' END AS [TYPE],\n"
+ " CASE\n"
+ " WHEN is_disabled = 1 THEN 'Disabled'\n"
+ " ELSE 'Active' END AS [status],\n"
+ " OBJECT_DEFINITION(trig.object_id) AS ACTION_STATEMENT\n"
+ "FROM sys.triggers trig\n"
+ " INNER JOIN sys.objects tab\n"
+ " ON trig.parent_id = tab.object_id\n"
+ "WHERE SCHEMA_NAME(tab.schema_id) = '%s' AND tab.name = '%s'";
return String.format(sql, condition.getSchemaName(), condition.getTableName());
}
}

View File

@@ -0,0 +1,19 @@
package com.salpa.subject.domain.render;
import com.salpa.subject.domain.data.DatabaseMeta;
import com.salpa.subject.domain.render.markdown.MarkdownTemplateRender;
import java.io.IOException;
import java.io.OutputStream;
public interface Render {
void rendering(DatabaseMeta meta, OutputStream outputStream) throws IOException;
static Render markdownRender(RenderConfig configuration) {
return new MarkdownTemplateRender(configuration);
}
}

View File

@@ -0,0 +1,65 @@
package com.salpa.subject.domain.render;
import com.salpa.subject.domain.data.ColumnMeta;
import com.salpa.subject.domain.data.IndexMeta;
import com.salpa.subject.domain.data.TriggerMeta;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.function.Function;
@Data
public class RenderConfig {
private Boolean renderTables = true;
private Boolean renderColumns = true;
private Boolean renderIndexes = true;
private Boolean renderTriggers = true;
private LinkedHashMap<String, Function<ColumnMeta, String>> columnTitleAndValueMapping =
columnTitleAndValueMapping();
private LinkedHashMap<String, Function<IndexMeta, String>> indexTitleAndValueMapping =
indexTitleAndValueMapping();
private LinkedHashMap<String, Function<TriggerMeta, String>> triggerTitleAndValueMapping =
triggerTitleAndValueMapping();
protected LinkedHashMap<String, Function<ColumnMeta, String>> columnTitleAndValueMapping() {
LinkedHashMap<String, Function<ColumnMeta, String>> mapping = new LinkedHashMap<>();
mapping.put("Name", ColumnMeta::getName);
mapping.put("Type", column -> {
String type;
if (column.getDecimalDigits() == null || column.getDecimalDigits().equals(0)) {
type = column.getType()
+ "(" + column.getSize().toString() + ")";
} else {
type = column.getType()
+ "(" + column.getSize().toString() + ", " + column.getDecimalDigits().toString() + ")";
}
return type;
});
return mapping;
}
protected LinkedHashMap<String, Function<IndexMeta, String>> indexTitleAndValueMapping() {
LinkedHashMap<String, Function<IndexMeta, String>> mapping = new LinkedHashMap<>();
mapping.put("Name", IndexMeta::getName);
mapping.put("IsUnique", index -> index.getIsUniqueKey() ? "YES" : "");
mapping.put("Columns", index -> String.join(", ", index.getColumnNames()));
return mapping;
}
protected LinkedHashMap<String, Function<TriggerMeta, String>> triggerTitleAndValueMapping() {
LinkedHashMap<String, Function<TriggerMeta, String>> mapping = new LinkedHashMap<>();
mapping.put("Name", TriggerMeta::getName);
mapping.put("Timing", trigger -> trigger.getTiming() + " " + trigger.getManipulation());
mapping.put("Statement", trigger -> trigger.getStatement().replace("\n", " ")
.replace("\r", " "));
mapping.put("Create At", TriggerMeta::getCreateAt);
return mapping;
}
}

View File

@@ -0,0 +1,111 @@
package com.salpa.subject.domain.render.markdown;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class MarkdownBuilder {
private static final String LINE = "\n";
private static final String DOUBLE_LINE = LINE + LINE;
private StringBuilder builder = new StringBuilder(1024);
private MarkdownBuilder() {
}
public static MarkdownBuilder builder() {
return new MarkdownBuilder();
}
public MarkdownBuilder primaryTitle(String title) {
builder.append("# ").append(title).append(DOUBLE_LINE);
return this;
}
public MarkdownBuilder secondTitle(String title) {
builder.append("## ").append(title).append(DOUBLE_LINE);
return this;
}
public MarkdownBuilder thirdTitle(String title) {
builder.append("### ").append(title).append(DOUBLE_LINE);
return this;
}
public MarkdownBuilder text(String text) {
builder.append(text).append(DOUBLE_LINE);
return this;
}
public MarkdownBuilder table(List<String> titles, List<List<String>> rows) {
if (titles == null || titles.isEmpty()) {
throw new IllegalArgumentException("titles must not be null or empty");
}
// build titles
builder.append("| ");
for (String title : titles) {
builder.append(title).append(" | ");
}
builder.append(LINE);
// build separators
builder.append("| ");
for (String title : titles) {
builder.append("------").append(" | ");
}
builder.append(LINE);
// build rows
for (List<String> row : rows) {
builder.append("| ");
for (String column : row) {
builder.append(column).append(" | ");
}
builder.append(LINE);
}
builder.append(LINE);
return this;
}
public MarkdownBuilder orderedList(List<String> list) {
for (int i = 0; i < list.size(); i++) {
builder.append(i + 1).append(". ").append(list.get(i)).append(LINE);
}
builder.append(LINE);
return this;
}
public MarkdownBuilder unorderedList(List<String> list) {
for (String item : list) {
builder.append("- ").append(item).append(LINE);
}
builder.append(LINE);
return this;
}
public MarkdownBuilder blockquotes(String content) {
builder.append("> ").append(content).append(DOUBLE_LINE);
return this;
}
public MarkdownBuilder code(String languageType, String statement) {
builder.append("```").append(languageType).append(LINE)
.append(statement)
.append("```")
.append(DOUBLE_LINE);
return this;
}
public MarkdownBuilder link(String text, String link) {
builder.append("[").append(text).append("]")
.append("(").append(link).append(")");
return this;
}
public String build() {
return builder.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More