package main import ( "fmt" "os" "strings" ) type permDecl struct { Code string Name string } type routePermDecl struct { Method string Path string PermCode string DataCode string } type fieldNode struct { Fields map[string]string Nested map[string]*fieldNode } type collectResult struct { perms []permDecl routePerms []routePermDecl fieldPerms map[string]*fieldPermMap // "METHOD /path" → fieldPermMap } type fieldPermMap struct { Request *fieldNode Response *fieldNode } func collect(input *PluginInput) *collectResult { seen := make(map[string]bool) result := &collectResult{ fieldPerms: make(map[string]*fieldPermMap), } if input == nil || input.Api == nil { return result } // 建立类型名 → TypeDef 的索引,用于展开嵌套类型 typeIndex := make(map[string]*TypeDef, len(input.Api.Types)) for i := range input.Api.Types { typeIndex[input.Api.Types[i].RawName] = &input.Api.Types[i] } addPerm := func(code, name string) { if seen[code] || code == "" { return } seen[code] = true if name == "" { name = generatePermName(code) } result.perms = append(result.perms, permDecl{Code: code, Name: name}) } for _, group := range input.Api.Service.Groups { for _, route := range group.Routes { method := strings.ToUpper(route.Method) path := route.Path key := method + " " + path permCode := "" permName := "" if route.AtDoc.Properties != nil { permCode = route.AtDoc.Properties["perm"] permName = route.AtDoc.Properties["summary"] } if permCode == "" { fmt.Fprintf(os.Stderr, "WARN: handler %s (%s %s) has no perm declaration, treated as public\n", route.Handler, method, path) continue } addPerm(permCode, permName) dataCode := apiToDataCode(permCode) if dataCode != "" { addPerm(dataCode, permName) } result.routePerms = append(result.routePerms, routePermDecl{ Method: method, Path: path, PermCode: permCode, DataCode: dataCode, }) var reqNode *fieldNode if route.RequestType != nil { reqNode = collectFieldPerms(route.RequestType.Members, typeIndex) collectPermCodes(reqNode, addPerm) } var respNode *fieldNode if route.ResponseType != nil { respNode = collectFieldPerms(route.ResponseType.Members, typeIndex) collectPermCodes(respNode, addPerm) } if reqNode != nil || respNode != nil { result.fieldPerms[key] = &fieldPermMap{ Request: reqNode, Response: respNode, } } } } return result } // collectFieldPerms 递归构建 fieldNode 树 func collectFieldPerms(members []Member, typeIndex map[string]*TypeDef) *fieldNode { node := &fieldNode{ Fields: make(map[string]string), Nested: make(map[string]*fieldNode), } for _, m := range members { jsonName := extractTagValue(m.Tag, "json") if jsonName == "" { jsonName = m.Name } permCode := extractTagValue(m.Tag, "perm") if permCode != "" { node.Fields[jsonName] = permCode } rawName := strings.TrimPrefix(m.Type.RawName, "[]") if nested, ok := typeIndex[rawName]; ok && len(nested.Members) > 0 { child := collectFieldPerms(nested.Members, typeIndex) if len(child.Fields) > 0 || len(child.Nested) > 0 { node.Nested[jsonName] = child } } } if len(node.Fields) == 0 && len(node.Nested) == 0 { return nil } return node } // collectPermCodes 从 fieldNode 树中提取所有 permCode 并注册 func collectPermCodes(node *fieldNode, addPerm func(string, string)) { if node == nil { return } for _, code := range node.Fields { addPerm(code, "") } for _, child := range node.Nested { collectPermCodes(child, addPerm) } } // extractTagValue 从 struct tag 字符串中提取指定 key 的值 // tag 格式:`json:"username" perm:"data:user:email:write"` func extractTagValue(tag, key string) string { tag = strings.Trim(tag, "`") search := key + `:"` idx := strings.Index(tag, search) if idx == -1 { return "" } rest := tag[idx+len(search):] end := strings.Index(rest, `"`) if end == -1 { return "" } val := rest[:end] // 取 json tag 的第一段(去掉 omitempty 等) if key == "json" { val = strings.Split(val, ",")[0] if val == "-" { return "" } } return val } func apiToDataCode(apiCode string) string { if !strings.HasPrefix(apiCode, "api:") { return "" } return "data:" + apiCode[4:] } func generatePermName(code string) string { parts := strings.Split(code, ":") if len(parts) < 2 { return code } actionMap := map[string]string{ "read": "读取", "write": "写入", "create": "创建", "update": "更新", "delete": "删除", "list": "列表", "detail": "详情", } capitalize := func(s string) string { if len(s) == 0 { return s } return strings.ToUpper(s[:1]) + s[1:] } // 字段权限:data:model:field:read|write → "{Model} {field} 字段 [读取|写入]" if parts[0] == "data" && len(parts) == 4 { action := actionMap[parts[3]] if action == "" { action = parts[3] } return capitalize(parts[1]) + " " + parts[2] + "字段 " + action } // 接口/数据权限:api:model:action 或 data:model:action var result []string for i := 1; i < len(parts); i++ { p := parts[i] if mapped, ok := actionMap[p]; ok { result = append(result, mapped) } else { result = append(result, capitalize(p)) } } return strings.Join(result, " ") }