Implement orders tab aligned with Android, including scan verify flows.

Add role-based order/deposit lists, AVFoundation QR scanning, verify dialogs, and ViewModel tests so photographers, scenic admins, and store admins match Android behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 16:59:53 +08:00
parent 6492f80ef0
commit 31d2b6094b
27 changed files with 4226 additions and 17 deletions

View File

@ -0,0 +1,243 @@
//
// OrderAPI.swift
// suixinkan
//
import Foundation
@MainActor
/// API Android `NetworkApi`
final class OrderAPI {
private let client: APIClient
init(client: APIClient) {
self.client = client
}
func photographerOrderList(
scenicId: Int,
page: Int,
pageSize: Int = OrderListQueryBuilder.photographerPageSize,
orderStatus: Int = 0,
startTime: String? = nil,
endTime: String? = nil,
userPhone: String? = nil,
isRefined: Int = 0
) async throws -> OrderListResponse<OrderEntity> {
var items: [URLQueryItem] = [
URLQueryItem(name: "scenic_id", value: String(scenicId)),
URLQueryItem(name: "page", value: String(page)),
URLQueryItem(name: "page_size", value: String(pageSize)),
]
if let orderStatus = OrderListQueryBuilder.orderStatusQuery(tabIndex: orderStatus) {
items.append(URLQueryItem(name: "order_status", value: orderStatus))
}
if let startTime, !startTime.isEmpty { items.append(URLQueryItem(name: "start_time", value: startTime)) }
if let endTime, !endTime.isEmpty { items.append(URLQueryItem(name: "end_time", value: endTime)) }
if let userPhone, !userPhone.isEmpty { items.append(URLQueryItem(name: "user_phone", value: userPhone)) }
if let isRefined = OrderListQueryBuilder.isRefinedQuery(rTabIndex: isRefined) {
items.append(URLQueryItem(name: "is_refined", value: String(isRefined)))
}
return try await client.send(
APIRequest(method: .get, path: "/api/yf-handset-app/photog/order/listv2", queryItems: items)
)
}
func scenicAdminOrderList(
scenicId: Int,
page: Int,
pageSize: Int = OrderListQueryBuilder.photographerPageSize,
orderStatus: Int = 0,
startTime: String? = nil,
endTime: String? = nil,
userPhone: String? = nil,
isRefined: Int = 0
) async throws -> OrderListResponse<OrderEntity> {
var items: [URLQueryItem] = [
URLQueryItem(name: "scenic_id", value: String(scenicId)),
URLQueryItem(name: "page", value: String(page)),
URLQueryItem(name: "page_size", value: String(pageSize)),
]
if let orderStatus = OrderListQueryBuilder.orderStatusQuery(tabIndex: orderStatus) {
items.append(URLQueryItem(name: "order_status", value: orderStatus))
}
if let startTime, !startTime.isEmpty { items.append(URLQueryItem(name: "start_time", value: startTime)) }
if let endTime, !endTime.isEmpty { items.append(URLQueryItem(name: "end_time", value: endTime)) }
if let userPhone, !userPhone.isEmpty { items.append(URLQueryItem(name: "user_phone", value: userPhone)) }
if let isRefined = OrderListQueryBuilder.isRefinedQuery(rTabIndex: isRefined) {
items.append(URLQueryItem(name: "is_refined", value: String(isRefined)))
}
return try await client.send(
APIRequest(method: .get, path: "/api/app/scenic-admin/order/list", queryItems: items)
)
}
func storeOrderList(
storeId: Int?,
page: Int,
pageSize: Int = OrderListQueryBuilder.storePageSize,
userPhone: String? = nil,
orderStatus: Int? = nil,
orderType: Int? = nil,
startTime: String? = nil,
endTime: String? = nil
) async throws -> StoreOrderListData {
var items: [URLQueryItem] = [
URLQueryItem(name: "page", value: String(page)),
URLQueryItem(name: "page_size", value: String(pageSize)),
]
if let storeId { items.append(URLQueryItem(name: "store_id", value: String(storeId))) }
if let userPhone, !userPhone.isEmpty { items.append(URLQueryItem(name: "user_phone", value: userPhone)) }
if let orderStatus { items.append(URLQueryItem(name: "order_status", value: String(orderStatus))) }
if let orderType { items.append(URLQueryItem(name: "order_type", value: String(orderType))) }
if let startTime, !startTime.isEmpty { items.append(URLQueryItem(name: "start_time", value: startTime)) }
if let endTime, !endTime.isEmpty { items.append(URLQueryItem(name: "end_time", value: endTime)) }
return try await client.send(
APIRequest(method: .get, path: "/api/app/store/order/list", queryItems: items)
)
}
func orderCheckIn(jsonBody: String) async throws -> OrderSignInResponse {
try await client.sendRawJSON(
method: .post,
path: "/api/yf-handset-app/photog/order/check-in",
jsonBody: jsonBody
)
}
func orderVerify(jsonBody: String) async throws -> WriteOffOrderResponse {
try await client.sendRawJSON(
method: .post,
path: "/api/yf-handset-app/photog/order/order-verify",
jsonBody: jsonBody
)
}
func multiTravelScenicSpotList(orderNumber: String, lat: Double, lng: Double) async throws -> MultiTravelScenicSpotListResponse {
try await client.send(
APIRequest(
method: .get,
path: "/api/yf-handset-app/photog/order/multi-travel/scenic-spot-list",
queryItems: [
URLQueryItem(name: "order_number", value: orderNumber),
URLQueryItem(name: "lat", value: String(lat)),
URLQueryItem(name: "lng", value: String(lng)),
]
)
)
}
func multiTravelOrderVerify(_ request: MultiTravelOrderVerifyRequest) async throws -> OrderSignInResponse {
try await client.send(
APIRequest(
method: .post,
path: "/api/yf-handset-app/photog/order/multi-travel/order-verify",
body: request
)
)
}
func cancelOrder(_ request: CancelRequest) async throws {
let _: EmptyPayload = try await client.send(
APIRequest(method: .post, path: "/api/yf-handset-app/photog/order/cancel", body: request)
)
}
func refundOrder(_ request: OrderRefundRequest) async throws {
let _: EmptyPayload = try await client.send(
APIRequest(method: .post, path: "/api/yf-handset-app/photog/order/refund", body: request)
)
}
func giftRetouch(_ request: SendGiftRequest) async throws {
let _: EmptyPayload = try await client.send(
APIRequest(method: .post, path: "/api/yf-handset-app/photog/order/gift-retouch", body: request)
)
}
func updateRefined(orderNumber: String, isRefined: Int) async throws {
let _: EmptyPayload = try await client.send(
APIRequest(
method: .post,
path: "/api/yf-handset-app/photog/order/refined",
queryItems: [
URLQueryItem(name: "order_number", value: orderNumber),
URLQueryItem(name: "is_refined", value: String(isRefined)),
]
)
)
}
func cooperativeSalers(storeId: String?, page: String = "1", pageSize: String = "50") async throws -> CooperativeSalerListResponse {
var items = [
URLQueryItem(name: "page", value: page),
URLQueryItem(name: "page_size", value: pageSize),
]
if let storeId { items.append(URLQueryItem(name: "store_id", value: storeId)) }
return try await client.send(
APIRequest(method: .get, path: "/api/yf-handset-app/photog/cooperative-salers", queryItems: items)
)
}
func referralOrders(saleUserId: String, page: String = "1", pageSize: String = "50") async throws -> ReferralOrderListResponse {
try await client.send(
APIRequest(
method: .get,
path: "/api/yf-handset-app/photog/sale-user/referral-orders",
queryItems: [
URLQueryItem(name: "sale_user_id", value: saleUserId),
URLQueryItem(name: "page", value: page),
URLQueryItem(name: "page_size", value: pageSize),
]
)
)
}
func bindReferralOrder(orderNumber: String, referralOrderNo: String) async throws {
let _: EmptyPayload = try await client.send(
APIRequest(
method: .post,
path: "/api/yf-handset-app/photog/sale-user/bind-referral-order",
queryItems: [
URLQueryItem(name: "order_number", value: orderNumber),
URLQueryItem(name: "referral_order_no", value: referralOrderNo),
]
)
)
}
func shootHistory(orderNumber: String) async throws -> MultiTravelShootHistoryResponse {
try await client.send(
APIRequest(
method: .get,
path: "/api/yf-handset-app/photog/order/multi-travel/shoot-history",
queryItems: [URLQueryItem(name: "order_number", value: orderNumber)]
)
)
}
func storeOrderVerify(_ request: StoreOrderVerifyRequest) async throws -> WriteOffOrderResponse {
try await client.send(
APIRequest(method: .post, path: "/api/app/store/order/order-verify", body: request)
)
}
func storeOrderRefund(_ request: StoreOrderRefundRequest) async throws {
let _: EmptyPayload = try await client.send(
APIRequest(method: .post, path: "/api/app/store/order/refund", body: request)
)
}
func storeOrderDetail(storeId: Int, orderNumber: String) async throws -> StoreOrderDetailData {
try await client.send(
APIRequest(
method: .get,
path: "/api/app/store/order/detail",
queryItems: [
URLQueryItem(name: "store_id", value: String(storeId)),
URLQueryItem(name: "order_number", value: orderNumber),
]
)
)
}
}