doc-generate/internal/swagger/respdoc.go

250 lines
5.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package swagger
import (
"fmt"
"regexp"
"strconv"
"strings"
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
)
// RespDoc 表示一個回應文檔定義
type RespDoc struct {
StatusCode int // HTTP 狀態碼
Schema string // 回應類型名稱(單一回應)
BizCodes map[string]BizCode // 業務錯誤碼映射(多個回應)
Description string // 描述
}
// BizCode 表示業務錯誤碼定義
type BizCode struct {
Code string // 業務錯誤碼(如 300101
Schema string // 對應的類型名稱
Description string // 描述
}
var (
// @respdoc-200 (TypeName) description
respdocSimpleRegex = regexp.MustCompile(`@respdoc-(\d+)\s+\((\w+)\)(?:\s+(.+))?`)
// 業務錯誤碼格式: 300101: (ValidationError) description
bizCodeRegex = regexp.MustCompile(`(\d+):\s+\((\w+)\)(?:\s+(.+))?`)
)
// parseRespDocsFromDoc 從 Doc 數組中解析所有 @respdoc 定義
func parseRespDocsFromDoc(doc apiSpec.Doc) []RespDoc {
// Doc 是字符串數組,合併為一個文本
var text string
for _, line := range doc {
text += line + "\n"
}
return parseRespDocsFromText(text)
}
// parseRespDocs 從 AtDoc 註解中解析所有 @respdoc 定義
func parseRespDocs(atDoc apiSpec.AtDoc) []RespDoc {
return parseRespDocsFromText(atDoc.Text)
}
// parseRespDocsFromText 從文本中解析所有 @respdoc 定義
func parseRespDocsFromText(text string) []RespDoc {
var respDocs []RespDoc
if text == "" {
return respDocs
}
lines := strings.Split(text, "\n")
var currentRespDoc *RespDoc
var inMultiLine bool
for _, line := range lines {
line = strings.TrimSpace(line)
// 跳過空行和純註釋符號
if line == "" || line == "/*" || line == "*/" || strings.HasPrefix(line, "//") {
continue
}
// 移除行首的註釋符號
line = strings.TrimPrefix(line, "*")
line = strings.TrimSpace(line)
// 檢查是否為 @respdoc 開頭
if strings.HasPrefix(line, "@respdoc-") {
// 保存上一個 respDoc
if currentRespDoc != nil {
respDocs = append(respDocs, *currentRespDoc)
}
// 解析新的 respdoc
currentRespDoc = parseRespDocLine(line)
// 檢查是否為多行格式(含有左括號但不含右括號,或右括號後還有註釋符號)
if strings.Contains(line, "(") && !strings.Contains(line, ")") {
inMultiLine = true
} else {
inMultiLine = false
}
// 如果是單行格式,直接添加
if !inMultiLine && currentRespDoc != nil {
respDocs = append(respDocs, *currentRespDoc)
currentRespDoc = nil
}
} else if inMultiLine && currentRespDoc != nil {
// 處理多行格式中的業務錯誤碼
if strings.Contains(line, ":") && strings.Contains(line, "(") {
bizCode := parseBizCodeLine(line)
if bizCode != nil {
if currentRespDoc.BizCodes == nil {
currentRespDoc.BizCodes = make(map[string]BizCode)
}
currentRespDoc.BizCodes[bizCode.Code] = *bizCode
}
}
// 檢查是否為多行結束(含有右括號和註釋)
if strings.Contains(line, ")") && strings.Contains(line, "//") {
// 提取描述
parts := strings.SplitN(line, "//", 2)
if len(parts) == 2 {
currentRespDoc.Description = strings.TrimSpace(parts[1])
}
respDocs = append(respDocs, *currentRespDoc)
currentRespDoc = nil
inMultiLine = false
}
}
}
// 添加最後一個 respDoc
if currentRespDoc != nil {
respDocs = append(respDocs, *currentRespDoc)
}
return respDocs
}
// parseRespDocLine 解析單行 @respdoc 定義
func parseRespDocLine(line string) *RespDoc {
// 移除 @respdoc- 前綴
line = strings.TrimPrefix(line, "@respdoc-")
// 提取狀態碼
parts := strings.SplitN(line, " ", 2)
if len(parts) == 0 {
return nil
}
statusCode, err := strconv.Atoi(strings.TrimSpace(parts[0]))
if err != nil {
return nil
}
respDoc := &RespDoc{
StatusCode: statusCode,
}
if len(parts) < 2 {
return respDoc
}
rest := strings.TrimSpace(parts[1])
// 檢查是否為簡單格式: (TypeName) description
matches := respdocSimpleRegex.FindStringSubmatch("@respdoc-" + line)
if len(matches) >= 3 {
respDoc.Schema = matches[2]
if len(matches) >= 4 {
respDoc.Description = strings.TrimSpace(matches[3])
}
return respDoc
}
// 檢查是否為多行格式開始: (
if strings.HasPrefix(rest, "(") && !strings.Contains(rest, ")") {
// 多行格式,等待後續行處理
return respDoc
}
return respDoc
}
// parseBizCodeLine 解析業務錯誤碼行
func parseBizCodeLine(line string) *BizCode {
matches := bizCodeRegex.FindStringSubmatch(line)
if len(matches) < 3 {
return nil
}
bizCode := &BizCode{
Code: matches[1],
Schema: matches[2],
}
if len(matches) >= 4 {
bizCode.Description = strings.TrimSpace(matches[3])
}
return bizCode
}
// findTypeByName 在 API 規範中查找類型定義
func findTypeByName(api *apiSpec.ApiSpec, typeName string) apiSpec.Type {
if typeName == "" {
return nil
}
for _, tp := range api.Types {
if defStruct, ok := tp.(apiSpec.DefineStruct); ok {
if defStruct.Name() == typeName {
return tp
}
}
}
return nil
}
// getHTTPStatusText 獲取 HTTP 狀態碼的標準文本
func getHTTPStatusText(code int) string {
switch code {
case 200:
return "OK"
case 201:
return "Created"
case 202:
return "Accepted"
case 204:
return "No Content"
case 400:
return "Bad Request"
case 401:
return "Unauthorized"
case 403:
return "Forbidden"
case 404:
return "Not Found"
case 405:
return "Method Not Allowed"
case 409:
return "Conflict"
case 422:
return "Unprocessable Entity"
case 429:
return "Too Many Requests"
case 500:
return "Internal Server Error"
case 502:
return "Bad Gateway"
case 503:
return "Service Unavailable"
case 504:
return "Gateway Timeout"
default:
return fmt.Sprintf("HTTP %d", code)
}
}