Browse Source

导入导出

master
xueyinfei 4 weeks ago
parent
commit
009c4aa11e
  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 fastapi import APIRouter, Depends, Request
from fastapi import APIRouter, Depends, Request, UploadFile, File, Query
from sqlalchemy.ext.asyncio import AsyncSession
from config.get_db import get_db
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.sscf_service import SscfService
from utils.response_util import ResponseUtil
from utils.common_util import bytes2file_response
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)):
result = await SscfService.delete_sscf(query_db, array)
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.ext.asyncio import AsyncSession
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 sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join
from utils.page_util import PageUtil
@ -45,6 +45,24 @@ class SscfDao:
)
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
async def update_sscf(cls, db: AsyncSession, saveObj: dict):
await db.execute(update(SysSscf), [saveObj])
@ -64,6 +82,17 @@ class SscfDao:
)
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
async def get_dasset_tree_by_code(cls, db: AsyncSession, code: str):
result = (

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

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

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

@ -1,7 +1,11 @@
import io
import json
import uuid
from typing import Optional, List
import pandas as pd
from fastapi import UploadFile
from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.vo.common_vo import CrudResponseModel
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 exceptions.exception import ServiceException, ServiceWarning
from datetime import datetime
from utils.common_util import CamelCaseUtil
from utils.common_util import *
class SscfService:
@ -94,3 +99,121 @@ class SscfService:
await db.commit()
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){
for (let i = 0; i < dataList.value.length; i++) {
data.push({
"系统":getSrcSysName(dassetList.value[i].ssysId),
"模式":dassetList.value[i].mdlName,
"系统":getSrcSysName(dataList.value[i].ssysId),
"模式":dataList.value[i].mdlName,
"批量对象表名":dataList.value[i].bathObjTabName,
"批里对象字段名":dataList.value[i].bathObjFldName,
"状态":dataList.value[i].status === '1'?"正常":"停用"

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

@ -246,11 +246,75 @@
</div>
</template>
</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>
</template>
<script setup>
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
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();
@ -291,6 +355,49 @@ const total = ref(0)
const showDialog = ref(false)
const showTreeDialog = ref(false)
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(){
showTreeDialog.value = true
currentTreeNode.value = {
@ -423,10 +530,29 @@ function handleDelete(array){
})
}
function handleImport(){
upload.title = "数据导入";
upload.open = true;
}
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){
multSelection.value= val
@ -452,9 +578,11 @@ function get_tree_data(){
/** 通过条件过滤节点 */
const filterNode = (value, data) => {
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(() => {
get_tree_data()
handleQuery()

Loading…
Cancel
Save