fix: route payment pushes to orders and prevent refresh crash

This commit is contained in:
2026-09-08 09:59:51 +08:00
parent 10249776ad
commit c4b0e7798b
10 changed files with 333 additions and 47 deletions
@@ -67,16 +67,20 @@ final class MainTabBarController: UITabBarController {
/// 响应推送点击,切换到目标 Tab 并打开对应业务页面。
func openPushDestination(_ destination: PushDestination) {
let targetTab = AppTab.home
loadViewIfNeeded()
let targetTab: AppTab = destination == .orders ? .orders : .home
let wasAlreadyOnOrders = targetTab == .orders && selectedViewController === tabNavigationControllers[.orders]
selectTab(targetTab)
guard let navigationController = tabNavigationControllers[targetTab] else { return }
_ = navigationController.popToRootViewController(animated: false)
let controller: UIViewController?
switch destination {
case .paymentRecord:
controller = PaymentCollectionRecordViewController()
case .orders:
(navigationController.viewControllers.first as? OrdersViewController)?.openFromOrderNotification()
if wasAlreadyOnOrders { badgeViewModel.refreshPendingWriteOffCount() }
controller = nil
case .paymentDetails:
controller = PaymentCollectionDetailsViewController()
case .messageCenter:
+118 -27
View File
@@ -8,17 +8,64 @@ import SnapKit
import UIKit
/// 订单 Tab 根页面,按角色分流摄影师/景区管理员/店铺管理员。
final class OrdersViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate {
final class OrdersViewController: BaseViewController, UITableViewDelegate {
private let orderAPI = NetworkServices.shared.orderAPI
private let orderListViewModel = OrderListViewModel()
private let depositViewModel = DepositOrderListViewModel()
private let appStore: AppStore
private let orderAPI: OrderAPI
private let orderListViewModel: OrderListViewModel
private let depositViewModel: DepositOrderListViewModel
/// 表格快照持有完整行数据,避免刷新清空 ViewModel 后仍按旧行号取值。
private struct OrderRow: Hashable {
/// 同一快照内按位置区分行,兼容接口返回重复订单号。
let position: Int
let content: Content
/// 摄影师和店铺订单的不可变展示数据。
enum Content: Equatable {
case photographer(OrderEntity, source: OrderSourceSelection?, readOnly: Bool, showSource: Bool)
case deposit(StoreOrderItem)
}
func hash(into hasher: inout Hasher) {
hasher.combine(position)
switch content {
case .photographer(let order, _, _, _):
hasher.combine(0)
hasher.combine(order.orderNumber)
case .deposit(let order):
hasher.combine(1)
hasher.combine(order.orderNumber)
}
}
}
private var dataSource: UITableViewDiffableDataSource<Int, OrderRow>?
/// 注入订单数据依赖;默认使用应用共享 API 和独立的页面 ViewModel。
init(
orderAPI: OrderAPI? = nil,
orderListViewModel: OrderListViewModel? = nil,
depositViewModel: DepositOrderListViewModel? = nil,
appStore: AppStore = .shared
) {
self.appStore = appStore
self.orderAPI = orderAPI ?? NetworkServices.shared.orderAPI
self.orderListViewModel = orderListViewModel ?? OrderListViewModel(appStore: appStore)
self.depositViewModel = depositViewModel ?? DepositOrderListViewModel(appStore: appStore)
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private let filterBar = OrderFilterBarView()
private let listView = OrderListView()
private var hasInitialized = false
private var isStoreMode: Bool { AppStore.shared.session.currentAppRole == .storeAdmin }
private var isStoreMode: Bool { appStore.session.currentAppRole == .storeAdmin }
private var pendingOrderSourceNumber: String?
private weak var orderSourcePicker: OrderSourcePickerViewController?
private var pendingGiftOrderNumber: String?
@@ -32,10 +79,10 @@ final class OrdersViewController: BaseViewController, UITableViewDataSource, UIT
override func setupUI() {
view.addSubview(filterBar)
view.addSubview(listView)
listView.tableView.dataSource = self
listView.tableView.delegate = self
listView.tableView.register(OrderCardCell.self, forCellReuseIdentifier: OrderCardCell.reuseIdentifier)
listView.tableView.register(DepositOrderCardCell.self, forCellReuseIdentifier: DepositOrderCardCell.reuseIdentifier)
configureDataSource()
}
override func setupConstraints() {
@@ -133,6 +180,21 @@ final class OrdersViewController: BaseViewController, UITableViewDataSource, UIT
Task { await initializeList() }
}
/// 从订单通知进入时恢复默认查询,并刷新已有或首次打开的列表。
func openFromOrderNotification() {
loadViewIfNeeded()
hasInitialized = true
if isStoreMode {
depositViewModel.resetFiltersForOrderNotification()
} else {
orderListViewModel.resetFiltersForOrderNotification()
}
applyViewModel()
Task { @MainActor [weak self] in
await self?.initializeList()
}
}
private func initializeList() async {
if isStoreMode {
await depositViewModel.refreshOrderList(api: orderAPI)
@@ -142,6 +204,7 @@ final class OrdersViewController: BaseViewController, UITableViewDataSource, UIT
}
private func applyViewModel() {
applyOrderSnapshot()
if isStoreMode {
filterBar.configure(
isStoreMode: true,
@@ -191,7 +254,6 @@ final class OrdersViewController: BaseViewController, UITableViewDataSource, UIT
presentMultiVerifySheet()
}
}
listView.tableView.reloadData()
}
private func dateRangeText(start: Date?, end: Date?) -> String {
@@ -314,31 +376,60 @@ final class OrdersViewController: BaseViewController, UITableViewDataSource, UIT
// MARK: - UITableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
isStoreMode ? depositViewModel.orderList.count : orderListViewModel.orderList.count
private func configureDataSource() {
dataSource = UITableViewDiffableDataSource<Int, OrderRow>(tableView: listView.tableView) { [weak self] tableView, _, row in
guard let self else { return nil }
switch row.content {
case .deposit(let item):
let cell = tableView.dequeueReusableCell(withIdentifier: DepositOrderCardCell.reuseIdentifier) as! DepositOrderCardCell
cell.configure(item: item, actions: self.makeDepositActions(for: item))
return cell
case .photographer(let order, let source, let readOnly, let showSource):
let cell = tableView.dequeueReusableCell(withIdentifier: OrderCardCell.reuseIdentifier) as! OrderCardCell
cell.configure(
order: order,
hideWriteActions: readOnly,
actions: self.makeOrderActions(for: order),
showOrderSource: showSource,
sourceSelection: source
)
return cell
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
private func applyOrderSnapshot() {
let rows: [OrderRow]
if isStoreMode {
let cell = tableView.dequeueReusableCell(withIdentifier: DepositOrderCardCell.reuseIdentifier, for: indexPath) as! DepositOrderCardCell
let item = depositViewModel.orderList[indexPath.row]
cell.configure(item: item, actions: makeDepositActions(for: item))
listView.notifyLoadMoreIfNeeded(at: indexPath.row, shouldLoad: depositViewModel.shouldLoadMore(at: indexPath.row))
return cell
rows = depositViewModel.orderList.enumerated().map {
OrderRow(position: $0.offset, content: .deposit($0.element))
}
} else {
rows = orderListViewModel.orderList.enumerated().map { index, order in
OrderRow(position: index, content: .photographer(
order,
source: orderListViewModel.orderSourceSelection(for: order),
readOnly: orderListViewModel.isScenicAdminReadOnly,
showSource: orderListViewModel.shouldShowOrderSource(for: order)
))
}
}
var snapshot = NSDiffableDataSourceSnapshot<Int, OrderRow>()
snapshot.appendSections([0])
snapshot.appendItems(rows)
dataSource?.apply(snapshot, animatingDifferences: true)
}
let cell = tableView.dequeueReusableCell(withIdentifier: OrderCardCell.reuseIdentifier, for: indexPath) as! OrderCardCell
let order = orderListViewModel.orderList[indexPath.row]
let source = orderListViewModel.orderSourceSelection(for: order)
cell.configure(
order: order,
hideWriteActions: orderListViewModel.isScenicAdminReadOnly,
actions: makeOrderActions(for: order),
showOrderSource: orderListViewModel.shouldShowOrderSource(for: order),
sourceSelection: source
)
listView.notifyLoadMoreIfNeeded(at: indexPath.row, shouldLoad: orderListViewModel.shouldLoadMore(at: indexPath.row))
return cell
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let row = dataSource?.itemIdentifier(for: indexPath) else { return }
let shouldLoad: Bool
switch row.content {
case .deposit:
shouldLoad = isStoreMode && depositViewModel.shouldLoadMore(at: row.position)
case .photographer:
shouldLoad = !isStoreMode && orderListViewModel.shouldLoadMore(at: row.position)
}
listView.notifyLoadMoreIfNeeded(at: row.position, shouldLoad: shouldLoad)
}
private func makeOrderActions(for order: OrderEntity) -> OrderCardActions {