添加景区选择与合作订单流程并对齐 Android
This commit is contained in:
@ -206,6 +206,93 @@ final class OrderAPI {
|
||||
)
|
||||
}
|
||||
|
||||
func referralLeads(page: String = "1", pageSize: String = "10") async throws -> OrderListResponse<ReferralLeadEntity> {
|
||||
try await client.send(
|
||||
APIRequest(
|
||||
method: .get,
|
||||
path: "/api/yf-handset-app/photog/sale-user/referral-leads",
|
||||
queryItems: [
|
||||
URLQueryItem(name: "page", value: page),
|
||||
URLQueryItem(name: "page_size", value: pageSize),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func acquisitionOrders(keyword: String? = nil, page: String = "1", pageSize: String = "10") async throws -> OrderListResponse<AcquisitionOrderEntity> {
|
||||
var items = [
|
||||
URLQueryItem(name: "page", value: page),
|
||||
URLQueryItem(name: "page_size", value: pageSize),
|
||||
]
|
||||
if let keyword, !keyword.isEmpty {
|
||||
items.append(URLQueryItem(name: "keyword", value: keyword))
|
||||
}
|
||||
return try await client.send(
|
||||
APIRequest(
|
||||
method: .get,
|
||||
path: "/api/yf-handset-app/photog/sale-user/acquisition-orders",
|
||||
queryItems: items
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func saleUserInviteInfo(saleUserId: String) async throws -> SaleUserInviteInfoResponse {
|
||||
try await client.send(
|
||||
APIRequest(
|
||||
method: .get,
|
||||
path: "/api/yf-handset-app/photog/sale-user/invite-info",
|
||||
queryItems: [URLQueryItem(name: "sale_user_id", value: saleUserId)]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func saleUserAcceptBind(saleUserId: Int, commissionRate: Int) async throws {
|
||||
let _: EmptyPayload = try await client.send(
|
||||
APIRequest(
|
||||
method: .post,
|
||||
path: "/api/yf-handset-app/photog/sale-user/accept-bind",
|
||||
queryItems: [
|
||||
URLQueryItem(name: "sale_user_id", value: String(saleUserId)),
|
||||
URLQueryItem(name: "commission_rate", value: String(commissionRate)),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func saleUserUpdateRemark(saleUserId: Int, photogRemarkName: String) async throws {
|
||||
let _: EmptyPayload = try await client.send(
|
||||
APIRequest(
|
||||
method: .post,
|
||||
path: "/api/yf-handset-app/photog/sale-user/update-remark",
|
||||
body: SaleUserUpdateRemarkRequest(saleUserId: saleUserId, photogRemarkName: photogRemarkName)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func saleUserCommissionRateSendSmsCode(phone: String) async throws {
|
||||
let _: EmptyPayload = try await client.send(
|
||||
APIRequest(
|
||||
method: .post,
|
||||
path: "/api/yf-handset-app/photog/sale-user/commission-rate/send-sms-code",
|
||||
body: SaleUserCommissionRateSmsRequest(phone: phone)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func saleUserUpdateCommissionRate(saleUserId: Int, commissionRate: Int, smsCode: String) async throws -> CooperativeSalerEntity {
|
||||
try await client.send(
|
||||
APIRequest(
|
||||
method: .post,
|
||||
path: "/api/yf-handset-app/photog/sale-user/commission-rate",
|
||||
body: SaleUserCommissionRateUpdateRequest(
|
||||
saleUserId: saleUserId,
|
||||
commissionRate: commissionRate,
|
||||
smsCode: smsCode
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func shootHistory(orderNumber: String) async throws -> MultiTravelShootHistoryResponse {
|
||||
try await client.send(
|
||||
APIRequest(
|
||||
|
||||
@ -789,10 +789,19 @@ struct CooperativeSalerEntity: Decodable, Equatable {
|
||||
let photogRemarkName: String
|
||||
let phone: String
|
||||
let salerPhone: String
|
||||
let commissionRate: Int
|
||||
let commissionRateLabel: String
|
||||
let bindTime: String
|
||||
let boundAt: String
|
||||
|
||||
var displayId: Int { saleUserId }
|
||||
var displayName: String { OrderDisplayName.saleUser(remark: photogRemarkName, primary: name, fallback: salerName) }
|
||||
var displayPhone: String { salerPhone.isEmpty ? phone : salerPhone }
|
||||
var displayBindTime: String { bindTime.isEmpty ? boundAt : bindTime }
|
||||
var displayCommissionRate: String {
|
||||
if !commissionRateLabel.isEmpty { return commissionRateLabel }
|
||||
return "\(commissionRate)%"
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case saleUserId = "sale_user_id"
|
||||
@ -802,6 +811,67 @@ struct CooperativeSalerEntity: Decodable, Equatable {
|
||||
case photogRemarkName = "photog_remark_name"
|
||||
case phone
|
||||
case salerPhone = "saler_phone"
|
||||
case commissionRate = "commission_rate"
|
||||
case commissionRateLabel = "commission_rate_label"
|
||||
case bindTime = "bind_time"
|
||||
case boundAt = "bound_at"
|
||||
}
|
||||
|
||||
init(
|
||||
saleUserId: Int = 0,
|
||||
bindingId: Int = 0,
|
||||
name: String = "",
|
||||
salerName: String = "",
|
||||
photogRemarkName: String = "",
|
||||
phone: String = "",
|
||||
salerPhone: String = "",
|
||||
commissionRate: Int = 0,
|
||||
commissionRateLabel: String = "",
|
||||
bindTime: String = "",
|
||||
boundAt: String = ""
|
||||
) {
|
||||
self.saleUserId = saleUserId
|
||||
self.bindingId = bindingId
|
||||
self.name = name
|
||||
self.salerName = salerName
|
||||
self.photogRemarkName = photogRemarkName
|
||||
self.phone = phone
|
||||
self.salerPhone = salerPhone
|
||||
self.commissionRate = commissionRate
|
||||
self.commissionRateLabel = commissionRateLabel
|
||||
self.bindTime = bindTime
|
||||
self.boundAt = boundAt
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0
|
||||
bindingId = try container.decodeIfPresent(Int.self, forKey: .bindingId) ?? 0
|
||||
name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
|
||||
salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? ""
|
||||
photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? ""
|
||||
phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? ""
|
||||
salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? ""
|
||||
commissionRate = try container.decodeIfPresent(Int.self, forKey: .commissionRate) ?? 0
|
||||
commissionRateLabel = try container.decodeIfPresent(String.self, forKey: .commissionRateLabel) ?? ""
|
||||
bindTime = try container.decodeIfPresent(String.self, forKey: .bindTime) ?? ""
|
||||
boundAt = try container.decodeIfPresent(String.self, forKey: .boundAt) ?? ""
|
||||
}
|
||||
|
||||
func withRemarkName(_ remark: String) -> CooperativeSalerEntity {
|
||||
CooperativeSalerEntity(
|
||||
saleUserId: saleUserId,
|
||||
bindingId: bindingId,
|
||||
name: name,
|
||||
salerName: salerName,
|
||||
photogRemarkName: remark,
|
||||
phone: phone,
|
||||
salerPhone: salerPhone,
|
||||
commissionRate: commissionRate,
|
||||
commissionRateLabel: commissionRateLabel,
|
||||
bindTime: bindTime,
|
||||
boundAt: boundAt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -819,6 +889,7 @@ struct ReferralOrderEntity: Decodable, Equatable {
|
||||
let createdAt: String
|
||||
let status: Int
|
||||
let statusLabel: String
|
||||
let images: [String]
|
||||
|
||||
var displayReferralOrderNo: String { referralOrderNo.isEmpty ? orderNo : referralOrderNo }
|
||||
|
||||
@ -831,6 +902,20 @@ struct ReferralOrderEntity: Decodable, Equatable {
|
||||
case createdAt = "created_at"
|
||||
case status
|
||||
case statusLabel = "status_label"
|
||||
case images
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0
|
||||
referralOrderNo = try container.decodeIfPresent(String.self, forKey: .referralOrderNo) ?? ""
|
||||
orderNo = try container.decodeIfPresent(String.self, forKey: .orderNo) ?? ""
|
||||
userMobile = try container.decodeIfPresent(String.self, forKey: .userMobile) ?? ""
|
||||
remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? ""
|
||||
createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
|
||||
status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
|
||||
statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
|
||||
images = (try? container.decode([String].self, forKey: .images)) ?? []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ final class MainTabScanHandler: NSObject, CLLocationManagerDelegate {
|
||||
var onShowWriteOff: ((WriteOffOrderResponse) -> Void)?
|
||||
var onMultiVerifyPending: ((MultiVerifyParseData) -> Void)?
|
||||
var onShowMultiVerifySheet: (([MultiTravelScenicSpotEntity], MultiTravelScenicSpotEntity?) -> Void)?
|
||||
var onNavigateBindAcquirer: ((Int) -> Void)?
|
||||
|
||||
private let api: OrderAPI
|
||||
private let locationManager = CLLocationManager()
|
||||
@ -64,7 +65,7 @@ final class MainTabScanHandler: NSObject, CLLocationManagerDelegate {
|
||||
await fetchSpotsForMultiVerify(pending: pending)
|
||||
case SaleUserQrParser.methodSaleUserBind:
|
||||
if let id = SaleUserQrParser.parseSaleUserId(scanResult), id > 0 {
|
||||
onShowMessage?("获客员绑定功能待接入")
|
||||
onNavigateBindAcquirer?(id)
|
||||
} else {
|
||||
onShowMessage?("请扫描正确的获客员二维码")
|
||||
}
|
||||
|
||||
@ -20,9 +20,59 @@ struct OrderSourcePerson: Equatable {
|
||||
|
||||
struct OrderSourceLead: Equatable {
|
||||
let key: String
|
||||
let displayText: String
|
||||
let userMobile: String
|
||||
let createdAt: String
|
||||
let userPhone: String
|
||||
let remark: String
|
||||
let images: [String]
|
||||
}
|
||||
|
||||
enum OrderSourceMapping {
|
||||
|
||||
/// 将 API 带客单实体映射为订单来源选择项,对齐 Android `toOrderSourceLead()`。
|
||||
static func lead(from entity: ReferralOrderEntity) -> OrderSourceLead {
|
||||
let orderNo = entity.displayReferralOrderNo
|
||||
let remark: String
|
||||
if !entity.remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
remark = entity.remark
|
||||
} else if !entity.statusLabel.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
remark = entity.statusLabel
|
||||
} else {
|
||||
remark = "带客单 \(orderNo)"
|
||||
}
|
||||
return OrderSourceLead(
|
||||
key: orderNo,
|
||||
userPhone: entity.userMobile,
|
||||
remark: remark,
|
||||
images: entity.images
|
||||
)
|
||||
}
|
||||
|
||||
/// 将已绑定带客单实体映射为订单来源选择项,对齐 Android `toOrderSourceSelection()`。
|
||||
static func selection(from referral: BoundReferralOrderEntity) -> OrderSourceSelection? {
|
||||
let referralNo = referral.orderNo.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !referralNo.isEmpty else { return nil }
|
||||
|
||||
let personName = referral.displaySalerName.isEmpty ? "—" : referral.displaySalerName
|
||||
let person = OrderSourcePerson(
|
||||
key: referral.saleUserId > 0 ? String(referral.saleUserId) : "",
|
||||
name: personName,
|
||||
phone: referral.salerPhone
|
||||
)
|
||||
let remark: String
|
||||
if !referral.remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
remark = referral.remark
|
||||
} else if !referral.displayText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
remark = referral.displayText
|
||||
} else {
|
||||
remark = "带客单号 \(referralNo)"
|
||||
}
|
||||
let lead = OrderSourceLead(
|
||||
key: referralNo,
|
||||
userPhone: referral.userMobile,
|
||||
remark: remark,
|
||||
images: []
|
||||
)
|
||||
return OrderSourceSelection(person: person, lead: lead)
|
||||
}
|
||||
}
|
||||
|
||||
struct OrderSourceSelection: Equatable {
|
||||
@ -54,13 +104,14 @@ final class OrderListViewModel {
|
||||
private(set) var orderSourceLeads: [String: [OrderSourceLead]] = [:]
|
||||
private(set) var orderSourceSelections: [String: OrderSourceSelection] = [:]
|
||||
private(set) var loadingOrderSourceLeadsFor: String?
|
||||
private(set) var isLoadingOrderSourcePeople = false
|
||||
|
||||
var onStateChange: (() -> Void)?
|
||||
var onShowMessage: ((String) -> Void)?
|
||||
var onNavigateBindAcquirer: ((Int) -> Void)?
|
||||
|
||||
private var currentPage = 1
|
||||
private var totalCount = 0
|
||||
private var loadingOrderSourcePeople = false
|
||||
|
||||
private let apiDateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
@ -188,7 +239,7 @@ final class OrderListViewModel {
|
||||
return
|
||||
case SaleUserQrParser.methodSaleUserBind:
|
||||
if let saleUserId = SaleUserQrParser.parseSaleUserId(scanResult), saleUserId > 0 {
|
||||
onShowMessage?("获客员绑定功能待接入")
|
||||
onNavigateBindAcquirer?(saleUserId)
|
||||
} else {
|
||||
onShowMessage?("请扫描正确的获客员二维码")
|
||||
}
|
||||
@ -287,27 +338,27 @@ final class OrderListViewModel {
|
||||
}
|
||||
|
||||
func orderSourceSelection(for order: OrderEntity) -> OrderSourceSelection? {
|
||||
if let selection = orderSourceSelections[order.orderNumber] { return selection }
|
||||
guard let referral = order.referralOrder else { return nil }
|
||||
let person = OrderSourcePerson(
|
||||
key: String(referral.saleUserId),
|
||||
name: referral.displaySalerName,
|
||||
phone: referral.salerPhone
|
||||
)
|
||||
let lead = OrderSourceLead(
|
||||
key: referral.orderNo,
|
||||
displayText: referral.displayText.isEmpty ? referral.orderNo : referral.displayText,
|
||||
userMobile: referral.userMobile,
|
||||
createdAt: referral.createdAt
|
||||
)
|
||||
return OrderSourceSelection(person: person, lead: lead)
|
||||
orderSourceSelection(orderNumber: order.orderNumber, referralOrder: order.referralOrder)
|
||||
}
|
||||
|
||||
/// 按订单号读取当前绑定状态,供订单来源 Sheet 使用。
|
||||
func orderSourceSelection(orderNumber: String, referralOrder: BoundReferralOrderEntity? = nil) -> OrderSourceSelection? {
|
||||
if let selection = orderSourceSelections[orderNumber] { return selection }
|
||||
if let referral = referralOrder ?? orderList.first(where: { $0.orderNumber == orderNumber })?.referralOrder {
|
||||
return OrderSourceMapping.selection(from: referral)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func onOrderSourceSheetOpen(api: OrderAPI) async {
|
||||
orderSourceLeads = [:]
|
||||
guard !loadingOrderSourcePeople else { return }
|
||||
loadingOrderSourcePeople = true
|
||||
defer { loadingOrderSourcePeople = false }
|
||||
guard !isLoadingOrderSourcePeople else { return }
|
||||
isLoadingOrderSourcePeople = true
|
||||
notifyStateChange()
|
||||
defer {
|
||||
isLoadingOrderSourcePeople = false
|
||||
notifyStateChange()
|
||||
}
|
||||
let storeId = AppStore.shared.currentStoreId
|
||||
let storeIdParam = storeId > 0 ? String(storeId) : nil
|
||||
do {
|
||||
@ -331,15 +382,7 @@ final class OrderListViewModel {
|
||||
}
|
||||
do {
|
||||
let response = try await api.referralOrders(saleUserId: person.key)
|
||||
let leads = response.list.map {
|
||||
OrderSourceLead(
|
||||
key: $0.displayReferralOrderNo,
|
||||
displayText: $0.displayReferralOrderNo,
|
||||
userMobile: $0.userMobile,
|
||||
createdAt: $0.createdAt
|
||||
)
|
||||
}
|
||||
orderSourceLeads[person.key] = leads
|
||||
orderSourceLeads[person.key] = response.list.map(OrderSourceMapping.lead(from:))
|
||||
notifyStateChange()
|
||||
} catch {
|
||||
onShowMessage?(error.localizedDescription)
|
||||
|
||||
Reference in New Issue
Block a user