新增更新token,提交素材,前测模板管理
This commit is contained in:
@@ -66,7 +66,7 @@ async def create_template(
|
|||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
try:
|
try:
|
||||||
#新增一个判断,如果模板名称重复,提示用户修改
|
#新增一个判断,如果模板名称重复,提示用户修改
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(PreTestTemplate).where(
|
select(PreTestTemplate).where(
|
||||||
PreTestTemplate.name == req.name,
|
PreTestTemplate.name == req.name,
|
||||||
@@ -119,13 +119,13 @@ async def create_template(
|
|||||||
@router.get(
|
@router.get(
|
||||||
"/list",
|
"/list",
|
||||||
summary="获取前测模板列表",
|
summary="获取前测模板列表",
|
||||||
description="获取当前用户的前测模板列表,支持按平台筛选和分页",
|
description="获取当前用户的前测模板列表,支持按平台筛选和分页",
|
||||||
response_model=PreTestTemplateListResponse,
|
response_model=PreTestTemplateListResponse,
|
||||||
)
|
)
|
||||||
async def list_templates(
|
async def list_templates(
|
||||||
platform: Optional[str] = Query(None, description="投放平台筛选(AD/QIANCHUAN/LOCAL)"),
|
platform: Optional[str] = Query(None, description="投放平台筛选(AD/QIANCHUAN/LOCAL)"),
|
||||||
page: int = Query(1, description="页码,默认1"),
|
page: int = Query(1, description="页码,默认1"),
|
||||||
page_size: int = Query(10, description="每页数量,默认10,最大100"),
|
page_size: int = Query(10, description="每页数量,默认10,最大100"),
|
||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
@@ -300,20 +300,20 @@ async def delete_template(
|
|||||||
@router.get(
|
@router.get(
|
||||||
"/getArea",
|
"/getArea",
|
||||||
summary="获取行政区域信息",
|
summary="获取行政区域信息",
|
||||||
description="获取指定级别的行政区域信息,支持一级、二级、三级区域,如果需要更新地区,执行:/api/pre-test-template/getArea?oauth_id=0019ecab9b8bc57d964&advertiser_id=1836693172153543",
|
description="获取指定级别的行政区域信息,支持一级、二级、三级区域,如果需要更新地区,执行:/api/pre-test-template/getArea?oauth_id=0019ecab9b8bc57d964&advertiser_id=1836693172153543",
|
||||||
)
|
)
|
||||||
async def get_template_area(
|
async def get_template_area(
|
||||||
oauth_id: str = Query(None, description="授权ID选填,更新地区必填"),
|
oauth_id: str = Query(None, description="授权ID选填,更新地区必填"),
|
||||||
advertiser_id: str = Query(default="1836693172153543", description="授权ID选填,更新地区必填"),
|
advertiser_id: str = Query(default="1836693172153543", description="授权ID选填,更新地区必填"),
|
||||||
code: Optional[str] = Query("CN", description="行政区域编码,默认中国CN,选填"),
|
code: Optional[str] = Query("CN", description="行政区域编码,默认中国CN,选填"),
|
||||||
level: Optional[str] = Query("ONE_LEVEL", description="行政区域层级,可选值:ONE_LEVEL(获取省份)、TWO_LEVEL(市级)、THREE_LEVEL(区级)"),
|
level: Optional[str] = Query("ONE_LEVEL", description="行政区域层级,可选值:ONE_LEVEL(获取省份)、TWO_LEVEL(市级)、THREE_LEVEL(区级)"),
|
||||||
parent_code: Optional[str] = Query(None, description="父级区域编码,获取二级时传一级编码,获取三级时传二级编码"),
|
parent_code: Optional[str] = Query(None, description="父级区域编码,获取二级时传一级编码,获取三级时传二级编码"),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
try:
|
try:
|
||||||
# 1. 先检查缓存是否存在
|
# 1. 先检查缓存是否存在
|
||||||
area_list = get_cached_area_data()
|
area_list = get_cached_area_data()
|
||||||
|
|
||||||
# 2. 如果缓存不存在,调用接口获取数据并保存到缓存
|
# 2. 如果缓存不存在,调用接口获取数据并保存到缓存
|
||||||
if not area_list:
|
if not area_list:
|
||||||
area_list = await fetch_and_cache_area_data(oauth_id, advertiser_id, code)
|
area_list = await fetch_and_cache_area_data(oauth_id, advertiser_id, code)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Dict
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.dependencies import get_current_user, get_db
|
from app.dependencies import get_current_user, get_db
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
|
from app.models.pre_test_template import PreTestTemplate
|
||||||
from app.services.upload_material_service import upload_material_to_platform
|
from app.services.upload_material_service import upload_material_to_platform
|
||||||
|
from sqlalchemy import select
|
||||||
|
from app.utils.douyinApi import DouyinApi
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/upload-material", tags=["上传素材"])
|
router = APIRouter(prefix="/upload-material", tags=["上传素材"])
|
||||||
|
|
||||||
@@ -15,8 +19,8 @@ class UploadTask(BaseModel):
|
|||||||
advertiser_ids: list[str] = Field(..., description="广告主id数组,支持多条")
|
advertiser_ids: list[str] = Field(..., description="广告主id数组,支持多条")
|
||||||
resource_ids: list[str] = Field(..., description="资源id数组(generated_resources表主键)")
|
resource_ids: list[str] = Field(..., description="资源id数组(generated_resources表主键)")
|
||||||
oauth_id: str = Field(..., description="授权表id")
|
oauth_id: str = Field(..., description="授权表id")
|
||||||
is_pre_test: Optional[str] = Field(None, description="是否开启前测:是/否/0/1,预留字段")
|
is_pre_test: Optional[str] = Field(None, description="是否开启前测:1=是/2=否")
|
||||||
pre_test_template: Optional[str] = Field(None, description="前测模板id,预留字段")
|
pre_test_template: Optional[str] = Field(None, description="前测模板id")
|
||||||
|
|
||||||
|
|
||||||
class BatchUploadRequest(BaseModel):
|
class BatchUploadRequest(BaseModel):
|
||||||
@@ -33,11 +37,35 @@ async def batch_upload_material(
|
|||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
) -> Any | dict:
|
) -> Any | dict:
|
||||||
|
# try:
|
||||||
|
# params = {
|
||||||
|
# "advertiser_id": 1836693172153543,
|
||||||
|
# "video_ids": ["tos-cn-i-sd07hgqsbj/2db9a53ee3ff4b6f8a2763b3bb463047"],
|
||||||
|
# "diagnose_config": {"platform": "AD", "external_action": "AD_APP_ACTIVATE"},
|
||||||
|
# }
|
||||||
|
# response = await DouyinApi().pre_test_material("0019ecab9b8bc57d964", params)
|
||||||
|
# return response
|
||||||
|
# except Exception as e:
|
||||||
|
# import traceback
|
||||||
|
# return {
|
||||||
|
# "code": 500,
|
||||||
|
# "message": "请求失败",
|
||||||
|
# "error": str(e),
|
||||||
|
# "traceback": traceback.format_exc()
|
||||||
|
# }
|
||||||
if not req.tasks:
|
if not req.tasks:
|
||||||
raise HTTPException(
|
return {
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
"code": 0,
|
||||||
detail="上传任务列表不能为空",
|
"message": "批量上传完成",
|
||||||
)
|
"summary": {
|
||||||
|
"total_tasks": 0,
|
||||||
|
"total_success_count": 0,
|
||||||
|
"total_fail_count": 0,
|
||||||
|
"total_requested": 0,
|
||||||
|
},
|
||||||
|
"details": [],
|
||||||
|
"error": "上传任务列表不能为空"
|
||||||
|
}
|
||||||
|
|
||||||
all_results = []
|
all_results = []
|
||||||
total_success = 0
|
total_success = 0
|
||||||
@@ -56,9 +84,73 @@ async def batch_upload_material(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if not task.advertiser_ids:
|
if not task.advertiser_ids:
|
||||||
raise ValueError("广告主id数组不能为空")
|
task_result["result"] = {
|
||||||
|
"success": False,
|
||||||
|
"error": "广告主id数组不能为空",
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": 0,
|
||||||
|
"total_count": 0,
|
||||||
|
"results": [],
|
||||||
|
}
|
||||||
|
all_results.append(task_result)
|
||||||
|
continue
|
||||||
|
|
||||||
if not task.resource_ids:
|
if not task.resource_ids:
|
||||||
raise ValueError("资源id数组不能为空")
|
task_result["result"] = {
|
||||||
|
"success": False,
|
||||||
|
"error": "资源id数组不能为空",
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": 0,
|
||||||
|
"total_count": 0,
|
||||||
|
"results": [],
|
||||||
|
}
|
||||||
|
all_results.append(task_result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (task.is_pre_test == "1") and (not task.pre_test_template):
|
||||||
|
task_result["result"] = {
|
||||||
|
"success": False,
|
||||||
|
"error": "开启前测功能时,必须指定前测模板id",
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"total_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"results": [{
|
||||||
|
"resource_id": rid,
|
||||||
|
"advertiser_id": aid,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": "开启前测功能时,必须指定前测模板id"
|
||||||
|
} for rid in task.resource_ids for aid in task.advertiser_ids],
|
||||||
|
}
|
||||||
|
total_fail += len(task.resource_ids) * len(task.advertiser_ids)
|
||||||
|
all_results.append(task_result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if task.is_pre_test == "1":
|
||||||
|
template = await db.execute(
|
||||||
|
select(PreTestTemplate).where(PreTestTemplate.id == task.pre_test_template).
|
||||||
|
where(PreTestTemplate.deleted_at.is_(None)).
|
||||||
|
where(PreTestTemplate.user_id == current_user.id)
|
||||||
|
)
|
||||||
|
template = template.scalar_one_or_none()
|
||||||
|
if not template:
|
||||||
|
task_result["result"] = {
|
||||||
|
"success": False,
|
||||||
|
"error": "前测模板id不存在",
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"total_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"results": [{
|
||||||
|
"resource_id": rid,
|
||||||
|
"advertiser_id": aid,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": "前测模板id不存在"
|
||||||
|
} for rid in task.resource_ids for aid in task.advertiser_ids],
|
||||||
|
}
|
||||||
|
total_fail += len(task.resource_ids) * len(task.advertiser_ids)
|
||||||
|
all_results.append(task_result)
|
||||||
|
continue
|
||||||
|
|
||||||
result = await upload_material_to_platform(
|
result = await upload_material_to_platform(
|
||||||
task.resource_ids,
|
task.resource_ids,
|
||||||
@@ -66,6 +158,7 @@ async def batch_upload_material(
|
|||||||
task.oauth_id,
|
task.oauth_id,
|
||||||
db,
|
db,
|
||||||
current_user.id,
|
current_user.id,
|
||||||
|
task.pre_test_template if task.is_pre_test == "1" else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
task_result["result"] = {
|
task_result["result"] = {
|
||||||
@@ -75,23 +168,37 @@ async def batch_upload_material(
|
|||||||
total_success += result["success_count"]
|
total_success += result["success_count"]
|
||||||
total_fail += result["fail_count"]
|
total_fail += result["fail_count"]
|
||||||
|
|
||||||
if task.is_pre_test and task.is_pre_test in ["是", "1", "true", True]:
|
|
||||||
task_result["pre_test_reserved"] = {
|
|
||||||
"status": "reserved",
|
|
||||||
"message": "前测功能已预留,待后续开通",
|
|
||||||
"template": task.pre_test_template,
|
|
||||||
}
|
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
task_result["result"] = {
|
task_result["result"] = {
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"total_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"results": [{
|
||||||
|
"resource_id": rid,
|
||||||
|
"advertiser_id": aid,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
} for rid in task.resource_ids for aid in task.advertiser_ids],
|
||||||
}
|
}
|
||||||
total_fail += len(task.resource_ids) * len(task.advertiser_ids)
|
total_fail += len(task.resource_ids) * len(task.advertiser_ids)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
task_result["result"] = {
|
task_result["result"] = {
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": f"上传失败: {str(e)}",
|
"error": f"上传失败: {str(e)}",
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"total_count": len(task.resource_ids) * len(task.advertiser_ids),
|
||||||
|
"results": [{
|
||||||
|
"resource_id": rid,
|
||||||
|
"advertiser_id": aid,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": f"上传失败: {str(e)}"
|
||||||
|
} for rid in task.resource_ids for aid in task.advertiser_ids],
|
||||||
}
|
}
|
||||||
total_fail += len(task.resource_ids) * len(task.advertiser_ids)
|
total_fail += len(task.resource_ids) * len(task.advertiser_ids)
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ async def juliang_callback(
|
|||||||
|
|
||||||
await get_token(auth_code, user_id, app_id, db)
|
await get_token(auth_code, user_id, app_id, db)
|
||||||
return {
|
return {
|
||||||
"message": "授权成功",
|
"message": "授权成功,这里需要跳转页面路径到 /user-oauth/oauth_list",
|
||||||
"code": 0,
|
"code": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +139,7 @@ async def oauth_list(
|
|||||||
"account_name": oauth.account_name,
|
"account_name": oauth.account_name,
|
||||||
"account_role": oauth.account_role,
|
"account_role": oauth.account_role,
|
||||||
"account_username": oauth.account_username,
|
"account_username": oauth.account_username,
|
||||||
|
"account_userid": oauth.account_userid,
|
||||||
"user_id": oauth.user_id,
|
"user_id": oauth.user_id,
|
||||||
"open_type": oauth.open_type,
|
"open_type": oauth.open_type,
|
||||||
"port_type": oauth.port_type,
|
"port_type": oauth.port_type,
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
expiry_task = asyncio.create_task(_order_expiry_loop())
|
expiry_task = asyncio.create_task(_order_expiry_loop())
|
||||||
|
|
||||||
|
# 启动token刷新定时任务(每5分钟检查一次,小于800秒有效期的token进行刷新)
|
||||||
|
from app.tasks.token_refresh_task import token_refresh_scheduler
|
||||||
|
token_refresh_task = asyncio.create_task(token_refresh_scheduler())
|
||||||
|
|
||||||
# 启动时立即同步一次未支付订单
|
# 启动时立即同步一次未支付订单
|
||||||
asyncio.create_task(asyncio.sleep(5)) # 等待5秒后再同步,让系统完全启动
|
asyncio.create_task(asyncio.sleep(5)) # 等待5秒后再同步,让系统完全启动
|
||||||
async def startup_sync():
|
async def startup_sync():
|
||||||
@@ -82,6 +86,7 @@ async def lifespan(app: FastAPI):
|
|||||||
task_queue.stop()
|
task_queue.stop()
|
||||||
await queue_task
|
await queue_task
|
||||||
expiry_task.cancel()
|
expiry_task.cancel()
|
||||||
|
token_refresh_task.cancel()
|
||||||
await close_database()
|
await close_database()
|
||||||
await close_redis()
|
await close_redis()
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.models.generated_resource import GeneratedResource
|
from app.models.generated_resource import GeneratedResource
|
||||||
from app.models.user_oauth import UserOAuth
|
from app.models.user_oauth import UserOAuth
|
||||||
from app.models.resources_material import ResourcesMaterial
|
from app.models.resources_material import ResourcesMaterial
|
||||||
|
from app.models.pre_test_template import PreTestTemplate
|
||||||
from app.utils.id_gen import generate_id
|
from app.utils.id_gen import generate_id
|
||||||
from app.utils.douyinApi import DouyinApi
|
from app.utils.douyinApi import DouyinApi
|
||||||
|
|
||||||
@@ -20,6 +22,7 @@ async def upload_material_to_platform(
|
|||||||
oauth_id: str,
|
oauth_id: str,
|
||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
current_user_id: str,
|
current_user_id: str,
|
||||||
|
pre_test_template_id: str | None = None,
|
||||||
) -> any:
|
) -> any:
|
||||||
oauth = await db.execute(
|
oauth = await db.execute(
|
||||||
select(UserOAuth).where(
|
select(UserOAuth).where(
|
||||||
@@ -30,13 +33,37 @@ async def upload_material_to_platform(
|
|||||||
)
|
)
|
||||||
oauth = oauth.scalar_one_or_none()
|
oauth = oauth.scalar_one_or_none()
|
||||||
if not oauth:
|
if not oauth:
|
||||||
raise ValueError("授权记录不存在")
|
return {
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": len(resource_ids) * len(advertiser_ids),
|
||||||
|
"total_count": len(resource_ids) * len(advertiser_ids),
|
||||||
|
"results": [{
|
||||||
|
"resource_id": rid,
|
||||||
|
"advertiser_id": aid,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": "授权记录不存在"
|
||||||
|
} for rid in resource_ids for aid in advertiser_ids],
|
||||||
|
"error": "授权记录不存在"
|
||||||
|
}
|
||||||
|
|
||||||
port_type = oauth.port_type
|
port_type = oauth.port_type
|
||||||
access_token = oauth.access_token
|
access_token = oauth.access_token
|
||||||
|
|
||||||
if not access_token:
|
if not access_token:
|
||||||
raise ValueError("授权token不存在")
|
return {
|
||||||
|
"success_count": 0,
|
||||||
|
"fail_count": len(resource_ids) * len(advertiser_ids),
|
||||||
|
"total_count": len(resource_ids) * len(advertiser_ids),
|
||||||
|
"results": [{
|
||||||
|
"resource_id": rid,
|
||||||
|
"advertiser_id": aid,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": "授权token不存在"
|
||||||
|
} for rid in resource_ids for aid in advertiser_ids],
|
||||||
|
"error": "授权token不存在"
|
||||||
|
}
|
||||||
|
|
||||||
resources = await db.execute(
|
resources = await db.execute(
|
||||||
select(GeneratedResource).where(
|
select(GeneratedResource).where(
|
||||||
@@ -48,26 +75,57 @@ async def upload_material_to_platform(
|
|||||||
resources = resources.scalars().all()
|
resources = resources.scalars().all()
|
||||||
|
|
||||||
resource_dict = {r.id: r for r in resources}
|
resource_dict = {r.id: r for r in resources}
|
||||||
for resource_id in resource_ids:
|
|
||||||
if resource_id not in resource_dict:
|
|
||||||
raise ValueError(f"资源 {resource_id} 不存在或不属于当前用户")
|
|
||||||
|
|
||||||
all_upload_results = []
|
all_upload_results = []
|
||||||
success_count = 0
|
success_count = 0
|
||||||
fail_count = 0
|
fail_count = 0
|
||||||
|
|
||||||
for resource_id in resource_ids:
|
for resource_id in resource_ids:
|
||||||
resource = resource_dict[resource_id]
|
if resource_id not in resource_dict:
|
||||||
resource_type = resource.resource_type
|
for advertiser_id in advertiser_ids:
|
||||||
storage_path = resource.storage_path
|
all_upload_results.append({
|
||||||
|
"resource_id": resource_id,
|
||||||
|
"advertiser_id": advertiser_id,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": f"资源 {resource_id} 不存在或不属于当前用户"
|
||||||
|
})
|
||||||
|
fail_count += 1
|
||||||
|
|
||||||
if resource_type not in ["image", "video"]:
|
for advertiser_id in advertiser_ids:
|
||||||
raise ValueError(f"资源 {resource_id} 不支持的资源类型,仅支持image和video")
|
# 为每个广告主上传素材
|
||||||
|
for resource_id in resource_ids:
|
||||||
|
if resource_id not in resource_dict:
|
||||||
|
continue
|
||||||
|
|
||||||
if not storage_path:
|
resource = resource_dict[resource_id]
|
||||||
raise ValueError(f"资源 {resource_id} 本地存储路径为空")
|
resource_type = resource.resource_type
|
||||||
|
storage_path = resource.storage_path
|
||||||
|
|
||||||
|
if resource_type not in ["image", "video"]:
|
||||||
|
result = {
|
||||||
|
"resource_id": resource_id,
|
||||||
|
"advertiser_id": advertiser_id,
|
||||||
|
"filename": os.path.basename(storage_path) if storage_path else "",
|
||||||
|
"success": False,
|
||||||
|
"error": f"不支持的资源类型: {resource_type},仅支持image和video",
|
||||||
|
}
|
||||||
|
all_upload_results.append(result)
|
||||||
|
fail_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not storage_path:
|
||||||
|
result = {
|
||||||
|
"resource_id": resource_id,
|
||||||
|
"advertiser_id": advertiser_id,
|
||||||
|
"filename": "",
|
||||||
|
"success": False,
|
||||||
|
"error": "资源本地存储路径为空",
|
||||||
|
}
|
||||||
|
all_upload_results.append(result)
|
||||||
|
fail_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
for advertiser_id in advertiser_ids:
|
|
||||||
result = await _upload_to_juliang(
|
result = await _upload_to_juliang(
|
||||||
oauth_id, storage_path, resource_type, advertiser_id, resource, db, current_user_id
|
oauth_id, storage_path, resource_type, advertiser_id, resource, db, current_user_id
|
||||||
)
|
)
|
||||||
@@ -77,7 +135,17 @@ async def upload_material_to_platform(
|
|||||||
else:
|
else:
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
|
|
||||||
await db.commit()
|
# 上传完成后提交事务
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
# 调用前测接口(仅当有模板ID且有视频上传成功时)
|
||||||
|
if pre_test_template_id:
|
||||||
|
success_video_ids = [
|
||||||
|
r["upload_id"] for r in all_upload_results
|
||||||
|
if r.get("success") and r.get("upload_id") and r.get("advertiser_id") == advertiser_id
|
||||||
|
]
|
||||||
|
if success_video_ids:
|
||||||
|
await _pre_test_material(oauth_id, advertiser_id, success_video_ids, pre_test_template_id, db)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success_count": success_count,
|
"success_count": success_count,
|
||||||
@@ -87,6 +155,159 @@ async def upload_material_to_platform(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#调用抖音前测接口
|
||||||
|
async def _pre_test_material(
|
||||||
|
oauth_id: str,
|
||||||
|
advertiser_id: str,
|
||||||
|
video_ids: list[str],
|
||||||
|
pre_test_template_id: str,
|
||||||
|
db: AsyncSession,
|
||||||
|
) -> any:
|
||||||
|
#1.查询数据库是否存在前测模板
|
||||||
|
pre_test_template = await db.execute(
|
||||||
|
select(PreTestTemplate).where(
|
||||||
|
PreTestTemplate.id == pre_test_template_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pre_test_template = pre_test_template.scalar_one_or_none()
|
||||||
|
if not pre_test_template:
|
||||||
|
note = f"前测模板 {pre_test_template_id} 不存在"
|
||||||
|
for video_id in video_ids:
|
||||||
|
await _update_material_pre_test_status(
|
||||||
|
db, oauth_id, advertiser_id, video_id,
|
||||||
|
task_id=None,
|
||||||
|
status="FAILED",
|
||||||
|
note=note,
|
||||||
|
pre_result=None,
|
||||||
|
)
|
||||||
|
await db.commit()
|
||||||
|
return {"code": -1, "message": note, "data": {}}
|
||||||
|
|
||||||
|
diagnose_config = {}
|
||||||
|
if pre_test_template.platform:
|
||||||
|
diagnose_config["platform"] = pre_test_template.platform
|
||||||
|
if pre_test_template.external_action:
|
||||||
|
diagnose_config["external_action"] = pre_test_template.external_action
|
||||||
|
if pre_test_template.cpa_bid:
|
||||||
|
diagnose_config["cpa_bid"] = pre_test_template.cpa_bid
|
||||||
|
if pre_test_template.audience_gender:
|
||||||
|
diagnose_config["audience_gender"] = pre_test_template.audience_gender
|
||||||
|
if pre_test_template.audience_age:
|
||||||
|
diagnose_config["audience_age"] = json.loads(pre_test_template.audience_age)
|
||||||
|
if pre_test_template.audience_region:
|
||||||
|
diagnose_config["audience_region"] = json.loads(pre_test_template.audience_region)
|
||||||
|
if pre_test_template.audience_network:
|
||||||
|
diagnose_config["audience_network"] = json.loads(pre_test_template.audience_network)
|
||||||
|
if pre_test_template.cus_name:
|
||||||
|
diagnose_config["cus_name"] = pre_test_template.cus_name
|
||||||
|
if pre_test_template.pricing_type:
|
||||||
|
diagnose_config["pricing_type"] = pre_test_template.pricing_type
|
||||||
|
if pre_test_template.cost_cap:
|
||||||
|
diagnose_config["cost_cap"] = pre_test_template.cost_cap
|
||||||
|
if pre_test_template.target_cost:
|
||||||
|
diagnose_config["target_cost"] = pre_test_template.target_cost
|
||||||
|
if pre_test_template.nobid:
|
||||||
|
diagnose_config["nobid"] = pre_test_template.nobid
|
||||||
|
if pre_test_template.cpc_bid:
|
||||||
|
diagnose_config["cpc_bid"] = pre_test_template.cpc_bid
|
||||||
|
if pre_test_template.budget:
|
||||||
|
diagnose_config["budget"] = pre_test_template.budget
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"advertiser_id": int(advertiser_id),
|
||||||
|
"video_ids": video_ids,
|
||||||
|
"diagnose_config": diagnose_config,
|
||||||
|
}
|
||||||
|
response = await douyin_api.pre_test_material(oauth_id, params)
|
||||||
|
|
||||||
|
# 2. 解析接口返回并更新数据库
|
||||||
|
code = response.get("code", -1)
|
||||||
|
|
||||||
|
if code != 0:
|
||||||
|
# 整体失败:更新所有视频状态为FAILED
|
||||||
|
note = response.get("message", "未知错误")
|
||||||
|
for video_id in video_ids:
|
||||||
|
await _update_material_pre_test_status(
|
||||||
|
db, oauth_id, advertiser_id, video_id,
|
||||||
|
task_id=None,
|
||||||
|
status="FAILED",
|
||||||
|
note=note,
|
||||||
|
pre_result=None,
|
||||||
|
)
|
||||||
|
await db.commit()
|
||||||
|
return response
|
||||||
|
|
||||||
|
# 成功:解析 data 中的任务信息
|
||||||
|
data = response.get("data", {})
|
||||||
|
task_ids = data.get("task_ids", [])
|
||||||
|
fail_video_ids = data.get("fail_video_ids", {})
|
||||||
|
|
||||||
|
# 3. 更新成功的视频状态为PENDING(等待审核)
|
||||||
|
success_count = 0
|
||||||
|
for i, video_id in enumerate(video_ids):
|
||||||
|
if video_id in fail_video_ids:
|
||||||
|
# 失败的视频
|
||||||
|
fail_info = fail_video_ids[video_id]
|
||||||
|
err_code = fail_info.get("err_code", "")
|
||||||
|
err_message = fail_info.get("err_message", "未知错误")
|
||||||
|
note = f"失败[{err_code}]: {err_message}"
|
||||||
|
|
||||||
|
await _update_material_pre_test_status(
|
||||||
|
db, oauth_id, advertiser_id, video_id,
|
||||||
|
task_id=None,
|
||||||
|
status="FAILED",
|
||||||
|
note=note,
|
||||||
|
pre_result=None,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# 成功的视频
|
||||||
|
task_id = str(task_ids[success_count]) if success_count < len(task_ids) else None
|
||||||
|
|
||||||
|
await _update_material_pre_test_status(
|
||||||
|
db, oauth_id, advertiser_id, video_id,
|
||||||
|
task_id=task_id,
|
||||||
|
status="PENDING",
|
||||||
|
note="",
|
||||||
|
pre_result=None,
|
||||||
|
)
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
# 提交事务,保存所有修改
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
async def _update_material_pre_test_status(
|
||||||
|
db: AsyncSession,
|
||||||
|
oauth_id: str,
|
||||||
|
advertiser_id: str,
|
||||||
|
upload_id: str,
|
||||||
|
task_id: str | None,
|
||||||
|
status: str,
|
||||||
|
note: str | None,
|
||||||
|
pre_result: str | None,
|
||||||
|
):
|
||||||
|
"""更新素材的前测状态"""
|
||||||
|
result = await db.execute(
|
||||||
|
select(ResourcesMaterial).where(
|
||||||
|
ResourcesMaterial.oauth_id == oauth_id,
|
||||||
|
ResourcesMaterial.advertiser_id == advertiser_id,
|
||||||
|
ResourcesMaterial.upload_id == upload_id,
|
||||||
|
ResourcesMaterial.deleted_at.is_(None),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
material = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if material:
|
||||||
|
material.task_id = task_id
|
||||||
|
material.status = status
|
||||||
|
material.note = note
|
||||||
|
material.pre_result = pre_result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def _upload_to_juliang(
|
async def _upload_to_juliang(
|
||||||
oauth_id: str,
|
oauth_id: str,
|
||||||
storage_path: str,
|
storage_path: str,
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
import asyncio
|
||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.models.user_oauth import UserOAuth
|
||||||
|
from app.models.user_oauth_app import UserOAuthApp
|
||||||
|
from app.models.base import async_session
|
||||||
|
from app.config import settings
|
||||||
|
from app.utils.redis import get_redis
|
||||||
|
|
||||||
|
REDIS_KEY = "douyin:tokens"
|
||||||
|
|
||||||
|
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "logs")
|
||||||
|
|
||||||
|
os.makedirs(LOG_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
logger = logging.getLogger("token_refresh")
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
def get_log_filename():
|
||||||
|
return os.path.join(LOG_DIR, f"token_refresh-{datetime.now().strftime('%Y-%m-%d')}.log")
|
||||||
|
|
||||||
|
class DailyRotatingFileHandler(logging.FileHandler):
|
||||||
|
def __init__(self, directory, encoding=None):
|
||||||
|
self.directory = directory
|
||||||
|
filename = get_log_filename()
|
||||||
|
super().__init__(filename, encoding=encoding)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
current_filename = get_log_filename()
|
||||||
|
if self.baseFilename != current_filename:
|
||||||
|
self.close()
|
||||||
|
self.baseFilename = current_filename
|
||||||
|
self.stream = self._open()
|
||||||
|
super().emit(record)
|
||||||
|
|
||||||
|
handler = DailyRotatingFileHandler(LOG_DIR, encoding="utf-8")
|
||||||
|
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S"))
|
||||||
|
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
REFRESH_THRESHOLD_SECONDS = 800
|
||||||
|
CHECK_INTERVAL_MINUTES = 5
|
||||||
|
|
||||||
|
|
||||||
|
async def _update_redis_token(oauth_id: str, token: str, expired_at: datetime):
|
||||||
|
"""更新Redis缓存中的token"""
|
||||||
|
redis = get_redis()
|
||||||
|
if not redis:
|
||||||
|
logger.warning("Redis连接未配置,跳过缓存更新")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
cache = {
|
||||||
|
"token": token,
|
||||||
|
"expired_at": expired_at.isoformat(),
|
||||||
|
}
|
||||||
|
await redis.hset(REDIS_KEY, oauth_id, json.dumps(cache))
|
||||||
|
logger.info(f"Redis缓存已更新: oauth_id={oauth_id}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新Redis缓存失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
async def refresh_juliang_token(oauth: UserOAuth, app: UserOAuthApp, db: AsyncSession):
|
||||||
|
"""刷新巨量引擎token"""
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
url = "https://api.oceanengine.com/open_api/oauth2/refresh_token/"
|
||||||
|
response = await client.post(
|
||||||
|
url,
|
||||||
|
json={
|
||||||
|
"app_id": app.app_id,
|
||||||
|
"secret": app.secret,
|
||||||
|
"refresh_token": oauth.refresh_token,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
if data.get("code") != 0:
|
||||||
|
logger.error(f"刷新巨量引擎token失败: oauth_id={oauth.id}, 错误信息: {data}")
|
||||||
|
#如果code=40103或者40107,传入refresh_token已失效,失效原因一般是由于refresh_token已被使用,或授权账号重新授权并生成了新的Token
|
||||||
|
if data.get("code") in [40103, 40107]:
|
||||||
|
#清空数据库中的token信息,和Redis缓存中的token
|
||||||
|
oauth.access_token = None
|
||||||
|
oauth.access_token_expired = None
|
||||||
|
oauth.refresh_token = None
|
||||||
|
oauth.refresh_token_expired = None
|
||||||
|
await db.commit()
|
||||||
|
await _update_redis_token(oauth.id, "", None)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
data = data.get("data", {})
|
||||||
|
new_access_token = data.get("access_token", "")
|
||||||
|
new_refresh_token = data.get("refresh_token", "")
|
||||||
|
expires_in = datetime.now(tz=oauth.access_token_expired.tzinfo) + timedelta(seconds=data.get("expires_in", 0))
|
||||||
|
refresh_token_expires_in = datetime.now(tz=oauth.refresh_token_expired.tzinfo) + timedelta(seconds=data.get("refresh_token_expires_in", 0))
|
||||||
|
|
||||||
|
oauth.access_token = new_access_token
|
||||||
|
oauth.access_token_expired = expires_in
|
||||||
|
oauth.refresh_token = new_refresh_token
|
||||||
|
oauth.refresh_token_expires_in = refresh_token_expires_in
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
await _update_redis_token(oauth.id, new_access_token, expires_in)
|
||||||
|
|
||||||
|
logger.info(f"成功刷新巨量引擎token: oauth_id={oauth.id}, account_id={oauth.account_id}")
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"HTTP请求失败: oauth_id={oauth.id}, 错误: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"刷新巨量引擎token发生异常: oauth_id={oauth.id}, 错误: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
async def check_and_refresh_tokens():
|
||||||
|
"""检查并刷新即将过期的token"""
|
||||||
|
async with async_session() as db:
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
query = select(UserOAuth).where(
|
||||||
|
UserOAuth.deleted_at.is_(None),
|
||||||
|
UserOAuth.access_token.is_not(None),
|
||||||
|
UserOAuth.access_token_expired.is_not(None),
|
||||||
|
UserOAuth.refresh_token.is_not(None),
|
||||||
|
UserOAuth.refresh_token_expired.is_not(None),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await db.execute(query)
|
||||||
|
oauth_list = result.scalars().all()
|
||||||
|
|
||||||
|
for oauth in oauth_list:
|
||||||
|
try:
|
||||||
|
#1.检查access_token是否过期,如果未过期,并且大于800秒,直接跳过不处理
|
||||||
|
if not oauth.access_token_expired:
|
||||||
|
continue
|
||||||
|
|
||||||
|
remaining_seconds = (oauth.access_token_expired - now).total_seconds()
|
||||||
|
|
||||||
|
# access_token剩余时间大于等于800秒,不需要刷新
|
||||||
|
if remaining_seconds >= REFRESH_THRESHOLD_SECONDS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
#2.如果access_token过期,或者剩余时间小于800秒,需要刷新token
|
||||||
|
#3.如果需要刷新token,检查refresh_token是否过期,如果refresh_token过期,说明不可刷新,需要直接重新授权,直接跳过不处理
|
||||||
|
if not oauth.refresh_token_expired:
|
||||||
|
continue
|
||||||
|
|
||||||
|
refresh_remaining_seconds = (oauth.refresh_token_expired - now).total_seconds()
|
||||||
|
if refresh_remaining_seconds <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
#5.获取应用配置
|
||||||
|
app_result = await db.execute(
|
||||||
|
select(UserOAuthApp).where(UserOAuthApp.app_id == oauth.appid)
|
||||||
|
)
|
||||||
|
app = app_result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not app:
|
||||||
|
continue
|
||||||
|
|
||||||
|
#检查是否为支持的平台(巨量引擎)
|
||||||
|
# port_type: 平台端口(1=巨量,2=磁力,3=巨量星图,4=服务单,5=腾讯)
|
||||||
|
if oauth.port_type in [1]:
|
||||||
|
#刷新token
|
||||||
|
await refresh_juliang_token(oauth, app, db)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
#7.增加错误日志
|
||||||
|
logger.error(f"刷新token失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
async def token_refresh_scheduler():
|
||||||
|
"""定时任务调度器"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await check_and_refresh_tokens()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"定时任务token_refresh_scheduler执行失败: {str(e)}")
|
||||||
|
|
||||||
|
await asyncio.sleep(CHECK_INTERVAL_MINUTES * 60)
|
||||||
|
|
||||||
|
|
||||||
|
def start_token_refresh_task():
|
||||||
|
"""启动token刷新定时任务"""
|
||||||
|
logger.info("启动token刷新定时任务")
|
||||||
|
asyncio.create_task(token_refresh_scheduler())
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
from asyncio import Condition
|
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
@@ -7,9 +5,13 @@ from sqlalchemy import select
|
|||||||
|
|
||||||
from app.models.base import async_session
|
from app.models.base import async_session
|
||||||
from app.models.user_oauth import UserOAuth
|
from app.models.user_oauth import UserOAuth
|
||||||
|
from app.models.user_oauth_account import UserOAuthAccount
|
||||||
from app.models.user_oauth_app import UserOAuthApp
|
from app.models.user_oauth_app import UserOAuthApp
|
||||||
from app.tasks.async_runner import run_async
|
from app.tasks.async_runner import run_async
|
||||||
from app.tasks.celery_app import celery_app
|
from app.tasks.celery_app import celery_app
|
||||||
|
from app.utils.douyinApi import DouyinApi
|
||||||
|
from app.utils.douyinRequest import DouyinRequest
|
||||||
|
from app.utils.id_gen import generate_id
|
||||||
|
|
||||||
|
|
||||||
async def _update_oauth_accounts(account_id: str, account_userid: str, current_user_id: str, db: async_session):
|
async def _update_oauth_accounts(account_id: str, account_userid: str, current_user_id: str, db: async_session):
|
||||||
@@ -27,18 +29,61 @@ async def _update_oauth_accounts(account_id: str, account_userid: str, current_u
|
|||||||
if not oauth_records:
|
if not oauth_records:
|
||||||
return True
|
return True
|
||||||
for oauth in oauth_records:
|
for oauth in oauth_records:
|
||||||
pass
|
if oauth.port_type == 1:
|
||||||
|
return await update_juliang(oauth, db)
|
||||||
|
|
||||||
if celery_app:
|
async def update_juliang(oauth: UserOAuth, db: async_session):
|
||||||
@celery_app.task(name="user_oauth.update_oauth_accounts", bind=True, max_retries=3, default_retry_delay=60)
|
if oauth.account_role == 'AGENT':
|
||||||
def update_oauth_accounts(self, account_id: str, account_userid: str, current_user_id: str, db):
|
#通过代理商获取账户列表
|
||||||
return run_async(_update_oauth_accounts(account_id, account_userid, current_user_id, db))
|
cursor : int = 0
|
||||||
else:
|
count : int = 10
|
||||||
class _DisabledTask:
|
while True:
|
||||||
def delay(self, *args, **kwargs):
|
params = {
|
||||||
pass
|
'advertiser_id': oauth.account_id,
|
||||||
|
'count': count,
|
||||||
|
}
|
||||||
|
if cursor:
|
||||||
|
params['cursor'] = cursor
|
||||||
|
response = await DouyinApi().get_advertiser_by_agent(oauth.id, params)
|
||||||
|
if response.get('code', 0) != 0:
|
||||||
|
#记录错误日志
|
||||||
|
break
|
||||||
|
|
||||||
def apply_async(self, *args, **kwargs):
|
data = response['data']['list'] or []
|
||||||
pass
|
if not data:
|
||||||
|
break
|
||||||
|
account_source = response['data']['account_source'] or ''
|
||||||
|
|
||||||
update_oauth_accounts = _DisabledTask()
|
account_list = []
|
||||||
|
for item in data:
|
||||||
|
account_list.append(UserOAuthAccount(
|
||||||
|
id=generate_id(),
|
||||||
|
oauth_id=oauth.id,
|
||||||
|
advertiser_id=str(item),
|
||||||
|
advertiser_name="",
|
||||||
|
advertiser_role=account_source,
|
||||||
|
))
|
||||||
|
|
||||||
|
if account_list:
|
||||||
|
db.add_all(account_list)
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
cursor = response['data'].get('cursor_page_info', {}).get('cursor')
|
||||||
|
has_more = response['data'].get('cursor_page_info', {}).get('has_more', False)
|
||||||
|
if not has_more:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
# if celery_app:
|
||||||
|
# @celery_app.task(name="user_oauth.update_oauth_accounts", bind=True, max_retries=3, default_retry_delay=60)
|
||||||
|
# def update_oauth_accounts(self, account_id: str, account_userid: str, current_user_id: str, db):
|
||||||
|
# return run_async(_update_oauth_accounts(account_id, account_userid, current_user_id, db))
|
||||||
|
# else:
|
||||||
|
# class _DisabledTask:
|
||||||
|
# def delay(self, *args, **kwargs):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# def apply_async(self, *args, **kwargs):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# update_oauth_accounts = _DisabledTask()
|
||||||
@@ -37,6 +37,7 @@ class DouyinApi:
|
|||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#上传视频素材
|
||||||
async def upload_video_material(self, oauth_id: str, data: Optional[Dict[str, Any]] = None, files: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
async def upload_video_material(self, oauth_id: str, data: Optional[Dict[str, Any]] = None, files: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||||
if not oauth_id:
|
if not oauth_id:
|
||||||
raise RuntimeError('OAuth ID is not set.')
|
raise RuntimeError('OAuth ID is not set.')
|
||||||
@@ -66,3 +67,15 @@ class DouyinApi:
|
|||||||
'GET',
|
'GET',
|
||||||
{'params': params or {}}
|
{'params': params or {}}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#前测素材,Adv创建前测任务
|
||||||
|
async def pre_test_material(self, oauth_id: str, params: any) -> Dict[str, Any]:
|
||||||
|
if not oauth_id:
|
||||||
|
raise RuntimeError('OAuth ID is not set.')
|
||||||
|
url = "https://api.oceanengine.com/open_api/2/diagnosis_task/adv/create/"
|
||||||
|
return await self.request.request_with_token_with_context(
|
||||||
|
oauth_id,
|
||||||
|
url,
|
||||||
|
'POST',
|
||||||
|
{"json": params or {}}
|
||||||
|
)
|
||||||
@@ -186,7 +186,7 @@ class DouyinRequest:
|
|||||||
oauth_id: str,
|
oauth_id: str,
|
||||||
url: str,
|
url: str,
|
||||||
method: str = 'GET',
|
method: str = 'GET',
|
||||||
options: Optional[Dict[str, Any]] = None,
|
options: any = None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
options = options or {}
|
options = options or {}
|
||||||
token = await self.get_access_token(oauth_id)
|
token = await self.get_access_token(oauth_id)
|
||||||
@@ -229,7 +229,7 @@ class DouyinRequest:
|
|||||||
await asyncio.sleep(wait_time)
|
await asyncio.sleep(wait_time)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if code >= 50000:
|
if code == 50000:
|
||||||
await asyncio.sleep(i * 10)
|
await asyncio.sleep(i * 10)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user