fix: use v9 store status for deregistration login

This commit is contained in:
2026-08-31 16:36:20 +08:00
parent 4444d328df
commit b8343ad9eb
8 changed files with 194 additions and 11 deletions
+5
View File
@@ -48,6 +48,7 @@ final class AuthModelsTests: XCTestCase {
"scenic_name": "示例景区",
"store_id": 20,
"store_name": "示例门店",
"status": 2,
"role_name": "店长",
"is_current": false
}
@@ -63,6 +64,8 @@ final class AuthModelsTests: XCTestCase {
XCTAssertEqual(response.accounts.map(\.businessUserId), [101, 201])
XCTAssertEqual(response.accounts.map(\.subtitle), ["示例景区 · 摄影师", "示例景区 · 店长"])
XCTAssertEqual(response.accounts.map(\.realName), ["张三", "李四"])
XCTAssertEqual(response.storeUsers.first?.status, 2)
XCTAssertTrue(response.storeUsers.first?.toAccountSwitchAccount().requiresDeregistrationConfirmation == true)
}
func testV9AuthResponseDecodesMissingAccountListsAsEmpty() throws {
@@ -137,6 +140,8 @@ final class AuthModelsTests: XCTestCase {
XCTAssertEqual(user.toAccountSwitchAccount().subtitle, "示例景区 · 店长")
XCTAssertEqual(user.toAccountSwitchAccount().realName, "张三")
XCTAssertEqual(user.toAccountSwitchAccount().toSetUserRequest(), SetUserRequest(storeUserId: 201))
XCTAssertEqual(user.status, 1)
XCTAssertFalse(user.toAccountSwitchAccount().requiresDeregistrationConfirmation)
}
func testV9AccountModelsIgnoreBackendIdentifiersAndUseRealNameFallback() throws {
+38 -1
View File
@@ -48,6 +48,42 @@ final class LoginViewModelTests: XCTestCase {
XCTAssertEqual(session.requests.map { $0.url?.path }, ["/api/app/v9/login", "/api/app/v9/set-user"])
}
/// 单一门店身份处于注销冷静期时,必须先返回确认状态,不能自动调用 set-user 撤销申请。
func testSingleCoolingOffStoreAccountRequiresConfirmationBeforeSetUser() async throws {
let login = Data(#"{"code":100000,"data":{"token":"temporary","scenic_users":[],"store_users":[{"store_user_id":101,"store_name":"待注销门店","status":2}]}}"#.utf8)
let selected = Data(#"{"code":100000,"data":{"token":"business","scenic_users":[],"store_users":[]}}"#.utf8)
let session = MockURLSession(responses: [login, selected])
let viewModel = LoginViewModel()
viewModel.updateAccount("13900000001")
viewModel.updatePassword("test-password")
let api = AuthAPI(client: APIClient(environment: .testing, session: session))
let resolution = try await viewModel.login(authAPI: api)
guard case let .needsDeregistrationConfirmation(confirmation) = resolution else {
return XCTFail("冷静期门店身份应等待用户确认")
}
XCTAssertEqual(confirmation.account.status, 2)
XCTAssertEqual(session.requests.map { $0.url?.path }, ["/api/app/v9/login"])
let response = try await viewModel.confirmDeregistrationLogin(confirmation, authAPI: api)
XCTAssertEqual(response.token, "business")
XCTAssertEqual(session.requests.map { $0.url?.path }, ["/api/app/v9/login", "/api/app/v9/set-user"])
}
/// 冷静期确认弹窗需明确告知登录将撤销注销申请,并提供保留申请的取消入口。
func testDeregistrationLoginAlertExplainsAutomaticCancellation() {
let alert = makeDeregistrationLoginAlert(
account: reentryAccount(isStore: true, status: 2),
onConfirm: {}
)
XCTAssertEqual(alert.title, "该账号正在注销")
XCTAssertTrue(alert.message?.contains("自动撤销之前的注销申请") == true)
XCTAssertEqual(alert.actions.map(\.title), ["暂不登录", "确认登录"])
}
/// 普通门店身份选择直接回调,不再无条件弹出注销提示。
func testSelectingStoreIdentityContinuesWithoutDeregistrationPrompt() throws {
let account = reentryAccount(isStore: true)
@@ -157,7 +193,7 @@ final class LoginViewModelTests: XCTestCase {
XCTAssertNil(controller.presentedViewController)
}
private func reentryAccount(isStore: Bool) -> AccountSwitchAccount {
private func reentryAccount(isStore: Bool, status: Int = 1) -> AccountSwitchAccount {
AccountSwitchAccount(
accountType: isStore ? "store_user" : "scenic_user",
businessUserId: 101,
@@ -170,6 +206,7 @@ final class LoginViewModelTests: XCTestCase {
storeId: isStore ? 25 : nil,
storeName: isStore ? "测试门店" : "",
scenicId: 10,
status: status,
isCurrent: false
)
}
@@ -83,8 +83,24 @@ final class StoreAccountDeregistrationRootTests: XCTestCase {
XCTAssertEqual(context.resumeCount, 1)
}
/// 新登录得到身份凭证后仍先核验,普通页面前台恢复不追加查询。
func testLoginChecksStatusBeforeEnteringBusiness() async {
/// 新登录只以 v9 的门店 status 判定;旧注销状态接口和本地未决记录都不能再拦截 status 为正常的登录。
func testNormalLoginRestoresBusinessWithoutLegacyDeregistrationCheck() async {
let context = DeregistrationRootContext()
defer { context.cleanUp() }
context.service.readError = APIError.networkFailed("旧注销查询不应影响 v9 正常登录")
context.startRestoringSession()
XCTAssertNil(context.coordinator.accessController)
XCTAssertFalse(context.coordinator.isChecking)
XCTAssertFalse(GlobalLoadingManager.shared.isShowing)
XCTAssertFalse(context.bindingSuspended)
XCTAssertEqual(context.createdRoots, 1)
XCTAssertEqual(context.resumeCount, 1)
XCTAssertEqual(context.service.statusCount, 0)
}
/// 旧核验入口仅在业务限制等已知受限事件发生时使用,不是正常登录路径的一部分。
func testExplicitRestrictionCheckStillKeepsOriginalPageUntilResult() async {
let context = DeregistrationRootContext()
defer { context.cleanUp() }
context.service.holdNextRead = true