collect.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. )
  7. type permDecl struct {
  8. Code string
  9. Name string
  10. }
  11. type routePermDecl struct {
  12. Method string
  13. Path string
  14. PermCode string
  15. DataCode string
  16. }
  17. type fieldNode struct {
  18. Fields map[string]string
  19. Nested map[string]*fieldNode
  20. }
  21. type collectResult struct {
  22. perms []permDecl
  23. routePerms []routePermDecl
  24. fieldPerms map[string]*fieldPermMap // "METHOD /path" → fieldPermMap
  25. }
  26. type fieldPermMap struct {
  27. Request *fieldNode
  28. Response *fieldNode
  29. }
  30. func collect(input *PluginInput) *collectResult {
  31. seen := make(map[string]bool)
  32. result := &collectResult{
  33. fieldPerms: make(map[string]*fieldPermMap),
  34. }
  35. if input == nil || input.Api == nil {
  36. return result
  37. }
  38. // 建立类型名 → TypeDef 的索引,用于展开嵌套类型
  39. typeIndex := make(map[string]*TypeDef, len(input.Api.Types))
  40. for i := range input.Api.Types {
  41. typeIndex[input.Api.Types[i].RawName] = &input.Api.Types[i]
  42. }
  43. addPerm := func(code, name string) {
  44. if seen[code] || code == "" {
  45. return
  46. }
  47. seen[code] = true
  48. if name == "" {
  49. name = generatePermName(code)
  50. }
  51. result.perms = append(result.perms, permDecl{Code: code, Name: name})
  52. }
  53. for _, group := range input.Api.Service.Groups {
  54. for _, route := range group.Routes {
  55. method := strings.ToUpper(route.Method)
  56. path := route.Path
  57. key := method + " " + path
  58. permCode := ""
  59. permName := ""
  60. if route.AtDoc.Properties != nil {
  61. permCode = route.AtDoc.Properties["perm"]
  62. permName = route.AtDoc.Properties["summary"]
  63. }
  64. if permCode == "" {
  65. fmt.Fprintf(os.Stderr, "WARN: handler %s (%s %s) has no perm declaration, treated as public\n",
  66. route.Handler, method, path)
  67. continue
  68. }
  69. addPerm(permCode, permName)
  70. dataCode := apiToDataCode(permCode)
  71. if dataCode != "" {
  72. addPerm(dataCode, permName)
  73. }
  74. result.routePerms = append(result.routePerms, routePermDecl{
  75. Method: method,
  76. Path: path,
  77. PermCode: permCode,
  78. DataCode: dataCode,
  79. })
  80. var reqNode *fieldNode
  81. if route.RequestType != nil {
  82. reqNode = collectFieldPerms(route.RequestType.Members, typeIndex)
  83. collectPermCodes(reqNode, addPerm)
  84. }
  85. var respNode *fieldNode
  86. if route.ResponseType != nil {
  87. respNode = collectFieldPerms(route.ResponseType.Members, typeIndex)
  88. collectPermCodes(respNode, addPerm)
  89. }
  90. if reqNode != nil || respNode != nil {
  91. result.fieldPerms[key] = &fieldPermMap{
  92. Request: reqNode,
  93. Response: respNode,
  94. }
  95. }
  96. }
  97. }
  98. return result
  99. }
  100. // collectFieldPerms 递归构建 fieldNode 树
  101. func collectFieldPerms(members []Member, typeIndex map[string]*TypeDef) *fieldNode {
  102. node := &fieldNode{
  103. Fields: make(map[string]string),
  104. Nested: make(map[string]*fieldNode),
  105. }
  106. for _, m := range members {
  107. jsonName := extractTagValue(m.Tag, "json")
  108. if jsonName == "" {
  109. jsonName = m.Name
  110. }
  111. permCode := extractTagValue(m.Tag, "perm")
  112. if permCode != "" {
  113. node.Fields[jsonName] = permCode
  114. }
  115. rawName := strings.TrimPrefix(m.Type.RawName, "[]")
  116. if nested, ok := typeIndex[rawName]; ok && len(nested.Members) > 0 {
  117. child := collectFieldPerms(nested.Members, typeIndex)
  118. if len(child.Fields) > 0 || len(child.Nested) > 0 {
  119. node.Nested[jsonName] = child
  120. }
  121. }
  122. }
  123. if len(node.Fields) == 0 && len(node.Nested) == 0 {
  124. return nil
  125. }
  126. return node
  127. }
  128. // collectPermCodes 从 fieldNode 树中提取所有 permCode 并注册
  129. func collectPermCodes(node *fieldNode, addPerm func(string, string)) {
  130. if node == nil {
  131. return
  132. }
  133. for _, code := range node.Fields {
  134. addPerm(code, "")
  135. }
  136. for _, child := range node.Nested {
  137. collectPermCodes(child, addPerm)
  138. }
  139. }
  140. // extractTagValue 从 struct tag 字符串中提取指定 key 的值
  141. // tag 格式:`json:"username" perm:"data:user:email:write"`
  142. func extractTagValue(tag, key string) string {
  143. tag = strings.Trim(tag, "`")
  144. search := key + `:"`
  145. idx := strings.Index(tag, search)
  146. if idx == -1 {
  147. return ""
  148. }
  149. rest := tag[idx+len(search):]
  150. end := strings.Index(rest, `"`)
  151. if end == -1 {
  152. return ""
  153. }
  154. val := rest[:end]
  155. // 取 json tag 的第一段(去掉 omitempty 等)
  156. if key == "json" {
  157. val = strings.Split(val, ",")[0]
  158. if val == "-" {
  159. return ""
  160. }
  161. }
  162. return val
  163. }
  164. func apiToDataCode(apiCode string) string {
  165. if !strings.HasPrefix(apiCode, "api:") {
  166. return ""
  167. }
  168. return "data:" + apiCode[4:]
  169. }
  170. func generatePermName(code string) string {
  171. parts := strings.Split(code, ":")
  172. if len(parts) < 2 {
  173. return code
  174. }
  175. actionMap := map[string]string{
  176. "read": "读取",
  177. "write": "写入",
  178. "create": "创建",
  179. "update": "更新",
  180. "delete": "删除",
  181. "list": "列表",
  182. "detail": "详情",
  183. }
  184. capitalize := func(s string) string {
  185. if len(s) == 0 {
  186. return s
  187. }
  188. return strings.ToUpper(s[:1]) + s[1:]
  189. }
  190. // 字段权限:data:model:field:read|write → "{Model} {field} 字段 [读取|写入]"
  191. if parts[0] == "data" && len(parts) == 4 {
  192. action := actionMap[parts[3]]
  193. if action == "" {
  194. action = parts[3]
  195. }
  196. return capitalize(parts[1]) + " " + parts[2] + "字段 " + action
  197. }
  198. // 接口/数据权限:api:model:action 或 data:model:action
  199. var result []string
  200. for i := 1; i < len(parts); i++ {
  201. p := parts[i]
  202. if mapped, ok := actionMap[p]; ok {
  203. result = append(result, mapped)
  204. } else {
  205. result = append(result, capitalize(p))
  206. }
  207. }
  208. return strings.Join(result, " ")
  209. }