|
|
@ -1,4 +1,4 @@ |
|
|
|
from fastapi import Request |
|
|
|
from fastapi import Request,UploadFile,HTTPException |
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession |
|
|
|
from exceptions.exception import ServiceException |
|
|
|
from module_admin.dao.datastd_dao import DataStdDao |
|
|
@ -6,15 +6,22 @@ from module_admin.entity.vo.common_vo import CrudResponseModel |
|
|
|
from module_admin.entity.vo.datastd_vo import DataStdCodeModel, DeleteDataStdModel, DataStdDictModel, DataStdMainModel,\ |
|
|
|
DataStdMainApprModel, DataStdDictApprModel, DataStdCodeApprModel, DataStdCodePageQueryModel, StdDictNoPageParam |
|
|
|
from utils.common_util import CamelCaseUtil |
|
|
|
from module_admin.entity.vo.datastd_vo import DataStdCodeModel,DeleteDataStdModel,DataStdDictModel,DataStdMainModel,DataStdMainApprModel,DataStdDictApprModel,DataStdCodeApprModel,DataStdCodePageQueryModel |
|
|
|
import uuid |
|
|
|
from module_admin.entity.vo.approval_vo import ApplyModel |
|
|
|
from module_admin.service.approval_service import ApprovalService |
|
|
|
from module_admin.service.metatask_service import MetataskService |
|
|
|
from collections import defaultdict |
|
|
|
from datetime import datetime |
|
|
|
from config.constant import CommonConstant |
|
|
|
from module_admin.entity.vo.data_ast_content_vo import DataCatalogPageQueryModel, DeleteDataCatalogModel,DataCatalogResponseWithChildren,DataCatalogMovedRequest,DataCatalogMergeRequest,DataCatalogChild,DataCatalogMoverelRequest |
|
|
|
from module_admin.entity.vo.user_vo import CurrentUserModel |
|
|
|
|
|
|
|
from utils.common_util import CamelCaseUtil, export_list2excel, get_excel_template |
|
|
|
import io |
|
|
|
from module_admin.service.config_service import ConfigService |
|
|
|
import requests |
|
|
|
import pandas as pd |
|
|
|
from config.env import AppConfig |
|
|
|
class DataStdService: |
|
|
|
""" |
|
|
|
数据源标准服务层 |
|
|
@ -1254,26 +1261,6 @@ class DataStdService: |
|
|
|
await ApprovalService.apply_services(query_db, applyModel, 'dataStdMain') |
|
|
|
return CrudResponseModel(is_success=True, message='新增标准成功') |
|
|
|
@classmethod |
|
|
|
async def add_std_dict_appr(cls, query_db: AsyncSession, model: DataStdDictModel): |
|
|
|
if not await cls.check_dict_unique_services(query_db, model): |
|
|
|
raise ServiceException(message=f"字典编号 {model.data_dict_no} 已存在") |
|
|
|
model.onum=str(uuid.uuid4()) |
|
|
|
model.data_dict_stat="1" |
|
|
|
# 将 DataStdMainModel 转换为 DataStdMainApprModel,保留字段原始名 |
|
|
|
apprModel = DataStdDictApprModel(**model.model_dump(exclude_unset=True, by_alias=True)) |
|
|
|
apprModel.changeType="add" |
|
|
|
apprModel.compareId=model.onum |
|
|
|
apprModel.oldInstId=model.onum |
|
|
|
apprModel.approStatus="waiting" |
|
|
|
apprModel.flowId=str(uuid.uuid4()) |
|
|
|
await DataStdDao.add_std_dict_appr(query_db, apprModel) |
|
|
|
applyModel = ApplyModel() |
|
|
|
applyModel.businessType = "dataStdDict" |
|
|
|
applyModel.businessId = apprModel.flowId |
|
|
|
applyModel.applicant = apprModel.create_by |
|
|
|
await ApprovalService.apply_services(query_db, applyModel, 'dataStdDict') |
|
|
|
return CrudResponseModel(is_success=True, message='新增标准成功') |
|
|
|
@classmethod |
|
|
|
async def edit_std_main_appr(cls, query_db: AsyncSession, model: DataStdMainModel): |
|
|
|
if not await cls.check_std_num_unique(query_db, model): |
|
|
|
raise ServiceException(message=f"标准编号 {model.data_std_no} 已存在") |
|
|
@ -1302,6 +1289,27 @@ class DataStdService: |
|
|
|
await ApprovalService.apply_services(query_db, applyModel, 'dataStdMain') |
|
|
|
return CrudResponseModel(is_success=True, message='修改标准成功') |
|
|
|
@classmethod |
|
|
|
async def add_std_dict_appr(cls, query_db: AsyncSession, model: DataStdDictModel): |
|
|
|
if not await cls.check_dict_unique_services(query_db, model): |
|
|
|
raise ServiceException(message=f"字典编号 {model.data_dict_no} 已存在") |
|
|
|
model.onum=str(uuid.uuid4()) |
|
|
|
model.data_dict_stat="1" |
|
|
|
# 将 DataStdMainModel 转换为 DataStdMainApprModel,保留字段原始名 |
|
|
|
apprModel = DataStdDictApprModel(**model.model_dump(exclude_unset=True, by_alias=True)) |
|
|
|
apprModel.changeType="add" |
|
|
|
apprModel.compareId=model.onum |
|
|
|
apprModel.oldInstId=model.onum |
|
|
|
apprModel.approStatus="waiting" |
|
|
|
apprModel.flowId=str(uuid.uuid4()) |
|
|
|
await DataStdDao.add_std_dict_appr(query_db, apprModel) |
|
|
|
applyModel = ApplyModel() |
|
|
|
applyModel.businessType = "dataStdDict" |
|
|
|
applyModel.businessId = apprModel.flowId |
|
|
|
applyModel.applicant = apprModel.create_by |
|
|
|
await ApprovalService.apply_services(query_db, applyModel, 'dataStdDict') |
|
|
|
return CrudResponseModel(is_success=True, message='新增标准成功') |
|
|
|
|
|
|
|
@classmethod |
|
|
|
async def edit_std_dict_appr(cls, query_db: AsyncSession, model: DataStdDictModel): |
|
|
|
if not await cls.check_dict_unique_services(query_db, model): |
|
|
|
raise ServiceException(message=f"字典编号 {model.c} 已存在") |
|
|
@ -1327,7 +1335,7 @@ class DataStdService: |
|
|
|
applyModel.businessId = apprModel.flowId |
|
|
|
applyModel.applicant = apprModel.create_by |
|
|
|
await ApprovalService.apply_services(query_db, applyModel, 'dataStdDict') |
|
|
|
return CrudResponseModel(is_success=True, message='修改标准成功') |
|
|
|
return CrudResponseModel(is_success=True, message='修改数据字典成功') |
|
|
|
@classmethod |
|
|
|
async def delete_std_main_Appr(cls, query_db: AsyncSession, ids: str): |
|
|
|
if ids: |
|
|
@ -1577,4 +1585,191 @@ class DataStdService: |
|
|
|
return { |
|
|
|
"tableData": table_data, |
|
|
|
"children": children |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@staticmethod |
|
|
|
async def get_main_import_template_services(): |
|
|
|
""" |
|
|
|
获取用户导入模板service |
|
|
|
|
|
|
|
:return: 用户导入模板excel的二进制数据 |
|
|
|
""" |
|
|
|
header_list = ['标准归属', '来源系统', '标准编号', '标准中文名', '标准英文名', '标准业务定义', '标准类型', '标准来源', '数据类别', '标准业务定义', '安全等级', '代码编号', '业务认则部门', '业务认则人员', '技术认则部门', '技术认则人员'] |
|
|
|
selector_header_list = ['标准归属', '标准类型',"标准来源"] |
|
|
|
option_list = [{'标准归属': ['公司级', '系统级']}, {'标准类型': ['基础数据', '指标数据']}, {'标准来源': ['行业标准', '自建标准']}] |
|
|
|
binary_data = get_excel_template( |
|
|
|
header_list=header_list, selector_header_list=selector_header_list, option_list=option_list |
|
|
|
) |
|
|
|
|
|
|
|
return binary_data |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod |
|
|
|
async def batch_import_std_services( |
|
|
|
cls, |
|
|
|
request: Request, |
|
|
|
query_db: AsyncSession, |
|
|
|
file: UploadFile, |
|
|
|
current_user: CurrentUserModel |
|
|
|
): |
|
|
|
# Step 1: 读取 Excel |
|
|
|
content = await file.read() |
|
|
|
try: |
|
|
|
df = pd.read_excel(io.BytesIO(content)) |
|
|
|
except Exception: |
|
|
|
raise HTTPException(status_code=400, detail="Excel 文件解析失败") |
|
|
|
|
|
|
|
if df.empty: |
|
|
|
raise HTTPException(status_code=400, detail="导入文件内容为空") |
|
|
|
std_type_mapping = { |
|
|
|
'基础数据': '0', |
|
|
|
'指标数据': '1' |
|
|
|
} |
|
|
|
std_vest_mapping = { |
|
|
|
'公司级': 'company', |
|
|
|
'系统级': 'sys' |
|
|
|
} |
|
|
|
# 获取全量标准代码 |
|
|
|
std_code_list = await DataStdDao.get_std_code_list_all(query_db) |
|
|
|
|
|
|
|
# Step 2: 表头映射(中文转英文字段名) |
|
|
|
header_dict = { |
|
|
|
'标准归属': 'data_std_vest', |
|
|
|
'来源系统': 'std_source_system', |
|
|
|
'标准编号': 'data_std_no', |
|
|
|
'标准中文名': 'std_name_zh', |
|
|
|
'标准英文名': 'std_name_en', |
|
|
|
'标准业务定义': 'std_definition', |
|
|
|
'标准类型': 'std_type', |
|
|
|
'标准来源': 'std_source', |
|
|
|
'数据类别': 'data_category', |
|
|
|
'安全等级': 'security_level', |
|
|
|
'代码编号': 'code_no', |
|
|
|
'业务认则部门': 'biz_dept', |
|
|
|
'业务认则人员': 'biz_person', |
|
|
|
'技术认则部门': 'tech_dept', |
|
|
|
'技术认则人员': 'tech_person', |
|
|
|
} |
|
|
|
df.rename(columns=header_dict, inplace=True) |
|
|
|
# ds系统列表 |
|
|
|
data_tree_result = await MetataskService.get_data_source_tree( request,current_user) |
|
|
|
|
|
|
|
# Step 3: 生成统一 flowId |
|
|
|
batch_flow_id = str(uuid.uuid4()) |
|
|
|
|
|
|
|
try: |
|
|
|
for idx, row in df.iterrows(): |
|
|
|
# VO 数据校验 |
|
|
|
try: |
|
|
|
input_code_no = row.get('code_no') |
|
|
|
matched_code = next((item for item in std_code_list if item.cd_no == input_code_no), None) |
|
|
|
|
|
|
|
if input_code_no and not matched_code: |
|
|
|
raise HTTPException(status_code=400, detail=f"第 {idx + 2} 行导入失败,代码编号 [{input_code_no}] 在系统中不存在") |
|
|
|
# 获取来源系统字段 |
|
|
|
input_src_sys = row.get("std_source_system") |
|
|
|
matched_source = next((ds for ds in data_tree_result if ds.name == input_src_sys or str(ds.id) == str(input_src_sys)), None) |
|
|
|
|
|
|
|
# 如果能匹配到系统,则使用 id;否则判断是否为空 |
|
|
|
if matched_source: |
|
|
|
src_sys_id = matched_source.id |
|
|
|
elif not input_src_sys or str(input_src_sys).strip() == "": |
|
|
|
src_sys_id = 10000 |
|
|
|
else: |
|
|
|
raise HTTPException(status_code=400, detail=f"第 {idx + 2} 行导入失败,来源系统 [{input_src_sys}] 不存在") |
|
|
|
|
|
|
|
# 如果存在,使用对应 onum 作为 cdId |
|
|
|
cd_id = matched_code.onum if matched_code else None |
|
|
|
vo = DataStdMainModel( |
|
|
|
dataStdVest=std_vest_mapping.get(row.get('data_std_vest'), 'company'), # 默认转为 'company', |
|
|
|
srcSys=src_sys_id, |
|
|
|
dataStdNo=row.get('data_std_no'), |
|
|
|
dataStdCnName=row.get('std_name_zh'), |
|
|
|
dataStdEngName=row.get('std_name_en'), |
|
|
|
dataStdBusiDefn=row.get('std_definition'), |
|
|
|
dataStdType=std_type_mapping.get(row.get('std_type'), '1'), # 默认转为 '1', |
|
|
|
dataStdSrc=row.get('std_source'), |
|
|
|
dataClas=row.get('data_category'), |
|
|
|
dataSecLvl=str(row.get('security_level')), |
|
|
|
cdId=cd_id, |
|
|
|
dataStdBusiOwnershipDept=row.get('biz_dept'), |
|
|
|
dataStdBusiOwnershipPrsn=row.get('biz_person'), |
|
|
|
dataStdItOwnershipDept=row.get('tech_dept'), |
|
|
|
dataStdItOwnershipPrsn=row.get('tech_person'), |
|
|
|
beltDataStdContent=2, # 固定值 |
|
|
|
) |
|
|
|
except Exception as ve: |
|
|
|
raise HTTPException(status_code=400, detail=f"第 {idx + 2} 行数据校验失败: {ve}") |
|
|
|
|
|
|
|
# 构造查询对象并检查是否存在 |
|
|
|
query_obj = DataStdMainModel(dataStdNo=vo.data_std_no) |
|
|
|
exist_model = await DataStdDao.get_data_main_by_info(query_db, query_obj) |
|
|
|
|
|
|
|
# 构造审批模型(无论新增/修改) |
|
|
|
model = DataStdMainModel(**vo.model_dump(exclude_unset=True, by_alias=True)) |
|
|
|
model.create_by = current_user.user.user_name |
|
|
|
model.std_status = "1" |
|
|
|
model.belt_data_std_content = 2 |
|
|
|
model.create_time = datetime.now() |
|
|
|
|
|
|
|
if exist_model: |
|
|
|
# === 修改审批逻辑 === |
|
|
|
# 标准正在审批中则抛错 |
|
|
|
wating_list = await DataStdDao.check_std_main_waiting(exist_model.onum, query_db) |
|
|
|
if wating_list: |
|
|
|
raise ServiceException(message=f"第 {idx + 2} 行数据标准正在审批中,请等待审批完成") |
|
|
|
|
|
|
|
last_appr = await DataStdDao.get_last_std_main_appr_by_id(query_db, exist_model.onum) |
|
|
|
model.onum = exist_model.onum |
|
|
|
|
|
|
|
appr_model = DataStdMainApprModel(**model.model_dump(exclude_unset=True, by_alias=True)) |
|
|
|
appr_model.changeType = "edit" |
|
|
|
appr_model.onum = str(uuid.uuid4()) |
|
|
|
appr_model.oldInstId = model.onum |
|
|
|
appr_model.compareId = last_appr.onum if last_appr else model.onum |
|
|
|
appr_model.approStatus = "waiting" |
|
|
|
appr_model.flowId = batch_flow_id |
|
|
|
else: |
|
|
|
# === 新增审批逻辑 === |
|
|
|
model.onum = str(uuid.uuid4()) |
|
|
|
appr_model = DataStdMainApprModel(**model.model_dump(exclude_unset=True, by_alias=True)) |
|
|
|
appr_model.changeType = "add" |
|
|
|
appr_model.compareId = model.onum |
|
|
|
appr_model.oldInstId = model.onum |
|
|
|
appr_model.oldInstId = model.onum |
|
|
|
appr_model.approStatus = "waiting" |
|
|
|
appr_model.flowId = batch_flow_id |
|
|
|
|
|
|
|
# 保存审批数据 |
|
|
|
await DataStdDao.add_std_main_appr(query_db, appr_model) |
|
|
|
|
|
|
|
# 全部处理完成后统一发起审批流程 |
|
|
|
apply_model = ApplyModel() |
|
|
|
apply_model.businessType = "dataStdMain" |
|
|
|
apply_model.businessId = batch_flow_id |
|
|
|
apply_model.applicant = current_user.user.user_name |
|
|
|
await ApprovalService.apply_services(query_db, apply_model, 'dataStdMain') |
|
|
|
|
|
|
|
await query_db.commit() |
|
|
|
return CrudResponseModel(is_success=True, message="批量导入标准成功") |
|
|
|
|
|
|
|
except Exception as e: |
|
|
|
await query_db.rollback() |
|
|
|
raise ServiceException(message=f"导入失败:{str(e)}") |
|
|
|
|
|
|
|
@staticmethod |
|
|
|
async def get_dict_import_template_services(): |
|
|
|
""" |
|
|
|
获取用户导入模板service |
|
|
|
|
|
|
|
:return: 用户导入模板excel的二进制数据 |
|
|
|
""" |
|
|
|
header_list = ['部门编号', '登录名称', '用户名称', '用户邮箱', '手机号码', '用户性别', '帐号状态'] |
|
|
|
selector_header_list = ['用户性别', '帐号状态'] |
|
|
|
option_list = [{'用户性别': ['男', '女', '未知']}, {'帐号状态': ['正常', '停用']}] |
|
|
|
binary_data = get_excel_template( |
|
|
|
header_list=header_list, selector_header_list=selector_header_list, option_list=option_list |
|
|
|
) |
|
|
|
|
|
|
|
return binary_data |