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", "POST:/api/v1/work-orders/status": "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", "POST:/api/v1/process-flows/status": "produce.processflow:edit", "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/binds": "produce.scan", "POST:/api/v1/binds/remove": "produce.scan", // 账号管理(拆分自原 sys.rbac:user;sys.account 菜单下增/改/删) "POST:/api/v1/users": "sys.account:add", "PUT:/api/v1/users": "sys.account:edit", "DELETE:/api/v1/users/*": "sys.account:delete", // 角色管理(拆分自原 sys.rbac:role/perm;sys.role 菜单下增/改/删) "POST:/api/v1/roles": "sys.role:add", "PUT:/api/v1/roles": "sys.role:edit", "DELETE:/api/v1/roles/*": "sys.role:delete", "POST:/api/v1/permissions": "sys.role:perm", "PUT:/api/v1/permissions": "sys.role:perm", "DELETE:/api/v1/permissions/*": "sys.role: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 }