202 lines
5.5 KiB
Go
202 lines
5.5 KiB
Go
package swagger
|
||
|
||
import (
|
||
"net/http"
|
||
"strings"
|
||
|
||
"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 {
|
||
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 回應
|
||
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),
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
// 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),
|
||
},
|
||
},
|
||
}
|
||
}
|