merge main
This commit is contained in:
@@ -21,6 +21,7 @@ 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.upload_material import router as upload_material_router
|
||||
from app.api.v1.pre_test_template import router as pre_test_template_router
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(auth_router)
|
||||
@@ -44,3 +45,4 @@ 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(upload_material_router)
|
||||
api_router.include_router(pre_test_template_router)
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
import json
|
||||
from typing import Any, Optional, Dict
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.models.pre_test_template import PreTestTemplate
|
||||
from app.utils.douyinApi import DouyinApi
|
||||
from app.utils.area import parse_district_data, get_area_by_level, get_cached_area_data, fetch_and_cache_area_data
|
||||
|
||||
|
||||
from app.dependencies import get_current_user, get_db
|
||||
from app.models.user import User
|
||||
from app.schemas.pre_test_template import (
|
||||
PreTestTemplateCreate,
|
||||
PreTestTemplateListResponse,
|
||||
PreTestTemplateOut,
|
||||
PreTestTemplateUpdate,
|
||||
)
|
||||
from app.services.pre_test_template_service import (
|
||||
create_pre_test_template,
|
||||
delete_pre_test_template,
|
||||
get_default_template,
|
||||
get_pre_test_template,
|
||||
get_pre_test_template_list,
|
||||
update_pre_test_template,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/pre-test-template", tags=["前测模板"])
|
||||
|
||||
|
||||
def _template_to_dict(template):
|
||||
return {
|
||||
"id": template.id,
|
||||
"name": template.name,
|
||||
"note": template.note,
|
||||
"platform": template.platform,
|
||||
"external_action": template.external_action,
|
||||
"cpa_bid": template.cpa_bid,
|
||||
"audience_gender": template.audience_gender,
|
||||
"audience_age": json.loads(template.audience_age) if template.audience_age else None,
|
||||
"audience_region": json.loads(template.audience_region) if template.audience_region else None,
|
||||
"audience_network": json.loads(template.audience_network) if template.audience_network else None,
|
||||
"cus_name": template.cus_name,
|
||||
"pricing_type": template.pricing_type,
|
||||
"cost_cap": template.cost_cap,
|
||||
"target_cost": template.target_cost,
|
||||
"nobid": template.nobid,
|
||||
"cpc_bid": template.cpc_bid,
|
||||
"budget": template.budget,
|
||||
"is_default": template.is_default,
|
||||
"user_id": template.user_id,
|
||||
"created_at": template.created_at,
|
||||
"updated_at": template.updated_at,
|
||||
}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/create",
|
||||
summary="创建前测模板",
|
||||
description="创建一个新的前测模板",
|
||||
)
|
||||
async def create_template(
|
||||
req: PreTestTemplateCreate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
try:
|
||||
#新增一个判断,如果模板名称重复,提示用户修改
|
||||
result = await db.execute(
|
||||
select(PreTestTemplate).where(
|
||||
PreTestTemplate.name == req.name,
|
||||
PreTestTemplate.user_id == current_user.id,
|
||||
PreTestTemplate.deleted_at.is_(None),
|
||||
)
|
||||
)
|
||||
existing_template = result.scalar()
|
||||
if existing_template:
|
||||
raise ValueError("模板名称已存在")
|
||||
|
||||
template = await create_pre_test_template(
|
||||
user_id=current_user.id,
|
||||
db=db,
|
||||
name=req.name,
|
||||
note=req.note,
|
||||
platform=req.platform,
|
||||
external_action=req.external_action,
|
||||
cpa_bid=req.cpa_bid,
|
||||
audience_gender=req.audience_gender,
|
||||
audience_age=req.audience_age,
|
||||
audience_region=req.audience_region,
|
||||
audience_network=req.audience_network,
|
||||
cus_name=req.cus_name,
|
||||
pricing_type=req.pricing_type,
|
||||
cost_cap=req.cost_cap,
|
||||
target_cost=req.target_cost,
|
||||
nobid=req.nobid,
|
||||
cpc_bid=req.cpc_bid,
|
||||
budget=req.budget,
|
||||
is_default=req.is_default,
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "创建成功",
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"创建失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/list",
|
||||
summary="获取前测模板列表",
|
||||
description="获取当前用户的前测模板列表,支持按平台筛选和分页",
|
||||
response_model=PreTestTemplateListResponse,
|
||||
)
|
||||
async def list_templates(
|
||||
platform: Optional[str] = Query(None, description="投放平台筛选(AD/QIANCHUAN/LOCAL)"),
|
||||
page: int = Query(1, description="页码,默认1"),
|
||||
page_size: int = Query(10, description="每页数量,默认10,最大100"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
try:
|
||||
result = await get_pre_test_template_list(
|
||||
user_id=current_user.id,
|
||||
db=db,
|
||||
platform=platform,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "查询成功",
|
||||
"data": [_template_to_dict(t) for t in result["data"]],
|
||||
"pagination": {
|
||||
"page": result["page"],
|
||||
"page_size": result["page_size"],
|
||||
"total": result["total"],
|
||||
"total_pages": (result["total"] + result["page_size"] - 1) // result["page_size"],
|
||||
},
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"查询失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/default",
|
||||
summary="获取默认前测模板",
|
||||
description="获取当前用户的默认前测模板",
|
||||
)
|
||||
async def get_default(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
try:
|
||||
template = await get_default_template(current_user.id, db)
|
||||
|
||||
if not template:
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "未设置默认模板",
|
||||
"data": None,
|
||||
}
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "查询成功",
|
||||
"data": _template_to_dict(template),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"查询失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/select/{template_id}",
|
||||
summary="获取前测模板详情",
|
||||
description="根据模板id获取前测模板详情",
|
||||
)
|
||||
async def get_template(
|
||||
template_id: str,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
try:
|
||||
template = await get_pre_test_template(template_id, current_user.id, db)
|
||||
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="模板不存在",
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "查询成功",
|
||||
"data": _template_to_dict(template),
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"查询失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/update/{template_id}",
|
||||
summary="更新前测模板",
|
||||
description="更新指定的前测模板",
|
||||
)
|
||||
async def update_template(
|
||||
template_id: str,
|
||||
req: PreTestTemplateUpdate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
try:
|
||||
update_data = req.dict(exclude_none=True)
|
||||
template = await update_pre_test_template(template_id, current_user.id, db, **update_data)
|
||||
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="模板不存在",
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "更新成功",
|
||||
"data": _template_to_dict(template),
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"更新失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/delete/{template_id}",
|
||||
summary="删除前测模板",
|
||||
description="软删除指定的前测模板",
|
||||
)
|
||||
async def delete_template(
|
||||
template_id: str,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
try:
|
||||
success = await delete_pre_test_template(template_id, current_user.id, db)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="模板不存在",
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "删除成功",
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"删除失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/getArea",
|
||||
summary="获取行政区域信息",
|
||||
description="获取指定级别的行政区域信息,支持一级、二级、三级区域,如果需要更新地区,执行:/api/pre-test-template/getArea?oauth_id=0019ecab9b8bc57d964&advertiser_id=1836693172153543",
|
||||
)
|
||||
async def get_template_area(
|
||||
oauth_id: str = Query(None, description="授权ID选填,更新地区必填"),
|
||||
advertiser_id: str = Query(default="1836693172153543", description="授权ID选填,更新地区必填"),
|
||||
code: Optional[str] = Query("CN", description="行政区域编码,默认中国CN,选填"),
|
||||
level: Optional[str] = Query("ONE_LEVEL", description="行政区域层级,可选值:ONE_LEVEL(获取省份)、TWO_LEVEL(市级)、THREE_LEVEL(区级)"),
|
||||
parent_code: Optional[str] = Query(None, description="父级区域编码,获取二级时传一级编码,获取三级时传二级编码"),
|
||||
) -> Any:
|
||||
try:
|
||||
# 1. 先检查缓存是否存在
|
||||
area_list = get_cached_area_data()
|
||||
|
||||
# 2. 如果缓存不存在,调用接口获取数据并保存到缓存
|
||||
if not area_list:
|
||||
area_list = await fetch_and_cache_area_data(oauth_id, advertiser_id, code)
|
||||
|
||||
# 3. 根据 level 参数过滤区域
|
||||
if level == "ONE_LEVEL":
|
||||
result = get_area_by_level(area_list, "ONE_LEVEL")
|
||||
elif level == "TWO_LEVEL":
|
||||
result = get_area_by_level(area_list, "TWO_LEVEL", parent_code)
|
||||
elif level == "THREE_LEVEL":
|
||||
if not parent_code:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="获取三级区域需要提供二级区域编码(parent_code)",
|
||||
)
|
||||
result = get_area_by_level(area_list, "THREE_LEVEL", parent_code)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"不支持的级别: {level}",
|
||||
)
|
||||
|
||||
# 4. 转换为字典格式返回
|
||||
result_dict = [area.to_dict() for area in result]
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "成功",
|
||||
"data": result_dict,
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取区域信息失败: {str(e)}",
|
||||
)
|
||||
@@ -123,7 +123,7 @@ async def send_sms_code(
|
||||
description="校验指定手机号、场景下的短信验证码。业务接口一般会内部校验,本接口主要用于前端调试或单独校验。",
|
||||
)
|
||||
async def verify_sms(req: SmsVerifyRequest):
|
||||
ok = await verify_sms_code(req.phone, req.code, req.scene.value)
|
||||
ok = await verify_sms_code(req.phone.strip(), req.code.strip(), req.scene.value)
|
||||
if not ok:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -6,13 +6,12 @@ from app.dependencies import get_current_user, get_db
|
||||
from app.models.user import User
|
||||
from app.models.user_oauth import UserOAuth
|
||||
from app.models.user_oauth_app import UserOAuthApp
|
||||
from app.schemas.user_oauth import RequestOAuthRequest, RequestOAuthResponse, UserOAuthOut
|
||||
from app.schemas.user_oauth import RequestOAuthRequest, RequestOAuthResponse, UserOAuthOut, OAuthListResponse
|
||||
from app.services.user_oauth_service import (
|
||||
build_oauth_url,
|
||||
get_token,
|
||||
get_oauth_list,
|
||||
)
|
||||
from app.tasks.user_oauth_tasks import _update_oauth_accounts
|
||||
|
||||
router = APIRouter(prefix="/user-oauth", tags=["oauth"])
|
||||
|
||||
@@ -108,6 +107,7 @@ async def juliang_callback(
|
||||
"/oauth_list",
|
||||
summary="获取账户下所有授权列表",
|
||||
description="获取当前用户下所有授权账户列表,支持按授权登录账号、开户方式、授权账户id筛选",
|
||||
response_model=OAuthListResponse,
|
||||
)
|
||||
async def oauth_list(
|
||||
account_userid: str | None = Query(None, description="授权登录账号id"),
|
||||
|
||||
Reference in New Issue
Block a user