Browse Source

✨ feat: 新增赠送乐跑次数审核功能

Pchen0 3 weeks ago
parent
commit
8711c39a00

+ 83 - 0
apis/Goods/Admin/ApproveSendCountRequest.js

@@ -0,0 +1,83 @@
+const API = require("../../../lib/API")
+const db = require("../../../plugin/DataBase/db")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class ApproveSendCountRequest extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Goods/SendCountRequest/Approve")
+        this.setMethod("POST")
+    }
+
+    async onRequest(req, res) {
+        let { uuid, session, id } = req.body
+        id = Number(id)
+
+        if ([uuid, session, id].some(value => value === "" || value === null || value === undefined || Number.isNaN(id)))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (!Number.isInteger(id) || id <= 0)
+            return res.json({ ...BaseStdResponse.ERR, msg: "参数错误" })
+
+        if (!await AccessControl.checkSession(uuid, session))
+            return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
+
+        let permission = await AccessControl.getPermission(uuid)
+        if (!permission.includes("admin") && !permission.includes("service"))
+            return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
+
+        const conn = await db.connect()
+        try {
+            await conn.beginTransaction()
+
+            const [requestRows] = await conn.execute(
+                `SELECT id, receiver_user_id, count, status
+                 FROM lepao_send_count_request
+                 WHERE id = ?
+                 FOR UPDATE`,
+                [id]
+            )
+
+            if (!requestRows || requestRows.length !== 1) {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到赠送申请记录!" })
+            }
+
+            const request = requestRows[0]
+            if (request.status !== "pending") {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.ERR, msg: "该申请已审核,请刷新后重试!" })
+            }
+
+            const [incResult] = await conn.execute(
+                "UPDATE users SET lepao_count = lepao_count + ? WHERE id = ?",
+                [request.count, request.receiver_user_id]
+            )
+            if (!incResult || incResult.affectedRows !== 1) {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.ERR, msg: "接收用户不存在,审核通过失败!" })
+            }
+
+            const [updateResult] = await conn.execute(
+                `UPDATE lepao_send_count_request
+                 SET status = 'approved', reviewer_uuid = ?, reviewed_at = NOW(), reject_reason = NULL
+                 WHERE id = ?`,
+                [uuid, id]
+            )
+            if (!updateResult || updateResult.affectedRows !== 1) {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.ERR, msg: "更新审核状态失败,请稍后再试!" })
+            }
+
+            await conn.commit()
+            return res.json({ ...BaseStdResponse.OK, msg: "审核通过成功" })
+        } catch (err) {
+            try { await conn.rollback() } catch (_) { }
+            this.logger.error(`审核通过赠送申请失败!${err.message || "未知错误"}`)
+            return res.json({ ...BaseStdResponse.ERR, msg: "审核通过失败,请稍后再试!" })
+        }
+    }
+}
+
+module.exports.ApproveSendCountRequest = ApproveSendCountRequest

+ 97 - 0
apis/Goods/Admin/GetSendCountRequestList.js

@@ -0,0 +1,97 @@
+const API = require("../../../lib/API")
+const db = require("../../../plugin/DataBase/db")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class GetSendCountRequestList extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Goods/SendCountRequest/List")
+        this.setMethod("GET")
+    }
+
+    async onRequest(req, res) {
+        let { uuid, session, pagesize, current, status } = req.query
+
+        if ([uuid, session, pagesize, current].some(value => value === "" || value === null || value === undefined))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (isNaN(pagesize) || pagesize <= 0 || pagesize > 50 || isNaN(current) || current <= 0)
+            return res.json({ ...BaseStdResponse.ERR, msg: "参数错误" })
+
+        if (!await AccessControl.checkSession(uuid, session))
+            return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
+
+        let permission = await AccessControl.getPermission(uuid)
+        if (!permission.includes("admin") && !permission.includes("service"))
+            return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
+
+        const statusList = ["pending", "approved", "rejected"]
+        if (status && !statusList.includes(status))
+            return res.json({ ...BaseStdResponse.ERR, msg: "状态参数错误" })
+
+        const offset = (Number(current) - 1) * Number(pagesize)
+
+        let sql = `
+            SELECT
+                r.id,
+                r.sender_uuid,
+                su.username AS sender_username,
+                r.receiver_user_id,
+                ru.username AS receiver_username,
+                r.count,
+                r.status,
+                r.created_at,
+                r.reviewed_at,
+                r.reviewer_uuid,
+                rv.username AS reviewer_username,
+                r.reject_reason
+            FROM
+                lepao_send_count_request r
+            LEFT JOIN users su ON su.uuid = r.sender_uuid
+            LEFT JOIN users ru ON ru.id = r.receiver_user_id
+            LEFT JOIN users rv ON rv.uuid = r.reviewer_uuid
+            WHERE 1 = 1
+        `
+
+        let countSql = `
+            SELECT COUNT(*) AS total
+            FROM lepao_send_count_request r
+            WHERE 1 = 1
+        `
+
+        const params = []
+        const countParams = []
+
+        if (status) {
+            sql += ` AND r.status = ?`
+            countSql += ` AND r.status = ?`
+            params.push(status)
+            countParams.push(status)
+        }
+
+        sql += `
+            ORDER BY r.id DESC
+            LIMIT ? OFFSET ?
+        `
+        params.push(String(pagesize), String(offset))
+
+        const rows = await db.query(sql, params)
+        const countRows = await db.query(countSql, countParams)
+
+        if (!rows || !countRows)
+            return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "获取赠送审核列表失败!" })
+
+        res.json({
+            ...BaseStdResponse.OK,
+            data: rows,
+            pagination: {
+                current: Number(current),
+                pagesize: Number(pagesize),
+                total: countRows[0]?.total || 0
+            }
+        })
+    }
+}
+
+module.exports.GetSendCountRequestList = GetSendCountRequestList

+ 87 - 0
apis/Goods/Admin/RejectSendCountRequest.js

@@ -0,0 +1,87 @@
+const API = require("../../../lib/API")
+const db = require("../../../plugin/DataBase/db")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class RejectSendCountRequest extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Goods/SendCountRequest/Reject")
+        this.setMethod("POST")
+    }
+
+    async onRequest(req, res) {
+        let { uuid, session, id, reject_reason } = req.body
+        id = Number(id)
+        reject_reason = typeof reject_reason === "string" ? reject_reason.trim() : ""
+
+        if ([uuid, session, id].some(value => value === "" || value === null || value === undefined || Number.isNaN(id)))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (!Number.isInteger(id) || id <= 0)
+            return res.json({ ...BaseStdResponse.ERR, msg: "参数错误" })
+
+        if (reject_reason.length > 255)
+            return res.json({ ...BaseStdResponse.ERR, msg: "拒绝原因过长,请控制在255字以内!" })
+
+        if (!await AccessControl.checkSession(uuid, session))
+            return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
+
+        let permission = await AccessControl.getPermission(uuid)
+        if (!permission.includes("admin") && !permission.includes("service"))
+            return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
+
+        const conn = await db.connect()
+        try {
+            await conn.beginTransaction()
+
+            const [requestRows] = await conn.execute(
+                `SELECT id, sender_uuid, count, status
+                 FROM lepao_send_count_request
+                 WHERE id = ?
+                 FOR UPDATE`,
+                [id]
+            )
+
+            if (!requestRows || requestRows.length !== 1) {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到赠送申请记录!" })
+            }
+
+            const request = requestRows[0]
+            if (request.status !== "pending") {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.ERR, msg: "该申请已审核,请刷新后重试!" })
+            }
+
+            const [refundResult] = await conn.execute(
+                "UPDATE users SET lepao_count = lepao_count + ? WHERE uuid = ?",
+                [request.count, request.sender_uuid]
+            )
+            if (!refundResult || refundResult.affectedRows !== 1) {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.ERR, msg: "赠送人不存在,退回次数失败!" })
+            }
+
+            const [updateResult] = await conn.execute(
+                `UPDATE lepao_send_count_request
+                 SET status = 'rejected', reviewer_uuid = ?, reviewed_at = NOW(), reject_reason = ?
+                 WHERE id = ?`,
+                [uuid, reject_reason || null, id]
+            )
+            if (!updateResult || updateResult.affectedRows !== 1) {
+                await conn.rollback()
+                return res.json({ ...BaseStdResponse.ERR, msg: "更新审核状态失败,请稍后再试!" })
+            }
+
+            await conn.commit()
+            return res.json({ ...BaseStdResponse.OK, msg: "已拒绝该赠送申请" })
+        } catch (err) {
+            try { await conn.rollback() } catch (_) { }
+            this.logger.error(`拒绝赠送申请失败!${err.message || "未知错误"}`)
+            return res.json({ ...BaseStdResponse.ERR, msg: "拒绝申请失败,请稍后再试!" })
+        }
+    }
+}
+
+module.exports.RejectSendCountRequest = RejectSendCountRequest

+ 121 - 0
apis/Goods/GetMySendCountRequestList.js

@@ -0,0 +1,121 @@
+const API = require("../../lib/API")
+const db = require("../../plugin/DataBase/db")
+const AccessControl = require("../../lib/AccessControl")
+const { BaseStdResponse } = require("../../BaseStdResponse")
+
+class GetMySendCountRequestList extends API {
+    constructor() {
+        super()
+        this.setPath("/Goods/SendCountRequest/MyList")
+        this.setMethod("GET")
+    }
+
+    async onRequest(req, res) {
+        let { uuid, session, pagesize, current, direction, status } = req.query
+
+        if ([uuid, session, pagesize, current].some(value => value === "" || value === null || value === undefined))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (isNaN(pagesize) || pagesize <= 0 || pagesize > 50 || isNaN(current) || current <= 0)
+            return res.json({ ...BaseStdResponse.ERR, msg: "参数错误" })
+
+        if (!await AccessControl.checkSession(uuid, session))
+            return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
+
+        const directionList = ["sent", "received"]
+        if (direction && !directionList.includes(direction))
+            return res.json({ ...BaseStdResponse.ERR, msg: "direction参数错误" })
+
+        const statusList = ["pending", "approved", "rejected"]
+        if (status && !statusList.includes(status))
+            return res.json({ ...BaseStdResponse.ERR, msg: "status参数错误" })
+
+        const userRows = await db.query("SELECT id FROM users WHERE uuid = ?", [uuid])
+        if (!userRows || userRows.length !== 1)
+            return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "获取用户信息失败!" })
+
+        const userId = userRows[0].id
+        const offset = (Number(current) - 1) * Number(pagesize)
+
+        let sql = `
+            SELECT
+                r.id,
+                r.sender_uuid,
+                su.username AS sender_username,
+                su.avatar AS sender_avatar,
+                r.receiver_user_id,
+                ru.username AS receiver_username,
+                ru.avatar AS receiver_avatar,
+                CASE WHEN r.sender_uuid = ? THEN 'sent' ELSE 'received' END AS direction,
+                CASE WHEN r.sender_uuid = ? THEN ru.username ELSE su.username END AS counterparty_username,
+                CASE WHEN r.sender_uuid = ? THEN ru.avatar ELSE su.avatar END AS counterparty_avatar,
+                r.count,
+                r.status,
+                r.created_at,
+                r.reviewed_at,
+                r.reject_reason
+            FROM
+                lepao_send_count_request r
+            LEFT JOIN users su ON su.uuid = r.sender_uuid
+            LEFT JOIN users ru ON ru.id = r.receiver_user_id
+            WHERE
+                (r.sender_uuid = ? OR r.receiver_user_id = ?)
+        `
+
+        let countSql = `
+            SELECT COUNT(*) AS total
+            FROM
+                lepao_send_count_request r
+            WHERE
+                (r.sender_uuid = ? OR r.receiver_user_id = ?)
+        `
+
+        const params = [uuid, uuid, uuid, uuid, userId]
+        const countParams = [uuid, userId]
+
+        if (direction === "sent") {
+            sql += ` AND r.sender_uuid = ?`
+            countSql += ` AND r.sender_uuid = ?`
+            params.push(uuid)
+            countParams.push(uuid)
+        }
+
+        if (direction === "received") {
+            sql += ` AND r.receiver_user_id = ?`
+            countSql += ` AND r.receiver_user_id = ?`
+            params.push(userId)
+            countParams.push(userId)
+        }
+
+        if (status) {
+            sql += ` AND r.status = ?`
+            countSql += ` AND r.status = ?`
+            params.push(status)
+            countParams.push(status)
+        }
+
+        sql += `
+            ORDER BY r.id DESC
+            LIMIT ? OFFSET ?
+        `
+        params.push(String(pagesize), String(offset))
+
+        const rows = await db.query(sql, params)
+        const countRows = await db.query(countSql, countParams)
+
+        if (!rows || !countRows)
+            return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "获取赠送记录失败!" })
+
+        res.json({
+            ...BaseStdResponse.OK,
+            data: rows,
+            pagination: {
+                current: Number(current),
+                pagesize: Number(pagesize),
+                total: countRows[0]?.total || 0
+            }
+        })
+    }
+}
+
+module.exports.GetMySendCountRequestList = GetMySendCountRequestList

+ 8 - 6
apis/Goods/SendCount.js

@@ -60,17 +60,19 @@ class SendCount extends API {
                 return res.json({ ...BaseStdResponse.ERR, msg: "剩余乐跑次数不足,请购买后再赠送!" })
             }
 
-            const [incResult] = await conn.execute(
-                "UPDATE users SET lepao_count = lepao_count + ? WHERE id = ?",
-                [count, targetRows[0].id]
+            const [insertResult] = await conn.execute(
+                `INSERT INTO lepao_send_count_request 
+                (sender_uuid, receiver_user_id, count, status, created_at) 
+                VALUES (?, ?, ?, 'pending', NOW())`,
+                [uuid, targetRows[0].id, count]
             )
-            if (incResult.affectedRows !== 1) {
+            if (!insertResult || insertResult.affectedRows !== 1) {
                 await conn.rollback()
-                return res.json({ ...BaseStdResponse.ERR, msg: "赠送次数失败,请稍后再试!" })
+                return res.json({ ...BaseStdResponse.ERR, msg: "提交赠送审核失败,请稍后再试!" })
             }
 
             await conn.commit()
-            return res.json({ ...BaseStdResponse.OK, msg: "赠送成功" })
+            return res.json({ ...BaseStdResponse.OK, msg: "已提交审核,审核通过后接收方将到账" })
 
         } catch (err) {
             try { await conn.rollback() } catch (_) { }

+ 2 - 2
apis/Public/GetAppVersion.js

@@ -13,8 +13,8 @@ class GetAppVersion extends API {
         res.json({
             ...BaseStdResponse.OK,
             data: {
-                version: '2.0',
-                msg: '\n更新内容:\n重构乐跑逻辑 优化乐跑任务调度功能'
+                version: '2.1',
+                msg: '\n更新内容:\优化乐跑次数赠送功能'
             }
         })
     }