"""admin 设备维度新手引导管理:列设备 / 重置单设备 / 全部重置。 背景:原 force_onboarding(运营「按用户」强制引导)客户端不认(被 onboarding_completed 压过), 已废弃。改为直接操作 onboarding_completion(设备+账号 维度):删记录 → 该(设备,账号)的 onboarding_completed 变 false → 客户端任何版本下次登录都重走引导。可靠、不依赖客户端实现。 """ from __future__ import annotations from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Request from app.admin.audit import write_audit from app.admin.deps import AdminDb, get_client_ip, get_current_admin, require_role from app.admin.repositories import mutations, queries from app.admin.schemas.common import CursorPage, OkResponse from app.admin.schemas.onboarding import DeviceOnboardingItem, ResetAllRequest from app.models.admin import AdminUser router = APIRouter( prefix="/admin/api/onboarding", tags=["admin-onboarding"], dependencies=[Depends(get_current_admin)], ) @router.get( "/devices", response_model=CursorPage[DeviceOnboardingItem], summary="设备列表(按设备聚合走过引导的账号)", ) def list_devices(db: AdminDb) -> CursorPage[DeviceOnboardingItem]: """列出走过新手引导的设备(ANDROID_ID),按最近完成时间倒序。没走过引导的设备不在表里、 本就会引导,无需管理,故不列。当前全量返回(上限 500;设备量大再加分页/搜索), next_cursor 恒 None。""" rows = queries.list_onboarding_devices(db) return CursorPage( items=[DeviceOnboardingItem(**r) for r in rows], next_cursor=None, ) @router.post( "/devices/{device_id}/reset", response_model=OkResponse, summary="重置单个设备(该设备所有账号下次重走引导)", ) def reset_device( device_id: str, request: Request, admin: Annotated[AdminUser, Depends(require_role("operator"))], db: AdminDb, ) -> OkResponse: """删该设备的全部 onboarding 完成记录 → 该设备上所有账号下次登录重走引导。""" # 业务写 + 审计写同一事务(commit=False),最后一起 commit(同 users 各写接口) deleted = mutations.delete_device_onboarding(db, device_id, commit=False) write_audit( db, admin, action="device_onboarding.reset", target_type="device", target_id=device_id, detail={"deleted_rows": deleted}, ip=get_client_ip(request), commit=False, ) db.commit() return OkResponse() @router.post( "/reset-all", response_model=OkResponse, summary="全部重设(清空所有设备引导记录)", ) def reset_all( body: ResetAllRequest, request: Request, admin: Annotated[AdminUser, Depends(require_role("operator"))], db: AdminDb, ) -> OkResponse: """清空 onboarding_completion 表 → 所有用户在所有设备下次登录都重走引导。 需 confirm=true 防误触(前端另有二次确认弹窗)。""" if not body.confirm: raise HTTPException(status_code=400, detail="需 confirm=true 确认") deleted = mutations.delete_all_onboarding(db, commit=False) write_audit( db, admin, action="device_onboarding.reset_all", target_type="onboarding", target_id=None, detail={"deleted_rows": deleted}, ip=get_client_ip(request), commit=False, ) db.commit() return OkResponse()