LNOrderManager.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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(skillId: String, count: Int,
  121. remark: String, scene: LNOrderScene = .normal,
  122. queue: DispatchQueue = .main,
  123. handler: @escaping (String?) -> Void)
  124. {
  125. LNHttpManager.shared.createOrderBySkill(skillId: skillId, count: count, remark: remark, scene: scene)
  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. LNPurchaseManager.shared.reloadWalletInfo()
  162. } else {
  163. showToast(err?.errorDesc)
  164. }
  165. }
  166. }
  167. }
  168. func finishOrder(orderId: String, queue: DispatchQueue = .main,
  169. handler: @escaping (Bool) -> Void) {
  170. LNHttpManager.shared.finishOrder(orderId: orderId) { [weak self] err in
  171. guard let self else { return }
  172. queue.asyncIfNotGlobal { [weak self] in
  173. handler(err == nil)
  174. guard let self else { return }
  175. if err == nil {
  176. notifyOrderInfoChanged(orderId: orderId)
  177. } else {
  178. showToast(err?.errorDesc)
  179. }
  180. }
  181. }
  182. }
  183. func refundOrder(orderId: String, reason: String,
  184. attachments: [String], queue: DispatchQueue = .main,
  185. handler: @escaping (Bool) -> Void) {
  186. LNHttpManager.shared.refundOrder(orderId: orderId, reason: reason, attachments: attachments)
  187. { [weak self] err in
  188. guard let self else { return }
  189. queue.asyncIfNotGlobal { [weak self] in
  190. handler(err == nil)
  191. guard let self else { return }
  192. if err == nil {
  193. notifyOrderInfoChanged(orderId: orderId)
  194. reloadMyOrderDiscountInfo()
  195. } else {
  196. showToast(err?.errorDesc)
  197. }
  198. }
  199. }
  200. }
  201. func commentOrder(orderId: String, star: Double, comment: String,
  202. queue: DispatchQueue = .main,
  203. handler: @escaping (Bool) -> Void) {
  204. LNHttpManager.shared.commentOrder(
  205. orderId: orderId, star: star, comment: comment)
  206. { [weak self] err in
  207. guard let self else { return }
  208. queue.asyncIfNotGlobal { [weak self] in
  209. handler(err == nil)
  210. guard let self else { return }
  211. if err == nil {
  212. notifyOrderInfoChanged(orderId: orderId)
  213. } else {
  214. showToast(err?.errorDesc)
  215. }
  216. }
  217. }
  218. }
  219. func rejectOrder(orderId: String, queue: DispatchQueue = .main,
  220. handler: @escaping (Bool) -> Void) {
  221. LNHttpManager.shared.rejectOrder(orderId: orderId) { [weak self] err in
  222. guard let self else { return }
  223. queue.asyncIfNotGlobal { [weak self] in
  224. handler(err == nil)
  225. guard let self else { return }
  226. if err == nil {
  227. notifyOrderInfoChanged(orderId: orderId)
  228. } else {
  229. showToast(err?.errorDesc)
  230. }
  231. }
  232. }
  233. }
  234. func acceptOrder(orderId: String, queue: DispatchQueue = .main,
  235. handler: @escaping (Bool) -> Void) {
  236. LNHttpManager.shared.acceptOrder(orderId: orderId) { [weak self] err in
  237. guard let self else { return }
  238. queue.asyncIfNotGlobal { [weak self] in
  239. handler(err == nil)
  240. guard let self else { return }
  241. if err == nil {
  242. notifyOrderInfoChanged(orderId: orderId)
  243. } else {
  244. showToast(err?.errorDesc)
  245. }
  246. }
  247. }
  248. }
  249. func startOrderService(orderId: String, queue: DispatchQueue = .main,
  250. handler: @escaping (Bool) -> Void) {
  251. LNHttpManager.shared.startOrderService(orderId: orderId) { [weak self] err in
  252. guard let self else { return }
  253. queue.asyncIfNotGlobal { [weak self] in
  254. handler(err == nil)
  255. guard let self else { return }
  256. if err == nil {
  257. notifyOrderInfoChanged(orderId: orderId)
  258. } else {
  259. showToast(err?.errorDesc)
  260. }
  261. }
  262. }
  263. }
  264. func finishOrderService(orderId: String, queue: DispatchQueue = .main,
  265. handler: @escaping (Bool) -> Void) {
  266. LNHttpManager.shared.finishOrderService(orderId: orderId) { [weak self] err in
  267. guard let self else { return }
  268. queue.asyncIfNotGlobal { [weak self] in
  269. handler(err == nil)
  270. guard let self else { return }
  271. if err == nil {
  272. notifyOrderInfoChanged(orderId: orderId)
  273. } else {
  274. showToast(err?.errorDesc)
  275. }
  276. }
  277. }
  278. }
  279. func protestOrder(orderId: String, reason: String,
  280. queue: DispatchQueue = .main,
  281. attachments: [String], handler: @escaping (Bool) -> Void)
  282. {
  283. LNHttpManager.shared.protestOrder(orderId: orderId, reason: reason, attachments: attachments)
  284. { [weak self] err in
  285. guard let self else { return }
  286. queue.asyncIfNotGlobal { [weak self] in
  287. handler(err == nil)
  288. guard let self else { return }
  289. if err == nil {
  290. notifyOrderInfoChanged(orderId: orderId)
  291. } else {
  292. showToast(err?.errorDesc)
  293. }
  294. }
  295. }
  296. }
  297. }
  298. // MARK: 二维码
  299. extension LNOrderManager {
  300. func createOrderQR(skillId: String,
  301. count: Int, type: LNOrderSource,
  302. queue: DispatchQueue = .main,
  303. handler: @escaping (String?) -> Void) {
  304. LNHttpManager.shared.createOrderQR(skillId: skillId, count: count, type: type) { [weak self] data, err in
  305. queue.asyncIfNotGlobal {
  306. handler(data?.qrCode)
  307. }
  308. if let err {
  309. showToast(err.errorDesc)
  310. } else if let self {
  311. reloadMyOrderDiscountInfo()
  312. }
  313. }
  314. }
  315. func getQRDetail(data: String,
  316. queue: DispatchQueue = .main,
  317. handler: @escaping (LNQRCodeDetailResponse?) -> Void) {
  318. LNHttpManager.shared.getQRDetail(data: data) { res, err in
  319. queue.asyncIfNotGlobal {
  320. handler(res)
  321. }
  322. if let err {
  323. showToast(err.errorDesc)
  324. }
  325. }
  326. }
  327. func createQRCodeOrder(data: String, count: Int,
  328. extra: String, scene: LNOrderScene = .normal,
  329. queue: DispatchQueue = .main,
  330. handler: @escaping (String?) -> Void) {
  331. LNHttpManager.shared.createQROrder(data: data, count: count, extra: extra, scene: scene) { [weak self] res, err in
  332. queue.asyncIfNotGlobal {
  333. handler(res?.orderNo)
  334. }
  335. if let err {
  336. showToast(err.errorDesc)
  337. } else if let self {
  338. LNPurchaseManager.shared.reloadWalletInfo()
  339. reloadMyOrderDiscountInfo()
  340. }
  341. }
  342. }
  343. }
  344. // MARK: 折扣
  345. extension LNOrderManager {
  346. func reloadMyOrderDiscountInfo() {
  347. LNHttpManager.shared.fetchOrderDiscount { [weak self] info, err in
  348. guard let self else { return }
  349. if err != nil { return }
  350. discount = info
  351. notifyDiscountInfoChange(info: info)
  352. }
  353. }
  354. }
  355. extension LNOrderManager: LNAccountManagerNotify {
  356. func onUserLogin() {
  357. reloadMyOrderDiscountInfo()
  358. }
  359. }
  360. extension LNOrderManager {
  361. private func notifyOrderInfoChanged(orderId: String) {
  362. LNEventDeliver.notifyEvent {
  363. ($0 as? LNOrderManagerNotify)?.onOrderInfoChanged(orderId: orderId)
  364. }
  365. }
  366. private func notifyDiscountInfoChange(info: LNOrderDiscountVO?) {
  367. LNEventDeliver.notifyEvent {
  368. ($0 as? LNOrderManagerNotify)?.onMyDiscountInfoChanged(info: info)
  369. }
  370. }
  371. }