si@aidatagov.com 4 weeks ago
parent
commit
28dc750821
  1. 20
      vue-fastapi-backend/module_admin/controller/sscf_controller.py
  2. 31
      vue-fastapi-backend/module_admin/dao/sscf_dao.py
  3. 4
      vue-fastapi-backend/module_admin/service/cdplb_service.py
  4. 125
      vue-fastapi-backend/module_admin/service/sscf_service.py
  5. 4
      vue-fastapi-frontend/src/views/dataint/cypz/cdplb.vue
  6. 136
      vue-fastapi-frontend/src/views/dataint/sscf/index.vue

20
vue-fastapi-backend/module_admin/controller/sscf_controller.py

@ -1,6 +1,6 @@
from typing import List from typing import List
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request, UploadFile, File, Query
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from config.get_db import get_db from config.get_db import get_db
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
@ -8,6 +8,7 @@ from module_admin.entity.vo.dataint_vo import SscfPageObject, SaveSscfModel, Tre
from module_admin.service.login_service import LoginService from module_admin.service.login_service import LoginService
from module_admin.service.sscf_service import SscfService from module_admin.service.sscf_service import SscfService
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
from utils.common_util import bytes2file_response
sscfController = APIRouter(prefix='/dataint/sscf', dependencies=[Depends(LoginService.get_current_user)]) sscfController = APIRouter(prefix='/dataint/sscf', dependencies=[Depends(LoginService.get_current_user)])
@ -53,3 +54,20 @@ async def delete_tsmcb(request: Request,
current_user: CurrentUserModel = Depends(LoginService.get_current_user)): current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
result = await SscfService.delete_sscf(query_db, array) result = await SscfService.delete_sscf(query_db, array)
return ResponseUtil.success(msg=result.message) return ResponseUtil.success(msg=result.message)
@sscfController.post("/importTemplate")
async def export_sscf_template(request: Request, query_db: AsyncSession = Depends(get_db)):
meta_import_template_result = await SscfService.get_import_template_services()
return ResponseUtil.streaming(data=bytes2file_response(meta_import_template_result))
@sscfController.post("/upload")
async def batch_import_sscf_data(
request: Request,
file: UploadFile = File(...),
overWrite: bool = Query(alias='overWrite'),
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
batch_import_result = await SscfService.batch_import_sscf_data(query_db, file, overWrite, current_user)
return ResponseUtil.success(data=batch_import_result)

31
vue-fastapi-backend/module_admin/dao/sscf_dao.py

@ -4,7 +4,7 @@ from typing import List
from sqlalchemy import desc, delete, func, select, update from sqlalchemy import desc, delete, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.entity.vo.dataint_vo import SscfPageObject, TreeOperateModel from module_admin.entity.vo.dataint_vo import SscfPageObject, TreeOperateModel, SaveSscfModel
from module_admin.entity.do.dataint_do import SysSscf, SysDassetTree from module_admin.entity.do.dataint_do import SysSscf, SysDassetTree
from sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join from sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join
from utils.page_util import PageUtil from utils.page_util import PageUtil
@ -45,6 +45,24 @@ class SscfDao:
) )
return result return result
@classmethod
async def get_oldSscf(cls, db: AsyncSession, sscf: SaveSscfModel):
result = (
(
await db.execute(
select(SysSscf).where(SysSscf.dasset_id == sscf.dasset_id,
SysSscf.keyword == sscf.keyword,
SysSscf.algorithm == sscf.algorithm,
SysSscf.order == sscf.order,
SysSscf.whole_sentence == sscf.whole_sentence,
SysSscf.type == sscf.type,
SysSscf.supp_expl == sscf.supp_expl,
).distinct()
)
).scalars().first()
)
return result
@classmethod @classmethod
async def update_sscf(cls, db: AsyncSession, saveObj: dict): async def update_sscf(cls, db: AsyncSession, saveObj: dict):
await db.execute(update(SysSscf), [saveObj]) await db.execute(update(SysSscf), [saveObj])
@ -64,6 +82,17 @@ class SscfDao:
) )
return result return result
@classmethod
async def getDassetIdByName(cls, db: AsyncSession, name: str):
result = (
(
await db.execute(
select(SysDassetTree).where(SysDassetTree.dasset_code == name).distinct()
)
).scalars().first()
)
return result
@classmethod @classmethod
async def get_dasset_tree_by_code(cls, db: AsyncSession, code: str): async def get_dasset_tree_by_code(cls, db: AsyncSession, code: str):
result = ( result = (

4
vue-fastapi-backend/module_admin/service/cdplb_service.py

@ -127,9 +127,9 @@ class CdplbService:
noneValid += "批量对象表名不能为空" noneValid += "批量对象表名不能为空"
if row['bath_obj_fld_name'] is None or len(row['bath_obj_fld_name']) == 0: if row['bath_obj_fld_name'] is None or len(row['bath_obj_fld_name']) == 0:
if len(noneValid) > 0: if len(noneValid) > 0:
noneValid += ",批对象字段名不能为空" noneValid += ",批对象字段名不能为空"
else: else:
noneValid += "对象字段名不能为空" noneValid += "对象字段名不能为空"
if row['ssys_cd'] is None or len(row['ssys_cd']) == 0: if row['ssys_cd'] is None or len(row['ssys_cd']) == 0:
if len(noneValid) > 0: if len(noneValid) > 0:
noneValid += ",系统不能为空" noneValid += ",系统不能为空"

125
vue-fastapi-backend/module_admin/service/sscf_service.py

@ -1,7 +1,11 @@
import io
import json import json
import uuid import uuid
from typing import Optional, List from typing import Optional, List
import pandas as pd
from fastapi import UploadFile
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.vo.common_vo import CrudResponseModel from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
@ -10,7 +14,8 @@ from module_admin.entity.do.dataint_do import SysSscf
from module_admin.dao.sscf_dao import SscfDao from module_admin.dao.sscf_dao import SscfDao
from exceptions.exception import ServiceException, ServiceWarning from exceptions.exception import ServiceException, ServiceWarning
from datetime import datetime from datetime import datetime
from utils.common_util import CamelCaseUtil from utils.common_util import *
class SscfService: class SscfService:
@ -94,3 +99,121 @@ class SscfService:
await db.commit() await db.commit()
return CrudResponseModel(is_success=True, message='操作成功') return CrudResponseModel(is_success=True, message='操作成功')
@staticmethod
async def get_import_template_services():
"""
获取元数据导入模板service
:return: 元数据导入模板excel的二进制数据
"""
table_header_list = ['归属资产名', '关键词', '算法', '按顺序匹配标志', '整句', '类型', '补充说明', '状态']
selector_header_list = ['算法', '按顺序匹配标志', '整句', '类型', '状态']
option_list = [{'算法': ['包含', '等于'], '按顺序匹配标志': ['', ''], '整句': ['', ''], '类型': ['补充', '替换'], '状态': ['正常', '停用']}]
sheet_config1 = dict(
sheet_name="短句配置", header_list=table_header_list, data_list=[], selector_header_list=selector_header_list
)
sheet_configs = [sheet_config1]
binary_data = get_excel_template_with_sheets(
sheet_configs, # 每个Sheet的配置(包含表头、选择器等)
option_list
)
return binary_data
@classmethod
async def batch_import_sscf_data(cls,
result_db: AsyncSession,
file: UploadFile,
overWrite: bool,
current_user: CurrentUserModel):
table_header_dict = {
'归属资产名': 'dassetName',
'关键词': 'keyword',
'算法': 'algorithm',
'按顺序匹配标志': 'order',
'整句': 'wholeSentence',
'类型': 'type',
'补充说明': 'suppExpl',
'状态': 'status'
}
contents = await file.read()
excel_file = pd.ExcelFile(io.BytesIO(contents))
await file.close()
# 获取所有sheet名称
sheet_names = excel_file.sheet_names
# 逐个读取
tableSheet = sheet_names[0]
result_list = {
"rows": [],
"successCount": 0
}
if tableSheet == '短句配置':
df = excel_file.parse(sheet_name=tableSheet, dtype=str, keep_default_na=False, na_values=[])
df.rename(columns=table_header_dict, inplace=True)
for index, row in df.iterrows():
noneValid = ''
if row['dassetName'] is None or len(row['dassetName']) == 0:
noneValid += "归属资产名不能为空"
if row['keyword'] is None or len(row['keyword']) == 0:
if len(noneValid) > 0:
noneValid += ",关键词不能为空"
else:
noneValid += "关键词不能为空"
if row['algorithm'] is None or len(row['algorithm']) == 0:
if len(noneValid) > 0:
noneValid += ",算法不能为空"
else:
noneValid += "算法不能为空"
if row['order'] is None or len(row['order']) == 0:
if len(noneValid) > 0:
noneValid += ",按顺序匹配标志不能为空"
else:
noneValid += "按顺序匹配标志不能为空"
if row['wholeSentence'] is None or len(row['wholeSentence']) == 0:
if len(noneValid) > 0:
noneValid += ",整句不能为空"
else:
noneValid += "整句不能为空"
if row['type'] is None or len(row['type']) == 0:
if len(noneValid) > 0:
noneValid += ",类型不能为空"
else:
noneValid += "类型不能为空"
if row['status'] is None or len(row['status']) == 0:
if len(noneValid) > 0:
noneValid += ",状态不能为空"
else:
noneValid += "状态不能为空"
dasset = await SscfDao.getDassetIdByName(result_db, row['dassetName'])
if dasset is None:
if len(noneValid) > 0:
noneValid += ",归属资产名为无效名称(找不到对应资产目录)"
else:
noneValid += "归属资产名为无效名称(找不到对应资产目录)"
if len(noneValid) > 0:
result_list['rows'].append({
"row": index + 2,
"errorInfo": noneValid
})
continue
sscf = SaveSscfModel()
sscf.dasset_id = dasset.onum,
sscf.keyword = row['keyword']
sscf.algorithm = row['algorithm']
sscf.order = 'True' if row['order'] == '' else 'False'
sscf.whole_sentence = 'True' if row['wholeSentence'] == '' else 'False'
sscf.type = row['type']
sscf.supp_expl = row['suppExpl']
sscf.status = '1' if row['status'] == '正常' else '0'
oldSscf =await SscfDao.get_oldSscf(result_db, sscf)
if oldSscf is not None:
sscf.onum = oldSscf.onum
await cls.save_sscf(result_db, sscf, current_user)
result_list['successCount'] += 1
return result_list

4
vue-fastapi-frontend/src/views/dataint/cypz/cdplb.vue

@ -534,8 +534,8 @@ function handleExport(){
if (dataList.value.length > 0){ if (dataList.value.length > 0){
for (let i = 0; i < dataList.value.length; i++) { for (let i = 0; i < dataList.value.length; i++) {
data.push({ data.push({
"系统":getSrcSysName(dassetList.value[i].ssysId), "系统":getSrcSysName(dataList.value[i].ssysId),
"模式":dassetList.value[i].mdlName, "模式":dataList.value[i].mdlName,
"批量对象表名":dataList.value[i].bathObjTabName, "批量对象表名":dataList.value[i].bathObjTabName,
"批里对象字段名":dataList.value[i].bathObjFldName, "批里对象字段名":dataList.value[i].bathObjFldName,
"状态":dataList.value[i].status === '1'?"正常":"停用" "状态":dataList.value[i].status === '1'?"正常":"停用"

136
vue-fastapi-frontend/src/views/dataint/sscf/index.vue

@ -246,11 +246,75 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?overWrite=' + upload.overWrite"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :disabled="upload.isUploading" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</template>
</el-dialog>
<el-dialog title="导入结果" v-model="upload.showResult" width="1000px" append-to-body>
<template v-if="upload.showResult">
<el-tabs type="border-card" v-if=" (upload.resultMsg.table && upload.resultMsg.table.length>0) || (upload.resultMsg.column && upload.resultMsg.column.length>0)">
<el-tab-pane label="表级问题" v-if="upload.resultMsg.table && upload.resultMsg.table.length>0">
<el-table :data="upload.resultMsg.table" stripe style="width: 100%">
<el-table-column prop="row" label="行" />
<el-table-column prop="ssysCd" label="系统" />
<el-table-column prop="mdlName" label="模式" />
<el-table-column prop="tabEngName" label="表名称" />
<el-table-column prop="errorInfo" label="问题" />
</el-table>
</el-tab-pane>
<el-tab-pane label="字段级问题" v-if="upload.resultMsg.column && upload.resultMsg.column.length>0">
<el-table :data="upload.resultMsg.column" stripe style="width: 100%">
<el-table-column prop="row" label="行" />
<el-table-column prop="ssysCd" label="系统" />
<el-table-column prop="mdlName" label="模式" />
<el-table-column prop="tabEngName" label="表名称" />
<el-table-column prop="fldEngName" label="字段名称" />
<el-table-column prop="errorInfo" label="问题" />
</el-table>
</el-tab-pane>
</el-tabs>
<span v-if=" (upload.resultMsg.table && upload.resultMsg.table.length===0) && (upload.resultMsg.column && upload.resultMsg.column.length===0) && upload.resultMsg.successCount === 0">
上传的文档内容为空请进行有效上传
</span>
</template>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="upload.showResult = false"> </el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue' import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import { getSscfList, saveSscf, deleteSscf, get_dasset_tree, saveDassetTreeNode } from "@/api/dataint/sscf" import { getSscfList, saveSscf, deleteSscf, get_dasset_tree, saveDassetTreeNode } from "@/api/dataint/sscf"
import * as XLSX from "xlsx";
import {getToken} from "../../../utils/auth.js";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@ -291,6 +355,49 @@ const total = ref(0)
const showDialog = ref(false) const showDialog = ref(false)
const showTreeDialog = ref(false) const showTreeDialog = ref(false)
const currentTreeNode = ref({}) const currentTreeNode = ref({})
/*** 导入参数 */
const upload = reactive({
//
open: false,
//
title: "",
//
isUploading: false,
//
headers: { Authorization: "Bearer " + getToken() },
overWrite: false,
//
url: import.meta.env.VITE_APP_BASE_API + "/dataint/sscf/upload",
showResult: false,
resultMsg: {}
});
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
upload.isUploading = true;
}
/** 提交上传文件 */
function submitFileForm() {
proxy.$refs["uploadRef"].submit();
}
/** 下载模板操作 */
function importTemplate() {
proxy.download("/dataint/sscf/importTemplate", {
}, `meta_template_${new Date().getTime()}.xlsx`);
}
/** 文件上传成功处理 */
const handleFileSuccess = (response, file, fileList) => {
upload.open = false;
upload.isUploading = false;
proxy.$refs["uploadRef"].handleRemove(file);
let resData = response.data
if (resData.successCount > 0 && resData.rows.length === 0){
proxy.$modal.msgSuccess("导入成功")
}else {
upload.resultMsg = resData
upload.showResult = true
}
getList();
}
function handleAddTree(){ function handleAddTree(){
showTreeDialog.value = true showTreeDialog.value = true
currentTreeNode.value = { currentTreeNode.value = {
@ -423,10 +530,29 @@ function handleDelete(array){
}) })
} }
function handleImport(){ function handleImport(){
upload.title = "数据导入";
upload.open = true;
} }
function handleExport(){ function handleExport(){
let data = [];
if (dataList.value.length > 0){
for (let i = 0; i < dataList.value.length; i++) {
data.push({
"关键词":dataList.value[i].keyword,
"算法":dataList.value[i].algorithm,
"按顺序匹配标志":dataList.value[i].order,
"整句":dataList.value[i].wholeSentence,
"类型":dataList.value[i].type,
"补充说明":dataList.value[i].suppExpl,
"状态":dataList.value[i].status,
"负责人员":dataList.value[i].updateBy
})
}
}
const workbook = XLSX.utils.book_new();
const worksheet = XLSX.utils.json_to_sheet(data);
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
XLSX.writeFile(workbook, "数据导出.xlsx");
} }
function handleSelection(val){ function handleSelection(val){
multSelection.value= val multSelection.value= val
@ -452,9 +578,11 @@ function get_tree_data(){
/** 通过条件过滤节点 */ /** 通过条件过滤节点 */
const filterNode = (value, data) => { const filterNode = (value, data) => {
if (!value) return true; if (!value) return true;
return data.label.indexOf(value) !== -1; return data.dassetName.indexOf(value) !== -1;
}; };
watch(dasset, val => {
proxy.$refs["deptTreeRef"].filter(val);
});
onMounted(() => { onMounted(() => {
get_tree_data() get_tree_data()
handleQuery() handleQuery()

Loading…
Cancel
Save