feat(auth): 测试号下发 force_onboarding,驱动应用内重登也重走引导

新增 TokenWithUser.force_onboarding(仅测试号置 true)。配合客户端:让测试号在
「退出后应用内重新登录」也重走新手引导;普通号 force_onboarding=false、按原逻辑回原页。
与 onboarding_completed 分工:后者管 App 启动路由,本字段管应用内登录后的去向。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zzhyyyyy
2026-06-23 16:07:23 +08:00
parent cc37905509
commit 7250682dc5
3 changed files with 20 additions and 4 deletions
+9 -4
View File
@@ -39,13 +39,17 @@ logger = logging.getLogger("shagua.auth")
router = APIRouter(prefix="/api/v1/auth", tags=["auth"])
def _login_response(user, *, onboarding_completed: bool) -> TokenWithUser:
"""登录类接口共用的响应组装。onboarding_completed 据登录请求的 device_id 算好后传入。"""
def _login_response(
user, *, onboarding_completed: bool, force_onboarding: bool = False
) -> TokenWithUser:
"""登录类接口共用的响应组装。onboarding_completed 据登录请求的 device_id 算好后传入;
force_onboarding 仅测试号置 True(让应用内重新登录也重走引导)。"""
tokens = issue_token_pair(user.id)
return TokenWithUser(
**tokens,
user=UserOut.model_validate(user),
onboarding_completed=onboarding_completed,
force_onboarding=force_onboarding,
)
@@ -115,8 +119,9 @@ def sms_login(req: SmsLoginRequest, db: DbSession) -> TokenWithUser:
if user.status != "active":
raise HTTPException(status_code=403, detail="account disabled")
logger.info("sms_login test_account ok user_id=%d phone=%s", user.id, mask_phone(req.phone))
# onboarding 恒 false:每次登录都重走引导,不读/不写 onboarding_completion 表。
return _login_response(user, onboarding_completed=False)
# onboarding 恒 false + force_onboarding=true:每次登录都重走引导(含应用内退出后重登),
# 不读/不写 onboarding_completion 表。
return _login_response(user, onboarding_completed=False, force_onboarding=True)
if not verify_code(req.phone, req.code):
raise HTTPException(status_code=400, detail="invalid sms code")
+6
View File
@@ -48,6 +48,12 @@ class TokenWithUser(TokenPair):
description="该 设备+账号 是否已走完新手引导(据登录请求里的 device_id 计算)。"
"true → 客户端登录后直接进首页,跳过引导。",
)
force_onboarding: bool = Field(
False,
description="是否强制本次登录走新手引导(仅测试号置 true)。客户端据此让【应用内重新登录】"
"(退出后再登)也重走引导,普通号为 false、按原逻辑回原页/首页。与 onboarding_completed "
"分工:后者管 App 启动路由,本字段管应用内登录后的去向。",
)
# ===== 极光一键登录 =====
+5
View File
@@ -13,6 +13,9 @@ import pytest
from app.core import test_account
from app.integrations import sms
# 测试自注入的固定号:各用例用 monkeypatch 把它塞进 settings.TEST_ACCOUNT_PHONE,
# 再断言行为。它与 .env 配的值【相互独立】——测试不读 .env(保证确定性 / CI 无 .env 也能跑),
# 写成和 .env 同值只为直观;换任意 11 位号测试照样全过,二者无需保持一致。
TEST_PHONE = "11111111111"
@@ -58,6 +61,7 @@ def test_login_skips_code_and_returns_tokens(client, enabled) -> None:
assert data["user"]["phone"] == TEST_PHONE
assert data["user"]["register_channel"] == "sms"
assert data["onboarding_completed"] is False
assert data["force_onboarding"] is True # 测试号:应用内重登也强制重走引导
assert "access_token" in data and "refresh_token" in data
@@ -158,3 +162,4 @@ def test_non_test_phone_unaffected_when_enabled(client, enabled) -> None:
r = client.post("/api/v1/auth/sms/login", json={"phone": phone, "code": "123456", "device_id": "d1"})
assert r.status_code == 200, r.text
assert r.json()["user"]["phone"] == phone
assert r.json()["force_onboarding"] is False # 普通号不强制重走,按原逻辑