package controllers import ( "fmt" "strings" "server/models" "server/pkg/jwtutil" "server/services" authsvc "server/services/auth" beego "github.com/beego/beego/v2/server/web" ) // tenantModuleItem 模块列表返回项:在模块基础信息上补充套餐开通状态, // locked=true 表示该模块不在当前租户已购套餐内(前端置灰并提示购买套餐)。 type tenantModuleItem struct { models.SystemModules Locked bool `json:"locked"` } // BackendModulesController backend 模块接口(yz_system_modules) type BackendModulesController struct { beego.Controller } func (c *BackendModulesController) backendModulesClaims() (*jwtutil.Claims, error) { auth := c.Ctx.Request.Header.Get("Authorization") if auth == "" { return nil, fmt.Errorf("未登录") } parts := strings.SplitN(auth, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { return nil, fmt.Errorf("认证信息格式错误") } claims, err := jwtutil.ParseToken(parts[1]) if err != nil { return nil, fmt.Errorf("无效的token") } if claims.UserType != "backend" { return nil, fmt.Errorf("无权访问") } return claims, nil } func (c *BackendModulesController) jsonErr(httpStatus, bizCode int, msg string) { c.Ctx.Output.SetStatus(httpStatus) c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} _ = c.ServeJSON() } // backendUserGroupID 解析当前登录者在当前租户的角色ID。 // claims.UserID 是认证中心 identity_id,不能直接当 yz_system_tenant_user 主键用; // 优先按认证绑定表(identity_id+tid → group_id,与 /auth/userinfo 同源), // 查不到再走业务表身份解析链(identity_id 换算 → 主键 → 老 uid)。 func (c *BackendModulesController) backendUserGroupID(claims *jwtutil.Claims, tid uint64) uint64 { uid := uint64(claims.UserID) if uid == 0 { return 0 } if bind, e := authsvc.GetTenantUser(uid, tid); e == nil { return bind.GroupID } if tu, e := findBackendTenantUser(uid, tid, false); e == nil { return tu.GroupID } return 0 } // moduleWithEnabled 带套餐开通标记的模块输出 type moduleWithEnabled struct { models.SystemModules Enabled bool `json:"enabled"` } // GetTenantList GET /backend/modules/getTenantList // 返回当前 backend 账号可见的模块(status=1 且 is_show=1),并根据租户绑定的套餐标记每个模块是否已开通。 // 前端可据此排序(已开通在前)、置灰未开通卡片、阻止跳转。 func (c *BackendModulesController) GetTenantList() { claims, err := c.backendModulesClaims() if err != nil { c.jsonErr(401, 401, err.Error()) return } var rows []models.SystemModules _, err = models.Orm.QueryTable(new(models.SystemModules)). Filter("delete_time__isnull", true). Filter("status", 1). Filter("is_show", 1). OrderBy("sort", "id"). All(&rows) if err != nil { c.jsonErr(500, 500, "获取失败:"+err.Error()) return } // 租户后台是 Web 端:只保留 web 可见的模块(未配置视为全端) rows = services.FilterModulesByPlatform(rows, models.PlatformWeb) // 查询当前租户已开通的模块 code 集合(套餐模块 + 已开通产品订单关联模块,编码已规范化) tid := uint64(claims.TenantId) allowedCodes := services.GetTenantModuleCodes(tid) enhanced := make([]moduleWithEnabled, 0, len(rows)) for _, m := range rows { enabled := true if len(allowedCodes) > 0 { enabled = allowedCodes[services.NormalizeModuleCode(m.Code)] } enhanced = append(enhanced, moduleWithEnabled{SystemModules: m, Enabled: enabled}) } // 角色权限过滤(与 /backend/menu/:id 同口径): // 未分配角色或角色 rights 为空 → 全部可见; // 否则仅保留「名下至少有一个被授权菜单」的模块,未授权模块直接不返回, // 避免首页出现可点击却进 404 的模块卡片。 if groupID := c.backendUserGroupID(claims, tid); groupID > 0 { var role models.AdminRole if re := models.Orm.QueryTable(new(models.AdminRole)). Filter("id", groupID). Filter("cid", 2). One(&role); re == nil && role.Rights != nil && strings.TrimSpace(*role.Rights) != "" { if len(parseRightsToSet(*role.Rights)) > 0 { var allMenus []models.SystemMenu if _, me := models.Orm.QueryTable(new(models.SystemMenu)).Filter("status", 1).All(&allMenus); me == nil { allowedMenus := filterMenusByRights(allMenus, role.Rights) owningAny := services.ModuleCodesOwningMenus(rows, allMenus) owningAllowed := services.ModuleCodesOwningMenus(rows, allowedMenus) visible := make([]moduleWithEnabled, 0, len(enhanced)) for _, item := range enhanced { code := services.NormalizeModuleCode(item.Code) // 模块名下有菜单但一个都没授权 → 隐藏; // 名下没有菜单(无法判定)→ 保持原行为不隐藏 if owningAny[code] && !owningAllowed[code] { continue } visible = append(visible, item) } enhanced = visible } } } } c.Data["json"] = map[string]interface{}{ "code": 200, "msg": "获取成功", "data": map[string]interface{}{ "list": enhanced, "total": len(enhanced), }, } _ = c.ServeJSON() }