Browse Source

✨ feat: 增加赠送次数免审功能

Pchen0 1 month ago
parent
commit
f7fe46977b

+ 94 - 7
apis/Goods/SendCount.js

@@ -32,7 +32,7 @@ class SendCount extends API {
             await conn.beginTransaction()
             await conn.beginTransaction()
 
 
             const [senderRows] = await conn.execute(
             const [senderRows] = await conn.execute(
-                "SELECT id, username, lepao_count FROM users WHERE uuid = ?",
+                "SELECT id, username, lepao_count, COALESCE(send_count_auto_approve, 0) AS send_count_auto_approve FROM users WHERE uuid = ?",
                 [uuid]
                 [uuid]
             )
             )
             if (!senderRows || senderRows.length !== 1) {
             if (!senderRows || senderRows.length !== 1) {
@@ -62,9 +62,96 @@ class SendCount extends API {
                 return res.json({ ...BaseStdResponse.ERR, msg: "剩余乐跑次数不足,请购买后再赠送!" })
                 return res.json({ ...BaseStdResponse.ERR, msg: "剩余乐跑次数不足,请购买后再赠送!" })
             }
             }
 
 
+            const senderLepaoBefore = Number(senderRows[0].lepao_count || 0)
+            const autoApprove = Number(senderRows[0].send_count_auto_approve) === 1
+
+            if (autoApprove) {
+                const [recvRows] = await conn.execute(
+                    "SELECT uuid, lepao_count FROM users WHERE id = ? FOR UPDATE",
+                    [targetRows[0].id]
+                )
+                if (!recvRows || recvRows.length !== 1) {
+                    await conn.rollback()
+                    return res.json({ ...BaseStdResponse.ERR, msg: "未找到接收用户,请检查用户名是否正确!" })
+                }
+                const receiverUuid = recvRows[0].uuid
+                const beforeRecv = Number(recvRows[0].lepao_count || 0)
+
+                const [incResult] = await conn.execute(
+                    "UPDATE users SET lepao_count = lepao_count + ? WHERE id = ?",
+                    [count, targetRows[0].id]
+                )
+                if (!incResult || incResult.affectedRows !== 1) {
+                    await conn.rollback()
+                    return res.json({ ...BaseStdResponse.ERR, msg: "接收方入账失败,请稍后再试!" })
+                }
+
+                const [insertResult] = await conn.execute(
+                    `INSERT INTO lepao_send_count_request
+                    (sender_uuid, receiver_user_id, count, status, created_at, reviewed_at, reviewer_uuid)
+                    VALUES (?, ?, ?, 'approved', NOW(), NOW(), NULL)`,
+                    [uuid, targetRows[0].id, count]
+                )
+                if (!insertResult || insertResult.affectedRows !== 1) {
+                    await conn.rollback()
+                    return res.json({ ...BaseStdResponse.ERR, msg: "记录赠送失败,请稍后再试!" })
+                }
+
+                const requestId = insertResult.insertId
+                await insertLedgerRecord({
+                    executor: conn,
+                    userUuid: uuid,
+                    delta: -count,
+                    balanceBefore: senderLepaoBefore,
+                    balanceAfter: senderLepaoBefore - count,
+                    bizType: "gift_send_lock",
+                    bizId: `send_request:${requestId}`,
+                    remark: `向${username}赠送${count}次`
+                })
+                await insertLedgerRecord({
+                    executor: conn,
+                    userUuid: receiverUuid,
+                    delta: count,
+                    balanceBefore: beforeRecv,
+                    balanceAfter: beforeRecv + count,
+                    bizType: "gift_receive",
+                    bizId: `send_request:${requestId}`,
+                    operatorUuid: null,
+                    remark: `${senderRows[0].username}赠送${count}次`
+                })
+
+                await conn.commit()
+                const reviewTime = new Date().getTime()
+
+                Promise.resolve().then(async () => {
+                    try {
+                        const infoSql = `
+                            SELECT ru.email AS receiver_email, ru.username AS receiver_username
+                            FROM users ru
+                            WHERE ru.id = ?
+                        `
+                        const infoRows = await db.query(infoSql, [targetRows[0].id])
+                        if (!infoRows || infoRows.length !== 1 || !infoRows[0].receiver_email) {
+                            this.logger.warn(`[SendCountNotify][auto][requestId=${requestId}] 接收人邮箱为空,跳过通知`)
+                            return
+                        }
+                        await EmailTemplate.sendCountRequestApproved(infoRows[0].receiver_email, {
+                            requestId,
+                            senderUsername: senderRows[0].username,
+                            count,
+                            reviewTime
+                        })
+                    } catch (mailErr) {
+                        this.logger.error(`[SendCountNotify][auto][requestId=${requestId}] 接收人通知发送失败:${mailErr.message || "未知错误"}`)
+                    }
+                })
+
+                return res.json({ ...BaseStdResponse.OK, msg: "赠送成功,对方已到账" })
+            }
+
             const [insertResult] = await conn.execute(
             const [insertResult] = await conn.execute(
-                `INSERT INTO lepao_send_count_request 
-                (sender_uuid, receiver_user_id, count, status, created_at) 
+                `INSERT INTO lepao_send_count_request
+                (sender_uuid, receiver_user_id, count, status, created_at)
                 VALUES (?, ?, ?, 'pending', NOW())`,
                 VALUES (?, ?, ?, 'pending', NOW())`,
                 [uuid, targetRows[0].id, count]
                 [uuid, targetRows[0].id, count]
             )
             )
@@ -78,9 +165,9 @@ class SendCount extends API {
                 executor: conn,
                 executor: conn,
                 userUuid: uuid,
                 userUuid: uuid,
                 delta: -count,
                 delta: -count,
-                balanceBefore: Number(senderRows[0].lepao_count || 0),
-                balanceAfter: Number(senderRows[0].lepao_count || 0) - count,
-                bizType: 'gift_send_lock',
+                balanceBefore: senderLepaoBefore,
+                balanceAfter: senderLepaoBefore - count,
+                bizType: "gift_send_lock",
                 bizId: `send_request:${requestId}`,
                 bizId: `send_request:${requestId}`,
                 remark: `向${username}赠送${count}次`
                 remark: `向${username}赠送${count}次`
             })
             })
@@ -93,7 +180,7 @@ class SendCount extends API {
                     const adminSql = `
                     const adminSql = `
                         SELECT email
                         SELECT email
                         FROM users
                         FROM users
-                        WHERE email IS NOT NULL 
+                        WHERE email IS NOT NULL
                           AND email <> ''
                           AND email <> ''
                           AND (JSON_CONTAINS(permission, '"admin"') OR JSON_CONTAINS(permission, '"service"'))
                           AND (JSON_CONTAINS(permission, '"admin"') OR JSON_CONTAINS(permission, '"service"'))
                     `
                     `

+ 1 - 0
apis/User/Admin/GetUserList.js

@@ -59,6 +59,7 @@ class GetUserList extends API {
                 registTime,
                 registTime,
                 avatar,
                 avatar,
                 lepao_count,
                 lepao_count,
+                COALESCE(send_count_auto_approve, 0) AS send_count_auto_approve,
                 (
                 (
                     SELECT lh.type
                     SELECT lh.type
                     FROM login_history lh
                     FROM login_history lh

+ 51 - 0
apis/User/Admin/SetSendCountAutoApprove.js

@@ -0,0 +1,51 @@
+const API = require("../../../lib/API")
+const db = require("../../../plugin/DataBase/db")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class SetSendCountAutoApprove extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/User/SetSendCountAutoApprove")
+        this.setMethod("POST")
+    }
+
+    async onRequest(req, res) {
+        let { uuid, session, userid, send_count_auto_approve } = req.body
+        const flag = Number(send_count_auto_approve)
+
+        if ([uuid, session, userid].some(value => value === "" || value === null || value === undefined))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (![0, 1].includes(flag))
+            return res.json({ ...BaseStdResponse.ERR, msg: "参数错误" })
+
+        if (!await AccessControl.checkSession(uuid, session))
+            return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
+
+        const permission = await AccessControl.getPermission(uuid)
+        if (!permission.includes("admin") && !permission.includes("service"))
+            return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
+
+        const conn = await db.connect()
+        try {
+            const [r] = await conn.execute(
+                "UPDATE users SET send_count_auto_approve = ? WHERE uuid = ?",
+                [flag, userid]
+            )
+            if (!r || r.affectedRows !== 1) {
+                return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到用户或更新失败" })
+            }
+            return res.json({ ...BaseStdResponse.OK, msg: "已保存" })
+        } catch (err) {
+            this.logger.error(`设置赠送免审白名单失败: ${err.message || err}`)
+            return res.json({ ...BaseStdResponse.ERR, msg: "保存失败,请稍后再试" })
+        } finally {
+            if (conn?.connection && typeof conn.connection.release === "function" && typeof conn?.release === "function") {
+                conn.release()
+            }
+        }
+    }
+}
+
+module.exports.SetSendCountAutoApprove = SetSendCountAutoApprove