小程序一些功能更新
This commit is contained in:
207
internal/logic/admin_perm.go
Normal file
207
internal/logic/admin_perm.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
|
||||
"tool-api/internal/consts"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// 后台权限分级(T17)
|
||||
//
|
||||
// 模型:角色(超管/运营/只读)→ 权限点集合;中间件按「路由 → 权限点」放行。
|
||||
// 权限点与角色的对应关系集中在本文件,避免散落到每个 handler。
|
||||
// ============================================================================
|
||||
|
||||
// 权限点
|
||||
const (
|
||||
PermUserWrite = "user:write"
|
||||
PermToolWrite = "tool:write"
|
||||
PermModuleWrite = "module:write"
|
||||
PermLevelWrite = "level:write"
|
||||
PermFeedbackWrite = "feedback:write"
|
||||
PermQuotaWrite = "quota:write"
|
||||
PermEventWrite = "event:write"
|
||||
PermAdminManage = "admin:manage"
|
||||
PermAuditRead = "audit:read"
|
||||
// 订单域:查看 / 退款(退款为可逆性资金动作,单独权限点便于收口)
|
||||
PermOrderRead = "order:read"
|
||||
PermOrderWrite = "order:write"
|
||||
)
|
||||
|
||||
// rolePermissions 角色 → 权限点集合。
|
||||
//
|
||||
// 超管:全部
|
||||
// 运营:内容读写(用户/工具/模块/等级/反馈/额度/年会/订单),不含管理员管理与审计
|
||||
// 只读:无写权限,仅可查看(含订单查看)
|
||||
var rolePermissions = map[string]map[string]bool{
|
||||
consts.AdminRoleSuper: {
|
||||
PermUserWrite: true, PermToolWrite: true, PermModuleWrite: true, PermLevelWrite: true,
|
||||
PermFeedbackWrite: true, PermQuotaWrite: true, PermEventWrite: true,
|
||||
PermAdminManage: true, PermAuditRead: true,
|
||||
PermOrderRead: true, PermOrderWrite: true,
|
||||
},
|
||||
consts.AdminRoleOperator: {
|
||||
PermUserWrite: true, PermToolWrite: true, PermModuleWrite: true, PermLevelWrite: true,
|
||||
PermFeedbackWrite: true, PermQuotaWrite: true, PermEventWrite: true,
|
||||
PermOrderRead: true, PermOrderWrite: true,
|
||||
},
|
||||
consts.AdminRoleReadonly: {
|
||||
PermOrderRead: true,
|
||||
},
|
||||
}
|
||||
|
||||
// HasPermission 判断角色是否拥有权限点;perm 为空表示「仅需登录」。
|
||||
// 未知角色(历史脏数据)→ 按最小权限处理(false),避免越权。
|
||||
func HasPermission(role, perm string) bool {
|
||||
if perm == "" {
|
||||
return true
|
||||
}
|
||||
perms, ok := rolePermissions[role]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return perms[perm]
|
||||
}
|
||||
|
||||
// PermissionsOf 返回角色的权限点列表(前端据此隐藏入口),已排序。
|
||||
func PermissionsOf(role string) []string {
|
||||
perms := rolePermissions[role]
|
||||
out := make([]string, 0, len(perms))
|
||||
for p := range perms {
|
||||
out = append(out, p)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// roleNameOf 角色值 → 展示名
|
||||
func roleNameOf(role string) string {
|
||||
switch role {
|
||||
case consts.AdminRoleSuper:
|
||||
return "超级管理员"
|
||||
case consts.AdminRoleOperator:
|
||||
return "运营"
|
||||
case consts.AdminRoleReadonly:
|
||||
return "只读"
|
||||
}
|
||||
return role
|
||||
}
|
||||
|
||||
// permissionForRoute 路由 → 所需权限点。空串 = 仅需登录(只读接口)。
|
||||
// 采用「方法 + 路径前缀」映射;/user/quota-period 是额度操作,需先于 /user/ 命中。
|
||||
func permissionForRoute(method, path string) string {
|
||||
if method != http.MethodPost {
|
||||
if strings.HasPrefix(path, "/audit/") {
|
||||
return PermAuditRead
|
||||
}
|
||||
if strings.HasPrefix(path, "/admin/") {
|
||||
return PermAdminManage
|
||||
}
|
||||
if strings.HasPrefix(path, "/order/") {
|
||||
return PermOrderRead
|
||||
}
|
||||
return ""
|
||||
}
|
||||
switch {
|
||||
case path == "/user/quota-period":
|
||||
return PermQuotaWrite
|
||||
case path == "/audit/record":
|
||||
// 上报审计≠读取审计:任何登录的管理员都可记录自己的动作(如导出)
|
||||
return ""
|
||||
case strings.HasPrefix(path, "/user/"):
|
||||
return PermUserWrite
|
||||
case strings.HasPrefix(path, "/tools/"):
|
||||
return PermToolWrite
|
||||
case strings.HasPrefix(path, "/module/"):
|
||||
return PermModuleWrite
|
||||
case strings.HasPrefix(path, "/level/"):
|
||||
return PermLevelWrite
|
||||
case strings.HasPrefix(path, "/feedback/"):
|
||||
return PermFeedbackWrite
|
||||
case strings.HasPrefix(path, "/quota/"), strings.HasPrefix(path, "/plan/"):
|
||||
return PermQuotaWrite
|
||||
case strings.HasPrefix(path, "/promo/"), strings.HasPrefix(path, "/coupon/"):
|
||||
// 优惠码 / 优惠券属商业化配置,归入「额度写」权限(运营+超管可写;只读不可写)
|
||||
return PermQuotaWrite
|
||||
case strings.HasPrefix(path, "/event/"):
|
||||
return PermEventWrite
|
||||
case strings.HasPrefix(path, "/order/"):
|
||||
// 订单退款属可逆性资金动作,按「订单写」权限放行(运营+超管可写;只读不可写)
|
||||
return PermOrderWrite
|
||||
case strings.HasPrefix(path, "/admin/"):
|
||||
return PermAdminManage
|
||||
case strings.HasPrefix(path, "/audit/"):
|
||||
return PermAuditRead
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func writeForbidden(r *ghttp.Request, message string) {
|
||||
r.Response.WriteJson(g.Map{"code": 403, "message": message, "result": nil})
|
||||
r.Exit()
|
||||
}
|
||||
|
||||
// AdminPerm 权限中间件(须挂在 AdminAuth 之后)。
|
||||
// 从库中复核管理员最新状态与角色(使「停用/改角色」立即生效,无需重新登录),
|
||||
// 按「路由 → 权限点」放行;管理员信息写入上下文,供审计复用。
|
||||
func AdminPerm(r *ghttp.Request) {
|
||||
adminId := r.GetCtxVar(consts.CtxAdminId).Int64()
|
||||
record, err := g.Model(consts.TableAdminUsers).Where("id", adminId).One()
|
||||
if err != nil {
|
||||
// 鉴权已由 AdminAuth 完成;DB 抖动不应把所有人挡在门外 → 放行并告警
|
||||
g.Log().Warningf(r.Context(), "[adminperm] 读取管理员失败,放行: %v", err)
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
if record.IsEmpty() {
|
||||
writeForbidden(r, "管理员不存在,请重新登录")
|
||||
return
|
||||
}
|
||||
// status 列可能因迁移未执行而缺失:缺失视为启用(fail-open),避免全站被锁
|
||||
if v, ok := record["status"]; ok && v.Int() != consts.AdminStatusEnabled {
|
||||
writeForbidden(r, "账号已停用")
|
||||
return
|
||||
}
|
||||
role := record["role_value"].String()
|
||||
if !HasPermission(role, permissionForRoute(r.Method, r.URL.Path)) {
|
||||
writeForbidden(r, "当前角色无此操作权限")
|
||||
return
|
||||
}
|
||||
r.SetCtxVar(consts.CtxAdminRole, role)
|
||||
r.SetCtxVar(consts.CtxAdminAccount, record["account"].String())
|
||||
r.SetCtxVar(consts.CtxAdminName, record["nick_name"].String())
|
||||
r.Middleware.Next()
|
||||
}
|
||||
|
||||
// ===== 审计上下文取值 =====
|
||||
|
||||
func CtxAdminRole(ctx context.Context) string {
|
||||
req := g.RequestFromCtx(ctx)
|
||||
if req == nil {
|
||||
return ""
|
||||
}
|
||||
return req.GetCtxVar(consts.CtxAdminRole).String()
|
||||
}
|
||||
|
||||
func CtxAdminAccount(ctx context.Context) string {
|
||||
req := g.RequestFromCtx(ctx)
|
||||
if req == nil {
|
||||
return ""
|
||||
}
|
||||
return req.GetCtxVar(consts.CtxAdminAccount).String()
|
||||
}
|
||||
|
||||
func CtxAdminName(ctx context.Context) string {
|
||||
req := g.RequestFromCtx(ctx)
|
||||
if req == nil {
|
||||
return ""
|
||||
}
|
||||
return req.GetCtxVar(consts.CtxAdminName).String()
|
||||
}
|
||||
Reference in New Issue
Block a user