collect.go 5.1 KB

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