Files
bj_power/bj_power_mes/internal/handler/perm.go
T
SunYF a3145a8bd6 docs: update and refine project requirement documents
1. 整理并更新工位终端、工艺流程、用户权限等多模块的需求文档
2. 移除冗余的内置账号相关旧需求,补充统一用户源、工艺步骤自定义等新需求细节
3. 优化文档结构,合并重复内容,修正英文状态词使用问题
4. 补充操作流程、PDF挂载、流程卡打印等业务场景的详细设计方案
2026-08-31 09:13:23 +08:00

96 lines
3.5 KiB
Go

package handler
import (
"net/http"
"strings"
"bj_power_mes/common/httpx"
"bj_power_mes/internal/svc"
)
// pathPermMap 请求方法+路径 → 所需按钮级权限码(块2:后端接口兜底校验)。
// 路径中的 :id 统一以 * 结尾匹配。
var pathPermMap = map[string]string{
"POST:/api/v1/product-types": "produce.product:add",
"PUT:/api/v1/product-types": "produce.product:edit",
"DELETE:/api/v1/product-types/*": "produce.product:delete",
"POST:/api/v1/work-orders": "produce.workorder:add",
"PUT:/api/v1/work-orders": "produce.workorder:edit",
"DELETE:/api/v1/work-orders/*": "produce.workorder:delete",
"POST:/api/v1/daily-plans": "produce.workorder:dailyplan",
"PUT:/api/v1/bom": "produce.bom:edit",
"POST:/api/v1/material-requests/generate": "produce.material:generate",
"POST:/api/v1/plc/send-process": "produce.plc:send",
"POST:/api/v1/process-flows": "produce.processflow:add",
"DELETE:/api/v1/process-flows/*": "produce.processflow:delete",
"POST:/api/v1/process-flows/upload": "produce.processflow:upload",
"POST:/api/v1/process-steps": "produce.processflow:edit",
"POST:/api/v1/stations": "produce.station:edit",
"POST:/api/v1/users": "sys.rbac:user",
"PUT:/api/v1/users": "sys.rbac:user",
"DELETE:/api/v1/users/*": "sys.rbac:user",
"POST:/api/v1/roles": "sys.rbac:role",
"PUT:/api/v1/roles": "sys.rbac:role",
"DELETE:/api/v1/roles/*": "sys.rbac:role",
"POST:/api/v1/permissions": "sys.rbac:perm",
"PUT:/api/v1/permissions": "sys.rbac:perm",
"DELETE:/api/v1/permissions/*": "sys.rbac:perm",
}
// userHasPerm 校验当前登录用户是否拥有某权限码(SUPER_ADMIN 或 * 放行)。
// 用户ID 使用 uidFromRequest:优先取 context,缺失时回退解析 Authorization token。
func userHasPerm(r *http.Request, svcCtx *svc.ServiceContext, code string) bool {
if code == "" {
return true
}
userId := uidFromRequest(r, svcCtx)
if userId <= 0 {
return false
}
usr, err := svcCtx.EntClient.User.Get(r.Context(), userId)
if err != nil {
return false
}
role, err := svcCtx.EntClient.Role.Get(r.Context(), usr.RoleId)
if err != nil {
return false
}
if role.Code == "SUPER_ADMIN" {
return true
}
for _, c := range role.PermissionCodes {
if c == "*" || c == code {
return true
}
}
return false
}
// permissionGuard 按钮级权限后端兜底中间件(仅对写操作映射表内的路径生效)
func permissionGuard(svcCtx *svc.ServiceContext) func(http.HandlerFunc) http.HandlerFunc {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
code, ok := pathPerm(r.Method, r.URL.Path)
if ok && !userHasPerm(r, svcCtx, code) {
httpx.FailHTTP(w, http.StatusForbidden, "无操作权限:"+code)
return
}
next(w, r)
}
}
}
// pathPerm 先按完整路径精确匹配;未命中时把末段替换为 * 再匹配(用于 :id 结尾的路径)。
func pathPerm(method, path string) (string, bool) {
key := method + ":" + strings.TrimSuffix(path, "/")
if code, ok := pathPermMap[key]; ok {
return code, true
}
segs := strings.Split(key, "/")
if len(segs) > 0 {
segs[len(segs)-1] = "*"
}
code, ok := pathPermMap[strings.Join(segs, "/")]
return code, ok
}