From 9c51aac97b6339807abbd2075f043b89e4dc29c2 Mon Sep 17 00:00:00 2001 From: 18610128193 <10574456+chenweiqiang-123@user.noreply.gitee.com> Date: Fri, 26 Jun 2026 15:22:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B4=A6=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- video-gen-api/app/api/v1/__init__.py | 2 + .../app/api/v1/user_oauth_account.py | 100 ++++++++++++++++ .../app/schemas/user_oauth_account.py | 32 +++++ .../services/user_oauth_account_service.py | 111 ++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 video-gen-api/app/api/v1/user_oauth_account.py create mode 100644 video-gen-api/app/schemas/user_oauth_account.py create mode 100644 video-gen-api/app/services/user_oauth_account_service.py diff --git a/video-gen-api/app/api/v1/__init__.py b/video-gen-api/app/api/v1/__init__.py index d26dd612..645beee2 100644 --- a/video-gen-api/app/api/v1/__init__.py +++ b/video-gen-api/app/api/v1/__init__.py @@ -20,6 +20,7 @@ from app.api.v1.shot_replicate import router as shot_replicate_router from app.api.v1.test import router as test_router from app.api.v1.user_oauth import router as user_oauth_router from app.api.v1.user_oauth_app import router as user_oauth_app_router +from app.api.v1.user_oauth_account import router as user_oauth_account_router from app.api.v1.upload_material import router as upload_material_router from app.api.v1.pre_test_template import router as pre_test_template_router from app.api.v1.material_consumption import router as material_consumption_router @@ -49,6 +50,7 @@ api_router.include_router(shot_replicate_router) api_router.include_router(test_router) api_router.include_router(user_oauth_router) api_router.include_router(user_oauth_app_router) +api_router.include_router(user_oauth_account_router) api_router.include_router(upload_material_router) api_router.include_router(pre_test_template_router) api_router.include_router(material_consumption_router) diff --git a/video-gen-api/app/api/v1/user_oauth_account.py b/video-gen-api/app/api/v1/user_oauth_account.py new file mode 100644 index 00000000..1d29bdfd --- /dev/null +++ b/video-gen-api/app/api/v1/user_oauth_account.py @@ -0,0 +1,100 @@ +from fastapi import APIRouter, Depends, HTTPException, Query, status +from sqlalchemy.ext.asyncio import AsyncSession + +from app.dependencies import get_current_user, get_db +from app.models.user import User +from app.schemas.user_oauth_account import ( + OAuthAccountListResponse, + DeleteOAuthAccountRequest, +) +from app.services.user_oauth_account_service import ( + get_oauth_account_list, + delete_oauth_account, +) + +router = APIRouter(prefix="/oauth-account", tags=["oauth-account"]) + + +@router.get( + "/list", + summary="获取授权账户列表", + description="获取授权账户列表,支持按广告主ID、授权ID、广告账户名称筛选", +) +async def oauth_account_list( + advertiser_id: str | None = Query(None, description="广告主账户ID"), + oauth_id: str | None = Query(None, description="授权ID"), + advertiser_name: str | None = Query(None, description="广告账户名称(模糊查询)"), + page: int = Query(1, ge=1, description="页码"), + page_size: int = Query(10, ge=1, le=100, description="每页数量"), + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> dict: + """ + 获取授权账户列表 + + - **advertiser_id**: 广告主账户ID(可选) + - **oauth_id**: 授权ID(可选) + - **advertiser_name**: 广告账户名称,支持模糊查询(可选) + - **page**: 页码,默认为1 + - **page_size**: 每页数量,默认为10,最大100 + """ + try: + result = await get_oauth_account_list( + db=db, + page=page, + page_size=page_size, + advertiser_id=advertiser_id, + oauth_id=oauth_id, + advertiser_name=advertiser_name, + user_id=current_user.id, + ) + + return { + "code": 0, + "message": "查询成功", + "data": result["data"], + "pagination": { + "page": result["page"], + "page_size": result["page_size"], + "total": result["total"], + "total_pages": result["total_pages"], + }, + } + except Exception as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(e), + ) + + +@router.get( + "/delete", + summary="删除授权账户", + description="软删除授权账户", +) +async def delete_oauth_account_api( + id: str = Query(..., description="授权账户表id"), + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> dict: + try: + await delete_oauth_account( + db=db, + account_id=id, + user_id=current_user.id, + ) + + return { + "code": 0, + "message": "删除成功", + } + except ValueError as e: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(e), + ) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=str(e), + ) diff --git a/video-gen-api/app/schemas/user_oauth_account.py b/video-gen-api/app/schemas/user_oauth_account.py new file mode 100644 index 00000000..bf718830 --- /dev/null +++ b/video-gen-api/app/schemas/user_oauth_account.py @@ -0,0 +1,32 @@ +from datetime import datetime +from typing import List, Optional + +from pydantic import BaseModel, Field + + +class UserOAuthAccountOut(BaseModel): + id: str = Field(..., description="主键") + oauth_id: str = Field(..., description="授权表中的id") + advertiser_id: Optional[str] = Field(None, description="广告主账户id") + advertiser_name: Optional[str] = Field(None, description="广告账户名") + advertiser_role: Optional[str] = Field(None, description="广告账户类型") + created_at: datetime = Field(..., description="创建时间") + updated_at: datetime = Field(..., description="更新时间") + + +class PaginationInfo(BaseModel): + page: int = Field(..., description="当前页码") + page_size: int = Field(..., description="每页数量") + total: int = Field(..., description="总记录数") + total_pages: int = Field(..., description="总页数") + + +class OAuthAccountListResponse(BaseModel): + code: int = Field(0, description="返回码,0表示成功") + message: str = Field("查询成功", description="返回消息") + data: List[UserOAuthAccountOut] = Field(..., description="授权账户列表数据") + pagination: PaginationInfo = Field(..., description="分页信息") + + +class DeleteOAuthAccountRequest(BaseModel): + id: str = Field(..., description="授权账户表id") diff --git a/video-gen-api/app/services/user_oauth_account_service.py b/video-gen-api/app/services/user_oauth_account_service.py new file mode 100644 index 00000000..c3c0aa2b --- /dev/null +++ b/video-gen-api/app/services/user_oauth_account_service.py @@ -0,0 +1,111 @@ +from sqlalchemy import select, func +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models.user_oauth_account import UserOAuthAccount +from app.models.user_oauth import UserOAuth + + +async def get_oauth_account_list( + db: AsyncSession, + page: int = 1, + page_size: int = 20, + advertiser_id: str | None = None, + oauth_id: str | None = None, + advertiser_name: str | None = None, + user_id: str | None = None, +) -> dict: + # 构建连表查询 + query = select( + UserOAuthAccount.id, + UserOAuthAccount.advertiser_id, + UserOAuthAccount.advertiser_name, + UserOAuthAccount.advertiser_role, + UserOAuthAccount.oauth_id, + UserOAuthAccount.created_at, + UserOAuth.account_id, + UserOAuth.account_name, + UserOAuth.account_role, + UserOAuth.account_username, + UserOAuth.account_userid, + UserOAuth.open_type, + ).join( + UserOAuth, + UserOAuth.id == UserOAuthAccount.oauth_id + ).where( + UserOAuth.deleted_at.is_(None), + UserOAuthAccount.deleted_at.is_(None), + UserOAuth.user_id == user_id, # 过滤当前登录用户 + ) + + # 添加筛选条件 + if advertiser_id: + query = query.where(UserOAuthAccount.advertiser_id == advertiser_id) + if oauth_id: + query = query.where(UserOAuthAccount.oauth_id == oauth_id) + if advertiser_name: + query = query.where(UserOAuthAccount.advertiser_name.like(f"%{advertiser_name}%")) + + # 查询总数 + count_query = select(func.count()).select_from(query.subquery()) + total_result = await db.execute(count_query) + total = total_result.scalar() or 0 + + # 查询分页数据 + offset = (page - 1) * page_size + query = query.offset(offset).limit(page_size).order_by(UserOAuthAccount.created_at.desc()) + + result = await db.execute(query) + accounts = result.all() + + total_pages = (total + page_size - 1) // page_size if total > 0 else 0 + # 转换为字典列表(或 Pydantic 实例列表) + data = [ + { + "id": row.id, + "advertiser_id": row.advertiser_id, + "advertiser_name": row.advertiser_name, + "advertiser_role": row.advertiser_role, + "oauth_id": row.oauth_id, + "account_id": row.account_id, + "account_name": row.account_name, + "account_role": row.account_role, + "account_username": row.account_username, + "account_userid": row.account_userid, + "open_type": row.open_type, + "created_at": row.created_at, + } + for row in accounts + ] + return { + "data": data, + "page": page, + "page_size": page_size, + "total": total, + "total_pages": total_pages, + } + + +async def delete_oauth_account( + db: AsyncSession, + account_id: str, + user_id: str, +) -> bool: + result = await db.execute( + select(UserOAuthAccount).join( + UserOAuth, + UserOAuth.id == UserOAuthAccount.oauth_id + ).where( + UserOAuthAccount.id == account_id, + UserOAuthAccount.deleted_at.is_(None), + UserOAuth.user_id == user_id, # 过滤当前登录用户 + ) + ) + account = result.scalar_one_or_none() + if not account: + raise ValueError("授权账户不存在") + + # 软删除 + account.deleted_at = func.now() + await db.commit() + + return True