LNOrderManager.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. //
  2. // LNOrderManager.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/11/6.
  6. //
  7. import Foundation
  8. protocol LNOrderManagerNotify {
  9. func onOrderInfoChanged(orderId: String)
  10. func onMyDiscountInfoChanged(info: LNOrderDiscountVO?)
  11. }
  12. extension LNOrderManagerNotify {
  13. func onOrderInfoChanged(orderId: String) { }
  14. func onMyDiscountInfoChanged(info: LNOrderDiscountVO?) { }
  15. }
  16. enum LNOrderErrorCode: Int {
  17. case NotEnoughMoney = 100018
  18. }
  19. protocol LNOrderProtocol {
  20. var buyerUserNo: String { get }
  21. var refundApply: Bool { get }
  22. var status: LNOrderStatus { get }
  23. }
  24. extension LNOrderProtocol {
  25. var statusUI: (title: String, icon: UIImage) {
  26. let isCreator = buyerUserNo.isMyUid
  27. if refundApply {
  28. return if isCreator {
  29. (.init(key: "A00049"), .icImChatOrderPending)
  30. } else {
  31. (.init(key: "A00051"), .icImChatOrderRefunding)
  32. }
  33. }
  34. return switch status {
  35. case .created, .waitingForAccept:
  36. if isCreator {
  37. (.init(key: "A00053"), .icImChatOrderSuccess)
  38. } else {
  39. (.init(key: "A00055"), .icImChatOrderPending)
  40. }
  41. case .completed: (.init(key: "A00057"), .icImChatOrderComplete)
  42. case .refunded: (.init(key: "A00060"), .icImChatOrderRefunded)
  43. case .accepted: (.init(key: "A00062"), .icImChatOrderAccepted)
  44. case .rejected: (.init(key: "A00065"), .icImChatOrderCancel)
  45. case .servicing: (.init(key: "A00068"), .icImChatOrderProcessing)
  46. case .serviceDone: (.init(key: "A00070"), .icImChatOrderComplete)
  47. case .cancelled: (.init(key: "A00071"), .icImChatOrderCancel)
  48. }
  49. }
  50. }
  51. class LNOrderManager {
  52. static var shared = LNOrderManager()
  53. static let orderExtraMaxLength = 150
  54. static let orderProtestMaxLength = 500
  55. static let orderRefundMaxLength = 500
  56. static let orderCommentMaxLength = 200
  57. private var discount: LNOrderDiscountVO?
  58. func discountFor(_ price: Double) -> Double? {
  59. if let discount, discount.hasChance, discount.eligiblePrice == price {
  60. discount.discountRate
  61. } else {
  62. nil
  63. }
  64. }
  65. private init() {
  66. LNEventDeliver.addObserver(self)
  67. }
  68. }
  69. // MARK: 订单信息
  70. extension LNOrderManager {
  71. func getList(size: Int, next: String? = nil, queue: DispatchQueue = .main,
  72. handler: @escaping ([LNOrderListItemVO]?, String?) -> Void) {
  73. LNHttpManager.shared.getOrderList(size: size, next: next ?? "") { res, err in
  74. queue.asyncIfNotGlobal {
  75. handler(res?.list, res?.next)
  76. }
  77. }
  78. }
  79. func getOrderDetail(orderId: String, queue: DispatchQueue = .main,
  80. handler: @escaping (LNOrderDetailResponse?) -> Void) {
  81. LNHttpManager.shared.getOrderDetail(orderId: orderId) { res, err in
  82. queue.asyncIfNotGlobal {
  83. handler(res)
  84. }
  85. }
  86. }
  87. func getRecordList(size: Int, next: String? = nil, queue: DispatchQueue = .main,
  88. handler: @escaping ([LNOrderRecordItemVO]?, String) -> Void) {
  89. LNHttpManager.shared.getOrderRecordList(size: size, next: next ?? "") { res, err in
  90. guard err == nil, let res else {
  91. queue.asyncIfNotGlobal {
  92. handler(nil, "")
  93. }
  94. return
  95. }
  96. queue.asyncIfNotGlobal {
  97. handler(res.list, res.next)
  98. }
  99. }
  100. }
  101. func getUnfinishedOrderWith(uid: String, size: Int, next: String?,
  102. queue: DispatchQueue = .main,
  103. handler: @escaping ([LNUnfinishedOrderVO]?, String?) -> Void) {
  104. LNHttpManager.shared.getUnfinishedOrderWith(uid: uid, size: size, next: next ?? "") { res, err in
  105. queue.asyncIfNotGlobal {
  106. handler(res?.list, res?.next)
  107. }
  108. }
  109. }
  110. func getLastOrder(queue: DispatchQueue = .main, handler: @escaping (LNLastOrderResponse?) -> Void) {
  111. LNHttpManager.shared.getLastOrder { res, err in
  112. queue.asyncIfNotGlobal {
  113. handler(res)
  114. }
  115. }
  116. }
  117. }
  118. // MARK: 订单操作
  119. extension LNOrderManager {
  120. func createOrder(
  121. skillId: String, count: Int, remark: String,
  122. queue: DispatchQueue = .main,
  123. handler: @escaping (String?) -> Void)
  124. {
  125. LNHttpManager.shared.createOrderBySkill(skillId: skillId, count: count, remark: remark)
  126. { [weak self] res, err in
  127. guard let self else { return }
  128. if let res, err == nil {
  129. LNPurchaseManager.shared.reloadWalletInfo()
  130. notifyOrderInfoChanged(orderId: res.orderNo)
  131. reloadMyOrderDiscountInfo()
  132. }
  133. queue.asyncIfNotGlobal {
  134. handler(res?.orderNo)
  135. }
  136. if case .serverError(let code, let err) = err {
  137. DispatchQueue.main.async {
  138. if code == LNOrderErrorCode.NotEnoughMoney.rawValue {
  139. let panel = LNPurchasePanel()
  140. panel.update(.coin)
  141. panel.popup()
  142. showToast(err)
  143. } else {
  144. showToast(err)
  145. }
  146. }
  147. }
  148. }
  149. }
  150. func cancelOrder(orderId: String,
  151. queue: DispatchQueue = .main,
  152. handler: @escaping (Bool) -> Void) {
  153. LNHttpManager.shared.cancelOrder(orderId: orderId) { [weak self] err in
  154. guard let self else { return }
  155. queue.asyncIfNotGlobal { [weak self] in
  156. handler(err == nil)
  157. guard let self else { return }
  158. if err == nil {
  159. notifyOrderInfoChanged(orderId: orderId)
  160. reloadMyOrderDiscountInfo()
  161. } else {
  162. showToast(err?.errorDesc)
  163. }
  164. }
  165. }
  166. }
  167. func finishOrder(orderId: String, queue: DispatchQueue = .main,
  168. handler: @escaping (Bool) -> Void) {
  169. LNHttpManager.shared.finishOrder(orderId: orderId) { [weak self] err in
  170. guard let self else { return }
  171. queue.asyncIfNotGlobal { [weak self] in
  172. handler(err == nil)
  173. guard let self else { return }
  174. if err == nil {
  175. notifyOrderInfoChanged(orderId: orderId)
  176. } else {
  177. showToast(err?.errorDesc)
  178. }
  179. }
  180. }
  181. }
  182. func refundOrder(orderId: String, reason: String,
  183. attachments: [String], queue: DispatchQueue = .main,
  184. handler: @escaping (Bool) -> Void) {
  185. LNHttpManager.shared.refundOrder(orderId: orderId, reason: reason, attachments: attachments)
  186. { [weak self] err in
  187. guard let self else { return }
  188. queue.asyncIfNotGlobal { [weak self] in
  189. handler(err == nil)
  190. guard let self else { return }
  191. if err == nil {
  192. notifyOrderInfoChanged(orderId: orderId)
  193. reloadMyOrderDiscountInfo()
  194. } else {
  195. showToast(err?.errorDesc)
  196. }
  197. }
  198. }
  199. }
  200. func commentOrder(orderId: String, star: Double, comment: String,
  201. queue: DispatchQueue = .main,
  202. handler: @escaping (Bool) -> Void) {
  203. LNHttpManager.shared.commentOrder(
  204. orderId: orderId, star: star, comment: comment)
  205. { [weak self] err in
  206. guard let self else { return }
  207. queue.asyncIfNotGlobal { [weak self] in
  208. handler(err == nil)
  209. guard let self else { return }
  210. if err == nil {
  211. notifyOrderInfoChanged(orderId: orderId)
  212. } else {
  213. showToast(err?.errorDesc)
  214. }
  215. }
  216. }
  217. }
  218. func rejectOrder(orderId: String, queue: DispatchQueue = .main,
  219. handler: @escaping (Bool) -> Void) {
  220. LNHttpManager.shared.rejectOrder(orderId: orderId) { [weak self] err in
  221. guard let self else { return }
  222. queue.asyncIfNotGlobal { [weak self] in
  223. handler(err == nil)
  224. guard let self else { return }
  225. if err == nil {
  226. notifyOrderInfoChanged(orderId: orderId)
  227. } else {
  228. showToast(err?.errorDesc)
  229. }
  230. }
  231. }
  232. }
  233. func acceptOrder(orderId: String, queue: DispatchQueue = .main,
  234. handler: @escaping (Bool) -> Void) {
  235. LNHttpManager.shared.acceptOrder(orderId: orderId) { [weak self] err in
  236. guard let self else { return }
  237. queue.asyncIfNotGlobal { [weak self] in
  238. handler(err == nil)
  239. guard let self else { return }
  240. if err == nil {
  241. notifyOrderInfoChanged(orderId: orderId)
  242. } else {
  243. showToast(err?.errorDesc)
  244. }
  245. }
  246. }
  247. }
  248. func startOrderService(orderId: String, queue: DispatchQueue = .main,
  249. handler: @escaping (Bool) -> Void) {
  250. LNHttpManager.shared.startOrderService(orderId: orderId) { [weak self] err in
  251. guard let self else { return }
  252. queue.asyncIfNotGlobal { [weak self] in
  253. handler(err == nil)
  254. guard let self else { return }
  255. if err == nil {
  256. notifyOrderInfoChanged(orderId: orderId)
  257. } else {
  258. showToast(err?.errorDesc)
  259. }
  260. }
  261. }
  262. }
  263. func finishOrderService(orderId: String, queue: DispatchQueue = .main,
  264. handler: @escaping (Bool) -> Void) {
  265. LNHttpManager.shared.finishOrderService(orderId: orderId) { [weak self] err in
  266. guard let self else { return }
  267. queue.asyncIfNotGlobal { [weak self] in
  268. handler(err == nil)
  269. guard let self else { return }
  270. if err == nil {
  271. notifyOrderInfoChanged(orderId: orderId)
  272. } else {
  273. showToast(err?.errorDesc)
  274. }
  275. }
  276. }
  277. }
  278. func protestOrder(orderId: String, reason: String,
  279. queue: DispatchQueue = .main,
  280. attachments: [String], handler: @escaping (Bool) -> Void)
  281. {
  282. LNHttpManager.shared.protestOrder(orderId: orderId, reason: reason, attachments: attachments)
  283. { [weak self] err in
  284. guard let self else { return }
  285. queue.asyncIfNotGlobal { [weak self] in
  286. handler(err == nil)
  287. guard let self else { return }
  288. if err == nil {
  289. notifyOrderInfoChanged(orderId: orderId)
  290. } else {
  291. showToast(err?.errorDesc)
  292. }
  293. }
  294. }
  295. }
  296. }
  297. // MARK: 二维码
  298. extension LNOrderManager {
  299. func createOrderQR(skillId: String,
  300. count: Int, type: LNOrderSource,
  301. queue: DispatchQueue = .main,
  302. handler: @escaping (String?) -> Void) {
  303. LNHttpManager.shared.createOrderQR(skillId: skillId, count: count, type: type) { [weak self] data, err in
  304. queue.asyncIfNotGlobal {
  305. handler(data?.qrCode)
  306. }
  307. if let err {
  308. showToast(err.errorDesc)
  309. } else if let self {
  310. reloadMyOrderDiscountInfo()
  311. }
  312. }
  313. }
  314. func getQRDetail(data: String,
  315. queue: DispatchQueue = .main,
  316. handler: @escaping (LNQRCodeDetailResponse?) -> Void) {
  317. LNHttpManager.shared.getQRDetail(data: data) { res, err in
  318. queue.asyncIfNotGlobal {
  319. handler(res)
  320. }
  321. if let err {
  322. showToast(err.errorDesc)
  323. }
  324. }
  325. }
  326. func createQRCodeOrder(data: String, count: Int, extra: String,
  327. queue: DispatchQueue = .main,
  328. handler: @escaping (String?) -> Void) {
  329. LNHttpManager.shared.createQROrder(data: data, count: count, extra: extra) { [weak self] res, err in
  330. queue.asyncIfNotGlobal {
  331. handler(res?.orderNo)
  332. }
  333. if let err {
  334. showToast(err.errorDesc)
  335. } else if let self {
  336. reloadMyOrderDiscountInfo()
  337. }
  338. }
  339. }
  340. }
  341. // MARK: 折扣
  342. extension LNOrderManager {
  343. func reloadMyOrderDiscountInfo() {
  344. LNHttpManager.shared.fetchOrderDiscount { [weak self] info, err in
  345. guard let self else { return }
  346. if err != nil { return }
  347. discount = info
  348. notifyDiscountInfoChange(info: info)
  349. }
  350. }
  351. }
  352. extension LNOrderManager: LNAccountManagerNotify {
  353. func onUserLogin() {
  354. reloadMyOrderDiscountInfo()
  355. }
  356. }
  357. extension LNOrderManager {
  358. private func notifyOrderInfoChanged(orderId: String) {
  359. LNEventDeliver.notifyEvent {
  360. ($0 as? LNOrderManagerNotify)?.onOrderInfoChanged(orderId: orderId)
  361. }
  362. }
  363. private func notifyDiscountInfoChange(info: LNOrderDiscountVO?) {
  364. LNEventDeliver.notifyEvent {
  365. ($0 as? LNOrderManagerNotify)?.onMyDiscountInfoChanged(info: info)
  366. }
  367. }
  368. }