doc-generate/internal/swagger/response.go

202 lines
5.5 KiB
Go
Raw Permalink Normal View History

2025-09-30 08:16:44 +00:00
package swagger
import (
"net/http"
2025-09-30 09:33:29 +00:00
"strings"
2025-09-30 08:16:44 +00:00
"github.com/go-openapi/spec"
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
)
func jsonResponseFromType(ctx Context, atDoc apiSpec.AtDoc, tp apiSpec.Type) *spec.Responses {
2025-09-30 09:33:29 +00:00
return jsonResponseFromTypeWithDocs(ctx, atDoc, tp, nil)
}
func jsonResponseFromTypeWithDocs(ctx Context, atDoc apiSpec.AtDoc, tp apiSpec.Type, handlerDoc apiSpec.Doc) *spec.Responses {
// 首先檢查是否有 @respdoc 註解(從 handlerDoc 或 atDoc
var respDocs []RespDoc
if len(handlerDoc) > 0 {
respDocs = parseRespDocsFromDoc(handlerDoc)
}
if len(respDocs) == 0 {
respDocs = parseRespDocs(atDoc)
}
if len(respDocs) > 0 {
return jsonResponseFromRespDocs(ctx, atDoc, respDocs, tp)
}
// 原有邏輯:使用默認的 200 回應
2025-09-30 08:16:44 +00:00
if tp == nil {
return &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: {
ResponseProps: spec.ResponseProps{
Description: "",
Schema: &spec.Schema{},
},
},
},
},
}
}
props := spec.SchemaProps{
AdditionalProperties: mapFromGoType(ctx, tp),
Items: itemsFromGoType(ctx, tp),
}
if ctx.UseDefinitions {
structName, ok := containsStruct(tp)
if ok {
props.Ref = spec.MustCreateRef(getRefName(structName))
return &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: {
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
},
},
},
},
},
}
}
}
p, _ := propertiesFromType(ctx, tp)
props.Type = typeFromGoType(ctx, tp)
props.Properties = p
return &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: {
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
},
},
},
},
},
}
}
2025-09-30 09:33:29 +00:00
// jsonResponseFromRespDocs 從 @respdoc 註解生成多狀態碼回應
func jsonResponseFromRespDocs(ctx Context, atDoc apiSpec.AtDoc, respDocs []RespDoc, defaultType apiSpec.Type) *spec.Responses {
responses := &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: make(map[int]spec.Response),
},
}
for _, respDoc := range respDocs {
// 如果有單一 Schema直接使用
if respDoc.Schema != "" {
tp := findTypeByName(ctx.Api, respDoc.Schema)
if tp == nil && respDoc.StatusCode == http.StatusOK && defaultType != nil {
// 如果找不到類型且是 200使用默認類型
tp = defaultType
}
response := createResponseFromType(ctx, atDoc, tp, respDoc.Description)
responses.StatusCodeResponses[respDoc.StatusCode] = response
continue
}
// 如果有多個業務錯誤碼BizCodes為 Swagger 2.0 創建一個通用回應
// OpenAPI 3.0 會在 convertSwagger2ToOpenAPI3 中特殊處理
if len(respDoc.BizCodes) > 0 {
// 對於 Swagger 2.0,我們使用 oneOf/anyOf 概念的註釋
// 但 Swagger 2.0 不支持 oneOf所以使用第一個類型作為示例
// 並在描述中列出所有可能的類型
var firstType apiSpec.Type
descriptions := []string{}
for code, bizCode := range respDoc.BizCodes {
tp := findTypeByName(ctx.Api, bizCode.Schema)
if firstType == nil && tp != nil {
firstType = tp
}
desc := code + ": " + bizCode.Schema
if bizCode.Description != "" {
desc += " - " + bizCode.Description
}
descriptions = append(descriptions, desc)
}
description := respDoc.Description
if len(descriptions) > 0 {
if description != "" {
description += "\n\n"
}
description += "Possible errors:\n" + strings.Join(descriptions, "\n")
}
response := createResponseFromType(ctx, atDoc, firstType, description)
// 在 VendorExtensible 中存儲業務錯誤碼信息,供 OpenAPI 3.0 使用
if response.Extensions == nil {
response.Extensions = make(spec.Extensions)
}
response.Extensions["x-biz-codes"] = respDoc.BizCodes
responses.StatusCodeResponses[respDoc.StatusCode] = response
}
}
// 如果沒有定義 200 回應,添加默認的
if _, ok := responses.StatusCodeResponses[http.StatusOK]; !ok && defaultType != nil {
responses.StatusCodeResponses[http.StatusOK] = createResponseFromType(ctx, atDoc, defaultType, "")
}
return responses
}
// createResponseFromType 從類型創建回應對象
func createResponseFromType(ctx Context, atDoc apiSpec.AtDoc, tp apiSpec.Type, description string) spec.Response {
if tp == nil {
return spec.Response{
ResponseProps: spec.ResponseProps{
Description: description,
Schema: &spec.Schema{},
},
}
}
props := spec.SchemaProps{
AdditionalProperties: mapFromGoType(ctx, tp),
Items: itemsFromGoType(ctx, tp),
}
if ctx.UseDefinitions {
structName, ok := containsStruct(tp)
if ok {
props.Ref = spec.MustCreateRef(getRefName(structName))
return spec.Response{
ResponseProps: spec.ResponseProps{
Description: description,
Schema: &spec.Schema{
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
},
},
}
}
}
p, _ := propertiesFromType(ctx, tp)
props.Type = typeFromGoType(ctx, tp)
props.Properties = p
return spec.Response{
ResponseProps: spec.ResponseProps{
Description: description,
Schema: &spec.Schema{
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
},
},
}
}