1、数据库文档功能实现
This commit is contained in:
parent
46c2f0726e
commit
7f9e0e1159
17
pom.xml
17
pom.xml
|
|
@ -20,7 +20,7 @@
|
||||||
<spring-cloud.version>2020.0.4</spring-cloud.version>
|
<spring-cloud.version>2020.0.4</spring-cloud.version>
|
||||||
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
|
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
|
||||||
<alibaba.nacos.version>2.0.3</alibaba.nacos.version>
|
<alibaba.nacos.version>2.0.3</alibaba.nacos.version>
|
||||||
<spring-boot-admin.version>2.6.0</spring-boot-admin.version>
|
<spring-boot-admin.version>2.6.3</spring-boot-admin.version>
|
||||||
<spring-boot.mybatis>2.2.0</spring-boot.mybatis>
|
<spring-boot.mybatis>2.2.0</spring-boot.mybatis>
|
||||||
<swagger.fox.version>3.0.0</swagger.fox.version>
|
<swagger.fox.version>3.0.0</swagger.fox.version>
|
||||||
<swagger.core.version>1.6.2</swagger.core.version>
|
<swagger.core.version>1.6.2</swagger.core.version>
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
<spring-cloud-alicloud-oss.version>2.2.0.RELEASE</spring-cloud-alicloud-oss.version>
|
<spring-cloud-alicloud-oss.version>2.2.0.RELEASE</spring-cloud-alicloud-oss.version>
|
||||||
<elasticsearch.version>7.12.1</elasticsearch.version>
|
<elasticsearch.version>7.12.1</elasticsearch.version>
|
||||||
<redisson.version>3.12.0</redisson.version>
|
<redisson.version>3.12.0</redisson.version>
|
||||||
|
<screw.version>1.0.5</screw.version>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
@ -122,6 +123,20 @@
|
||||||
<version>${redisson.version}</version>
|
<version>${redisson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 实现数据库文档 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.smallbun.screw</groupId>
|
||||||
|
<artifactId>screw-core</artifactId>
|
||||||
|
<version>${screw.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<!-- 移除 Freemarker 依赖,采用 Velocity 作为模板引擎 -->
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.freemarker</groupId>
|
||||||
|
<artifactId>freemarker</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.xjs</groupId>
|
<groupId>com.xjs</groupId>
|
||||||
<artifactId>xjs-business-common</artifactId>
|
<artifactId>xjs-business-common</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
package com.ruoyi.common.core.utils;
|
package com.ruoyi.common.core.utils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import java.io.UnsupportedEncodingException;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import java.net.URLDecoder;
|
import com.ruoyi.common.core.constant.Constants;
|
||||||
import java.net.URLEncoder;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import java.util.Enumeration;
|
import com.ruoyi.common.core.text.Convert;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
@ -18,12 +13,19 @@ import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
import org.springframework.web.context.request.RequestAttributes;
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import com.ruoyi.common.core.constant.Constants;
|
|
||||||
import com.ruoyi.common.core.domain.R;
|
|
||||||
import com.ruoyi.common.core.text.Convert;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户端工具类
|
* 客户端工具类
|
||||||
*
|
*
|
||||||
|
|
@ -305,4 +307,20 @@ public class ServletUtils
|
||||||
DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
|
DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
|
||||||
return response.writeWith(Mono.just(dataBuffer));
|
return response.writeWith(Mono.just(dataBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回附件
|
||||||
|
*
|
||||||
|
* @param response 响应
|
||||||
|
* @param filename 文件名
|
||||||
|
* @param content 附件内容
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
|
||||||
|
// 设置 header 和 contentType
|
||||||
|
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
|
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
// 输出附件
|
||||||
|
IoUtil.write(response.getOutputStream(), false, content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function exportHtml(key) {
|
||||||
|
return request({
|
||||||
|
url: '/monitor/db-doc/export-html',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
params:key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportWord(key) {
|
||||||
|
return request({
|
||||||
|
url: '/monitor/db-doc/export-word',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
params:key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportMarkdown(key) {
|
||||||
|
return request({
|
||||||
|
url: '/monitor/db-doc/export-markdown',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
params:key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataSource() {
|
||||||
|
return request({
|
||||||
|
url: '/monitor/db-doc/getDataSource',
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
height: document.documentElement.clientHeight - 94.5 + "px;",
|
height: document.documentElement.clientHeight - 94.5 + "px",
|
||||||
loading: true,
|
loading: true,
|
||||||
url: this.src
|
url: this.src
|
||||||
};
|
};
|
||||||
|
|
@ -29,7 +29,7 @@ export default {
|
||||||
}, 300);
|
}, 300);
|
||||||
const that = this;
|
const that = this;
|
||||||
window.onresize = function temp() {
|
window.onresize = function temp() {
|
||||||
that.height = document.documentElement.clientHeight - 94.5 + "px;";
|
that.height = document.documentElement.clientHeight - 94.5 + "px";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,45 @@ export default {
|
||||||
const rspObj = JSON.parse(resText);
|
const rspObj = JSON.parse(resText);
|
||||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||||
Message.error(errMsg);
|
Message.error(errMsg);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// 下载 Excel 方法
|
||||||
|
excel(data, fileName) {
|
||||||
|
this.download0(data, fileName, 'application/vnd.ms-excel');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下载 Word 方法
|
||||||
|
word(data, fileName) {
|
||||||
|
this.download0(data, fileName, 'application/msword');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下载 Zip 方法
|
||||||
|
/*zip(data, fileName) {
|
||||||
|
this.download0(data, fileName, 'application/zip');
|
||||||
|
},*/
|
||||||
|
|
||||||
|
// 下载 Html 方法
|
||||||
|
html(data, fileName) {
|
||||||
|
this.download0(data, fileName, 'text/html');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下载 Markdown 方法
|
||||||
|
markdown(data, fileName) {
|
||||||
|
this.download0(data, fileName, 'text/markdown');
|
||||||
|
},
|
||||||
|
|
||||||
|
download0(data, fileName, mineType) {
|
||||||
|
// 创建 blob
|
||||||
|
let blob = new Blob([data], {type: mineType});
|
||||||
|
// 创建 href 超链接,点击进行下载
|
||||||
|
window.URL = window.URL || window.webkitURL;
|
||||||
|
let href = URL.createObjectURL(blob);
|
||||||
|
let downA = document.createElement("a");
|
||||||
|
downA.href = href;
|
||||||
|
downA.download = fileName;
|
||||||
|
downA.click();
|
||||||
|
// 销毁超连接
|
||||||
|
window.URL.revokeObjectURL(href);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import auth from '@/plugins/auth'
|
import auth from '@/plugins/auth'
|
||||||
import router, { constantRoutes, dynamicRoutes } from '@/router'
|
import router, {constantRoutes, dynamicRoutes} from '@/router'
|
||||||
import { getRouters } from '@/api/menu'
|
import {getRouters} from '@/api/menu'
|
||||||
import Layout from '@/layout/index'
|
import Layout from '@/layout/index'
|
||||||
import ParentView from '@/components/ParentView'
|
import ParentView from '@/components/ParentView'
|
||||||
import InnerLink from '@/layout/components/InnerLink'
|
import InnerLink from '@/layout/components/InnerLink'
|
||||||
|
|
@ -23,10 +23,12 @@ const permission = {
|
||||||
},
|
},
|
||||||
SET_TOPBAR_ROUTES: (state, routes) => {
|
SET_TOPBAR_ROUTES: (state, routes) => {
|
||||||
// 顶部导航菜单默认添加统计报表栏指向首页
|
// 顶部导航菜单默认添加统计报表栏指向首页
|
||||||
const index = [{
|
const index = [
|
||||||
path: 'index',
|
/*{
|
||||||
meta: { title: '统计报表', icon: 'dashboard' }
|
path: 'index',
|
||||||
}]
|
meta: {title: '统计报表', icon: 'dashboard'}
|
||||||
|
}*/
|
||||||
|
]
|
||||||
state.topbarRouters = routes.concat(index);
|
state.topbarRouters = routes.concat(index);
|
||||||
},
|
},
|
||||||
SET_SIDEBAR_ROUTERS: (state, routes) => {
|
SET_SIDEBAR_ROUTERS: (state, routes) => {
|
||||||
|
|
@ -35,7 +37,7 @@ const permission = {
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
// 生成路由
|
// 生成路由
|
||||||
GenerateRoutes({ commit }) {
|
GenerateRoutes({commit}) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
// 向后端请求路由数据
|
// 向后端请求路由数据
|
||||||
getRouters().then(res => {
|
getRouters().then(res => {
|
||||||
|
|
@ -44,7 +46,7 @@ const permission = {
|
||||||
const sidebarRoutes = filterAsyncRouter(sdata)
|
const sidebarRoutes = filterAsyncRouter(sdata)
|
||||||
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
||||||
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
|
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
|
||||||
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
|
rewriteRoutes.push({path: '*', redirect: '/404', hidden: true})
|
||||||
router.addRoutes(asyncRoutes);
|
router.addRoutes(asyncRoutes);
|
||||||
commit('SET_ROUTES', rewriteRoutes)
|
commit('SET_ROUTES', rewriteRoutes)
|
||||||
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
|
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportHtml">导出 HTML</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportWord">导出 Word</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportMarkdown">导出 Markdown</el-button>
|
||||||
|
<el-select @change="getHtml"
|
||||||
|
v-model="selectValue"
|
||||||
|
placeholder="请选择数据源"
|
||||||
|
size="mini"
|
||||||
|
style="margin-left: 30px;width: 150px;margin-right: 15px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in dataSourceKey"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-tooltip content="选择需要展示的数据库源" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 展示文档 -->
|
||||||
|
<div v-loading="loading" :style="'height:'+ height">
|
||||||
|
<i-frame :src="src"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import {exportHtml, exportWord, exportMarkdown, getDataSource} from "@/api/business/monitor/db/dbDoc";
|
||||||
|
import iFrame from "@/components/iFrame/index";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DBDoc",
|
||||||
|
components: {iFrame},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
height: document.documentElement.clientHeight - 94.5 + "px",
|
||||||
|
loading: true,
|
||||||
|
src: '',
|
||||||
|
|
||||||
|
dataSourceKey: [
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
label: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
selectValue: ''
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
mounted: function () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = false;
|
||||||
|
}, 100);
|
||||||
|
const that = this;
|
||||||
|
window.onresize = function temp() {
|
||||||
|
that.height = document.documentElement.clientHeight - 94.5 + "px";
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
|
||||||
|
this.getDataSource()
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 加载所有数据源key
|
||||||
|
getDataSource() {
|
||||||
|
getDataSource().then(res => {
|
||||||
|
this.dataSourceKey = res.data
|
||||||
|
|
||||||
|
//下拉框默认选中
|
||||||
|
this.selectValue=this.dataSourceKey[0].value
|
||||||
|
|
||||||
|
//根据数据源名去加载数据源
|
||||||
|
this.getHtml()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getHtml() {
|
||||||
|
// 加载 Html,进行预览
|
||||||
|
let param={
|
||||||
|
dataSourceKey:this.selectValue
|
||||||
|
}
|
||||||
|
exportHtml(param).then(response => {
|
||||||
|
let blob = new Blob([response], {type: 'text/html'});
|
||||||
|
this.src = window.URL.createObjectURL(blob);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/** 处理导出 HTML */
|
||||||
|
handleExportHtml() {
|
||||||
|
let param={
|
||||||
|
dataSourceKey:this.selectValue
|
||||||
|
}
|
||||||
|
exportHtml(param).then(response => {
|
||||||
|
this.$download.html(response, '数据库文档.html');
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 处理导出 Word */
|
||||||
|
handleExportWord() {
|
||||||
|
let param={
|
||||||
|
dataSourceKey:this.selectValue
|
||||||
|
}
|
||||||
|
exportWord(param).then(response => {
|
||||||
|
this.$download.word(response, '数据库文档.doc');
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 处理导出 Markdown */
|
||||||
|
handleExportMarkdown() {
|
||||||
|
let param={
|
||||||
|
dataSourceKey:this.selectValue
|
||||||
|
}
|
||||||
|
exportMarkdown(param).then(response => {
|
||||||
|
this.$download.markdown(response, '数据库文档.md');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -24,6 +24,17 @@
|
||||||
<artifactId>oshi-core</artifactId>
|
<artifactId>oshi-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 实现数据库文档 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.smallbun.screw</groupId>
|
||||||
|
<artifactId>screw-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--生成数据库文档模板-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.xjs</groupId>
|
<groupId>com.xjs</groupId>
|
||||||
<artifactId>xjs-business-common</artifactId>
|
<artifactId>xjs-business-common</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.xjs;
|
package com.xjs;
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
|
|
||||||
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
|
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
|
||||||
import com.ruoyi.common.security.config.ApplicationConfig;
|
import com.ruoyi.common.security.config.ApplicationConfig;
|
||||||
import com.ruoyi.common.security.feign.FeignAutoConfiguration;
|
import com.ruoyi.common.security.feign.FeignAutoConfiguration;
|
||||||
|
|
@ -16,11 +15,11 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author xiejs
|
* @author xiejs
|
||||||
* @desc 业务监控服务启动器
|
* @desc 业务监控服务启动器
|
||||||
* @create 2022-01-02
|
* @create 2022-01-02
|
||||||
*/
|
*/
|
||||||
//排除两个关于数据源的自动配置类、及seata配置类
|
//排除两个关于数据源的自动配置类、及seata配置类
|
||||||
@SpringBootApplication(exclude = {DynamicDataSourceAutoConfiguration.class,
|
@SpringBootApplication(exclude = {
|
||||||
DataSourceAutoConfiguration.class,
|
DataSourceAutoConfiguration.class,
|
||||||
SeataAutoConfiguration.class})
|
SeataAutoConfiguration.class})
|
||||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||||
|
|
@ -28,9 +27,9 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
// 开启线程异步执行
|
// 开启线程异步执行
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
// 自动加载类
|
// 自动加载类
|
||||||
@Import({ ApplicationConfig.class, FeignAutoConfiguration.class })
|
@Import({ApplicationConfig.class, FeignAutoConfiguration.class})
|
||||||
//自定义bean扫描,添加xjs路径下的bean
|
//自定义bean扫描,添加xjs路径下的bean
|
||||||
@ComponentScan(basePackages = {"com.ruoyi","com.xjs"})
|
@ComponentScan(basePackages = {"com.ruoyi", "com.xjs"})
|
||||||
@EnableCustomSwagger2
|
@EnableCustomSwagger2
|
||||||
@EnableRyFeignClients
|
@EnableRyFeignClients
|
||||||
public class XjsMonitorApp {
|
public class XjsMonitorApp {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
package com.xjs.dbmonitor;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.smallbun.screw.core.Configuration;
|
||||||
|
import cn.smallbun.screw.core.engine.EngineConfig;
|
||||||
|
import cn.smallbun.screw.core.engine.EngineFileType;
|
||||||
|
import cn.smallbun.screw.core.engine.EngineTemplateType;
|
||||||
|
import cn.smallbun.screw.core.execute.DocumentationExecute;
|
||||||
|
import cn.smallbun.screw.core.process.ProcessConfig;
|
||||||
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||||
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
|
||||||
|
import com.ruoyi.common.core.utils.ServletUtils;
|
||||||
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||||
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiejs
|
||||||
|
* @since 2022-04-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Api(tags = "业务模块 - 数据库文档")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/db-doc")
|
||||||
|
public class DbDocController {
|
||||||
|
@Resource
|
||||||
|
private DynamicDataSourceProperties dynamicDataSourceProperties;
|
||||||
|
|
||||||
|
private static final String FILE_OUTPUT_DIR = System.getProperty("java.io.tmpdir") + File.separator
|
||||||
|
+ "db-doc";
|
||||||
|
private static final String DOC_FILE_NAME = "数据库文档";
|
||||||
|
private static final String DOC_VERSION = "1.0.0";
|
||||||
|
private static final String DOC_DESCRIPTION = "谢哥数据库文档";
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("getDataSource")
|
||||||
|
@ApiOperation("获取所有数据源")
|
||||||
|
public AjaxResult getDataSource() {
|
||||||
|
Map<String, DataSourceProperty> datasource = dynamicDataSourceProperties.getDatasource();
|
||||||
|
ArrayList<Map<String,String>> list = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, DataSourceProperty> propertyEntry : datasource.entrySet()) {
|
||||||
|
Map<String,String> map = new HashMap();
|
||||||
|
map.put("value", propertyEntry.getKey());
|
||||||
|
map.put("label", propertyEntry.getKey());
|
||||||
|
list.add(map);
|
||||||
|
}
|
||||||
|
return AjaxResult.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-html")
|
||||||
|
@ApiOperation("导出 html 格式的数据文档")
|
||||||
|
@ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true",
|
||||||
|
dataTypeClass = Boolean.class)
|
||||||
|
public void exportHtml(@RequestParam(defaultValue = "true") Boolean deleteFile,
|
||||||
|
@RequestParam(defaultValue = "xjs-business") String dataSourceKey,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
doExportFile(EngineFileType.HTML, deleteFile, dataSourceKey,response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-word")
|
||||||
|
@ApiOperation("导出 word 格式的数据文档")
|
||||||
|
@ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true",
|
||||||
|
dataTypeClass = Boolean.class)
|
||||||
|
public void exportWord(@RequestParam(defaultValue = "true") Boolean deleteFile,
|
||||||
|
@RequestParam(defaultValue = "xjs-business") String dataSourceKey,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
doExportFile(EngineFileType.WORD, deleteFile,dataSourceKey, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-markdown")
|
||||||
|
@ApiOperation("导出 markdown 格式的数据文档")
|
||||||
|
@ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true",
|
||||||
|
dataTypeClass = Boolean.class)
|
||||||
|
public void exportMarkdown(@RequestParam(defaultValue = "true") Boolean deleteFile,
|
||||||
|
@RequestParam(defaultValue = "xjs-business") String dataSourceKey,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
doExportFile(EngineFileType.MD, deleteFile,dataSourceKey, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doExportFile(EngineFileType fileOutputType, Boolean deleteFile, String dataSourceKey,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
String docFileName = DOC_FILE_NAME + "_" + IdUtil.fastSimpleUUID();
|
||||||
|
String filePath = doExportFile(fileOutputType, docFileName,dataSourceKey);
|
||||||
|
String downloadFileName = DOC_FILE_NAME + fileOutputType.getFileSuffix(); //下载后的文件名
|
||||||
|
try {
|
||||||
|
// 读取,返回
|
||||||
|
ServletUtils.writeAttachment(response, downloadFileName, FileUtil.readBytes(filePath));
|
||||||
|
} finally {
|
||||||
|
handleDeleteFile(deleteFile, filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出文件,返回文件路径
|
||||||
|
*
|
||||||
|
* @param fileOutputType 文件类型
|
||||||
|
* @param fileName 文件名, 无需 ".docx" 等文件后缀
|
||||||
|
* @return 生成的文件所在路径
|
||||||
|
*/
|
||||||
|
private String doExportFile(EngineFileType fileOutputType, String fileName,String dataSourceKey) {
|
||||||
|
try (HikariDataSource dataSource = buildDataSource(dataSourceKey)) {
|
||||||
|
// 创建 screw 的配置
|
||||||
|
Configuration config = Configuration.builder()
|
||||||
|
.version(DOC_VERSION) // 版本
|
||||||
|
.description(DOC_DESCRIPTION) // 描述
|
||||||
|
.dataSource(dataSource) // 数据源
|
||||||
|
.engineConfig(buildEngineConfig(fileOutputType, fileName)) // 引擎配置
|
||||||
|
.produceConfig(buildProcessConfig()) // 处理配置
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 执行 screw,生成数据库文档
|
||||||
|
new DocumentationExecute(config).execute();
|
||||||
|
|
||||||
|
return FILE_OUTPUT_DIR + File.separator + fileName + fileOutputType.getFileSuffix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDeleteFile(Boolean deleteFile, String filePath) {
|
||||||
|
if (!deleteFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileUtil.del(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建数据源
|
||||||
|
* @param dataSourceKey 数据源key
|
||||||
|
*/
|
||||||
|
private HikariDataSource buildDataSource(String dataSourceKey) {
|
||||||
|
// 获得 DataSource 数据源,目前只支持首个
|
||||||
|
DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(dataSourceKey);
|
||||||
|
|
||||||
|
// 创建 HikariConfig 配置类
|
||||||
|
HikariConfig hikariConfig = new HikariConfig();
|
||||||
|
hikariConfig.setJdbcUrl(dataSourceProperty.getUrl());
|
||||||
|
hikariConfig.setUsername(dataSourceProperty.getUsername());
|
||||||
|
hikariConfig.setPassword(dataSourceProperty.getPassword());
|
||||||
|
hikariConfig.addDataSourceProperty("useInformationSchema", "true"); // 设置可以获取 tables remarks 信息
|
||||||
|
// 创建数据源'
|
||||||
|
return new HikariDataSource(hikariConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 screw 的引擎配置
|
||||||
|
*/
|
||||||
|
private static EngineConfig buildEngineConfig(EngineFileType fileOutputType, String docFileName) {
|
||||||
|
return EngineConfig.builder()
|
||||||
|
.fileOutputDir(FILE_OUTPUT_DIR) // 生成文件路径
|
||||||
|
.openOutputDir(false) // 打开目录
|
||||||
|
.fileType(fileOutputType) // 文件类型
|
||||||
|
.produceType(EngineTemplateType.velocity) // 文件类型
|
||||||
|
.fileName(docFileName) // 自定义文件名称
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 screw 的处理配置,一般可忽略
|
||||||
|
* 指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时,将生成指定表,其余表不生成、并跳过忽略表配置
|
||||||
|
*/
|
||||||
|
private static ProcessConfig buildProcessConfig() {
|
||||||
|
return ProcessConfig.builder()
|
||||||
|
.ignoreTablePrefix(Arrays.asList("QRTZ_", "ACT_")) // 忽略表前缀
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue