Browse Source

✨ feat: 增加权限管理

Pchen0 1 month ago
parent
commit
b52a887f55

+ 1 - 2
apis/Goods/Admin/ApproveSendCountRequest.js

@@ -25,8 +25,7 @@ class ApproveSendCountRequest extends API {
         if (!await AccessControl.checkSession(uuid, session))
         if (!await AccessControl.checkSession(uuid, session))
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
 
 
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['action.goods.reviewSendCount']))
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
 
 
         const conn = await db.connect()
         const conn = await db.connect()

+ 1 - 2
apis/Goods/Admin/GetSendCountRequestList.js

@@ -22,8 +22,7 @@ class GetSendCountRequestList extends API {
         if (!await AccessControl.checkSession(uuid, session))
         if (!await AccessControl.checkSession(uuid, session))
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
 
 
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['page.admin.goods.sendCountRequestList']))
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
 
 
         const statusList = ["pending", "approved", "rejected"]
         const statusList = ["pending", "approved", "rejected"]

+ 1 - 2
apis/Goods/Admin/RejectSendCountRequest.js

@@ -29,8 +29,7 @@ class RejectSendCountRequest extends API {
         if (!await AccessControl.checkSession(uuid, session))
         if (!await AccessControl.checkSession(uuid, session))
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
 
 
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['action.goods.reviewSendCount']))
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
 
 
         const conn = await db.connect()
         const conn = await db.connect()

+ 1 - 2
apis/Kefu/Order/Admin/GetOrderDetail.js

@@ -25,8 +25,7 @@ class GetOrderDetail extends API {
             })
             })
 
 
         // 检查权限
         // 检查权限
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['page.admin.service.orderList']))
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })

+ 1 - 2
apis/Kefu/Order/Admin/GetOrderList.js

@@ -41,8 +41,7 @@ class GetOrderList extends API {
             })
             })
 
 
         // 检查权限
         // 检查权限
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['page.admin.service.orderList']))
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })

+ 1 - 2
apis/Kefu/Order/Admin/ReplyOrder.js

@@ -27,8 +27,7 @@ class ReplyOrder extends API {
             })
             })
 
 
         // 检查权限
         // 检查权限
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['action.service.replyOrder']))
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })

+ 1 - 2
apis/Lepao/Account/Admin/ChangeLepaoCount.js

@@ -39,8 +39,7 @@ class ChangeLepaoCount extends API {
             })
             })
 
 
         // 检查权限
         // 检查权限
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['action.user.changeCount']))
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })

+ 13 - 7
apis/Lepao/Account/Admin/GetAccountList.js

@@ -12,7 +12,7 @@ class GetAccountList extends API {
     }
     }
 
 
     async onRequest(req, res) {
     async onRequest(req, res) {
-        let { uuid, session, email, area, user_uuid, username, student_num, state, auto_time, queryTime, bind_code, bot_account, pagesize, current } = req.query
+        let { uuid, session, email, area, user_uuid, username, student_num, notes, state, auto_time, queryTime, bind_code, bot_account, pagesize, current } = req.query
 
 
         if ([uuid, session, pagesize, current].some(value => value === '' || value === null || value === undefined))
         if ([uuid, session, pagesize, current].some(value => value === '' || value === null || value === undefined))
             return res.json({
             return res.json({
@@ -41,8 +41,7 @@ class GetAccountList extends API {
             })
             })
 
 
         // 检查权限
         // 检查权限
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['page.admin.lepaoAccount']))
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })
@@ -151,6 +150,13 @@ class GetAccountList extends API {
             countParams.push(`%${username}%`)
             countParams.push(`%${username}%`)
         }
         }
 
 
+        if (notes) {
+            sql += ` AND l.notes LIKE ?`
+            countSql += ` AND l.notes LIKE ?`
+            params.push(`%${notes}%`)
+            countParams.push(`%${notes}%`)
+        }
+
         if (bind_code) {
         if (bind_code) {
             sql += ` AND f.bind_code = ?`
             sql += ` AND f.bind_code = ?`
             countSql += ` AND f.bind_code = ?`
             countSql += ` AND f.bind_code = ?`
@@ -166,10 +172,10 @@ class GetAccountList extends API {
         }
         }
 
 
         if (auto_time !== 0) {
         if (auto_time !== 0) {
-            sql += ` AND l.auto_time = ?`
-            countSql += ` AND l.auto_time = ?`
-            params.push(auto_time)
-            countParams.push(auto_time)
+            sql += ` AND (l.auto_time = ? OR l.auto_time = -1 AND l.today_auto_time = ?)`
+            countSql += ` AND (l.auto_time = ? OR l.auto_time = -1 AND l.today_auto_time = ?)`
+            params.push(auto_time, auto_time)
+            countParams.push(auto_time, auto_time)
         }
         }
 
 
         if (state !== -1) {
         if (state !== -1) {

+ 1 - 2
apis/Lepao/Account/Admin/UpdateAccountInfo.js

@@ -26,8 +26,7 @@ class UpdateAccountInfo extends API {
             })
             })
         }
         }
 
 
-        const permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes('admin') && !permission.includes('service')) {
+        if (!await AccessControl.canAccess(uuid, ['action.lepao.admin.updateAccount'])) {
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })

+ 1 - 1
apis/Lepao/Account/DeleteAccount.js

@@ -37,7 +37,7 @@ class DeleteAccount extends API {
         let source = BindAuditSource.USER_API
         let source = BindAuditSource.USER_API
         if (selectRows[0].create_user !== uuid) {
         if (selectRows[0].create_user !== uuid) {
             let permission = await AccessControl.getPermission(uuid)
             let permission = await AccessControl.getPermission(uuid)
-            if (!permission.includes("admin") && !permission.includes("service"))
+            if (!await AccessControl.canAccess(uuid, ['action.lepao.deleteAccount']))
                 return res.json({
                 return res.json({
                     ...BaseStdResponse.ERR,
                     ...BaseStdResponse.ERR,
                     msg: '解绑账号失败!未找到账户信息'
                     msg: '解绑账号失败!未找到账户信息'

+ 12 - 5
apis/Lepao/Account/GetAccount.js

@@ -12,7 +12,7 @@ class GetAccount extends API {
     }
     }
 
 
     async onRequest(req, res) {
     async onRequest(req, res) {
-        let { uuid, session, email, area, username, student_num, state, auto_time, queryTime, pagesize, current } = req.query
+        let { uuid, session, email, area, username, student_num, notes, state, auto_time, queryTime, pagesize, current } = req.query
 
 
         if ([uuid, session, pagesize, current].some(value => value === '' || value === null || value === undefined))
         if ([uuid, session, pagesize, current].some(value => value === '' || value === null || value === undefined))
             return res.json({
             return res.json({
@@ -118,11 +118,18 @@ class GetAccount extends API {
             countParams.push(`%${username}%`)
             countParams.push(`%${username}%`)
         }
         }
 
 
+        if (notes) {
+            sql += ` AND l.notes LIKE ?`
+            countSql += ` AND l.notes LIKE ?`
+            params.push(`%${notes}%`)
+            countParams.push(`%${notes}%`)
+        }
+
         if (auto_time !== 0) {
         if (auto_time !== 0) {
-            sql += ` AND l.auto_time = ?`
-            countSql += ` AND l.auto_time = ?`
-            params.push(auto_time)
-            countParams.push(auto_time)
+            sql += ` AND (l.auto_time = ? OR l.auto_time = -1 AND l.today_auto_time = ?)`
+            countSql += ` AND (l.auto_time = ? OR l.auto_time = -1 AND l.today_auto_time = ?)`
+            params.push(auto_time, auto_time)
+            countParams.push(auto_time, auto_time)
         }
         }
 
 
         if (state !== -1) {
         if (state !== -1) {

+ 1 - 2
apis/Lepao/ChangeAutoRun.js

@@ -33,8 +33,7 @@ class ChangeAutoRun extends API {
             })
             })
 
 
         if (selectRows[0].create_user !== uuid) {
         if (selectRows[0].create_user !== uuid) {
-            let permission = await AccessControl.getPermission(uuid)
-            if (!permission.includes("admin") && !permission.includes("service"))
+            if (!await AccessControl.canAccess(uuid, ['action.lepao.changeAutoRun']))
                 return res.json({
                 return res.json({
                     ...BaseStdResponse.ERR,
                     ...BaseStdResponse.ERR,
                     msg: '切换自动乐跑状态失败!未找到账户信息'
                     msg: '切换自动乐跑状态失败!未找到账户信息'

+ 1 - 2
apis/Lepao/SingleRun.js

@@ -63,8 +63,7 @@ class SingleRun extends API {
                 })
                 })
 
 
             if (selectRows[0].create_user !== uuid) {
             if (selectRows[0].create_user !== uuid) {
-                let permission = await AccessControl.getPermission(uuid)
-                if (!permission.includes("admin") && !permission.includes("service"))
+                if (!await AccessControl.canAccess(uuid, ['action.lepao.singleRun']))
                     return res.json({
                     return res.json({
                         ...BaseStdResponse.ERR,
                         ...BaseStdResponse.ERR,
                         msg: '发起乐跑失败!未找到账户信息'
                         msg: '发起乐跑失败!未找到账户信息'

+ 85 - 0
apis/Order/Admin/GetOrderDetail.js

@@ -0,0 +1,85 @@
+const API = require("../../../lib/API")
+const db = require("../../../plugin/DataBase/db")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class GetOrderDetail extends API {
+    constructor() {
+        super()
+
+        this.setPath('/Admin/Order/Detail')
+        this.setMethod('get')
+    }
+
+    async onRequest(req, res) {
+        const { uuid, session, orderId } = req.query
+
+        if (!uuid || !session || !orderId) {
+            return res.json({
+                ...BaseStdResponse.MISSING_PARAMETER
+            })
+        }
+
+        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("product")) {
+            return res.json({
+                ...BaseStdResponse.PERMISSION_DENIED
+            })
+        }
+
+        const sql = `
+            SELECT
+                o.orderId,
+                o.create_time,
+                o.pay_time,
+                o.price,
+                o.state,
+                o.pay_id,
+                o.pay_type,
+                o.goods_id,
+                o.create_user,
+                g.name,
+                g.content,
+                g.icon,
+                g.isHot,
+                g.description,
+                g.category,
+                g.features,
+                g.lepao_count,
+                g.ic_count,
+                g.vip,
+                u.username,
+                u.avatar,
+                u.email AS user_email
+            FROM
+                orders o
+            LEFT JOIN
+                goods g ON o.goods_id = g.id
+            LEFT JOIN
+                users u ON o.create_user = u.uuid
+            WHERE
+                o.orderId = ?
+        `
+        const rows = await db.query(sql, [orderId])
+
+        if (!rows || rows.length !== 1) {
+            return res.json({
+                ...BaseStdResponse.ERR,
+                msg: '订单不存在'
+            })
+        }
+
+        res.json({
+            ...BaseStdResponse.OK,
+            data: rows[0]
+        })
+    }
+}
+
+module.exports.GetOrderDetail = GetOrderDetail

+ 177 - 0
apis/Order/Admin/GetOrderList.js

@@ -0,0 +1,177 @@
+const API = require("../../../lib/API")
+const db = require("../../../plugin/DataBase/db")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class GetOrderList extends API {
+    constructor() {
+        super()
+
+        this.setPath('/Admin/Order/List')
+        this.setMethod('get')
+    }
+
+    async onRequest(req, res) {
+        let {
+            uuid,
+            session,
+            orderId,
+            username,
+            user_email,
+            goods_name,
+            state,
+            pay_type,
+            queryTime,
+            pagesize,
+            current
+        } = 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) {
+            return res.json({
+                ...BaseStdResponse.ERR,
+                msg: '参数错误'
+            })
+        }
+
+        if (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("product"))
+            return res.json({
+                ...BaseStdResponse.PERMISSION_DENIED
+            })
+
+        const offset = (current - 1) * pagesize
+
+        let sql = `
+            SELECT
+                o.orderId,
+                o.state,
+                o.create_time,
+                o.pay_time,
+                o.price,
+                o.pay_type,
+                o.pay_id,
+                o.goods_id,
+                g.name AS goods_name,
+                u.username,
+                u.avatar,
+                u.email AS user_email,
+                o.create_user
+            FROM
+                orders o
+            LEFT JOIN
+                goods g ON o.goods_id = g.id
+            LEFT JOIN
+                users u ON o.create_user = u.uuid
+            WHERE
+                1 = 1
+        `
+
+        let countSql = `
+            SELECT COUNT(*) AS total
+            FROM
+                orders o
+            LEFT JOIN
+                goods g ON o.goods_id = g.id
+            LEFT JOIN
+                users u ON o.create_user = u.uuid
+            WHERE
+                1 = 1
+        `
+
+        let params = []
+        let countParams = []
+
+        if (orderId) {
+            sql += ` AND o.orderId LIKE ?`
+            countSql += ` AND o.orderId LIKE ?`
+            params.push(`%${orderId}%`)
+            countParams.push(`%${orderId}%`)
+        }
+
+        if (username) {
+            sql += ` AND u.username LIKE ?`
+            countSql += ` AND u.username LIKE ?`
+            params.push(`%${username}%`)
+            countParams.push(`%${username}%`)
+        }
+
+        if (user_email) {
+            sql += ` AND u.email LIKE ?`
+            countSql += ` AND u.email LIKE ?`
+            params.push(`%${user_email}%`)
+            countParams.push(`%${user_email}%`)
+        }
+
+        if (goods_name) {
+            sql += ` AND g.name LIKE ?`
+            countSql += ` AND g.name LIKE ?`
+            params.push(`%${goods_name}%`)
+            countParams.push(`%${goods_name}%`)
+        }
+
+        if (state !== undefined && state !== '' && state !== '-1' && Number(state) !== -1) {
+            sql += ` AND o.state = ?`
+            countSql += ` AND o.state = ?`
+            params.push(Number(state))
+            countParams.push(Number(state))
+        }
+
+        if (pay_type) {
+            sql += ` AND o.pay_type = ?`
+            countSql += ` AND o.pay_type = ?`
+            params.push(pay_type)
+            countParams.push(pay_type)
+        }
+
+        if (Array.isArray(queryTime) && queryTime.length === 2) {
+            sql += ` AND o.create_time >= ? AND o.create_time < ?`
+            countSql += ` AND o.create_time >= ? AND o.create_time < ?`
+            params.push(queryTime[0], queryTime[1])
+            countParams.push(queryTime[0], queryTime[1])
+        }
+
+        sql += `
+            ORDER BY o.create_time DESC
+            LIMIT ? OFFSET ?
+        `
+        params.push(String(pagesize), String(offset))
+
+        let rows = await db.query(sql, params)
+        let countResult = await db.query(countSql, countParams)
+
+        if (!rows || !countResult)
+            return res.json({
+                ...BaseStdResponse.MISSING_FILE,
+                msg: '获取订单数据失败!'
+            })
+
+        res.json({
+            ...BaseStdResponse.OK,
+            data: rows,
+            pagination: {
+                current,
+                pagesize,
+                total: countResult[0].total
+            }
+        })
+    }
+}
+
+module.exports.GetOrderList = GetOrderList

+ 22 - 0
apis/Permission/Admin/GetPermissionPoints.js

@@ -0,0 +1,22 @@
+const API = require("../../../lib/API")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class GetPermissionPoints extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Permission/Points")
+        this.setMethod("GET")
+        this.setPermissionCode("action.user.permissionManage")
+    }
+
+    async onRequest(req, res) {
+        const points = await AccessControl.getPermissionPoints()
+        return res.json({
+            ...BaseStdResponse.OK,
+            data: points
+        })
+    }
+}
+
+module.exports.GetPermissionPoints = GetPermissionPoints

+ 22 - 0
apis/Permission/Admin/GetResourceRules.js

@@ -0,0 +1,22 @@
+const API = require("../../../lib/API")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class GetResourceRules extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Permission/Resources")
+        this.setMethod("GET")
+        this.setPermissionCode("action.user.permissionManage")
+    }
+
+    async onRequest(req, res) {
+        const rules = await AccessControl.getResourceRules()
+        return res.json({
+            ...BaseStdResponse.OK,
+            data: rules
+        })
+    }
+}
+
+module.exports.GetResourceRules = GetResourceRules

+ 48 - 0
apis/Permission/Admin/GetUserPermissions.js

@@ -0,0 +1,48 @@
+const API = require("../../../lib/API")
+const AccessControl = require("../../../lib/AccessControl")
+const db = require("../../../plugin/DataBase/db")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class GetUserPermissions extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Permission/User")
+        this.setMethod("GET")
+        this.setPermissionCode("action.user.permissionManage")
+    }
+
+    async onRequest(req, res) {
+        const { userid } = req.query
+
+        if ([userid].some(value => value === "" || value === null || value === undefined))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        const rows = await db.query(
+            "SELECT uuid, username, permission FROM users WHERE uuid = ? LIMIT 1",
+            [userid]
+        )
+
+        if (!rows || rows.length !== 1)
+            return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到用户" })
+
+        const directPermissionCodes = await AccessControl.getUserDirectPermissionCodes(userid)
+        const deniedBasicPermissionCodes = await AccessControl.getUserDeniedBasicPermissionCodes(userid)
+        const effectivePermissionCodes = await AccessControl.getUserPermissionCodes(userid)
+
+        return res.json({
+            ...BaseStdResponse.OK,
+            data: {
+                user: {
+                    uuid: rows[0].uuid,
+                    username: rows[0].username,
+                    roles: AccessControl.parseArray(rows[0].permission)
+                },
+                directPermissionCodes,
+                deniedBasicPermissionCodes,
+                effectivePermissionCodes
+            }
+        })
+    }
+}
+
+module.exports.GetUserPermissions = GetUserPermissions

+ 46 - 0
apis/Permission/Admin/SetUserPermissions.js

@@ -0,0 +1,46 @@
+const API = require("../../../lib/API")
+const AccessControl = require("../../../lib/AccessControl")
+const db = require("../../../plugin/DataBase/db")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class SetUserPermissions extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Permission/User")
+        this.setMethod("POST")
+        this.setPermissionCode("action.user.permissionManage")
+    }
+
+    async onRequest(req, res) {
+        const { userid, permissionCodes, deniedBasicPermissionCodes } = req.body
+
+        if ([userid, permissionCodes].some(value => value === "" || value === null || value === undefined))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (!Array.isArray(permissionCodes))
+            return res.json({ ...BaseStdResponse.ERR, msg: "权限列表格式错误" })
+
+        if (deniedBasicPermissionCodes !== undefined && deniedBasicPermissionCodes !== null && !Array.isArray(deniedBasicPermissionCodes))
+            return res.json({ ...BaseStdResponse.ERR, msg: "基础权限关闭列表格式错误" })
+
+        const rows = await db.query("SELECT uuid FROM users WHERE uuid = ? LIMIT 1", [userid])
+        if (!rows || rows.length !== 1)
+            return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到用户" })
+
+        try {
+            await AccessControl.setUserPermissionCodes(userid, permissionCodes)
+            if (Array.isArray(deniedBasicPermissionCodes))
+                await AccessControl.setUserDeniedBasicPermissionCodes(userid, deniedBasicPermissionCodes)
+            const effectivePermissionCodes = await AccessControl.getUserPermissionCodes(userid)
+            return res.json({
+                ...BaseStdResponse.OK,
+                msg: "权限已保存",
+                data: { effectivePermissionCodes }
+            })
+        } catch (error) {
+            return res.json({ ...BaseStdResponse.ERR, msg: error.message || "保存权限失败" })
+        }
+    }
+}
+
+module.exports.SetUserPermissions = SetUserPermissions

+ 34 - 0
apis/Permission/Admin/UpdateResourceRule.js

@@ -0,0 +1,34 @@
+const API = require("../../../lib/API")
+const AccessControl = require("../../../lib/AccessControl")
+const { BaseStdResponse } = require("../../../BaseStdResponse")
+
+class UpdateResourceRule extends API {
+    constructor() {
+        super()
+        this.setPath("/Admin/Permission/Resource")
+        this.setMethod("POST")
+        this.setPermissionCode("action.user.permissionManage")
+    }
+
+    async onRequest(req, res) {
+        const { id, required_codes, enabled } = req.body
+
+        if ([id, required_codes].some(value => value === "" || value === null || value === undefined))
+            return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+        if (!Array.isArray(required_codes))
+            return res.json({ ...BaseStdResponse.ERR, msg: "权限列表格式错误" })
+
+        try {
+            const ok = await AccessControl.updateResourceRule({ id, required_codes, enabled })
+            if (!ok)
+                return res.json({ ...BaseStdResponse.MISSING_FILE, msg: "未找到权限规则" })
+
+            return res.json({ ...BaseStdResponse.OK, msg: "权限规则已保存" })
+        } catch (error) {
+            return res.json({ ...BaseStdResponse.ERR, msg: error.message || "保存权限规则失败" })
+        }
+    }
+}
+
+module.exports.UpdateResourceRule = UpdateResourceRule

+ 2 - 2
apis/Public/GetAppVersion.js

@@ -13,8 +13,8 @@ class GetAppVersion extends API {
         res.json({
         res.json({
             ...BaseStdResponse.OK,
             ...BaseStdResponse.OK,
             data: {
             data: {
-                version: '2.5',
-                msg: '\n更新内容:新增QQ/微信双绑定与统一登录功能'
+                version: '2.6',
+                msg: '\n更新内容:修复已知问题,优化用户体验'
             }
             }
         })
         })
     }
     }

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

@@ -41,8 +41,7 @@ class GetUserList extends API {
             })
             })
 
 
         // 检查权限
         // 检查权限
-        let permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['page.admin.userList']))
             return res.json({
             return res.json({
                 ...BaseStdResponse.PERMISSION_DENIED
                 ...BaseStdResponse.PERMISSION_DENIED
             })
             })

+ 1 - 2
apis/User/Admin/SetSendCountAutoApprove.js

@@ -23,8 +23,7 @@ class SetSendCountAutoApprove extends API {
         if (!await AccessControl.checkSession(uuid, session))
         if (!await AccessControl.checkSession(uuid, session))
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
 
 
-        const permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['action.user.setSendCountAutoApprove']))
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
 
 
         const conn = await db.connect()
         const conn = await db.connect()

+ 1 - 2
apis/User/Admin/SetUserBan.js

@@ -23,8 +23,7 @@ class SetUserBan extends API {
         if (!await AccessControl.checkSession(uuid, session))
         if (!await AccessControl.checkSession(uuid, session))
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
             return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
 
 
-        const permission = await AccessControl.getPermission(uuid)
-        if (!permission.includes("admin") && !permission.includes("service"))
+        if (!await AccessControl.canAccess(uuid, ['action.user.ban']))
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
             return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
 
 
         if (userid === uuid)
         if (userid === uuid)

+ 8 - 1
apis/User/GetPermissions.js

@@ -36,10 +36,17 @@ class GetPermissions extends API {
         }
         }
 
 
         let permission = await AccessControl.getPermission(uuid);
         let permission = await AccessControl.getPermission(uuid);
+        let permissionCodes = await AccessControl.getUserPermissionCodes(uuid);
+        let permissionPoints = await AccessControl.getPermissionPoints();
 
 
         res.json({
         res.json({
             ...BaseStdResponse.OK,
             ...BaseStdResponse.OK,
-            roles: permission
+            roles: permission,
+            data: {
+                roles: permission,
+                permissionCodes,
+                permissionPoints
+            }
         });
         });
     }
     }
 }
 }

+ 2 - 0
apis/User/GetUserInfo.js

@@ -41,12 +41,14 @@ class GetRepoList extends API {
 
 
         const userSession = await Redis.get(`userSession:${uuid}`)
         const userSession = await Redis.get(`userSession:${uuid}`)
         const bindings = await getUserSocialBindings(uuid)
         const bindings = await getUserSocialBindings(uuid)
+        const permissionCodes = await AccessControl.getUserPermissionCodes(uuid)
 
 
         res.json({
         res.json({
             ...BaseStdResponse.OK,
             ...BaseStdResponse.OK,
             data: {
             data: {
                 ...rows[0],
                 ...rows[0],
                 session: userSession,
                 session: userSession,
+                permissionCodes,
                 socialBindings: toSocialBindingSummary(bindings),
                 socialBindings: toSocialBindingSummary(bindings),
                 boundTypes: bindings.map(binding => binding.social_type)
                 boundTypes: bindings.map(binding => binding.social_type)
             }
             }

+ 41 - 1
lib/API.js

@@ -6,6 +6,7 @@ const fs = require('fs')
 const forge = require('node-forge')
 const forge = require('node-forge')
 const crypto = require('crypto')
 const crypto = require('crypto')
 const { BaseStdResponse } = require("../BaseStdResponse")
 const { BaseStdResponse } = require("../BaseStdResponse")
+const AccessControl = require('./AccessControl')
 
 
 class API {
 class API {
     constructor() {
     constructor() {
@@ -14,6 +15,7 @@ class API {
         this.path = ''
         this.path = ''
         this.method = 'get'
         this.method = 'get'
         this.encrypt = true
         this.encrypt = true
+        this.permissionCodes = []
 
 
         this.privateKey = fs.readFileSync(path.join(__dirname, '../keys/private_key.pem'), 'utf8')
         this.privateKey = fs.readFileSync(path.join(__dirname, '../keys/private_key.pem'), 'utf8')
 
 
@@ -33,6 +35,14 @@ class API {
         this.method = method.toLowerCase()
         this.method = method.toLowerCase()
     }
     }
 
 
+    setPermissionCode(code) {
+        this.permissionCodes = code ? [code] : []
+    }
+
+    setPermissionCodes(codes) {
+        this.permissionCodes = Array.isArray(codes) ? codes.filter(Boolean) : []
+    }
+
     getRouter() {
     getRouter() {
         return this.router
         return this.router
     }
     }
@@ -115,8 +125,38 @@ class API {
         return encryptedData
         return encryptedData
     }
     }
 
 
+    async checkPermission(req, res, next) {
+        try {
+            const payload = this.method === 'get' ? req.query : req.body
+            const requiredCodes = this.permissionCodes.length > 0
+                ? this.permissionCodes
+                : await AccessControl.getResourceRequiredCodes({
+                    method: this.method,
+                    path: this.path
+                })
+
+            if (!requiredCodes || requiredCodes.length === 0)
+                return next()
+
+            const { uuid, session } = payload || {}
+            if ([uuid, session].some(value => value === '' || value === null || value === undefined))
+                return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
+
+            if (!await AccessControl.checkSession(uuid, session))
+                return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
+
+            if (!await AccessControl.canAccess(uuid, requiredCodes))
+                return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
+
+            return next()
+        } catch (err) {
+            this.logger.error(`权限校验失败: ${err.stack || err}`)
+            return res.json({ ...BaseStdResponse.ERR, msg: '权限校验失败,请稍后再试' })
+        }
+    }
+
     setupRoute() {
     setupRoute() {
-        this.router[this.method](this.path, this.decryptPayload.bind(this), async (req, res) => {
+        this.router[this.method](this.path, this.decryptPayload.bind(this), this.checkPermission.bind(this), async (req, res) => {
             // 拦截返回值统一加密
             // 拦截返回值统一加密
             const originalJson = res.json.bind(res)
             const originalJson = res.json.bind(res)
             res.json = (data) => {
             res.json = (data) => {

+ 311 - 1
lib/AccessControl.js

@@ -1,7 +1,137 @@
 const db = require('../plugin/DataBase/db')
 const db = require('../plugin/DataBase/db')
 const Redis = require('../plugin/DataBase/Redis')
 const Redis = require('../plugin/DataBase/Redis')
+const {
+    DEFAULT_PERMISSION_POINTS,
+    DEFAULT_PERMISSION_RESOURCE_RULES,
+    DEFAULT_BASIC_USER_PERMISSION_CODES,
+    LEGACY_ROLE_PERMISSION_MAP
+} = require('./PermissionCatalog')
 
 
 class AccessControl {
 class AccessControl {
+    constructor() {
+        this.schemaReady = false
+    }
+
+    parseArray(value) {
+        if (Array.isArray(value)) return value
+        if (!value) return []
+        if (typeof value !== 'string') return []
+        try {
+            const parsed = JSON.parse(value)
+            return Array.isArray(parsed) ? parsed : []
+        } catch (_) {
+            return []
+        }
+    }
+
+    normalizeCodes(codes) {
+        if (!codes) return []
+        const list = Array.isArray(codes) ? codes : [codes]
+        return [...new Set(list.map(code => String(code || '').trim()).filter(Boolean))]
+    }
+
+    async ensurePermissionSchema() {
+        if (this.schemaReady) return
+
+        await db.query(`
+            CREATE TABLE IF NOT EXISTS permission_points (
+                id INT NOT NULL AUTO_INCREMENT,
+                code VARCHAR(120) NOT NULL,
+                name VARCHAR(120) NOT NULL,
+                category VARCHAR(40) NOT NULL DEFAULT 'action',
+                scope_type VARCHAR(40) NOT NULL DEFAULT 'action',
+                page_route_name VARCHAR(120) DEFAULT NULL,
+                enabled TINYINT NOT NULL DEFAULT 1,
+                remark VARCHAR(255) DEFAULT '',
+                created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                PRIMARY KEY (id),
+                UNIQUE KEY uniq_permission_points_code (code)
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
+        `)
+
+        await db.query(`
+            CREATE TABLE IF NOT EXISTS user_permission_points (
+                id INT NOT NULL AUTO_INCREMENT,
+                user_uuid VARCHAR(64) NOT NULL,
+                permission_code VARCHAR(120) NOT NULL,
+                created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                PRIMARY KEY (id),
+                UNIQUE KEY uniq_user_permission (user_uuid, permission_code),
+                KEY idx_user_permission_user_uuid (user_uuid),
+                KEY idx_user_permission_code (permission_code)
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
+        `)
+
+        await db.query(`
+            CREATE TABLE IF NOT EXISTS user_basic_permission_denials (
+                id INT NOT NULL AUTO_INCREMENT,
+                user_uuid VARCHAR(64) NOT NULL,
+                permission_code VARCHAR(120) NOT NULL,
+                created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                PRIMARY KEY (id),
+                UNIQUE KEY uniq_user_basic_denial (user_uuid, permission_code),
+                KEY idx_user_basic_denial_user_uuid (user_uuid)
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
+        `)
+
+        await db.query(`
+            CREATE TABLE IF NOT EXISTS permission_resource_rules (
+                id INT NOT NULL AUTO_INCREMENT,
+                resource_type VARCHAR(40) NOT NULL,
+                resource_key VARCHAR(180) NOT NULL,
+                api_method VARCHAR(16) DEFAULT NULL,
+                api_path VARCHAR(180) DEFAULT NULL,
+                required_codes TEXT NOT NULL,
+                enabled TINYINT NOT NULL DEFAULT 1,
+                remark VARCHAR(255) DEFAULT '',
+                created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                PRIMARY KEY (id),
+                UNIQUE KEY uniq_permission_resource (resource_type, resource_key),
+                KEY idx_permission_resource_api (api_method, api_path)
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
+        `)
+
+        for (const point of DEFAULT_PERMISSION_POINTS) {
+            await db.query(
+                `INSERT INTO permission_points
+                (code, name, category, scope_type, page_route_name, enabled, remark)
+                VALUES (?, ?, ?, ?, ?, 1, ?)
+                ON DUPLICATE KEY UPDATE
+                    name = VALUES(name),
+                    category = VALUES(category),
+                    scope_type = VALUES(scope_type),
+                    page_route_name = VALUES(page_route_name),
+                    remark = VALUES(remark)`,
+                [point.code, point.name, point.category, point.scope_type, point.page_route_name || null, point.remark || '']
+            )
+        }
+
+        for (const rule of DEFAULT_PERMISSION_RESOURCE_RULES) {
+            const requiredCodes = JSON.stringify(this.normalizeCodes(rule.required_codes))
+            await db.query(
+                `INSERT INTO permission_resource_rules
+                (resource_type, resource_key, api_method, api_path, required_codes, enabled, remark)
+                VALUES (?, ?, ?, ?, ?, 1, ?)
+                ON DUPLICATE KEY UPDATE
+                    api_method = VALUES(api_method),
+                    api_path = VALUES(api_path),
+                    remark = VALUES(remark)`,
+                [
+                    rule.resource_type,
+                    rule.resource_key,
+                    rule.api_method || null,
+                    rule.api_path || null,
+                    requiredCodes,
+                    rule.remark || ''
+                ]
+            )
+        }
+
+        this.schemaReady = true
+    }
+
     async checkSession(uuid, session) {
     async checkSession(uuid, session) {
         return (await Redis.get(`userSession:${uuid}`)) === session
         return (await Redis.get(`userSession:${uuid}`)) === session
     }
     }
@@ -20,7 +150,187 @@ class AccessControl {
         const sql = 'SELECT permission FROM users WHERE uuid = ?'
         const sql = 'SELECT permission FROM users WHERE uuid = ?'
         const rows = await db.query(sql, [uuid])
         const rows = await db.query(sql, [uuid])
 
 
-        return rows[0]?.permission || []
+        return this.parseArray(rows?.[0]?.permission)
+    }
+
+    isSuperPermission(permission) {
+        return this.parseArray(permission).includes('admin')
+    }
+
+    async isSuperAdmin(uuid) {
+        const permission = await this.getPermission(uuid)
+        return this.isSuperPermission(permission)
+    }
+
+    async getPermissionPoints() {
+        await this.ensurePermissionSchema()
+        const rows = await db.query(`
+            SELECT id, code, name, category, scope_type, page_route_name, enabled, remark
+            FROM permission_points
+            ORDER BY category, id
+        `)
+        return rows || []
+    }
+
+    async getUserDirectPermissionCodes(uuid) {
+        await this.ensurePermissionSchema()
+        const rows = await db.query(
+            `SELECT permission_code FROM user_permission_points WHERE user_uuid = ? ORDER BY permission_code`,
+            [uuid]
+        )
+        return (rows || []).map(row => row.permission_code)
+    }
+
+    async getUserDeniedBasicPermissionCodes(uuid) {
+        await this.ensurePermissionSchema()
+        const rows = await db.query(
+            `SELECT permission_code FROM user_basic_permission_denials WHERE user_uuid = ? ORDER BY permission_code`,
+            [uuid]
+        )
+        const denied = (rows || []).map(row => row.permission_code)
+        const basicSet = new Set(DEFAULT_BASIC_USER_PERMISSION_CODES)
+        return this.normalizeCodes(denied.filter(code => basicSet.has(code)))
+    }
+
+    getEnabledBasicPermissionCodes(deniedBasicCodes = []) {
+        const deniedSet = new Set(this.normalizeCodes(deniedBasicCodes))
+        return DEFAULT_BASIC_USER_PERMISSION_CODES.filter(code => !deniedSet.has(code))
+    }
+
+    async getUserPermissionCodes(uuid) {
+        await this.ensurePermissionSchema()
+        const legacyRoles = await this.getPermission(uuid)
+        const directCodes = await this.getUserDirectPermissionCodes(uuid)
+        const deniedBasicCodes = await this.getUserDeniedBasicPermissionCodes(uuid)
+        const roleCodes = legacyRoles.flatMap(role => LEGACY_ROLE_PERMISSION_MAP[role] || [])
+
+        return this.normalizeCodes([
+            ...this.getEnabledBasicPermissionCodes(deniedBasicCodes),
+            ...legacyRoles,
+            ...roleCodes,
+            ...directCodes
+        ])
+    }
+
+    async setUserPermissionCodes(uuid, codes) {
+        await this.ensurePermissionSchema()
+        const permissionCodes = this.normalizeCodes(codes)
+        const points = await this.getPermissionPoints()
+        const validCodes = new Set(points.map(point => point.code))
+        const invalidCodes = permissionCodes.filter(code => !validCodes.has(code))
+
+        if (invalidCodes.length > 0)
+            throw new Error(`存在无效权限点:${invalidCodes.join(', ')}`)
+
+        const conn = await db.connect()
+        await conn.beginTransaction()
+        try {
+            await conn.execute(`DELETE FROM user_permission_points WHERE user_uuid = ?`, [uuid])
+            for (const code of permissionCodes) {
+                await conn.execute(
+                    `INSERT INTO user_permission_points (user_uuid, permission_code) VALUES (?, ?)`,
+                    [uuid, code]
+                )
+            }
+            await conn.commit()
+        } catch (error) {
+            try { await conn.rollback() } catch (_) { }
+            throw error
+        }
+    }
+
+    async setUserDeniedBasicPermissionCodes(uuid, codes) {
+        await this.ensurePermissionSchema()
+        const basicSet = new Set(DEFAULT_BASIC_USER_PERMISSION_CODES)
+        const deniedCodes = this.normalizeCodes(codes).filter(code => basicSet.has(code))
+        const invalidCodes = this.normalizeCodes(codes).filter(code => !basicSet.has(code))
+
+        if (invalidCodes.length > 0)
+            throw new Error(`仅可关闭基础权限:${invalidCodes.join(', ')}`)
+
+        const conn = await db.connect()
+        await conn.beginTransaction()
+        try {
+            await conn.execute(`DELETE FROM user_basic_permission_denials WHERE user_uuid = ?`, [uuid])
+            for (const code of deniedCodes) {
+                await conn.execute(
+                    `INSERT INTO user_basic_permission_denials (user_uuid, permission_code) VALUES (?, ?)`,
+                    [uuid, code]
+                )
+            }
+            await conn.commit()
+        } catch (error) {
+            try { await conn.rollback() } catch (_) { }
+            throw error
+        }
+    }
+
+    async getResourceRules() {
+        await this.ensurePermissionSchema()
+        const rows = await db.query(`
+            SELECT id, resource_type, resource_key, api_method, api_path, required_codes, enabled, remark
+            FROM permission_resource_rules
+            ORDER BY FIELD(resource_type, 'page', 'action', 'api'), id
+        `)
+
+        return (rows || []).map(row => ({
+            ...row,
+            required_codes: this.parseArray(row.required_codes)
+        }))
+    }
+
+    async getResourceRequiredCodes({ resourceType, resourceKey, method, path }) {
+        await this.ensurePermissionSchema()
+        let rows = []
+
+        if (resourceType && resourceKey) {
+            rows = await db.query(
+                `SELECT required_codes
+                FROM permission_resource_rules
+                WHERE resource_type = ? AND resource_key = ? AND enabled = 1
+                LIMIT 1`,
+                [resourceType, resourceKey]
+            )
+        } else if (method && path) {
+            rows = await db.query(
+                `SELECT required_codes
+                FROM permission_resource_rules
+                WHERE resource_type = 'api' AND api_method = ? AND api_path = ? AND enabled = 1
+                LIMIT 1`,
+                [String(method).toUpperCase(), path]
+            )
+        }
+
+        return this.parseArray(rows?.[0]?.required_codes)
+    }
+
+    async updateResourceRule({ id, required_codes, enabled }) {
+        await this.ensurePermissionSchema()
+        const requiredCodes = this.normalizeCodes(required_codes)
+        const points = await this.getPermissionPoints()
+        const validCodes = new Set(points.map(point => point.code))
+        const invalidCodes = requiredCodes.filter(code => !validCodes.has(code))
+
+        if (invalidCodes.length > 0)
+            throw new Error(`存在无效权限点:${invalidCodes.join(', ')}`)
+
+        const rows = await db.query(
+            `UPDATE permission_resource_rules
+            SET required_codes = ?, enabled = ?
+            WHERE id = ?`,
+            [JSON.stringify(requiredCodes), Number(enabled) === 0 ? 0 : 1, id]
+        )
+
+        return rows?.affectedRows === 1
+    }
+
+    async canAccess(uuid, requiredCodes) {
+        const codes = this.normalizeCodes(requiredCodes)
+        if (codes.length === 0) return true
+        if (await this.isSuperAdmin(uuid)) return true
+
+        const userCodes = await this.getUserPermissionCodes(uuid)
+        return codes.some(code => userCodes.includes(code))
     }
     }
 
 
     async checkJwAccount(uuid, username) {
     async checkJwAccount(uuid, username) {

+ 111 - 0
lib/PermissionCatalog.js

@@ -0,0 +1,111 @@
+const PermissionCategory = {
+    PAGE: 'page',
+    ACTION: 'action'
+}
+
+const DEFAULT_PERMISSION_POINTS = [
+    { code: 'page.admin.userList', name: '用户管理', category: PermissionCategory.PAGE, scope_type: 'page', page_route_name: 'admin.userList', remark: '访问后台用户管理页面' },
+    { code: 'page.admin.lepaoAccount', name: '乐跑账号管理', category: PermissionCategory.PAGE, scope_type: 'page', page_route_name: 'admin.lepaoAccount', remark: '访问后台乐跑账号管理页面' },
+    { code: 'page.admin.service.orderList', name: '工单管理', category: PermissionCategory.PAGE, scope_type: 'page', page_route_name: 'admin.service.orderList', remark: '访问后台工单管理页面' },
+    { code: 'page.admin.goods.sendCountRequestList', name: '赠送审核', category: PermissionCategory.PAGE, scope_type: 'page', page_route_name: 'admin.goods.sendCountRequestList', remark: '访问乐跑次数赠送审核页面' },
+    { code: 'page.service.createOrder', name: '提交工单', category: PermissionCategory.PAGE, scope_type: 'page', page_route_name: 'service.createOrder', remark: '访问用户提交工单页面' },
+    { code: 'page.lepao.accountList', name: '乐跑账号', category: PermissionCategory.PAGE, scope_type: 'page', page_route_name: 'lepao.accountList', remark: '访问用户乐跑账号页面' },
+
+    { code: 'action.user.changeCount', name: '更改乐跑次数', category: PermissionCategory.ACTION, scope_type: 'action', remark: '管理员调整用户乐跑次数' },
+    { code: 'action.user.ban', name: '封禁账户', category: PermissionCategory.ACTION, scope_type: 'action', remark: '封禁或解封用户账户' },
+    { code: 'action.user.permissionManage', name: '权限管理', category: PermissionCategory.ACTION, scope_type: 'action', remark: '查看和编辑用户权限、权限规则' },
+    { code: 'action.user.setSendCountAutoApprove', name: '赠送免审配置', category: PermissionCategory.ACTION, scope_type: 'action', remark: '配置用户赠送乐跑次数免审白名单' },
+    { code: 'action.lepao.addAccount', name: '新增乐跑账号', category: PermissionCategory.ACTION, scope_type: 'action', remark: '用户新增或绑定乐跑账号' },
+    { code: 'action.lepao.singleRun', name: '发起单次乐跑', category: PermissionCategory.ACTION, scope_type: 'action', remark: '用户发起单次乐跑' },
+    { code: 'action.lepao.changeAutoRun', name: '配置自动乐跑', category: PermissionCategory.ACTION, scope_type: 'action', remark: '用户开启或关闭自动乐跑' },
+    { code: 'action.lepao.updateAccount', name: '更新乐跑账号', category: PermissionCategory.ACTION, scope_type: 'action', remark: '更新乐跑账号信息' },
+    { code: 'action.lepao.deleteAccount', name: '解绑乐跑账号', category: PermissionCategory.ACTION, scope_type: 'action', remark: '解绑乐跑账号' },
+    { code: 'action.lepao.admin.updateAccount', name: '后台更新乐跑账号', category: PermissionCategory.ACTION, scope_type: 'action', remark: '管理员更新乐跑账号信息' },
+    { code: 'action.goods.sendCount', name: '赠送乐跑次数', category: PermissionCategory.ACTION, scope_type: 'action', remark: '用户向他人赠送乐跑次数' },
+    { code: 'action.goods.reviewSendCount', name: '审核赠送次数', category: PermissionCategory.ACTION, scope_type: 'action', remark: '管理员审核乐跑次数赠送申请' },
+    { code: 'action.service.createOrder', name: '发起工单', category: PermissionCategory.ACTION, scope_type: 'action', remark: '用户提交新工单或回复自己的工单' },
+    { code: 'action.service.replyOrder', name: '回复工单', category: PermissionCategory.ACTION, scope_type: 'action', remark: '客服或管理员回复工单' }
+]
+
+const DEFAULT_PERMISSION_RESOURCE_RULES = [
+    { resource_type: 'page', resource_key: 'admin.userList', required_codes: ['page.admin.userList'] },
+    { resource_type: 'page', resource_key: 'admin.lepaoAccount', required_codes: ['page.admin.lepaoAccount'] },
+    { resource_type: 'page', resource_key: 'admin.service.orderList', required_codes: ['page.admin.service.orderList'] },
+    { resource_type: 'page', resource_key: 'admin.goods.sendCountRequestList', required_codes: ['page.admin.goods.sendCountRequestList'] },
+    { resource_type: 'page', resource_key: 'service.createOrder', required_codes: ['page.service.createOrder'] },
+    { resource_type: 'page', resource_key: 'lepao.accountList', required_codes: ['page.lepao.accountList'] },
+
+    { resource_type: 'action', resource_key: 'action.user.changeCount', required_codes: ['action.user.changeCount'] },
+    { resource_type: 'action', resource_key: 'action.user.ban', required_codes: ['action.user.ban'] },
+    { resource_type: 'action', resource_key: 'action.user.permissionManage', required_codes: ['action.user.permissionManage'] },
+    { resource_type: 'action', resource_key: 'action.user.setSendCountAutoApprove', required_codes: ['action.user.setSendCountAutoApprove'] },
+    { resource_type: 'action', resource_key: 'action.lepao.addAccount', required_codes: ['action.lepao.addAccount'] },
+    { resource_type: 'action', resource_key: 'action.lepao.singleRun', required_codes: ['action.lepao.singleRun'] },
+    { resource_type: 'action', resource_key: 'action.lepao.changeAutoRun', required_codes: ['action.lepao.changeAutoRun'] },
+    { resource_type: 'action', resource_key: 'action.lepao.updateAccount', required_codes: ['action.lepao.updateAccount'] },
+    { resource_type: 'action', resource_key: 'action.lepao.deleteAccount', required_codes: ['action.lepao.deleteAccount'] },
+    { resource_type: 'action', resource_key: 'action.lepao.admin.updateAccount', required_codes: ['action.lepao.admin.updateAccount'] },
+    { resource_type: 'action', resource_key: 'action.goods.sendCount', required_codes: ['action.goods.sendCount'] },
+    { resource_type: 'action', resource_key: 'action.goods.reviewSendCount', required_codes: ['action.goods.reviewSendCount'] },
+    { resource_type: 'action', resource_key: 'action.service.createOrder', required_codes: ['action.service.createOrder'] },
+    { resource_type: 'action', resource_key: 'action.service.replyOrder', required_codes: ['action.service.replyOrder'] },
+
+    { resource_type: 'api', resource_key: 'GET /Admin/User/GetUserList', api_method: 'GET', api_path: '/Admin/User/GetUserList', required_codes: ['page.admin.userList'] },
+    { resource_type: 'api', resource_key: 'POST /Admin/User/ChangeLepaoCount', api_method: 'POST', api_path: '/Admin/User/ChangeLepaoCount', required_codes: ['action.user.changeCount'] },
+    { resource_type: 'api', resource_key: 'POST /Admin/User/SetUserBan', api_method: 'POST', api_path: '/Admin/User/SetUserBan', required_codes: ['action.user.ban'] },
+    { resource_type: 'api', resource_key: 'POST /Admin/User/SetSendCountAutoApprove', api_method: 'POST', api_path: '/Admin/User/SetSendCountAutoApprove', required_codes: ['action.user.setSendCountAutoApprove'] },
+    { resource_type: 'api', resource_key: 'GET /Admin/Lepao/Account', api_method: 'GET', api_path: '/Admin/Lepao/Account', required_codes: ['page.admin.lepaoAccount'] },
+    { resource_type: 'api', resource_key: 'POST /Admin/Lepao/Account/UpdateAccountInfo', api_method: 'POST', api_path: '/Admin/Lepao/Account/UpdateAccountInfo', required_codes: ['action.lepao.admin.updateAccount'] },
+    { resource_type: 'api', resource_key: 'POST /Lepao/Account', api_method: 'POST', api_path: '/Lepao/Account', required_codes: ['action.lepao.addAccount'] },
+    { resource_type: 'api', resource_key: 'GET /Lepao/SingleRun', api_method: 'GET', api_path: '/Lepao/SingleRun', required_codes: ['action.lepao.singleRun'] },
+    { resource_type: 'api', resource_key: 'GET /Lepao/ChangeAutoRun', api_method: 'GET', api_path: '/Lepao/ChangeAutoRun', required_codes: ['action.lepao.changeAutoRun'] },
+    { resource_type: 'api', resource_key: 'POST /Lepao/Account/UpdateSelfAccount', api_method: 'POST', api_path: '/Lepao/Account/UpdateSelfAccount', required_codes: ['action.lepao.updateAccount'] },
+    { resource_type: 'api', resource_key: 'DELETE /Lepao/Account', api_method: 'DELETE', api_path: '/Lepao/Account', required_codes: ['action.lepao.deleteAccount'] },
+    { resource_type: 'api', resource_key: 'POST /Goods/SendCount', api_method: 'POST', api_path: '/Goods/SendCount', required_codes: ['action.goods.sendCount'] },
+    { resource_type: 'api', resource_key: 'POST /Kefu/Order', api_method: 'POST', api_path: '/Kefu/Order', required_codes: ['action.service.createOrder'] },
+    { resource_type: 'api', resource_key: 'GET /Admin/Kefu/Order', api_method: 'GET', api_path: '/Admin/Kefu/Order', required_codes: ['page.admin.service.orderList'] },
+    { resource_type: 'api', resource_key: 'GET /Admin/Kefu/OrderDetail', api_method: 'GET', api_path: '/Admin/Kefu/OrderDetail', required_codes: ['page.admin.service.orderList'] },
+    { resource_type: 'api', resource_key: 'PUT /Admin/Kefu/Order', api_method: 'PUT', api_path: '/Admin/Kefu/Order', required_codes: ['action.service.replyOrder'] },
+    { resource_type: 'api', resource_key: 'GET /Admin/Goods/SendCountRequest/List', api_method: 'GET', api_path: '/Admin/Goods/SendCountRequest/List', required_codes: ['page.admin.goods.sendCountRequestList'] },
+    { resource_type: 'api', resource_key: 'POST /Admin/Goods/SendCountRequest/Approve', api_method: 'POST', api_path: '/Admin/Goods/SendCountRequest/Approve', required_codes: ['action.goods.reviewSendCount'] },
+    { resource_type: 'api', resource_key: 'POST /Admin/Goods/SendCountRequest/Reject', api_method: 'POST', api_path: '/Admin/Goods/SendCountRequest/Reject', required_codes: ['action.goods.reviewSendCount'] }
+]
+
+// 所有登录用户默认拥有的基础操作权限(无需在后台单独勾选)
+const DEFAULT_BASIC_USER_PERMISSION_CODES = [
+    'page.lepao.accountList',
+    'action.lepao.addAccount',
+    'action.lepao.singleRun',
+    'action.lepao.changeAutoRun',
+    'action.lepao.updateAccount',
+    'action.lepao.deleteAccount',
+    'action.goods.sendCount',
+    'page.service.createOrder',
+    'action.service.createOrder'
+]
+
+const LEGACY_ROLE_PERMISSION_MAP = {
+    service: [
+        'page.admin.userList',
+        'page.admin.lepaoAccount',
+        'page.admin.service.orderList',
+        'page.admin.goods.sendCountRequestList',
+        'action.user.changeCount',
+        'action.user.ban',
+        'action.user.setSendCountAutoApprove',
+        'action.lepao.admin.updateAccount',
+        'action.goods.reviewSendCount',
+        'action.service.replyOrder'
+    ],
+    product: [
+        'action.goods.reviewSendCount'
+    ],
+    path: []
+}
+
+module.exports = {
+    DEFAULT_PERMISSION_POINTS,
+    DEFAULT_PERMISSION_RESOURCE_RULES,
+    DEFAULT_BASIC_USER_PERMISSION_CODES,
+    LEGACY_ROLE_PERMISSION_MAP
+}