diff --git a/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py b/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py new file mode 100644 index 0000000..a419d07 --- /dev/null +++ b/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py @@ -0,0 +1,80 @@ +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.metadata_config_vo import MetadataClasModel, MetadataClasPageQueryModel +from module_admin.service.metadata_config_service import MetadataConfigService +from config.get_db import get_db +from utils.response_util import ResponseUtil +from utils.page_util import PageResponseModel +from utils.log_util import logger +from module_admin.service.login_service import LoginService +from module_admin.entity.vo.user_vo import CurrentUserModel + +metadataConfigController = APIRouter(prefix="/metadataConfig") + +@metadataConfigController.get("/clas/list", response_model=PageResponseModel) +async def get_metadata_clas_list( + request: Request, + query: MetadataClasPageQueryModel = Depends(MetadataClasPageQueryModel.as_query), + query_db: AsyncSession = Depends(get_db), +): + result = await MetadataConfigService.get_metadata_clas_list_services(query_db, query, is_page=True) + logger.info("获取元数据分类列表成功") + return ResponseUtil.success(model_content=result) + + +@metadataConfigController.post("/clas") +@ValidateFields(validate_model="add_metadata_clas") +async def add_metadata_clas( + request: Request, + add_clas: MetadataClasModel, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), + +): + add_clas.upd_time = datetime.now() + add_clas.rec_subm_prsn = current_user.user.user_name + + # 这里没有用户信息,若需要创建人信息可自行补充 + result = await MetadataConfigService.add_metadata_clas_services(query_db, add_clas) + logger.info(result.message) + return ResponseUtil.success(msg=result.message) + + +@metadataConfigController.put("/clas") +@ValidateFields(validate_model="edit_metadata_clas") +async def edit_metadata_clas( + request: Request, + edit_clas: MetadataClasModel, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), + +): + edit_clas.upd_time = datetime.now() + edit_clas.rec_subm_prsn = current_user.user.user_name + result = await MetadataConfigService.edit_metadata_clas_services(query_db, edit_clas) + logger.info(result.message) + return ResponseUtil.success(msg=result.message) + + +@metadataConfigController.delete("/clas/{clas_ids}") +async def delete_metadata_clas( + request: Request, + clas_ids: str, + query_db: AsyncSession = Depends(get_db), +): + result = await MetadataConfigService.delete_metadata_clas_services(query_db, clas_ids) + logger.info(result.message) + return ResponseUtil.success(msg=result.message) + + +@metadataConfigController.get("/clas/{clas_id}", response_model=MetadataClasModel) +async def get_metadata_clas_detail( + request: Request, + clas_id: int, + query_db: AsyncSession = Depends(get_db), +): + result = await MetadataConfigService.get_metadata_clas_detail_services(query_db, clas_id) + logger.info(f"获取元数据分类ID={clas_id}详情成功") + return ResponseUtil.success(data=result) diff --git a/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py b/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py new file mode 100644 index 0000000..ce16c58 --- /dev/null +++ b/vue-fastapi-backend/module_admin/dao/metadata_config_dao.py @@ -0,0 +1,80 @@ +from datetime import datetime, time +from sqlalchemy import delete, select, update +from sqlalchemy.ext.asyncio import AsyncSession +from module_admin.entity.do.meta_do import MetadataClas # ORM 类 + + +class MetadataConfigDao: + """ + 标签信息数据库操作层 + """ + + @classmethod + async def get_clas_detail_by_id(cls, db: AsyncSession, clas_id: int): + """ + 根据标签序号获取标签详细信息 + """ + result = await db.execute(select(MetadataClas).where(MetadataClas.clas_onum == clas_id)) + return result.scalars().first() + + @classmethod + async def get_clas_detail_by_info(cls, db: AsyncSession, clas): + """ + 根据标签参数获取标签信息(根据 MetadataClasModel 实例) + """ + result = await db.execute( + select(MetadataClas).where( + MetadataClas.clas_name == clas.clas_name if clas.clas_name else True, + MetadataClas.clas_eff_flag == clas.clas_eff_flag if clas.clas_eff_flag else True, + ) + ) + return result.scalars().first() + + @classmethod + async def get_metadata_clas_list(cls, db: AsyncSession, query_object, is_page: bool = False): + """ + 获取标签信息列表(支持分页) + """ + 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.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( + datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(0, 0, 0)), + datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)), + ) + if query_object.begin_time and query_object.end_time else True, + ).order_by(MetadataClas.clas_onum).distinct() + + from utils.page_util import PageUtil + return await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) + + @classmethod + async def add_metadata_clas_dao(cls, db: AsyncSession, clas): + """ + 新增标签信息 + """ + db_clas = MetadataClas(**clas.model_dump()) + db.add(db_clas) + await db.flush() + return db_clas + + @classmethod + async def edit_metadata_clas_dao(cls, db: AsyncSession, clas_id: int, update_data: dict): + """ + 修改标签信息 + """ + await db.execute( + update(MetadataClas) + .where(MetadataClas.clas_onum == clas_id) + .values(**update_data) + ) + + @classmethod + async def delete_metadata_clas_dao(cls, db: AsyncSession, clas_onum_list: list[int]): + """ + 删除标签信息(支持批量) + """ + await db.execute(delete(MetadataClas).where(MetadataClas.clas_onum.in_(clas_onum_list))) 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 5c0658f..5a5f991 100644 --- a/vue-fastapi-backend/module_admin/entity/do/meta_do.py +++ b/vue-fastapi-backend/module_admin/entity/do/meta_do.py @@ -80,6 +80,7 @@ class MetadataClas(Base): 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_tmpl = Column(String(200, 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无效)') 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 new file mode 100644 index 0000000..5d21c2f --- /dev/null +++ b/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py @@ -0,0 +1,20 @@ +from datetime import datetime +from sqlalchemy import Column, String, Integer, DateTime, TIMESTAMP, func +from config.database import Base + + +# class MetadataClas(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_name = Column(String(200), default=None, comment='标签名称') +# clas_tmpl = Column(String(200), 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='更新时间') 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 new file mode 100644 index 0000000..bcea6a1 --- /dev/null +++ b/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py @@ -0,0 +1,61 @@ +from datetime import datetime +from pydantic import BaseModel, ConfigDict, Field +from pydantic.alias_generators import to_camel +from pydantic_validation_decorator import NotBlank, Size +from typing import Literal, Optional +from module_admin.annotation.pydantic_annotation import as_query + + +class MetadataClasModel(BaseModel): + """ + 标签信息表对应 Pydantic 模型 + """ + + 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='标签三级分类') + 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='标签有效标志') + rec_subm_prsn: Optional[str] = Field(default=None, description='记录提交人') + upd_time: Optional[datetime] = Field(default=None, description='更新时间') + + @NotBlank(field_name='clas_name', message='标签名称不能为空') + @Size(field_name='clas_name', min_length=1, max_length=200, message='标签名称长度不能超过200个字符') + def get_clas_name(self): + return self.clas_name + + def validate_fields(self): + self.get_clas_name() + + +class MetadataClasQueryModel(MetadataClasModel): + """ + 标签信息不分页查询模型 + """ + begin_time: Optional[str] = Field(default=None, description='开始时间') + end_time: Optional[str] = Field(default=None, description='结束时间') + + + +@as_query +class MetadataClasPageQueryModel(MetadataClasQueryModel): + """ + 标签信息分页查询模型 + """ + page_num: int = Field(default=1, description='当前页码') + page_size: int = Field(default=10, description='每页记录数') + + +class DeleteMetadataClasModel(BaseModel): + """ + 删除标签信息模型 + """ + + model_config = ConfigDict(alias_generator=to_camel) + + clas_onums: str = Field(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 new file mode 100644 index 0000000..f31fce9 --- /dev/null +++ b/vue-fastapi-backend/module_admin/service/metadata_config_service.py @@ -0,0 +1,90 @@ +from sqlalchemy.ext.asyncio import AsyncSession +from module_admin.dao.metadata_config_dao import MetadataConfigDao +from module_admin.entity.vo.common_vo import CrudResponseModel +from module_admin.entity.vo.metadata_config_vo import MetadataClasModel, MetadataClasPageQueryModel +from utils.common_util import CamelCaseUtil +from exceptions.exception import ServiceException + + +class MetadataConfigService: + """ + 元数据分类管理 Service + """ + + @classmethod + async def get_metadata_clas_list_services( + cls, query_db: AsyncSession, query_object: MetadataClasPageQueryModel, is_page: bool = False + ): + """ + 查询元数据分类列表 + """ + result = await MetadataConfigDao.get_metadata_clas_list(query_db, query_object, is_page) + return result + + @classmethod + async def add_metadata_clas_services( + cls, query_db: AsyncSession, page_object: MetadataClasModel + ): + """ + 新增元数据分类 + """ + try: + await MetadataConfigDao.add_metadata_clas_dao(query_db, page_object) + await query_db.commit() + return CrudResponseModel(is_success=True, message="新增成功") + except Exception as e: + await query_db.rollback() + raise e + + @classmethod + async def edit_metadata_clas_services( + cls, query_db: AsyncSession, page_object: MetadataClasModel + ): + """ + 编辑元数据分类 + """ + edit_data = page_object.model_dump(exclude_unset=True) + info = await cls.get_metadata_clas_detail_services(query_db, page_object.clas_onum) + if info.clas_onum: + try: + await MetadataConfigDao.edit_metadata_clas_dao(query_db, page_object.clas_onum, edit_data) + await query_db.commit() + return CrudResponseModel(is_success=True, message="更新成功") + except Exception as e: + await query_db.rollback() + raise e + else: + raise ServiceException(message="元数据标签不存在") + + @classmethod + async def delete_metadata_clas_services( + cls, query_db: AsyncSession, clas_ids: str + ): + """ + 删除元数据分类(支持多个ID用逗号分隔) + """ + if not clas_ids: + raise ServiceException(message="传入的分类ID为空") + + id_list = [int(id_str) for id_str in clas_ids.split(",") if id_str.isdigit()] + if not id_list: + raise ServiceException(message="无效的分类ID列表") + + try: + await MetadataConfigDao.delete_metadata_clas_dao(query_db, id_list) + await query_db.commit() + return CrudResponseModel(is_success=True, message="删除成功") + except Exception as e: + await query_db.rollback() + raise e + + @classmethod + async def get_metadata_clas_detail_services(cls, query_db: AsyncSession, clas_id: int): + """ + 查询元数据分类详情 + """ + result = await MetadataConfigDao.get_clas_detail_by_id(query_db, clas_id) + if result: + return MetadataClasModel(**CamelCaseUtil.transform_result(result)) + else: + return MetadataClasModel(**dict()) diff --git a/vue-fastapi-backend/server.py b/vue-fastapi-backend/server.py index 545e875..770dc3e 100644 --- a/vue-fastapi-backend/server.py +++ b/vue-fastapi-backend/server.py @@ -35,6 +35,7 @@ from module_admin.controller.cdplb_controller import cdplbController from module_admin.controller.sscf_controller import sscfController from module_admin.controller.vecset_controller import vecsetController from module_admin.controller.data_asset_controller import dataAssetController +from module_admin.controller.metadata_config_controller import metadataConfigController from sub_applications.handle import handle_sub_applications from utils.common_util import worship from utils.log_util import logger @@ -103,6 +104,7 @@ controller_list = [ {'router': sscfController, 'tags': ['智能数据-短句配置']}, {'router': vecsetController, 'tags': ['智能数据-全句配置']}, {'router': dataAssetController, 'tags': ['系统管理-数据资产详情']}, + {'router': metadataConfigController, 'tags': ['元数据管理-元数据配置管理']}, ] for controller in controller_list: diff --git a/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js b/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js new file mode 100644 index 0000000..42ceb64 --- /dev/null +++ b/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询元数据分类列表,带分页 +export function listMetadataClas(query) { + return request({ + url: '/default-api/metadataConfig/clas/list', + method: 'get', + params: query + }) +} + +// 查询元数据分类详情 +export function getMetadataClas(clasId) { + return request({ + url: '/default-api/metadataConfig/clas/' + clasId, + method: 'get' + }) +} + +// 新增元数据分类 +export function addMetadataClas(data) { + return request({ + url: '/default-api/metadataConfig/clas', + method: 'post', + data: data + }) +} + +// 修改元数据分类 +export function updateMetadataClas(data) { + return request({ + url: '/default-api/metadataConfig/clas', + method: 'put', + data: data + }) +} + +// 删除元数据分类(支持批量,逗号分隔) +export function delMetadataClas(clasIds) { + return request({ + url: '/default-api/metadataConfig/clas/' + clasIds, + method: 'delete' + }) +} diff --git a/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue b/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue new file mode 100644 index 0000000..bb8afeb --- /dev/null +++ b/vue-fastapi-frontend/src/views/metadataConfig/clas/index.vue @@ -0,0 +1,301 @@ + + + +