对齐 Android 立即收款页面 UI,并补齐推送状态与语音开关。
统一收款详情布局、设置金额弹窗和收款记录样式,接入 type 6/1 推送驱动与轮询 fallback,使 iOS 收款反馈与 Android 一致。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -124,6 +124,13 @@ final class PushNotificationManager: NSObject, UNUserNotificationCenterDelegate
|
||||
}
|
||||
|
||||
private func route(from payload: PushPayload) {
|
||||
if let type = payload.paymentEventType, type == 1 || type == 6 {
|
||||
PaymentPushEventCenter.shared.post(type: type, dataJSON: payload.paymentEventData)
|
||||
if type == 1 {
|
||||
handlePaymentVoiceIfNeeded(payload: payload)
|
||||
}
|
||||
}
|
||||
|
||||
guard let router else { return }
|
||||
|
||||
switch payload.route {
|
||||
@ -147,4 +154,15 @@ final class PushNotificationManager: NSObject, UNUserNotificationCenterDelegate
|
||||
router.select(.home)
|
||||
router.router(for: .home).navigate(to: .home(route))
|
||||
}
|
||||
|
||||
/// 付款成功推送且语音开关开启时播报到账金额。
|
||||
private func handlePaymentVoiceIfNeeded(payload: PushPayload) {
|
||||
let preferences = AppPreferencesStore()
|
||||
guard preferences.loadPaymentReceiveVoiceEnabled(),
|
||||
let amount = payload.paymentSuccessAmount,
|
||||
!amount.isEmpty else {
|
||||
return
|
||||
}
|
||||
PaymentVoiceSpeaker.shared.speak("收款到账\(amount)元")
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ struct PushPayload: Sendable {
|
||||
let actionText = values["action"] ?? ""
|
||||
let merged = [typeText, routeText, uriText, actionText].joined(separator: " ").lowercased()
|
||||
|
||||
if typeText == "1" || merged.contains("payment") || merged.contains("pay") || merged.contains("收款") {
|
||||
if typeText == "1" || typeText == "6" || merged.contains("payment") || merged.contains("pay") || merged.contains("收款") {
|
||||
return .payment
|
||||
}
|
||||
if merged.contains("queue") || merged.contains("排队") || merged.contains("叫号") || uriText == "/scenic-queue" {
|
||||
@ -64,6 +64,34 @@ struct PushPayload: Sendable {
|
||||
return .messageCenter
|
||||
}
|
||||
|
||||
/// 推送 extras 中的 type 字段,供收款页区分扫码/付款事件。
|
||||
nonisolated var paymentEventType: Int? {
|
||||
guard let typeText = values["type"], !typeText.isEmpty else { return nil }
|
||||
return Int(typeText)
|
||||
}
|
||||
|
||||
/// 推送 extras 中的 data 字段,付款成功时通常为 JSON 字符串。
|
||||
nonisolated var paymentEventData: String? {
|
||||
let data = values["data"]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return data.isEmpty ? nil : data
|
||||
}
|
||||
|
||||
/// 从付款成功 data JSON 中解析到账金额,供全局语音播报使用。
|
||||
nonisolated var paymentSuccessAmount: String? {
|
||||
guard let dataJSON = paymentEventData,
|
||||
let data = dataJSON.data(using: .utf8),
|
||||
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
||||
return nil
|
||||
}
|
||||
if let amount = json["order_amount"] as? String, !amount.isEmpty {
|
||||
return amount
|
||||
}
|
||||
if let amount = json["order_amount"] as? NSNumber {
|
||||
return amount.stringValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
nonisolated private static func mergeJSON(_ value: Any, into result: inout [String: String]) {
|
||||
if let dict = value as? [String: Any] {
|
||||
for (key, value) in dict {
|
||||
|
||||
@ -13,6 +13,7 @@ final class AppPreferencesStore {
|
||||
private let defaults: UserDefaults
|
||||
private let lastLoginUsernameKey = "suixinkan.preferences.last_login_username"
|
||||
private let privacyAgreementAcceptedKey = "suixinkan.preferences.privacy_agreement_accepted"
|
||||
private let paymentReceiveVoiceKey = "suixinkan.preferences.payment_receive_voice"
|
||||
|
||||
/// 初始化偏好存储服务,并允许测试注入独立的 UserDefaults。
|
||||
init(defaults: UserDefaults = .standard) {
|
||||
@ -43,6 +44,16 @@ final class AppPreferencesStore {
|
||||
defaults.bool(forKey: privacyAgreementAcceptedKey)
|
||||
}
|
||||
|
||||
/// 读取收款到账语音提醒开关。
|
||||
func loadPaymentReceiveVoiceEnabled() -> Bool {
|
||||
defaults.bool(forKey: paymentReceiveVoiceKey)
|
||||
}
|
||||
|
||||
/// 保存收款到账语音提醒开关。
|
||||
func savePaymentReceiveVoiceEnabled(_ enabled: Bool) {
|
||||
defaults.set(enabled, forKey: paymentReceiveVoiceKey)
|
||||
}
|
||||
|
||||
/// 清空登录页偏好。
|
||||
func clear() {
|
||||
defaults.removeObject(forKey: lastLoginUsernameKey)
|
||||
|
||||
Reference in New Issue
Block a user