From 3ea5af0709b9f91b29ce90f7ba70e61925b2510e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 6 Jan 2026 23:25:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=88=9D=E5=A7=8B=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openapi/generate.go | 246 +++++++++++++++++++++------------------ openapi/generate_test.go | 9 +- openapi/option.go | 33 ++++++ 3 files changed, 172 insertions(+), 116 deletions(-) create mode 100644 openapi/option.go diff --git a/openapi/generate.go b/openapi/generate.go index 36e6b49..23c2219 100644 --- a/openapi/generate.go +++ b/openapi/generate.go @@ -19,108 +19,130 @@ import ( "github.com/getkin/kin-openapi/openapi3" ) -// NewGenerate 生成文档实例 -func NewGenerate() *Generate { - return &Generate{ - doc: &openapi3.T{ - Extensions: map[string]any{}, - OpenAPI: "3.1.0", - Components: &openapi3.Components{ - Extensions: map[string]any{}, - Origin: &openapi3.Origin{ - Key: &openapi3.Location{ - Line: 0, - Column: 0, - }, - Fields: make(map[string]openapi3.Location), - }, - Schemas: map[string]*openapi3.SchemaRef{}, - Parameters: map[string]*openapi3.ParameterRef{}, - Headers: map[string]*openapi3.HeaderRef{}, - RequestBodies: map[string]*openapi3.RequestBodyRef{}, - Responses: map[string]*openapi3.ResponseRef{}, - SecuritySchemes: map[string]*openapi3.SecuritySchemeRef{}, - Examples: map[string]*openapi3.ExampleRef{}, - Links: map[string]*openapi3.LinkRef{}, - Callbacks: map[string]*openapi3.CallbackRef{}, - }, - Info: &openapi3.Info{ - Extensions: map[string]any{}, - Origin: &openapi3.Origin{ - Key: &openapi3.Location{ - Line: 0, - Column: 0, - }, - Fields: make(map[string]openapi3.Location), - }, - Title: "服务 API接口 文档", - Description: "服务 API接口 文档", - TermsOfService: "", - Contact: &openapi3.Contact{ - Extensions: map[string]any{}, - Origin: &openapi3.Origin{ - Key: &openapi3.Location{ - Line: 0, - Column: 0, - }, - Fields: make(map[string]openapi3.Location), - }, - Name: "developer", - URL: "", - Email: "developer@test.com", - }, - License: &openapi3.License{ - Extensions: map[string]any{}, - Origin: &openapi3.Origin{ - Key: &openapi3.Location{ - Line: 0, - Column: 0, - }, - Fields: make(map[string]openapi3.Location), - }, - Name: consts.LicenseApache20, - URL: consts.LicenseUrlTable[consts.LicenseApache20], - }, - Version: "0.0.1", - }, - Paths: &openapi3.Paths{ - Extensions: map[string]any{}, - Origin: &openapi3.Origin{ - Key: &openapi3.Location{ - Line: 0, - Column: 0, - }, - Fields: make(map[string]openapi3.Location), - }, - }, - Security: []openapi3.SecurityRequirement{}, - Servers: []*openapi3.Server{ - { - Extensions: nil, - Origin: nil, - URL: "http://127.0.0.1:8080/v1", - Description: "开发环境", - Variables: nil, - }, - }, - Tags: []*openapi3.Tag{}, - /*ExternalDocs: &openapi3.ExternalDocs{ - Extensions: map[string]any{}, - Origin: nil, - Description: "", - URL: "", - },*/ - }, +var ( + DocManager = &Generate{ + docTable: make(map[string]*openapi3.T), } +) + +// NewOpenApiDoc 生成文档实例 +func NewOpenApiDoc(optionFunc ...OptionFunc) *openapi3.T { + t := &openapi3.T{ + Extensions: map[string]any{}, + OpenAPI: "3.1.0", + Components: &openapi3.Components{ + Extensions: map[string]any{}, + Origin: &openapi3.Origin{ + Key: &openapi3.Location{ + Line: 0, + Column: 0, + }, + Fields: make(map[string]openapi3.Location), + }, + Schemas: map[string]*openapi3.SchemaRef{}, + Parameters: map[string]*openapi3.ParameterRef{}, + Headers: map[string]*openapi3.HeaderRef{}, + RequestBodies: map[string]*openapi3.RequestBodyRef{}, + Responses: map[string]*openapi3.ResponseRef{}, + SecuritySchemes: map[string]*openapi3.SecuritySchemeRef{}, + Examples: map[string]*openapi3.ExampleRef{}, + Links: map[string]*openapi3.LinkRef{}, + Callbacks: map[string]*openapi3.CallbackRef{}, + }, + Info: &openapi3.Info{ + Extensions: map[string]any{}, + Origin: &openapi3.Origin{ + Key: &openapi3.Location{ + Line: 0, + Column: 0, + }, + Fields: make(map[string]openapi3.Location), + }, + Title: "服务 API接口 文档", + Description: "服务 API接口 文档", + TermsOfService: "", + Contact: &openapi3.Contact{ + Extensions: map[string]any{}, + Origin: &openapi3.Origin{ + Key: &openapi3.Location{ + Line: 0, + Column: 0, + }, + Fields: make(map[string]openapi3.Location), + }, + Name: "developer", + URL: "", + Email: "developer@test.com", + }, + License: &openapi3.License{ + Extensions: map[string]any{}, + Origin: &openapi3.Origin{ + Key: &openapi3.Location{ + Line: 0, + Column: 0, + }, + Fields: make(map[string]openapi3.Location), + }, + Name: consts.LicenseApache20, + URL: consts.LicenseUrlTable[consts.LicenseApache20], + }, + Version: "0.0.1", + }, + Paths: &openapi3.Paths{ + Extensions: map[string]any{}, + Origin: &openapi3.Origin{ + Key: &openapi3.Location{ + Line: 0, + Column: 0, + }, + Fields: make(map[string]openapi3.Location), + }, + }, + Security: []openapi3.SecurityRequirement{}, + Servers: []*openapi3.Server{ + { + Extensions: nil, + Origin: nil, + URL: "http://127.0.0.1:8080", + Description: "开发环境", + Variables: nil, + }, + }, + Tags: []*openapi3.Tag{}, + /*ExternalDocs: &openapi3.ExternalDocs{ + Extensions: map[string]any{}, + Origin: nil, + Description: "", + URL: "", + },*/ + } + for _, item := range optionFunc { + item(t) + } + return t } // Generate 生成 OpenApi 标准规范的文档 type Generate struct { - doc *openapi3.T + docTable map[string]*openapi3.T +} + +// DocData 获取一个文档数据 +func (g *Generate) DocData(docFlag string) *openapi3.T { + return g.docTable[docFlag] +} + +func (g *Generate) NewOpenApiDoc(docFlag string, docOption ...OptionFunc) *openapi3.T { + t := NewOpenApiDoc() + for _, item := range docOption { + item(t) + } + g.docTable[docFlag] = t + return t } // AddApiDoc 添加接口文档 -func (g *Generate) AddApiDoc(apiMeta define.UriConfig, request any, response any) error { +func (g *Generate) AddApiDoc(docFlag string, apiMeta define.UriConfig, request any, response any) error { var ( err error requestType reflect.Type @@ -144,7 +166,7 @@ func (g *Generate) AddApiDoc(apiMeta define.UriConfig, request any, response any } schemaData := GenerateOpenAPISchema(requestType) - apiOperate, isRead := g.initApiConfig(apiMeta) + apiOperate, isRead := g.initApiConfig(docFlag, apiMeta) requestTypeStr := requestType.String() if isRead { for paramName, paramConfig := range schemaData.Value.Properties { @@ -196,12 +218,12 @@ func (g *Generate) AddApiDoc(apiMeta define.UriConfig, request any, response any } // 初始化接口配置 - if _, exist := g.doc.Components.Schemas[requestTypeStr]; !exist { - g.doc.Components.Schemas[requestTypeStr] = schemaData + if _, exist := g.docTable[docFlag].Components.Schemas[requestTypeStr]; !exist { + g.docTable[docFlag].Components.Schemas[requestTypeStr] = schemaData } responseTypeStr := responseType.String() - if _, exist := g.doc.Components.Schemas[responseTypeStr]; !exist { - g.doc.Components.Schemas[responseTypeStr] = GenerateOpenAPISchema(responseType) + if _, exist := g.docTable[docFlag].Components.Schemas[responseTypeStr]; !exist { + g.docTable[docFlag].Components.Schemas[responseTypeStr] = GenerateOpenAPISchema(responseType) } desc := "请求成功" apiOperate.Responses.Set(fmt.Sprintf("%v", http.StatusOK), &openapi3.ResponseRef{ @@ -217,7 +239,7 @@ func (g *Generate) AddApiDoc(apiMeta define.UriConfig, request any, response any consts.MimeTypeJson: { Extensions: nil, Origin: nil, - Schema: g.doc.Components.Schemas[responseTypeStr], + Schema: g.docTable[docFlag].Components.Schemas[responseTypeStr], Example: nil, Examples: nil, Encoding: nil, @@ -230,9 +252,9 @@ func (g *Generate) AddApiDoc(apiMeta define.UriConfig, request any, response any } // initApiConfig 初始化接口配置, 并返回 operation 以及 是否读请求 -func (g *Generate) initApiConfig(apiMeta define.UriConfig) (*openapi3.Operation, bool) { - if nil == g.doc.Paths.Value(apiMeta.Path) { - g.doc.Paths.Set(apiMeta.Path, &openapi3.PathItem{ +func (g *Generate) initApiConfig(docFlag string, apiMeta define.UriConfig) (*openapi3.Operation, bool) { + if nil == g.docTable[docFlag].Paths.Value(apiMeta.Path) { + g.docTable[docFlag].Paths.Set(apiMeta.Path, &openapi3.PathItem{ Extensions: nil, Origin: nil, Ref: "", @@ -261,29 +283,29 @@ func (g *Generate) initApiConfig(apiMeta define.UriConfig) (*openapi3.Operation, isRead := false switch apiMeta.RequestMethod { case http.MethodGet: - g.doc.Paths.Value(apiMeta.Path).Get = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Get = newOperate isRead = true case http.MethodHead: - g.doc.Paths.Value(apiMeta.Path).Head = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Head = newOperate isRead = true case http.MethodConnect: - g.doc.Paths.Value(apiMeta.Path).Connect = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Connect = newOperate isRead = true case http.MethodOptions: - g.doc.Paths.Value(apiMeta.Path).Options = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Options = newOperate isRead = true case http.MethodTrace: - g.doc.Paths.Value(apiMeta.Path).Trace = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Trace = newOperate isRead = true // 读请求 case http.MethodPost: - g.doc.Paths.Value(apiMeta.Path).Post = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Post = newOperate case http.MethodPut: - g.doc.Paths.Value(apiMeta.Path).Put = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Put = newOperate case http.MethodPatch: - g.doc.Paths.Value(apiMeta.Path).Patch = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Patch = newOperate case http.MethodDelete: - g.doc.Paths.Value(apiMeta.Path).Delete = newOperate + g.docTable[docFlag].Paths.Value(apiMeta.Path).Delete = newOperate // 写请求 } return newOperate, isRead diff --git a/openapi/generate_test.go b/openapi/generate_test.go index ca06d11..985a944 100644 --- a/openapi/generate_test.go +++ b/openapi/generate_test.go @@ -34,20 +34,21 @@ func TestGenerate_AddApiDoc(t *testing.T) { UpdatedAt *time.Time `json:"updated_at,omitempty" description:"更新时间"` Category *Category `json:"category,omitempty" description:"分类"` } - instance := NewGenerate() - instance.AddApiDoc(define.UriConfig{ + docFlag := "demo" + DocManager.NewOpenApiDoc(docFlag) + DocManager.AddApiDoc(docFlag, define.UriConfig{ Path: "/a/b/c", RequestMethod: http.MethodGet, TagList: []string{"test"}, Desc: "测试接口", }, Category{}, Product{}) - instance.AddApiDoc(define.UriConfig{ + DocManager.AddApiDoc(docFlag, define.UriConfig{ Path: "/a/b/c", RequestMethod: http.MethodPost, TagList: []string{"test"}, Desc: "测试接口", }, Category{}, Product{}) // 输出 JSON - data, _ := json.MarshalIndent(instance.doc, "", " ") + data, _ := json.MarshalIndent(DocManager.docTable[docFlag], "", " ") fmt.Println(string(data)) } diff --git a/openapi/option.go b/openapi/option.go new file mode 100644 index 0000000..3603a99 --- /dev/null +++ b/openapi/option.go @@ -0,0 +1,33 @@ +// Package openapi ... +// +// Description : openapi ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2026-01-06 22:48 +package openapi + +import "github.com/getkin/kin-openapi/openapi3" + +// OptionFunc 设置文档选项 +type OptionFunc func(t *openapi3.T) + +// WithServers 设置文档服务器 +func WithServers(serverList openapi3.Servers) OptionFunc { + return func(t *openapi3.T) { + if len(serverList) == 0 { + return + } + t.Servers = serverList + } +} + +// WithInfo 文档基础信息 +func WithInfo(info *openapi3.Info) OptionFunc { + return func(t *openapi3.T) { + if nil == info { + return + } + t.Info = info + } +}