From 4b85c1a8d01b096a076b3d95635672956c19790f Mon Sep 17 00:00:00 2001 From: "si@aidatagov.com" Date: Wed, 20 Aug 2025 02:21:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=87=E7=AD=BE=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/metadata_config_controller.py | 148 ++++++ .../module_admin/dao/metadata_config_dao.py | 391 ++++++++++++++- .../module_admin/entity/do/meta_do.py | 7 +- .../entity/do/metadata_config_do.py | 38 +- .../entity/vo/metadata_config_vo.py | 5 +- .../service/metadata_config_service.py | 294 +++++++++++- .../src/api/metadataConfig/directory.js | 92 ++++ .../clas/components/AssetMoveDialog.vue | 171 +++++++ .../clas/components/FormDialog.vue | 193 ++++++++ .../clas/components/MergerDialog.vue | 175 +++++++ .../clas/components/MoveDialog.vue | 173 +++++++ .../src/views/metadataConfig/clas/index.vue | 452 +++++++++++++++++- 12 files changed, 2097 insertions(+), 42 deletions(-) create mode 100644 vue-fastapi-frontend/src/api/metadataConfig/directory.js create mode 100644 vue-fastapi-frontend/src/views/metadataConfig/clas/components/AssetMoveDialog.vue create mode 100644 vue-fastapi-frontend/src/views/metadataConfig/clas/components/FormDialog.vue create mode 100644 vue-fastapi-frontend/src/views/metadataConfig/clas/components/MergerDialog.vue create mode 100644 vue-fastapi-frontend/src/views/metadataConfig/clas/components/MoveDialog.vue diff --git a/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py b/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py index 3e729f2..a95a041 100644 --- a/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py +++ b/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py @@ -2,6 +2,12 @@ from datetime import datetime from fastapi import APIRouter, Depends, Request, Form from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from module_admin.entity.vo.data_ast_content_vo import DataCatalogPageQueryModel, DeleteDataCatalogModel, \ + DataCatalogResponseWithChildren, DataAssetCatalogTreeResponse, DataCatalogMovedRequest, DataCatalogMergeRequest, \ + DataCatalogChild, DataCatalogMoverelRequest +from utils.log_util import logger +from config.enums import BusinessType +from module_admin.annotation.log_annotation import Log from module_admin.entity.vo.metadata_config_vo import ( MetadataClasModel, MetadataClasPageQueryModel, @@ -745,3 +751,145 @@ async def task_biz_DS_meta_metatask_delete( edit_config_result = await MetadataConfigService.ds_metatask_delete(request, query_db, process, current_user) return ResponseUtil.success(msg=edit_config_result) +# --------------------------------------------------------------------------------标签树--------------------------------------------------- +@metadataConfigController.get( + '/cata/list', response_model=PageResponseModel) +async def get_data_catalog_list( + request: Request, + catalog_page_query: DataCatalogPageQueryModel = Depends(DataCatalogPageQueryModel.as_query), + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 设置字段 + user_id = current_user.user.user_id + # 获取分页数据 + catalog_page_query_result = await MetadataConfigService.get_catalog_list_services(query_db, catalog_page_query, user_id, + is_page=True) + logger.info('获取成功') + + return ResponseUtil.success(model_content=catalog_page_query_result) +@metadataConfigController.delete('/cata/{content_onums}', + ) +@Log(title='标准分类管理', business_type=BusinessType.DELETE) +async def delete_data_catalog(request: Request, content_onums: str, query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), + ): + delete_catalog = DeleteDataCatalogModel(content_onums=content_onums) + delete_catalog_result = await MetadataConfigService.delete_catalog_services(query_db, delete_catalog, + user_id=current_user.user.user_id) + logger.info(delete_catalog_result.message) + + return ResponseUtil.success(msg=delete_catalog_result.message) +@metadataConfigController.put('/cata/removerel') +@ValidateFields(validate_model='removerel_data_ast_catalog') +@Log(title='标准分类管理', business_type=BusinessType.UPDATE) +async def removerel_data_ast_catalog( + request: Request, + removerel_catalog: DataCatalogChild, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 调用服务层方法 + removerel_result = await MetadataConfigService.removerel_data_ast_catalog_services(query_db, removerel_catalog) + logger.info(removerel_result.message) + + # 返回标准化响应 + return ResponseUtil.success() +@metadataConfigController.put('/cata/moved') +@ValidateFields(validate_model='moved_data_catalog') +@Log(title='标准分类管理', business_type=BusinessType.UPDATE) +async def moved_data_catalog( + request: Request, + moved_catalog: DataCatalogMovedRequest, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 调用服务层方法 + moved_result = await MetadataConfigService.moved_catalog_instr_services(query_db, moved_catalog) + logger.info(moved_result.message) + + # 返回标准化响应 + return ResponseUtil.success( + msg=moved_result.message + ) + + +@metadataConfigController.put('/cata/merge') +@ValidateFields(validate_model='merge_data_catalog') +@Log(title='标准分类管理', business_type=BusinessType.UPDATE) +async def moved_data_catalog( + request: Request, + merge_catalog: DataCatalogMergeRequest, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 调用服务层方法 + merge_result = await MetadataConfigService.merge_catalog_instr_services(query_db, merge_catalog) + logger.info(merge_result.message) + + # 返回标准化响应 + return ResponseUtil.success( + msg=merge_result.message + ) +@metadataConfigController.put('/cata/moverel') +@ValidateFields(validate_model='moverel_data_ast_catalog') +@Log(title='标准分类管理', business_type=BusinessType.UPDATE) +async def moverel_data_ast_catalog( + request: Request, + moverel_catalog: DataCatalogMoverelRequest, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 调用服务层方法 + moverel_result = await MetadataConfigService.moverel_data_ast_catalog_services(query_db, moverel_catalog) + logger.info(moverel_result.message) + + # 返回标准化响应 + return ResponseUtil.success() +@metadataConfigController.put('/cata/edit') +@ValidateFields(validate_model='edit_data_catalog') +@Log(title='标准分类管理', business_type=BusinessType.UPDATE) +async def edit_data_catalog( + request: Request, + edit_catalog: DataCatalogResponseWithChildren, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 设置审计字段 + edit_catalog.upd_prsn = current_user.user.user_name + + # 调用服务层方法 + edit_result = await MetadataConfigService.edit_catalog_child_services(query_db, edit_catalog) + logger.info(edit_result.message) + + # 返回标准化响应 + return ResponseUtil.success( + msg=edit_result.message + ) +@metadataConfigController.post('/cata') +@ValidateFields(validate_model='add_data_catalog') +@Log(title='标准分类管理', business_type=BusinessType.INSERT) +async def add_data_catalog( + request: Request, + add_catalog: DataCatalogResponseWithChildren, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + # 设置字段 + add_catalog.upd_prsn = current_user.user.user_name + + # 调用服务层方法 + add_result = await MetadataConfigService.add_catalog_services(query_db, add_catalog) + logger.info(add_result.message) + + # 新增成功后,更新新增数据目录的父亲节点的叶子标志为0 + if add_result.is_success: + if add_catalog.supr_content_onum is not None: + supr_content_onum = add_catalog.supr_content_onum + await MetadataConfigService.edit_catalog_leaf_services(query_db, supr_content_onum, 0) + else: + logger.error(add_result.message) + # 返回标准化响应 + return ResponseUtil.success( + msg=add_result.message + ) \ No newline at end of file diff --git a/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py b/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py index 620a4c2..bffd2c2 100644 --- a/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py +++ b/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py @@ -1,5 +1,4 @@ from datetime import datetime, time -from sqlalchemy import delete, select, update, and_ from sqlalchemy.ext.asyncio import AsyncSession from module_admin.entity.do.meta_do import MetadataClas, MetadataExtractInfo # ORM 类 from module_admin.entity.do.metadata_config_do import MetadataSec, SecuBizConfig, SecuBizPermiConfig, SecuBizConfigRela, \ @@ -7,11 +6,14 @@ from module_admin.entity.do.metadata_config_do import MetadataSec, SecuBizConfig TaskBizConfigRela, TaskBizConfig # ORM 类 from typing import List - +from module_admin.entity.do.metadata_config_do import DataAstContent, DataAstContentRela +from sqlalchemy.orm import aliased from sqlalchemy.orm import joinedload from sqlalchemy.sql import true from utils.page_util import PageUtil - +from module_admin.entity.vo.data_ast_content_vo import DataCatalogPageQueryModel, DeleteDataCatalogModel, \ + DataCatalogChild +from sqlalchemy import delete, select, update, desc, or_, not_, and_ class MetadataConfigDao: """ @@ -56,10 +58,7 @@ class MetadataConfigDao: 获取标签信息列表(支持分页) """ query = select(MetadataClas).where( - MetadataClas.clas_pri_clas.like(f"%{query_object.clas_pri_clas}%") if query_object.clas_pri_clas else True, - MetadataClas.clas_scd_clas.like(f"%{query_object.clas_scd_clas}%") if query_object.clas_scd_clas else True, - MetadataClas.clas_thre_clas.like( - f"%{query_object.clas_thre_clas}%") if query_object.clas_thre_clas else True, + MetadataClas.belt_batch_content==query_object.belt_batch_content if query_object.belt_batch_content else True, MetadataClas.clas_name.like(f"%{query_object.clas_name}%") if query_object.clas_name else True, MetadataClas.clas_eff_flag == query_object.clas_eff_flag if query_object.clas_eff_flag else True, MetadataClas.upd_time.between( @@ -422,3 +421,381 @@ class MetadataConfigDao: .where(TaskBizConfigRela.biz_onum == biz_onum) ) return result.scalars().all() + # 数据标准目录 + + @classmethod + async def get_catalog_by_id(cls, db: AsyncSession, content_onum: int): + """ + 根据目录ID获取目录详细信息 + + :param db: orm对象 + :param content_onum: 目录ID + :return: 目录信息对象 + """ + catalog_info = ( + (await db.execute(select(DataAstContent).where(DataAstContent.content_onum == content_onum, + DataAstContent.content_stat == 1))) + .scalars() + .first() + ) + + return catalog_info + + @classmethod + async def get_catalog_detail_by_info(cls, db: AsyncSession, catalog: DataCatalogPageQueryModel): + """ + 根据目录参数获取目录信息 + + :param db: orm对象 + :param catalog: 目录参数对象 + :return: 目录信息对象 + """ + catalog_info = ( + ( + await db.execute( + select(DataAstContent).where( + DataAstContent.content_name == catalog.content_name if catalog.content_name else True, + DataAstContent.content_stat == catalog.content_stat if catalog.content_stat else True, + DataAstContent.content_pic == catalog.content_pic if catalog.content_pic else True, + DataAstContent.content_stat == 1, + ) + ) + ) + .scalars() + .first() + ) + + return catalog_info + + @classmethod + async def update_leaf_node_flag(cls, db: AsyncSession): + """ + 更新leaf_node_flag字段 + """ + # 创建别名对象 + t2 = aliased(DataAstContent, name='t2') # 正确使用aliased创建别名 + subquery = ( + select(DataAstContent.content_onum) + .where( + DataAstContent.content_stat == '1', + DataAstContent.leaf_node_flag == 0, + not_( + select(1) + .select_from(t2) # 使用别名后的表 + .where( + t2.supr_content_onum == DataAstContent.content_onum, + t2.content_stat == '1' + ) + .exists() # 添加exists()方法 + ) + ) + ).alias('temp') + + stmt = ( + update(DataAstContent) + .where(DataAstContent.content_onum.in_(subquery)) + .values(leaf_node_flag=1, upd_time=datetime.now()) + ) + await db.execute(stmt) + + @classmethod + async def add_catalog_dao(cls, db: AsyncSession, catalog1: dict, catalog2: dict): + """ + 新增目录数据库操作 + + :param db: orm对象 + :param catalog: 目录对象 + :return: + """ + db_catalog = DataAstContent(**catalog1) + db.add(db_catalog) + await db.flush() + + # 处理子关系(统一转换为 ORM 模型) + for child in catalog2.get('children', []): + # 如果是 Pydantic 模型实例,先转换为字典 + if isinstance(child, DataCatalogChild): + child_dict = child.model_dump() + elif isinstance(child, dict): + child_dict = child + else: + raise TypeError("不支持的子关系数据类型") + + # 创建 ORM 模型实例 + processed_child = dict(child_dict) + processed_child['content_onum'] = db_catalog.content_onum + db_child = DataAstContentRela(**processed_child) + + db.add(db_child) + await db.flush() + + return db_catalog + + @classmethod + async def edit_catalog_leaf_dao(cls, db: AsyncSession, catalog: dict): + """ + 编辑叶子节点目录数据库操作 + + :param db: orm对象 + :param catalog: 需要更新的目录字典 + :return: + """ + content_onum = catalog['content_onum'] + stmt = ( + update(DataAstContent) + .where(DataAstContent.content_onum == content_onum) + .values( + leaf_node_flag=catalog['leaf_node_flag'] + ) + ) + + await db.execute(stmt) + + @classmethod + async def edit_catalog_child_dao(cls, db: AsyncSession, catalog: dict): + """ + 编辑目录数据库操作 + + :param db: orm对象 + :param catalog: 需要更新的目录字典 + :return: + """ + content_onum = catalog['content_onum'] + + stmt = ( + update(DataAstContent) + .where(DataAstContent.content_onum == content_onum) + .values( + content_name=catalog['content_name'], + content_stat=catalog['content_stat'], + content_intr=catalog['content_intr'], + content_pic=catalog['content_pic'], + supr_content_onum=catalog['supr_content_onum'], + leaf_node_flag=catalog['leaf_node_flag'], + upd_prsn=catalog['upd_prsn'], + upd_time=datetime.now() + )) + + await db.execute(stmt) + + # 处理子关系 + for child in catalog.get('children', []): + rela_onum = child.get('rela_onum') + if rela_onum: + st = ( + update(DataAstContentRela) + .where(DataAstContentRela.rela_onum == rela_onum) + .values( + content_onum=child.get('content_onum'), + ast_onum=child.get('ast_onum'), + rela_type=child.get('rela_type'), + rela_eff_begn_date=child.get('rela_eff_begn_date'), + rela_eff_end_date=child.get('rela_eff_end_date'), + upd_prsn=child.get('upd_prsn')) + ) + + await db.execute(st) + await cls.update_leaf_node_flag(db) + else: + child['content_onum'] = content_onum + db_child = DataAstContentRela(**child) + db.add(db_child) + await db.flush() + await cls.update_leaf_node_flag(db) + + @classmethod + async def delete_catalog_dao(cls, db: AsyncSession, catalog: DeleteDataCatalogModel): + """ + 删除目录数据库操作 + + :param db: orm对象 + :param catalog: 目录对象 + :content_stat=0 作废 + :return: + """ + content_onums = catalog.content_onums.split(',') + await db.execute( + update(DataAstContentRela) + .where(DataAstContentRela.content_onum.in_(content_onums)) + .values( + rela_status=0, + rela_eff_end_date=datetime.now() + ) + ) + await db.execute( + update(DataAstContent) + .where(DataAstContent.content_onum.in_(content_onums)) + .values( + content_stat=0, + upd_time=datetime.now() + ) + ) + + await cls.update_leaf_node_flag(db) + + @classmethod + async def moved_catalog_instr_dao(cls, db: AsyncSession, moved_catalog_data: dict): + """ + 编辑目录数据库操作 + + :param db: orm对象 + :param catalog: 需要更新的目录字典 + :return: + """ + # content_onum = moved_catalog_data['content_onum'] + + stmt = ( + update(DataAstContent) + .where(DataAstContent.content_onum == moved_catalog_data['content_onum'], + DataAstContent.supr_content_onum == moved_catalog_data['supr_content_onum']) + .values( + supr_content_onum=moved_catalog_data['supr_content_onum_after'], + upd_time=datetime.now() + )) + + await db.execute(stmt) + await cls.update_leaf_node_flag(db) + + @classmethod + async def merge_catalog_instr_dao(cls, db: AsyncSession, merge_catalog_data: dict): + """ + 编辑目录数据库操作 + + :param db: orm对象 + :param catalog: 需要更新的目录字典 + :return: + """ + + # stmt = ( + # update(DataAstContent) + # .where(DataAstContent.content_onum == merge_catalog_data['content_onum'] , DataAstContent.supr_content_onum == merge_catalog_data['supr_content_onum']) + # .values( + # content_onum=merge_catalog_data['content_onum_after'], + # supr_content_onum=merge_catalog_data['supr_content_onum_after'], + # upd_time=datetime.now() + # ) ) + + # await db.execute(stmt) + stmt1 = ( + update(DataAstContentRela) + .where(DataAstContentRela.content_onum == merge_catalog_data[ + 'content_onum'] and DataAstContentRela.rela_status == 1) + .values( + content_onum=merge_catalog_data['content_onum_after'], + rela_eff_begn_date=datetime.now() + ) + ) + await db.execute(stmt1) + + stmt2 = ( + update(DataAstContent) + .where(DataAstContent.content_onum == merge_catalog_data['content_onum'], + DataAstContent.supr_content_onum == merge_catalog_data['supr_content_onum']) + .values(content_stat='0') + ) + await db.execute(stmt2) + + await cls.update_leaf_node_flag(db) + + @classmethod + async def removerel_data_ast_catalog_dao(cls, db: AsyncSession, removerel_catalog_data: dict): + """ + 编辑资产关系数据库操作 + + :param db: orm对象 + :param catalog: 需要更新的目录字典 + :return: + """ + + stmt = ( + update(DataAstContentRela) + .where(DataAstContentRela.rela_onum == removerel_catalog_data['rela_onum'], + DataAstContentRela.content_onum == removerel_catalog_data['content_onum']) + .values( + rela_status=removerel_catalog_data['rela_status'] + )) + + await db.execute(stmt) + await cls.update_leaf_node_flag(db) + + @classmethod + async def moverel_data_ast_catalog_dao(cls, db: AsyncSession, moverel_catalog_data: dict): + """ + 编辑资产关系数据库操作 + + :param db: orm对象 + :param catalog: 需要更新的目录字典 + :return: + """ + + stmt = ( + update(DataAstContentRela) + .where(DataAstContentRela.rela_onum == moverel_catalog_data['rela_onum'], + DataAstContentRela.content_onum == moverel_catalog_data['content_onum']) + .values( + content_onum=moverel_catalog_data['content_onum_after'], + rela_eff_end_date=datetime.now() + )) + + await db.execute(stmt) + await cls.update_leaf_node_flag(db) + + @classmethod + async def get_catalog_list(cls, db: AsyncSession, query_object: DataCatalogPageQueryModel, user_id: int, + is_page: bool = False): + """ + 根据查询参数获取数据资产目录列表 + + :param db: 异步会话对象 + :param query_object: 分页查询参数对象 + :param is_page: 是否分页 + :return: 数据资产目录分页列表 + """ + + # 修改子查询部分 + subquery_t1 = ( + select(DataAstContentRela) + .where(DataAstContentRela.upd_prsn == query_object.upd_prsn, + DataAstContentRela.content_onum == '2' and DataAstContentRela.rela_status == '1') + .union_all( + select(DataAstContentRela) + .where(DataAstContentRela.content_onum != '2' and DataAstContentRela.rela_status == '1') + ) + ).alias('subquery_t1') # 为子查询分配唯一别名 + + query = ( + select( + DataAstContent.content_onum, + DataAstContent.content_name, + DataAstContent.content_stat, + DataAstContent.content_intr, + DataAstContent.content_pic, + DataAstContent.supr_content_onum, + DataAstContent.leaf_node_flag, + DataAstContent.upd_prsn, + DataAstContent.upd_time, + + subquery_t1.c.rela_onum, # 明确指定子查询的字段 + subquery_t1.c.ast_onum, + subquery_t1.c.rela_type, + subquery_t1.c.rela_eff_begn_date, + subquery_t1.c.rela_eff_end_date, + subquery_t1.c.upd_prsn, + ) + .distinct() + .select_from(DataAstContent) + .outerjoin(subquery_t1, DataAstContent.content_onum == subquery_t1.c.content_onum) # 明确使用子查询别名 + .where(DataAstContent.content_stat == 1) + .order_by(DataAstContent.content_onum) + ) + + # 使用分页工具进行查询 + data_ast_list = await PageUtil.paginate( + db, + query, + page_num=query_object.page_num, + page_size=query_object.page_size, + is_page=is_page + ) + + return data_ast_list \ No newline at end of file diff --git a/vue-fastapi-backend/module_admin/entity/do/meta_do.py b/vue-fastapi-backend/module_admin/entity/do/meta_do.py index 07aa07c..0bb3e64 100644 --- a/vue-fastapi-backend/module_admin/entity/do/meta_do.py +++ b/vue-fastapi-backend/module_admin/entity/do/meta_do.py @@ -78,14 +78,15 @@ class MetadataClas(Base): __tablename__ = 't_metadata_clas' clas_onum = Column(Integer, primary_key=True, comment='分类编号') - clas_pri_clas = Column(String(50, collation='utf8_general_ci'), comment='一级分类') - clas_scd_clas = Column(String(50, collation='utf8_general_ci'), comment='二级分类') + # clas_pri_clas = Column(String(50, collation='utf8_general_ci'), comment='一级分类') + # clas_scd_clas = Column(String(50, collation='utf8_general_ci'), comment='二级分类') clas_tmpl = Column(String(200, collation='utf8_general_ci'), comment='标签模板') - clas_thre_clas = Column(String(50, collation='utf8_general_ci'), comment='三级分类') + # clas_thre_clas = Column(String(50, collation='utf8_general_ci'), comment='三级分类') clas_name = Column(String(255, collation='utf8_general_ci'), comment='分类名称') clas_eff_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='生效标志(0有效 1无效)') rec_subm_prsn = Column(String(255, collation='utf8_general_ci'), comment='记录提交人') upd_time = Column(DateTime, comment='更新时间') + belt_batch_content = Column(Integer, default=None, comment='分类') class MetadataFldTabExtractInfo(Base): diff --git a/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py b/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py index 61209e0..da309ba 100644 --- a/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py +++ b/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py @@ -1,5 +1,5 @@ from datetime import datetime -from sqlalchemy import Column, String, Integer, DateTime, TIMESTAMP,Boolean, func,DECIMAL +from sqlalchemy import Column,Text,Date, String, Integer, DateTime, TIMESTAMP,Boolean, func,DECIMAL from config.database import Base @@ -10,11 +10,12 @@ from config.database import Base # __tablename__ = 't_metadata_clas' # clas_onum = Column(Integer, primary_key=True, default=0, comment='标签序号') -# clas_pri_clas = Column(String(50), default=None, comment='标签一级分类') -# clas_scd_clas = Column(String(50), default=None, comment='标签二级分类') -# clas_thre_clas = Column(String(50), default=None, comment='标签三级分类') +# # clas_pri_clas = Column(String(50), default=None, comment='标签一级分类') +# # clas_scd_clas = Column(String(50), default=None, comment='标签二级分类') +# # clas_thre_clas = Column(String(50), default=None, comment='标签三级分类') # clas_name = Column(String(200), default=None, comment='标签名称') # clas_tmpl = Column(String(200), default=None, comment='标签模版') +# belt_batch_content = Column(Integer, default=None, comment='分类') # clas_eff_flag = Column(String(1), default=None, comment='标签有效标志') # rec_subm_prsn = Column(String(64), default=None, comment='记录提交人') # upd_time = Column(TIMESTAMP, default=func.now(), onupdate=func.now(), nullable=True, comment='更新时间') @@ -157,4 +158,31 @@ class DatasecConfig(Base): status = Column(String(10), nullable=True, default='OFFLINE', comment='状态') ds_time = Column(DateTime, nullable=True, comment="调度时间") ds_ids = Column(String(50), nullable=True, comment="任务ID") - schId = Column(String(50), nullable=True, comment="调度id") \ No newline at end of file + schId = Column(String(50), nullable=True, comment="调度id") + +class DataAstContent(Base): + __tablename__ = "t_batch_content" + + content_onum = Column(Integer, primary_key=True, autoincrement=True, comment='目录序号') + content_name = Column(String(255), nullable=False, comment='目录名称') + content_stat = Column(String(10), nullable=False, comment='目录状态(有效/废弃/停用)') + content_intr = Column(Text, comment='目录简介') + content_pic = Column(String(255), comment='目录负责人') + supr_content_onum = Column(Integer, comment='上级目录序号') + leaf_node_flag = Column(Integer, default=1, comment='叶子节点标志') + upd_prsn = Column(String(255), nullable=False, comment='更新人员') + upd_time = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间') + + +class DataAstContentRela(Base): + __tablename__ = "t_batch_content_rela" + + + rela_onum = Column(Integer, primary_key=True, autoincrement=True, comment='关系序号') + content_onum = Column(Integer, nullable=False, comment='目录序号') + ast_onum = Column(Integer, nullable=False, comment='资产序号') + rela_type = Column(String(50), default='归属关系', comment='关系类型') + rela_eff_begn_date = Column(Date, nullable=True, comment='关系生效开始日期') + rela_eff_end_date = Column(Date, nullable=True, comment='关系生效结束日期') + upd_prsn = Column(String(255), nullable=False, comment='更新人员') + rela_status = Column(String(18), nullable=True, comment='关系状态') \ No newline at end of file diff --git a/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py b/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py index 55f0305..15a178c 100644 --- a/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py +++ b/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py @@ -14,9 +14,8 @@ class MetadataClasModel(BaseModel): model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) clas_onum: Optional[int] = Field(default=None, description='标签序号') - clas_pri_clas: Optional[str] = Field(default=None, description='标签一级分类') - clas_scd_clas: Optional[str] = Field(default=None, description='标签二级分类') - clas_thre_clas: Optional[str] = Field(default=None, description='标签三级分类') + belt_batch_content: Optional[int] = Field(default=None, description='分类') + clas_name: Optional[str] = Field(default=None, description='标签名称') clas_tmpl: Optional[str] = Field(default=None, description='标签模版') clas_eff_flag: Optional[Literal['0', '1']] = Field(default=None, description='标签有效标志') diff --git a/vue-fastapi-backend/module_admin/service/metadata_config_service.py b/vue-fastapi-backend/module_admin/service/metadata_config_service.py index 0d357e8..d97ef47 100644 --- a/vue-fastapi-backend/module_admin/service/metadata_config_service.py +++ b/vue-fastapi-backend/module_admin/service/metadata_config_service.py @@ -9,6 +9,8 @@ from utils.common_util import CamelCaseUtil from module_admin.entity.do.metadata_config_do import SecuBizConfigRela,TaskBizConfigRela # ORM 类 from exceptions.exception import ServiceException,ServiceWarning import uuid +from collections import defaultdict +from module_admin.entity.vo.data_ast_content_vo import DataCatalogPageQueryModel, DeleteDataCatalogModel,DataCatalogResponseWithChildren,DataCatalogMovedRequest,DataCatalogMergeRequest,DataCatalogChild,DataCatalogMoverelRequest from module_admin.entity.vo.dataSource_vo import ProcessDefinition,ParmScheduleVo,ProcessInstancePage,ParmSchedule from typing import List from datetime import datetime @@ -50,7 +52,10 @@ class MetadataConfigService: existing = await MetadataConfigDao.get_clas_by_onum(query_db, page_object.clas_onum) if existing: raise ServiceException(message=f"主键重复,clas_onum={page_object.clas_onum} 已存在") - + # 默认临时 + if not page_object.belt_batch_content: + page_object.belt_batch_content=2 + await MetadataConfigDao.add_metadata_clas_dao(query_db, page_object) await query_db.commit() return CrudResponseModel(is_success=True, message="新增成功") @@ -69,7 +74,7 @@ class MetadataConfigService: edit_data = page_object.model_dump(exclude_unset=True) # 当前记录旧主键(假设表单传入) - original_onum = page_object.original_clas_onum # 你需要在 model 中加上这个字段 + original_onum = page_object.clas_onum # 你需要在 model 中加上这个字段 # 查询当前记录是否存在 existing = await cls.get_metadata_clas_detail_services(query_db, original_onum) @@ -789,3 +794,288 @@ class MetadataConfigService: async def get_task_biz_config_rela_list_services(cls, result_db: AsyncSession, biz_onum: int): ai_session_list = await MetadataConfigDao.get_task_rela_by_biz_id(result_db, biz_onum) # 查询最新的20条 return CamelCaseUtil.transform_result(ai_session_list) +# -------------------------------标签分类分级---------------------------- + @classmethod + async def get_catalog_list_services( + cls, query_db: AsyncSession, query_object: DataCatalogPageQueryModel, user_id: int, is_page: bool = False + ): + """ + 获取数据目录列表信息service + + :param query_db: orm对象 + :param query_object: 查询参数对象 + :param is_page: 是否开启分页 + :return: 数据目录列表信息对象 + """ + catalog_list_result = await MetadataConfigDao.get_catalog_list(query_db, query_object, user_id, is_page) + + # 按contentOnum分组 + grouped = defaultdict(list) + for item in catalog_list_result.rows: + grouped[item['contentOnum']].append(item) + + nodes = {} # 存储所有处理后的节点 + + # 处理每个组,生成节点 + for belt_data_std_content, items in grouped.items(): + first_item = items[0] + is_leaf = first_item['leafNodeFlag'] == 1 + rela_onum = first_item['relaOnum'] is not None + + # 公共字段提取 + common_fields = { + 'contentOnum': first_item['contentOnum'], + 'contentName': first_item['contentName'], + 'contentStat': first_item['contentStat'], + 'contentIntr': first_item['contentIntr'], + 'contentPic': first_item['contentPic'], + 'suprContentOnum': first_item['suprContentOnum'], + 'leafNodeFlag': first_item['leafNodeFlag'], + 'updPrsn': first_item['updPrsn'], + 'updTime': first_item['updTime'], + 'children': [] + } + + nodes[belt_data_std_content] = common_fields + + + # 构建父子关系 + root = None + for belt_data_std_content, node in nodes.items(): + supr = node['suprContentOnum'] + if supr is None: + root = node + else: + parent = nodes.get(supr) + if parent: + parent['children'].append(node) + + # 对每个父节点的children进行排序,将'临时的节点'放到最后 + for belt_data_std_content, node in nodes.items(): + if 'children' in node: + # 排序时,'临时的节点'会被放到最后 + node['children'] = sorted( + node['children'], + key=lambda x: x['contentName'] == '临时', + reverse=False # True会将'临时的节点'排在最前面,False是排在最后 + ) + + print('获取数据清单内容:',root) + + catalog_list_result.rows = [root] + + return catalog_list_result + @classmethod + async def delete_catalog_services(cls, query_db: AsyncSession, request: DeleteDataCatalogModel,user_id): + """ + 删除数据目录信息service + + :param query_db: orm对象 + :param request: 删除数据目录请求对象 + :return: 删除目录操作结果 + """ + if request.content_onums: + content_onum_list = request.content_onums.split(',') + try: + for belt_data_std_content in content_onum_list: + catalog = await cls.get_catalog_detail_services(query_db, int(belt_data_std_content)) + if not catalog: + raise ServiceException(message=f'目录ID {belt_data_std_content} 不存在') + await MetadataConfigDao.delete_catalog_dao(query_db, DeleteDataCatalogModel(content_onums=belt_data_std_content)) + # await MetadataConfigDao.delete_ast_book_mark_rela_by_content_onum(query_db, int(belt_data_std_content), user_id) + await query_db.commit() + return CrudResponseModel(is_success=True, message='删除成功') + except Exception as e: + await query_db.rollback() + raise e + else: + raise ServiceException(message='传入目录id为空') + @classmethod + async def removerel_data_ast_catalog_services(cls, query_db: AsyncSession, request: DataCatalogChild): + + """ + 移除数据资产目录service + """ + + removerel_catalog_data = { + 'rela_onum': request.rela_onum, + 'content_onum': request.content_onum, + 'ast_onum': request.ast_onum, + 'rela_type': request.rela_type, + 'rela_eff_begn_date': request.rela_eff_begn_date, + 'rela_eff_end_date': request.rela_eff_end_date, + 'upd_prsn': request.upd_prsn, + 'rela_status': request.rela_status + } + + + try: + await MetadataConfigDao.removerel_data_ast_catalog_dao(query_db, removerel_catalog_data) + await query_db.commit() + return CrudResponseModel(is_success=True, message='资产移除成功') + except Exception as e: + await query_db.rollback() + raise ServiceException(message=f"移除资产时发生错误: {str(e)}") + @classmethod + async def get_catalog_detail_services(cls, query_db: AsyncSession, belt_data_std_content: int): + """ + 获取数据目录详细信息service + + :param query_db: orm对象 + :param belt_data_std_content: 数据目录ID + :return: 数据目录详细信息对象 + """ + + catalog_detail_result = await MetadataConfigDao.get_catalog_by_id(query_db, belt_data_std_content) + + return catalog_detail_result + @classmethod + async def moved_catalog_instr_services(cls, query_db: AsyncSession, request: DataCatalogMovedRequest): + + """ + 移动数据目录service + """ + + moved_catalog_data = { + 'content_onum': request.content_onum, + 'supr_content_onum': request.supr_content_onum, + 'supr_content_onum_after': request.supr_content_onum_after + } + + + try: + await MetadataConfigDao.moved_catalog_instr_dao(query_db, moved_catalog_data) + await query_db.commit() + return CrudResponseModel(is_success=True, message='目录移动成功') + except Exception as e: + await query_db.rollback() + raise ServiceException(message=f"移动目录时发生错误: {str(e)}") + @classmethod + async def merge_catalog_instr_services(cls, query_db: AsyncSession, request: DataCatalogMergeRequest): + + """ + 移动数据目录service + """ + + merge_catalog_data = { + 'content_onum': request.content_onum, + 'supr_content_onum': request.supr_content_onum, + 'content_onum_after': request.content_onum_after, + 'supr_content_onum_after': request.supr_content_onum_after + } + try: + await MetadataConfigDao.merge_catalog_instr_dao(query_db, merge_catalog_data) + await query_db.commit() + return CrudResponseModel(is_success=True, message='目录合并成功') + except Exception as e: + await query_db.rollback() + raise ServiceException(message=f"目录合并时发生错误: {str(e)}") + + @classmethod + async def edit_catalog_child_services(cls, query_db: AsyncSession, request: DataCatalogResponseWithChildren): + """ + 编辑数据目录信息service + + :param query_db: orm对象 + :param request: 编辑数据目录请求对象 + :return: 编辑目录操作结果 + """ + + catalog_data = { + 'content_onum': request.content_onum, + 'content_name': request.content_name, + 'content_stat': request.content_stat, + 'content_intr': request.content_intr, + 'content_pic': request.content_pic, + 'supr_content_onum': request.supr_content_onum, + 'leaf_node_flag': request.leaf_node_flag, + 'upd_prsn': request.upd_prsn, + 'children': [child.model_dump() for child in request.children] # 将 children 转换为字典列表 + } + + try: + for child in catalog_data["children"]: + # 设置 rela_eff_begn_date + if child.get("rela_eff_begn_date"): + child["rela_eff_begn_date"] = child["rela_eff_begn_date"].strftime("%Y-%m-%d %H:%M:%S") + else: + child["rela_eff_begn_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + # 设置 rela_eff_end_date + if child.get("rela_eff_end_date"): + child["rela_eff_end_date"] = child["rela_eff_end_date"].strftime("%Y-%m-%d %H:%M:%S") + else: + child["rela_eff_end_date"] = datetime(year=2999, month=12, day=31, hour=0, minute=0, second=0).strftime("%Y-%m-%d %H:%M:%S") + + child["upd_prsn"] = request.upd_prsn + child["rela_status"] = "1" + await MetadataConfigDao.edit_catalog_child_dao(query_db, catalog_data) + await query_db.commit() + return CrudResponseModel(is_success=True, message='更新成功') + except Exception as e: + await query_db.rollback() + raise ServiceException(message=f"更新目录时发生错误: {str(e)}") + + @classmethod + async def add_catalog_services(cls, query_db: AsyncSession, request: DataCatalogResponseWithChildren): + """ + 新增数据目录信息service + + :param query_db: orm对象 + :param request: 新增数据目录请求对象 + :return: 新增目录操作结果 + """ + catalog_data1 = { + 'content_name': request.content_name, + 'content_stat': request.content_stat, + 'content_intr': request.content_intr, + 'content_pic': request.content_pic, + 'supr_content_onum': request.supr_content_onum, + 'leaf_node_flag': request.leaf_node_flag, + 'upd_prsn': request.upd_prsn + } + catalog_data2 = { + 'content_name': request.content_name, + 'content_stat': request.content_stat, + 'content_intr': request.content_intr, + 'content_pic': request.content_pic, + 'supr_content_onum': request.supr_content_onum, + 'leaf_node_flag': request.leaf_node_flag, + 'upd_prsn': request.upd_prsn, + 'children': [child.model_dump() for child in request.children] # 将 children 转换为字典列表 + } + + + try: + for child in catalog_data2["children"]: + child["rela_eff_begn_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S"), # 设置默认值,当前时间 + child["rela_eff_end_date"] = datetime(year=2999, month=12, day=31, hour=0, minute=0, second=0).strftime("%Y-%m-%d %H:%M:%S"), # 设置默认值,2999-12-31 + child["upd_prsn"] = request.upd_prsn, + child["rela_status"] = "1" + + new_catalog = await MetadataConfigDao.add_catalog_dao(query_db, catalog_data1, catalog_data2) + await query_db.commit() + return CrudResponseModel(is_success=True, message='新增成功', data=new_catalog) + except Exception as e: + await query_db.rollback() + raise ServiceException(message=f"创建目录时发生错误: {str(e)}") + @classmethod + async def edit_catalog_leaf_services(cls, query_db: AsyncSession,content_onum : int, leaf_node_flag : int): + """ + 编辑数据目录信息service + + :param query_db: orm对象 + :param request: 编辑数据目录请求对象 + :return: 编辑目录操作结果 + """ + catalog_data1 = { + 'content_onum': content_onum, + 'leaf_node_flag': leaf_node_flag + } + try: + await MetadataConfigDao.edit_catalog_leaf_dao(query_db, catalog_data1) + await query_db.commit() + return CrudResponseModel(is_success=True, message='更新成功') + except Exception as e: + await query_db.rollback() + raise ServiceException(message=f"更新目录时发生错误: {str(e)}") \ No newline at end of file diff --git a/vue-fastapi-frontend/src/api/metadataConfig/directory.js b/vue-fastapi-frontend/src/api/metadataConfig/directory.js new file mode 100644 index 0000000..e879729 --- /dev/null +++ b/vue-fastapi-frontend/src/api/metadataConfig/directory.js @@ -0,0 +1,92 @@ +import request from '@/utils/request' + +export function getDirectoryTree(params) { + return request({ + url: '/default-api/metadataConfig/cata/list', + method: 'get', + params, + }) +} + +export function delDirectory(id) { + return request({ + url: `/default-api/metadataConfig/cata/${id}`, + method: 'delete', + }) +} +export function addDirectoryCollection(data) { + return request({ + url: '/default-api/metadataConfig/cata/bookmark ', + method: 'post', + data, + }) +} + +export function cancelDirectoryCollection(id) { + return request({ + url: `/default-api/metadataConfig/cata/bookmark/${id}`, + method: 'delete', + }) +} +export function delDirectoryAsset(data) { + return request({ + url: '/default-api/metadataConfig/cata/removerel', + method: 'put', + data, + }) +} + + + + +export function moveDirectory(data) { + return request({ + url: '/default-api/metadataConfig/cata/moved', + method: 'put', + data, + }) +} +export function mergeDirectory(data) { + return request({ + url: '/default-api/metadataConfig/cata/merge', + method: 'put', + data, + }) +} +export function moveDirectoryAsset(data) { + return request({ + url: '/default-api/metadataConfig/cata/moverel', + method: 'put', + data, + }) +} + +export function addDirectory(data) { + return request({ + url: '/default-api/metadataConfig/cata', + method: 'post', + data, + }) +} + +export function updateDirectory(data) { + return request({ + url: `/default-api/metadataConfig/cata/edit`, + method: 'put', + data, + }) +} + +export function getDirectoryAsset(params) { + return request({ + url: '/default-api/metadataConfig/cata/atree', + method: 'get', + params, + }) +} +export function getDirectory(id) { + return request({ + url: `/default-api/metadataConfig/cata/${id}`, + method: 'get', + }) +} \ No newline at end of file diff --git a/vue-fastapi-frontend/src/views/metadataConfig/clas/components/AssetMoveDialog.vue b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/AssetMoveDialog.vue new file mode 100644 index 0000000..5e78d2e --- /dev/null +++ b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/AssetMoveDialog.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/vue-fastapi-frontend/src/views/metadataConfig/clas/components/FormDialog.vue b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/FormDialog.vue new file mode 100644 index 0000000..22aaf2c --- /dev/null +++ b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/FormDialog.vue @@ -0,0 +1,193 @@ + + + diff --git a/vue-fastapi-frontend/src/views/metadataConfig/clas/components/MergerDialog.vue b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/MergerDialog.vue new file mode 100644 index 0000000..92f811e --- /dev/null +++ b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/MergerDialog.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/vue-fastapi-frontend/src/views/metadataConfig/clas/components/MoveDialog.vue b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/MoveDialog.vue new file mode 100644 index 0000000..050b9a4 --- /dev/null +++ b/vue-fastapi-frontend/src/views/metadataConfig/clas/components/MoveDialog.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue b/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue index 014f907..4cc58b9 100644 --- a/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue +++ b/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue @@ -1,10 +1,178 @@ + + + + +