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 fieldPermMap struct { Request map[string]string // json字段名 → permCode Response map[string]string // json字段名 → permCode } type collectResult struct { perms []permDecl routePerms []routePermDecl fieldPerms map[string]fieldPermMap // "METHOD /path" → fieldPermMap } 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, }) fm := fieldPermMap{ Request: make(map[string]string), Response: make(map[string]string), } if route.RequestType != nil { for jsonField, permTag := range extractFieldPermsDeep(route.RequestType, typeIndex) { fm.Request[jsonField] = permTag addPerm(permTag, "") } } if route.ResponseType != nil { for jsonField, permTag := range extractFieldPermsDeep(route.ResponseType, typeIndex) { fm.Response[jsonField] = permTag addPerm(permTag, "") } } if len(fm.Request) > 0 || len(fm.Response) > 0 { result.fieldPerms[key] = fm } } } return result } // extractFieldPermsDeep 递归展开嵌套类型,提取所有 perm tag func extractFieldPermsDeep(t *TypeDef, typeIndex map[string]*TypeDef) map[string]string { result := make(map[string]string) if t == nil { return result } collectFieldPerms(t.Members, typeIndex, result) return result } func collectFieldPerms(members []Member, typeIndex map[string]*TypeDef, result map[string]string) { for _, m := range members { jsonName := extractTagValue(m.Tag, "json") if jsonName == "" { jsonName = m.Name } permCode := extractTagValue(m.Tag, "perm") if permCode != "" { result[jsonName] = permCode continue } // 没有 perm tag,尝试展开嵌套类型(去掉 [] 前缀) rawName := strings.TrimPrefix(m.Type.RawName, "[]") if nested, ok := typeIndex[rawName]; ok && len(nested.Members) > 0 { collectFieldPerms(nested.Members, typeIndex, result) } } } // 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, " ") }