// // LNOrderManager.swift // Lanu // // Created by OneeChan on 2025/11/6. // import Foundation protocol LNOrderManagerNotify { func onOrderInfoChanged(orderId: String) func onMyDiscountInfoChanged(info: LNOrderDiscountVO?) } extension LNOrderManagerNotify { func onOrderInfoChanged(orderId: String) { } func onMyDiscountInfoChanged(info: LNOrderDiscountVO?) { } } enum LNOrderErrorCode: Int { case NotEnoughMoney = 100018 case NotEnoughMoneyButCanExchange = 50002 } protocol LNOrderProtocol { var buyerUserNo: String { get } var refundApply: Bool { get } var status: LNOrderStatus { get } } extension LNOrderProtocol { var statusUI: (title: String, icon: UIImage) { let isCreator = buyerUserNo.isMyUid if refundApply { return if isCreator { (.init(key: "A00049"), .icImChatOrderPending) } else { (.init(key: "A00051"), .icImChatOrderRefunding) } } return switch status { case .created, .waitingForAccept: if isCreator { (.init(key: "A00053"), .icImChatOrderSuccess) } else { (.init(key: "A00055"), .icImChatOrderPending) } case .completed: (.init(key: "A00057"), .icImChatOrderComplete) case .refunded: (.init(key: "A00060"), .icImChatOrderRefunded) case .accepted: (.init(key: "A00062"), .icImChatOrderAccepted) case .rejected: (.init(key: "A00065"), .icImChatOrderCancel) case .servicing: (.init(key: "A00068"), .icImChatOrderProcessing) case .serviceDone: (.init(key: "A00070"), .icImChatOrderComplete) case .cancelled: (.init(key: "A00071"), .icImChatOrderCancel) } } } class LNOrderManager { static var shared = LNOrderManager() static let orderExtraMaxLength = 150 static let orderProtestMaxLength = 500 static let orderRefundMaxLength = 500 static let orderCommentMaxLength = 200 private var discount: LNOrderDiscountVO? func discountFor(_ price: Double) -> Double? { if let discount, discount.hasChance, discount.eligiblePrice == price { discount.discountRate } else { nil } } private init() { LNEventDeliver.addObserver(self) } } // MARK: 订单信息 extension LNOrderManager { func getList(size: Int, next: String? = nil, queue: DispatchQueue = .main, handler: @escaping ([LNOrderListItemVO]?, String?) -> Void) { LNHttpManager.shared.getOrderList(size: size, next: next ?? "") { res, err in queue.asyncIfNotGlobal { handler(res?.list, res?.next) } } } func getOrderDetail(orderId: String, queue: DispatchQueue = .main, handler: @escaping (LNOrderDetailResponse?) -> Void) { LNHttpManager.shared.getOrderDetail(orderId: orderId) { res, err in queue.asyncIfNotGlobal { handler(res) } } } func getRecordList(size: Int, next: String? = nil, queue: DispatchQueue = .main, handler: @escaping ([LNOrderRecordItemVO]?, String) -> Void) { LNHttpManager.shared.getOrderRecordList(size: size, next: next ?? "") { res, err in guard err == nil, let res else { queue.asyncIfNotGlobal { handler(nil, "") } return } queue.asyncIfNotGlobal { handler(res.list, res.next) } } } func getUnfinishedOrderWith(uid: String, size: Int, next: String?, queue: DispatchQueue = .main, handler: @escaping ([LNUnfinishedOrderVO]?, String?) -> Void) { LNHttpManager.shared.getUnfinishedOrderWith(uid: uid, size: size, next: next ?? "") { res, err in queue.asyncIfNotGlobal { handler(res?.list, res?.next) } } } func getLastOrder(queue: DispatchQueue = .main, handler: @escaping (LNLastOrderResponse?) -> Void) { LNHttpManager.shared.getLastOrder { res, err in queue.asyncIfNotGlobal { handler(res) } } } } // MARK: 订单操作 extension LNOrderManager { func createOrder(skillId: String, count: Int, remark: String, scene: LNOrderScene = .normal, queue: DispatchQueue = .main, handler: @escaping (String?) -> Void) { LNHttpManager.shared.createOrderBySkill(skillId: skillId, count: count, remark: remark, scene: scene) { [weak self] res, err in guard let self else { return } if let res, err == nil { LNPurchaseManager.shared.reloadWalletInfo() notifyOrderInfoChanged(orderId: res.orderNo) reloadMyOrderDiscountInfo() } queue.asyncIfNotGlobal { handler(res?.orderNo) } if case .serverError(let code, let err) = err { runOnMain { if code == LNOrderErrorCode.NotEnoughMoney.rawValue { let panel = LNPurchasePanel() panel.update(.coin) panel.popup() showToast(err) } else { showToast(err) } } } } } func cancelOrder(orderId: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.cancelOrder(orderId: orderId) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) reloadMyOrderDiscountInfo() LNPurchaseManager.shared.reloadWalletInfo() } else { showToast(err?.errorDesc) } } } } func finishOrder(orderId: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.finishOrder(orderId: orderId) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } func refundOrder(orderId: String, reason: String, attachments: [String], queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.refundOrder(orderId: orderId, reason: reason, attachments: attachments) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) reloadMyOrderDiscountInfo() } else { showToast(err?.errorDesc) } } } } func commentOrder(orderId: String, star: Double, comment: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.commentOrder( orderId: orderId, star: star, comment: comment) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } func rejectOrder(orderId: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.rejectOrder(orderId: orderId) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } func acceptOrder(orderId: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.acceptOrder(orderId: orderId) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } func startOrderService(orderId: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.startOrderService(orderId: orderId) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } func finishOrderService(orderId: String, queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) { LNHttpManager.shared.finishOrderService(orderId: orderId) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } func protestOrder(orderId: String, reason: String, queue: DispatchQueue = .main, attachments: [String], handler: @escaping (Bool) -> Void) { LNHttpManager.shared.protestOrder(orderId: orderId, reason: reason, attachments: attachments) { [weak self] err in guard let self else { return } queue.asyncIfNotGlobal { [weak self] in handler(err == nil) guard let self else { return } if err == nil { notifyOrderInfoChanged(orderId: orderId) } else { showToast(err?.errorDesc) } } } } } // MARK: 二维码 extension LNOrderManager { func createOrderQR(skillId: String, count: Int, type: LNOrderSource, queue: DispatchQueue = .main, handler: @escaping (String?) -> Void) { LNHttpManager.shared.createOrderQR(skillId: skillId, count: count, type: type) { [weak self] data, err in queue.asyncIfNotGlobal { handler(data?.qrCode) } if let err { showToast(err.errorDesc) } else if let self { reloadMyOrderDiscountInfo() } } } func getQRDetail(data: String, queue: DispatchQueue = .main, handler: @escaping (LNQRCodeDetailResponse?) -> Void) { LNHttpManager.shared.getQRDetail(data: data) { res, err in queue.asyncIfNotGlobal { handler(res) } if let err { showToast(err.errorDesc) } } } func createQRCodeOrder(data: String, count: Int, extra: String, scene: LNOrderScene = .normal, queue: DispatchQueue = .main, handler: @escaping (String?) -> Void) { LNHttpManager.shared.createQROrder(data: data, count: count, extra: extra, scene: scene) { [weak self] res, err in queue.asyncIfNotGlobal { handler(res?.orderNo) } if let err { showToast(err.errorDesc) } else if let self { LNPurchaseManager.shared.reloadWalletInfo() reloadMyOrderDiscountInfo() } } } } // MARK: 折扣 extension LNOrderManager { func reloadMyOrderDiscountInfo() { LNHttpManager.shared.fetchOrderDiscount { [weak self] info, err in guard let self else { return } if err != nil { return } discount = info notifyDiscountInfoChange(info: info) } } } extension LNOrderManager: LNAccountManagerNotify { func onUserLogin() { reloadMyOrderDiscountInfo() } } extension LNOrderManager { private func notifyOrderInfoChanged(orderId: String) { LNEventDeliver.notifyEvent { ($0 as? LNOrderManagerNotify)?.onOrderInfoChanged(orderId: orderId) } } private func notifyDiscountInfoChange(info: LNOrderDiscountVO?) { LNEventDeliver.notifyEvent { ($0 as? LNOrderManagerNotify)?.onMyDiscountInfoChanged(info: info) } } }