ApproveSendCountRequest.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. const API = require("../../../lib/API")
  2. const db = require("../../../plugin/DataBase/db")
  3. const AccessControl = require("../../../lib/AccessControl")
  4. const { BaseStdResponse } = require("../../../BaseStdResponse")
  5. const EmailTemplate = require("../../../plugin/Email/emailTemplate")
  6. const { insertLedgerRecord } = require("../../../lib/Lepao/CountLedger")
  7. class ApproveSendCountRequest extends API {
  8. constructor() {
  9. super()
  10. this.setPath("/Admin/Goods/SendCountRequest/Approve")
  11. this.setMethod("POST")
  12. }
  13. async onRequest(req, res) {
  14. let { uuid, session, id } = req.body
  15. id = Number(id)
  16. if ([uuid, session, id].some(value => value === "" || value === null || value === undefined || Number.isNaN(id)))
  17. return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
  18. if (!Number.isInteger(id) || id <= 0)
  19. return res.json({ ...BaseStdResponse.ERR, msg: "参数错误" })
  20. if (!await AccessControl.checkSession(uuid, session))
  21. return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
  22. let permission = await AccessControl.getPermission(uuid)
  23. if (!permission.includes("admin") && !permission.includes("service"))
  24. return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
  25. const conn = await db.connect()
  26. try {
  27. await conn.beginTransaction()
  28. const [requestRows] = await conn.execute(
  29. `SELECT id, sender_uuid, receiver_user_id, count, status
  30. FROM lepao_send_count_request
  31. WHERE id = ?
  32. FOR UPDATE`,
  33. [id]
  34. )
  35. if (!requestRows || requestRows.length !== 1) {
  36. await conn.rollback()
  37. return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到赠送申请记录!" })
  38. }
  39. const request = requestRows[0]
  40. if (request.status !== "pending") {
  41. await conn.rollback()
  42. return res.json({ ...BaseStdResponse.ERR, msg: "该申请已审核,请刷新后重试!" })
  43. }
  44. const [senderRows] = await conn.execute(
  45. "SELECT username FROM users WHERE uuid = ?",
  46. [request.sender_uuid]
  47. )
  48. const senderUsername = senderRows && senderRows[0] ? senderRows[0].username : "未知用户"
  49. const [receiverRows] = await conn.execute(
  50. "SELECT uuid, lepao_count FROM users WHERE id = ? FOR UPDATE",
  51. [request.receiver_user_id]
  52. )
  53. if (!receiverRows || receiverRows.length !== 1) {
  54. await conn.rollback()
  55. return res.json({ ...BaseStdResponse.ERR, msg: "接收用户不存在,审核通过失败!" })
  56. }
  57. const receiverUuid = receiverRows[0].uuid
  58. const beforeCount = Number(receiverRows[0].lepao_count || 0)
  59. const [incResult] = await conn.execute(
  60. "UPDATE users SET lepao_count = lepao_count + ? WHERE id = ?",
  61. [request.count, request.receiver_user_id]
  62. )
  63. if (!incResult || incResult.affectedRows !== 1) {
  64. await conn.rollback()
  65. return res.json({ ...BaseStdResponse.ERR, msg: "接收用户不存在,审核通过失败!" })
  66. }
  67. const [updateResult] = await conn.execute(
  68. `UPDATE lepao_send_count_request
  69. SET status = 'approved', reviewer_uuid = ?, reviewed_at = NOW(), reject_reason = NULL
  70. WHERE id = ?`,
  71. [uuid, id]
  72. )
  73. if (!updateResult || updateResult.affectedRows !== 1) {
  74. await conn.rollback()
  75. return res.json({ ...BaseStdResponse.ERR, msg: "更新审核状态失败,请稍后再试!" })
  76. }
  77. await insertLedgerRecord({
  78. executor: conn,
  79. userUuid: receiverUuid,
  80. delta: Number(request.count || 0),
  81. balanceBefore: beforeCount,
  82. balanceAfter: beforeCount + Number(request.count || 0),
  83. bizType: 'gift_receive',
  84. bizId: `send_request:${id}`,
  85. operatorUuid: uuid,
  86. remark: `${senderUsername}赠送${request.count}次`
  87. })
  88. await conn.commit()
  89. const requestId = request.id
  90. const reviewTime = new Date().getTime()
  91. // 非阻塞通知接收人,不影响审核结果
  92. Promise.resolve().then(async () => {
  93. try {
  94. const infoSql = `
  95. SELECT
  96. ru.email AS receiver_email,
  97. ru.username AS receiver_username,
  98. su.username AS sender_username
  99. FROM users ru
  100. LEFT JOIN users su ON su.uuid = ?
  101. WHERE ru.id = ?
  102. `
  103. const infoRows = await db.query(infoSql, [request.sender_uuid, request.receiver_user_id])
  104. if (!infoRows || infoRows.length !== 1 || !infoRows[0].receiver_email) {
  105. this.logger.warn(`[SendCountNotify][approve][requestId=${requestId}] 接收人邮箱为空或用户不存在,跳过通知`)
  106. return
  107. }
  108. await EmailTemplate.sendCountRequestApproved(infoRows[0].receiver_email, {
  109. requestId,
  110. senderUsername: infoRows[0].sender_username || "未知用户",
  111. count: request.count,
  112. reviewTime
  113. })
  114. } catch (mailErr) {
  115. this.logger.error(`[SendCountNotify][approve][requestId=${requestId}] 接收人通知发送失败:${mailErr.message || "未知错误"}`)
  116. }
  117. })
  118. return res.json({ ...BaseStdResponse.OK, msg: "审核通过成功" })
  119. } catch (err) {
  120. try { await conn.rollback() } catch (_) { }
  121. this.logger.error(`审核通过赠送申请失败!${err.message || "未知错误"}`)
  122. return res.json({ ...BaseStdResponse.ERR, msg: "审核通过失败,请稍后再试!" })
  123. } finally {
  124. if (conn?.connection && typeof conn.connection.release === 'function' && typeof conn?.release === 'function') {
  125. conn.release()
  126. }
  127. }
  128. }
  129. }
  130. module.exports.ApproveSendCountRequest = ApproveSendCountRequest