merge main

This commit is contained in:
2026-06-26 15:44:39 +08:00
21 changed files with 539 additions and 2072 deletions
+2
View File
@@ -21,6 +21,7 @@ from app.api.v1.recent_generation import router as recent_generation_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
@@ -51,6 +52,7 @@ api_router.include_router(recent_generation_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)
@@ -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),
)
+3 -1
View File
@@ -7,4 +7,6 @@ from app.enums.user import *
from app.enums.credit_record import *
from app.enums.token_usage import *
from app.enums.generation_task import *
from app.enums.recent_generation import *
from app.enums.recent_generation import *
from app.enums.generation_status import *
from app.enums.sms import *
+49
View File
@@ -0,0 +1,49 @@
from enum import Enum
class GenerationMode(str, Enum):
"""生成模式。"""
STANDARD = "standard"
FAST = "fast"
class GenerationType(str, Enum):
"""生成类型。"""
video = "video"
image = "image"
class ChatGenerationTaskStatus(str, Enum):
"""聊天生成任务状态。"""
pending = "pending"
processing = "processing"
completed = "completed"
failed = "failed"
class ChatGenerationPipelineStage(str, Enum):
"""聊天生成任务阶段。"""
waiting = "waiting"
prompt_optimization = "prompt_optimization"
video_generation = "video_generation"
post_processing = "post_processing"
completed = "completed"
failed = "failed"
class ChatGenerationTaskEventType(str, Enum):
"""聊天生成任务事件类型。"""
TASK_CREATED = "TASK_CREATED"
TASK_DELETED = "TASK_DELETED"
TASK_CANCELLED = "TASK_CANCELLED"
PROMPT_OPT_STARTED = "PROMPT_OPT_STARTED"
PROMPT_OPT_COMPLETED = "PROMPT_OPT_COMPLETED"
PROMPT_OPT_FAILED = "PROMPT_OPT_FAILED"
VIDEO_GEN_STARTED = "VIDEO_GEN_STARTED"
VIDEO_GEN_COMPLETED = "VIDEO_GEN_COMPLETED"
VIDEO_GEN_FAILED = "VIDEO_GEN_FAILED"
POST_PROCESSING_STARTED = "POST_PROCESSING_STARTED"
POST_PROCESSING_COMPLETED = "POST_PROCESSING_COMPLETED"
POST_PROCESSING_FAILED = "POST_PROCESSING_FAILED"
TASK_COMPLETED = "TASK_COMPLETED"
TASK_FAILED = "TASK_FAILED"
@@ -0,0 +1,22 @@
from enum import Enum
class GenerationStatus(str, Enum):
"""生成状态。"""
prompt_optimized = "prompt_optimized"
generating = "generating"
completed = "completed"
failed = "failed"
class GenerationType(str, Enum):
"""生成类型。"""
video = "video"
image = "image"
# 生成配置常量
DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
ASPECT_RATIOS = ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9"]
RESOLUTIONS = ["480p", "720p", "1080p"]
IMAGE_SIZES = ["2K", "4K"]
+9
View File
@@ -0,0 +1,9 @@
from enum import Enum
class SmsScene(str, Enum):
"""短信场景。"""
register = "register"
login = "login"
common = "common"
set_password = "set_password"
+8 -20
View File
@@ -1,29 +1,17 @@
from enum import Enum
from pydantic import BaseModel, Field
from app.enums.generation_status import (
GenerationStatus,
GenerationType,
DURATIONS,
ASPECT_RATIOS,
RESOLUTIONS,
IMAGE_SIZES,
)
from app.schemas.common import NaiveDatetime, NaiveDatetimeOptional
from app.services.operation_log import log_operation
class GenerationStatus(str, Enum):
prompt_optimized = "prompt_optimized"
generating = "generating"
completed = "completed"
failed = "failed"
class GenerationType(str, Enum):
video = "video"
image = "image"
DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
ASPECT_RATIOS = ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9"]
RESOLUTIONS = ["480p", "720p", "1080p"]
IMAGE_SIZES = ["2K", "4K"]
class OptimizeParams(BaseModel):
project_id: str
prompt: str = Field(..., max_length=500)
+1 -8
View File
@@ -1,13 +1,6 @@
from enum import Enum
from pydantic import BaseModel, Field
class SmsScene(str, Enum):
register = "register"
login = "login"
common = "common"
set_password = "set_password"
from app.enums.sms import SmsScene
class SmsSendRequest(BaseModel):
@@ -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")
@@ -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