LNOrderManager.swift 14 KB

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