OrderSettlement.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. const db = require('../plugin/DataBase/db')
  2. const Redis = require('../plugin/DataBase/Redis')
  3. const { releaseUsageForOrder } = require('./CouponService')
  4. const { insertLedgerRecord } = require('./Lepao/CountLedger')
  5. const ORDER_STATE_PENDING = 0
  6. const ORDER_STATE_PROCESSING = 1
  7. const ORDER_STATE_COMPLETED = 2
  8. const ORDER_STATE_CLOSED = 3
  9. const ORDER_STATE_ERROR = 4
  10. function normalizeResult(result) {
  11. return Array.isArray(result) ? result[0] : result
  12. }
  13. async function closePendingOrder({ orderId, operatorUuid = null, payId = null, logger } = {}) {
  14. if (!orderId) return { closed: false, reason: 'missing_order_id' }
  15. const conn = await db.connect()
  16. try {
  17. await conn.beginTransaction()
  18. const [orderRows] = await conn.execute(
  19. 'SELECT orderId, state, goods_id, create_user FROM orders WHERE orderId = ? FOR UPDATE',
  20. [orderId]
  21. )
  22. const order = orderRows?.[0]
  23. if (!order) {
  24. await conn.rollback()
  25. return { closed: false, reason: 'not_found' }
  26. }
  27. if (Number(order.state) !== ORDER_STATE_PENDING) {
  28. await conn.rollback()
  29. return { closed: false, reason: 'state_changed', state: Number(order.state) }
  30. }
  31. const params = payId
  32. ? [ORDER_STATE_CLOSED, payId, orderId, ORDER_STATE_PENDING]
  33. : [ORDER_STATE_CLOSED, orderId, ORDER_STATE_PENDING]
  34. const sql = payId
  35. ? 'UPDATE orders SET state = ?, pay_id = ? WHERE orderId = ? AND state = ?'
  36. : 'UPDATE orders SET state = ? WHERE orderId = ? AND state = ?'
  37. const [closeRes] = await conn.execute(sql, params)
  38. if (!closeRes || closeRes.affectedRows !== 1) {
  39. await conn.rollback()
  40. return { closed: false, reason: 'update_failed' }
  41. }
  42. if (order.goods_id != null) {
  43. await conn.execute('UPDATE goods SET num = num + 1 WHERE id = ?', [order.goods_id])
  44. }
  45. await conn.commit()
  46. } catch (error) {
  47. try { await conn.rollback() } catch (_) { }
  48. logger?.error?.(`关闭订单失败 ${orderId}: ${error.stack || error}`)
  49. throw error
  50. }
  51. try {
  52. await releaseUsageForOrder(orderId)
  53. await Redis.del(`payData:${orderId}`)
  54. } catch (cleanupError) {
  55. logger?.warn?.(`关闭订单后清理资源失败 ${orderId}: ${cleanupError.stack || cleanupError}`)
  56. }
  57. logger?.info?.(`订单已关闭并返还库存,订单号:${orderId}${operatorUuid ? `,操作人:${operatorUuid}` : ''}`)
  58. return { closed: true }
  59. }
  60. async function completePaidOrder({ orderId, payType, payId, payTime = Date.now(), paidAmount = null, logger } = {}) {
  61. if (!orderId) return { completed: false, reason: 'missing_order_id' }
  62. const conn = await db.connect()
  63. try {
  64. await conn.beginTransaction()
  65. const [lockRows] = await conn.execute(
  66. 'SELECT orderId, state, price FROM orders WHERE orderId = ? FOR UPDATE',
  67. [orderId]
  68. )
  69. const locked = lockRows?.[0]
  70. if (!locked || Number(locked.state) !== ORDER_STATE_PENDING) {
  71. await conn.rollback()
  72. return {
  73. completed: false,
  74. reason: locked ? 'state_changed' : 'not_found',
  75. state: locked ? Number(locked.state) : null
  76. }
  77. }
  78. if (paidAmount !== null && Math.abs(Number(locked.price) - Number(paidAmount)) > 0.001) {
  79. await conn.rollback()
  80. logger?.error?.(`订单支付金额不匹配,订单号:${orderId},应付:${locked.price},实付:${paidAmount}`)
  81. return { completed: false, reason: 'amount_mismatch' }
  82. }
  83. const [markRows] = await conn.execute(
  84. 'UPDATE orders SET state = ?, pay_type = ?, pay_id = ?, pay_time = ? WHERE orderId = ? AND state = ?',
  85. [ORDER_STATE_PROCESSING, payType, payId, payTime, orderId, ORDER_STATE_PENDING]
  86. )
  87. if (!markRows || markRows.affectedRows !== 1) {
  88. await conn.rollback()
  89. return { completed: false, reason: 'state_changed' }
  90. }
  91. const [orderRows] = await conn.execute(
  92. `SELECT
  93. o.create_user,
  94. g.lepao_count,
  95. g.ic_count,
  96. g.vip
  97. FROM orders o
  98. LEFT JOIN goods g ON o.goods_id = g.id
  99. WHERE o.orderId = ?
  100. LIMIT 1`,
  101. [orderId]
  102. )
  103. const order = orderRows?.[0]
  104. if (!order || !order.create_user) {
  105. await conn.execute('UPDATE orders SET state = ? WHERE orderId = ?', [ORDER_STATE_ERROR, orderId])
  106. await conn.commit()
  107. return { completed: false, reason: 'bad_order_data' }
  108. }
  109. const addLepao = Number(order.lepao_count || 0)
  110. const addIc = Number(order.ic_count || 0)
  111. const [userRows] = await conn.execute(
  112. 'SELECT lepao_count FROM users WHERE uuid = ? FOR UPDATE',
  113. [order.create_user]
  114. )
  115. if (!userRows || userRows.length !== 1) {
  116. await conn.execute('UPDATE orders SET state = ? WHERE orderId = ?', [ORDER_STATE_ERROR, orderId])
  117. await conn.commit()
  118. return { completed: false, reason: 'user_not_found' }
  119. }
  120. const beforeLepao = Number(userRows[0].lepao_count || 0)
  121. const afterLepao = beforeLepao + addLepao
  122. const [updateUserRes] = await conn.execute(
  123. 'UPDATE users SET lepao_count = lepao_count + ?, ic_count = ic_count + ?, vip = ? WHERE uuid = ?',
  124. [addLepao, addIc, order.vip, order.create_user]
  125. )
  126. if (!updateUserRes || updateUserRes.affectedRows !== 1) {
  127. await conn.execute('UPDATE orders SET state = ? WHERE orderId = ?', [ORDER_STATE_ERROR, orderId])
  128. await conn.commit()
  129. return { completed: false, reason: 'update_user_failed' }
  130. }
  131. const [finishRes] = await conn.execute(
  132. 'UPDATE orders SET state = ? WHERE orderId = ? AND state = ?',
  133. [ORDER_STATE_COMPLETED, orderId, ORDER_STATE_PROCESSING]
  134. )
  135. if (!finishRes || finishRes.affectedRows !== 1) {
  136. await conn.rollback()
  137. return { completed: false, reason: 'finish_failed' }
  138. }
  139. if (addLepao !== 0) {
  140. await insertLedgerRecord({
  141. executor: conn,
  142. userUuid: order.create_user,
  143. delta: addLepao,
  144. balanceBefore: beforeLepao,
  145. balanceAfter: afterLepao,
  146. bizType: 'purchase',
  147. bizId: orderId,
  148. remark: `订单号:${orderId}`
  149. })
  150. }
  151. await conn.commit()
  152. try { await Redis.del(`payData:${orderId}`) } catch (_) { }
  153. logger?.info?.(`订单支付处理完成,订单号:${orderId}`)
  154. return { completed: true, userUuid: order.create_user, addLepao }
  155. } catch (error) {
  156. try { await conn.rollback() } catch (_) { }
  157. logger?.error?.(`完成订单失败 ${orderId}: ${error.stack || error}`)
  158. throw error
  159. }
  160. }
  161. module.exports = {
  162. ORDER_STATE_PENDING,
  163. ORDER_STATE_PROCESSING,
  164. ORDER_STATE_COMPLETED,
  165. ORDER_STATE_CLOSED,
  166. ORDER_STATE_ERROR,
  167. closePendingOrder,
  168. completePaidOrder,
  169. normalizeResult
  170. }