新增门店订单与核销管理列表,对齐 Android 订单能力并完善账号级缓存。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 14:46:10 +08:00
parent c4de3d17d0
commit 08492df6ce
51 changed files with 3668 additions and 102 deletions

View File

@ -223,6 +223,93 @@ final class OrdersViewModelTests: XCTestCase {
XCTAssertEqual(query["end_time"], "2026-05-31")
}
///
func testUpdateRefinedChoiceUpdatesLocalOrder() async throws {
let api = MockOrderService()
api.storeResponses = [try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success")]
let viewModel = OrdersViewModel()
try await viewModel.reloadStoreOrders(api: api, scenicId: 88, appRole: nil)
try await viewModel.updateRefinedChoice(api: api, orderNumber: "ORDV2001", isRefined: 0)
XCTAssertEqual(api.refinedUpdates.count, 1)
XCTAssertEqual(api.refinedUpdates[0].0, "ORDV2001")
XCTAssertEqual(api.refinedUpdates[0].1, 0)
let order = try XCTUnwrap(viewModel.storeOrders.first { $0.orderNumber == "ORDV2001" })
XCTAssertEqual(order.isRefined, 0)
}
///
func testCancelOrderRefreshesStoreOrders() async throws {
let api = MockOrderService()
api.storeResponses = [
try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success"),
try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success")
]
let viewModel = OrdersViewModel()
try await viewModel.reloadStoreOrders(api: api, scenicId: 88, appRole: nil)
try await viewModel.cancelOrder(api: api, scenicId: 88, appRole: nil, orderNumber: "ORDV2001")
XCTAssertEqual(api.cancelledOrders, ["ORDV2001"])
XCTAssertEqual(api.orderListCalls.count, 2)
}
///
func testSendGiftFlowPreparesAndSubmits() async throws {
let api = MockOrderService()
api.storeResponses = [
try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success"),
try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success")
]
let viewModel = OrdersViewModel()
try await viewModel.reloadStoreOrders(api: api, scenicId: 88, appRole: nil)
let order = try XCTUnwrap(viewModel.storeOrders.first)
viewModel.prepareSendGift(for: order)
XCTAssertEqual(viewModel.sendGiftContext?.orderNumber, order.orderNumber)
XCTAssertEqual(viewModel.sendGiftContext?.sendPhotoNum, 2)
try await viewModel.submitSendGift(api: api, scenicId: 88, appRole: nil, photoNum: 3, videoNum: 1)
XCTAssertEqual(api.sendGiftCalls.count, 1)
XCTAssertEqual(api.sendGiftCalls[0].0, order.orderNumber)
XCTAssertEqual(api.sendGiftCalls[0].1, 3)
XCTAssertEqual(api.sendGiftCalls[0].2, 1)
XCTAssertNil(viewModel.sendGiftContext)
XCTAssertEqual(api.orderListCalls.count, 2)
}
/// 退
func testShouldShowRefundButtonConditions() async throws {
let api = MockOrderService()
api.storeResponses = [try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success")]
let viewModel = OrdersViewModel()
try await viewModel.reloadStoreOrders(api: api, scenicId: 88, appRole: nil)
let multiTravel = try XCTUnwrap(viewModel.storeOrders.first { $0.orderType == 19 })
let deposit = try XCTUnwrap(viewModel.storeOrders.first { $0.orderType == 4 })
XCTAssertFalse(viewModel.shouldShowRefundButton(order: multiTravel, appRole: nil))
XCTAssertTrue(viewModel.shouldShowRefundButton(order: deposit, appRole: nil))
XCTAssertFalse(viewModel.shouldShowRefundButton(order: deposit, appRole: .scenicAdmin))
}
///
func testShouldShowVerifyButtonConditions() async throws {
let api = MockOrderService()
api.storeResponses = [try TestFixture.payload(ListPayload<OrderEntity>.self, named: "order_v2_success")]
let viewModel = OrdersViewModel()
try await viewModel.reloadStoreOrders(api: api, scenicId: 88, appRole: nil)
let multiTravel = try XCTUnwrap(viewModel.storeOrders.first { $0.orderType == 19 })
let deposit = try XCTUnwrap(viewModel.storeOrders.first { $0.orderType == 4 })
XCTAssertTrue(viewModel.shouldShowVerifyButton(order: multiTravel, appRole: nil))
XCTAssertFalse(viewModel.shouldShowVerifyButton(order: deposit, appRole: nil))
XCTAssertFalse(viewModel.shouldShowVerifyButton(order: multiTravel, appRole: .scenicAdmin))
}
private static func date(_ text: String) -> Date {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
@ -261,6 +348,9 @@ private final class MockOrderService: OrderServing {
private(set) var writeOffListCalls: [WriteOffListCall] = []
private(set) var detailCalls: [(storeId: Int, orderNumber: String)] = []
private(set) var verifiedOrderNumbers: [String] = []
private(set) var refinedUpdates: [(String, Int)] = []
private(set) var cancelledOrders: [String] = []
private(set) var sendGiftCalls: [(String, Int, Int)] = []
///
func orderList(
@ -291,11 +381,50 @@ private final class MockOrderService: OrderServing {
}
///
func writeOffList(scenicId: Int, storeId: Int?, page: Int, pageSize: Int) async throws -> ListPayload<WriteOffOrderItem> {
func writeOffList(
scenicId: Int,
storeId: Int?,
page: Int,
pageSize: Int,
orderStatus: Int?,
orderVerifyStatus: Int?,
startTime: String?,
endTime: String?,
userPhone: String?
) async throws -> ListPayload<WriteOffOrderItem> {
writeOffListCalls.append(WriteOffListCall(scenicId: scenicId, storeId: storeId, page: page, pageSize: pageSize))
return writeOffResponses.isEmpty ? ListPayload(total: 0, list: []) : writeOffResponses.removeFirst()
}
func updateRefinedChoice(orderNumber: String, isRefined: Int) async throws {
refinedUpdates.append((orderNumber, isRefined))
}
func cancelOrder(orderNumber: String) async throws {
cancelledOrders.append(orderNumber)
}
func sendGift(orderNumber: String, photoNum: Int, videoNum: Int) async throws {
sendGiftCalls.append((orderNumber, photoNum, videoNum))
}
func storeOrderList(
storeId: Int?,
page: Int,
pageSize: Int,
userPhone: String?,
orderStatus: Int?,
orderType: Int?,
startTime: String?,
endTime: String?
) async throws -> ListPayload<StoreOrderListItem> {
ListPayload(total: 0, list: [])
}
func storeOrderRefund(
storeId: Int,
orderNumber: String,
refundType: OrderRefundMode,
refundAmount: String,
refundReason: String
) async throws {}
///
func writeOff(orderNumber: String) async throws {
verifiedOrderNumbers.append(orderNumber)