mirror of
https://github.com/xinliangnote/go-gin-api.git
synced 2024-04-21 12:31:46 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3823eee2ca | ||
|
|
0b6b9dd69f | ||
|
|
7327275b7a | ||
|
|
9b5028e9de | ||
|
|
cc8012b09d | ||
|
|
334bfbb4aa |
@@ -18,8 +18,9 @@
|
||||
1. 支持 errno 统一定义错误码
|
||||
1. 支持 [zap](https://go.uber.org/zap) 日志收集
|
||||
1. 支持 [viper](https://github.com/spf13/viper) 配置文件解析
|
||||
1. 支持 [grom](https://gorm.io/gorm) 数据库组件
|
||||
1. 支持 [gorm](https://gorm.io/gorm) 数据库组件
|
||||
1. 支持 [go-redis](https://github.com/go-redis/redis/v7) 组件
|
||||
1. 支持 RESTful API 返回值规范
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
## 执行命令
|
||||
1. 定义生成的表,设置 config 中 cmd.genTables,可以自定义设置多张表,为空表示生成库中所有的表,如果设置多个表可用','分割;
|
||||
1. 在根目录下执行脚本文件:`./scripts/gormgen.sh`;
|
||||
|
||||
## 参考
|
||||
- https://github.com/MohamedBassem/gormgen
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/cmd/gormgen/pkg"
|
||||
)
|
||||
|
||||
var (
|
||||
input string
|
||||
structs []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flagStructs := flag.String("structs", "", "[Required] The name of schema structs to generate structs for, comma seperated\n")
|
||||
flagInput := flag.String("input", "", "[Required] The name of the input file dir\n")
|
||||
flag.Parse()
|
||||
|
||||
if *flagStructs == "" || *flagInput == "" {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
structs = strings.Split(*flagStructs, ",")
|
||||
input = *flagInput
|
||||
}
|
||||
|
||||
func main() {
|
||||
gen := pkg.NewGenerator(input)
|
||||
p := pkg.NewParser(input)
|
||||
if err := gen.ParserAST(p, structs).Generate().Format().Flush(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// fieldConfig
|
||||
type fieldConfig struct {
|
||||
FieldName string
|
||||
ColumnName string
|
||||
FieldType string
|
||||
HumpName string
|
||||
}
|
||||
|
||||
// structConfig
|
||||
type structConfig struct {
|
||||
config
|
||||
StructName string
|
||||
OnlyFields []fieldConfig
|
||||
OptionFields []fieldConfig
|
||||
}
|
||||
|
||||
type ImportPkg struct {
|
||||
Pkg string
|
||||
}
|
||||
|
||||
type structHelpers struct {
|
||||
Titelize func(string) string
|
||||
}
|
||||
|
||||
type config struct {
|
||||
PkgName string
|
||||
Helpers structHelpers
|
||||
QueryBuilderName string
|
||||
}
|
||||
|
||||
// The Generator is the one responsible for generating the code, adding the imports, formating, and writing it to the file.
|
||||
type Generator struct {
|
||||
buf map[string]*bytes.Buffer
|
||||
inputFile string
|
||||
config config
|
||||
structConfigs []structConfig
|
||||
}
|
||||
|
||||
// NewGenerator function creates an instance of the generator given the name of the output file as an argument.
|
||||
func NewGenerator(outputFile string) *Generator {
|
||||
return &Generator{
|
||||
buf: map[string]*bytes.Buffer{},
|
||||
inputFile: outputFile,
|
||||
}
|
||||
}
|
||||
|
||||
// ParserAST parse by go file
|
||||
func (g *Generator) ParserAST(p *Parser, structs []string) (ret *Generator) {
|
||||
for _, v := range structs {
|
||||
g.buf[gorm.ToDBName(v)] = new(bytes.Buffer)
|
||||
}
|
||||
g.structConfigs = p.Parse()
|
||||
g.config.PkgName = p.pkg.Name
|
||||
g.config.Helpers = structHelpers{
|
||||
Titelize: strings.Title,
|
||||
}
|
||||
g.config.QueryBuilderName = SQLColumnToHumpStyle(p.pkg.Name) + "QueryBuilder"
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *Generator) checkConfig() (err error) {
|
||||
if len(g.config.PkgName) == 0 {
|
||||
err = errors.New("package name dose'n set")
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(g.structConfigs); i++ {
|
||||
g.structConfigs[i].config = g.config
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Generate executes the template and store it in an internal buffer.
|
||||
func (g *Generator) Generate() *Generator {
|
||||
if err := g.checkConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, v := range g.structConfigs {
|
||||
if _, ok := g.buf[gorm.ToDBName(v.StructName)]; !ok {
|
||||
continue
|
||||
}
|
||||
if err := outputTemplate.Execute(g.buf[gorm.ToDBName(v.StructName)], v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// Format function formats the output of the generation.
|
||||
func (g *Generator) Format() *Generator {
|
||||
for k := range g.buf {
|
||||
formattedOutput, err := format.Source(g.buf[k].Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.buf[k] = bytes.NewBuffer(formattedOutput)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// Flush function writes the output to the output file.
|
||||
func (g *Generator) Flush() error {
|
||||
for k := range g.buf {
|
||||
filename := g.inputFile + "/gen_" + strings.ToLower(k) + ".go"
|
||||
if err := ioutil.WriteFile(filename, g.buf[k].Bytes(), 0777); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// The Parser is used to parse a directory and expose information about the structs defined in the files of this directory.
|
||||
type Parser struct {
|
||||
dir string
|
||||
pkg *build.Package
|
||||
parsedFiles []*ast.File
|
||||
}
|
||||
|
||||
// NewParser create a new parser instance.
|
||||
func NewParser(dir string) *Parser {
|
||||
return &Parser{
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
// getPackage parse dir get go file and package
|
||||
func (p *Parser) getPackage() {
|
||||
pkg, err := build.Default.ImportDir(p.dir, build.ImportComment)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot process directory %s: %s", p.dir, err)
|
||||
}
|
||||
p.pkg = pkg
|
||||
|
||||
}
|
||||
|
||||
// parseGoFiles parse go file
|
||||
func (p *Parser) parseGoFiles() {
|
||||
var parsedFiles []*ast.File
|
||||
fs := token.NewFileSet()
|
||||
for _, file := range p.pkg.GoFiles {
|
||||
file = p.dir + "/" + file
|
||||
parsedFile, err := parser.ParseFile(fs, file, nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing package: %s: %s\n", file, err)
|
||||
}
|
||||
parsedFiles = append(parsedFiles, parsedFile)
|
||||
}
|
||||
p.parsedFiles = parsedFiles
|
||||
}
|
||||
|
||||
// parseTypes parse type of struct
|
||||
func (p *Parser) parseTypes(file *ast.File) (ret []structConfig) {
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
decl, ok := n.(*ast.GenDecl)
|
||||
if !ok || decl.Tok != token.TYPE {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, spec := range decl.Specs {
|
||||
var (
|
||||
data structConfig
|
||||
)
|
||||
typeSpec, _ok := spec.(*ast.TypeSpec)
|
||||
if !_ok {
|
||||
continue
|
||||
}
|
||||
// We only care about struct declaration (for now)
|
||||
var structType *ast.StructType
|
||||
if structType, ok = typeSpec.Type.(*ast.StructType); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
data.StructName = typeSpec.Name.Name
|
||||
for _, v := range structType.Fields.List {
|
||||
var (
|
||||
optionField fieldConfig
|
||||
)
|
||||
|
||||
// type is ident, get onlyField type
|
||||
if t, _ok := v.Type.(*ast.Ident); _ok {
|
||||
optionField.FieldType = t.String()
|
||||
} else {
|
||||
if v.Tag != nil {
|
||||
if strings.Contains(v.Tag.Value, "gorm") && strings.Contains(v.Tag.Value, "time") {
|
||||
optionField.FieldType = "time.Time"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get file name
|
||||
if len(v.Names) > 0 {
|
||||
optionField.FieldName = v.Names[0].String()
|
||||
optionField.ColumnName = gorm.ToDBName(optionField.FieldName)
|
||||
optionField.HumpName = SQLColumnToHumpStyle(optionField.ColumnName)
|
||||
}
|
||||
|
||||
data.OptionFields = append(data.OptionFields, optionField)
|
||||
}
|
||||
|
||||
ret = append(ret, data)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse should be called before any type querying for the parser. It takes the directory to be parsed and extracts all the structs defined in this directory.
|
||||
func (p *Parser) Parse() (ret []structConfig) {
|
||||
var (
|
||||
data []structConfig
|
||||
)
|
||||
p.getPackage()
|
||||
p.parseGoFiles()
|
||||
for _, f := range p.parsedFiles {
|
||||
data = append(data, p.parseTypes(f)...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package pkg
|
||||
|
||||
import "text/template"
|
||||
|
||||
// Make sure that the template compiles during package initialization
|
||||
func parseTemplateOrPanic(t string) *template.Template {
|
||||
tpl, err := template.New("output_template").Parse(t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tpl
|
||||
}
|
||||
|
||||
var outputTemplate = parseTemplateOrPanic(`
|
||||
///////////////////////////////////////////////////////////
|
||||
// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT //
|
||||
// ANY CHANGES DONE HERE WILL BE LOST //
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
package {{.PkgName}}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewModel() *{{.StructName}} {
|
||||
return new({{.StructName}})
|
||||
}
|
||||
|
||||
func NewQueryBuilder() *{{.QueryBuilderName}} {
|
||||
return new({{.QueryBuilderName}})
|
||||
}
|
||||
|
||||
func (t *{{.StructName}}) Create(db *gorm.DB) (id int32, err error) {
|
||||
if err = db.Create(t).Error; err != nil {
|
||||
return 0, errors.Wrap(err, "create err")
|
||||
}
|
||||
return t.Id, nil
|
||||
}
|
||||
|
||||
func (t *{{.StructName}}) Delete(db *gorm.DB) (err error) {
|
||||
if err = db.Delete(t).Error; err != nil {
|
||||
return errors.Wrap(err, "delete err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *{{.StructName}}) Updates(db *gorm.DB, m map[string]interface{}) (err error) {
|
||||
if err = db.Model(&UserDemo{}).Where("id = ?", t.Id).Updates(m).Error; err != nil {
|
||||
return errors.Wrap(err, "updates err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type {{.QueryBuilderName}} struct {
|
||||
order []string
|
||||
where []struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}
|
||||
limit int
|
||||
offset int
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) buildQuery(db *gorm.DB) *gorm.DB {
|
||||
ret := db
|
||||
for _, where := range qb.where {
|
||||
ret = ret.Where(where.prefix, where.value)
|
||||
}
|
||||
for _, order := range qb.order {
|
||||
ret = ret.Order(order)
|
||||
}
|
||||
ret = ret.Limit(qb.limit).Offset(qb.offset)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) Count(db *gorm.DB) (int64, error) {
|
||||
var c int64
|
||||
res := qb.buildQuery(db).Model(&{{.StructName}}{}).Count(&c)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
c = 0
|
||||
}
|
||||
return c, res.Error
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) First(db *gorm.DB) (*{{.StructName}}, error) {
|
||||
ret := &{{.StructName}}{}
|
||||
res := qb.buildQuery(db).First(ret)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
ret = nil
|
||||
}
|
||||
return ret, res.Error
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) QueryOne(db *gorm.DB) (*{{.StructName}}, error) {
|
||||
qb.limit = 1
|
||||
ret, err := qb.QueryAll(db)
|
||||
if len(ret) > 0 {
|
||||
return ret[0], err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) QueryAll(db *gorm.DB) ([]*{{.StructName}}, error) {
|
||||
var ret []*{{.StructName}}
|
||||
err := qb.buildQuery(db).Find(&ret).Error
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) Limit(limit int) *{{.QueryBuilderName}} {
|
||||
qb.limit = limit
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) Offset(offset int) *{{.QueryBuilderName}} {
|
||||
qb.offset = offset
|
||||
return qb
|
||||
}
|
||||
|
||||
{{$queryBuilderName := .QueryBuilderName}}
|
||||
{{range .OptionFields}}
|
||||
func (qb *{{$queryBuilderName}}) Where{{call $.Helpers.Titelize .FieldName}}(p db_repo.Predicate, value {{.FieldType}}) *{{$queryBuilderName}} {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "{{.ColumnName}}", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *{{$queryBuilderName}}) OrderBy{{call $.Helpers.Titelize .FieldName}}(asc bool) *{{$queryBuilderName}} {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "{{.ColumnName}} " + order)
|
||||
return qb
|
||||
}
|
||||
{{end}}
|
||||
`)
|
||||
@@ -0,0 +1,18 @@
|
||||
package pkg
|
||||
|
||||
import "strings"
|
||||
|
||||
// SQLColumnToHumpStyle sql转换成驼峰模式
|
||||
func SQLColumnToHumpStyle(in string) (ret string) {
|
||||
for i := 0; i < len(in); i++ {
|
||||
if i > 0 && in[i-1] == '_' && in[i] != '_' {
|
||||
s := strings.ToUpper(string(in[i]))
|
||||
ret += s
|
||||
} else if in[i] == '_' {
|
||||
continue
|
||||
} else {
|
||||
ret += string(in[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
## 执行命令
|
||||
|
||||
```$xslt
|
||||
// test_handler 为 ./internal/api/controller/ 中的包名
|
||||
./scripts/handlergen.sh test_handler
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var handlerName string
|
||||
|
||||
func init() {
|
||||
handler := flag.String("handler", "", "请输入需要生成的 handler 名称\n")
|
||||
flag.Parse()
|
||||
|
||||
handlerName = strings.ToLower(*handler)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fs := token.NewFileSet()
|
||||
file := fmt.Sprintf("./internal/api/controller/%s/handler.go", handlerName)
|
||||
parsedFile, err := parser.ParseFile(fs, file, nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing package: %s: %s\n", file, err)
|
||||
}
|
||||
|
||||
ast.Inspect(parsedFile, func(n ast.Node) bool {
|
||||
decl, ok := n.(*ast.GenDecl)
|
||||
if !ok || decl.Tok != token.TYPE {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, spec := range decl.Specs {
|
||||
typeSpec, _ok := spec.(*ast.TypeSpec)
|
||||
if !_ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var interfaceType *ast.InterfaceType
|
||||
if interfaceType, ok = typeSpec.Type.(*ast.InterfaceType); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, v := range interfaceType.Methods.List {
|
||||
if len(v.Names) > 0 {
|
||||
if v.Names[0].String() == "i" {
|
||||
continue
|
||||
}
|
||||
|
||||
filepath := "./internal/api/controller/" + handlerName
|
||||
filename := fmt.Sprintf("%s/func_%s.go", filepath, strings.ToLower(v.Names[0].String()))
|
||||
funcFile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("create and open func file error %v\n", err.Error())
|
||||
}
|
||||
|
||||
funcContent := fmt.Sprintf("package %s\n\n", handlerName)
|
||||
funcContent += "import (\n"
|
||||
funcContent += `"github.com/xinliangnote/go-gin-api/internal/pkg/core"`
|
||||
funcContent += "\n)\n\n"
|
||||
funcContent += fmt.Sprintf("\n\ntype %sRequest struct {}\n\n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("type %sResponse struct {}\n\n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("func (h *handler) %s() core.HandlerFunc { \n return func(c core.Context) {\n\n}}", v.Names[0].String())
|
||||
|
||||
funcFile.WriteString(funcContent)
|
||||
funcFile.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func Lcfirst(str string) string {
|
||||
for i, v := range str {
|
||||
return string(unicode.ToLower(v)) + str[i+1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/env"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type tableInfo struct {
|
||||
Name string `db:"table_name"` // name
|
||||
Comment sql.NullString `db:"table_comment"` // comment
|
||||
}
|
||||
|
||||
type tableColumn struct {
|
||||
OrdinalPosition uint16 `db:"ORDINAL_POSITION"` // position
|
||||
ColumnName string `db:"COLUMN_NAME"` // name
|
||||
ColumnType string `db:"COLUMN_TYPE"` // column_type
|
||||
DataType string `db:"DATA_TYPE"` // data_type
|
||||
ColumnKey sql.NullString `db:"COLUMN_KEY"` // key
|
||||
IsNullable string `db:"IS_NULLABLE"` // nullable
|
||||
Extra sql.NullString `db:"EXTRA"` // extra
|
||||
ColumnComment sql.NullString `db:"COLUMN_COMMENT"` // comment
|
||||
ColumnDefault sql.NullString `db:"COLUMN_DEFAULT"` // default value
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 初始化 logger
|
||||
loggers, err := logger.NewJSONLogger(
|
||||
logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName(), env.Active().Value())),
|
||||
logger.WithTimeLayout("2006-01-02 15:04:05"),
|
||||
logger.WithFileP(configs.ProjectLogFile()),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer loggers.Sync()
|
||||
|
||||
// 初始化 DB
|
||||
dbRepo, err := db.New()
|
||||
if err != nil {
|
||||
loggers.Fatal("new db err", zap.Error(err))
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := dbRepo.DbWClose(); err != nil {
|
||||
loggers.Error("dbw close err", zap.Error(err))
|
||||
}
|
||||
|
||||
if err := dbRepo.DbRClose(); err != nil {
|
||||
loggers.Error("dbr close err", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
dbName := configs.Get().MySQL.Read.Name
|
||||
genTables := configs.Get().Cmd.GenTables
|
||||
tables, err := queryTables(dbRepo.GetDbR(), dbName, genTables)
|
||||
if err != nil {
|
||||
loggers.Error("query tables of database err", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
|
||||
filepath := "./internal/api/repository/db_repo/" + table.Name + "_repo"
|
||||
_ = os.Mkdir(filepath, 0766)
|
||||
|
||||
mdName := fmt.Sprintf("%s/gen_table.md", filepath)
|
||||
mdFile, err := os.OpenFile(mdName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("create and open markdown file error %v\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
modelName := fmt.Sprintf("%s/gen_model.go", filepath)
|
||||
modelFile, err := os.OpenFile(modelName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("create and open model file error %v\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
modelContent := fmt.Sprintf("package %s%s\n", table.Name, "_repo")
|
||||
modelContent += fmt.Sprintf(`import "time"`)
|
||||
modelContent += fmt.Sprintf("\n\n// %s \n", table.Comment.String)
|
||||
modelContent += fmt.Sprintf("//go:generate gormgen -structs %s -input . \n", capitalize(table.Name))
|
||||
modelContent += fmt.Sprintf("type %s struct {\n", capitalize(table.Name))
|
||||
|
||||
tableContent := fmt.Sprintf("#### %s.%s \n", dbName, table.Name)
|
||||
if table.Comment.String != "" {
|
||||
tableContent += table.Comment.String + "\n"
|
||||
}
|
||||
tableContent += "\n" +
|
||||
"| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |\n" +
|
||||
"| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |\n"
|
||||
|
||||
columnInfo, columnInfoErr := queryTableColumn(dbRepo.GetDbR(), dbName, table.Name)
|
||||
if columnInfoErr != nil {
|
||||
continue
|
||||
}
|
||||
for _, info := range columnInfo {
|
||||
tableContent += fmt.Sprintf(
|
||||
"| %d | %s | %s | %s | %s | %s | %s | %s |\n",
|
||||
info.OrdinalPosition,
|
||||
info.ColumnName,
|
||||
strings.ReplaceAll(strings.ReplaceAll(info.ColumnComment.String, "|", "\\|"), "\n", ""),
|
||||
info.ColumnType,
|
||||
info.ColumnKey.String,
|
||||
info.IsNullable,
|
||||
info.Extra.String,
|
||||
info.ColumnDefault.String,
|
||||
)
|
||||
|
||||
if textType(info.DataType) == "time.Time" {
|
||||
modelContent += fmt.Sprintf("%s %s `%s` // %s\n", capitalize(info.ColumnName), textType(info.DataType), "gorm:\"time\"", info.ColumnComment.String)
|
||||
} else {
|
||||
modelContent += fmt.Sprintf("%s %s // %s\n", capitalize(info.ColumnName), textType(info.DataType), info.ColumnComment.String)
|
||||
}
|
||||
}
|
||||
|
||||
mdFile.WriteString(tableContent)
|
||||
mdFile.Close()
|
||||
|
||||
modelContent += "}\n"
|
||||
modelFile.WriteString(modelContent)
|
||||
modelFile.Close()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func queryTables(db *gorm.DB, dbName string, tableName string) ([]tableInfo, error) {
|
||||
var tableCollect []tableInfo
|
||||
var tableArray []string
|
||||
var commentArray []sql.NullString
|
||||
|
||||
sqlTables := fmt.Sprintf("SELECT `table_name`,`table_comment` FROM `information_schema`.`tables` WHERE `table_schema`= '%s'", dbName)
|
||||
rows, err := db.Raw(sqlTables).Rows()
|
||||
if err != nil {
|
||||
return tableCollect, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var info tableInfo
|
||||
err = rows.Scan(&info.Name, &info.Comment)
|
||||
if err != nil {
|
||||
fmt.Printf("execute query tables action error,had ignored, detail is [%v]\n", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
tableCollect = append(tableCollect, info)
|
||||
tableArray = append(tableArray, info.Name)
|
||||
commentArray = append(commentArray, info.Comment)
|
||||
}
|
||||
|
||||
// filter tables when specified tables params
|
||||
if tableName != "" {
|
||||
tableCollect = nil
|
||||
chooseTables := strings.Split(tableName, ",")
|
||||
indexMap := make(map[int]int)
|
||||
for _, item := range chooseTables {
|
||||
subIndexMap := getTargetIndexMap(tableArray, item)
|
||||
for k, v := range subIndexMap {
|
||||
if _, ok := indexMap[k]; ok {
|
||||
continue
|
||||
}
|
||||
indexMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if len(indexMap) != 0 {
|
||||
for _, v := range indexMap {
|
||||
var info tableInfo
|
||||
info.Name = tableArray[v]
|
||||
info.Comment = commentArray[v]
|
||||
tableCollect = append(tableCollect, info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tableCollect, err
|
||||
}
|
||||
|
||||
func queryTableColumn(db *gorm.DB, dbName string, tableName string) ([]tableColumn, error) {
|
||||
// 定义承载列信息的切片
|
||||
var columns []tableColumn
|
||||
|
||||
sqlTableColumn := fmt.Sprintf("SELECT `ORDINAL_POSITION`,`COLUMN_NAME`,`COLUMN_TYPE`,`DATA_TYPE`,`COLUMN_KEY`,`IS_NULLABLE`,`EXTRA`,`COLUMN_COMMENT`,`COLUMN_DEFAULT` FROM `information_schema`.`columns` WHERE `table_schema`= '%s' AND `table_name`= '%s' ORDER BY `ORDINAL_POSITION` ASC",
|
||||
dbName, tableName)
|
||||
|
||||
rows, err := db.Raw(sqlTableColumn).Rows()
|
||||
if err != nil {
|
||||
fmt.Printf("execute query table column action error, detail is [%v]\n", err.Error())
|
||||
return columns, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var column tableColumn
|
||||
err = rows.Scan(
|
||||
&column.OrdinalPosition,
|
||||
&column.ColumnName,
|
||||
&column.ColumnType,
|
||||
&column.DataType,
|
||||
&column.ColumnKey,
|
||||
&column.IsNullable,
|
||||
&column.Extra,
|
||||
&column.ColumnComment,
|
||||
&column.ColumnDefault)
|
||||
if err != nil {
|
||||
fmt.Printf("query table column scan error, detail is [%v]\n", err.Error())
|
||||
return columns, err
|
||||
}
|
||||
columns = append(columns, column)
|
||||
}
|
||||
|
||||
return columns, err
|
||||
}
|
||||
|
||||
func getTargetIndexMap(tableNameArr []string, item string) map[int]int {
|
||||
indexMap := make(map[int]int)
|
||||
for i := 0; i < len(tableNameArr); i++ {
|
||||
if match, _ := regexp.MatchString(item, tableNameArr[i]); match {
|
||||
if _, ok := indexMap[i]; ok {
|
||||
continue
|
||||
}
|
||||
indexMap[i] = i
|
||||
}
|
||||
}
|
||||
return indexMap
|
||||
}
|
||||
|
||||
func capitalize(s string) string {
|
||||
var upperStr string
|
||||
chars := strings.Split(s, "_")
|
||||
for _, val := range chars {
|
||||
vv := []rune(val)
|
||||
for i := 0; i < len(vv); i++ {
|
||||
if i == 0 {
|
||||
if vv[i] >= 97 && vv[i] <= 122 {
|
||||
vv[i] -= 32
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
} else {
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return upperStr
|
||||
}
|
||||
|
||||
func textType(s string) string {
|
||||
var mysqlTypeToGoType = map[string]string{
|
||||
"tinyint": "int32",
|
||||
"smallint": "int32",
|
||||
"mediumint": "int32",
|
||||
"int": "int32",
|
||||
"integer": "int64",
|
||||
"bigint": "int64",
|
||||
"float": "float64",
|
||||
"double": "float64",
|
||||
"decimal": "float64",
|
||||
"date": "string",
|
||||
"time": "string",
|
||||
"year": "string",
|
||||
"datetime": "time.Time",
|
||||
"timestamp": "time.Time",
|
||||
"char": "string",
|
||||
"varchar": "string",
|
||||
"tinyblob": "string",
|
||||
"tinytext": "string",
|
||||
"blob": "string",
|
||||
"text": "string",
|
||||
"mediumblob": "string",
|
||||
"mediumtext": "string",
|
||||
"longblob": "string",
|
||||
"longtext": "string",
|
||||
}
|
||||
return mysqlTypeToGoType[s]
|
||||
}
|
||||
@@ -63,6 +63,10 @@ type Config struct {
|
||||
Private string `toml:"private"`
|
||||
Public string `toml:"public"`
|
||||
} `toml:"rsa"`
|
||||
|
||||
Cmd struct {
|
||||
GenTables string `toml:"genTables"`
|
||||
} `toml:"cmd"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -77,3 +77,5 @@ xLYEFN9h2MWYgxLm9Z0rLMrWwMM+E2rCs8tsxAD5sO9RZMJPl1C0FIsMR53ngqbz
|
||||
owIDAQAB
|
||||
-----END PUBLIC KEY-----'
|
||||
|
||||
[cmd]
|
||||
genTables = 'user_demo'
|
||||
+114
-23
@@ -43,9 +43,15 @@ var doc = `{
|
||||
"summary": "获取授权信息",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/demo.authResponse"
|
||||
"$ref": "#/definitions/demo_handler.authResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +81,7 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "用户信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -92,6 +98,18 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,11 +130,11 @@ var doc = `{
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateRequest"
|
||||
"$ref": "#/definitions/user_handler.createRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -129,9 +147,21 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateResponse"
|
||||
"$ref": "#/definitions/user_handler.createResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +169,7 @@ var doc = `{
|
||||
},
|
||||
"/user/delete/{id}": {
|
||||
"patch": {
|
||||
"description": "删除用户 - 更新 is_deleted = 1",
|
||||
"description": "删除用户",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -149,7 +179,7 @@ var doc = `{
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "删除用户 - 更新 is_deleted = 1",
|
||||
"summary": "删除用户",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
@@ -168,7 +198,22 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息"
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_handler.deleteResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,9 +249,21 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.DetailResponse"
|
||||
"$ref": "#/definitions/user_handler.detailResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,11 +285,11 @@ var doc = `{
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDRequest"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -245,9 +302,21 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +324,20 @@ var doc = `{
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"demo.authResponse": {
|
||||
"code.Failure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"description": "业务码",
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"description": "描述信息",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"demo_handler.authResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorization": {
|
||||
@@ -268,7 +350,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateRequest": {
|
||||
"user_handler.createRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mobile": {
|
||||
@@ -285,7 +367,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateResponse": {
|
||||
"user_handler.createResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -294,7 +376,16 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.DetailResponse": {
|
||||
"user_handler.deleteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "用户主键ID",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_handler.detailResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -302,7 +393,7 @@ var doc = `{
|
||||
"type": "integer"
|
||||
},
|
||||
"mobile": {
|
||||
"description": "手机号",
|
||||
"description": "手机号(脱敏)",
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
@@ -315,7 +406,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDRequest": {
|
||||
"user_handler.updateNickNameByIDRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -328,7 +419,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDResponse": {
|
||||
"user_handler.updateNickNameByIDResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+114
-23
@@ -26,9 +26,15 @@
|
||||
"summary": "获取授权信息",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/demo.authResponse"
|
||||
"$ref": "#/definitions/demo_handler.authResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +64,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "用户信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -75,6 +81,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,11 +113,11 @@
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateRequest"
|
||||
"$ref": "#/definitions/user_handler.createRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -112,9 +130,21 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateResponse"
|
||||
"$ref": "#/definitions/user_handler.createResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +152,7 @@
|
||||
},
|
||||
"/user/delete/{id}": {
|
||||
"patch": {
|
||||
"description": "删除用户 - 更新 is_deleted = 1",
|
||||
"description": "删除用户",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -132,7 +162,7 @@
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "删除用户 - 更新 is_deleted = 1",
|
||||
"summary": "删除用户",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
@@ -151,7 +181,22 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息"
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_handler.deleteResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,9 +232,21 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.DetailResponse"
|
||||
"$ref": "#/definitions/user_handler.detailResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,11 +268,11 @@
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDRequest"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -228,9 +285,21 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +307,20 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"demo.authResponse": {
|
||||
"code.Failure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"description": "业务码",
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"description": "描述信息",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"demo_handler.authResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorization": {
|
||||
@@ -251,7 +333,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateRequest": {
|
||||
"user_handler.createRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mobile": {
|
||||
@@ -268,7 +350,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateResponse": {
|
||||
"user_handler.createResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -277,7 +359,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.DetailResponse": {
|
||||
"user_handler.deleteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "用户主键ID",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_handler.detailResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -285,7 +376,7 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"mobile": {
|
||||
"description": "手机号",
|
||||
"description": "手机号(脱敏)",
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
@@ -298,7 +389,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDRequest": {
|
||||
"user_handler.updateNickNameByIDRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -311,7 +402,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDResponse": {
|
||||
"user_handler.updateNickNameByIDResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+84
-23
@@ -1,5 +1,14 @@
|
||||
definitions:
|
||||
demo.authResponse:
|
||||
code.Failure:
|
||||
properties:
|
||||
code:
|
||||
description: 业务码
|
||||
type: integer
|
||||
message:
|
||||
description: 描述信息
|
||||
type: string
|
||||
type: object
|
||||
demo_handler.authResponse:
|
||||
properties:
|
||||
authorization:
|
||||
description: 签名
|
||||
@@ -8,7 +17,7 @@ definitions:
|
||||
description: 过期时间
|
||||
type: integer
|
||||
type: object
|
||||
user_model.CreateRequest:
|
||||
user_handler.createRequest:
|
||||
properties:
|
||||
mobile:
|
||||
description: 手机号
|
||||
@@ -20,19 +29,25 @@ definitions:
|
||||
description: 用户名
|
||||
type: string
|
||||
type: object
|
||||
user_model.CreateResponse:
|
||||
user_handler.createResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 主键ID
|
||||
type: integer
|
||||
type: object
|
||||
user_model.DetailResponse:
|
||||
user_handler.deleteResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
type: integer
|
||||
type: object
|
||||
user_handler.detailResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
type: integer
|
||||
mobile:
|
||||
description: 手机号
|
||||
description: 手机号(脱敏)
|
||||
type: string
|
||||
nick_name:
|
||||
description: 昵称
|
||||
@@ -41,7 +56,7 @@ definitions:
|
||||
description: 用户名
|
||||
type: string
|
||||
type: object
|
||||
user_model.UpdateNickNameByIDRequest:
|
||||
user_handler.updateNickNameByIDRequest:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
@@ -50,7 +65,7 @@ definitions:
|
||||
description: 昵称
|
||||
type: string
|
||||
type: object
|
||||
user_model.UpdateNickNameByIDResponse:
|
||||
user_handler.updateNickNameByIDResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
@@ -74,9 +89,13 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/demo.authResponse'
|
||||
$ref: '#/definitions/demo_handler.authResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 获取授权信息
|
||||
tags:
|
||||
- Demo
|
||||
@@ -95,7 +114,7 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 用户信息
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
properties:
|
||||
@@ -107,6 +126,14 @@ paths:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: Trace 示例
|
||||
tags:
|
||||
- Demo
|
||||
@@ -118,10 +145,10 @@ paths:
|
||||
parameters:
|
||||
- description: 请求信息
|
||||
in: body
|
||||
name: RequestInfo
|
||||
name: Request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.CreateRequest'
|
||||
$ref: '#/definitions/user_handler.createRequest'
|
||||
- description: 签名
|
||||
in: header
|
||||
name: Authorization
|
||||
@@ -131,9 +158,17 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.CreateResponse'
|
||||
$ref: '#/definitions/user_handler.createResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 创建用户
|
||||
tags:
|
||||
- User
|
||||
@@ -141,7 +176,7 @@ paths:
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除用户 - 更新 is_deleted = 1
|
||||
description: 删除用户
|
||||
parameters:
|
||||
- description: 用户ID
|
||||
in: path
|
||||
@@ -157,8 +192,18 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
summary: 删除用户 - 更新 is_deleted = 1
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_handler.deleteResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 删除用户
|
||||
tags:
|
||||
- User
|
||||
/user/info/{username}:
|
||||
@@ -181,9 +226,17 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.DetailResponse'
|
||||
$ref: '#/definitions/user_handler.detailResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 用户详情
|
||||
tags:
|
||||
- User
|
||||
@@ -195,10 +248,10 @@ paths:
|
||||
parameters:
|
||||
- description: 请求信息
|
||||
in: body
|
||||
name: RequestInfo
|
||||
name: Request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.UpdateNickNameByIDRequest'
|
||||
$ref: '#/definitions/user_handler.updateNickNameByIDRequest'
|
||||
- description: 签名
|
||||
in: header
|
||||
name: Authorization
|
||||
@@ -208,9 +261,17 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.UpdateNickNameByIDResponse'
|
||||
$ref: '#/definitions/user_handler.updateNickNameByIDResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
tags:
|
||||
- User
|
||||
|
||||
@@ -12,6 +12,7 @@ require (
|
||||
github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/go-cmp v0.5.4 // indirect
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/onsi/ginkgo v1.14.2 // indirect
|
||||
github.com/onsi/gomega v1.10.4 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
||||
@@ -19,6 +19,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -33,6 +34,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -58,6 +60,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
@@ -65,6 +69,8 @@ github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMN
|
||||
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
@@ -123,6 +129,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -192,8 +200,11 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
@@ -220,6 +231,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
@@ -237,6 +250,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
@@ -381,9 +396,11 @@ go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -409,6 +426,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -428,6 +446,8 @@ golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
## 错误码规则
|
||||
|
||||
- 错误码需在 `code.go` 文件中定义。
|
||||
- 错误码需为 > 1 的数,反之表示正确。
|
||||
- 错误码需在 `code` 包中进行定义。
|
||||
|
||||
#### 错误码为 5 位数
|
||||
|
||||
@@ -9,6 +8,6 @@
|
||||
| :------ | :------ | :------ |
|
||||
| 服务级错误码 | 模块级错误码 | 具体错误码 |
|
||||
|
||||
- 服务级别错误码:1 位数进行表示,比如 1 为系统级错误;2 为普通错误,通常是由用户非法操作引起。
|
||||
- 服务级错误码:1 位数进行表示,比如 1 为系统级错误;2 为普通错误,通常是由用户非法操作引起。
|
||||
- 模块级错误码:2 位数进行表示,比如 01 为用户模块;02 为订单模块。
|
||||
- 具体错误码:2 位数进行表示,比如 01 为手机号不合法;02 为验证码输入错误。
|
||||
- 具体的错误码:2 位数进行表示,比如 01 为手机号不合法;02 为验证码输入错误。
|
||||
+32
-19
@@ -1,28 +1,41 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
var (
|
||||
// OK
|
||||
OK = errno.NewError(http.StatusOK, 1, "OK")
|
||||
// 错误时返回结构
|
||||
type Failure struct {
|
||||
Code int `json:"code"` // 业务码
|
||||
Message string `json:"message"` // 描述信息
|
||||
}
|
||||
|
||||
const (
|
||||
// 服务级错误码
|
||||
ErrServer = errno.NewError(http.StatusInternalServerError, 10101, http.StatusText(http.StatusInternalServerError))
|
||||
ErrManyRequest = errno.NewError(http.StatusTooManyRequests, 10102, http.StatusText(http.StatusTooManyRequests))
|
||||
ErrParamBind = errno.NewError(http.StatusBadRequest, 10103, "参数信息有误")
|
||||
ErrAuthorization = errno.NewError(http.StatusUnauthorized, 10104, "签名信息有误")
|
||||
ServerError = 10101
|
||||
TooManyRequests = 10102
|
||||
ParamBindError = 10103
|
||||
AuthorizationError = 10104
|
||||
CallHTTPError = 10105
|
||||
|
||||
// 模块级错误码 - 用户模块
|
||||
ErrUser = errno.NewError(http.StatusBadRequest, 20101, "非法用户")
|
||||
ErrUserName = errno.NewError(http.StatusBadRequest, 20102, "账号不能为空")
|
||||
ErrUserCreate = errno.NewError(http.StatusBadRequest, 20103, "创建用户失败")
|
||||
ErrUserUpdate = errno.NewError(http.StatusBadRequest, 20104, "更新用户失败")
|
||||
ErrUserSearch = errno.NewError(http.StatusBadRequest, 20105, "查询用户失败")
|
||||
ErrUserHTTP = errno.NewError(http.StatusBadRequest, 20106, "调用他方接口失败")
|
||||
IllegalUserName = 20101
|
||||
UserCreateError = 20102
|
||||
UserUpdateError = 20103
|
||||
UserSearchError = 20104
|
||||
|
||||
// ...
|
||||
)
|
||||
|
||||
var codeText = map[int]string{
|
||||
ServerError: "Internal Server Error",
|
||||
TooManyRequests: "Too Many Requests",
|
||||
ParamBindError: "参数信息有误",
|
||||
AuthorizationError: "签名信息有误",
|
||||
CallHTTPError: "调用第三方 HTTP 接口失败",
|
||||
|
||||
IllegalUserName: "非法用户名",
|
||||
UserCreateError: "创建用户失败",
|
||||
UserUpdateError: "更新用户失败",
|
||||
UserSearchError: "查询用户失败",
|
||||
}
|
||||
|
||||
func Text(code int) string {
|
||||
return codeText[code]
|
||||
}
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/third_party_request/go_gin_api_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/p"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/token"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Demo struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func NewDemo(logger *zap.Logger, db db.Repo, cache cache.Repo) *Demo {
|
||||
return &Demo{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Demo) Get() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `uri:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Tom" {
|
||||
c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Tom")))
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(code.OK.WithData(&response{
|
||||
Name: "Tom",
|
||||
Job: "Student",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Demo) Post() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `form:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindPostForm(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Jack" {
|
||||
c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Jack")))
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(code.OK.WithData(&response{
|
||||
Name: "Jack",
|
||||
Job: "Teacher",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
type authResponse struct {
|
||||
Authorization string `json:"authorization"` // 签名
|
||||
ExpireTime int64 `json:"expire_time"` // 过期时间
|
||||
}
|
||||
|
||||
type traceResponse []struct {
|
||||
Name string `json:"name"` //用户名
|
||||
Job string `json:"job"` //工作
|
||||
}
|
||||
|
||||
// 获取授权信息
|
||||
// @Summary 获取授权信息
|
||||
// @Description 获取授权信息
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} authResponse "返回信息"
|
||||
// @Router /auth/get [post]
|
||||
func (d *Demo) Auth() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
cfg := configs.Get().JWT
|
||||
tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrAuthorization.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res := new(authResponse)
|
||||
res.Authorization = tokenString
|
||||
res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix()
|
||||
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
|
||||
// Trace 示例
|
||||
// @Summary Trace 示例
|
||||
// @Description Trace 示例
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} traceResponse "用户信息"
|
||||
// @Router /demo/trace [get]
|
||||
func (d *Demo) Trace() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
// 三方请求信息
|
||||
res1, err := go_gin_api_repo.DemoGet("Tom",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api_repo.DemoGetRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
d.logger.Error("get [demo/get] err", zap.Error(err))
|
||||
c.AbortWithError(code.ErrUserHTTP.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res1.Data.Name", res1.Data.Name, p.WithTrace(c.Trace()))
|
||||
|
||||
// 三方请求信息
|
||||
res2, err := go_gin_api_repo.DemoPost("Jack",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api_repo.DemoPostRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
d.logger.Error("post [demo/post] err", zap.Error(err))
|
||||
c.AbortWithError(code.ErrUserHTTP.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res2.Data.Name",
|
||||
res2.Data.Name,
|
||||
p.WithTrace(c.Trace()),
|
||||
)
|
||||
|
||||
// 执行 SQL 信息
|
||||
d.userService.GetUserByUserName(c, "test_user")
|
||||
|
||||
// 执行 Redis 信息
|
||||
_ = d.cache.Set("name", "tom", time.Minute*10, cache.WithTrace(c.Trace()))
|
||||
val, _ := d.cache.Get("name", cache.WithTrace(c.Trace()))
|
||||
p.Println("redis-name", val, p.WithTrace(c.Trace()))
|
||||
|
||||
//// 执行 gRPC 信息
|
||||
//conn, err := grpclient.New("127.0.0.1:9001",
|
||||
// grpclient.WithDialTimeout(time.Second*5),
|
||||
// grpclient.WithTrace(c.Trace()),
|
||||
// grpclient.WithSign(func(message []byte) (authorization string, err error) {
|
||||
// return grpclient.GenerateSign("abcdef", message)
|
||||
// }),
|
||||
//)
|
||||
//if err != nil {
|
||||
// d.logger.Error("grpc conn err", zap.Error(err))
|
||||
//}
|
||||
//defer conn.Close()
|
||||
//
|
||||
//// 初始化客户端
|
||||
//client := hello.NewHelloClient(conn)
|
||||
//
|
||||
//// 调用 SayHello 方法
|
||||
//ctx := context.Background()
|
||||
//// 设置 SayHello 超时间
|
||||
//ctx, cancel := context.WithTimeout(ctx, time.Second*3)
|
||||
//defer cancel()
|
||||
//
|
||||
//client.SayHello(ctx, &hello.HelloRequest{Name: "Hello World"})
|
||||
|
||||
data := &traceResponse{
|
||||
{
|
||||
Name: res1.Data.Name,
|
||||
Job: res1.Data.Job,
|
||||
},
|
||||
{
|
||||
Name: res2.Data.Name,
|
||||
Job: res2.Data.Job,
|
||||
},
|
||||
}
|
||||
c.Payload(code.OK.WithData(data))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/token"
|
||||
)
|
||||
|
||||
type authResponse struct {
|
||||
Authorization string `json:"authorization"` // 签名
|
||||
ExpireTime int64 `json:"expire_time"` // 过期时间
|
||||
}
|
||||
|
||||
// 获取授权信息
|
||||
// @Summary 获取授权信息
|
||||
// @Description 获取授权信息
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} authResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /auth/get [post]
|
||||
func (h *handler) Auth() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
cfg := configs.Get().JWT
|
||||
tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res := new(authResponse)
|
||||
res.Authorization = tokenString
|
||||
res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix()
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (h *handler) Get() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `uri:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Tom" {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.IllegalUserName,
|
||||
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Tom")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(&response{
|
||||
Name: "Tom",
|
||||
Job: "Student",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (h *handler) Post() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `form:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindPostForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Jack" {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.IllegalUserName,
|
||||
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Jack")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(&response{
|
||||
Name: "Jack",
|
||||
Job: "Teacher",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/third_party_request/go_gin_api_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/p"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type traceResponse []struct {
|
||||
Name string `json:"name"` //用户名
|
||||
Job string `json:"job"` //工作
|
||||
}
|
||||
|
||||
// Trace 示例
|
||||
// @Summary Trace 示例
|
||||
// @Description Trace 示例
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} traceResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /demo/trace [get]
|
||||
func (h *handler) Trace() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
// 三方请求信息
|
||||
res1, err := go_gin_api_repo.DemoGet("Tom",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api_repo.DemoGetRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
h.logger.Error("get [demo/get] err", zap.Error(err))
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.CallHTTPError,
|
||||
code.Text(code.CallHTTPError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res1.Name", res1.Name, p.WithTrace(c.Trace()))
|
||||
|
||||
// 三方请求信息
|
||||
res2, err := go_gin_api_repo.DemoPost("Jack",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api_repo.DemoPostRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
h.logger.Error("post [demo/post] err", zap.Error(err))
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.CallHTTPError,
|
||||
code.Text(code.CallHTTPError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res2.Name",
|
||||
res2.Name,
|
||||
p.WithTrace(c.Trace()),
|
||||
)
|
||||
|
||||
// 执行 SQL 信息
|
||||
h.userService.GetUserByUserName(c, "test_user")
|
||||
|
||||
// 执行 Redis 信息
|
||||
_ = h.cache.Set("name", "tom", time.Minute*10, cache.WithTrace(c.Trace()))
|
||||
val, _ := h.cache.Get("name", cache.WithTrace(c.Trace()))
|
||||
p.Println("redis-name", val, p.WithTrace(c.Trace()))
|
||||
|
||||
// 初始化客户端
|
||||
// client := hello.NewHelloClient(d.grpConn.Conn())
|
||||
// client.SayHello(grpc.ContextWithValueAndTimeout(c, time.Second*3), &hello.HelloRequest{Name: "Hello World"})
|
||||
|
||||
data := &traceResponse{
|
||||
{
|
||||
Name: res1.Name,
|
||||
Job: res1.Job,
|
||||
},
|
||||
{
|
||||
Name: res2.Name,
|
||||
Job: res2.Job,
|
||||
},
|
||||
}
|
||||
c.Payload(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
// 示例:支持 get 请求的方法
|
||||
Get() core.HandlerFunc
|
||||
// 示例:支持 post 请求的方法
|
||||
Post() core.HandlerFunc
|
||||
// 获取授权信息
|
||||
Auth() core.HandlerFunc
|
||||
// Trace 示例
|
||||
Trace() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
grpConn grpc.ClientConn
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.ClientConn) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
grpConn: grpConn,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
@@ -0,0 +1,77 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type createRequest struct {
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
type createResponse struct {
|
||||
Id int32 `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
// @Summary 创建用户
|
||||
// @Description 创建用户
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body createRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/create [post]
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createRequest)
|
||||
res := new(createResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserName == "" {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.IllegalUserName,
|
||||
code.Text(code.IllegalUserName)).WithErr(errors.New("req.UserName = ''")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createUserData := new(user_service.CreateUserInfo)
|
||||
createUserData.Mobile = req.Mobile
|
||||
createUserData.NickName = req.NickName
|
||||
createUserData.UserName = req.UserName
|
||||
|
||||
id, err := h.userService.Create(c, createUserData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserCreateError,
|
||||
code.Text(code.UserCreateError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
c.Payload(res)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteRequest struct {
|
||||
Id int32 `uri:"id"` // 用户ID
|
||||
}
|
||||
|
||||
type deleteResponse struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
// @Summary 删除用户
|
||||
// @Description 删除用户
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "用户ID"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} deleteResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/delete/{id} [patch]
|
||||
func (h *handler) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteRequest)
|
||||
res := new(deleteResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.userService.Delete(c, req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserUpdateError,
|
||||
code.Text(code.UserUpdateError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = req.Id
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/ddm"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type detailRequest struct {
|
||||
UserName string `uri:"username"` // 用户名
|
||||
}
|
||||
|
||||
type detailResponse struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile ddm.Mobile `json:"mobile"` // 手机号(脱敏)
|
||||
}
|
||||
|
||||
// 用户详情
|
||||
// @Summary 用户详情
|
||||
// @Description 用户详情
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param username path string true "用户名"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/info/{username} [get]
|
||||
func (h *handler) Detail() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(detailRequest)
|
||||
res := new(detailResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.GetUserByUserName(c, req.UserName)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserSearchError,
|
||||
code.Text(code.UserSearchError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = user.Id
|
||||
res.UserName = user.UserName
|
||||
res.NickName = user.NickName
|
||||
res.Mobile = ddm.Mobile(user.Mobile)
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type updateNickNameByIDRequest struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
}
|
||||
|
||||
type updateNickNameByIDResponse struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
}
|
||||
|
||||
// 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Summary 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Description 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body updateNickNameByIDRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} updateNickNameByIDResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/update [put]
|
||||
func (h *handler) UpdateNickNameByID() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(updateNickNameByIDRequest)
|
||||
res := new(updateNickNameByIDResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.userService.UpdateNickNameByID(c, req.Id, req.NickName)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserUpdateError,
|
||||
code.Text(code.UserUpdateError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = req.Id
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
// Create 创建用户
|
||||
Create() core.HandlerFunc
|
||||
// UpdateNickNameByID 编辑用户 - 通过主键ID更新用户昵称
|
||||
UpdateNickNameByID() core.HandlerFunc
|
||||
// Delete 删除用户
|
||||
Delete() core.HandlerFunc
|
||||
// Detail 用户详情
|
||||
Detail() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
@@ -1,176 +0,0 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/user_model"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/ddm"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ UserDemo = (*userDemo)(nil)
|
||||
|
||||
type UserDemo interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
// Create 创建用户
|
||||
Create() core.HandlerFunc
|
||||
|
||||
// UpdateNickNameByID 编辑用户 - 通过主键ID更新用户昵称
|
||||
UpdateNickNameByID() core.HandlerFunc
|
||||
|
||||
// Delete 删除用户 - 通过主键ID更新 is_deleted = 1
|
||||
Delete() core.HandlerFunc
|
||||
|
||||
// Detail 用户详情
|
||||
Detail() core.HandlerFunc
|
||||
}
|
||||
|
||||
type userDemo struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func NewUserDemo(logger *zap.Logger, db db.Repo, cache cache.Repo) UserDemo {
|
||||
return &userDemo{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userDemo) i() {}
|
||||
|
||||
// 创建用户
|
||||
// @Summary 创建用户
|
||||
// @Description 创建用户
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param RequestInfo body user_model.CreateRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} user_model.CreateResponse "返回信息"
|
||||
// @Router /user/create [post]
|
||||
func (u *userDemo) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.CreateRequest)
|
||||
res := new(user_model.CreateResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserName == "" {
|
||||
c.AbortWithError(code.ErrUserName.WithErr(errors.New("req.UserName = ''")))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := u.userService.Create(c, req)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserCreate.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Summary 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Description 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param RequestInfo body user_model.UpdateNickNameByIDRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} user_model.UpdateNickNameByIDResponse "返回信息"
|
||||
// @Router /user/update [put]
|
||||
func (u *userDemo) UpdateNickNameByID() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.UpdateNickNameByIDRequest)
|
||||
res := new(user_model.UpdateNickNameByIDResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
err := u.userService.UpdateNickNameByID(c, req.Id, req.NickName)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserUpdate.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = req.Id
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
|
||||
// 删除用户 - 更新 is_deleted = 1
|
||||
// @Summary 删除用户 - 更新 is_deleted = 1
|
||||
// @Description 删除用户 - 更新 is_deleted = 1
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "用户ID"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 "返回信息"
|
||||
// @Router /user/delete/{id} [patch]
|
||||
func (u *userDemo) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.DeleteRequest)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
err := u.userService.Delete(c, req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserUpdate.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(code.OK.WithData(nil))
|
||||
}
|
||||
}
|
||||
|
||||
// 用户详情
|
||||
// @Summary 用户详情
|
||||
// @Description 用户详情
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param username path string true "用户名"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} user_model.DetailResponse "返回信息"
|
||||
// @Router /user/info/{username} [get]
|
||||
func (u *userDemo) Detail() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.DetailRequest)
|
||||
res := new(user_model.DetailResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := u.userService.GetUserByUserName(c, req.UserName)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserSearch.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = user.Id
|
||||
res.UserName = user.UserName
|
||||
res.NickName = user.NickName
|
||||
res.Mobile = ddm.Mobile(user.Mobile)
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
## model
|
||||
|
||||
实体层。
|
||||
|
||||
- 请求实体、返回实体。
|
||||
- 数据库实体。
|
||||
|
||||
命名规范:
|
||||
|
||||
- 包名以 `_model` 结尾。
|
||||
@@ -1,63 +0,0 @@
|
||||
package user_model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/ddm"
|
||||
)
|
||||
|
||||
// 用户Demo表
|
||||
type UserDemo struct {
|
||||
Id uint `gorm:"column:id;primary_key;AUTO_INCREMENT"` // 主键
|
||||
UserName string `gorm:"column:user_name;NOT NULL"` // 用户名
|
||||
NickName string `gorm:"column:nick_name;NOT NULL"` // 昵称
|
||||
Mobile string `gorm:"column:mobile;NOT NULL"` // 手机号
|
||||
IsDeleted int `gorm:"column:is_deleted;default:-1;NOT NULL"` // 是否删除 1:是 -1:否
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 更新时间
|
||||
}
|
||||
|
||||
func (m *UserDemo) TableName() string {
|
||||
return "user_demo"
|
||||
}
|
||||
|
||||
// user_handler Create Request
|
||||
type CreateRequest struct {
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
// user_handler Create Response
|
||||
type CreateResponse struct {
|
||||
Id uint `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// user_handler UpdateNickNameByID Request
|
||||
type UpdateNickNameByIDRequest struct {
|
||||
Id uint `json:"id"` // 用户主键ID
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
}
|
||||
|
||||
// user_handler UpdateNickNameByID Response
|
||||
type UpdateNickNameByIDResponse struct {
|
||||
Id uint `json:"id"` // 用户主键ID
|
||||
}
|
||||
|
||||
// user_handler Delete Request
|
||||
type DeleteRequest struct {
|
||||
Id uint `uri:"id"` // 用户ID
|
||||
}
|
||||
|
||||
// user_handler Detail Request
|
||||
type DetailRequest struct {
|
||||
UserName string `uri:"username"` // 用户名
|
||||
}
|
||||
|
||||
// user_handler Detail Response
|
||||
type DetailResponse struct {
|
||||
Id uint `json:"id"` // 用户主键ID
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile ddm.Mobile `json:"mobile"` // 手机号(脱敏)
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
## repository
|
||||
|
||||
数据访问层。
|
||||
#### 数据访问层。
|
||||
|
||||
- `./db_repo` 访问 DB 数据
|
||||
- `./cache_repo` 访问 Cache 数据
|
||||
- `./third_party_request` 访问外部 HTTP 接口数据。
|
||||
|
||||
SQL 建议:
|
||||
- 禁止使用 SQL k v 拼接,好处是避免 SQL 注入;
|
||||
- 禁止使用连表查询,好处是易扩展,比如分库分表;
|
||||
- 禁止使用万能方法,好处是便于后期维护,比如字段调整;
|
||||
- 禁止使用删除方法,好处是避免数据丢失;
|
||||
#### SQL 建议:
|
||||
- 建议每张表需包含字段:主键(id)、标记删除(is_deteled)、创建时间(created_at)、更新时间(updated_at)
|
||||
|
||||
```mysql
|
||||
@@ -19,11 +15,18 @@ SQL 建议:
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
```
|
||||
什么是万能方法?
|
||||
|
||||
指的是特别灵活的查询,比如通过非固定的参数返回全部字段,建议做到需要什么返回什么,不要返回大而全的数据,更新时也不能传递什么参数更新什么参数,更新字段要提前约定好。
|
||||
|
||||
命名规范:
|
||||
#### 命名规范:
|
||||
|
||||
- 包名应以 `_repo` 结尾;
|
||||
- `./db_repo` 目录下的包名以 `数据表名`+ `_repo` 命名;
|
||||
|
||||
#### 脚本生成 MySQL CURD
|
||||
|
||||
1. 定义生成的表,设置 config 中 cmd.genTables,可以自定义设置多张表,为空表示生成库中所有的表,如果设置多个表可用','分割;
|
||||
1. 在根目录下执行脚本文件:`./scripts/gormgen.sh`;
|
||||
|
||||
以用户表(user_demo)为例:
|
||||
- 结构体文件:user_demo_repo/gen_model.go;
|
||||
- CURD 方法文件:user_demo_repo/gen_user_demo.go;
|
||||
- 表结构 MD 文件:user_demo_repo/gen_table.md;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package db_repo
|
||||
|
||||
// Predicate is a string that acts as a condition in the where clause
|
||||
type Predicate string
|
||||
|
||||
var (
|
||||
EqualPredicate = Predicate("=")
|
||||
NotEqualPredicate = Predicate("<>")
|
||||
GreaterThanPredicate = Predicate(">")
|
||||
GreaterThanOrEqualPredicate = Predicate(">=")
|
||||
SmallerThanPredicate = Predicate("<")
|
||||
SmallerThanOrEqualPredicate = Predicate("<=")
|
||||
LikePredicate = Predicate("LIKE")
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package user_demo_repo
|
||||
|
||||
import "time"
|
||||
|
||||
// 用户Demo表
|
||||
//go:generate gormgen -structs UserDemo -input .
|
||||
type UserDemo struct {
|
||||
Id int32 // 主键
|
||||
UserName string // 用户名
|
||||
NickName string // 昵称
|
||||
Mobile string // 手机号
|
||||
IsDeleted int32 // 是否删除 1:是 -1:否
|
||||
CreatedAt time.Time `gorm:"time"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"time"` // 更新时间
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#### xin_ceshi.user_demo
|
||||
用户Demo表
|
||||
|
||||
| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |
|
||||
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
|
||||
| 1 | id | 主键 | int(11) unsigned | PRI | NO | auto_increment | |
|
||||
| 2 | user_name | 用户名 | varchar(32) | | NO | | |
|
||||
| 3 | nick_name | 昵称 | varchar(100) | | NO | | |
|
||||
| 4 | mobile | 手机号 | varchar(20) | | NO | | |
|
||||
| 5 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 |
|
||||
| 6 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP |
|
||||
| 7 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP |
|
||||
@@ -0,0 +1,257 @@
|
||||
///////////////////////////////////////////////////////////
|
||||
// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT //
|
||||
// ANY CHANGES DONE HERE WILL BE LOST //
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
package user_demo_repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewModel() *UserDemo {
|
||||
return new(UserDemo)
|
||||
}
|
||||
|
||||
func NewQueryBuilder() *userDemoRepoQueryBuilder {
|
||||
return new(userDemoRepoQueryBuilder)
|
||||
}
|
||||
|
||||
func (t *UserDemo) Create(db *gorm.DB) (id int32, err error) {
|
||||
if err = db.Create(t).Error; err != nil {
|
||||
return 0, errors.Wrap(err, "create err")
|
||||
}
|
||||
return t.Id, nil
|
||||
}
|
||||
|
||||
func (t *UserDemo) Delete(db *gorm.DB) (err error) {
|
||||
if err = db.Delete(t).Error; err != nil {
|
||||
return errors.Wrap(err, "delete err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *UserDemo) Updates(db *gorm.DB, m map[string]interface{}) (err error) {
|
||||
if err = db.Model(&UserDemo{}).Where("id = ?", t.Id).Updates(m).Error; err != nil {
|
||||
return errors.Wrap(err, "updates err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type userDemoRepoQueryBuilder struct {
|
||||
order []string
|
||||
where []struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}
|
||||
limit int
|
||||
offset int
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) buildQuery(db *gorm.DB) *gorm.DB {
|
||||
ret := db
|
||||
for _, where := range qb.where {
|
||||
ret = ret.Where(where.prefix, where.value)
|
||||
}
|
||||
for _, order := range qb.order {
|
||||
ret = ret.Order(order)
|
||||
}
|
||||
ret = ret.Limit(qb.limit).Offset(qb.offset)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) Count(db *gorm.DB) (int64, error) {
|
||||
var c int64
|
||||
res := qb.buildQuery(db).Model(&UserDemo{}).Count(&c)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
c = 0
|
||||
}
|
||||
return c, res.Error
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) First(db *gorm.DB) (*UserDemo, error) {
|
||||
ret := &UserDemo{}
|
||||
res := qb.buildQuery(db).First(ret)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
ret = nil
|
||||
}
|
||||
return ret, res.Error
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) QueryOne(db *gorm.DB) (*UserDemo, error) {
|
||||
qb.limit = 1
|
||||
ret, err := qb.QueryAll(db)
|
||||
if len(ret) > 0 {
|
||||
return ret[0], err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*UserDemo, error) {
|
||||
var ret []*UserDemo
|
||||
err := qb.buildQuery(db).Find(&ret).Error
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) Limit(limit int) *userDemoRepoQueryBuilder {
|
||||
qb.limit = limit
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) Offset(offset int) *userDemoRepoQueryBuilder {
|
||||
qb.offset = offset
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "id", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderById(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "id "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUserName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "user_name", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByUserName(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "user_name "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereNickName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "nick_name", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByNickName(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "nick_name "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereMobile(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "mobile", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByMobile(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "mobile "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereIsDeleted(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "is_deleted", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByIsDeleted(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "is_deleted "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "created_at", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByCreatedAt(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "created_at "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "updated_at", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByUpdatedAt(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "updated_at "+order)
|
||||
return qb
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package user_demo_repo
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/user_model"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ UserRepo = (*userRepo)(nil)
|
||||
|
||||
type UserRepo interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
Create(ctx core.Context, user user_model.UserDemo) (id uint, err error)
|
||||
UpdateNickNameByID(ctx core.Context, id uint, username string) (err error)
|
||||
GetUserByUserName(ctx core.Context, username string) (*user_model.UserDemo, error)
|
||||
Delete(ctx core.Context, id uint) (err error)
|
||||
getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error)
|
||||
}
|
||||
|
||||
type userRepo struct {
|
||||
db db.Repo
|
||||
}
|
||||
|
||||
func NewUserRepo(db db.Repo) UserRepo {
|
||||
return &userRepo{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userRepo) i() {}
|
||||
|
||||
func (u *userRepo) Create(ctx core.Context, user user_model.UserDemo) (id uint, err error) {
|
||||
err = u.db.GetDbW().WithContext(ctx.RequestContext()).Create(&user).Error
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "[user_repo] create user err")
|
||||
}
|
||||
return user.Id, nil
|
||||
}
|
||||
|
||||
func (u *userRepo) getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error) {
|
||||
data := new(user_model.UserDemo)
|
||||
err := u.db.GetDbR().
|
||||
WithContext(ctx.RequestContext()).First(data, id).Where("is_deleted = ?", -1).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (u *userRepo) UpdateNickNameByID(ctx core.Context, id uint, nickname string) (err error) {
|
||||
user, err := u.getUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return u.db.GetDbW().WithContext(ctx.RequestContext()).Model(user).Update("nick_name", nickname).Error
|
||||
}
|
||||
|
||||
func (u *userRepo) Delete(ctx core.Context, id uint) (err error) {
|
||||
user, err := u.getUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return u.db.GetDbW().WithContext(ctx.RequestContext()).Model(user).Update("is_deleted", 1).Error
|
||||
}
|
||||
|
||||
func (u *userRepo) GetUserByUserName(ctx core.Context, username string) (*user_model.UserDemo, error) {
|
||||
data := new(user_model.UserDemo)
|
||||
err := u.db.GetDbR().
|
||||
WithContext(ctx.RequestContext()).
|
||||
Select([]string{"id", "user_name", "nick_name", "mobile"}).
|
||||
Where("user_name = ? and is_deleted = ?", username, -1).
|
||||
First(data).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package go_gin_api_repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
@@ -11,23 +10,13 @@ import (
|
||||
)
|
||||
|
||||
type demoGetResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
} `json:"data"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
type demoPostResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
} `json:"data"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
func DemoGet(name string, opts ...httpclient.Option) (res *demoGetResponse, err error) {
|
||||
@@ -43,10 +32,6 @@ func DemoGet(name string, opts ...httpclient.Option) (res *demoGetResponse, err
|
||||
return nil, errors.Wrap(err, "DemoGet json unmarshal error")
|
||||
}
|
||||
|
||||
if res.Code != 1 {
|
||||
return nil, errors.New(fmt.Sprintf("code err: %d-%s", res.Code, res.Msg))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -55,17 +40,7 @@ func DemoGetRetryVerify(body []byte) (shouldRetry bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
resp := new(Response)
|
||||
if err := json.Unmarshal(body, resp); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// 例如 无需重试的 code 码,code !=1 需要重试
|
||||
successCode := 1
|
||||
return resp.Code != successCode
|
||||
return false
|
||||
}
|
||||
|
||||
func DemoPost(name string, opts ...httpclient.Option) (res *demoPostResponse, err error) {
|
||||
@@ -83,10 +58,6 @@ func DemoPost(name string, opts ...httpclient.Option) (res *demoPostResponse, er
|
||||
return nil, errors.Wrap(err, "DemoPost json unmarshal error")
|
||||
}
|
||||
|
||||
if res.Code != 1 {
|
||||
return nil, errors.New(fmt.Sprintf("code err: %d-%s", res.Code, res.Msg))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -95,15 +66,5 @@ func DemoPostRetryVerify(body []byte) (shouldRetry bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
resp := new(Response)
|
||||
if err := json.Unmarshal(body, resp); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// 例如 无需重试的 code 码,code !=1 需要重试
|
||||
successCode := 1
|
||||
return resp.Code != successCode
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@ import "encoding/json"
|
||||
|
||||
func MockDemoGet() (body []byte) {
|
||||
res := new(demoGetResponse)
|
||||
res.Code = 1
|
||||
res.Msg = "ok"
|
||||
res.Data.Name = "AA"
|
||||
res.Data.Job = "AA_JOB"
|
||||
res.Name = "AA"
|
||||
res.Job = "AA_JOB"
|
||||
|
||||
body, _ = json.Marshal(res)
|
||||
return body
|
||||
@@ -15,10 +13,8 @@ func MockDemoGet() (body []byte) {
|
||||
|
||||
func MockDemoPost() (body []byte) {
|
||||
res := new(demoPostResponse)
|
||||
res.Code = 1
|
||||
res.Msg = "ok"
|
||||
res.Data.Name = "BB"
|
||||
res.Data.Job = "BB_JOB"
|
||||
res.Name = "BB"
|
||||
res.Job = "BB_JOB"
|
||||
|
||||
body, _ = json.Marshal(res)
|
||||
return body
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
)
|
||||
|
||||
var authorization = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEwLCJVc2VyTmFtZSI6IjEyMyIsImV4cCI6MTYxMDcxODA3NCwiaWF0IjoxNjEwNjMxNjc0LCJpc3MiOiJnby1naW4tYXBpIiwibmJmIjoxNjEwNjMxNjc0fQ.S3T4MaIaz3XjkbJ-xkMDkwzuZ_jfZ8ZRf4cPMz0oXBE"
|
||||
var authorization = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJOYW1lIjoieGlubGlhbmdub3RlIiwiZXhwIjoxNjEzODI3MTEzLCJpYXQiOjE2MTM3NDA3MTMsIm5iZiI6MTYxMzc0MDcxM30.SnooP1ikO33ryGPdohsmOKqISa-bWzMkMvUNb5f2zc0"
|
||||
|
||||
func TestDemoGet(t *testing.T) {
|
||||
res, err := DemoGet("Tom",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
@@ -13,20 +15,32 @@ import (
|
||||
func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Error) {
|
||||
auth := ctx.GetHeader("Authorization")
|
||||
if auth == "" {
|
||||
err = code.ErrAuthorization.WithErr(errors.New("Header 中缺少 Authorization 参数"))
|
||||
err = errno.NewError(
|
||||
http.StatusUnauthorized,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Authorization 参数"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cfg := configs.Get().JWT
|
||||
claims, errParse := token.New(cfg.Secret).Parse(auth)
|
||||
if errParse != nil {
|
||||
err = code.ErrAuthorization.WithErr(errParse)
|
||||
err = errno.NewError(
|
||||
http.StatusUnauthorized,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithErr(errParse)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
userId = claims.UserID
|
||||
if userId <= 0 {
|
||||
err = code.ErrAuthorization.WithErr(errors.New("claims.UserID <= 0 "))
|
||||
err = errno.NewError(
|
||||
http.StatusUnauthorized,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithErr(errors.New("claims.UserID <= 0 "))
|
||||
|
||||
return
|
||||
}
|
||||
userName = claims.UserName
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo_handler"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/controller/user_handler"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/router/middleware/auth"
|
||||
"github.com/xinliangnote/go-gin-api/internal/graph/handler"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/metrics"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
|
||||
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo) (core.Mux, error) {
|
||||
func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.ClientConn) (core.Mux, error) {
|
||||
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger required")
|
||||
@@ -32,8 +33,6 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo) (core.Mux, err
|
||||
panic(err)
|
||||
}
|
||||
|
||||
demoHandler := demo.NewDemo(logger, db, cache)
|
||||
userHandler := user_handler.NewUserDemo(logger, db, cache)
|
||||
gqlHandler := handler.New(logger, db, cache)
|
||||
|
||||
gql := mux.Group("/graphql")
|
||||
@@ -42,6 +41,9 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo) (core.Mux, err
|
||||
gql.POST("/query", gqlHandler.Query())
|
||||
}
|
||||
|
||||
demoHandler := demo_handler.New(logger, db, cache, grpConn)
|
||||
userHandler := user_handler.New(logger, db, cache)
|
||||
|
||||
// user_demo CURD
|
||||
user := mux.Group("/user", core.WrapAuthHandler(auth.AuthHandler))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
type CreateUserInfo struct {
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
func (u *userSer) Create(ctx core.Context, user *CreateUserInfo) (id int32, err error) {
|
||||
model := user_demo_repo.NewModel()
|
||||
model.UserName = user.UserName
|
||||
model.NickName = user.NickName
|
||||
model.Mobile = user.Mobile
|
||||
|
||||
id, err = model.Create(u.db.GetDbW().WithContext(ctx.RequestContext()))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (u *userSer) Delete(ctx core.Context, id int32) (err error) {
|
||||
model := user_demo_repo.NewModel()
|
||||
model.Id = id
|
||||
err = model.Delete(u.db.GetDbW().WithContext(ctx.RequestContext()))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (u *userSer) GetUserByUserName(ctx core.Context, username string) (user *user_demo_repo.UserDemo, err error) {
|
||||
user, err = user_demo_repo.NewQueryBuilder().
|
||||
WhereUserName(db_repo.EqualPredicate, username).
|
||||
QueryOne(u.db.GetDbR().WithContext(ctx.RequestContext()))
|
||||
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
user = user_demo_repo.NewModel()
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (u *userSer) UpdateNickNameByID(ctx core.Context, id int32, nickname string) (err error) {
|
||||
model := user_demo_repo.NewModel()
|
||||
model.Id = id
|
||||
|
||||
data := map[string]interface{}{
|
||||
"nick_name": nickname,
|
||||
}
|
||||
|
||||
err = model.Updates(u.db.GetDbW().WithContext(ctx.RequestContext()), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
)
|
||||
|
||||
var _ UserService = (*userSer)(nil)
|
||||
|
||||
type UserService interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
Create(ctx core.Context, user *CreateUserInfo) (id int32, err error)
|
||||
UpdateNickNameByID(ctx core.Context, id int32, username string) (err error)
|
||||
GetUserByUserName(ctx core.Context, username string) (user *user_demo_repo.UserDemo, err error)
|
||||
Delete(ctx core.Context, id int32) (err error)
|
||||
}
|
||||
|
||||
type userSer struct {
|
||||
db db.Repo
|
||||
cache cache.Repo
|
||||
}
|
||||
|
||||
func NewUserService(db db.Repo, cache cache.Repo) UserService {
|
||||
return &userSer{
|
||||
db: db,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userSer) i() {}
|
||||
@@ -1,76 +0,0 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/user_model"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
)
|
||||
|
||||
var _ UserService = (*userSer)(nil)
|
||||
|
||||
type UserService interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
Create(ctx core.Context, user *user_model.CreateRequest) (id uint, err error)
|
||||
UpdateNickNameByID(ctx core.Context, id uint, username string) (err error)
|
||||
GetUserByUserName(ctx core.Context, username string) (user *user_model.UserDemo, err error)
|
||||
Delete(ctx core.Context, id uint) (err error)
|
||||
}
|
||||
|
||||
type userSer struct {
|
||||
db db.Repo
|
||||
cache cache.Repo
|
||||
userRepo user_demo_repo.UserRepo
|
||||
}
|
||||
|
||||
func NewUserService(db db.Repo, cache cache.Repo) UserService {
|
||||
userRepo := user_demo_repo.NewUserRepo(db)
|
||||
return &userSer{
|
||||
db: db,
|
||||
cache: cache,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userSer) i() {}
|
||||
|
||||
func (u *userSer) Create(ctx core.Context, user *user_model.CreateRequest) (id uint, err error) {
|
||||
create := user_model.UserDemo{
|
||||
UserName: user.UserName,
|
||||
NickName: user.NickName,
|
||||
Mobile: user.Mobile,
|
||||
}
|
||||
|
||||
id, err = u.userRepo.Create(ctx, create)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (u *userSer) UpdateNickNameByID(ctx core.Context, id uint, username string) (err error) {
|
||||
err = u.userRepo.UpdateNickNameByID(ctx, id, username)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userSer) GetUserByUserName(ctx core.Context, username string) (user *user_model.UserDemo, err error) {
|
||||
user, err = u.userRepo.GetUserByUserName(ctx, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (u *userSer) Delete(ctx core.Context, id uint) (err error) {
|
||||
err = u.userRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Vendored
+3
-3
@@ -35,7 +35,7 @@ type Repo interface {
|
||||
ExpireAt(key string, ttl time.Time) bool
|
||||
Del(keys ...string) bool
|
||||
Incr(key string, options ...Option) int64
|
||||
Close()
|
||||
Close() error
|
||||
}
|
||||
|
||||
type cacheRepo struct {
|
||||
@@ -179,8 +179,8 @@ func (c *cacheRepo) Incr(key string, options ...Option) int64 {
|
||||
}
|
||||
|
||||
// Close close redis client
|
||||
func (c *cacheRepo) Close() {
|
||||
c.client.Close()
|
||||
func (c *cacheRepo) Close() error {
|
||||
return c.client.Close()
|
||||
}
|
||||
|
||||
// WithTrace 设置trace信息
|
||||
|
||||
@@ -90,8 +90,8 @@ type Context interface {
|
||||
setLogger(logger *zap.Logger)
|
||||
|
||||
// Payload 正确返回
|
||||
Payload(payload errno.Error)
|
||||
getPayload() errno.Error
|
||||
Payload(payload interface{})
|
||||
getPayload() interface{}
|
||||
|
||||
// GraphPayload GraphQL返回值 与 api 返回结构不同
|
||||
GraphPayload(payload interface{})
|
||||
@@ -225,14 +225,14 @@ func (c *context) setLogger(logger *zap.Logger) {
|
||||
c.ctx.Set(_LoggerName, logger)
|
||||
}
|
||||
|
||||
func (c *context) getPayload() errno.Error {
|
||||
func (c *context) getPayload() interface{} {
|
||||
if payload, ok := c.ctx.Get(_PayloadName); ok != false {
|
||||
return payload.(errno.Error)
|
||||
return payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) Payload(payload errno.Error) {
|
||||
func (c *context) Payload(payload interface{}) {
|
||||
c.ctx.Set(_PayloadName, payload)
|
||||
}
|
||||
|
||||
|
||||
+30
-17
@@ -336,7 +336,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
if err := recover(); err != nil {
|
||||
stackInfo := string(debug.Stack())
|
||||
logger.Error("got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
|
||||
context.AbortWithError(code.ErrServer)
|
||||
context.AbortWithError(errno.NewError(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
code.Text(code.ServerError)),
|
||||
)
|
||||
|
||||
if notify := opt.panicNotify; notify != nil {
|
||||
notify(context, err, stackInfo)
|
||||
@@ -348,7 +352,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
response errno.Error
|
||||
response interface{}
|
||||
businessCode int
|
||||
businessCodeMsg string
|
||||
abortErr error
|
||||
@@ -364,23 +368,28 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
if err := context.abortError(); err != nil { // customer err
|
||||
multierr.AppendInto(&abortErr, err.GetErr())
|
||||
response = err
|
||||
businessCode = err.GetBusinessCode()
|
||||
businessCodeMsg = err.GetMsg()
|
||||
|
||||
if x := context.Trace(); x != nil {
|
||||
context.SetHeader(trace.Header, x.ID())
|
||||
traceId = x.ID()
|
||||
}
|
||||
|
||||
ctx.JSON(err.GetHttpCode(), &code.Failure{
|
||||
Code: businessCode,
|
||||
Message: businessCodeMsg,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
response = context.getPayload()
|
||||
}
|
||||
|
||||
if response != nil {
|
||||
if x := context.Trace(); x != nil {
|
||||
context.SetHeader(trace.Header, x.ID())
|
||||
response.WithID(x.ID())
|
||||
traceId = x.ID()
|
||||
} else {
|
||||
response.WithID("")
|
||||
if response != nil {
|
||||
if x := context.Trace(); x != nil {
|
||||
context.SetHeader(trace.Header, x.ID())
|
||||
traceId = x.ID()
|
||||
}
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
businessCode = response.GetBusinessCode()
|
||||
businessCodeMsg = response.GetMsg()
|
||||
ctx.JSON(response.GetHttpCode(), response)
|
||||
}
|
||||
|
||||
graphResponse = context.getGraphPayload()
|
||||
@@ -464,7 +473,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
defer releaseContext(context)
|
||||
|
||||
if !limiter.Allow() {
|
||||
context.AbortWithError(code.ErrManyRequest)
|
||||
context.AbortWithError(errno.NewError(
|
||||
http.StatusTooManyRequests,
|
||||
code.TooManyRequests,
|
||||
code.Text(code.TooManyRequests)),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -489,7 +502,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
Host: ctx.Host(),
|
||||
Status: "ok",
|
||||
}
|
||||
ctx.Payload(code.OK.WithData(resp))
|
||||
ctx.Payload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
var _ Repo = (*dbRepo)(nil)
|
||||
@@ -80,6 +81,9 @@ func dbConnect(user, pass, addr, dbName string) (*gorm.DB, error) {
|
||||
"Local")
|
||||
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true,
|
||||
},
|
||||
//Logger: logger.Default.LogMode(logger.Info), // 日志配置
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var _ ClientConn = (*clientConn)(nil)
|
||||
|
||||
type ClientConn interface {
|
||||
i()
|
||||
Conn() *grpc.ClientConn
|
||||
}
|
||||
|
||||
type clientConn struct {
|
||||
conn *grpc.ClientConn
|
||||
}
|
||||
|
||||
func New() (ClientConn, error) {
|
||||
|
||||
// TODO 需从配置文件中获取
|
||||
//target := "127.0.0.1:9988"
|
||||
//secret := "abcdef"
|
||||
//
|
||||
//clientInterceptor := NewClientInterceptor(func(message []byte) (authorization string, err error) {
|
||||
// return GenerateSign(secret, message)
|
||||
//})
|
||||
//
|
||||
//conn, err := grpclient.New(target,
|
||||
// grpclient.WithKeepAlive(keepAlive),
|
||||
// grpclient.WithDialTimeout(time.Second*5),
|
||||
// grpclient.WithUnaryInterceptor(clientInterceptor.UnaryInterceptor),
|
||||
//)
|
||||
//
|
||||
//return &clientConn{
|
||||
// conn: conn,
|
||||
//}, err
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *clientConn) i() {}
|
||||
|
||||
func (c *clientConn) Conn() *grpc.ClientConn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func ContextWithValueAndTimeout(value interface{}, duration time.Duration) context.Context {
|
||||
ctx, _ := context.WithTimeout(context.Background(), duration)
|
||||
return context.WithValue(ctx, ClientWithContextKey, value)
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/p"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/time_parse"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/trace"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
// Trace ID
|
||||
TraceID = "trace-id"
|
||||
)
|
||||
|
||||
type clientWithContextKeyType struct{ name string }
|
||||
|
||||
var ClientWithContextKey = clientWithContextKeyType{"_client_with_context"}
|
||||
|
||||
// ClientInterceptor the client's interceptor
|
||||
type ClientInterceptor struct {
|
||||
sign Sign
|
||||
}
|
||||
|
||||
// NewClientInterceptor create a client interceptor
|
||||
func NewClientInterceptor(sign Sign) *ClientInterceptor {
|
||||
return &ClientInterceptor{
|
||||
sign: sign,
|
||||
}
|
||||
}
|
||||
|
||||
// UnaryInterceptor a interceptor for client unary operations
|
||||
func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
var (
|
||||
invokerErr error
|
||||
ts = time.Now()
|
||||
coreContext = ctx.Value(ClientWithContextKey).(core.Context)
|
||||
)
|
||||
|
||||
defer func() { // double recover for safety
|
||||
if err := recover(); err != nil {
|
||||
stackInfo := string(debug.Stack())
|
||||
coreContext.Logger().Error("UnaryInterceptor got double panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
|
||||
coreContext.AbortWithError(errno.NewError(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
code.Text(code.ServerError)),
|
||||
)
|
||||
notify.OnPanicNotify(coreContext, err, stackInfo)
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
stackInfo := string(debug.Stack())
|
||||
coreContext.Logger().Error("UnaryInterceptor got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
|
||||
coreContext.AbortWithError(errno.NewError(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
code.Text(code.ServerError)),
|
||||
)
|
||||
notify.OnPanicNotify(coreContext, err, stackInfo)
|
||||
}
|
||||
|
||||
if coreContext.Trace() != nil {
|
||||
var mapReq, mapReply map[string]interface{}
|
||||
mapReq, err := ProtoMessage2Map(req.(proto.Message))
|
||||
if err != nil {
|
||||
p.Println("req ProtoMessage2Map err", err, p.WithTrace(coreContext.Trace()))
|
||||
}
|
||||
|
||||
mapReply, err = ProtoMessage2Map(reply.(proto.Message))
|
||||
if err != nil {
|
||||
p.Println("reply ProtoMessage2Map err", err, p.WithTrace(coreContext.Trace()))
|
||||
}
|
||||
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
|
||||
gRPCTrace := new(trace.Grpc)
|
||||
gRPCTrace.Timestamp = time_parse.CSTLayoutString()
|
||||
gRPCTrace.Addr = cc.Target()
|
||||
gRPCTrace.Method = method
|
||||
gRPCTrace.Meta = meta
|
||||
gRPCTrace.Request = mapReq
|
||||
gRPCTrace.Response = mapReply
|
||||
gRPCTrace.CostSeconds = time.Since(ts).Seconds()
|
||||
|
||||
if invokerErr != nil {
|
||||
statusErr, ok := status.FromError(invokerErr)
|
||||
if ok {
|
||||
gRPCTrace.Code = statusErr.Code().String()
|
||||
gRPCTrace.Message = statusErr.Message()
|
||||
}
|
||||
}
|
||||
|
||||
coreContext.Trace().AppendGRPC(gRPCTrace)
|
||||
}
|
||||
}()
|
||||
|
||||
if c.sign != nil {
|
||||
var (
|
||||
raw string
|
||||
signature string
|
||||
err error
|
||||
)
|
||||
|
||||
if req != nil {
|
||||
if raw, err = ProtoMessage2JSON(req.(proto.Message)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if signature, err = c.sign([]byte(raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
if meta == nil {
|
||||
meta = make(metadata.MD)
|
||||
}
|
||||
|
||||
meta.Set(ProxyAuthorization, signature)
|
||||
ctx = metadata.NewOutgoingContext(ctx, meta)
|
||||
}
|
||||
|
||||
if coreContext.Trace() != nil {
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
if meta == nil {
|
||||
meta = make(metadata.MD)
|
||||
}
|
||||
|
||||
meta.Set(TraceID, coreContext.Trace().ID())
|
||||
ctx = metadata.NewOutgoingContext(ctx, meta)
|
||||
}
|
||||
|
||||
invokerErr = invoker(ctx, method, req, reply, cc, opts...)
|
||||
return invokerErr
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/keepalive"
|
||||
)
|
||||
|
||||
var (
|
||||
keepAlive = &keepalive.ClientParameters{
|
||||
Time: 10 * time.Second,
|
||||
Timeout: time.Second,
|
||||
PermitWithoutStream: true,
|
||||
}
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package grpclient
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package grpclient
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/router"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/env"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/logger"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/shutdown"
|
||||
@@ -54,8 +55,14 @@ func main() {
|
||||
loggers.Fatal("new cache err", zap.Error(err))
|
||||
}
|
||||
|
||||
// 初始化 gRPC client
|
||||
gRPCRepo, err := grpc.New()
|
||||
if err != nil {
|
||||
loggers.Fatal("new grpc err", zap.Error(err))
|
||||
}
|
||||
|
||||
// 初始化 HTTP 服务
|
||||
mux, err := router.NewHTTPMux(loggers, dbRepo, cacheRepo)
|
||||
mux, err := router.NewHTTPMux(loggers, dbRepo, cacheRepo, gRPCRepo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -89,16 +96,33 @@ func main() {
|
||||
func() {
|
||||
if err := dbRepo.DbWClose(); err != nil {
|
||||
loggers.Error("dbw close err", zap.Error(err))
|
||||
} else {
|
||||
loggers.Info("dbw close success")
|
||||
}
|
||||
|
||||
if err := dbRepo.DbRClose(); err != nil {
|
||||
loggers.Error("dbr close err", zap.Error(err))
|
||||
} else {
|
||||
loggers.Info("dbr close success")
|
||||
}
|
||||
},
|
||||
|
||||
// 关闭 cache
|
||||
func() {
|
||||
cacheRepo.Close()
|
||||
if err := cacheRepo.Close(); err != nil {
|
||||
loggers.Error("cache close err", zap.Error(err))
|
||||
} else {
|
||||
loggers.Info("cache close success")
|
||||
}
|
||||
},
|
||||
|
||||
// 关闭 gRPC client
|
||||
//func() {
|
||||
// if err := gRPCRepo.Conn().Close(); err != nil {
|
||||
// loggers.Error("gRPC client close err", zap.Error(err))
|
||||
// } else {
|
||||
// loggers.Info("gRPC client close success")
|
||||
// }
|
||||
//},
|
||||
)
|
||||
}
|
||||
|
||||
+10
-32
@@ -11,10 +11,6 @@ var _ Error = (*err)(nil)
|
||||
type Error interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
// WithData 设置成功时返回的数据
|
||||
WithData(data interface{}) Error
|
||||
// WithID 设置当前请求的唯一ID
|
||||
WithID(id string) Error
|
||||
// WithErr 设置错误信息
|
||||
WithErr(err error) Error
|
||||
// GetBusinessCode 获取 Business Code
|
||||
@@ -30,36 +26,22 @@ type Error interface {
|
||||
}
|
||||
|
||||
type err struct {
|
||||
HttpCode int `json:"-"` // HTTP Code
|
||||
BusinessCode int `json:"code"` // Business Code
|
||||
Msg string `json:"msg"` // 描述信息
|
||||
Data interface{} `json:"data"` // 接口数据
|
||||
Err error `json:"-"` // 错误信息
|
||||
ID string `json:"id,omitempty"` // 当前请求的唯一ID,便于问题定位,忽略也可以
|
||||
HttpCode int // HTTP Code
|
||||
BusinessCode int // Business Code
|
||||
Message string // 描述信息
|
||||
Err error // 错误信息
|
||||
}
|
||||
|
||||
func NewError(httpCode, businessCode int, msg string) Error {
|
||||
return &err{
|
||||
HttpCode: httpCode,
|
||||
BusinessCode: businessCode,
|
||||
Msg: msg,
|
||||
Data: nil,
|
||||
Err: nil,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *err) i() {}
|
||||
|
||||
func (e *err) WithData(data interface{}) Error {
|
||||
e.Data = data
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *err) WithID(id string) Error {
|
||||
e.ID = id
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *err) WithErr(err error) Error {
|
||||
e.Err = errors.WithStack(err)
|
||||
return e
|
||||
@@ -74,7 +56,7 @@ func (e *err) GetBusinessCode() int {
|
||||
}
|
||||
|
||||
func (e *err) GetMsg() string {
|
||||
return e.Msg
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *err) GetErr() error {
|
||||
@@ -84,17 +66,13 @@ func (e *err) GetErr() error {
|
||||
// ToString 返回 JSON 格式的错误详情
|
||||
func (e *err) ToString() string {
|
||||
err := &struct {
|
||||
HttpCode int `json:"http_code"`
|
||||
BusinessCode int `json:"business_code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
ID string `json:"id,omitempty"`
|
||||
HttpCode int `json:"http_code"`
|
||||
BusinessCode int `json:"business_code"`
|
||||
Message string `json:"message"`
|
||||
}{
|
||||
HttpCode: e.HttpCode,
|
||||
BusinessCode: e.BusinessCode,
|
||||
Msg: e.Msg,
|
||||
Data: e.Data,
|
||||
ID: e.ID,
|
||||
Message: e.Message,
|
||||
}
|
||||
|
||||
raw, _ := json.Marshal(err)
|
||||
|
||||
+10
-28
@@ -4,31 +4,23 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/trace"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultDialTimeout = time.Second * 2
|
||||
)
|
||||
|
||||
type Trace = trace.T
|
||||
|
||||
type Option func(*option)
|
||||
|
||||
type option struct {
|
||||
credential credentials.TransportCredentials
|
||||
keepalive *keepalive.ClientParameters
|
||||
resolverBuilder resolver.Builder
|
||||
dialTimeout time.Duration
|
||||
sign Sign
|
||||
trace *trace.Trace
|
||||
grpc *trace.Grpc
|
||||
credential credentials.TransportCredentials
|
||||
keepalive *keepalive.ClientParameters
|
||||
dialTimeout time.Duration
|
||||
unaryInterceptor grpc.UnaryClientInterceptor
|
||||
}
|
||||
|
||||
// WithCredential setup credential for tls
|
||||
@@ -52,20 +44,9 @@ func WithDialTimeout(timeout time.Duration) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithSign setup the signature handler
|
||||
func WithSign(sign Sign) Option {
|
||||
func WithUnaryInterceptor(unaryInterceptor grpc.UnaryClientInterceptor) Option {
|
||||
return func(opt *option) {
|
||||
opt.sign = sign
|
||||
}
|
||||
}
|
||||
|
||||
// WithTrace setup trace info
|
||||
func WithTrace(t Trace) Option {
|
||||
return func(opt *option) {
|
||||
if t != nil {
|
||||
opt.trace = t.(*trace.Trace)
|
||||
opt.grpc = new(trace.Grpc)
|
||||
}
|
||||
opt.unaryInterceptor = unaryInterceptor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,12 +70,13 @@ func New(target string, options ...Option) (*grpc.ClientConn, error) {
|
||||
dialTimeout = opt.dialTimeout
|
||||
}
|
||||
|
||||
clientInterceptor := NewClientInterceptor(opt.sign, opt.trace, opt.grpc)
|
||||
|
||||
dialOptions := []grpc.DialOption{
|
||||
grpc.WithBlock(),
|
||||
grpc.WithKeepaliveParams(*kacp),
|
||||
grpc.WithUnaryInterceptor(clientInterceptor.UnaryInterceptor),
|
||||
}
|
||||
|
||||
if opt.unaryInterceptor != nil {
|
||||
dialOptions = append(dialOptions, grpc.WithUnaryInterceptor(opt.unaryInterceptor))
|
||||
}
|
||||
|
||||
if opt.credential == nil {
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package grpclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/time_parse"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/trace"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
// Trace ID
|
||||
TraceID = "trace-id"
|
||||
)
|
||||
|
||||
// ClientInterceptor the client's interceptor
|
||||
type ClientInterceptor struct {
|
||||
sign Sign
|
||||
trace *trace.Trace
|
||||
grpc *trace.Grpc
|
||||
}
|
||||
|
||||
// NewClientInterceptor create a client interceptor
|
||||
func NewClientInterceptor(sign Sign, trace *trace.Trace, grpc *trace.Grpc) *ClientInterceptor {
|
||||
return &ClientInterceptor{
|
||||
sign: sign,
|
||||
trace: trace,
|
||||
grpc: grpc,
|
||||
}
|
||||
}
|
||||
|
||||
// UnaryInterceptor a interceptor for client unary operations
|
||||
func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) {
|
||||
ts := time.Now()
|
||||
var invokerErr error
|
||||
|
||||
defer func() { // double recover for safety
|
||||
if p := recover(); p != nil {
|
||||
debug.PrintStack()
|
||||
err = status.Errorf(codes.Internal, "got double panic err: %+v,Stack: %s", p, debug.Stack())
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
debug.PrintStack()
|
||||
err = status.Errorf(codes.Internal, "got panic err: %+v,Stack: %s", p, debug.Stack())
|
||||
return
|
||||
}
|
||||
|
||||
var mapReq, mapReply map[string]interface{}
|
||||
mapReq, err = ProtoMessage2Map(req.(proto.Message))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mapReply, err = ProtoMessage2Map(reply.(proto.Message))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
|
||||
if c.trace != nil {
|
||||
c.grpc.Timestamp = time_parse.CSTLayoutString()
|
||||
c.grpc.Addr = cc.Target()
|
||||
c.grpc.Method = method
|
||||
c.grpc.Meta = meta
|
||||
c.grpc.Request = mapReq
|
||||
c.grpc.Response = mapReply
|
||||
c.grpc.CostSeconds = time.Since(ts).Seconds()
|
||||
|
||||
if invokerErr != nil {
|
||||
statusErr, ok := status.FromError(invokerErr)
|
||||
if ok {
|
||||
c.grpc.Code = statusErr.Code().String()
|
||||
c.grpc.Message = statusErr.Message()
|
||||
}
|
||||
}
|
||||
|
||||
c.trace.AppendGRPC(c.grpc)
|
||||
}
|
||||
}()
|
||||
|
||||
if c.sign != nil {
|
||||
var raw string
|
||||
if req != nil {
|
||||
if raw, err = ProtoMessage2JSON(req.(proto.Message)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var signature string
|
||||
if signature, err = c.sign([]byte(raw)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
if meta == nil {
|
||||
meta = make(metadata.MD)
|
||||
}
|
||||
|
||||
meta.Set(ProxyAuthorization, signature)
|
||||
ctx = metadata.NewOutgoingContext(ctx, meta)
|
||||
}
|
||||
|
||||
if c.trace != nil {
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
if meta == nil {
|
||||
meta = make(metadata.MD)
|
||||
}
|
||||
|
||||
meta.Set(TraceID, c.trace.ID())
|
||||
ctx = metadata.NewOutgoingContext(ctx, meta)
|
||||
}
|
||||
|
||||
invokerErr = invoker(ctx, method, req, reply, cc, opts...)
|
||||
if invokerErr != nil {
|
||||
return invokerErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package httpclient
|
||||
|
||||
var _ ReplyErr = (*replyErr)(nil)
|
||||
|
||||
// ReplyErr 错误响应,当 resp.StatusCode != http.StatusOK 时用来包装返回的 httpcode 和 body 。
|
||||
type ReplyErr interface {
|
||||
error
|
||||
StatusCode() int
|
||||
Body() []byte
|
||||
}
|
||||
|
||||
type replyErr struct {
|
||||
err error
|
||||
statusCode int
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (r *replyErr) Error() string {
|
||||
return r.err.Error()
|
||||
}
|
||||
|
||||
func (r *replyErr) StatusCode() int {
|
||||
return r.statusCode
|
||||
}
|
||||
|
||||
func (r *replyErr) Body() []byte {
|
||||
return r.body
|
||||
}
|
||||
|
||||
func newReplyErr(statusCode int, body []byte, err error) ReplyErr {
|
||||
return &replyErr{
|
||||
statusCode: statusCode,
|
||||
body: body,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// ToReplyErr 尝试将 err 转换为 ReplyErr
|
||||
func ToReplyErr(err error) (ReplyErr, bool) {
|
||||
if err == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
e, ok := err.(ReplyErr)
|
||||
return e, ok
|
||||
}
|
||||
@@ -105,7 +105,11 @@ func doHTTP(ctx context.Context, method, url string, payload []byte, opt *option
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return body, resp.StatusCode, errors.Errorf("do [%s %s] return code: %d message: %s", method, url, resp.StatusCode, string(body))
|
||||
return nil, resp.StatusCode, newReplyErr(
|
||||
resp.StatusCode,
|
||||
body,
|
||||
errors.Errorf("do [%s %s] return code: %d message: %s", method, url, resp.StatusCode, string(body)),
|
||||
)
|
||||
}
|
||||
|
||||
return body, http.StatusOK, nil
|
||||
|
||||
@@ -18,6 +18,8 @@ type T interface {
|
||||
WithResponse(resp *Response) *Trace
|
||||
AppendDialog(dialog *Dialog) *Trace
|
||||
AppendSQL(sql *SQL) *Trace
|
||||
AppendRedis(redis *Redis) *Trace
|
||||
AppendGRPC(grpc *Grpc) *Trace
|
||||
}
|
||||
|
||||
// Trace 记录的参数
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
printf "\nRegenerating mysql file\n\n"
|
||||
time go run -v ./cmd/mysqlmd/main.go -env fat
|
||||
|
||||
printf "\nRegenerating code\n\n"
|
||||
time go build -o gormgen ./cmd/gormgen/main.go
|
||||
mv gormgen $GOPATH/bin
|
||||
|
||||
go generate ./...
|
||||
|
||||
printf "\nFormatting code\n\n"
|
||||
time go run -v github.com/koketama/mfmt
|
||||
|
||||
printf "\nDone.\n\n"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
printf "\nRegenerating handler file\n\n"
|
||||
time go run -v ./cmd/handlergen/main.go -handler $1
|
||||
|
||||
printf "\nFormatting code\n\n"
|
||||
time go run -v github.com/koketama/mfmt
|
||||
|
||||
printf "\nDone.\n\n"
|
||||
Reference in New Issue
Block a user