代码拉取完成,页面将自动刷新
package autodoc
import (
"errors"
"fmt"
"io/ioutil"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
)
const (
template=`
####%s
请求地址:%s
请求方式:%s
####请求参数
| 参数 | 类型 | 必填 | 说明 |
| :---: | :---: | :---: | :---: |
%s
%s
####响应参数
%s
`
structTagField="field"
)
//自动化文档
type AutoDoc struct {
open bool //自动文档开关
url string// 请求地址
titleMap map[string/*url*/]string/*方法注释*/ //url标题map
method string //请求方式
file string //文档文件地址
requiredFields []string //必填字段
requestCareField []string //非必填字段,为空,表示所有结构体字段都是非必填字段
requestParams []RequestParam //存储请求数据
requestRemark string //请求备注数据
responseString string //生成的响应数据
controllerDirFiles []string //控制器下文件
controllerDir string //控制器文件路径
}
//请求参数
type RequestParam struct {
Field string //字段
FieldType string //字段类型
Required bool //必填字段
Desc string //字段说明
}
func NewAutoDoc(controllerDir string) *AutoDoc{
this:=&AutoDoc{
titleMap:make(map[string]string),
controllerDir:controllerDir,
}
this.loadControllerFiles().setTitleMap()
return this
}
//设置文档文件地址
func (this *AutoDoc)getFile() (file string,err error) {
urlArr:=strings.Split(this.url,"/")
if len(urlArr)== 0 {
return "",errors.New("未设置文档请求地址")
}
return this.file+fmt.Sprintf("%s.md",urlArr[0]),nil
}
//设置请求地址
func (this *AutoDoc)SetUrl(url string) *AutoDoc {
this.url=url
return this
}
//设置请求方式
func (this *AutoDoc)SetMethod(method string) *AutoDoc {
this.method=method
return this
}
func (this *AutoDoc)SetRequestCareField(fields ...string) *AutoDoc {
this.requestCareField=append(this.requestCareField,fields...)
return this
}
func (this *AutoDoc)setRequestRecursive(t reflect.Type) {
var (
requestParams []RequestParam
)
for i:=0;i<t.NumField();i++{
field:=t.Field(i)
//不处理
if field.Tag.Get("json")== "-" {
continue
}
if len(this.requestCareField)!= 0 {
if !inArray(field.Name,append(this.requestCareField,this.requiredFields...)) {
continue
}
}
fieldType:=field.Type.String()
if strings.Contains(fieldType, ".") {
fieldTypeArr:=strings.Split(fieldType,".")
if strings.Contains(fieldType, "[]") {
fieldType="[]"+fieldTypeArr[len(fieldTypeArr)-1]
}else{
fieldType=fieldTypeArr[len(fieldTypeArr)-1]
}
}
item:= RequestParam{
Field:field.Name,
FieldType:fieldType,
Required:inArray(field.Name,this.requiredFields),
Desc:field.Tag.Get(structTagField),
}
if item.Field==item.FieldType {
this.setRequestRecursive(field.Type)
continue
}
switch field.Type.Kind() {
case reflect.Struct:
this.setRequestRemark(field.Type)
case reflect.Slice,reflect.Ptr:
this.setRequestRemark(field.Type.Elem())
}
if item.Desc== "" {
item.Desc=item.Field
}
requestParams=append(requestParams,item)
}
this.SetRequestParam(requestParams)
}
//设置请求参数
func (this *AutoDoc)SetRequest(param interface{},requiredFields ...string) *AutoDoc {
this.requiredFields=append(this.requiredFields,requiredFields...)
var (
t reflect.Type
)
t,_,_=getStructTV(param)
this.setRequestRecursive(t)
return this
}
//设置备注
func (this *AutoDoc)setRequestRemark(t reflect.Type) {
if t.Kind()==reflect.Slice {
t=t.Elem()
}
if t.Kind()!=reflect.Struct {
return
}
this.requestRemark+=fmt.Sprintf("%s:{\n",t.Name())
for i:=0;i<t.NumField();i++{
field:=t.Field(i)
if field.Tag.Get("json")== "-" {
continue
}
tag:=field.Tag.Get(structTagField)
if tag!= "" {
tag="//"+tag
}
this.requestRemark+=fmt.Sprintf(" %s %s %s\n",field.Name,field.Type.String(),tag)
}
this.requestRemark+="}\n"
}
//设置参数数据
func (this *AutoDoc) SetRequestParam(requestParams []RequestParam) *AutoDoc {
this.requestParams=append(this.requestParams,requestParams...)
return this
}
func (this *AutoDoc)getRequestParamString() (requestString string) {
boolConvert:= func(a bool) int{
if a {
return 1
}
return 0
}
//必填顺序调整
sort.SliceStable(this.requestParams, func(i, j int) bool {
return boolConvert(this.requestParams[i].Required)>boolConvert(this.requestParams[j].Required)
})
for _,v:=range this.requestParams {
required:=""
if v.Required {
required="是"
}
requestString+=fmt.Sprintf("| %s | %s | %s | %s |\n",v.Field,v.FieldType,required,v.Desc)
}
return
}
//设置响应参数
func (this *AutoDoc)SetResponse(param interface{}) *AutoDoc {
if param== nil {
return this
}
t,_,_:=getStructTV(param)
this.responseString=this.responseStringRecursive(t,""," ",false)
return this
}
//递归生成输出参数
func (this *AutoDoc)responseStringRecursive(t reflect.Type,name string,space string,embed bool) (s string) {
if !embed{
if t.Kind()==reflect.Slice {
if name!= "" {
name+=":"
}
name+="[]"
t=t.Elem()
}else{
if name!= "" {
name+=":"
}
}
s=fmt.Sprintf("%s%s{\n",space,name)
}
if t.Kind()==reflect.Ptr {
t=t.Elem()
}
if t.Kind()!=reflect.Struct {
return ""
}
for i:=0;i<t.NumField();i++{
field:=t.Field(i)
if field.Tag.Get("json")== "-" {
continue
}
switch field.Type.Kind() {
case reflect.Ptr:
fieldType:=field.Type.String()
if strings.Contains(fieldType, ".") {
fieldTypeArr:=strings.Split(fieldType,".")
if strings.Contains(fieldType, "[]") {
fieldType="[]"+fieldTypeArr[len(fieldTypeArr)-1]
}else{
fieldType=fieldTypeArr[len(fieldTypeArr)-1]
}
}
if fieldType==field.Name {
//嵌入的结构体
s+=this.responseStringRecursive(field.Type,"",space,true)
}else{
s+=this.responseStringRecursive(field.Type,field.Name," "+space,false)
}
case reflect.Slice,reflect.Struct,reflect.Interface:
s+=this.responseStringRecursive(field.Type,field.Name," "+space,false)
case reflect.String,reflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64,reflect.Uint8,reflect.Uint16,reflect.Uint32,reflect.Uint64,reflect.Float32,reflect.Float64:
tag:=field.Tag.Get(structTagField)
if tag!= "" {
tag="//"+tag
}
s+=fmt.Sprintf("%s%s %s %s\n"," "+space,field.Name,field.Type.String(),tag)
}
}
if !embed {
s+=space+"}\n"
}
return s
}
//自动生成文档
func (this *AutoDoc)MakeAutoFile() (err error) {
var (
file string
fileContent []byte
newContent string
)
if file,err=this.getFile();err!= nil {
return
}
fileContent,_=ioutil.ReadFile(file);
//已经产生过地址,不再生成
if strings.Contains(string(fileContent),this.url) {
return
}
newContent+=string(fileContent)+this.make()
return ioutil.WriteFile(file,[]byte(newContent),0664)
}
//临时输出
func (this *AutoDoc)Create() {
err:=ioutil.WriteFile("./autodoc.md",[]byte(this.make()),0664)
if err != nil {
fmt.Println("AutoDoc.Create 错误:",err)
}
}
func (this *AutoDoc)make()(newContent string) {
var (
remark string
)
if this.requestRemark!= "" {
remark=fmt.Sprintf("####备注```\n%s```\n",this.requestRemark)
}
this.responseString=strings.Trim(this.responseString," ")
this.responseString=strings.Trim(this.responseString,"\n")
responseString:=fmt.Sprintf("```\n"+`{
"Code": 0,
"Data":%s,
"Msg": ""
}
`+"```\n",this.responseString)
newContent+="---\n"
newContent+=fmt.Sprintf(template,this.getTitle(),this.url,this.method,this.getRequestParamString(),remark,responseString)
newContent+="---\n"
return
}
//设置url和title的关系
func (this *AutoDoc)setTitleMap() {
for _,file:=range this.controllerDirFiles {
this.parseControllerFiles(this.controllerDir+file)
}
}
//获取接口标题
func (this *AutoDoc)getTitle() string {
return this.titleMap[this.url]
}
//载入controller下所有控制器文件
func (this *AutoDoc)loadControllerFiles() *AutoDoc {
files, _ := ioutil.ReadDir(this.controllerDir)
for _, f := range files {
if strings.Contains(f.Name(), "_test.go") ||!strings.Contains(f.Name(),".go") {
continue
}
this.controllerDirFiles=append(this.controllerDirFiles,f.Name())
}
return this
}
//从文件获取带有权限的方法
func (this *AutoDoc)parseControllerFiles(file string) {
content,err:=ioutil.ReadFile(file)
if err != nil {
fmt.Println(err)
return
}
re:=regexp.MustCompile(fmt.Sprintf(`//([^\n]+)\nfunc \(this \*([A-Z][A-Z0-9a-z]*Controller)\) ([A-Z][A-Z0-9a-z]*)\(\) \{`))
res:=re.FindAllStringSubmatch(string(content),-1)
for _,v:=range res {
if len(v)!= 4 {
continue
}
name,_:= parseMethodNameAndPermTreeId(v[1])
permUrl:= parsePermUrl(v[2],v[3])
if name!="" && permUrl!= "" {
this.titleMap[permUrl]=name
}
}
return
}
//从注释中解析出方法名称和PermTreeId 测试数据:comment="获取登录用户信息{perm:22}"
func parseMethodNameAndPermTreeId(comment string)(name string,permTreeId uint32) {
if !strings.Contains(comment, "{perm") {
name=comment
return
}
comment=strings.TrimRight(comment,"}")
commentArr:=strings.Split(comment,"{perm:")
switch len(commentArr) {
case 1:
name=commentArr[0]
case 2:
name=commentArr[0]
id,_:=strconv.Atoi(commentArr[1])
permTreeId=uint32(id)
}
return
}
//从注释中解析出permUrl 测试数据: controller="UserController" action="AuthUser"
func parsePermUrl(controller string ,action string)(permUrl string) {
if controller=="" || action== "" {
return
}
controller=strings.Replace(controller,"Controller","",1)
permUrl=strings.ToLower(controller)+"/"+strings.ToLower(action)
return
}
func inArray(item interface{},array interface{}) bool {
if reflect.TypeOf(array).Kind() != reflect.Slice {
return false
}
n := reflect.ValueOf(array).Len()
for i := 0; i < n; i++ {
if reflect.ValueOf(array).Index(i).Interface() == reflect.ValueOf(item).Interface() {
return true
}
}
return false
}
func isStruct(t reflect.Type) bool {
return t.Kind() == reflect.Struct
}
//判定是否为结构体指针
func isStructPtr(t reflect.Type) bool {
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}
//获取结构体或者指针的类型和值
func getStructTV(obj interface{}) (reflect.Type, reflect.Value, error) {
objT := reflect.TypeOf(obj)
objV := reflect.ValueOf(obj)
switch {
case isStruct(objT):
case isStructPtr(objT):
objT = objT.Elem()
objV = objV.Elem()
default:
return objT, objV, fmt.Errorf("%v must be a struct or a struct pointer", obj)
}
return objT, objV, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。