from datetime import datetime from typing import Annotated from fastapi import File, Form, Path, Query, Request, Response, UploadFile from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel from exceptions.exception import ServiceException from module_admin.entity.vo.designer_vo import ( DeleteDesignerProfileModel, DeleteDesignerCategoryModel, DeleteDesignerWorkModel, DesignerCategoryModel, DesignerCategoryPageQueryModel, DesignerProfileModel, DesignerProfilePageQueryModel, DesignerWorkModel, DesignerWorkPageQueryModel, ) from module_admin.entity.vo.common_vo import UploadResponseModel from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.service.hf_upload_service import HfUploadService from module_admin.service.designer_service import DesignerCategoryService, DesignerProfileService, DesignerWorkService from utils.storage_runtime_util import request_backup_sync from utils.log_util import logger from utils.response_util import ResponseUtil designer_controller = APIRouterPro( prefix='/designer', order_num=20, tags=['设计师管理'], dependencies=[PreAuthDependency()] ) DESIGNER_ROLE_KEY = 'designer' def _is_designer_user(current_user: CurrentUserModel) -> bool: return bool(current_user and DESIGNER_ROLE_KEY in (current_user.roles or []) and not current_user.user.admin) async def _get_current_designer_profile(query_db: AsyncSession, current_user: CurrentUserModel) -> DesignerProfileModel: profile = await DesignerProfileService.get_profile_by_user_services(query_db, current_user.user.user_id) if not profile.profile_id: raise ServiceException(message='当前账号尚未完成设计师入驻') return profile async def _ensure_category_access(query_db: AsyncSession, category_id: int | None, profile_id: int | None = None) -> None: if not category_id: return category = await DesignerCategoryService.category_detail_services(query_db, category_id) if not category.category_id: raise ServiceException(message='分类不存在') if profile_id is not None and category.profile_id != profile_id: raise ServiceException(message='无权操作该分类') async def _ensure_work_access(query_db: AsyncSession, work_id: int, profile_id: int | None = None) -> DesignerWorkModel: work = await DesignerWorkService.work_detail_services(query_db, work_id) if not work.work_id: raise ServiceException(message='作品不存在') if profile_id is not None and work.profile_id != profile_id: raise ServiceException(message='只能操作自己的作品') return work # ======================== 作品管理 ======================== @designer_controller.get( '/work/list', summary='获取设计师作品分页列表接口', description='用于获取设计师作品分页列表', response_model=PageResponseModel[DesignerWorkModel], dependencies=[UserInterfaceAuthDependency('designer:work:list')], ) async def get_designer_work_list( request: Request, work_page_query: Annotated[DesignerWorkPageQueryModel, Query()], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) work_page_query.profile_id = current_profile.profile_id work_page_query_result = await DesignerWorkService.get_work_list_services(query_db, work_page_query, is_page=True) logger.info('获取成功') return ResponseUtil.success(model_content=work_page_query_result) @designer_controller.post( '/work', summary='新增设计师作品接口', description='用于新增设计师作品', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:work:add')], ) @ValidateFields(validate_model='add_work') @Log(title='设计师作品', business_type=BusinessType.INSERT) async def add_designer_work( request: Request, add_work: DesignerWorkModel, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) add_work.profile_id = current_profile.profile_id await _ensure_category_access(query_db, add_work.category_id, add_work.profile_id) add_work.create_by = current_user.user.user_name add_work.create_time = datetime.now() add_work.update_by = current_user.user.user_name add_work.update_time = datetime.now() add_work_result = await DesignerWorkService.add_work_services(query_db, add_work) request_backup_sync('postgres') logger.info(add_work_result.message) return ResponseUtil.success(msg=add_work_result.message) @designer_controller.put( '/work', summary='编辑设计师作品接口', description='用于编辑设计师作品', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:work:edit')], ) @ValidateFields(validate_model='edit_work') @Log(title='设计师作品', business_type=BusinessType.UPDATE) async def edit_designer_work( request: Request, edit_work: DesignerWorkModel, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) if edit_work.work_id: await _ensure_work_access(query_db, edit_work.work_id, current_profile.profile_id) edit_work.profile_id = current_profile.profile_id await _ensure_category_access(query_db, edit_work.category_id, edit_work.profile_id) edit_work.update_by = current_user.user.user_name edit_work.update_time = datetime.now() edit_work_result = await DesignerWorkService.edit_work_services(query_db, edit_work) request_backup_sync('postgres') logger.info(edit_work_result.message) return ResponseUtil.success(msg=edit_work_result.message) @designer_controller.delete( '/work/{work_ids}', summary='删除设计师作品接口', description='用于删除设计师作品', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:work:remove')], ) @Log(title='设计师作品', business_type=BusinessType.DELETE) async def delete_designer_work( request: Request, work_ids: Annotated[str, Path(description='需要删除的作品ID')], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) for work_id in work_ids.split(','): await _ensure_work_access(query_db, int(work_id), current_profile.profile_id) delete_work = DeleteDesignerWorkModel(workIds=work_ids) delete_work_result = await DesignerWorkService.delete_work_services(query_db, delete_work) request_backup_sync('postgres') logger.info(delete_work_result.message) return ResponseUtil.success(msg=delete_work_result.message) @designer_controller.get( '/work/{work_id}', summary='获取设计师作品详情接口', description='用于获取指定设计师作品的详细信息', response_model=DataResponseModel[DesignerWorkModel], dependencies=[UserInterfaceAuthDependency('designer:work:query')], ) async def query_detail_designer_work( request: Request, work_id: Annotated[int, Path(description='作品ID')], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) await _ensure_work_access(query_db, work_id, current_profile.profile_id) work_detail_result = await DesignerWorkService.work_detail_services(query_db, work_id) logger.info(f'获取work_id为{work_id}的信息成功') return ResponseUtil.success(data=work_detail_result) # ======================== 分类管理 ======================== @designer_controller.get( '/category/list', summary='获取设计师作品分类分页列表接口', description='用于获取设计师作品分类分页列表', response_model=PageResponseModel[DesignerCategoryModel], dependencies=[UserInterfaceAuthDependency('designer:category:list')], ) async def get_designer_category_list( request: Request, category_page_query: Annotated[DesignerCategoryPageQueryModel, Query()], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) category_page_query.profile_id = current_profile.profile_id category_page_query_result = await DesignerCategoryService.get_category_list_services( query_db, category_page_query, is_page=True ) logger.info('获取成功') return ResponseUtil.success(model_content=category_page_query_result) @designer_controller.post( '/category', summary='新增设计师作品分类接口', description='用于新增设计师作品分类', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:category:add')], ) @ValidateFields(validate_model='add_category') @Log(title='设计师作品分类', business_type=BusinessType.INSERT) async def add_designer_category( request: Request, add_category: DesignerCategoryModel, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) add_category.profile_id = current_profile.profile_id add_category.create_by = current_user.user.user_name add_category.create_time = datetime.now() add_category.update_by = current_user.user.user_name add_category.update_time = datetime.now() add_category_result = await DesignerCategoryService.add_category_services(query_db, add_category) request_backup_sync('postgres') logger.info(add_category_result.message) return ResponseUtil.success(msg=add_category_result.message) @designer_controller.put( '/category', summary='编辑设计师作品分类接口', description='用于编辑设计师作品分类', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:category:edit')], ) @ValidateFields(validate_model='edit_category') @Log(title='设计师作品分类', business_type=BusinessType.UPDATE) async def edit_designer_category( request: Request, edit_category: DesignerCategoryModel, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) if edit_category.category_id: await _ensure_category_access(query_db, edit_category.category_id, current_profile.profile_id) edit_category.profile_id = current_profile.profile_id edit_category.update_by = current_user.user.user_name edit_category.update_time = datetime.now() edit_category_result = await DesignerCategoryService.edit_category_services(query_db, edit_category) request_backup_sync('postgres') logger.info(edit_category_result.message) return ResponseUtil.success(msg=edit_category_result.message) @designer_controller.delete( '/category/{category_ids}', summary='删除设计师作品分类接口', description='用于删除设计师作品分类', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:category:remove')], ) @Log(title='设计师作品分类', business_type=BusinessType.DELETE) async def delete_designer_category( request: Request, category_ids: Annotated[str, Path(description='需要删除的分类ID')], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) for category_id in category_ids.split(','): await _ensure_category_access(query_db, int(category_id), current_profile.profile_id) delete_category = DeleteDesignerCategoryModel(categoryIds=category_ids) delete_category_result = await DesignerCategoryService.delete_category_services(query_db, delete_category) request_backup_sync('postgres') logger.info(delete_category_result.message) return ResponseUtil.success(msg=delete_category_result.message) @designer_controller.get( '/category/{category_id}', summary='获取设计师作品分类详情接口', description='用于获取指定设计师作品分类的详细信息', response_model=DataResponseModel[DesignerCategoryModel], dependencies=[UserInterfaceAuthDependency('designer:category:query')], ) async def query_detail_designer_category( request: Request, category_id: Annotated[int, Path(description='分类ID')], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) await _ensure_category_access(query_db, category_id, current_profile.profile_id) category_detail_result = await DesignerCategoryService.category_detail_services(query_db, category_id) logger.info(f'获取category_id为{category_id}的信息成功') return ResponseUtil.success(data=category_detail_result) # ======================== 档案管理 ======================== @designer_controller.get( '/profile/list', summary='获取设计师档案分页列表接口', description='用于获取设计师档案分页列表', response_model=PageResponseModel[DesignerProfileModel], dependencies=[UserInterfaceAuthDependency('designer:profile:list')], ) async def get_designer_profile_list( request: Request, profile_page_query: Annotated[DesignerProfilePageQueryModel, Query()], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): profile_page_query.user_id = current_user.user.user_id profile_page_query_result = await DesignerProfileService.get_profile_list_services( query_db, profile_page_query, is_page=True ) logger.info('获取设计师档案列表成功') return ResponseUtil.success(model_content=profile_page_query_result) @designer_controller.get( '/profile', summary='获取当前用户设计师档案接口', description='用于获取当前登录用户的设计师档案', response_model=DataResponseModel[DesignerProfileModel], dependencies=[UserInterfaceAuthDependency('designer:profile:query')], ) async def get_designer_profile( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: profile_result = await DesignerProfileService.get_profile_by_user_services(query_db, current_user.user.user_id) logger.info('获取设计师档案成功') return ResponseUtil.success(data=profile_result) @designer_controller.get( '/profile/{profile_id}', summary='获取设计师档案详情接口', description='用于获取指定设计师档案信息', response_model=DataResponseModel[DesignerProfileModel], dependencies=[UserInterfaceAuthDependency('designer:profile:query')], ) async def query_detail_designer_profile( request: Request, profile_id: Annotated[int, Path(description='档案ID')], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) if current_profile.profile_id != profile_id: raise ServiceException(message='只能查看自己的设计师档案') profile_result = await DesignerProfileService.get_profile_services(query_db, profile_id) logger.info(f'获取profile_id为{profile_id}的设计师档案成功') return ResponseUtil.success(data=profile_result) @designer_controller.post( '/profile', summary='新增设计师档案接口', description='用于新增设计师档案', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:profile:add')], ) @ValidateFields(validate_model='add_profile') @Log(title='设计师档案', business_type=BusinessType.INSERT) async def add_designer_profile( request: Request, add_profile: DesignerProfileModel, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): add_profile.user_id = current_user.user.user_id add_profile.create_by = current_user.user.user_name add_profile.create_time = datetime.now() add_profile.update_by = current_user.user.user_name add_profile.update_time = datetime.now() add_profile_result = await DesignerProfileService.add_profile_services(query_db, add_profile) request_backup_sync('postgres') logger.info(add_profile_result.message) return ResponseUtil.success(msg=add_profile_result.message) @designer_controller.put( '/profile', summary='编辑设计师档案接口', description='用于编辑设计师档案', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:profile:edit')], ) @ValidateFields(validate_model='edit_profile') @Log(title='设计师档案', business_type=BusinessType.UPDATE) async def edit_designer_profile( request: Request, edit_profile: DesignerProfileModel, query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) if edit_profile.profile_id and edit_profile.profile_id != current_profile.profile_id: raise ServiceException(message='只能修改自己的设计师档案') edit_profile.profile_id = current_profile.profile_id edit_profile.user_id = current_user.user.user_id elif not edit_profile.profile_id and edit_profile.user_id is None: edit_profile.user_id = current_user.user.user_id edit_profile.update_by = current_user.user.user_name edit_profile.update_time = datetime.now() edit_profile_result = await DesignerProfileService.edit_profile_services(query_db, edit_profile) request_backup_sync('postgres') logger.info(edit_profile_result.message) return ResponseUtil.success(msg=edit_profile_result.message) @designer_controller.delete( '/profile/{profile_ids}', summary='删除设计师档案接口', description='用于删除设计师档案及其关联作品/分类', response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('designer:profile:remove')], ) @Log(title='设计师档案', business_type=BusinessType.DELETE) async def delete_designer_profile( request: Request, profile_ids: Annotated[str, Path(description='需要删除的档案ID')], query_db: Annotated[AsyncSession, DBSessionDependency()], current_user: Annotated[CurrentUserModel, CurrentUserDependency()], ) -> Response: if _is_designer_user(current_user): current_profile = await _get_current_designer_profile(query_db, current_user) requested_profile_ids = [int(profile_id) for profile_id in profile_ids.split(',') if profile_id] if requested_profile_ids != [current_profile.profile_id]: raise ServiceException(message='只能删除自己的设计师档案') delete_profile = DeleteDesignerProfileModel(profileIds=profile_ids) delete_profile_result = await DesignerProfileService.delete_profile_services(query_db, delete_profile) request_backup_sync('postgres') logger.info(delete_profile_result.message) return ResponseUtil.success(msg=delete_profile_result.message) @designer_controller.post( '/upload', summary='设计师图片上传接口', description='上传图片到 Hugging Face 文件服务', response_model=DataResponseModel[UploadResponseModel], ) async def upload_designer_image( request: Request, file: Annotated[UploadFile, File(description='图片文件')], folder_query: Annotated[str | None, Query(alias='folder', description='上传目录(works/avatar/banner/category)')] = None, folder: Annotated[str | None, Form(alias='folder', description='上传目录(works/avatar/banner/category)')] = None, ) -> Response: upload_folder = folder or folder_query or 'works' upload_result = await HfUploadService.upload_image(file, folder=upload_folder) logger.info('设计师图片上传成功') return ResponseUtil.success(model_content=upload_result.result) @designer_controller.delete( '/file/{file_id}', summary='设计师图片删除接口', description='删除 Hugging Face 文件服务中的设计师图片', response_model=ResponseBaseModel, ) async def delete_designer_image( request: Request, file_id: Annotated[str, Path(description='文件ID')], ) -> Response: delete_result = await HfUploadService.delete_file(file_id) logger.info(f'设计师图片删除成功: {file_id}') return ResponseUtil.success(msg=delete_result.message)