This commit is contained in:
hero920103 committed 2026-11-03 11:13:59 +08:00
1 parent 012f8edf8b
commit 36ff960fbd
33 files changed
+1644 -344

No files matched your search

+111
View File
@@ -125,6 +125,117 @@ func (c *AppAggregateController) Home() {
})
}
// publicModuleItem 移动端免登录可见的模块卡片:只暴露展示与安全开关,不含菜单/KPI。
// 供未登录用户渲染 nav 宫格,require_login 决定点击是「进登录引导」还是「直接进入」。
type publicModuleItem struct {
ID uint64 `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
Icon string `json:"icon"`
Path string `json:"path"`
Description string `json:"description"`
RequireLogin int8 `json:"require_login"`
}
// PublicModules GET /api/modules/public
//
// 公开接口(无需登录,路径不在 jwt 中间件 protectedPrefixes 内):
// 返回移动端可见的模块宫格 + 每个模块的 require_login 标记,
// 让未登录用户也能进入 nav 默认页并分辨「哪些能免登录用、哪些必须先登录」。
func (c *AppAggregateController) PublicModules() {
var rows []models.SystemModules
if _, err := models.Orm.QueryTable(new(models.SystemModules)).
Filter("delete_time__isnull", true).
Filter("status", 1).
Filter("is_show", 1).
OrderBy("sort", "id").
All(&rows); err != nil {
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": []publicModuleItem{}})
return
}
// 移动端只展示 app 可见模块(未配置 platforms 视为全端)
rows = services.FilterModulesByPlatform(rows, models.PlatformApp)
out := make([]publicModuleItem, 0, len(rows))
for _, m := range rows {
out = append(out, publicModuleItem{
ID: m.ID,
Code: m.Code,
Name: m.Name,
Icon: m.Icon,
Path: m.Path,
Description: m.Description,
RequireLogin: m.RequireLogin,
})
}
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": out})
}
// PublicMenus GET /api/menus/public
//
// 公开接口(无需登录):返回移动端免登录菜单(require_login=0)。
// 免登录控制按菜单粒度配置(Platform 菜单管理),游客持游客令牌直接使用;
// nav 宫格据此判定模块卡片是否可免登录进入(模块级 require_login 不再作为判定依据)。
// 不做角色/套餐过滤:免登录功能对所有人开放,未配 platforms 的菜单继承所属模块。
func (c *AppAggregateController) PublicMenus() {
var rows []models.SystemMenu
if _, err := models.Orm.QueryTable(new(models.SystemMenu)).
Filter("status", 1).
Filter("require_login", 0).
All(&rows); err != nil {
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": []appMenuItem{}})
return
}
allModules := c.allModules()
sortedModules := services.SortedModulesForScope(allModules)
visible := make([]models.SystemMenu, 0, len(rows))
for _, m := range rows {
if m.Type == 3 { // 接口类菜单是权限标识载体,不下发
continue
}
if m.IsVisible != nil && *m.IsVisible == 0 {
continue
}
if !services.MenuVisibleOn(m, sortedModules, allModules, models.PlatformApp) {
continue
}
visible = append(visible, m)
}
// 公开集合内统计子节点数:目录若只包含未放开(或不存在)的子菜单,leaf 为 true 但不跳任何页,
// 前端按 NATIVE_RULES 解析不到路由时会落「开发中」占位,不会误入受保护页。
childCount := map[int64]int{}
for _, m := range visible {
if m.Pid != 0 {
childCount[m.Pid]++
}
}
out := make([]appMenuItem, 0, len(visible))
for _, m := range visible {
item := appMenuItem{
ID: m.ID,
Pid: m.Pid,
Type: m.Type,
Sort: m.Sort,
Title: strings.TrimSpace(m.Title),
}
if m.Path != nil {
item.Path = strings.TrimSpace(*m.Path)
}
if m.Icon != nil {
item.Icon = strings.TrimSpace(*m.Icon)
}
item.Module = appModuleCodeOfPath(item.Path, allModules)
item.Leaf = childCount[int64(m.ID)] == 0
out = append(out, item)
}
sort.SliceStable(out, func(i, j int) bool { return out[i].Sort < out[j].Sort })
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": out})
}
// appQuickMenuIds 读取当前用户自定义的常用功能菜单 ID 列表(有序)。
// 未设置或解析失败一律返回空数组,前端据此回落到默认取前 N 个叶子菜单。
func (c *AppAggregateController) appQuickMenuIds(tid, uid uint64) []uint64 {