from typing import Optional, List, Tuple from datetime import datetime from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from app.models.user import User from app.models.user_oauth import UserOAuth from app.models.user_oauth_account import UserOAuthAccount from app.models.resources_material import ResourcesMaterial from app.models.pre_test_template import PreTestTemplate from app.models.upload_task import UploadTask from app.utils.datetime_util import BEIJING_TZ, datetime_to_db_tz_str, db_tz_str_to_datetime, parse_date_range async def get_oauth_list( id: Optional[str] = None, db: AsyncSession = None, phone: Optional[str] = None, account_id: Optional[str] = None, account_userid: Optional[str] = None, created_at: Optional[datetime] = None, appid: Optional[str] = None, open_type: Optional[int] = None, page: int = 1, page_size: int = 10, ) -> Tuple[List[UserOAuth], int]: offset = (page - 1) * page_size query = select(UserOAuth).where(UserOAuth.deleted_at.is_(None)) if id: query = query.where(UserOAuth.id == id) if phone: query = query.join(User, User.id == UserOAuth.user_id).where(User.phone == phone) if account_id: query = query.where(UserOAuth.account_id == account_id) if account_userid: query = query.where(UserOAuth.account_userid == account_userid) # 【改动3】适配入参 created_at=["2025-01-01","2026-01-01"] 字符串日期场景 if created_at and len(created_at) == 2: try: start_dt, end_dt = parse_date_range(created_at, is_timezone=True) # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 query = query.where( UserOAuth.created_at >= start_dt, UserOAuth.created_at <= end_dt ) except ValueError: # 捕获日期格式错误,非法时间直接不附加该查询条件 pass if appid: query = query.where(UserOAuth.appid == appid) if open_type: query = query.where(UserOAuth.open_type == open_type) count_query = select(func.count(UserOAuth.id)).where(UserOAuth.deleted_at.is_(None)) if phone: count_query = count_query.join(User, User.id == UserOAuth.user_id).where(User.phone == phone) if account_id: count_query = count_query.where(UserOAuth.account_id == account_id) if account_userid: count_query = count_query.where(UserOAuth.account_userid == account_userid) if created_at: try: start_dt, end_dt = parse_date_range(created_at, is_timezone=True) # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 count_query = count_query.where( UserOAuth.created_at >= start_dt, UserOAuth.created_at <= end_dt ) except ValueError: # 捕获日期格式错误,非法时间直接不附加该查询条件 pass if appid: count_query = count_query.where(UserOAuth.appid == appid) if open_type: count_query = count_query.where(UserOAuth.open_type == open_type) total_result = await db.execute(count_query) total = total_result.scalar_one() query = query.order_by(UserOAuth.id.desc()).offset(offset).limit(page_size) result = await db.execute(query) list = []; for oauth in result.scalars().all(): user_query = select(User).where(User.id == oauth.user_id) user_result = await db.execute(user_query) user = user_result.scalar_one() if user: phone = user.phone else: phone = None item = { "id": oauth.id, "account_id": oauth.account_id, "account_name": oauth.account_name, "account_role": oauth.account_role, "account_username": oauth.account_username, "account_userid": oauth.account_userid, "user_id": oauth.user_id, "user_phone": phone, "open_type": oauth.open_type, "port_type": oauth.port_type, "appid": oauth.appid, "access_token": oauth.access_token, "refresh_token": oauth.refresh_token, "access_token_expired": oauth.access_token_expired, "refresh_token_expired": oauth.refresh_token_expired, "created_at": oauth.created_at, "updated_at": oauth.updated_at, } list.append(item) return list, total async def get_material_list( db: AsyncSession, phone: Optional[str] = None, id: Optional[str] = None, resource_type: Optional[str] = None, advertiser_id: Optional[str] = None, material_id: Optional[str] = None, upload_id: Optional[str] = None, created_at: Optional[datetime] = None, status: Optional[str] = None, page: int = 1, page_size: int = 20, ) -> Tuple[List[ResourcesMaterial], int]: offset = (page - 1) * page_size query = select(ResourcesMaterial).where(ResourcesMaterial.deleted_at.is_(None)) if phone: query = query.join(UserOAuth, UserOAuth.id == ResourcesMaterial.oauth_id).join(User, User.id == UserOAuth.user_id).where(User.phone == phone) if id: query = query.where(ResourcesMaterial.id == id) if resource_type: query = query.where(ResourcesMaterial.resource_type == resource_type) if advertiser_id: query = query.where(ResourcesMaterial.advertiser_id == advertiser_id) if material_id: query = query.where(ResourcesMaterial.material_id == material_id) if upload_id: query = query.where(ResourcesMaterial.upload_id == upload_id) if created_at: start_dt, end_dt = parse_date_range(created_at, is_timezone=True) # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 query = query.where( ResourcesMaterial.created_at >= start_dt, ResourcesMaterial.created_at <= end_dt ) if status: query = query.where(ResourcesMaterial.status == status) count_query = select(func.count(ResourcesMaterial.id)).where(ResourcesMaterial.deleted_at.is_(None)) if phone: count_query = count_query.join(UserOAuth, UserOAuth.id == ResourcesMaterial.oauth_id).join(User, User.id == UserOAuth.user_id).where(User.phone == phone) if id: count_query = count_query.where(ResourcesMaterial.id == id) if resource_type: count_query = count_query.where(ResourcesMaterial.resource_type == resource_type) if advertiser_id: count_query = count_query.where(ResourcesMaterial.advertiser_id == advertiser_id) if material_id: count_query = count_query.where(ResourcesMaterial.material_id == material_id) if upload_id: count_query = count_query.where(ResourcesMaterial.upload_id == upload_id) if created_at: start_dt, end_dt = parse_date_range(created_at, is_timezone=True) # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 count_query = count_query.where( ResourcesMaterial.created_at >= start_dt, ResourcesMaterial.created_at <= end_dt ) if status: count_query = count_query.where(ResourcesMaterial.status == status) total_result = await db.execute(count_query) total = total_result.scalar_one() query = query.order_by(ResourcesMaterial.id.desc()).offset(offset).limit(page_size) result = await db.execute(query) list = [] for item in result.scalars().all(): user_query = select(User).where(User.id == item.user_id) user_result = await db.execute(user_query) user = user_result.scalar_one() if user: phone = user.phone else: phone = None item = { "id": item.id, "oauth_id": item.oauth_id, "user_phone": phone, "advertiser_id": item.advertiser_id, "target_table": item.target_table, "target_id": item.target_id, "material_id": item.material_id, "upload_id": item.upload_id, "resource_type": item.resource_type, "user_id": item.user_id, "created_at": item.created_at, "updated_at": item.updated_at, "task_id": item.task_id, "note": item.note, "status": item.status, "pre_result": item.pre_result, "pre_test_template_id": item.pre_test_template_id, } list.append(item) return list, total async def get_pre_test_template_list( db: AsyncSession, phone: Optional[str] = None, id: Optional[str] = None, created_at: Optional[datetime] = None, page: int = 1, page_size: int = 10, ) -> Tuple[List[PreTestTemplate], int]: offset = (page - 1) * page_size query = select(PreTestTemplate).where(PreTestTemplate.deleted_at.is_(None)) if phone: query = query.join(User, User.id == PreTestTemplate.user_id).where(User.phone == phone) if id: query = query.where(PreTestTemplate.id == id) if created_at: start_dt, end_dt = parse_date_range(created_at, is_timezone=True) # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 query = query.where( PreTestTemplate.created_at >= start_dt, PreTestTemplate.created_at <= end_dt ) count_query = select(func.count(PreTestTemplate.id)).where(PreTestTemplate.deleted_at.is_(None)) if phone: count_query = count_query.join(User, User.id == PreTestTemplate.user_id).where(User.phone == phone) if id: count_query = count_query.where(PreTestTemplate.id == id) if created_at: # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 start_dt, end_dt = parse_date_range(created_at, is_timezone=True) count_query = count_query.where( PreTestTemplate.created_at >= start_dt, PreTestTemplate.created_at <= end_dt ) total_result = await db.execute(count_query) total = total_result.scalar_one() query = query.order_by(PreTestTemplate.id.desc()).offset(offset).limit(page_size) result = await db.execute(query) list = [] for item in result.scalars().all(): user_query = select(User).where(User.id == item.user_id) user_result = await db.execute(user_query) user = user_result.scalar_one() if user: phone = user.phone else: phone = None item = { "id": item.id, "name": item.name, "user_phone": phone, "user_id": item.user_id, "platform": item.platform, "external_action": item.external_action, "cpa_bid": item.cpa_bid, "audience_gender": item.audience_gender, "audience_age": item.audience_age, "audience_region": item.audience_region, "audience_network": item.audience_network, "cus_name": item.cus_name, "pricing_type": item.pricing_type, "cost_cap": item.cost_cap, "target_cost": item.target_cost, "nobid": item.nobid, "cpc_bid": item.cpc_bid, "budget": item.budget, "is_default": item.is_default, "created_at": item.created_at, "updated_at": item.updated_at, "note": item.note, } list.append(item) return list, total async def get_upload_task_list( db: AsyncSession, phone: Optional[str] = None, id: Optional[str] = None, created_at: Optional[List[str]] = None, advertiser_id: Optional[str] = None, status: Optional[int] = None, resource_id: Optional[str] = None, page: int = 1, page_size: int = 10, ) -> Tuple[List[UploadTask], int]: offset = (page - 1) * page_size query = select(UploadTask).where(UploadTask.deleted_at.is_(None)) if phone: query = query.join(User, User.id == UploadTask.user_id).where(User.phone == phone) if id: query = query.where(UploadTask.id == id) if created_at and len(created_at) >= 2: # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 start_dt, end_dt = parse_date_range(created_at, is_timezone=True) query = query.where( UploadTask.created_at >= start_dt, UploadTask.created_at <= end_dt ) if advertiser_id: query = query.where(UploadTask.advertiser_id == advertiser_id) if status: query = query.where(UploadTask.status == status) if resource_id: query = query.where(UploadTask.resource_id == resource_id) count_query = select(func.count(UploadTask.id)).where(UploadTask.deleted_at.is_(None)) if phone: count_query = count_query.join(User, User.id == UploadTask.user_id).where(User.phone == phone) if id: count_query = count_query.where(UploadTask.id == id) if created_at and len(created_at) >= 2: # 直接用datetime范围匹配,不走func.date,保留时分秒精度,命中索引 start_dt, end_dt = parse_date_range(created_at, is_timezone=True) count_query = count_query.where( UploadTask.created_at >= start_dt, UploadTask.created_at <= end_dt ) if advertiser_id: count_query = count_query.where(UploadTask.advertiser_id == advertiser_id) if status: count_query = count_query.where(UploadTask.status == status) if resource_id: count_query = count_query.where(UploadTask.resource_id == resource_id) total_result = await db.execute(count_query) total = total_result.scalar_one() query = query.order_by(UploadTask.id.desc()).offset(offset).limit(page_size) result = await db.execute(query) list = [] for item in result.scalars().all(): user_query = select(User).where(User.id == item.user_id) user_result = await db.execute(user_query) user = user_result.scalar_one() if user: phone = user.phone else: phone = None item = { "id": item.id, "user_phone": phone, "user_id": item.user_id, "advertiser_id": item.advertiser_id, "resource_id": item.resource_id, "status": item.status, "note": item.note, "oauth_id": item.oauth_id, "created_at": item.created_at, "updated_at": item.updated_at, "other_info": item.other_info, } list.append(item) return list, total