Browse Source

血缘解析与存储过程

master
xueyinfei 2 days ago
parent
commit
1b4718fd61
  1. 18
      vue-fastapi-backend/module_admin/controller/meta_controller.py
  2. 34
      vue-fastapi-backend/module_admin/dao/meta_dao.py
  3. 38
      vue-fastapi-backend/module_admin/entity/do/meta_do.py
  4. 8
      vue-fastapi-backend/module_admin/entity/vo/meta_vo.py
  5. 95
      vue-fastapi-backend/module_admin/service/meta_service.py
  6. 13
      vue-fastapi-frontend/src/api/meta/metaInfo.js
  7. 8
      vue-fastapi-frontend/src/components/codemirror/SQLCodeMirror.vue
  8. 737
      vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelation.vue
  9. 52
      vue-fastapi-frontend/src/views/meta/metaInfo/index.vue

18
vue-fastapi-backend/module_admin/controller/meta_controller.py

@ -6,7 +6,7 @@ from module_admin.service.login_service import LoginService
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
from module_admin.service.meta_service import MetaService from module_admin.service.meta_service import MetaService
from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject, SuppleModel, MetaBusinessRelShipObject from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject, SuppleModel, MetaBusinessRelShipObject, MetaProcQueryObject
metaController = APIRouter(prefix='/dasset', dependencies=[Depends(LoginService.get_current_user)]) metaController = APIRouter(prefix='/dasset', dependencies=[Depends(LoginService.get_current_user)])
@ -63,3 +63,19 @@ async def getBusinessRelationShip(request: Request,
query_db: AsyncSession = Depends(get_db)): query_db: AsyncSession = Depends(get_db)):
result = await MetaService.getBusinessRelationShip(query_db, meta_query) result = await MetaService.getBusinessRelationShip(query_db, meta_query)
return ResponseUtil.success(data=result) return ResponseUtil.success(data=result)
@metaController.get("/meta/business/proc")
async def getBusinessProc(request: Request,
meta_query: MetaProcQueryObject = Depends(MetaProcQueryObject.as_query),
query_db: AsyncSession = Depends(get_db)):
result = await MetaService.getMetaProc(query_db, meta_query)
return ResponseUtil.success(data=result)
@metaController.get("/meta/blood/relation/{procId}")
async def getBloodRelationShip(request: Request,
procId: int,
query_db: AsyncSession = Depends(get_db)):
result = await MetaService.getBloodRelationShip(query_db, procId)
return ResponseUtil.success(data=result)

34
vue-fastapi-backend/module_admin/dao/meta_dao.py

@ -2,7 +2,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join, update, desc from sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join, update, desc
from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject
from module_admin.entity.do.meta_do import MetadataExtractInfo, MetadataSuppInfo, MetadataFldTabExtractInfo, \ from module_admin.entity.do.meta_do import MetadataExtractInfo, MetadataSuppInfo, MetadataFldTabExtractInfo, \
MetadataFldSuppInfo, MetadataClas, MetadataSuppInfoVett, MetadataFldSuppInfoVett, MetaBatchTabClas, MetaBatchFldClas MetadataFldSuppInfo, MetadataClas, MetadataSuppInfoVett, MetadataFldSuppInfoVett, MetaBatchTabClas,\
MetaBatchFldClas, MetaBloodAnalysis
from utils.common_util import CamelCaseUtil from utils.common_util import CamelCaseUtil
import uuid import uuid
from utils.page_util import PageUtil from utils.page_util import PageUtil
@ -191,7 +192,7 @@ class MetaDao:
MetadataFldTabExtractInfo.ssys_cd == query_object.ssys_cd, MetadataFldTabExtractInfo.ssys_cd == query_object.ssys_cd,
MetadataFldTabExtractInfo.mdl_name == query_object.mdl_name, MetadataFldTabExtractInfo.mdl_name == query_object.mdl_name,
MetadataFldTabExtractInfo.tab_eng_name == query_object.tab_name MetadataFldTabExtractInfo.tab_eng_name == query_object.tab_name
).distinct() ).distinct().order_by(MetadataFldTabExtractInfo.fld_no)
) )
).all() ).all()
) )
@ -432,6 +433,7 @@ class MetaDao:
MetadataFldTabExtractInfo.mdl_name == mdlName, MetadataFldTabExtractInfo.mdl_name == mdlName,
MetadataFldTabExtractInfo.tab_eng_name == tabEngName, MetadataFldTabExtractInfo.tab_eng_name == tabEngName,
MetadataFldTabExtractInfo.fld_eng_name == fldEngName).distinct() MetadataFldTabExtractInfo.fld_eng_name == fldEngName).distinct()
.order_by(MetadataFldTabExtractInfo.fld_no)
) )
).scalars().first() ).scalars().first()
) )
@ -519,11 +521,12 @@ class MetaDao:
MetadataFldTabExtractInfo.fld_cn_name, MetadataFldTabExtractInfo.fld_cn_name,
MetadataFldTabExtractInfo.fld_type, MetadataFldTabExtractInfo.fld_type,
MetadataFldTabExtractInfo.pk_flag, MetadataFldTabExtractInfo.pk_flag,
MetadataFldTabExtractInfo.fld_no
).where( ).where(
MetadataFldTabExtractInfo.ssys_cd == query_object.ssys_cd, MetadataFldTabExtractInfo.ssys_cd == query_object.ssys_cd,
MetadataFldTabExtractInfo.mdl_name == query_object.mdl_name, MetadataFldTabExtractInfo.mdl_name == query_object.mdl_name,
MetadataFldTabExtractInfo.tab_eng_name == query_object.tab_name MetadataFldTabExtractInfo.tab_eng_name == query_object.tab_name
).distinct() ).distinct().order_by(MetadataFldTabExtractInfo.fld_no)
) )
).all() ).all()
) )
@ -644,3 +647,28 @@ class MetaDao:
# 将结果转换为字典列表 # 将结果转换为字典列表
result_as_dict = [dict(zip(columns, row)) for row in result.fetchall()] result_as_dict = [dict(zip(columns, row)) for row in result.fetchall()]
return result_as_dict return result_as_dict
@classmethod
async def get_proc_by_table(cls, result_db: AsyncSession, ssys_cd: str, mdl_name: str, tab_eng_name: str):
sql = text("select onum, proc_text from t_metadata_data_lineage_info"
" where onum in ("
"select proId from meta_blood_analysis "
"where targetSysCd = :ssysCd and targetMdlName = :mdlName and targetTableName= :tableName)")
result = (await result_db.execute(sql, {"ssysCd": ssys_cd, "mdlName": mdl_name, "tableName": tab_eng_name}))
# 获取列名
columns = result.keys() # 返回列名列表
# 将结果转换为字典列表
result_as_dict = [dict(zip(columns, row)) for row in result.fetchall()]
return result_as_dict
@classmethod
async def get_blood_by_procId(cls, result_db: AsyncSession, procId: int):
query_result = (
(
await result_db.execute(
select(MetaBloodAnalysis).where(MetaBloodAnalysis.proId == procId).distinct()
)
).scalars().all()
)
return query_result

38
vue-fastapi-backend/module_admin/entity/do/meta_do.py

@ -188,19 +188,25 @@ class MetaBatchFldClas(Base):
clas_value = Column(String(200, collation='utf8_general_ci'), comment='标签值') clas_value = Column(String(200, collation='utf8_general_ci'), comment='标签值')
# class MetaBatchFldRelation(Base): class MetaBloodAnalysis(Base):
# __tablename__ = 't_batch_fld_relation' """字段标签表"""
# a_ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='a系统代码') __tablename__ = 'meta_blood_analysis'
# a_data_whs_name = Column(String(50, collation='utf8_general_ci'), comment='a数据库名称')
# a_mdl_name = Column(String(50, collation='utf8_general_ci'), comment='a模式名称') id = Column(String(50, collation='utf8_general_ci'), primary_key=True, comment='id')
# a_tab_no = Column(Integer, comment='a表编号') proId = Column(Integer, comment='存储过程ID')
# a_tab_eng_name = Column(String(200, collation='utf8_general_ci'), comment='a表英文名称') proName = Column(String(100, collation='utf8_general_ci'), comment='存储过程名称')
# a_fld_eng_name = Column(String(200, collation='utf8_general_ci'), comment='a字段英文名称') targetSysCd = Column(String(100, collation='utf8_general_ci'), comment='目标系统')
# b_ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='b源系统代码') targetMdlName = Column(String(100, collation='utf8_general_ci'), comment='目标模式')
# b_data_whs_name = Column(String(50, collation='utf8_general_ci'), comment='b数据库名称') targetTableName = Column(String(100, collation='utf8_general_ci'), comment='目标表')
# b_mdl_name = Column(String(50, collation='utf8_general_ci'), comment='b数据库名称') targetTableCnName = Column(String(100, collation='utf8_general_ci'), comment='目标表中文名')
# b_tab_no = Column(Integer, comment='b表编号') targetColName = Column(String(100, collation='utf8_general_ci'), comment='目标字段名')
# b_tab_eng_name = Column(String(200, collation='utf8_general_ci'), comment='b表英文名称') targetColCnName = Column(String(100, collation='utf8_general_ci'), comment='目标字段中文名')
# b_fld_eng_name = Column(String(200, collation='utf8_general_ci'), comment='b表英文名称') targetColType = Column(String(100, collation='utf8_general_ci'), comment='目标字段类型')
# rela_type = Column(String(200, collation='utf8_general_ci'), comment='关系类型') sourceSysCd = Column(String(100, collation='utf8_general_ci'), comment='源系统代码')
# rela_value = Column(String(200, collation='utf8_general_ci'), comment='关系值') sourceMdlName = Column(String(100, collation='utf8_general_ci'), comment='源系统模式')
sourceTableName = Column(String(100, collation='utf8_general_ci'), comment='源系统表名')
sourceTableCnName = Column(String(100, collation='utf8_general_ci'), comment='源系统表中文名')
sourceColName = Column(String(100, collation='utf8_general_ci'), comment='源系统字段名')
sourceColCnName = Column(String(100, collation='utf8_general_ci'), comment='源系统字段中文名')
sourceColType = Column(String(100, collation='utf8_general_ci'), comment='源系统字段类型')

8
vue-fastapi-backend/module_admin/entity/vo/meta_vo.py

@ -63,3 +63,11 @@ class MetaBusinessRelShipObject(BaseModel):
mdl_name: Optional[str] mdl_name: Optional[str]
tab_eng_name: Optional[str] tab_eng_name: Optional[str]
type: Optional[str] type: Optional[str]
@as_query
class MetaProcQueryObject(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
ssys_cd: Optional[str]
mdl_name: Optional[str]
tab_eng_name: Optional[str]

95
vue-fastapi-backend/module_admin/service/meta_service.py

@ -1,7 +1,8 @@
import json import json
import uuid import uuid
from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject, SuppleModel, MetaBusinessRelShipObject from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject, SuppleModel, MetaBusinessRelShipObject, \
MetaProcQueryObject
from module_admin.entity.do.meta_do import MetadataSuppInfo, MetadataFldSuppInfo, MetadataSuppInfoVett, \ from module_admin.entity.do.meta_do import MetadataSuppInfo, MetadataFldSuppInfo, MetadataSuppInfoVett, \
MetadataFldSuppInfoVett, MetadataExtractInfo, MetadataFldTabExtractInfo MetadataFldSuppInfoVett, MetadataExtractInfo, MetadataFldTabExtractInfo
from module_admin.dao.meta_dao import MetaDao from module_admin.dao.meta_dao import MetaDao
@ -267,16 +268,16 @@ class MetaService:
'tab_eng_name': nextNode['b_tab_eng_name'], 'tab_eng_name': nextNode['b_tab_eng_name'],
'fld_eng_name': nextNode['b_fld_eng_name']}, 'fld_eng_name': nextNode['b_fld_eng_name']},
"endArrow": True} if nextNode['father'] == 'A' else \ "endArrow": True} if nextNode['father'] == 'A' else \
{"source": {'ssys_cd': nextNode['b_ssys_cd'], {"source": {'ssys_cd': nextNode['b_ssys_cd'],
'mdl_name': nextNode['b_mdl_name'], 'mdl_name': nextNode['b_mdl_name'],
'tab_eng_name': nextNode['b_tab_eng_name'], 'tab_eng_name': nextNode['b_tab_eng_name'],
'fld_eng_name': nextNode['b_fld_eng_name']}, 'fld_eng_name': nextNode['b_fld_eng_name']},
"target": {"ssys_cd": nextNode['a_ssys_cd'], "target": {"ssys_cd": nextNode['a_ssys_cd'],
"mdl_name": nextNode['a_mdl_name'], "mdl_name": nextNode['a_mdl_name'],
"tab_eng_name": nextNode['a_tab_eng_name'], "tab_eng_name": nextNode['a_tab_eng_name'],
"fld_eng_name": nextNode['a_fld_eng_name']}, "fld_eng_name": nextNode['a_fld_eng_name']},
"endArrow": True "endArrow": True
} }
relationList.append(relation) relationList.append(relation)
if currentNode['father'] == 'B': if currentNode['father'] == 'B':
# b为上游, 取b字段的上上游 # b为上游, 取b字段的上上游
@ -296,15 +297,15 @@ class MetaService:
'mdl_name': preNode['b_mdl_name'], 'mdl_name': preNode['b_mdl_name'],
'tab_eng_name': preNode['b_tab_eng_name'], 'tab_eng_name': preNode['b_tab_eng_name'],
'fld_eng_name': preNode['b_fld_eng_name']}, 'fld_eng_name': preNode['b_fld_eng_name']},
"endArrow": True} if preNode['father'] == 'A' else\ "endArrow": True} if preNode['father'] == 'A' else \
{ "source": {'ssys_cd': preNode['b_ssys_cd'], {"source": {'ssys_cd': preNode['b_ssys_cd'],
'mdl_name': preNode['b_mdl_name'], 'mdl_name': preNode['b_mdl_name'],
'tab_eng_name': preNode['b_tab_eng_name'], 'tab_eng_name': preNode['b_tab_eng_name'],
'fld_eng_name': preNode['b_fld_eng_name']}, 'fld_eng_name': preNode['b_fld_eng_name']},
"target": {"ssys_cd": preNode['a_ssys_cd'], "mdl_name": preNode['a_mdl_name'], "target": {"ssys_cd": preNode['a_ssys_cd'], "mdl_name": preNode['a_mdl_name'],
"tab_eng_name": preNode['a_tab_eng_name'], "tab_eng_name": preNode['a_tab_eng_name'],
"fld_eng_name": preNode['a_fld_eng_name']}, "fld_eng_name": preNode['a_fld_eng_name']},
"endArrow": True} "endArrow": True}
relationList.append(relation) relationList.append(relation)
if currentNode['b_tab_eng_name'] == meta_query.tab_eng_name: if currentNode['b_tab_eng_name'] == meta_query.tab_eng_name:
if currentNode['father'] == 'A': if currentNode['father'] == 'A':
@ -439,3 +440,57 @@ class MetaService:
currentNodeList = await MetaDao.get_er_relation_by_column(result_db, col, module) currentNodeList = await MetaDao.get_er_relation_by_column(result_db, col, module)
return currentNodeList return currentNodeList
return None return None
@classmethod
async def getMetaProc(cls, result_db: AsyncSession, meta_query: MetaProcQueryObject):
result = await MetaDao.get_proc_by_table(result_db, meta_query.ssys_cd, meta_query.mdl_name,
meta_query.tab_eng_name)
return result
@classmethod
async def getBloodRelationShip(cls, result_db: AsyncSession, procId: int):
bloodRelations = await MetaDao.get_blood_by_procId(result_db, procId)
tableList = []
if bloodRelations is not None and len(bloodRelations) > 0:
for blood in bloodRelations:
if len(tableList) > 0:
exists1 = any(item["ssys_cd"].lower() == blood.targetSysCd.lower()
and item["mdl_name"].lower() == blood.targetMdlName.lower()
and item["tab_eng_name"].lower() == blood.targetTableName.lower()
for item in tableList)
if not exists1:
tableList.append({"ssys_cd": blood.targetSysCd.lower(),
"mdl_name": blood.targetMdlName.lower(),
"tab_eng_name": blood.targetTableName.lower()})
exists2 = any(item["ssys_cd"].lower() == blood.sourceSysCd.lower()
and item["mdl_name"].lower() == blood.sourceMdlName.lower()
and item["tab_eng_name"].lower() == blood.sourceTableName.lower()
for item in tableList)
if not exists2:
tableList.append({"ssys_cd": blood.sourceSysCd.lower(),
"mdl_name": blood.sourceMdlName.lower(),
"tab_eng_name": blood.sourceTableName.lower()})
else:
tableList.append({"ssys_cd": blood.targetSysCd.lower(),
"mdl_name": blood.targetMdlName.lower(),
"tab_eng_name": blood.targetTableName.lower()})
tableList.append({"ssys_cd": blood.sourceSysCd.lower(),
"mdl_name": blood.sourceMdlName.lower(),
"tab_eng_name": blood.sourceTableName.lower()})
if len(tableList) > 0:
for table in tableList:
query_object = MetaColObject()
query_object.ssys_cd = table['ssys_cd']
query_object.mdl_name = table['mdl_name']
query_object.tab_name = table['tab_eng_name']
meta_result = await MetaDao.get_meta_col_name_list(result_db, query_object)
result1 = CamelCaseUtil.transform_result(meta_result)
tableCnName = await MetaDao.get_meta_table_cn_name(result_db, table['ssys_cd'], table['mdl_name'],
table['tab_eng_name'])
table['tab_cn_name'] = tableCnName
table['column'] = result1
result = {
"relation": bloodRelations,
"tableList": tableList
}
return result

13
vue-fastapi-frontend/src/api/meta/metaInfo.js

@ -54,6 +54,19 @@ export function getMetaDataRelship(data){
params: data params: data
}) })
} }
export function getMetaDataBloodRelship(data){
return request({
url:'/default-api/dasset/meta/blood/relation/'+data,
method: 'get',
})
}
export function getProcData(param){
return request({
url:'/default-api/dasset/meta/business/proc',
method: 'get',
params: param
})
}

8
vue-fastapi-frontend/src/components/codemirror/SQLCodeMirror.vue

@ -1,5 +1,5 @@
<template> <template>
<codemirror v-model:value="value" :options="sqlOptions" /> <codemirror v-model:value="value" :options="sqlOptions" :dbType="dbType" />
</template> </template>
<script setup> <script setup>
@ -34,12 +34,13 @@ import {watch,ref} from "vue";
const props = defineProps({ const props = defineProps({
data: String, data: String,
dbType: String,
}) })
const sqlOptions = { const sqlOptions = {
autorefresh: true, // autorefresh: true, //
smartIndent: true, // smartIndent: true, //
tabSize: 4, // 4 tabSize: 4, // 4
mode: 'sql', // mode: "text/x-sql", //
line: true, // line: true, //
viewportMargin: Infinity, // viewportMargin: Infinity, //
highlightDifferences: true, highlightDifferences: true,
@ -61,7 +62,8 @@ const sqlOptions = {
const value = ref("") const value = ref("")
watch(() => value.value, watch(() => value.value,
(val) =>{ (val) =>{
value.value = sqlFormatter.format(val) console.log(props.dbType)
value.value = sqlFormatter.format(val,{ language: props.dbType?.toLowerCase() || "sql" })
} }
) )
onMounted(()=>{ onMounted(()=>{

737
vue-fastapi-frontend/src/views/meta/metaInfo/bloodRelation.vue

@ -0,0 +1,737 @@
<template>
<div id="bloodRelationG6" style="width: 100%;height: 100%;overflow: auto;position: relative"></div>
<div id="bloodmini-container"></div>
</template>
<script setup>
import G6 from '@antv/g6';
import {watch} from "vue";
const props = defineProps({
data: {
type: Object,
default: () => {}
},
currentTable: {
type:Object,
default:()=>{}
}
})
const g6data = ref([])
function initG6() {
const {
Util,
registerBehavior,
registerEdge,
registerNode
} = G6;
const isInBBox = (point, bbox) => {
const {
x,
y
} = point;
const {
minX,
minY,
maxX,
maxY
} = bbox;
return x < maxX && x > minX && y > minY && y < maxY;
};
const itemHeight = 20;
registerBehavior("dice-er-scroll", {
getDefaultCfg() {
return {
multiple: true,
};
},
getEvents() {
return {
itemHeight,
wheel: "scorll",
click: "click",
"node:mousemove": "move",
"node:mousedown": "mousedown",
"node:mouseup": "mouseup"
};
},
scorll(e) {
// e.preventDefault();
const {
graph
} = this;
const nodes = graph.getNodes().filter((n) => {
const bbox = n.getBBox();
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
});
const x = e.deltaX || e.movementX;
let y = e.deltaY || e.movementY;
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
if (nodes) {
const edgesToUpdate = new Set();
nodes.forEach((node) => {
return;
const model = node.getModel();
if (model.attrs.length < 2) {
return;
}
const idx = model.startIndex || 0;
let startX = model.startX || 0.5;
let startIndex = idx + y * 0.02;
startX -= x;
if (startIndex < 0) {
startIndex = 0;
}
if (startX > 0) {
startX = 0;
}
if (startIndex > model.attrs.length - 1) {
startIndex = model.attrs.length - 1;
}
graph.updateItem(node, {
startIndex,
startX,
});
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
});
// G6 update the related edges when graph.updateItem with a node according to the new properties
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
edgesToUpdate.forEach(edge => edge.refresh())
}
},
click(e) {
const {
graph
} = this;
const item = e.item;
const shape = e.shape;
if (!item) {
return;
}
if (shape.get("name") === "collapse") {
graph.updateItem(item, {
collapsed: true,
size: [300, 50],
});
// setTimeout(() => graph.layout(), 100);
} else if (shape.get("name") === "expand") {
graph.updateItem(item, {
collapsed: false,
size: [300, 80],
});
// setTimeout(() => graph.layout(), 100);
} else if (shape.get("name") && shape.get("name").startsWith("item")) {
let edges = graph.getEdges()
let columnName = ''
g6data.value.forEach(data => {
if (data.id === item.getModel().id) {
columnName = data.attrs[shape.get("name").split("-")[1]].key
}
})
edges.forEach(edg => {
if (edg.getModel().sourceKey === columnName && edg.getModel().source === item.getModel().id) {
edg.show()
} else if(edg.getModel().targetKey === columnName && edg.getModel().target === item.getModel().id){
edg.show()
} else{
edg.hide()
}
})
let nodes = graph.getNodes()
let index = Number(shape.get("name").split("-")[1])
nodes.forEach(node => {
let model = node.getModel()
model.selectedIndex = NaN
graph.updateItem(node, model)
})
this.graph.updateItem(item, {
selectedIndex: index,
});
} else {
graph.updateItem(item, {
selectedIndex: NaN,
});
let edges = graph.getEdges()
edges.forEach(edg => {
edg.show()
})
}
},
mousedown(e) {
this.isMousedown = true;
},
mouseup(e) {
this.isMousedown = false;
},
move(e) {
if (this.isMousedown) return;
// const name = e.shape.get("name");
// const item = e.item;
//
// if (name && name.startsWith("item")) {
// this.graph.updateItem(item, {
// selectedIndex: Number(name.split("-")[1]),
// });
// } else {
// this.graph.updateItem(item, {
// selectedIndex: NaN,
// });
// }
},
});
registerEdge("dice-er-edge", {
draw(cfg, group) {
const edge = group.cfg.item;
const sourceNode = edge.getSource().getModel();
const targetNode = edge.getTarget().getModel();
const sourceIndex = sourceNode.attrs.findIndex(
(e) => e.key === cfg.sourceKey
);
const sourceStartIndex = sourceNode.startIndex || 0;
let sourceY = 15;
if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) {
sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * itemHeight;
}
const targetIndex = targetNode.attrs.findIndex(
(e) => e.key === cfg.targetKey
);
const targetStartIndex = targetNode.startIndex || 0;
let targetY = 15;
if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) {
targetY = (targetIndex - targetStartIndex + 0.5) * itemHeight + 30;
}
const startPoint = {
...cfg.startPoint
};
const endPoint = {
...cfg.endPoint
};
startPoint.y = startPoint.y + sourceY;
endPoint.y = endPoint.y + targetY;
let shape;
if (sourceNode.id !== targetNode.id) {
shape = group.addShape("path", {
attrs: {
stroke: "#5B8FF9",
path: [
["M", startPoint.x, startPoint.y],
[
"C",
endPoint.x / 3 + (2 / 3) * startPoint.x,
startPoint.y,
endPoint.x / 3 + (2 / 3) * startPoint.x,
endPoint.y,
endPoint.x,
endPoint.y,
],
],
endArrow: true,
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: "path-shape",
});
} else {
let gap = Math.abs((startPoint.y - endPoint.y) / 3);
if (startPoint["index"] === 1) {
gap = -gap;
}
shape = group.addShape("path", {
attrs: {
stroke: "#5B8FF9",
path: [
["M", startPoint.x, startPoint.y],
[
"C",
startPoint.x - gap,
startPoint.y,
startPoint.x - gap,
endPoint.y,
startPoint.x,
endPoint.y,
],
],
endArrow: props.type === 'er'? {path: G6.Arrow.triangle(10,-10,6),fill:'#5B8FF9'} : false//cfg.endArrow,
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: "path-shape",
});
}
return shape;
},
afterDraw(cfg, group) {
const labelCfg = cfg.labelCfg || {};
const edge = group.cfg.item;
const sourceNode = edge.getSource().getModel();
const targetNode = edge.getTarget().getModel();
if (sourceNode.collapsed && targetNode.collapsed) {
return;
}
const path = group.find(
(element) => element.get("name") === "path-shape"
);
const labelStyle = Util.getLabelPosition(path, 0.5, 0, 0, true);
const label = group.addShape("text", {
attrs: {
...labelStyle,
text: cfg.label || '',
fill: "#000",
textAlign: "center",
stroke: "#fff",
lineWidth: 1,
},
});
label.rotateAtStart(labelStyle.rotate);
},
});
registerNode("dice-er-box", {
draw(cfg, group) {
const width = 300;
const {
attrs = [],
startIndex = 0,
selectedIndex,
collapsed,
icon,
} = cfg;
let currentTableLabel = props.currentTable.tabEngName
if (props.currentTable.tabCnName && props.currentTable.tabCnName.length>0){
currentTableLabel += "("+props.currentTable.tabCnName+")"
}else if (props.currentTable.tabCrrctName && props.currentTable.tabCnName.tabCrrctName>0){
currentTableLabel += "("+props.currentTable.tabCrrctName+")"
}
const list = attrs;
const itemCount = list.length;
const boxStyle = {
stroke: currentTableLabel === cfg.label?"#67C23A":"#096DD9",
radius: 4,
};
const afterList = list.slice(
Math.floor(startIndex),
Math.floor(startIndex + itemCount)
);
const offsetY = (0.5 - (startIndex % 1)) * itemHeight + 30;
//
group.addShape("rect", {
attrs: {
fill: boxStyle.stroke,
height: 30,
width,
radius: [boxStyle.radius, boxStyle.radius, 0, 0],
},
draggable: true,
});
let fontLeft = 12;
//
if (icon && icon.show !== false) {
group.addShape("image", {
attrs: {
x: 8,
y: 8,
height: 16,
width: 16,
...icon,
},
});
fontLeft += 18;
}
//
group.addShape("text", {
attrs: {
y: 22,
x: fontLeft,
fill: "#fff",
text: cfg.label,
fontSize: 12,
fontWeight: 500,
},
});
//div
group.addShape("rect", {
attrs: {
x: 0,
y: collapsed ? 30 : (list.length + 1) * itemHeight + 15,
height: 15,
width,
fill: "#eee",
radius: [0, 0, boxStyle.radius, boxStyle.radius],
cursor: "pointer",
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: collapsed ? "expand" : "collapse",
});
//
group.addShape("text", {
attrs: {
x: width / 2 - 6,
y: (collapsed ? 30 : (list.length + 1) * itemHeight + 15) + 12,
text: collapsed ? "+" : "-",
width,
fill: "#000",
radius: [0, 0, boxStyle.radius, boxStyle.radius],
cursor: "pointer",
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: collapsed ? "expand" : "collapse",
});
//
const keyshape = group.addShape("rect", {
attrs: {
x: 0,
y: 0,
width,
height: collapsed ? 45 : (list.length + 1) * itemHeight + 15 + 15,
...boxStyle,
},
draggable: true,
});
if (collapsed) {
return keyshape;
}
const listContainer = group.addGroup({});
//
listContainer.setClip({
type: "rect",
attrs: {
x: -8,
y: 30,
width: width + 16,
height: (list.length + 1) * itemHeight + 15 - 30,
},
});
//
listContainer.addShape({
type: "rect",
attrs: {
x: 1,
y: 30,
width: width - 2,
height: (list.length + 1) * itemHeight + 15 - 30,
fill: "#fff",
},
draggable: true,
});
if (list.length + 1 > itemCount) {
const barStyle = {
width: 4,
padding: 0,
boxStyle: {
stroke: "#00000022",
},
innerStyle: {
fill: "#00000022",
},
};
//
listContainer.addShape("rect", {
attrs: {
y: 30,
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: (list.length + 1) * itemHeight - 30,
...barStyle.boxStyle,
},
});
const indexHeight =
afterList.length > itemCount ?
(afterList.length / list.length + 1) * (list.length + 1) * itemHeight :
10;
//
listContainer.addShape("rect", {
attrs: {
y: 30 +
barStyle.padding +
(startIndex / list.length + 1) * ((list.length + 1) * itemHeight - 30),
x: width - barStyle.padding - barStyle.width,
width: barStyle.width,
height: Math.min((list.length + 1) * itemHeight, indexHeight),
...barStyle.innerStyle,
},
});
}
if (afterList) {
afterList.forEach((e, i) => {
const isSelected =
Math.floor(startIndex) + i === Number(selectedIndex);
let {
key = "", type
} = e;
if (type) {
key += " - " + type;
}
const label = key;
//
listContainer.addShape("rect", {
attrs: {
x: 1,
y: i * itemHeight - itemHeight / 2 + offsetY,
width: width - 4,
height: itemHeight,
radius: 2,
lineWidth: 1,
cursor: "pointer",
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: `item-${Math.floor(startIndex) + i}-content`,
draggable: true,
});
if (!cfg.hideDot) {
//
listContainer.addShape("circle", {
attrs: {
x: 0,
y: i * itemHeight + offsetY,
r: 3,
stroke: boxStyle.stroke,
fill: "white",
radius: 2,
lineWidth: 1,
cursor: "pointer",
},
});
//
listContainer.addShape("circle", {
attrs: {
x: width,
y: i * itemHeight + offsetY,
r: 3,
stroke: boxStyle.stroke,
fill: "white",
radius: 2,
lineWidth: 1,
cursor: "pointer",
},
});
}
//
listContainer.addShape("text", {
attrs: {
x: 12,
y: i * itemHeight + offsetY + 6,
text: label,
fontSize: 12,
fill: "#000",
fontFamily: "Avenir,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol",
full: e,
fontWeight: isSelected ? 500 : 100,
cursor: "pointer",
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: `item-${Math.floor(startIndex) + i}`,
});
});
}
return keyshape;
},
getAnchorPoints() {
return [
[0, 0],
[1, 0],
];
},
});
const dataTransform = (data) => {
const nodes = [];
let edges = [];
data.map((node) => {
nodes.push({
...node
});
if (node.attrs) {
node.attrs.forEach((attr) => {
if (attr.relation) {
attr.relation.forEach((relation) => {
edges.push({
source: node.id,
target: relation.nodeId,
sourceKey: attr.key,
endArrow: relation.endArrow,
targetKey: relation.key,
label: relation.label,
});
});
}
});
}
});
return {
nodes,
edges,
};
}
const container = document.getElementById("bloodRelationG6");
const mini_container = document.getElementById("bloodmini-container");
const width = container.scrollWidth;
const height = ((container.height || 500) - 20);
// minimap
const minimap = new G6.Minimap({
size: [200, 200],
container: mini_container,
type: 'delegate',
});
const graph = new G6.Graph({
container: container,
plugins: [minimap],
width,
height,
defaultNode: {
size: [350, 200],
type: 'dice-er-box',
color: '#5B8FF9',
style: {
fill: '#9EC9FF',
lineWidth: 3,
},
labelCfg: {
style: {
fill: 'black',
fontSize: 20,
},
},
},
defaultEdge: {
type: 'dice-er-edge',
style: {
stroke: '#e2e2e2',
lineWidth: 4,
endArrow: {path: G6.Arrow.triangle(10,-10,6),fill:'#5B8FF9'},
},
},
modes: {
default: ['dice-er-scroll', 'drag-node', 'drag-canvas', 'zoom-canvas'],
},
layout: {
type: 'dagre',
rankdir: 'LR',
align: 'UL',
controlPoints: true,
nodesep: 1,
ranksep: 1
},
animate: true,
// fitView: true
})
graph.data(dataTransform(g6data.value))
graph.render();
}
watch(
() => props.data,
(val) => {
g6data.value = []
if (props.data.tableList && props.data.tableList.length>0){
for (let i = 0; i < props.data.tableList.length; i++) {
let table = props.data.tableList[i]
let g6Tab = {
id: table.ssys_cd+"-"+table.mdl_name+"-"+table.tab_eng_name,
label: table.tab_eng_name + ((table.tab_cn_name && table.tab_cn_name.length>0)?"("+table.tab_cn_name+")":""),
attrs:[],
collapsed:true
}
for (let j = 0; j < table.column.length; j++) {
g6Tab.attrs.push({
key: table.column[j].fldEngName,
type: table.column[j].fldType
})
}
g6data.value.push(g6Tab)
}
if (props.data.relation && props.data.relation.length>0){
for (let i = 0; i < props.data.relation.length; i++) {
let relation = props.data.relation[i]
let key = relation.targetColName.toLowerCase()
let tableKey = relation.sourceSysCd.toLowerCase()+"-"+relation.sourceMdlName.toLowerCase()+"-"+relation.sourceTableName.toLowerCase()
let nodeId = relation.targetSysCd.toLowerCase()+"-"+relation.targetMdlName.toLowerCase()+"-"+relation.targetTableName.toLowerCase()
if (g6data.value.length > 0){
for (let j = 0; j < g6data.value.length; j++) {
if (g6data.value[j].id === tableKey){
for (let k = 0; k < g6data.value[j].attrs.length; k++) {
if (g6data.value[j].attrs[k].key === relation.sourceColName.toLowerCase()){
if (g6data.value[j].attrs[k].relation && g6data.value[j].attrs[k].relation.length>0){
let hasRelation = false
for (let l = 0; l < g6data.value[j].attrs[k].relation.length; l++) {
if (g6data.value[j].attrs[k].relation[l].key === key && g6data.value[j].attrs[k].relation[l].nodeId === nodeId){
hasRelation = true
}
}
if (!hasRelation){
g6data.value[j].attrs[k].relation.push({
key: key,
nodeId: nodeId,
endArrow: true
})
}
}else {
g6data.value[j].attrs[k].relation = [{
key: key,
nodeId: nodeId,
endArrow: true
}]
}
}
}
}
}
}
}
}
let g6 = document.getElementById("bloodRelationG6")
const mini_container = document.getElementById("bloodmini-container");
if (mini_container){
mini_container.innerHTML=''
}
if (g6){
g6.innerHTML=''
initG6()
}
}
},
{ deep: true,immediate: true }
)
</script>
<style scoped lang="scss">
#bloodmini-container{
position: absolute !important;
right: 20px;
bottom: 20px;
border: 1px solid #e2e2e2;
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.9);
z-index: 10;
}
</style>

52
vue-fastapi-frontend/src/views/meta/metaInfo/index.vue

@ -334,9 +334,13 @@
</div> </div>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="血缘关系" name="bloodRelation">血缘关系</el-tab-pane> <el-tab-pane label="血缘关系" name="bloodRelation">
<div style="width: 100%;height: 100%">
<blood-relation :currentTable="currentMetaData" :data="bloodRelation"></blood-relation>
</div>
</el-tab-pane>
<el-tab-pane label="存储过程" name="proc"> <el-tab-pane label="存储过程" name="proc">
<SQLCodeMirror v-if="activeColumnTab === 'proc'" :data="procStr"></SQLCodeMirror> <SQLCodeMirror v-if="activeColumnTab === 'proc'" :data="procStr" :dbType="dbType"></SQLCodeMirror>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-col> </el-col>
@ -565,12 +569,13 @@
</template> </template>
<script setup name="Meta"> <script setup name="Meta">
import {getDataSourceList, getMetaDataList, getColumnList, getMetaClasList, postMetaSupp, getMetaDataRelship} from "@/api/meta/metaInfo" import {getDataSourceList, getMetaDataList, getColumnList, getMetaClasList, postMetaSupp, getMetaDataRelship, getMetaDataBloodRelship, getProcData} from "@/api/meta/metaInfo"
import { getMetaSecurityData } from "@/api/dataAsset/directory" import { getMetaSecurityData } from "@/api/dataAsset/directory"
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue' import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import SQLCodeMirror from "@/components/codemirror/SQLCodeMirror.vue"; import SQLCodeMirror from "@/components/codemirror/SQLCodeMirror.vue";
import cache from "@/plugins/cache"; import cache from "@/plugins/cache";
import BusinssRelation from "./businssRelation.vue"; import BusinssRelation from "./businssRelation.vue";
import BloodRelation from "./bloodRelation.vue";
const data = reactive({ const data = reactive({
queryParams:{ queryParams:{
@ -632,12 +637,15 @@
const tableTags = ref([]); const tableTags = ref([]);
const database = ref(""); const database = ref("");
const procStr = ref(""); const procStr = ref("");
const procId = ref(-1);
const activeColumnTab = ref("column"); const activeColumnTab = ref("column");
const businessOptionSelect = ref("er"); const businessOptionSelect = ref("er");
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const changedColumns = ref([]) const changedColumns = ref([])
const businessRelation = ref([]) const businessRelation = ref([])
const bloodRelation = ref([])
const demoDataList = ref([]) const demoDataList = ref([])
const dbType = ref("MYSQL")
function changeColumnTab(){ function changeColumnTab(){
if (activeColumnTab.value === 'businessRelation'){ if (activeColumnTab.value === 'businessRelation'){
@ -646,11 +654,11 @@
if (activeColumnTab.value === 'demoData'){ if (activeColumnTab.value === 'demoData'){
generateDemoData() generateDemoData()
} }
if (activeColumnTab.value === 'proc'){ if (activeColumnTab.value === 'bloodRelation'){
procStr.value = "--基金量化产品监管报送存储过程:ADS.SP_ADS_SAC_QNTPRD_ALL\n" + changeBloodOption()
"CREATE OR REPLACE PROCEDURE ADS.SP_ADS_SAC_QNTPRD_ALL(I_BUSI_DATE VARCHAR2, O_RET_CODE OUT NUMBER, O_RET_MSG OUT VARCHAR2) AS V_STATUS NUMBER DEFAULT 0; --状态,0:成功,-1失败 \n V_ETL_NAME VARCHAR2(50) DEFAULT 'ADS.SP_ADS_SAC_QNTPRD_ALL'; V_ETL_NAME_CN VARCHAR2(100) DEFAULT '量化产品监管报送报表总调度'; ----ETL中文名称 V_ETL_TYPE NUMBER DEFAULT 1; --ETL类型,0:采集,1:转换 V_START_TIME DATE DEFAULT SYSDATE; --开始时间 V_MSG VARCHAR2(1000) DEFAULT '成功'; ----ETL信息 V_SOURCE_TABLE VARCHAR2(200) DEFAULT 'ADS.SP_ADS_SAC_QNTPRD_DTL,ADS.SP_ADS_SAC_QNTPRD_AGGR'; V_DEST_TABLE VARCHAR2(200) DEFAULT 'ADS.T_ADS_SAC_QNTPRD_DTL_M_M_OC,ADS.T_ADS_SAC_QNTPRD_AGGR_M_M_OC'; V_SRC_CNT NUMBER DEFAULT 0; --源数据量 V_DEST_CNT NUMBER DEFAULT 0; --目标新增数据量 V_CNT NUMBER; V_SOURCE_CODE VARCHAR2(4) ; V_DATE DATE DEFAULT TO_DATE(I_BUSI_DATE,'YYYY-MM-DD'); --每月第一个交易日 V_RET_CODE NUMBER; V_RET_MSG VARCHAR2(4000); BEGIN O_RET_CODE := 0; O_RET_MSG := 'SUCCESS'; --每个月第一个交易日更新数据 SELECT T.TM_SH_FST_TRDY_FLAG INTO V_CNT FROM DWS.T_DWS_DIM_DATE_OC T WHERE T.D_DATE= V_DATE; IF V_CNT = 0 THEN RETURN; END IF; --加载明细表数据 ADS.SP_ADS_SAC_QNTPRD_DTL (I_BUSI_DATE => I_BUSI_DATE, --每月第一个交易日 O_RET_CODE => V_RET_CODE, O_RET_MSG => V_RET_MSG ) ; IF V_RET_CODE <> 0 THEN O_RET_CODE := V_RET_CODE; O_RET_MSG := V_RET_MSG; RETURN; END IF; --加载汇总表数据 ADS.SP_ADS_SAC_QNTPRD_AGGR(I_BUSI_DATE => I_BUSI_DATE, --每月第一个交易日 O_RET_CODE => V_RET_CODE, O_RET_MSG => V_RET_MSG ) ; IF V_RET_CODE <> 0 THEN O_RET_CODE := V_RET_CODE; O_RET_MSG := V_RET_MSG; RETURN; END IF; --日志:判断是否源表目标表的数量是否相同,如不相同则修改日志校验MSG --日志:写日志 DWS.SP_DWT_LOG(I_ETL_NAME => V_ETL_NAME, I_ETL_NAME_CN => V_ETL_NAME_CN, I_ETL_TYPE => V_ETL_TYPE, I_START_TIME => V_START_TIME, I_END_TIME => SYSDATE, I_STATUS => V_STATUS, I_MSG => V_MSG, I_SOURCE_TABLES => V_SOURCE_TABLE, I_DEST_TABLES => V_DEST_TABLE, I_SRC_CNT => V_SRC_CNT, I_DEST_CNT => V_DEST_CNT, I_VERI_SRC_CNT => NULL, I_VERI_DEST_CNT => NULL, I_VERI_MSG => NULL, I_VERI_STATUS => NULL, I_BUSI_DATE => I_BUSI_DATE); EXCEPTION WHEN OTHERS THEN O_RET_CODE := -1; O_RET_MSG := SQLCODE || ',' || SQLERRM; ROLLBACK; V_STATUS := -1; V_MSG := O_RET_MSG; DWS.SP_DWT_LOG(I_ETL_NAME => V_ETL_NAME, I_ETL_NAME_CN => V_ETL_NAME_CN, I_ETL_TYPE => V_ETL_TYPE, I_START_TIME => V_START_TIME, I_END_TIME => SYSDATE, I_STATUS => V_STATUS, I_MSG => V_MSG, I_SOURCE_TABLES => V_SOURCE_TABLE, I_DEST_TABLES => V_DEST_TABLE, I_SRC_CNT => V_SRC_CNT, I_DEST_CNT => V_DEST_CNT, I_VERI_SRC_CNT => NULL, I_VERI_DEST_CNT => NULL, I_VERI_MSG => NULL, I_VERI_STATUS => NULL, I_BUSI_DATE => I_BUSI_DATE); END;"
} }
} }
function generateDemoData(){ function generateDemoData(){
let param ={ let param ={
@ -675,6 +683,18 @@
loadingBusiness.value = false; loadingBusiness.value = false;
}) })
} }
function changeBloodOption(){
if (procId.value === -1){
proxy.$modal.msgWarning("此表暂无血缘,请执行存储过程跑批任务后查看");
bloodRelation.value = []
}else {
getMetaDataBloodRelship(procId.value).then(res=>{
bloodRelation.value = res.data
})
}
}
function confirmTableTags(){ function confirmTableTags(){
currentMetaData.value.tags = JSON.parse(JSON.stringify(tableTags.value)) currentMetaData.value.tags = JSON.parse(JSON.stringify(tableTags.value))
tableTagDialog.value = false tableTagDialog.value = false
@ -973,6 +993,26 @@
activeColumnTab.value = 'column' activeColumnTab.value = 'column'
drawer.value = true drawer.value = true
changedColumns.value = [] changedColumns.value = []
let param = {
ssysCd: currentMetaData.value.ssysCd,
mdlName: currentMetaData.value.mdlName,
tabEngName: currentMetaData.value.tabEngName
}
getProcData(param).then(res=>{
if(res.data.length >0){
let dbList = databaseList.value[0].children;
for (let i = 0; i < dbList.length; i++) {
if (currentMetaData.value.ssysCd.toUpperCase() === dbList[i].name.toUpperCase()){
dbType.value = dbList[i].type
}
}
procStr.value = res.data[0].proc_text+""
procId.value = res.data[0].onum
}else {
procStr.value = "--暂无存储过程,等待解析任务完成后再行查询"
procId.value = -1;
}
})
} }
function getDatabaseList() { function getDatabaseList() {

Loading…
Cancel
Save