package hotime import ( "encoding/json" "fmt" "os" "path/filepath" "sort" "strings" . "code.hoteas.com/golang/hotime/common" ) type swaggerTestCase struct { Name string `json:"name"` Method string `json:"method"` Passed bool `json:"passed"` Query map[string]interface{} `json:"query,omitempty"` Json interface{} `json:"json,omitempty"` Form map[string]interface{} `json:"form,omitempty"` HasFile bool `json:"hasFile,omitempty"` FileField string `json:"fileField,omitempty"` Response map[string]interface{} `json:"response,omitempty"` } func (that *TestApp) GenerateSwagger(title, version, outputDir string) error { if outputDir == "" { outputDir = "tpt" } swaggerDir := filepath.Join(outputDir, "swagger") if err := os.MkdirAll(swaggerDir, os.ModePerm); err != nil { return fmt.Errorf("创建 swagger 目录失败: %w", err) } that.collector.mu.Lock() records := make([]TestRecord, len(that.collector.Records)) copy(records, that.collector.Records) that.collector.mu.Unlock() pathRecords := map[string][]TestRecord{} for _, r := range records { pathRecords[r.Path] = append(pathRecords[r.Path], r) } endpoints := []map[string]interface{}{} for projName, projDef := range that.projs { for ctrName, ctr := range projDef.Proj { for methodName := range ctr { apiPath := "/" + projName + "/" + ctrName + "/" + methodName ctrTest, hasCtr := projDef.Tests[ctrName] hasTest := false var apiTest ApiTestDef if hasCtr { apiTest, hasTest = ctrTest[methodName] } summary := methodName if hasTest { summary = apiTest.Desc } recs := pathRecords[apiPath] httpMethod := "POST" if len(recs) > 0 && recs[0].Method != "" { httpMethod = strings.ToUpper(recs[0].Method) } var cases []swaggerTestCase for _, r := range recs { tc := swaggerTestCase{ Name: r.CaseName, Method: r.Method, Passed: r.Passed, Response: r.ResponseBody, } if r.Query != nil { tc.Query = r.Query } if r.JsonBody != nil { tc.Json = r.JsonBody } if r.FormBody != nil { tc.Form = r.FormBody } if r.HasFile { tc.HasFile = true tc.FileField = r.FileField } cases = append(cases, tc) } endpoints = append(endpoints, map[string]interface{}{ "path": apiPath, "project": projName, "ctr": ctrName, "method": httpMethod, "summary": summary, "tested": hasTest, "cases": cases, }) } } } sort.Slice(endpoints, func(i, j int) bool { return endpoints[i]["path"].(string) < endpoints[j]["path"].(string) }) spec := map[string]interface{}{ "title": title, "version": version, "endpoints": endpoints, } specJSON, err := json.MarshalIndent(spec, "", " ") if err != nil { return fmt.Errorf("序列化 JSON 失败: %w", err) } if err := os.WriteFile(filepath.Join(swaggerDir, "api-spec.json"), specJSON, os.ModePerm); err != nil { return fmt.Errorf("写入 api-spec.json 失败: %w", err) } if err := os.WriteFile(filepath.Join(swaggerDir, "index.html"), []byte(apiConsoleHTML()), os.ModePerm); err != nil { return fmt.Errorf("写入 index.html 失败: %w", err) } fmt.Printf("Swagger 文档已生成: %s\n", swaggerDir) return nil } func apiConsoleHTML() string { return `