1、修改Mysql线程池逻辑

2、新增数据表信息查询接口
3、优化前端代码逻辑
4、新增业面业务功能提示信息窗口
This commit is contained in:
2019-04-10 16:32:13 +08:00
parent abc220eb35
commit 9623b70875
50 changed files with 1197 additions and 1195 deletions

4
.gitignore vendored
View File

@@ -9,4 +9,6 @@
/.idea/
/out/
/tmp/
/libs/
/libs/
apidoc.*

View File

@@ -35,6 +35,12 @@
<version>5.0.1</version>
</dependency>
<dependency>
<groupId>com.lxyer</groupId>
<artifactId>excel</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>

View File

@@ -1,167 +0,0 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MySql数据库表结构导出</title>
<!-- zui -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/css/zui.min.css">
<link rel="stylesheet" href="../res/css/zui-theme.css">
<link rel="stylesheet" href="../res/css/red-kit.css">
<style>
.input-group {
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container-fixed">
<row>
<div class="col-md-8">
<h3>导出数据库表结构(导出后使用wps打开)</h3>
<form>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default" type="button">jdbc.url</button>
</span>
<input id="url" type="text" class="form-control" placeholder="jdbc:mysql://127.0.0.1:3306" v-model="jdbc.url">
</div>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default" type="button">jdbc.user</button>
</span>
<input id="user" type="text" class="form-control" placeholder="请输入数据库用户" v-model="jdbc.user">
</div>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default" type="button">jdbc.pwd</button>
</span>
<input id="pwd" type="text" class="form-control" placeholder="请输入数据库密码" v-model="jdbc.pwd">
</div>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default" type="button">Database</button>
</span>
<input id="database" type="text" class="form-control" placeholder="请输入Database" v-model="jdbc.database">
</div>
<div class="input-group">
<div class="radio">
<label>
<input type="radio" v-model="cate" value="word" checked> 导出表结构到word
</label>
</div>
<div class="radio">
<label>
<input type="radio" v-model="cate" value="excel"> 导出表结构到excel
</label>
</div>
</div>
<button id="export" data-toggle="button" class="btn btn-primary" data-cate="word" type="button">导出表结构</button>
</form>
</div>
<div class="col-md-4" v-show="logs.total">
<h3>最近使用记录 <small>共 {{logs.total}} 条记录</small></h3>
<table class="table-bordered">
<thead>
<tr>
<th>IP</th>
<th>时间</th>
<th>导出数据类型</th>
</tr>
</thead>
<tbody>
<tr v-for="row in logs.rows">
<td v-text="row.remoteAddr"></td>
<td v-text="timeFmt(new Date(row.time*1), 'yyyy-MM-dd HH:mm:ss')"></td>
<td v-text="row.cate"></td>
</tr>
</tbody>
</table>
</div>
</row>
</div>
<!-- jQuery (ZUI中的Javascript组件依赖于jQuery) -->
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<!-- ZUI Javascript组件 -->
<script src="//cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/js/zui.min.js"></script>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>
function getData(key, defaultValue) {
var v = localStorage.getItem(key) || defaultValue || "";
if (typeof(v) == "string" && v.startsWith("{") && v.endsWith("}")) {
v = JSON.parse(v);
} else if (typeof(v) == "string" && v.startsWith("[") && v.endsWith("]")) {
v = JSON.parse(v);
}
return v;
}
var vm = new Vue({
el:".container-fixed",
data:{
jdbc:getData("jdbc", {url: "jdbc:mysql://192.168.202.11:3306/", user: "root"}),
cate: getData("cate", "word"),
logs: {}
},
watch: {
},
methods: {
loadLog: function() {
$.getJSON("/ddl/ddllist",function (json) {
vm.logs = json;
});
},
timeFmt: function (date,fmt){
var o = {
"M+" : date.getMonth()+1, //月份
"d+" : date.getDate(), //日
"H+" : date.getHours(), //小时
"m+" : date.getMinutes(), //分
"s+" : date.getSeconds(), //秒
"q+" : Math.floor((date.getMonth()+3)/3), //季度
"S" : date.getMilliseconds() //毫秒
};
if(/(y+)/.test(fmt))
fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length));
for(var k in o)
if(new RegExp("("+ k +")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
return fmt;
}
},
mounted: function () {
this.loadLog();
}
});
$("#export").click(function () {
localStorage.setItem("jdbc", JSON.stringify(vm.jdbc));
localStorage.setItem("cate", vm.cate);
$.post("/"+ vm.cate +"/build", {account:JSON.stringify(vm.jdbc)}, function (json) {
console.log(json)
if (json.code == 0) {
location.href = "/"+ vm.cate +"/download";
} else {
alert(json.message);
}
});
});
</script>
</body>
</html>

View File

@@ -1,321 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>高级查询</title>
<!-- zui -->
<link rel="stylesheet" href="../res/zui/css/zui.min.css">
<link rel="stylesheet" href="../res/css/zui-theme.css">
<link rel="stylesheet" href="../res/css/red-kit.css">
<style>
.item {
padding-top: 10px;
}
.table > thead > tr > th.sort:after{
display: inline-block;
margin-left: 5px;
font-family: ZenIcon;
font-size: 14px;
font-style: normal;
font-weight: normal;
font-variant: normal;
line-height: 1;
color: #808080;
text-transform: none;
content: '\e6bd';
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.table > thead > tr > th.sort-up:after {
color: #145ccd;
content: '\e6b9';
}
.table > thead > tr > th.sort-down:after {
color: #145ccd;
content: '\e6b8';
}
</style>
</head>
<body>
<div class="container-fixed">
<h3>高级查询 - [测试用例]</h3>
<div class="row">
<div class="col-md-6">
<select class="form-control" v-model="table" style="width: 300px;">
<option v-for="t in tables" :value="t.name">{{t.comment}}&nbsp;&nbsp;&nbsp;[{{t.name}}]</option>
</select>
</div>
<div class="col-md-6">
<div class="input-group pull-left">
<span class="input-group-btn">
<button class="btn btn-default" type="button">添加过滤条件</button>
</span>
<select class="form-control" v-model="addFilter" style="width: 130px;">
<option></option>
<option v-for="f in cfg.filters" :value="f.name" :disabled="f.add">{{f.label}}</option>
</select>
<span class="input-group-btn">
<button @click="findList" class="btn btn-primary" type="button"> 查询</button>
</span>
<span class="input-group-btn" style="padding-left: 10px">
<button @click="exportExcel" class="btn" type="button"> 导出</button>
</span>
</div>
</div>
<div class="col-md-12">
<div class="input-group item" v-for="(x,index) in formFilter">
<span class="input-group-addon" style="width: 130px">{{x.label}}</span>
<select class="form-control" :name="formFilter[index].name + '_cate'" style="width: 100px">
<option v-for="t in x.filterType" :value="t.name">{{t.remark}}</option>
</select>
<span class="input-group-addon fix-border fix-padding"></span>
<input type="text" class="form-control" v-model="para[formFilter[index].name]">
</div>
</div>
<div class="col-md-12" style="padding-top: 10px;overflow:auto;">
<table class="table table-bordered table-hover" style="width: 100%">
<thead>
<tr>
<th v-for="field in cfg.cols"
v-text="field.label"
:class="{'sort':field.order>0, 'sort-up':field.col==order.col && order.desc==1, 'sort-down':field.col==order.col && order.desc!=1}"
@click="sortEvent(field.col)"
>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in list.rows">
<td v-for="field in cfg.cols" v-title="dealField(row, field.col)" v-text="dealField(row, field.col)"></td>
</tr>
</tbody>
<!--<tfoot>
<tr>
<td :colspan="meta.field_label.length">
<ul class="pager pull-right">
<li class="previous">
<a>{{list.total}}条数据</a>
</li>
<li :class="['previous', {'disabled':limit.pn==1}]">
<a @click="findList(&#45;&#45; limit.pn)" href="javascript:;">«上一页</a>
</li>
<li :class="['next', {'disabled':limit.pn >= limit.total}]">
<a @click="findList(limit.pn = ++limit.pn )" href="javascript:;">»下一页</a>
</li>
</ul>
</td>
</tr>
</tfoot>-->
</table>
</div>
<div>
<ul class="pager pull-right" style="margin: 5px 10px">
<li class="previous">
<a style="border: 0;">共{{list.total}}条数据</a>
</li>
<li :class="['previous', {'disabled':limit.pn==1}]">
<a @click="findList(-- limit.pn)" href="javascript:;">«上一页</a>
</li>
<li :class="['next', {'disabled':limit.pn >= limit.total}]">
<a @click="findList(limit.pn = ++limit.pn )" href="javascript:;">»下一页</a>
</li>
<li class="previous">
<a style="border-bottom: 0;border-right: 0;border-top: 0;">到第<input v-model="limit.pn" style="width: 30px;height: 21px;">/ {{limit.total}}页</a>
</li>
<li class="previous">
<a @click="findList(limit.pn)" href="javascript:;">确定</a>
</li>
</ul>
</div>
</div>
</div>
<script src="../res/zui/lib/jquery/jquery.js"></script>
<script src="../res/zui/js/zui.js"></script>
<script src="../res/zui/lib/sortable/zui.sortable.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:".row",
data: {
cfg:{
cols: [],
filters: []
},
tables: [],
table: "",
filters: [],
formFilter: [
],
addFilter: "recompany",
para: {},
list: {},
limit: {pn: 1, ps: 10, total: 0},
order:{col:"", desc:1}
},
watch: {
addFilter: function (v) {
vm.cfg.filters.forEach(function (f) {
if (f.name == v) {
f["add"] = 1;
vm.add = "";
vm.formFilter.push(f);
}
})
},
table: function (v) {
vm.loadCfg();
vm.formFilter = [];
vm.findList();
},
list: function () {
var limit = vm.limit;
var list = vm.list;
var total = parseInt(list.total/limit.ps) + (list.total%limit.ps > 0 ? 1 : 0);
vm.limit["total"] = total;
},
limit: {
handler(a, b) {
},
immediate: true,
deep: true
},
},
methods: {
findList: function (n) {
var data = [];
for (k in vm.para) {
if (vm.para[k] != ''){
var d = {};
d["col"] = k;
d["value"] = vm.para[k];
d["type"] = $("select[name="+ k +"_cate]").val();
data.push(d);
}
}
data.push({col:"status", value: 9, type: "NOTEQUAL"});
var orders = [];
if (vm.order.col) {
orders.push(vm.order);
}
var fBean = {
keyService: vm.table,
filters: data,
orders: orders,
limit: vm.limit
}
$.post("/db/list", {fBean: JSON.stringify(fBean)}, function (json) {
vm.list = json.body;
});
},
exportExcel: function() {
var data = [];
for (k in vm.para) {
if (vm.para[k] != ''){
var d = {};
d["col"] = k;
d["value"] = vm.para[k];
d["type"] = $("select[name="+ k +"_cate]").val();
data.push(d);
}
}
data.push({col:"status", value: 9, type: "NOTEQUAL"});
var fBean = {
keyService: vm.table,
filters: data,
orders: [{col: "status", desc: -1}],
limit:{ps: 5}
}
location.href = "/export/data?fBean=" + JSON.stringify(fBean);
return;
},
loadTables: function() {
$.getJSON("/meta/alltable",function (json) {
vm.tables = json;
if (!vm.table) {
//vm.table = vm.tables[0].name;
vm.table = "basic_platform";
}
});
},
loadCfg: function () {
$.getJSON("/meta/listcfg", {key: vm.table}, function (json) {
vm.cfg = json;
});
},
dealField: function (bean, field) {
var str = "";
if (!bean || !field){
}else if (typeof(field) === 'function'){
str = field(bean);
}else if (field.indexOf("||") > 0){//处理字典数据
var dic_type = field.split("||")[1];
var v = bean[field.split("||")[0]];
str = kvExtDeal(dic_type, v);
}else if (field.indexOf("|") > 0){//处理字典数据
var dic_type = field.split("|")[1];
var v = bean[field.split("|")[0]];
str = v;//kvDeal(dic_type, v);
}
else if (field.indexOf("=") > 0){//处理字典数据
var fun = field.split("=")[1];
var v = bean[field.split("=")[0]];
return v;
//eval("str =$funs."+ fun +"('"+ v +"')");
}
else if (field.indexOf("-") > 0){
var name = field.split("-")[0];
var path = field.split("-")[1];
if (bean[name]){
var href = config.services.issct+"/downLoadFdfs?fileId="+encodeURI(bean[path])+"&filename="+encodeURI(bean[name]);
str = "<a href='"+ href +"' target='_blank'>"+ bean[name] +"</a>";
}
}else if (bean[field] === 0) { //特殊值 "0" 处理
str = "0";
}else if (bean[field] == "unknown") { //特殊值处理
str = "";
}else {
str = bean[field] || "";
}
return str;
},
sortEvent: function (col) {
if(vm.order.col == col){
vm.order.desc = - vm.order.desc
}else{
vm.order.col = col;
vm.order.desc = 1;
}
vm.findList();
}
},
mounted: function () {
this.loadTables();
}
});
</script>
</body>
</html>

View File

@@ -1,511 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- zui -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/css/zui.min.css">
<link rel="stylesheet" href="../res/css/zui-theme.css">
<link rel="stylesheet" href="../res/css/red-kit.css">
<style>
.row {
margin-top: 10px;
}
.panel-body {
padding: 0;
}
.icon-move{
padding-left: 5px;
}
</style>
</head>
<body>
<div class="container-fixed">
<div class="row">
<div class="col-md-8">
<div class="input-group list-head">
<span class="input-group-btn">
<button class="btn btn-default" type="button">选择业务类型</button>
</span>
<select class="form-control" v-model="table" style="width: 300px;">
<option v-for="t in tables" :value="t.name">{{t.comment}}&nbsp;&nbsp;&nbsp;[{{t.name}}]</option>
</select>
<span class="input-group-btn">
<button @click="status=1" :class="['btn',{'btn-primary':status==1}]" type="button"> 排序</button>
</span>
<span class="input-group-btn">
<button @click="status=2" :class="['btn',{'btn-primary':status==2}]" type="button"> 编辑属性</button>
</span>
<span class="input-group-btn" style="padding-left: 20px">
<button @click="status=5" :class="['btn',{'btn-primary':status==5}]" type="button"> 列表配置</button>
</span>
<span class="input-group-btn">
<button @click="status=6" :class="['btn',{'btn-primary':status==6}]" type="button"> 查询配置</button>
</span>
<span class="input-group-btn" style="padding-left: 20px">
<button @click="status=3" :class="['btn',{'btn-primary':status==3}]" type="button"> 导出配置</button>
</span>
<span class="input-group-btn">
<button @click="status=4" :class="['btn',{'btn-primary':status==4}]" type="button"> 导入配置</button>
</span>
<span class="input-group-btn" style="padding-left: 30px">
<button @click="metaSave()" :class="['btn',{'btn-primary':status!=0}]" type="button"> 保存修改</button>
</span>
</div>
</div>
<div class="col-md-8">
<table class="table-bordered">
<thead>
<tr style="background-color: #f1f1f1">
<td v-show="status==3 || status==4 || status==5"><input type="checkbox"></td>
<th v-show="status==1"></th>
<th>字段名</th>
<th>中文名</th>
<th>数据类型</th>
<th>输入类型</th>
<th>附加属性</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in meta.items">
<td v-show="status==3"><input type="checkbox" v-model="meta.exports" :value="item.name" class="form-control"></td>
<td v-show="status==4"><input type="checkbox" v-model="meta.imports" :value="item.name" class="form-control"></td>
<td v-show="status==5"><input type="checkbox" v-model="meta.shows" :value="item.name" class="form-control"></td>
<td v-show="status==1" class="icon icon-move"></td>
<td v-show="status!=2" v-text="item.name" style="background-color: rgb(235, 235, 228);"></td>
<td v-show="status!=2" v-text="item.label"></td>
<td v-show="status!=2" v-text="item.type"></td>
<td v-show="status!=2" v-text="item.inType"></td>
<td v-show="status!=2" v-text="item.inExt"></td>
<td v-show="status!=2" v-text="item.remark"></td>
<td v-show="status==2">
<input v-model="item" type="hidden">
<input :value="item.name" disabled class="form-control">
<input v-model="item.name" type="hidden">
<input name="name" type="hidden" :value="item.name">
</td>
<td v-show="status==2"><input v-model="item.label" class="form-control"></td>
<td v-show="status==2"><input v-model="item.type" class="form-control"></td>
<td v-show="status==2"><!--<input v-model="item.inType">-->
<select v-model="item.inType" class="form-control" style="width: 130px">
<option></option>
<option v-for="x in inTypes" :value="x">{{x}}</option>
</select>
</td>
<td v-show="status==2"><input v-model="item.inExt" class="form-control"></td>
<td v-show="status==2"><input v-model="item.remark" class="form-control"></td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-4" v-show="false">
<table class="table-bordered table-auto">
<thead>
<tr style="background-color: #f1f1f1">
<th></th>
<th>字段名</th>
<th>中文名</th>
<th>展示名</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in meta.items">
<td v-show="status!=2" class="icon icon-move"></td>
<td v-show="status!=2" v-text="item.name" style="background-color: rgb(235, 235, 228);"></td>
<td v-show="status!=2" v-text="item.label"></td>
<td v-show="status!=2"><input v-model="item.label" style="width: 100px;"></td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-4" v-show="status==5">
<div class="panel">
<div class="panel-heading">
列表展示的属性
</div>
<div id="show" class="panel-body">
<table class="table-bordered table-auto" style="width: 100%">
<tr style="background-color: #f1f1f1">
<th></th>
<th>字段名</th>
<th>中文名</th>
<th>支持排序</th>
</tr>
<tbody>
<tr v-for="(item, index) in meta.shows">
<td class="icon icon-move">
<input name="name" :value="item" type="hidden">
</td>
<td v-text="item" style="background-color: rgb(235, 235, 228);"></td>
<td v-text="">{{getFieldLabel(item)}}</td>
<td v-show="false"><input v-model="item.label" style="width: 100px;"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- exports -->
<div class="col-md-4" v-show="status==3">
<div class="panel">
<div class="panel-heading">
导出的属性配置
</div>
<div id="export" class="panel-body">
<table class="table-bordered table-auto" style="width: 100%">
<tr style="background-color: #f1f1f1">
<th></th>
<th>字段名</th>
<th>中文名</th>
<!--<th>展示名</th>-->
</tr>
<tbody>
<tr v-for="(item, index) in meta.exports">
<td class="icon icon-move">
<input name="name" :value="item" type="hidden">
</td>
<td v-text="item" style="background-color: rgb(235, 235, 228);"></td>
<td v-text="">{{getFieldLabel(item)}}</td>
<td v-show="false"><input v-model="item.label" style="width: 100px;"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- imports -->
<div class="col-md-4" v-show="status==4">
<div class="panel">
<div class="panel-heading">
导入的属性配置
</div>
<div id="import" class="panel-body">
<table class="table-bordered table-auto" style="width: 100%">
<tr style="background-color: #f1f1f1">
<th></th>
<th>字段名</th>
<th>中文名</th>
<!--<th>展示名</th>-->
</tr>
<tbody>
<tr v-for="(item, index) in meta.imports">
<td class="icon icon-move">
<input name="name" :value="item" type="hidden">
</td>
<td v-text="item" style="background-color: rgb(235, 235, 228);"></td>
<td v-text="">{{getFieldLabel(item)}}</td>
<td v-show="false"><input v-model="item.label" style="width: 100px;"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- findList -->
<div class="col-md-6" v-show="status==6">
<!--<div class="form-group">
<label class="checkbox-inline">
<input type="checkbox"> fdsaf
</label>
</div>-->
<div class="panel">
<div class="panel-heading">
高级查询的属性配置
</div>
<div id="find" class="panel-body" style="overflow:auto;">
<table class="table-bordered table-auto" style="width: 100%">
<tr style="background-color: #f1f1f1">
<th></th>
<th>字段名</th>
<th>中文名</th>
<th>支持查询类型</th>
</tr>
<tbody>
<tr v-for="(item, index) in meta.filters">
<td class="icon icon-move">
<input name="name" :value="item.name" type="hidden">
</td>
<td v-text="item.name" style="background-color: rgb(235, 235, 228);"></td>
<td v-text="">{{getFieldLabel(item.name)}}</td>
<td>
<label class="checkbox-inline" v-for="item in filterCate">
<input type="checkbox"> {{item}}
</label>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- jQuery (ZUI中的Javascript组件依赖于jQuery) -->
<script src="../res/zui/lib/jquery/jquery.js"></script>
<!-- ZUI Javascript组件 -->
<script src="../res/zui/js/zui.js"></script>
<script src="../res/zui/lib/sortable/zui.sortable.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!--<script src="https://unpkg.com/axios/dist/axios.min.js"></script>-->
<script>
var vm = new Vue({
el:".row",
data: {
inTypes: ["INPUT", "SELECT_EXT", "INPUT_DT"],
dataTypes: ["bigint(20)","varchar(255)","varchar(64)","varchar(32)","varchar(16)","int(11)", "int(3)","int(2)","datetime"],
filterCate:["EQUAL","NOTEQUAL","LIKE","IN","NOTEQUAL","LIKE","IN"],
tables: [],//所有的业务类型,【测试用】
meta: {
items: [],
shows: [],
exports: [],
imports: []
},//完整的元数据数据,
move: false,
status:0, //页面默认状态
table: "", //页面选择的业务类型
itemSort:[], //待保存的业务属性
oldItems:[], //不被修改的字段属性
itemEdit:{}, //待修改的字段属性
},
watch: {
status : function (v) {
this.canMove(v)
},
table: function (table) {
this.load(table);
},
"meta.items": {
handler: function (nv, ov) {
var itemNv = nv || [];
var itemOv = vm.oldItems || [];
if (itemOv.length == 0) return;
var itemEdit = [];
a:for (var i = 0; i < itemOv.length; i++) {
var attr = ["label", "name", "remark", "type", "inType"];
for (var j = 0; j < attr.length; j++) {
var k = attr[j];
if (itemOv[i][k] != itemNv[i][k]) {
itemEdit.push(itemNv[i]);
continue a;
}
}
}
vm.itemEdit = itemEdit;
},
deep: true
},
"meta.shows": function (v) {
console.log(v.length)
}
},
methods: {
load: function (cate) {
this.status = 0;
$.getJSON("/meta/info?key="+this.table,function (json) {
vm.meta = json;
var oldItems = [];
json.items.forEach(function (item) {
var d = {};
["label", "name", "remark", "type", "inType"].forEach(function (k) {
d[k] = item[k];
});
oldItems.push(d);
});
vm.oldItems = oldItems;
});
/*axios.get("/meta/info?key="+this.table).then(function (json) {
console.log("axios", json)
vm.meta = json.data;
});*/
},
getAllTables: function() {
$.getJSON("/meta/alltable",function (json) {
vm.tables = json;
if (!vm.table) {
//vm.table = vm.tables[0].name;
vm.table = "basic_platform";
}
});
},
canMove: function () {
if (this.status == 1) {
$('#sortableList,table>tbody').sortable({
selector: '.list-group-item, tr',
finish: function(e) {
var rows = e.list;
vm.itemSort = [];
for (var i=0;i<rows.length;i++) {
var item = $(rows[i]).find("input[name='name']").val();
vm.itemSort.push(item);
}
},
// 设置更多选项...
});
}
else if (this.status == 5) {
$('#show>table>tbody').sortable({
selector: 'tr',
finish: function (e) {
var rows = e.list;
var shows = [];
for (var i=0;i<rows.length;i++) {
var item = $(rows[i]).find("input[name='name']").val();
shows.push(item);
}
vm.meta.shows = shows;
}
});
}else if (this.status == 4) {
$('#import>table>tbody').sortable({
selector: 'tr',
finish: function (e) {
var rows = e.list;
var shows = [];
for (var i=0;i<rows.length;i++) {
var item = $(rows[i]).find("input[name='name']").val();
shows.push(item);
}
vm.meta.imports = shows;
}
});
}else if (this.status == 3) {
$('#export>table>tbody').sortable({
selector: 'tr',
finish: function (e) {
var rows = e.list;
var shows = [];
for (var i=0;i<rows.length;i++) {
var item = $(rows[i]).find("input[name='name']").val();
shows.push(item);
}
vm.meta.exports = shows;
}
});
} else {
$('table>tbody').sortable('destroy');
}
},
/**
* 保存元数据变更:
* 1、基础数据排序
* --> 传递元素的顺序,后台对元素顺序重排
* 2、基础数据属性修改
* --> 只提交被修改过的元素及属性数据,后端通过属性名称对应修改,
* 3、导出
* 导出排序
* --> 传递元素的顺序,后台对元素的顺利重排,(同基础元素排序)
* 导出元素加减
* --> 将元素完整传递到后台,进行覆盖保存
* 4、导入
* 导入排序
* 导入元素加减
*
*/
metaSave: function () {
if (vm.status == 1 && vm.itemSort.length > 0) {
$.getJSON("/meta/itemsort", {serviceKey:vm.table, items:JSON.stringify(vm.itemSort)}, function (json) {
console.log(json);
new $.zui.Messager('操作成功', {
type: 'info' // 定义颜色主题
,placement: "bottom-right"
}).show();
//vm.status = 0;
});
}
else if (vm.status == 2 && vm.itemEdit.length > 0) {
$.getJSON("/meta/itemupdate", {serviceKey:vm.table, items:JSON.stringify(vm.itemEdit)}, function (json) {
console.log(json);
//vm.status = 0;
new $.zui.Messager('操作成功', {
type: 'info' // 定义颜色主题
,placement: "bottom-right"
}).show();
vm.itemEdit = [];
});
}
else if (vm.status == 3 || vm.status == 4 || vm.status == 5) {
var urls = {3: "/meta/exportsort", 4: "/meta/importsort", 5: "/meta/showsort"};
var items = {3: vm.meta.exports, 4: vm.meta.imports, 5: vm.meta.shows};
$.getJSON(urls[vm.status], {serviceKey:vm.table, items:JSON.stringify(items[vm.status])}, function (json) {
console.log(json);
//vm.status = 0;
new $.zui.Messager('操作成功', {
type: 'info' // 定义颜色主题
,placement: "bottom-right"
}).show();
});
}
else if (vm.status == 5) {
$.getJSON("/meta/showsort", {serviceKey:vm.table, items:JSON.stringify(vm.meta.shows)}, function (json) {
console.log(json);
//vm.status = 0;
new $.zui.Messager('操作成功', {
type: 'info' // 定义颜色主题
,placement: "bottom-right"
}).show();
});
}
else {
new $.zui.Messager('操作成功', {
type: 'info' // 定义颜色主题
,placement: "bottom-right"
}).show();
}
},
getFieldLabel: function (col) {
for (var i = 0; i < vm.oldItems.length; i++) {
if (vm.oldItems[i].name == col) {
return vm.oldItems[i].label;
}
}
},
},
mounted: function (){
this.getAllTables();
//this.canMove();
/*for (var i = 0; i < 100; i++) {
this.load('record_ip')
}*/
}
});
</script>
</body>
</html>

View File

@@ -1,7 +1,8 @@
const plat = {
platList(params) { // 平台列表
platList(params = {}) { // 平台列表
params['platToken'] = 'xx'
return red.getX('/plat/list', params)
},
platSave({plat}) {
@@ -14,4 +15,8 @@ const plat = {
dbSave({plat}) {
return red.postX('/plat/db_save', {plat})
},
catalogList({dbAccount, dbPlatId}) { // database列表
console.log(dbAccount)
return red.postX('/_db/catalog_list', {dbAccount: JSON.stringify(dbAccount), dbPlatId})
}
}

View File

@@ -0,0 +1,5 @@
const qtask = {
qtaskList() {
return red.getX('_qtask/list',{})
}
}

View File

@@ -1,9 +1,7 @@
//import red from '../res/js/red'
const login = ({username, pwd}, callback) => {
$.post("/user/login", {username, pwd}, function (json) {
callback(json);
});
const login = ({username, pwd}) => {
return red.postX("/user/login", {username, pwd, platToken: 'xx'})
}
const logout = () => {

View File

@@ -91,7 +91,9 @@
<script src="./res/js/red.js" defer></script>
<script src="./api/meta.js" defer></script>
<script src="./api/plat.js" defer></script>
<script src="./api/qtask.js" defer></script>
<script src="./res/zui/lib/sortable/zui.sortable.js" defer></script>
<script src="./res/zui/lib/uploader/zui.uploader.min.js" defer></script>
<!--<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>-->
<script type="module">
import { logout } from './api/user.js'
@@ -108,7 +110,8 @@
{name: "MetaData", url: "/meta", nodes: [
/*{url:"/metadata/metatable/list.html", name:"TableList"},*/
{url:"/metadata/metatable/metaTable.html", name:"MetaTable", icon: "icon-table"},
/*{url:"/metadata/metaLink.html", name:"MetaLink"},*/
{url:"/metadata/metatable/import.html", name:"导入实体"},
{url:"/metadata/metaLink.html", name:"MetaLink"},
{url:"/metadata/metaService.html", name:"MetaService", icon:"icon-usecase"},
{url:"/metadata/dataList.html", name:"数据查询", icon:"icon-bug"},
]
@@ -130,7 +133,11 @@
{url:"/plat/db.html", name:"数据中心", icon: "icon-database"},
]
},
{name: "关于", url: "/single/about.html"}
{name: "关于", url: "/single", nodes: [
{url:"/single/about.html", name:"关于", icon:"icon-server"},
{url:"/single/metaFlow.html", name:"MetaKit使用", icon:"icon-server"},
]
},
],
menus: {},
@@ -156,7 +163,13 @@
}
page = page || this.page;
this.page = page;
$("#main").load(page.url);
$("#main").load(page.url, () => this.showInfo());
},
showInfo() {
if (red.getData('userName') === 'root') {
$('#main .info').show()
}
},
loadMain(item) {
this.menus = item.nodes || [item];
@@ -179,14 +192,14 @@
$this.closest('.nav-parent').addClass('active');
});
$("#main").load(this.page.url);
$("#main").load(this.page.url,() => this.showInfo());
//this.loadPage(this.page);
//监听浏览器窗口大小变化
function autoLeftHeight() {
var h = document.documentElement.clientHeight || document.body.clientHeight;
$("#left").attr("style", "height:" + (h - 50) + "px");
$("#main").attr("style", "height:" + (h - 50) + "px; overflow: auto;");
$("#main").attr("style", "height:" + (h - 90) + "px; overflow: auto;");
}
autoLeftHeight();

View File

@@ -6,6 +6,28 @@
</style>
<row class="data-list">
<div class="info">
<a @click="showInfo()" href="javascript:;"><i class="icon icon-info"></i></a>
</div>
<!-- info -->
<div class="modal fade" id="f-info">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button>
<h4 class="modal-title">功能概览</h4>
</div>
<div class="modal-body" style="text-align: center">
<img src="../res/img/meta_flow.png">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<h3>高级查询 - [测试用例]</h3>
<div class="col-md-6">
<select class="form-control" v-model="service" style="width: 300px;">
@@ -274,6 +296,9 @@
}
return '';
},
showInfo() {
$('#f-info').modal({moveable: true})
}
},
mounted() {
this.serviceList();

View File

@@ -1,29 +0,0 @@
<row class="meta-link">
</row>
<script>
var vm = new Vue({
el: ".meta-link",
data: {
},
watch: {
},
methods: {
},
mounted: function () {
var m = {"a.name": "lxyer", "a.`age`": 11}
console.log(m["a.name"])
console.log("---------")
for (var k in m) {
console.log(k)
}
}
});
</script>

View File

@@ -0,0 +1,52 @@
<row class="meta-link">
<div class="info">
<a @click="showInfo()" href="javascript:;"><i class="icon icon-info"></i></a>
</div>
<!-- info -->
<div class="modal fade" id="f-info">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button>
<h4 class="modal-title">功能概览</h4>
</div>
<div class="modal-body" style="text-align: center">
<p> 数据结构:<br>
系统中每个业务实体表都有唯一别名如:用户表-a,部门表-b,角色表-c
</p>
<img src="../res/img/meta_link.png">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</row>
<script>
var vm = new Vue({
el: ".meta-link",
data: {
},
watch: {
},
methods: {
showInfo() {
$('#f-info').modal({moveable: true})
}
},
mounted: function () {
var m = {"a.field": "lxyer", "a.`age`": 11}
console.log(m["a.field"])
console.log("---------")
for (var k in m) {
console.log(k)
}
}
});
</script>

View File

@@ -16,6 +16,48 @@
</style>
<row class="meta-service">
<div class="col-md-12" style="padding-top: 10px;">
<div class="info" style="padding-left: 10px">
<a @click="showInfo()" href="javascript:;"><i class="icon icon-info"></i></a>
</div>
<!-- info -->
<div class="modal fade" id="f-info">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button>
<h4 class="modal-title">功能概览</h4>
</div>
<div class="modal-body" style="text-align: left">
<div style="text-align: left">
<p>
<b>如何创建一个业务?</b><br><!-- <span style="color: #f1a325;font-size: 25px">so easy</span> -->
1、选择业务主表、填写名称、标识码<br>
2、保存<br>
3、业务维护(常规业务维护:列表,查询配置,导出配置,修改基本信息, 以及未开发的:详情配置,表单配置)<br>
</p>
<p>
<b>选择主表的意义,关联表信息业务中如何控制?</b><br>
业务主表选择某个表,也即当前业务主要围绕这个实体表进行;<br>
对于关联表只需要在MetaLink中建立关联即可此处选择了业务主表后会自动查询所有已经关联关联的业务表<br>
然后便可以轻松配置 列表要展示,导出哪些字段,查询过滤条件用哪些表的哪些字段,等等。。
</p>
</div>
<!--<p>数据存贮结构如下:</p>
<img src="../res/img/meta_service.png">-->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="input-group list-head">
<span class="input-group-btn">
@@ -25,6 +67,9 @@
<option v-for="item in services" :value="item.name"> {{item.name}}&nbsp;&nbsp;&nbsp;[{{item.comment}}]</option>
</select>
<span class="input-group-btn pull-left" style="padding-left: 20px">
<button @click="tipShow('开发中')" :class="['btn']" type="button"> 新建业务</button>
</span>
<span class="input-group-btn" style="padding-left: 20px">
@@ -40,6 +85,9 @@
<!--<span class="input-group-btn">
<button @click="status=4" :class="['btn',{'btn-primary':status==4}]" type="button"> 导入配置</button>
</span>-->
<!--<span class="input-group-btn" style="padding-left: 20px">
<button @click="tipShow()" :class="['btn',{'btn-primary':status==7}]" type="button"> 基本属性</button>
</span>-->
<span class="input-group-btn" style="padding-left: 30px">
<button @click="save()" :class="['btn',{'btn-primary':status!=0},{'disabled':status==0}]" type="button"> 保存</button>
@@ -218,6 +266,7 @@
//import { login } from '../api/user.js'
//import { getServiceInfo, getServiceDetail, getServiceList } from '/api/meta.js'
let { getServiceInfo, getServiceDetail, getServiceList } = meta;
let { showOk } = red
var vm = new Vue({
el: ".meta-service",
@@ -453,14 +502,14 @@
red.postX("/meta/itemsort", {
serviceKey: vm.table,
items: JSON.stringify(vm.itemSort)
});
}).then(() => showOk());
}
else if (vm.status == 2 && vm.itemEdit.length > 0) {
red.postX("/meta/itemupdate", {
serviceKey: vm.table,
items: JSON.stringify(vm.itemEdit)
});
}).then(() => showOk());
}
else if (vm.status == 3 || vm.status == 4 /*|| vm.status == 5*/) {
var urls = {3: "/meta/exportsort", 4: "/meta/importsort", 5: "/meta/showsort"};
@@ -471,7 +520,7 @@
red.postX(urls[vm.status], {
serviceKey: this.service,
items: dataStr
});
}).then(() => showOk());
}
else if (vm.status == 5) {
@@ -480,7 +529,7 @@
red.postX("/meta/showsort", {
serviceKey: vm.service,
items: dataStr
});
}).then(() => showOk());
}
else if (vm.status == 6) {
@@ -493,18 +542,24 @@
red.postX("/meta/filter_update", {
serviceKey: vm.service,
filters: JSON.stringify(red.replace$(filterData))
});
}).then(() => showOk());
}
else if (vm.status == 7) {
console.log(vm.row);
red.postX("/meta/dbplatupdate", vm.row)
red.postX("/meta/dbplatupdate", vm.row).then(() => showOk())
}
else {
red.showMsg();
}
},
tipShow() {
red.showMsg({msg: '开发中'})
},
showInfo() {
$('#f-info').modal({moveable: true})
}
},

View File

@@ -0,0 +1,336 @@
<style>
.importDev {
padding-top: 5px;
}
.checkbox-inline:first-child{
left: 10px;
margin-right: 10px!important;
}
.checkbox input[type=checkbox], .checkbox-inline input[type=checkbox], .radio input[type=radio], .radio-inline input[type=radio] {
margin-top: 12px;
}
.hv {
color: #ea644a!important;
}
.hv:focus,.hv:hover {
color: #8b8a15!important;
}
.active>.hv{
color: #8b8a15!important;
}
.tlist{
height:100%; overflow: auto;
}
.tlist>li.active>a,
.tlist>li.active>a:focus,
.tlist>li.active>a:hover,
.tlist>li>a:hover {
background-color: #92b0cb;
}
</style>
<!-- 对话框HTML -->
<div class="importDev">
<div class="info">
<a @click="showInfo()" href="javascript:;"><i class="icon icon-info"></i></a>
</div>
<!-- info -->
<div class="modal fade" id="f-info">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button>
<h4 class="modal-title">功能概览</h4>
</div>
<div class="modal-body">
<p>
本地导入、从数据源导入流程相似,都是: 介质中读取可用业务实体 => 选择要导入的实体 => 保存实体到元数据MetaTable
</p>
<img src="../res/img/table_import_flow.png">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a href="###" data-target="#tab2Content1" data-toggle="tab">从本地上传</a></li>
<li><a href="###" data-target="#tab2Content2" data-toggle="tab">从数据源导入</a></li>
<li><a href="###" data-target="#tab2Content3" data-toggle="tab">其他</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane fade active in" id="tab2Content1">
<div id="tableUploader" class="uploader">
<!--<div class="uploader-message text-center">
<div class="content"></div>
<button type="button" class="close">×</button>
</div>-->
<table class="table table-bordered">
<thead>
<tr>
<th colspan="3">选择上传文件</th>
<!--<th style="width: 100px">大小</th>
<th style="width: 160px; text-align: center;">状态/操作</th>-->
</tr>
</thead>
<tbody class="uploader-files">
<tr class="file template">
<td style="width: 38px; padding: 3px"><div class="file-icon"></div></td>
<td style="padding: 0">
<div style="position: relative; padding: 8px;">
<strong class="file-field"></strong>
<div class="file-progress-bar"></div>
</div>
</td>
<td><span class="file-size text-muted"></span></td>
<!--<td class="actions text-right" style="padding: 0 4px;">
<div class="file-status" data-toggle="tooltip" style="margin: 8px;"><i class="icon"></i> <span class="text"></span></div>
<a data-toggle="tooltip" class="btn btn-link btn-download-file" target="_blank"><i class="icon icon-download-alt"></i></a>
<button type="button" data-toggle="tooltip" class="btn btn-link btn-reset-file" title="Repeat"><i class="icon icon-repeat"></i></button>
<button type="button" data-toggle="tooltip" class="btn btn-link btn-rename-file" title="Rename"><i class="icon icon-pencil"></i></button>
<button type="button" data-toggle="tooltip" title="Remove" class="btn btn-link btn-delete-file"><i class="icon icon-trash text-danger"></i></button>
</td>-->
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4" style="padding: 4px 0">
<div style="position: relative;">
<div class="pull-right text-muted" style="margin-top: 5px;margin-right: 5px">
<p v-show="sheet">数据文件已上传,下面<code class="text-danger">红色</code> 部分已经导入过的数据表</p>
</div>
<button type="button" class="btn btn-link uploader-btn-browse"><i class="icon icon-plus"></i> 选择数据文件</button>
<button type="button" class="btn btn-link uploader-btn-start"><i class="icon icon-cloud-upload"></i> 上传文件</button>
</div>
</td>
</tr>
</tfoot>
</table>
<div class="row" v-show="sheet">
<div class="col-xs-2 sheet-cell">
<div style="padding-left: 10px;background-color: #ccc;width: 100%"> Excel-Sheet</div>
<ul class="nav nav-tabs nav-stacked tlist" style="height: 100%">
<li class="checkbox-inline clearfix" v-for="item in sheetArr" >
<input type="checkbox"
v-model="ck"
:value="sheetData[item]['field']"
:disabled="item=='表说明' || item=='表名称' || sheetData[item]['hv']==1"
>
<a :class="[{hv: sheetData[item]['hv']==1 }]" href="javascript:;" @click="sheet=item" data-target="#tab3Content1" data-toggle="tab" v-text="item"></a>
</li>
</ul>
<p v-show="ck && ck.length">已选择 <code v-text="ck.length||0"></code> 个实体待导入</p>
</div>
<div class="col-xs-8">
<div style="padding-left: 10px;background-color: #ccc;width: 100%"> Sheet-Detail</div>
<div class="tab-content col-xs-9">
<div class="tab-pane fade active in" id="">
<table class="table-bordered" style="width: 100%">
<caption class="" v-show="sheet && (sheet!='表说明' || sheet!='表名称')">
表名称: <span v-text="metaTable.field"></span>,中文名:<span v-text="metaTable.comment"></span>
</caption>
<thead>
<tr style="background-color: #f1f1f1" v-show="sheet && sheet!='表说明' && sheet!='表名称'">
<th>字段名</th>
<th>中文名</th>
<th>数据类型</th>
<th>输入类型</th>
<th>附加属性</th>
<th>备注</th>
</tr>
<tr style="background-color: #f1f1f1" v-show="sheet && (sheet=='表说明' || sheet=='表名称')">
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in metaTable.items">
<td v-text="item.field"></td>
<td v-text="item.label"></td>
<td v-text="item.type"></td>
<td v-text="item.inType"></td>
<td v-text="item.inExt"></td>
<td v-text="item.remark"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!--<div class="col-xs-2">
<div style="padding-left: 10px;background-color: #ccc;width: 100%"> Checked-Sheet</div>
<ul class="nav nav-tabs nav-stacked tlist" style="height: 100%">
<li class="checkbox-inline clearfix" v-for="item in ck" >
<a href="javascript:;" @click="sheet=getSheetName(item)" data-target="#tab3Content1" data-toggle="tab" v-text="getSheetName(item)"></a>
</li>
</ul>
<p v-show="ck && ck.length">已选择 <code v-text="ck.length||0"></code> 个实体待导入</p>
</div>-->
</div>
</div>
</div>
<div class="tab-pane fade" id="tab2Content2">
<div class="form-group">
<label class="col-sm-1">数据源</label>
<div class="col-md-2 col-sm-10">
<select class="form-control">
<option v-for="item in dbPlats" :value="item" v-text="item.field"></option>
</select>
</div>
</div>
<div class="clearfix"></div>
<!--<p>1、选择数据源</p>
<p>2、选择查询到的数据源实体</p>
<p>3、导入选中的实体</p>-->
</div>
<div class="tab-pane fade" id="tab2Content3">
<p>其他一些定制的特殊格式的 实体数据形式</p>
</div>
</div>
<div class="modal-footer">
<button @click="ck=[]" type="button" class="btn btn-default" data-dismiss="modal">取消选择</button>
<button @click="back()" type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button @click="saveSheet()" type="button" class="btn btn-primary">确定</button>
</div>
</div>
<script>
let {dbList} = plat
var vm = new Vue({
el: ".importDev",
data: {
filePath: '',
importStatus: false,
sheetData: {}, // {name1: {}}
sheetArr: [], // [name1,name2,]
sheet: '',
ck: [],
metaTable: {items: []},
// 从数据源导入实体
dbPlats: red.getData('dbPlats'),
dbPlatId: '',
cache: {
local: {
sheetData: {},
sheetArr: [],
sheet: '',
ck: [],
metaTable: {items: []},
},
dbPlat: {}
},
},
watch: {
sheetData: function (v) {
var arr = [];
var ck = [];
for (k in v) {
let field = v[k]['field'];
if (k!='表说明' && k!='表名称' && v[k]['hv'] != 1) {
ck.push(field);
}
arr.push(k);
}
this.sheetArr = arr;
this.ck = ck;
if (arr.length > 0) {
this.sheet = arr[0]
} else {
this.sheet = "";
}
},
sheet: function (v) {
this.metaTable = this.sheetData[v];
console.log(this.metaTable)
if (this.metaTable['load'] != 1) {
this.loadSheetData();
}
}
},
methods: {
loadSheets: function(filePath) {
if(!filePath) {
return false;
}
red.post("/file/data",{filePath: filePath}, function (json) {
console.log(json)
vm.sheetData = json;
})
},
loadSheetData: function() {
let sheetName = this.sheet;
red.post("/file/sheet_data",{filePath: this.filePath, sheetName: sheetName}, function (json) {
json["hv"] = vm.sheetData[sheetName]["hv"];
json["load"] = 1;
vm.sheetData[sheetName] = json;
if (vm.sheet == sheetName) {
vm.metaTable = json;
}
})
},
saveSheet: function () {
if (!this.ck || this.ck.length == 0) {
red.showMsg({msg: "请选择导入数据", type:"danger", placement: "top"});
return false;
}
red.post("/file/import_metatable", {sheetArr: JSON.stringify(this.ck), filePath: this.filePath});
},
getSheetName: function (tableName) {
for(k in this.sheetData) {
if (this.sheetData[k]['field'] == tableName) {
return k;
}
}
//return "";
},
back() {
$("#main").load('/metadata/metatable/metaTable.html')
},
showInfo() {
$('#f-info').modal({moveable: true})
}
},
mounted: function () {
// 初始化文件上传组件
$('#tableUploader').uploader({
url: '/upload/x',
responseHandler: function (res, file) {
var file = JSON.parse(res.response)["body"][0];
vm.loadSheets(file["filePath"]);
vm.importStatus = true;
vm.filePath = file["filePath"];
},
filters: {
mime_types: [
{title: 'Excel文件', extensions: 'xls,xlsx'},
],
prevent_duplicates: true
}
});
dbList().then(res => this.dbPlats = res.rows)
var h = document.documentElement.clientHeight || document.body.clientHeight;
$(".sheet-cell").attr("style", "height:" + (h - 265) + "px;margin-bottom:20px;");
}
});
</script>

View File

@@ -12,6 +12,10 @@
<option v-for="item in tables" :value="item.name"> {{item.name}}&nbsp;&nbsp;&nbsp;[{{item.comment}}]</option>
</select>
<span class="input-group-btn pull-left" style="padding-left: 20px">
<button @click="loadImportPage()" :class="['btn']" type="button"> 导入实体</button>
</span>
<span class="input-group-btn">
<button @click="status=2" :class="['btn',{'btn-primary':status==2}]" type="button"> 字段编辑</button>
</span>
@@ -283,7 +287,8 @@
</row>
<script>
let { getTableList,getTableDetail } = meta;
let {getTableList, getTableDetail} = meta;
let {showOk} = red
var vm = new Vue({
el: ".meta-list",
@@ -493,30 +498,30 @@
red.postX("/meta/itemsort", {
serviceKey: vm.table,
items: JSON.stringify(vm.itemSort)
});
}).then(() => showOk())
}
else if (vm.status == 2 && vm.itemEdit.length > 0) {
red.postX("/meta/itemupdate", {
serviceKey: vm.table,
items: JSON.stringify(vm.itemEdit)
});
}).then(() => showOk())
}
else if (vm.status == 3 || vm.status == 4 || vm.status == 5) {
/*else if (vm.status == 3 || vm.status == 4 || vm.status == 5) {
var urls = {3: "/meta/exportsort", 4: "/meta/importsort", 5: "/meta/showsort"};
var items = {3: vm.meta.exports, 4: vm.meta.imports, 5: vm.meta.shows};
red.post(urls[vm.status], {
serviceKey: vm.table,
items: JSON.stringify(items[vm.status])
});
}
})
}*/
else if (vm.status == 5) {
red.post("/meta/showsort", {
serviceKey: vm.table,
items: JSON.stringify(vm.meta.shows)
});
}).then(() => showOk())
}
else if (vm.status == 6) {
@@ -524,13 +529,13 @@
red.post("/meta/filter_update", {
serviceKey: vm.table,
filters: JSON.stringify(vm.filters)
});
})
}
else if (vm.status == 7) {
console.log(vm.row);
red.postX("/meta/dbplatupdate", {metaTable: JSON.stringify(vm.row)})
red.postX("/meta/dbplatupdate", {metaTable: JSON.stringify(vm.row)}).then(() => showOk())
}
else {
@@ -546,6 +551,9 @@
return vm.oldItems[i].label;
}
}
},
loadImportPage() {
$("#main").load('/metadata/metatable/import.html')
}
},

View File

@@ -1,5 +1,30 @@
<row class="plat">
<div class="info">
<a @click="showInfo()" href="javascript:;"><i class="icon icon-info"></i></a>
</div>
<!-- info -->
<div class="modal fade" id="f-info">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button>
<h4 class="modal-title">功能概览</h4>
</div>
<div class="modal-body">
<p>
数据平台可以获取数据的平台除java-method类型的qtask 实列都会对应绑定相应的数据平台)
</p>
<img src="../res/img/db_plat.png">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- head -->
<div class="col-md-11">
<h3 v-text="cfg.title"></h3>
@@ -54,6 +79,17 @@
<input v-model="row.name" class="form-control" id="queryId" placeholder="请输入 平台名称">
</div>
</div>
<div class="form-group">
<label for="queryId" class="col-sm-2">平台类型</label>
<div class="col-md-2 col-sm-10">
<select class="form-control" v-model="row.cate">
<option value="mysql">MySql</option>
<option value="es">ES</option>
<option value="htttp">Htttp</option>
<option value="method">Method</option>
</select>
</div>
</div>
<div class="form-group">
<label for="url" class="col-sm-2">连接URL</label>
@@ -100,7 +136,8 @@
</row>
<script>
let {dbList, dbSave} = plat
let {showOk} = red
let {dbList, dbSave, catalogList} = plat
var vm = new Vue({
el: ".plat",
@@ -150,24 +187,11 @@
},
methods: {
findList: function () {
dbList().then(res => {
vm.list = res;
})
/*var para = {
doc:"db_plat",
shows: JSON.stringify(["_id", "name", "url", "user", "pwd", "catalogs", "remark", "status"])
};
red.getJSON("/meta/find", para, function (json) {
for (var i=0; i<json.rows.length; i++) {
json.rows[i]["pulse"] = "";
}
vm.list = json;
})*/
findList() {
dbList().then(res => vm.list = res)
},
update: function (kv, row) {
red.post("/meta/save", {
/*update: function (kv, row) {
red.post('/meta/save', {
_id: row._id,
doc: JSON.stringify(kv)
}, function (json) {
@@ -175,38 +199,30 @@
red.putAll(row, kv);
vm.findList();
});
},
},*/
edit: function (row) {
row["catalogs"] = row["catalogs"] || [];
vm.row = row;
this.loadCatalogs();
$('#myModal').modal({moveable: true});
this.row = row
this.loadCatalogs()
row['catalogs'] = row['catalogs'] || []
$('#myModal').modal({moveable: true})
},
save: function (row) {
dbSave({plat: row}).then(() => {
red.showMsg({msg: "操作成功"})
red.showOk()
$('#myModal').modal('hide')
vm.findList()
this.findList()
})
/* red.post("/meta/save", {
_id: row._id || "db_plat",
doc: JSON.stringify(row)
}, function (json) {
red.showMsg();
$('#myModal').modal('hide');
vm.findList();
}); */
},
loadCatalogs: function () {
vm.catalogs = [];
red.post("/_db/catalogs", {
url: this.row.url,
user: this.row.user,
pwd: this.row.pwd
}, function (json) {
vm.catalogs = json;
});
//let [cate, url, user, pwd] = this.row
let dbAccount = {cate:'', url:'', user:'', pwd:''}
for (let key in dbAccount) {
dbAccount[key] = this.row[key]
}
catalogList({dbAccount}).then(res => {
this.catalogs = res;
})
},
dbPulse: function (row) {
/*row["check"] = true;
@@ -216,6 +232,9 @@
row["pulse"] = json.body;
row["check"] = false;
});*/
},
showInfo() {
$('#f-info').modal({moveable: true})
}
},
mounted: function () {

View File

@@ -139,6 +139,8 @@
</row>
<script src="http://www.1216.top/res/layui/layui.js"></script>
<script>
let {qtaskList} = qtask
var vm = new Vue({
el: ".qtask-list",
data: {
@@ -163,7 +165,8 @@
},
methods: {
loadList: function () {
var para = {
qtaskList().then(res => this.list = res)
/*var para = {
pn:1,
ps:120,
doc:"qtask",
@@ -172,7 +175,8 @@
};
red.getJSON("/meta/find", para, function (json) {
vm.list = json;
});
});*/
},
openDia: function (row) {
vm.row = row;

View File

@@ -142,4 +142,16 @@ th{
.table td, .table th{
padding: 5px;
}
/* 页面信息按钮样式 */
#main .info {
float: right;
padding-top: 10px;
display: none;
}
/* 设置对话框内容居中显示 */
.modal-body {
text-align: center;
}

BIN
root/res/img/db_plat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
root/res/img/meta_flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
root/res/img/meta_link.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
root/res/img/user_link.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -12,6 +12,9 @@ var red = {
,placement: option.placement
}).show();
},
showOk(msg = '操作成功') {
red.showMsg({msg})
},
getData: function(key, defaultValue) {
var v = localStorage.getItem(key) || defaultValue || "";
if (typeof(v) == "string" && v.startsWith("{") && v.endsWith("}")) {
@@ -42,7 +45,7 @@ var red = {
}
return plat["token"];
},
getJSON: function (url, params, callback) {
getJSON: function (url, params = {}, callback) {
params["platToken"] = red.getPlatToken()
axios.get(url, {params}).then(res => {
let data = res.data || {}
@@ -57,8 +60,10 @@ var red = {
callback(data)
})
},
getX(url, params) {
return new Promise((resolve, reject) => {
getX(url, params = {}) {
if (!params['platToken'])
params['platToken'] = red.getPlatToken()
return new Promise(resolve => {
axios.get(url, params).then(res => {
let data = res.data || {}
red.loginCheck(data)
@@ -71,11 +76,15 @@ var red = {
}
resolve(data)
}).catch(res => {
console.log(res)
red.showMsg({type:"error", msg:'操作失败!'})
})
})
},
postX(url, params) {
params["platToken"] = red.getPlatToken()
postX(url, params = {}) {
if (!params['platToken'])
params['platToken'] = red.getPlatToken()
return new Promise(resolve => {
axios({
url,
@@ -91,13 +100,12 @@ var red = {
else if (data.code == 0) {
data = data.body
}
red.showMsg()
resolve(data)
});
})
},
post: function(url, params, callback) {
params["platToken"] = red.getPlatToken()
post: function(url, params = {}, callback) {
params['platToken'] = red.getPlatToken()
axios.post(url, params).then(res => {
let data = red.loginCheck(res.data)
if (data && data.code == -1) {
@@ -173,9 +181,9 @@ var red = {
},
loginCheck: function (json) {
if (json && (json['code'] == -2 || json['referid'])) {
red.showMsg({type:'error', placement: 'center', msg: '登陆过期,请前往登陆'});
//red.showMsg({type:'error', placement: 'center', msg: '登陆过期,请前往登陆'});
setTimeout(function () {
location.href = "/user/login.html";
//location.href = "/user/login.html";
}, 2000);
}
},

View File

View File

@@ -1,5 +1,5 @@
<div id="about">
{{about}}
<h3>解放生产力,告别重复、累赘;流水线式的创造基础软件;集中火力构筑更扎实的城墙;</h3>
</div>
<script>
var vm = new Vue({

12
root/single/metaFlow.html Normal file
View File

@@ -0,0 +1,12 @@
<div id="meta-flow">
<h3>MetaKit使用流程</h3>
<img src="../res/img/meta_flow.png">
</div>
<script>
var vm = new Vue({
el: "#meta-flow",
data: {
about: "AbOUT",
}
});
</script>

View File

@@ -120,8 +120,30 @@
},
methods: {
loginCheck: function ({username, pwd}) {
login({username, pwd}, function (json) {
login({username, pwd}) {
login({username, pwd}).then(res => {
console.log(res)
platList().then(res => {
let rows = res.rows
console.log(rows)
red.showMsg({msg:"请选择业务平台"})
this.sysPlats = rows;
if (!this.sysPlat) {
this.sysPlat = rows[0];
}
red.setData("sysPlats", rows);
red.setData('userName', username)
setTimeout(function () {
vm.choose = true;
}, 500);
})
})
},
/*loginCheck: function ({username, pwd}) {
/!*login({username, pwd}, function (json) {
if (json.code == 0) {
platList().then(res => {
let rows = res.rows
@@ -139,12 +161,12 @@
} else {
red.showMsg({msg: json.message})
}
});
},
});*!/
},*/
/*loadPlats: function () {
var para = {
doc:"sys_plat",
shows: JSON.stringify(["_id", "_key", "name", "token","remark", "status"])
shows: JSON.stringify(["_id", "_key", "field", "token","remark", "status"])
};
red.getJSON("/meta/plat_list", {}, function (json) {
vm.sysPlats = json;
@@ -166,7 +188,7 @@
$('body').bind('keyup', function(event) {
if (event.keyCode == "13") {
if (!vm.choose) {
vm.loginCheck(vm.row);
vm.login(vm.row);
} else {
vm.goIndex();
}

View File

@@ -1,10 +1,15 @@
package net.tccn.meta;
package net.tccn.base;
import net.tccn.base.Kv;
import net.tccn.base.UtilityExt;
import net.tccn.base.arango.Doc;
import net.tccn.dbq.Field;
import net.tccn.dbq.fbean.FilterType;
import net.tccn.dbq.jdbc.api.DbAccount;
import net.tccn.dbq.jdbc.api.DbKit;
import net.tccn.meta.MetaLink;
import net.tccn.meta.MetaService;
import net.tccn.meta.MetaTable;
import net.tccn.plat.DbPlat;
import net.tccn.plat.SysPlat;
import java.util.*;
import java.util.function.BiFunction;
@@ -23,6 +28,7 @@ public class MetaKit {
private static List<MetaService> metaServices;
private static List<DbAccount> dbPlats;
private static List<SysPlat> sysPlats;
static {
metaTables = MetaTable.dao.find();
@@ -30,6 +36,17 @@ public class MetaKit {
metaServices = MetaService.dao.find();
dbPlats = DbAccount.dao.find();
sysPlats = SysPlat.dao.find();
}
public static <T extends Doc> void reload(Class<T> clazz) {
if (MetaTable.class == clazz) metaTables = MetaTable.dao.find();
else if (MetaLink.class == clazz) metaLinks = MetaLink.dao.find();
else if (MetaService.class == clazz) metaServices = MetaService.dao.find();
else if (DbAccount.class == clazz) dbPlats = DbAccount.dao.find();
else if (DbPlat.class == clazz) dbPlats = DbAccount.dao.find();
else if (SysPlat.class == clazz) sysPlats = SysPlat.dao.find();
}
//----- get/set ----
@@ -61,6 +78,14 @@ public class MetaKit {
MetaKit.dbPlats = dbPlats;
}
public static List<SysPlat> getSysPlats() {
return sysPlats;
}
public static void setSysPlats(List<SysPlat> sysPlats) {
MetaKit.sysPlats = sysPlats;
}
//-----------------------------------
public static void setMetaServices(List<MetaService> metaServices) {
MetaKit.metaServices = metaServices;
@@ -433,6 +458,18 @@ public class MetaKit {
return tables;
}
public static DbKit getDbKit(String dbPlatId) {
Optional<DbAccount> dbAccount = dbPlats.stream().filter(x -> x.getKey().equals(dbPlatId)).findAny();
return new DbKit(dbAccount.get());
}
public static DbAccount getDbPlat(String dbPlatId) {
Optional<DbAccount> dbAccount = dbPlats.stream().filter(x -> x.getKey().equals(dbPlatId)).findFirst();
return dbAccount.get();
}
public String nextAlias(String x) {
return next(x, "");
}

View File

@@ -206,7 +206,7 @@ public class ArangoSource {
StringBuilder buf = new StringBuilder();
Map<String, Integer> order = t.getOrder();
if (isEmpty.test(order)) {
return buf;
return buf.append(" sort d._key desc");
}
buf.append(" sort ");
order.forEach((k, v) -> {

View File

@@ -25,6 +25,7 @@ public abstract class Doc<T extends Doc> {
private Set<String> _shows;
private Map _order;
private List<Map> _filters;//[{col, value, expr}]
public String getId() {
return _id;
@@ -194,6 +195,9 @@ public abstract class Doc<T extends Doc> {
if (flipper == null) {
flipper = new Flipper();
}
if (t == null) {
t = (T) this;
}
List<T> list = find(t, flipper.getOffset(), flipper.getLimit());
long count = count(t);
@@ -251,7 +255,7 @@ public abstract class Doc<T extends Doc> {
return (T) collection.getDocument(String.valueOf(key), this.getClass());
}
//ok
// ok todo: 将数据放入回收库
public void delete() {
collection.deleteDocument(getKey());
}

View File

@@ -1,23 +1,24 @@
package net.tccn.dbq;
import net.tccn.base.MetaKit;
import net.tccn.base.PageBean;
import net.tccn.dbq.fbean.FBean;
import net.tccn.dbq.jdbc.api.DbAccount;
import net.tccn.dbq.jdbc.api.DbKit;
import net.tccn.dbq.parser.ParseMysql;
import net.tccn.dbq.parser.Parser;
import net.tccn.meta.MetaKit;
import net.tccn.meta.MetaService;
import net.tccn.meta.MetaTable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class DbExecutors {
private final static Parser PARSER = new ParseMysql();
public static PageBean findPage(FBean fBean) {
public static PageBean findPage(FBean fBean) throws ExecutionException, InterruptedException {
//sql解析
String[] sqls = PARSER.parse(fBean);
//当前的业务 => 获取主表 信息 => 数据源信息 => 数据源对象 => 创建数据工具对象 => 查询数据
@@ -25,15 +26,13 @@ public class DbExecutors {
MetaTable mainTable = MetaKit.getMetaTableByAlias(metaService.getTable());
DbAccount dbAccount = DbAccount.dao.findByKey(mainTable.getDbPlatId());
DbKit dbKit = new DbKit(dbAccount);
DbKit dbKit = MetaKit.getDbKit(mainTable.getDbPlatId());
System.out.println("countSql: " + sqls[0]);
System.out.println("findSql: " + sqls[1]);
Integer count = dbKit.queryColumn(sqls[0], int.class);
List<Map> list = dbKit.findList(sqls[1], Map.class);
CompletableFuture<Integer> countFuture = CompletableFuture.supplyAsync(() -> dbKit.queryColumn(sqls[0], int.class));
CompletableFuture<List<Map>> listFuture = CompletableFuture.supplyAsync(() -> dbKit.findList(sqls[1], Map.class));
return PageBean.by(list, count);
return PageBean.by(listFuture.get(), countFuture.get());
}
}

View File

@@ -34,4 +34,14 @@ public class DbKit implements DbSource{
public <T> T queryColumn(String sql, Class<T> type) {
return dbSource.queryColumn(sql, type);
}
@Override
public void createTable(String sql) {
dbSource.createTable(sql);
}
@Override
public void dropTable(String tableName) {
dbSource.dropTable(tableName);
}
}

View File

@@ -32,4 +32,7 @@ public interface DbSource {
default Date queryDate(String sql) {
return queryColumn(sql, Date.class);
}
void createTable(String sql);
void dropTable(String tableName);
}

View File

@@ -8,8 +8,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* Created by liangxianyou at 2019/3/12 14:20.
@@ -17,7 +17,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
@SuppressWarnings("Duplicates")
public class DbSourceMysql implements DbSource {
private static ConcurrentHashMap<String, AtomicReferenceArray<Connection>> conns = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, LinkedBlockingQueue<Connection>> conns = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, AtomicInteger> counter = new ConcurrentHashMap<>();
private String accountKey;
@@ -111,57 +111,66 @@ public class DbSourceMysql implements DbSource {
}
}
@Override
public void createTable(String sql) {
new RuntimeException("DbSourceMysql.createTable NOT SUPPORT right now" ); // todo:
}
@Override
public void dropTable(String tableName) {
new RuntimeException("[DbSourceMysql.dropTable] NOT SUPPORT right now" ); // todo:
}
private Connection connection() {
return connection(0);
}
private Connection connection(int n) {
AtomicReferenceArray<Connection> arr = conns.getOrDefault(accountKey, new AtomicReferenceArray<>(15));
Connection connection = null;
AtomicInteger num = counter.getOrDefault(accountKey, new AtomicInteger(0));
for (int i = 0; num.get() > 0 && i < arr.length() && connection == null; i++) {
try {
connection = arr.getAndUpdate(i, null);
} catch (Exception e) {
System.out.println("getAndUpdate exception");
}
}
if (connection == null) {
try {
if (num.get() < 15) {
connection = DriverManager.getConnection(dbAccount.getUrl(), dbAccount.getUser(), dbAccount.getPwd());
num.getAndIncrement();
} else {
//连接被全部使用中等待1s后再次获取连接,直到得到连接
Thread.sleep(1000);
if (++n > 3)
connection(n);
}
} catch (SQLException e) {
e.printStackTrace();
throw new IllegalArgumentException("获取数据库连接失败");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LinkedBlockingQueue<Connection> queue = conns.getOrDefault(accountKey, new LinkedBlockingQueue<>(15));
return connection;
Connection conn = null;
AtomicInteger num = counter.getOrDefault(accountKey, new AtomicInteger(0));
try {
if (queue.size() == 0 && num.get() < 15) {
conn = DriverManager.getConnection(dbAccount.getUrl(), dbAccount.getUser(), dbAccount.getPwd());
int x = num.incrementAndGet();
counter.put(accountKey, num);
System.out.println("创建新的连接:" + x);
} else {
conn = queue.take();
if (conn != null) {
System.out.println("获取已有连接" + conn);
}
}
} catch (SQLException | InterruptedException e) {
if (e instanceof InterruptedException) {
try {
conn = DriverManager.getConnection(dbAccount.getUrl(), dbAccount.getUser(), dbAccount.getPwd());
num.getAndIncrement();
if (conn != null) {
System.out.println("获取连接异常,并重新创建成功");
}
} catch (SQLException ex) {
new IllegalArgumentException("创建连接失败", e);
}
num.getAndIncrement();
counter.put(accountKey, num);
} else {
new IllegalArgumentException("获取连接失败", e);
}
}
conns.put(accountKey, queue);
return conn;
}
private void release(Connection connection) {
AtomicReferenceArray<Connection> arr = conns.getOrDefault(accountKey, new AtomicReferenceArray<>(15));
int i = 0;
boolean bool = false;
while (i < arr.length() && !bool){
bool = arr.compareAndSet(i++, null, connection);
}
//如果没成功释放,关系连接
if (!bool && connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
LinkedBlockingQueue<Connection> queue = conns.getOrDefault(accountKey, new LinkedBlockingQueue<>(15));
try {
if (connection != null) {
queue.put(connection);
conns.put(accountKey, queue);
System.out.println("还回连接:" + connection);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@@ -2,7 +2,7 @@ package net.tccn.dbq.parser;
import net.tccn.base.Kv;
import net.tccn.dbq.fbean.*;
import net.tccn.meta.MetaKit;
import net.tccn.base.MetaKit;
import net.tccn.meta.MetaLink;
import net.tccn.meta.MetaService;
import net.tccn.meta.MetaTable;

View File

@@ -5,7 +5,7 @@ package net.tccn.dbq.table;
* @author: liangxianyou at 2018/10/8 10:59.
*/
public class Column {
private String name; //列名称
private String field; //列名称
private String type; //列类型
private boolean notNull; //不为null
private String comment; //列说明
@@ -14,18 +14,18 @@ public class Column {
}
public Column(String name, String type, boolean notNull, String comment) {
this.name = name;
this.field = name;
this.type = type;
this.notNull = notNull;
this.comment = comment;
}
public String getName() {
return name;
public String getField() {
return field;
}
public void setName(String name) {
this.name = name;
public void setField(String field) {
this.field = field;
}
public String getType() {
@@ -52,4 +52,9 @@ public class Column {
this.comment = comment;
}
//-----------------------
public void setNull(String notNull) {
this.notNull = "NO".equalsIgnoreCase(notNull) ? true : false;
}
}

View File

@@ -60,7 +60,7 @@ public class Table {
buf.append("CREATE TABLE " + name + "(");
columns.forEach(x -> {
buf.append("\n " + x.getName() + " " + x.getType() + ",");
buf.append("\n " + x.getField() + " " + x.getType() + ",");
});
buf.deleteCharAt(buf.length() - 1);

View File

@@ -1,6 +1,7 @@
package net.tccn.meta;
import net.tccn.base.JBean;
import net.tccn.qtask.Task;
/**
* Created by liangxianyou at 2019/3/7 16:13.

View File

@@ -20,6 +20,7 @@ public class MetaTable extends Doc<MetaTable> implements Serializable {
private String alias; //表别名:全库唯一,程序自动生成
private String comment;
private List<Field> items;
private String sysPlatId; //所属系统平台
private String dbPlatId; //所属数据平台
private String catalog; //所在database
@@ -57,6 +58,14 @@ public class MetaTable extends Doc<MetaTable> implements Serializable {
this.items = items;
}
public String getSysPlatId() {
return sysPlatId;
}
public void setSysPlatId(String sysPlatId) {
this.sysPlatId = sysPlatId;
}
public String getDbPlatId() {
return dbPlatId;
}

View File

@@ -1,7 +0,0 @@
package net.tccn.meta;
/**
* Created by liangxianyou at 2019/3/7 16:24.
*/
public class Task {
}

View File

@@ -0,0 +1,91 @@
package net.tccn.meta;
import net.tccn.base.JBean;
import net.tccn.base.MetaKit;
import net.tccn.dbq.jdbc.api.DbAccount;
import net.tccn.dbq.jdbc.api.DbKit;
import net.tccn.dbq.table.Column;
import net.tccn.dbq.table.Table;
import net.tccn.service.BaseService;
import org.redkale.net.http.RestMapping;
import org.redkale.net.http.RestService;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Stream;
@RestService(automapping = true, comment = "数据库操作类")
public class _DbService extends BaseService {
@RestMapping(name = "catalog_list", comment = "获取数据源的database")
public JBean catalogList(DbAccount dbAccount, String dbPlatId) {
JBean jBean = new JBean();
DbKit dbKit = null;
if (dbAccount != null) {
dbKit = new DbKit(dbAccount);
} else {
dbKit = MetaKit.getDbKit(dbPlatId);
}
List<Map> list = dbKit.findList("SHOW DATABASES;", Map.class);
Stream<String> database = list.stream().map(x -> String.valueOf(x.get("Database")));
return jBean.setBody(database.toArray());
}
@RestMapping(name = "table_list", comment = "数据库表列表")
public JBean tableList(DbAccount dbAccount, String dbPlatId, String[] catalogs) {
JBean jBean = new JBean();
DbKit dbKit = MetaKit.getDbKit(dbPlatId);
StringBuffer sqlBuf = new StringBuffer("SELECT TABLE_NAME 'name',TABLE_COMMENT 'comment',table_schema 'catalog' FROM INFORMATION_SCHEMA.TABLES");
if (catalogs != null && catalogs.length > 0) {
sqlBuf.append("WHERE TABLE_SCHEMA in (");
for (String catalog : catalogs) {
sqlBuf.append("'").append(catalog).append("',");
}
sqlBuf.deleteCharAt(sqlBuf.length() - 1);
sqlBuf.append(")");
}
List<Table> list = dbKit.findList(sqlBuf.toString(), Table.class);
return jBean.setBody(list);
}
@RestMapping(name = "table_info", comment = "数据库表详情")
public JBean tableInfo(DbAccount dbAccount,
String dbPlatId, String catalog, String tableName) {
JBean jBean = new JBean();
DbKit dbKit = MetaKit.getDbKit(dbPlatId);
String sql = String.format("SELECT TABLE_NAME 'name',TABLE_COMMENT 'comment',table_schema 'catalog' FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME='%s'", tableName);
String columnSql = String.format("SHOW FULL COLUMNS FROM '%s.%s'", catalog, tableName);
CompletableFuture<Table> tableFuture = CompletableFuture.supplyAsync(() -> dbKit.findfirst(sql, Table.class));
CompletableFuture<List<Column>> columnFuture = CompletableFuture.supplyAsync(() -> dbKit.findList(columnSql, Column.class));
try {
Table table = tableFuture.get();
table.setColumns(columnFuture.get());
jBean.setBody(table);
} catch (InterruptedException | ExecutionException e) {
jBean.set(-1, "查询表信息失败");
new IllegalArgumentException("查询表信息失败", e);
}
return jBean;
}
@RestMapping(name = "table_create", comment = "新建表[mysql]")
public JBean tableCreate(String dbPlatId, String catalog, String sql) {
JBean jBean = new JBean();
DbKit dbKit = MetaKit.getDbKit(dbPlatId);
dbKit.createTable(sql);
return jBean;
}
}

View File

@@ -1,13 +1,17 @@
package net.tccn.plat;
import net.tccn.base.JBean;
import net.tccn.base.MetaKit;
import net.tccn.base.PageBean;
import net.tccn.dbq.jdbc.api.DbAccount;
import net.tccn.service.BaseService;
import org.redkale.net.http.RestMapping;
import org.redkale.net.http.RestService;
import org.redkale.source.Flipper;
import org.redkale.util.Comment;
import java.util.List;
@RestService(name = "plat", automapping = true, comment = "业务/数据平台")
public class PlatService extends BaseService {
@@ -15,7 +19,9 @@ public class PlatService extends BaseService {
public JBean list(SysPlat plat, Flipper flipper) {
JBean jBean = new JBean();
PageBean<SysPlat> page = SysPlat.dao.findPage(plat, flipper);
//PageBean<SysPlat> page = SysPlat.dao.findPage(plat, flipper);
List<SysPlat> list = MetaKit.getSysPlats();
PageBean page = PageBean.by(list, list.size());
return jBean.setBody(page);
}
@@ -42,19 +48,24 @@ public class PlatService extends BaseService {
public JBean dbList(DbPlat plat, Flipper flipper) {
JBean jBean = new JBean();
PageBean<DbPlat> page = DbPlat.dao.findPage(plat, flipper);
//PageBean<DbPlat> page = DbPlat.dao.findPage(plat, flipper);
List<DbAccount> list = MetaKit.getDbPlats();
PageBean page = PageBean.by(list, list.size());
return jBean.setBody(page);
}
@RestMapping(name = "db_save", comment = "数据源信息保存")
public JBean dbSave(DbPlat plat) {
//DbAccount dbPlat = MetaKit.getDbPlat(plat.getKey());
if (plat.getKey() == null) {
plat.save();
} else {
plat.update();
}
MetaKit.reload(DbPlat.class);
return new JBean();
}

View File

@@ -2,6 +2,9 @@ package net.tccn.qtask;
import net.tccn.base.Kv;
import net.tccn.base.arango.Doc;
import javax.persistence.Table;
/**
* cate:mysql
@@ -22,7 +25,9 @@ import net.tccn.base.Kv;
*
*
*/
public class Task {
@Table(name = "qtask", catalog = "db_dev")
public class Task extends Doc<Task> {
public static Task dao = dao(Task.class);
public String cate;//MYSQL,ES,METHOD,HTTP
public String queryId;

View File

@@ -23,6 +23,9 @@ import java.util.logging.Logger;
*/
public class BaseService implements Service {
@Resource(name = "SERVER_ROOT")
protected File webroot;
public static Gson gson = new Gson();
public Logger logger = Logger.getLogger(this.getClass().getSimpleName());

View File

@@ -5,7 +5,7 @@ import net.tccn.base.Kv;
import net.tccn.dbq.Field;
import net.tccn.dbq.jdbc.api.DbAccount;
import net.tccn.plat.SysPlat;
import net.tccn.meta.MetaKit;
import net.tccn.base.MetaKit;
import net.tccn.meta.MetaService;
import net.tccn.meta.MetaTable;
import org.redkale.net.http.RestMapping;

View File

@@ -0,0 +1,243 @@
package net.tccn.service;
import com.lxyer.excel.poi.ExcelKit;
import net.tccn.base.JBean;
import net.tccn.base.Kv;
import net.tccn.dbq.Field;
import net.tccn.meta.MetaTable;
import net.tccn.plat.SysPlat;
import org.redkale.net.http.RestMapping;
import org.redkale.net.http.RestParam;
import org.redkale.net.http.RestService;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.Arrays.asList;
/**
* @author: liangxianyou at 2018/10/24 10:57.
*/
@RestService(automapping = true, comment = "文件服务")
public class _FileService extends BaseService {
private final static String[] FIELDS = {"field", "cate", "must", "remark1", "remark2", "tag", "selects", "column", "filter", "ck", "edit"};
@Resource
private QtaskService qtaskService;
@RestMapping(name = "sheets", comment = "得到所有的sheetName")
public List<String> sheets(String filePath) {
List<String> sheets = new ArrayList<>();
try {
File file = new File(webroot, filePath);
if (file.exists()) {
sheets = ExcelKit.getSheetNames(file);
}
} catch (IOException e) {
e.printStackTrace();
}
return sheets.stream().filter(x -> {
return !x.contains("版本记录") && !x.contains("表说明") && !x.contains("表名称");
}).collect(Collectors.toList());
}
@RestMapping(name = "data", comment = "得到文件数据")
public JBean data(String filePath, @RestParam(name = "platToken") String token) {
JBean jBean = new JBean();
SysPlat sysPlat = qtaskService.getSysPlat(token);
File file = new File(webroot, filePath);
if (file.exists()) {
try {
Map<String, List<Map>> map = ExcelKit.readExcelAll(file, FIELDS);
Kv<String, MetaTable> data = Kv.of();
map.forEach((k, v) -> {
if (v.size() > 2) {
data.put(k.replace(" ", ""), toCols(v));
}
});
StringBuffer buf = new StringBuffer();
buf.append("for d in meta_cols\n" +
" filter d.name in [");
data.values().forEach(x -> {
buf.append("'").append(x.getName()).append("',");
});
buf.deleteCharAt(buf.length() - 1);
buf.append("] and d.sysPlatId=='" + sysPlat.getKey() + "'\n" +
" return d.name");
List<String> hv = MetaTable.dao.find(buf.toString(), String.class);
Kv res = Kv.of();
data.forEach((k, v) -> {
Kv kv = Kv.of();
res.put(k,
kv.set("name", v.getName())
.set("hv", hv.contains(v.getName()) ? 1 : 0)
.set("comment", v.getComment())
);
});
jBean.setBody(res);
} catch (IOException e) {
e.printStackTrace();
}
}
return jBean;
}
@RestMapping(name = "sheet_data", comment = "得到sheet数据")
public JBean sheetData(String filePath, String sheetName, @RestParam(name = "platToken") String token) {
JBean jBean = new JBean();
SysPlat sysPlat = qtaskService.getSysPlat(token);
File file = new File(webroot, filePath);
try {
List<Map> list = ExcelKit.readExcel(file, FIELDS, sheetName);
MetaTable metaTable = toCols(list);
jBean.setBody(metaTable);
} catch (IOException e) {
e.printStackTrace();
jBean.set(-1, String.format("读取sheet[%s]失败", sheetName));
}
return jBean;
}
@RestMapping(name = "import_metatable", comment = "导入excel数据到metatable")
public JBean importMetaTable(@RestParam(name = "sheetArr", comment = "sheet名") String[] sheetArr,
@RestParam(name = "filePath", comment = "文件路径") String filePath,
@RestParam(name = "platToken") String token) {
JBean jBean = new JBean();
SysPlat sysPlat = qtaskService.getSysPlat(token);
File file = new File(webroot, filePath);
String[] fields = {"field", "cate", "must", "remark1", "remark2", "tag", "selects", "column", "filter", "ck", "edit"};
try {
Map<String, List<Map>> map = ExcelKit.readExcelAll(file, fields);
map.forEach((k, v) -> {
if (v.size() > 2) {
MetaTable table = toCols(v);
for (String sn : sheetArr) {
if (table.getName().equals(sn)) { //在选中列表中
MetaTable metaTable = new MetaTable();
metaTable.setSysPlatId(sysPlat.getKey());
metaTable.setName(table.getName());
MetaTable _metaTable = MetaTable.dao.findFirst(metaTable);
if (_metaTable == null) {//库里没有数据保存
table.setSysPlatId(sysPlat.getKey());
table.save();
}
break;
}
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
return jBean;
}
/**
* 组装元数据
*/
private MetaTable toCols(List<Map> list) {
//Kv col = Kv.of();
MetaTable metaTable = new MetaTable();
list.remove(1);//list[0] head info
Map rowHead = list.remove(0);
String comment = getComment(rowHead);//list[1] comment,
String tableName = getTableName(rowHead);
//col.set("name", tableName).set("comment", comment);
metaTable.setName(tableName);
metaTable.setComment(comment);
//所有字段
List<Field> items = new ArrayList<>();
//展示的字段
List<String> shows = new ArrayList<>();
//编辑的字段
List<Map> edits = new ArrayList<>();
//查询过滤用字段
List<Map> filters = new ArrayList<>();
list.forEach(x -> {
String colName = x.getOrDefault("field", "") + "";
Field item = new Field();
item.setName(colName);
item.setLabel(x.getOrDefault("remark1", "") + "");
item.setType(x.getOrDefault("cate", "") + "");
item.setInType(x.getOrDefault("tag", "") + "");
item.setInExt(x.getOrDefault("selects", "") + "");
items.add(item);
if ("1".equals(x.getOrDefault("column", "") + "")) {
shows.add(colName);
}
if ("1".equals(x.getOrDefault("edit", "") + "")) {
edits.add(Kv.of("col", colName));
}
if (x.get("filter") != null && !"".equals(x.get("filter") + "")) {
String filter = x.getOrDefault("filter", "") + "";
filter = filter.replace("1", "EQUAL");
filter = filter.replace("!1", "NOTEQUAL");
filter = filter.replace("like", "LIKE");
filters.add(Kv.of("name", item).set("filterType", asList(filter.split(","))));
}
});
metaTable.setItems(items);
//metaTable.setShows(shows);
//metaTable.setEdits(edits);
//metaTable.setFilters(filters);
return metaTable;
}
private String getTableName(Map rowHead) {
String field = rowHead.get("field") + "";
int s = field.indexOf("(");
if (s > 0) {
return field.substring(0, s);
}
return field;
}
private String getComment(Map rowHead) {
String field = rowHead.get("field") + "";
int s = field.indexOf("(");
int e = field.indexOf(")");
if (s > 0) {
return field.substring(s + 1, e > 0 ? e : field.length());
}
return "";
}
}

View File

@@ -0,0 +1,22 @@
package net.tccn.service;
import net.tccn.base.JBean;
import net.tccn.base.PageBean;
import net.tccn.qtask.Task;
import org.redkale.net.http.RestParam;
import org.redkale.net.http.RestService;
import org.redkale.source.Flipper;
import org.redkale.util.Comment;
@RestService(automapping = true)
public class _QtaskService extends BaseService{
@Comment("qtask列表")
public JBean list(Task task, Flipper flipper, @RestParam(name = "platToken") String token) {
PageBean<Task> page = Task.dao.findPage(task, flipper);
return JBean.by(0, "", page);
}
}

View File

@@ -4,7 +4,7 @@ import net.tccn.dbq.fbean.FBean;
import net.tccn.dbq.parser.ParseMysql;
import net.tccn.dbq.jdbc.api.DbAccount;
import net.tccn.dbq.jdbc.api.DbKit;
import net.tccn.meta.MetaKit;
import net.tccn.base.MetaKit;
import net.tccn.qtask.QRuner;
import net.tccn.qtask.Task;
import org.junit.Test;
@@ -60,8 +60,11 @@ public class RunTest<T> {
DbKit dbKit = new DbKit(dbAccount);
//String sql = "select * from basic_concat limit 1";
String sql = "show databases;";
// find list
List<Map> list = dbKit.findList("select * from basic_concat limit 1", Map.class);
List<Map> list = dbKit.findList(sql, Map.class);
System.out.println(list.get(0));