75 lines
2.4 KiB
Python
75 lines
2.4 KiB
Python
"""用户操作日志上报:仅特定账号(如 256kb@gmail.com)开启,用于排查 bug"""
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database import get_db
|
|
from app.deps import Identity, require_user
|
|
from app.core import errors
|
|
from app.core.response import BizException, ok
|
|
from app.models.user import User
|
|
from app.models.user_action_log import UserActionLog
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
# 仅这些邮箱的用户允许上报操作日志(白名单,避免给所有用户上传)
|
|
ALLOWED_EMAILS = {"256kb@gmail.com"}
|
|
|
|
# 单条 detail 最大长度(防御性截断)
|
|
MAX_DETAIL_LEN = 800
|
|
# 单次批量最大条数
|
|
MAX_BATCH = 100
|
|
|
|
|
|
class ActionLogItem(BaseModel):
|
|
action: str = Field(..., max_length=50)
|
|
page: str = Field("", max_length=100)
|
|
detail: str = Field("", max_length=MAX_DETAIL_LEN)
|
|
device_info: str = Field("", max_length=200)
|
|
app_version: str = Field("", max_length=20)
|
|
# 客户端本地时间戳(毫秒),用于在服务端时钟漂移时仍能排序
|
|
client_ts: int = 0
|
|
|
|
|
|
class ActionLogBatch(BaseModel):
|
|
items: list[ActionLogItem] = Field(..., max_length=MAX_BATCH)
|
|
|
|
|
|
@router.post("/logs/action")
|
|
async def report_action_log(
|
|
body: ActionLogBatch,
|
|
request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
identity: Identity = Depends(require_user),
|
|
):
|
|
"""接收 App 上报的用户操作日志。仅白名单邮箱可上报,其他账号返回 403 不上报"""
|
|
user = (await db.execute(
|
|
select(User).where(User.id == identity.user_id)
|
|
)).scalar_one_or_none()
|
|
if not user or user.email not in ALLOWED_EMAILS:
|
|
# 静默拒绝:不报错,不写入,避免在普通用户端暴露此通道存在
|
|
raise BizException(errors.FORBIDDEN, "forbidden")
|
|
|
|
rows = [
|
|
UserActionLog(
|
|
user_id=user.id,
|
|
action=item.action[:50],
|
|
page=item.page[:100],
|
|
detail=item.detail[:MAX_DETAIL_LEN],
|
|
device_info=item.device_info[:200],
|
|
app_version=item.app_version[:20],
|
|
)
|
|
for item in body.items
|
|
]
|
|
db.add_all(rows)
|
|
await db.commit()
|
|
logger.info(
|
|
"user_action_log user=%s count=%s ip=%s",
|
|
user.id, len(rows), request.client.host if request.client else "-",
|
|
)
|
|
return ok({"received": len(rows)})
|