增加功能报价

This commit is contained in:
2026-09-16 00:22:17 +08:00
parent 71a14c46f9
commit 60b2658d4b
32 changed files with 3578 additions and 77 deletions
+36 -8
View File
@@ -113,10 +113,12 @@ func (c *BackendMenuController) GetBackendMenu() {
}
}
// 套餐功能开通过滤:仅展示租户套餐已包含的功能模块对应的菜单(未包含的整块菜单不展示)
backendMenus = filterMenusByTenantPackage(c.Ctx.Request.Header.Get("Authorization"), backendMenus, tid)
// 套餐功能开通标记:未开通模块的菜单保留但标记 locked(前端展示锁定态与升级提示,不再 404)
backendMenus, lockedMenuIDs := filterMenusByTenantPackageLocked(c.Ctx.Request.Header.Get("Authorization"), backendMenus, tid, true)
menuTree := buildMenuTree(backendMenus, 0)
markTreeLocked(menuTree, lockedMenuIDs)
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": buildMenuTree(backendMenus, 0)}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": menuTree}
_ = c.ServeJSON()
}
@@ -139,26 +141,51 @@ func backendJWTTenantID(auth string) uint64 {
return uint64(claims.TenantId)
}
// filterMenusByTenantPackage 按租户套餐包含的功能模块过滤菜单。
// filterMenusByTenantPackage 按租户套餐包含的功能模块过滤菜单(剔除未开通模块,用于角色菜单分配等场景)。
// tid 优先取 token 中的租户ID,其次用调用方传入的兜底值;两者都取不到时不处理(保持原行为)。
func filterMenusByTenantPackage(authHeader string, menus []models.SystemMenu, tid uint64) []models.SystemMenu {
filtered, _ := filterMenusByTenantPackageLocked(authHeader, menus, tid, false)
return filtered
}
// filterMenusByTenantPackageLocked 按租户套餐处理菜单。
// markLocked=false:剔除未开通模块的菜单(原行为);markLocked=true:保留菜单并返回锁定菜单ID集合(含后代)。
func filterMenusByTenantPackageLocked(authHeader string, menus []models.SystemMenu, tid uint64, markLocked bool) ([]models.SystemMenu, map[uint64]bool) {
if jwtTid := backendJWTTenantID(authHeader); jwtTid > 0 {
tid = jwtTid
}
if tid == 0 {
return menus
return menus, nil
}
allModules, err := services.ListEnabledModules()
if err != nil || len(allModules) == 0 {
return menus
return menus, nil
}
allowed := services.GetTenantModuleCodes(tid)
// 安全兜底:租户没有绑定套餐或套餐未配置任何功能模块时不做过滤,
// 避免因套餐数据缺失导致租户端菜单整体消失。
if len(allowed) == 0 {
return menus
return menus, nil
}
if markLocked {
return services.MarkMenusLockedByTenantModules(menus, allModules, allowed)
}
return services.FilterMenusByTenantModules(menus, allModules, allowed), nil
}
// markTreeLocked 将菜单树中位于锁定集合的节点标记为 locked
func markTreeLocked(nodes []*menuNode, locked map[uint64]bool) {
if len(nodes) == 0 || len(locked) == 0 {
return
}
for _, n := range nodes {
if locked[n.ID] {
n.Locked = true
}
if len(n.Children) > 0 {
markTreeLocked(n.Children, locked)
}
}
return services.FilterMenusByTenantModules(menus, allModules, allowed)
}
// parseRightsToSet 将角色 rights 解析为菜单 ID 集合(兼容 JSON 数组 / 逗号分隔)
@@ -350,6 +377,7 @@ type menuNode struct {
Views []int `json:"views,omitempty"`
Type int8 `json:"type"`
Permission string `json:"permission,omitempty"`
Locked bool `json:"locked,omitempty"`
Children []*menuNode `json:"children,omitempty"`
}