4b8ec7c539
原 poller 把所有 weapp 卡片写死成 source="meituan",分享京东也会让手机弹/开 美团。现按 weapp 结构判源: - poller 新增 _resolve_source(): 按 weapp username/appid/title 关键词判平台 (jingdong/京东 → jd, meituan/美团 → meituan);认不出默认 meituan + 打 WARNING 并日志全量 weapp 结构,便于照真实 username/appid 精确补规则。 - app-server _SOURCE_PACKAGES 加 jd → com.jingdong.app.mall。 客户端无需改动(只认 source_package 反查平台名 + launch),纯服务端可热部署。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
"""设备注册 / 心跳 endpoint(无障碍保护存活检测)。
|
|
|
|
路由前缀 /api/v1/device,需 Bearer 鉴权(设备绑登录用户)。
|
|
POST /register 注册设备 / 更新 registration_id(App 前台、拿到 push token 时调)
|
|
POST /heartbeat 上报心跳(无障碍服务存活时周期调,刷新存活)
|
|
|
|
后端 heartbeat_monitor_worker 据此发现心跳超时的设备并极光推送告警。
|
|
见 spec: spec/accessibility-liveness-push.md。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter
|
|
|
|
from app.api.deps import CurrentUser, DbSession
|
|
from app.core import wx_poc_signal
|
|
from app.repositories import device as device_repo
|
|
from app.schemas.device import (
|
|
DeviceOut,
|
|
DeviceRegisterRequest,
|
|
HeartbeatRequest,
|
|
HeartbeatResponse,
|
|
LivenessAckRequest,
|
|
LivenessOut,
|
|
OkResponse,
|
|
PendingCompare,
|
|
)
|
|
|
|
logger = logging.getLogger("shagua.device")
|
|
|
|
# PoC:源平台代号 → Android 包名(前端 launch 用)。poller 按分享的小程序卡区分平台。
|
|
# 客户端只认 source_package:用它反查平台名(弹窗"原平台")+ launch 对应 App,故新增平台只需在此登记包名。
|
|
_SOURCE_PACKAGES = {
|
|
"meituan": "com.sankuai.meituan",
|
|
"jd": "com.jingdong.app.mall", # 京东主 App(含京东秒送/外卖),后端 intent skill=jd_waimai
|
|
}
|
|
|
|
router = APIRouter(prefix="/api/v1/device", tags=["device"])
|
|
|
|
|
|
@router.post("/register", response_model=DeviceOut, summary="注册设备/更新推送token")
|
|
def register_device(
|
|
req: DeviceRegisterRequest,
|
|
user: CurrentUser,
|
|
db: DbSession,
|
|
) -> DeviceOut:
|
|
device = device_repo.register_or_update(
|
|
db,
|
|
user_id=user.id,
|
|
device_id=req.device_id,
|
|
registration_id=req.registration_id,
|
|
platform=req.platform,
|
|
app_version=req.app_version,
|
|
)
|
|
logger.info(
|
|
"device register user_id=%d device_id=%s reg=%s",
|
|
user.id,
|
|
req.device_id,
|
|
bool(req.registration_id),
|
|
)
|
|
return DeviceOut.model_validate(device)
|
|
|
|
|
|
@router.post("/heartbeat", response_model=HeartbeatResponse, summary="上报心跳")
|
|
def report_heartbeat(
|
|
req: HeartbeatRequest,
|
|
user: CurrentUser,
|
|
db: DbSession,
|
|
) -> HeartbeatResponse:
|
|
device_repo.touch_heartbeat(
|
|
db,
|
|
user_id=user.id,
|
|
device_id=req.device_id,
|
|
accessibility_enabled=req.accessibility_enabled,
|
|
registration_id=req.registration_id,
|
|
)
|
|
# PoC:该设备有"待比价"信号则带回(只对写死的测试设备生效, 取走即清、只弹一次)
|
|
source = wx_poc_signal.pop_pending(req.device_id)
|
|
pending = None
|
|
if source:
|
|
pending = PendingCompare(
|
|
source_platform=source,
|
|
source_package=_SOURCE_PACKAGES.get(source, ""),
|
|
)
|
|
logger.info("heartbeat 下发比价信号 device=%s source=%s", req.device_id, source)
|
|
return HeartbeatResponse(pending_compare=pending)
|
|
|
|
|
|
@router.get("/liveness", response_model=LivenessOut, summary="查询本机掉线告警(后置检测)")
|
|
def get_liveness(
|
|
device_id: str,
|
|
user: CurrentUser,
|
|
db: DbSession,
|
|
) -> LivenessOut:
|
|
"""客户端进 App 时拉取: 本机是否被判定掉线过(kill_alert_pending)。
|
|
|
|
设备从未注册过 → 返回默认(无告警)。命中 → 客户端弹「开启自启动」引导, 之后调 /liveness/ack 清。
|
|
见 spec: spec/accessibility-liveness-pull-prompt.md。
|
|
"""
|
|
device = device_repo.get_device(db, user_id=user.id, device_id=device_id)
|
|
if device is None:
|
|
return LivenessOut()
|
|
return LivenessOut.model_validate(device)
|
|
|
|
|
|
@router.post("/liveness/ack", response_model=OkResponse, summary="确认已提醒(清掉线告警)")
|
|
def ack_liveness(
|
|
req: LivenessAckRequest,
|
|
user: CurrentUser,
|
|
db: DbSession,
|
|
) -> OkResponse:
|
|
"""客户端已弹过「开启自启动」引导 → 清掉「待提醒」标记(下次真掉线 worker 再置)。"""
|
|
device_repo.ack_kill_alert(db, user_id=user.id, device_id=req.device_id)
|
|
return OkResponse()
|