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), }, }, } }