feat: 增加门店身份注销流程
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import Foundation
|
||||
@testable import suixinkan
|
||||
|
||||
/// 从已实测响应整理的脱敏测试夹具;覆盖变化场景时显式覆盖字段,不宣称为真实服务端返回。
|
||||
enum StoreAccountDeregistrationFixtures {
|
||||
/// 2026-08-28 15:29 实测 userinfo 的150015响应;无time字段,data.status为字符串。
|
||||
static let observedCoolingRestrictionEnvelope = Data(#"""
|
||||
{
|
||||
"code": 150015,
|
||||
"msg": "账号处于注销冷静期,请先撤销注销后再继续使用",
|
||||
"data": {
|
||||
"status": "cooling",
|
||||
"cooling_until": "2026-09-04 15:27:24",
|
||||
"remaining_seconds": 604681
|
||||
}
|
||||
}
|
||||
"""#.utf8)
|
||||
|
||||
/// 同次150015请求后的真实status响应,仅将注销记录ID替换为测试值301。
|
||||
static let observedCoolingStatusEnvelope = Data(#"""
|
||||
{
|
||||
"code": 100000,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"deregister": {
|
||||
"id": 301,
|
||||
"status": 1,
|
||||
"status_label": "冷静期中",
|
||||
"reason": "Test account deregistration",
|
||||
"apply_time": "2026-08-28 15:27:24",
|
||||
"cooling_until": "2026-09-04 15:27:24",
|
||||
"remaining_seconds": 604681,
|
||||
"cancel_time": null,
|
||||
"blocked_code": "",
|
||||
"blocked_reason": "",
|
||||
"completed_at": null
|
||||
}
|
||||
},
|
||||
"time": "2026-08-28 15:29:22"
|
||||
}
|
||||
"""#.utf8)
|
||||
|
||||
/// 真实样例的结构,身份 ID 替换为测试用的 101/201。
|
||||
static func eligibilityData(overrides: [String: Any] = [:]) throws -> Data {
|
||||
var data: [String: Any] = [
|
||||
"can_apply": false, "store_user_id": 101, "finance_identity_id": 201,
|
||||
"wallet_balance_fen": 0, "wallet_balance": "0.00", "points_balance": 0,
|
||||
"wallet_waived": false, "points_waived": false,
|
||||
"unfulfilled_count": 37, "fulfillment_in_progress_count": 0,
|
||||
"risk_end_at": "2026-08-27 15:47:32", "eligible_at": "2026-09-03 15:47:32", "risk_window_hours": 168,
|
||||
"blockers": [
|
||||
["code": "ORDER_UNFULFILLED", "message": "账户仍有未履约订单或带单", "action": "complete_orders"],
|
||||
["code": "RISK_WINDOW_NOT_EXPIRED", "message": "最后一笔业务的风险结束时间尚未超过7天", "action": "wait_risk_window"],
|
||||
["code": "WALLET_WAIVER_MISSING", "message": "请先确认放弃现金余额", "action": "confirm_wallet_waiver"],
|
||||
["code": "POINTS_WAIVER_MISSING", "message": "请先确认放弃积分", "action": "confirm_points_waiver"],
|
||||
],
|
||||
"deregister": NSNull(),
|
||||
]
|
||||
data.merge(overrides) { _, new in new }
|
||||
return try JSONSerialization.data(withJSONObject: data)
|
||||
}
|
||||
|
||||
/// 解码已实测结构,供协议替身返回值使用。
|
||||
static func eligibility(overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationEligibility {
|
||||
try JSONDecoder().decode(StoreAccountDeregistrationEligibility.self, from: eligibilityData(overrides: overrides))
|
||||
}
|
||||
|
||||
/// 构造所有条件满足的规则测试输入;不是通过真实资产放弃或申请获得的响应。
|
||||
static func ready(overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationEligibility {
|
||||
var fields: [String: Any] = ["can_apply": true, "wallet_waived": true, "points_waived": true,
|
||||
"unfulfilled_count": 0, "blockers": []]
|
||||
fields.merge(overrides) { _, new in new }
|
||||
return try eligibility(overrides: fields)
|
||||
}
|
||||
|
||||
/// 2026-08-28 零资产确认后的真实草稿结构,仅将记录 ID 替换为测试值 301。
|
||||
static func draftRecord(overrides: [String: Any] = [:]) -> [String: Any] {
|
||||
var record: [String: Any] = [
|
||||
"id": 301, "status": 0, "status_label": "待确认", "reason": "",
|
||||
"apply_time": NSNull(), "cooling_until": NSNull(), "remaining_seconds": 0,
|
||||
"cancel_time": NSNull(), "blocked_code": "", "blocked_reason": "", "completed_at": NSNull(),
|
||||
]
|
||||
record.merge(overrides) { _, new in new }
|
||||
return record
|
||||
}
|
||||
|
||||
/// 解码实测草稿状态;覆盖字段时用于验证未知或矛盾记录不会放行。
|
||||
static func draftStatus(overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationStatus {
|
||||
try JSONDecoder().decode(StoreAccountDeregistrationStatus.self,
|
||||
from: JSONSerialization.data(withJSONObject: ["deregister": draftRecord(overrides: overrides)]))
|
||||
}
|
||||
|
||||
/// 两次真实确认之间及之后的条件结构,没有历史业务时风险时间为 null。
|
||||
static func draftEligibility(pointsWaived: Bool) throws -> StoreAccountDeregistrationEligibility {
|
||||
try eligibility(overrides: [
|
||||
"can_apply": pointsWaived, "wallet_waived": true, "points_waived": pointsWaived,
|
||||
"unfulfilled_count": 0, "risk_end_at": NSNull(), "eligible_at": NSNull(),
|
||||
"blockers": pointsWaived ? [] : [
|
||||
["code": "POINTS_WAIVER_MISSING", "message": "请先确认放弃积分", "action": "confirm_points_waiver"],
|
||||
],
|
||||
"deregister": draftRecord(),
|
||||
])
|
||||
}
|
||||
|
||||
/// 实测的冷静期和主动撤销记录,ID 脱敏;覆盖字段只用于防御性测试。
|
||||
static func lifecycleRecord(cancelled: Bool = false, overrides: [String: Any] = [:]) -> [String: Any] {
|
||||
var record = draftRecord(overrides: [
|
||||
"status": cancelled ? 9 : 1, "status_label": cancelled ? "已撤销" : "冷静期中",
|
||||
"reason": "API test; cancel immediately", "apply_time": "2026-08-28 14:23:40",
|
||||
"cooling_until": "2026-09-04 14:23:40", "remaining_seconds": cancelled ? 0 : 604776,
|
||||
"cancel_time": cancelled ? "2026-08-28 14:24:04" : NSNull(),
|
||||
"blocked_code": cancelled ? "CANCELLED_BY_USER" : "",
|
||||
"blocked_reason": cancelled ? "用户主动撤销注销" : "",
|
||||
])
|
||||
record.merge(overrides) { _, new in new }
|
||||
return record
|
||||
}
|
||||
|
||||
/// 解码冷静期/已撤销实测样例。
|
||||
static func lifecycleStatus(cancelled: Bool = false, overrides: [String: Any] = [:]) throws -> StoreAccountDeregistrationStatus {
|
||||
try JSONDecoder().decode(StoreAccountDeregistrationStatus.self,
|
||||
from: JSONSerialization.data(withJSONObject: ["deregister": lifecycleRecord(cancelled: cancelled, overrides: overrides)]))
|
||||
}
|
||||
|
||||
/// 冷静期及撤销后真实查询均返回两项确认失效,不能在冷静期重新确认资产。
|
||||
static func lifecycleEligibility(cancelled: Bool = false) throws -> StoreAccountDeregistrationEligibility {
|
||||
try eligibility(overrides: ["unfulfilled_count": 0, "risk_end_at": NSNull(), "eligible_at": NSNull(),
|
||||
"blockers": [
|
||||
["code": "WALLET_WAIVER_MISSING", "message": "请先确认放弃现金余额", "action": "confirm_wallet_waiver"],
|
||||
["code": "POINTS_WAIVER_MISSING", "message": "请先确认放弃积分", "action": "confirm_points_waiver"],
|
||||
], "deregister": lifecycleRecord(cancelled: cancelled)])
|
||||
}
|
||||
|
||||
/// 统一成功 Envelope,仅用于 MockURLSession。
|
||||
static func envelope(_ data: Data) throws -> Data {
|
||||
try JSONSerialization.data(withJSONObject: ["code": 100000, "msg": "success",
|
||||
"data": JSONSerialization.jsonObject(with: data)])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user