diff --git a/vue-fastapi-backend/module_admin/controller/data_asset_controller.py b/vue-fastapi-backend/module_admin/controller/data_asset_controller.py index cbece15..9175392 100644 --- a/vue-fastapi-backend/module_admin/controller/data_asset_controller.py +++ b/vue-fastapi-backend/module_admin/controller/data_asset_controller.py @@ -212,6 +212,30 @@ async def add_dataast_appr( return ResponseUtil.success(msg=add_dataast_result.message) +@dataAssetController.post('/delastappr') +@Log(title='数据资产删除申请', business_type=BusinessType.DELETE) +async def delete_dataast_appr( + request: Request, + delete_dataasts: DataAstApprBatchModel, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + now = datetime.now() + for delete_dataast in delete_dataasts.assetItems: + delete_dataast.upd_prsn = current_user.user.user_name + delete_dataast.upd_time = now + if not delete_dataast.create_by: + delete_dataast.create_by = current_user.user.user_name + if not delete_dataast.create_time: + delete_dataast.create_time = now + + delete_dataast_result = await DataAstService.delete_dataasts_appr( + query_db, delete_dataasts, current_user.user.user_name + ) + logger.info(delete_dataast_result.message) + return ResponseUtil.success(msg=delete_dataast_result.message) + + @dataAssetController.get('/listastappr', response_model=PageResponseModel ) async def get_ast_main_appr_list( @@ -221,4 +245,4 @@ async def get_ast_main_appr_list( ): main_page_query_result = await DataAstService.get_ast_main_appr_list(query_db, flowId, is_page=True) logger.info('获取数据标准列表成功') - return ResponseUtil.success(data=main_page_query_result) \ No newline at end of file + return ResponseUtil.success(data=main_page_query_result) diff --git a/vue-fastapi-backend/module_admin/dao/data_asset_dao.py b/vue-fastapi-backend/module_admin/dao/data_asset_dao.py index 6353ccd..d88ea68 100644 --- a/vue-fastapi-backend/module_admin/dao/data_asset_dao.py +++ b/vue-fastapi-backend/module_admin/dao/data_asset_dao.py @@ -84,7 +84,11 @@ class DataAssetDao: :return: 数据资产信息对象 """ data_asset = ( - await db.execute(select(DataAssetInfoAppr).where(DataAssetInfoAppr.ast_no == ast_no)) + await db.execute( + select(DataAssetInfoAppr) + .where(DataAssetInfoAppr.ast_no == ast_no) + .order_by(DataAssetInfoAppr.create_time.desc()) + ) ).scalars().first() return data_asset @@ -220,4 +224,4 @@ class DataAssetDao: # 分页查询 result = await PageUtil.paginate(db, query, page_num, page_size, is_page) - return result \ No newline at end of file + return result diff --git a/vue-fastapi-backend/module_admin/dao/dataast_dao.py b/vue-fastapi-backend/module_admin/dao/dataast_dao.py index 2dbdb05..69f8b6f 100644 --- a/vue-fastapi-backend/module_admin/dao/dataast_dao.py +++ b/vue-fastapi-backend/module_admin/dao/dataast_dao.py @@ -125,6 +125,11 @@ class DataAstDao: await db.rollback() raise e + @classmethod + async def delete_dataast_data_by_ast_no(cls, db: AsyncSession, ast_no: int): + await db.execute(delete(DataAssetInfo).where(DataAssetInfo.ast_no == ast_no)) + await db.flush() + @classmethod async def update_data_ast_appr(cls, db: AsyncSession, update_data: DataAstInfoModel): @@ -137,4 +142,4 @@ class DataAstDao: filters = [] filters.append(DataAssetInfoAppr.flowId == flowId) query = select(DataAssetInfoAppr).where(*filters).order_by(desc(DataAssetInfoAppr.create_time)) - return await PageUtil.paginate(db, query, 0, 0, False) \ No newline at end of file + return await PageUtil.paginate(db, query, 0, 0, False) diff --git a/vue-fastapi-backend/module_admin/service/approval_service.py b/vue-fastapi-backend/module_admin/service/approval_service.py index afd91b8..b9729a8 100644 --- a/vue-fastapi-backend/module_admin/service/approval_service.py +++ b/vue-fastapi-backend/module_admin/service/approval_service.py @@ -253,6 +253,8 @@ class ApprovalService: # logger.info(f"发现新增类型变更,准备添加数据资产,AST_NO: {appr_model.ast_no}") main_model = DataAstInfoModel(**appr_model.model_dump(exclude_unset=True, by_alias=True)) await DataAstDao.add_dataast_data(result_db, main_model) + elif change_type == "delete" and appr_model.ast_no is not None: + await DataAstDao.delete_dataast_data_by_ast_no(result_db, appr_model.ast_no) # logger.info(f"数据资产添加成功,AST_NO: {main_model.ast_no}") # 更新 approStatus 状态 diff --git a/vue-fastapi-backend/module_admin/service/dataast_service.py b/vue-fastapi-backend/module_admin/service/dataast_service.py index b0aed9d..ccb5c43 100644 --- a/vue-fastapi-backend/module_admin/service/dataast_service.py +++ b/vue-fastapi-backend/module_admin/service/dataast_service.py @@ -65,6 +65,28 @@ class DataAstService: return CrudResponseModel(is_success=True, message='新增数据资产成功') + @classmethod + async def delete_dataasts_appr(cls, query_db: AsyncSession, models: DataAstApprBatchModel, username: str): + flowId = str(uuid.uuid4()) + for model in models.assetItems: + original_onum = model.onum + appr_onum = str(uuid.uuid4()) + model.onum = appr_onum + apprModel = DataAstApprModel(**model.model_dump(exclude_unset=True, by_alias=True)) + apprModel.changeType = "delete" + apprModel.compareId = original_onum + apprModel.oldInstId = str(model.ast_no) if model.ast_no is not None else original_onum + apprModel.approStatus = "waiting" + apprModel.flowId = flowId + await DataAstDao.add_dataast_appr(query_db, apprModel) + + applyModel = ApplyModel() + applyModel.businessType = "dataAssetMain" + applyModel.businessId = flowId + applyModel.applicant = username + await ApprovalService.apply_services(query_db, applyModel, 'dataAssetMain') + return CrudResponseModel(is_success=True, message='删除数据资产申请提交成功') + @classmethod async def get_ast_main_appr_list(cls, query_db: AsyncSession, flowId: str, is_page: bool = False): - return await DataAstDao.get_ast_main_appr_list(flowId, query_db) \ No newline at end of file + return await DataAstDao.get_ast_main_appr_list(flowId, query_db) diff --git a/vue-fastapi-backend/module_admin/service/meta_service.py b/vue-fastapi-backend/module_admin/service/meta_service.py index d604d48..938535a 100644 --- a/vue-fastapi-backend/module_admin/service/meta_service.py +++ b/vue-fastapi-backend/module_admin/service/meta_service.py @@ -56,7 +56,7 @@ class MetaService: table['hasAsset'] = '1' # 已发布 else: hasAsset2 = await DataAssetDao.get_data_asset_appr_by_ast_no(result_db, table['extractOnum']) - if hasAsset2: + if hasAsset2 and hasAsset2.approStatus == 'waiting': table['hasAsset'] = '2' # 发布中 table['batchTabClas'] = CamelCaseUtil.transform_result(tab_list) return meta_rel_list diff --git a/vue-fastapi-backend/module_admin/service/metasecurity_service.py b/vue-fastapi-backend/module_admin/service/metasecurity_service.py index a62b2a4..abc7ff9 100644 --- a/vue-fastapi-backend/module_admin/service/metasecurity_service.py +++ b/vue-fastapi-backend/module_admin/service/metasecurity_service.py @@ -23,6 +23,7 @@ from sqlglot.expressions import Table from sqlglot import exp ,parse_one from typing import Set from sqlparse.tokens import Keyword, DML +from urllib.parse import quote_plus class MetaSecurityService: """ 数据源安全管理模块服务层 @@ -379,7 +380,9 @@ class MetaSecurityService: # 1️⃣ 去掉 jdbc 前缀 jdbc_prefixes = { "jdbc:mysql://": len("jdbc:mysql://"), - "jdbc:postgresql://": len("jdbc:postgresql://") + "jdbc:postgresql://": len("jdbc:postgresql://"), + "jdbc:oracle:thin:@//": len("jdbc:oracle:thin:@//"), + "jdbc:oracle:thin:@": len("jdbc:oracle:thin:@") } for prefix, length in jdbc_prefixes.items(): if address.startswith(prefix): @@ -410,6 +413,21 @@ class MetaSecurityService: pool_pre_ping=True, connect_args={"timeout": 5} # ⭐ 关键 ) + elif db_type.lower() == "oracle": + address = db_params["address"].lstrip("/") + user = quote_plus(db_params["user"]) + password = quote_plus(db_params["password"]) + connect_type = (db_params.get("connectType") or "").upper() + service_or_sid = quote_plus(db_params.get("database", "")) + if connect_type == "ORACLE_SID": + conn_str = f"oracle+oracledb://{user}:{password}@{address}/?sid={service_or_sid}" + else: + conn_str = f"oracle+oracledb://{user}:{password}@{address}/?service_name={service_or_sid}" + engine = create_async_engine( + conn_str, + pool_pre_ping=True, + connect_args={"transport_connect_timeout": 5} + ) else: raise ValueError("不支持的数据库类型") @@ -438,7 +456,8 @@ class MetaSecurityService: try: async with async_session() as session: - await session.execute(text("SET statement_timeout = 30000")) + if (db_type or "").upper() == "POSTGRESQL": + await session.execute(text("SET statement_timeout = 30000")) # ⭐ 原始数量 if sql_type == "原始结果": count_sql = cls.build_count_sql(sql_query,db_type) @@ -474,6 +493,7 @@ class MetaSecurityService: if select: select.set("order", None) select.set("limit", None) + select.set("offset", None) cleaned_sql = parsed.sql(dialect=dialect) return f"SELECT COUNT(*) AS cnt FROM ({cleaned_sql}) t" @@ -495,10 +515,18 @@ class MetaSecurityService: elif db_type.lower() == "mysql": # MySQL: 直接查询表字段(MySQL 没有 schema 的概念) query = f""" - SELECT COLUMN_NAME + SELECT COLUMN_NAME AS column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{table}' """ + elif db_type.lower() == "oracle": + query = f""" + SELECT COLUMN_NAME AS "column_name" + FROM ALL_TAB_COLUMNS + WHERE OWNER = UPPER('{schema}') + AND TABLE_NAME = UPPER('{table}') + ORDER BY COLUMN_ID + """ else: raise ValueError(f"暂不支持数据库类型: {db_type}") diff --git a/vue-fastapi-backend/requirements.txt b/vue-fastapi-backend/requirements.txt index 5d642c8..3d370ae 100644 --- a/vue-fastapi-backend/requirements.txt +++ b/vue-fastapi-backend/requirements.txt @@ -34,6 +34,7 @@ mdurl==0.1.2 minio==7.2.14 numpy==2.2.1 openpyxl==3.1.5 +oracledb==2.2.1 orjson==3.10.12 packaging==25.0 pandas==2.2.2 @@ -85,4 +86,4 @@ uvloop==0.21.0 watchfiles==1.0.3 wcwidth==0.2.13 websockets==14.1 -zope.interface==7.2 \ No newline at end of file +zope.interface==7.2 diff --git a/vue-fastapi-frontend/src/api/dataAsset/assetDetail.js b/vue-fastapi-frontend/src/api/dataAsset/assetDetail.js index b6a498f..e5923fc 100644 --- a/vue-fastapi-frontend/src/api/dataAsset/assetDetail.js +++ b/vue-fastapi-frontend/src/api/dataAsset/assetDetail.js @@ -24,6 +24,14 @@ export function batch(data) { }) } +export function delAstAppr(data) { + return request({ + url: '/default-api/system/dataAsset/delastappr', + method: 'post', + data, + }) +} + export function deptTreeSelect() { return request({ url: '/default-api/system/dataAsset/sources', @@ -38,4 +46,4 @@ export function listAssetMainAppr(data) { }) } - \ No newline at end of file + diff --git a/vue-fastapi-frontend/src/api/meta/metaInfo.js b/vue-fastapi-frontend/src/api/meta/metaInfo.js index bcdc5e1..8b82e4d 100644 --- a/vue-fastapi-frontend/src/api/meta/metaInfo.js +++ b/vue-fastapi-frontend/src/api/meta/metaInfo.js @@ -1,4 +1,5 @@ import request from '@/utils/request' +import cache from '@/plugins/cache' // 查询参数列表 export function getDataSourceList(query) { @@ -100,7 +101,17 @@ export function getProcData(param){ params: param }) } - +export function getschemaById(id) { + const query = { + userName: cache.local.get("username"), + password: cache.local.get("password") + } + return request({ + url: '/ds-api/dolphinscheduler/datasources/schemas?datasource='+id, + method: 'get', + headers: {dashUserName:query.userName,dashPassword:query.password} + }) +} diff --git a/vue-fastapi-frontend/src/views/dataAsset/assetDetail/index.vue b/vue-fastapi-frontend/src/views/dataAsset/assetDetail/index.vue index 290bd18..176253f 100644 --- a/vue-fastapi-frontend/src/views/dataAsset/assetDetail/index.vue +++ b/vue-fastapi-frontend/src/views/dataAsset/assetDetail/index.vue @@ -256,7 +256,7 @@ \ No newline at end of file + + + diff --git a/vue-fastapi-frontend/src/views/meta/metatask/index.vue b/vue-fastapi-frontend/src/views/meta/metatask/index.vue index 7d417d8..61b2f3f 100644 --- a/vue-fastapi-frontend/src/views/meta/metatask/index.vue +++ b/vue-fastapi-frontend/src/views/meta/metatask/index.vue @@ -500,13 +500,22 @@ - + > + + @@ -568,6 +577,7 @@