This commit is contained in:
新亮
2021-02-19 22:00:01 +08:00
parent 334bfbb4aa
commit cc8012b09d
17 changed files with 521 additions and 194 deletions

View File

@@ -43,10 +43,16 @@ var doc = `{
"summary": "获取授权信息", "summary": "获取授权信息",
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/demo.authResponse" "$ref": "#/definitions/demo.authResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -75,7 +81,7 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "用户信息", "description": "OK",
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
@@ -92,6 +98,18 @@ var doc = `{
} }
} }
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -129,10 +147,22 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/user_model.CreateResponse" "$ref": "#/definitions/user_model.CreateResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -168,7 +198,19 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息" "description": ""
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -204,10 +246,22 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/user_model.DetailResponse" "$ref": "#/definitions/user_model.DetailResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -245,16 +299,41 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse" "$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
} }
}, },
"definitions": { "definitions": {
"code.Failure": {
"type": "object",
"properties": {
"code": {
"description": "业务码",
"type": "integer"
},
"message": {
"description": "描述信息",
"type": "string"
}
}
},
"demo.authResponse": { "demo.authResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -302,7 +381,7 @@ var doc = `{
"type": "integer" "type": "integer"
}, },
"mobile": { "mobile": {
"description": "手机号", "description": "手机号(脱敏)",
"type": "string" "type": "string"
}, },
"nick_name": { "nick_name": {

View File

@@ -26,10 +26,16 @@
"summary": "获取授权信息", "summary": "获取授权信息",
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/demo.authResponse" "$ref": "#/definitions/demo.authResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -58,7 +64,7 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "用户信息", "description": "OK",
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
@@ -75,6 +81,18 @@
} }
} }
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -112,10 +130,22 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/user_model.CreateResponse" "$ref": "#/definitions/user_model.CreateResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -151,7 +181,19 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息" "description": ""
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -187,10 +229,22 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/user_model.DetailResponse" "$ref": "#/definitions/user_model.DetailResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
@@ -228,16 +282,41 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "返回信息", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse" "$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
} }
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
} }
} }
} }
} }
}, },
"definitions": { "definitions": {
"code.Failure": {
"type": "object",
"properties": {
"code": {
"description": "业务码",
"type": "integer"
},
"message": {
"description": "描述信息",
"type": "string"
}
}
},
"demo.authResponse": { "demo.authResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -285,7 +364,7 @@
"type": "integer" "type": "integer"
}, },
"mobile": { "mobile": {
"description": "手机号", "description": "手机号(脱敏)",
"type": "string" "type": "string"
}, },
"nick_name": { "nick_name": {

View File

@@ -1,4 +1,13 @@
definitions: definitions:
code.Failure:
properties:
code:
description: 业务码
type: integer
message:
description: 描述信息
type: string
type: object
demo.authResponse: demo.authResponse:
properties: properties:
authorization: authorization:
@@ -32,7 +41,7 @@ definitions:
description: 用户主键ID description: 用户主键ID
type: integer type: integer
mobile: mobile:
description: 手机号 description: 手机号(脱敏)
type: string type: string
nick_name: nick_name:
description: 昵称 description: 昵称
@@ -74,9 +83,13 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: 返回信息 description: OK
schema: schema:
$ref: '#/definitions/demo.authResponse' $ref: '#/definitions/demo.authResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
summary: 获取授权信息 summary: 获取授权信息
tags: tags:
- Demo - Demo
@@ -95,7 +108,7 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: 用户信息 description: OK
schema: schema:
items: items:
properties: properties:
@@ -107,6 +120,14 @@ paths:
type: string type: string
type: object type: object
type: array type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/code.Failure'
summary: Trace 示例 summary: Trace 示例
tags: tags:
- Demo - Demo
@@ -131,9 +152,17 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: 返回信息 description: OK
schema: schema:
$ref: '#/definitions/user_model.CreateResponse' $ref: '#/definitions/user_model.CreateResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/code.Failure'
summary: 创建用户 summary: 创建用户
tags: tags:
- User - User
@@ -157,7 +186,15 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: 返回信息 description: ""
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/code.Failure'
summary: 删除用户 - 更新 is_deleted = 1 summary: 删除用户 - 更新 is_deleted = 1
tags: tags:
- User - User
@@ -181,9 +218,17 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: 返回信息 description: OK
schema: schema:
$ref: '#/definitions/user_model.DetailResponse' $ref: '#/definitions/user_model.DetailResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/code.Failure'
summary: 用户详情 summary: 用户详情
tags: tags:
- User - User
@@ -208,9 +253,17 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: 返回信息 description: OK
schema: schema:
$ref: '#/definitions/user_model.UpdateNickNameByIDResponse' $ref: '#/definitions/user_model.UpdateNickNameByIDResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/code.Failure'
summary: 编辑用户 - 通过用户主键ID更新用户昵称 summary: 编辑用户 - 通过用户主键ID更新用户昵称
tags: tags:
- User - User

View File

@@ -1,28 +1,41 @@
package code package code
import ( // 错误时返回结构
"net/http" type Failure struct {
Code int `json:"code"` // 业务码
"github.com/xinliangnote/go-gin-api/pkg/errno" Message string `json:"message"` // 描述信息
) }
var (
// OK
OK = errno.NewError(http.StatusOK, 1, "OK")
const (
// 服务级错误码 // 服务级错误码
ErrServer = errno.NewError(http.StatusInternalServerError, 10101, http.StatusText(http.StatusInternalServerError)) ServerError = 10101
ErrManyRequest = errno.NewError(http.StatusTooManyRequests, 10102, http.StatusText(http.StatusTooManyRequests)) TooManyRequests = 10102
ErrParamBind = errno.NewError(http.StatusBadRequest, 10103, "参数信息有误") ParamBindError = 10103
ErrAuthorization = errno.NewError(http.StatusUnauthorized, 10104, "签名信息有误") AuthorizationError = 10104
CallHTTPError = 10105
// 模块级错误码 - 用户模块 // 模块级错误码 - 用户模块
ErrUser = errno.NewError(http.StatusBadRequest, 20101, "非法用户") IllegalUserName = 20101
ErrUserName = errno.NewError(http.StatusBadRequest, 20102, "账号不能为空") UserCreateError = 20102
ErrUserCreate = errno.NewError(http.StatusBadRequest, 20103, "创建用户失败") UserUpdateError = 20103
ErrUserUpdate = errno.NewError(http.StatusBadRequest, 20104, "更新用户失败") UserSearchError = 20104
ErrUserSearch = errno.NewError(http.StatusBadRequest, 20105, "查询用户失败")
ErrUserHTTP = errno.NewError(http.StatusBadRequest, 20106, "调用他方接口失败")
// ... // ...
) )
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]
}

View File

@@ -1,6 +1,7 @@
package demo package demo
import ( import (
"net/http"
"time" "time"
"github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/configs"
@@ -11,6 +12,7 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/db"
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc" "github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/httpclient" "github.com/xinliangnote/go-gin-api/pkg/httpclient"
"github.com/xinliangnote/go-gin-api/pkg/p" "github.com/xinliangnote/go-gin-api/pkg/p"
"github.com/xinliangnote/go-gin-api/pkg/token" "github.com/xinliangnote/go-gin-api/pkg/token"
@@ -49,19 +51,27 @@ func (d *Demo) Get() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(request) req := new(request)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(code.ErrParamBind.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return return
} }
if req.Name != "Tom" { if req.Name != "Tom" {
c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Tom"))) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.IllegalUserName,
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Tom")),
)
return return
} }
c.Payload(code.OK.WithData(&response{ c.Payload(&response{
Name: "Tom", Name: "Tom",
Job: "Student", Job: "Student",
})) })
} }
} }
@@ -78,19 +88,27 @@ func (d *Demo) Post() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(request) req := new(request)
if err := c.ShouldBindPostForm(req); err != nil { if err := c.ShouldBindPostForm(req); err != nil {
c.AbortWithError(code.ErrParamBind.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return return
} }
if req.Name != "Jack" { if req.Name != "Jack" {
c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Jack"))) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.IllegalUserName,
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Jack")),
)
return return
} }
c.Payload(code.OK.WithData(&response{ c.Payload(&response{
Name: "Jack", Name: "Jack",
Job: "Teacher", Job: "Teacher",
})) })
} }
} }
@@ -110,14 +128,19 @@ type traceResponse []struct {
// @Tags Demo // @Tags Demo
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Success 200 {object} authResponse "返回信息" // @Success 200 {object} authResponse
// @Failure 400 {object} code.Failure
// @Router /auth/get [post] // @Router /auth/get [post]
func (d *Demo) Auth() core.HandlerFunc { func (d *Demo) Auth() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
cfg := configs.Get().JWT cfg := configs.Get().JWT
tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration) tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
if err != nil { if err != nil {
c.AbortWithError(code.ErrAuthorization.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err),
)
return return
} }
@@ -125,7 +148,7 @@ func (d *Demo) Auth() core.HandlerFunc {
res.Authorization = tokenString res.Authorization = tokenString
res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix() res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix()
c.Payload(code.OK.WithData(res)) c.Payload(res)
} }
} }
@@ -136,7 +159,9 @@ func (d *Demo) Auth() core.HandlerFunc {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param Authorization header string true "签名" // @Param Authorization header string true "签名"
// @Success 200 {object} traceResponse "用户信息" // @Success 200 {object} traceResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /demo/trace [get] // @Router /demo/trace [get]
func (d *Demo) Trace() core.HandlerFunc { func (d *Demo) Trace() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
@@ -151,12 +176,16 @@ func (d *Demo) Trace() core.HandlerFunc {
if err != nil { if err != nil {
d.logger.Error("get [demo/get] err", zap.Error(err)) d.logger.Error("get [demo/get] err", zap.Error(err))
c.AbortWithError(code.ErrUserHTTP.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.CallHTTPError,
code.Text(code.CallHTTPError)).WithErr(err),
)
return return
} }
// 调试信息 // 调试信息
p.Println("res1.Data.Name", res1.Data.Name, p.WithTrace(c.Trace())) p.Println("res1.Name", res1.Name, p.WithTrace(c.Trace()))
// 三方请求信息 // 三方请求信息
res2, err := go_gin_api_repo.DemoPost("Jack", res2, err := go_gin_api_repo.DemoPost("Jack",
@@ -169,13 +198,17 @@ func (d *Demo) Trace() core.HandlerFunc {
if err != nil { if err != nil {
d.logger.Error("post [demo/post] err", zap.Error(err)) d.logger.Error("post [demo/post] err", zap.Error(err))
c.AbortWithError(code.ErrUserHTTP.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.CallHTTPError,
code.Text(code.CallHTTPError)).WithErr(err),
)
return return
} }
// 调试信息 // 调试信息
p.Println("res2.Data.Name", p.Println("res2.Name",
res2.Data.Name, res2.Name,
p.WithTrace(c.Trace()), p.WithTrace(c.Trace()),
) )
@@ -193,14 +226,14 @@ func (d *Demo) Trace() core.HandlerFunc {
data := &traceResponse{ data := &traceResponse{
{ {
Name: res1.Data.Name, Name: res1.Name,
Job: res1.Data.Job, Job: res1.Job,
}, },
{ {
Name: res2.Data.Name, Name: res2.Name,
Job: res2.Data.Job, Job: res2.Job,
}, },
} }
c.Payload(code.OK.WithData(data)) c.Payload(data)
} }
} }

View File

@@ -2,6 +2,7 @@ package user_handler
import ( import (
"errors" "errors"
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code" "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/model/user_model"
@@ -10,6 +11,7 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/db"
"github.com/xinliangnote/go-gin-api/pkg/ddm" "github.com/xinliangnote/go-gin-api/pkg/ddm"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -57,30 +59,45 @@ func (u *userDemo) i() {}
// @Produce json // @Produce json
// @Param RequestInfo body user_model.CreateRequest true "请求信息" // @Param RequestInfo body user_model.CreateRequest true "请求信息"
// @Param Authorization header string true "签名" // @Param Authorization header string true "签名"
// @Success 200 {object} user_model.CreateResponse "返回信息" // @Success 200 {object} user_model.CreateResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/create [post] // @Router /user/create [post]
func (u *userDemo) Create() core.HandlerFunc { func (u *userDemo) Create() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(user_model.CreateRequest) req := new(user_model.CreateRequest)
res := new(user_model.CreateResponse) res := new(user_model.CreateResponse)
if err := c.ShouldBindJSON(req); err != nil { if err := c.ShouldBindJSON(req); err != nil {
c.AbortWithError(code.ErrParamBind.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return return
} }
if req.UserName == "" { if req.UserName == "" {
c.AbortWithError(code.ErrUserName.WithErr(errors.New("req.UserName = ''"))) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.IllegalUserName,
code.Text(code.IllegalUserName)).WithErr(errors.New("req.UserName = ''")),
)
return return
} }
id, err := u.userService.Create(c, req) id, err := u.userService.Create(c, req)
if err != nil { if err != nil {
c.AbortWithError(code.ErrUserCreate.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserCreateError,
code.Text(code.UserCreateError)).WithErr(err),
)
return return
} }
res.Id = id res.Id = id
c.Payload(code.OK.WithData(res)) c.Payload(res)
} }
} }
@@ -92,25 +109,36 @@ func (u *userDemo) Create() core.HandlerFunc {
// @Produce json // @Produce json
// @Param RequestInfo body user_model.UpdateNickNameByIDRequest true "请求信息" // @Param RequestInfo body user_model.UpdateNickNameByIDRequest true "请求信息"
// @Param Authorization header string true "签名" // @Param Authorization header string true "签名"
// @Success 200 {object} user_model.UpdateNickNameByIDResponse "返回信息" // @Success 200 {object} user_model.UpdateNickNameByIDResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/update [put] // @Router /user/update [put]
func (u *userDemo) UpdateNickNameByID() core.HandlerFunc { func (u *userDemo) UpdateNickNameByID() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(user_model.UpdateNickNameByIDRequest) req := new(user_model.UpdateNickNameByIDRequest)
res := new(user_model.UpdateNickNameByIDResponse) res := new(user_model.UpdateNickNameByIDResponse)
if err := c.ShouldBindJSON(req); err != nil { if err := c.ShouldBindJSON(req); err != nil {
c.AbortWithError(code.ErrParamBind.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return return
} }
err := u.userService.UpdateNickNameByID(c, req.Id, req.NickName) err := u.userService.UpdateNickNameByID(c, req.Id, req.NickName)
if err != nil { if err != nil {
c.AbortWithError(code.ErrUserUpdate.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserUpdateError,
code.Text(code.UserUpdateError)).WithErr(err),
)
return return
} }
res.Id = req.Id res.Id = req.Id
c.Payload(code.OK.WithData(res))
c.Payload(res)
} }
} }
@@ -122,23 +150,33 @@ func (u *userDemo) UpdateNickNameByID() core.HandlerFunc {
// @Produce json // @Produce json
// @Param id path int true "用户ID" // @Param id path int true "用户ID"
// @Param Authorization header string true "签名" // @Param Authorization header string true "签名"
// @Success 200 "返回信息" // @Success 200
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/delete/{id} [patch] // @Router /user/delete/{id} [patch]
func (u *userDemo) Delete() core.HandlerFunc { func (u *userDemo) Delete() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(user_model.DeleteRequest) req := new(user_model.DeleteRequest)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(code.ErrParamBind.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return return
} }
err := u.userService.Delete(c, req.Id) err := u.userService.Delete(c, req.Id)
if err != nil { if err != nil {
c.AbortWithError(code.ErrUserUpdate.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserUpdateError,
code.Text(code.UserUpdateError)).WithErr(err),
)
return return
} }
c.Payload(code.OK.WithData(nil)) c.Payload("")
} }
} }
@@ -150,20 +188,30 @@ func (u *userDemo) Delete() core.HandlerFunc {
// @Produce json // @Produce json
// @Param username path string true "用户名" // @Param username path string true "用户名"
// @Param Authorization header string true "签名" // @Param Authorization header string true "签名"
// @Success 200 {object} user_model.DetailResponse "返回信息" // @Success 200 {object} user_model.DetailResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/info/{username} [get] // @Router /user/info/{username} [get]
func (u *userDemo) Detail() core.HandlerFunc { func (u *userDemo) Detail() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(user_model.DetailRequest) req := new(user_model.DetailRequest)
res := new(user_model.DetailResponse) res := new(user_model.DetailResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(code.ErrParamBind.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return return
} }
user, err := u.userService.GetUserByUserName(c, req.UserName) user, err := u.userService.GetUserByUserName(c, req.UserName)
if err != nil { if err != nil {
c.AbortWithError(code.ErrUserSearch.WithErr(err)) c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserSearchError,
code.Text(code.UserSearchError)).WithErr(err),
)
return return
} }
@@ -171,6 +219,7 @@ func (u *userDemo) Detail() core.HandlerFunc {
res.UserName = user.UserName res.UserName = user.UserName
res.NickName = user.NickName res.NickName = user.NickName
res.Mobile = ddm.Mobile(user.Mobile) res.Mobile = ddm.Mobile(user.Mobile)
c.Payload(code.OK.WithData(res))
c.Payload(res)
} }
} }

View File

@@ -2,7 +2,6 @@ package go_gin_api_repo
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/url" "net/url"
"github.com/xinliangnote/go-gin-api/pkg/httpclient" "github.com/xinliangnote/go-gin-api/pkg/httpclient"
@@ -11,23 +10,13 @@ import (
) )
type demoGetResponse struct { type demoGetResponse struct {
Code int `json:"code"` Name string `json:"name"`
Msg string `json:"msg"` Job string `json:"job"`
Data struct {
Name string `json:"name"`
Job string `json:"job"`
} `json:"data"`
ID string `json:"id"`
} }
type demoPostResponse struct { type demoPostResponse struct {
Code int `json:"code"` Name string `json:"name"`
Msg string `json:"msg"` Job string `json:"job"`
Data struct {
Name string `json:"name"`
Job string `json:"job"`
} `json:"data"`
ID string `json:"id"`
} }
func DemoGet(name string, opts ...httpclient.Option) (res *demoGetResponse, err error) { 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") 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 return res, nil
} }
@@ -55,17 +40,7 @@ func DemoGetRetryVerify(body []byte) (shouldRetry bool) {
return true return true
} }
type Response struct { return false
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
} }
func DemoPost(name string, opts ...httpclient.Option) (res *demoPostResponse, err error) { 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") 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 return res, nil
} }
@@ -95,15 +66,5 @@ func DemoPostRetryVerify(body []byte) (shouldRetry bool) {
return true return true
} }
type Response struct { return false
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
} }

View File

@@ -4,10 +4,8 @@ import "encoding/json"
func MockDemoGet() (body []byte) { func MockDemoGet() (body []byte) {
res := new(demoGetResponse) res := new(demoGetResponse)
res.Code = 1 res.Name = "AA"
res.Msg = "ok" res.Job = "AA_JOB"
res.Data.Name = "AA"
res.Data.Job = "AA_JOB"
body, _ = json.Marshal(res) body, _ = json.Marshal(res)
return body return body
@@ -15,10 +13,8 @@ func MockDemoGet() (body []byte) {
func MockDemoPost() (body []byte) { func MockDemoPost() (body []byte) {
res := new(demoPostResponse) res := new(demoPostResponse)
res.Code = 1 res.Name = "BB"
res.Msg = "ok" res.Job = "BB_JOB"
res.Data.Name = "BB"
res.Data.Job = "BB_JOB"
body, _ = json.Marshal(res) body, _ = json.Marshal(res)
return body return body

View File

@@ -9,7 +9,7 @@ import (
"github.com/xinliangnote/go-gin-api/pkg/httpclient" "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) { func TestDemoGet(t *testing.T) {
res, err := DemoGet("Tom", res, err := DemoGet("Tom",

View File

@@ -1,6 +1,8 @@
package auth package auth
import ( import (
"net/http"
"github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code" "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/core"
@@ -13,20 +15,32 @@ import (
func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Error) { func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Error) {
auth := ctx.GetHeader("Authorization") auth := ctx.GetHeader("Authorization")
if auth == "" { 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 return
} }
cfg := configs.Get().JWT cfg := configs.Get().JWT
claims, errParse := token.New(cfg.Secret).Parse(auth) claims, errParse := token.New(cfg.Secret).Parse(auth)
if errParse != nil { if errParse != nil {
err = code.ErrAuthorization.WithErr(errParse) err = errno.NewError(
http.StatusUnauthorized,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errParse)
return return
} }
userId = claims.UserID userId = claims.UserID
if userId <= 0 { 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 return
} }
userName = claims.UserName userName = claims.UserName

View File

@@ -90,8 +90,8 @@ type Context interface {
setLogger(logger *zap.Logger) setLogger(logger *zap.Logger)
// Payload 正确返回 // Payload 正确返回
Payload(payload errno.Error) Payload(payload interface{})
getPayload() errno.Error getPayload() interface{}
// GraphPayload GraphQL返回值 与 api 返回结构不同 // GraphPayload GraphQL返回值 与 api 返回结构不同
GraphPayload(payload interface{}) GraphPayload(payload interface{})
@@ -225,14 +225,14 @@ func (c *context) setLogger(logger *zap.Logger) {
c.ctx.Set(_LoggerName, 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 { if payload, ok := c.ctx.Get(_PayloadName); ok != false {
return payload.(errno.Error) return payload
} }
return nil return nil
} }
func (c *context) Payload(payload errno.Error) { func (c *context) Payload(payload interface{}) {
c.ctx.Set(_PayloadName, payload) c.ctx.Set(_PayloadName, payload)
} }

View File

@@ -336,7 +336,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
if err := recover(); err != nil { if err := recover(); err != nil {
stackInfo := string(debug.Stack()) stackInfo := string(debug.Stack())
logger.Error("got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo)) 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 { if notify := opt.panicNotify; notify != nil {
notify(context, err, stackInfo) notify(context, err, stackInfo)
@@ -348,7 +352,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
} }
var ( var (
response errno.Error response interface{}
businessCode int businessCode int
businessCodeMsg string businessCodeMsg string
abortErr error abortErr error
@@ -364,23 +368,27 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
if err := context.abortError(); err != nil { // customer err if err := context.abortError(); err != nil { // customer err
multierr.AppendInto(&abortErr, err.GetErr()) multierr.AppendInto(&abortErr, err.GetErr())
response = err response = err
businessCode = err.GetBusinessCode()
businessCodeMsg = err.GetMsg()
reply := &code.Failure{
Code: businessCode,
Message: businessCodeMsg,
}
ctx.JSON(err.GetHttpCode(), reply)
} }
} else { } else {
response = context.getPayload() response = context.getPayload()
if response != nil {
ctx.JSON(http.StatusOK, response)
}
} }
if response != nil { if response != nil {
if x := context.Trace(); x != nil { if x := context.Trace(); x != nil {
context.SetHeader(trace.Header, x.ID()) context.SetHeader(trace.Header, x.ID())
response.WithID(x.ID())
traceId = x.ID() traceId = x.ID()
} else {
response.WithID("")
} }
businessCode = response.GetBusinessCode()
businessCodeMsg = response.GetMsg()
ctx.JSON(response.GetHttpCode(), response)
} }
graphResponse = context.getGraphPayload() graphResponse = context.getGraphPayload()
@@ -464,7 +472,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
defer releaseContext(context) defer releaseContext(context)
if !limiter.Allow() { if !limiter.Allow() {
context.AbortWithError(code.ErrManyRequest) context.AbortWithError(errno.NewError(
http.StatusTooManyRequests,
code.TooManyRequests,
code.Text(code.TooManyRequests)),
)
return return
} }
@@ -489,7 +501,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
Host: ctx.Host(), Host: ctx.Host(),
Status: "ok", Status: "ok",
} }
ctx.Payload(code.OK.WithData(resp)) ctx.Payload(resp)
}) })
} }

View File

@@ -3,12 +3,14 @@ package grpc
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"runtime/debug" "runtime/debug"
"time" "time"
"github.com/xinliangnote/go-gin-api/internal/api/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/notify" "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/p"
"github.com/xinliangnote/go-gin-api/pkg/time_parse" "github.com/xinliangnote/go-gin-api/pkg/time_parse"
"github.com/xinliangnote/go-gin-api/pkg/trace" "github.com/xinliangnote/go-gin-api/pkg/trace"
@@ -53,7 +55,11 @@ func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string,
if err := recover(); err != nil { if err := recover(); err != nil {
stackInfo := string(debug.Stack()) stackInfo := string(debug.Stack())
coreContext.Logger().Error("UnaryInterceptor got double panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo)) coreContext.Logger().Error("UnaryInterceptor got double panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
coreContext.AbortWithError(code.ErrServer) coreContext.AbortWithError(errno.NewError(
http.StatusInternalServerError,
code.ServerError,
code.Text(code.ServerError)),
)
notify.OnPanicNotify(coreContext, err, stackInfo) notify.OnPanicNotify(coreContext, err, stackInfo)
} }
}() }()
@@ -62,7 +68,11 @@ func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string,
if err := recover(); err != nil { if err := recover(); err != nil {
stackInfo := string(debug.Stack()) stackInfo := string(debug.Stack())
coreContext.Logger().Error("UnaryInterceptor got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo)) coreContext.Logger().Error("UnaryInterceptor got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
coreContext.AbortWithError(code.ErrServer) coreContext.AbortWithError(errno.NewError(
http.StatusInternalServerError,
code.ServerError,
code.Text(code.ServerError)),
)
notify.OnPanicNotify(coreContext, err, stackInfo) notify.OnPanicNotify(coreContext, err, stackInfo)
} }

14
main.go
View File

@@ -117,12 +117,12 @@ func main() {
}, },
// 关闭 gRPC client // 关闭 gRPC client
func() { //func() {
if err := gRPCRepo.Conn().Close(); err != nil { // if err := gRPCRepo.Conn().Close(); err != nil {
loggers.Error("gRPC client close err", zap.Error(err)) // loggers.Error("gRPC client close err", zap.Error(err))
} else { // } else {
loggers.Info("gRPC client close success") // loggers.Info("gRPC client close success")
} // }
}, //},
) )
} }

View File

@@ -11,10 +11,6 @@ var _ Error = (*err)(nil)
type Error interface { type Error interface {
// i 为了避免被其他包实现 // i 为了避免被其他包实现
i() i()
// WithData 设置成功时返回的数据
WithData(data interface{}) Error
// WithID 设置当前请求的唯一ID
WithID(id string) Error
// WithErr 设置错误信息 // WithErr 设置错误信息
WithErr(err error) Error WithErr(err error) Error
// GetBusinessCode 获取 Business Code // GetBusinessCode 获取 Business Code
@@ -30,36 +26,22 @@ type Error interface {
} }
type err struct { type err struct {
HttpCode int `json:"-"` // HTTP Code HttpCode int // HTTP Code
BusinessCode int `json:"code"` // Business Code BusinessCode int // Business Code
Msg string `json:"msg"` // 描述信息 Message string // 描述信息
Data interface{} `json:"data"` // 接口数据 Err error // 错误信息
Err error `json:"-"` // 错误信息
ID string `json:"id,omitempty"` // 当前请求的唯一ID便于问题定位忽略也可以
} }
func NewError(httpCode, businessCode int, msg string) Error { func NewError(httpCode, businessCode int, msg string) Error {
return &err{ return &err{
HttpCode: httpCode, HttpCode: httpCode,
BusinessCode: businessCode, BusinessCode: businessCode,
Msg: msg, Message: msg,
Data: nil,
Err: nil,
} }
} }
func (e *err) i() {} 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 { func (e *err) WithErr(err error) Error {
e.Err = errors.WithStack(err) e.Err = errors.WithStack(err)
return e return e
@@ -74,7 +56,7 @@ func (e *err) GetBusinessCode() int {
} }
func (e *err) GetMsg() string { func (e *err) GetMsg() string {
return e.Msg return e.Message
} }
func (e *err) GetErr() error { func (e *err) GetErr() error {
@@ -84,17 +66,13 @@ func (e *err) GetErr() error {
// ToString 返回 JSON 格式的错误详情 // ToString 返回 JSON 格式的错误详情
func (e *err) ToString() string { func (e *err) ToString() string {
err := &struct { err := &struct {
HttpCode int `json:"http_code"` HttpCode int `json:"http_code"`
BusinessCode int `json:"business_code"` BusinessCode int `json:"business_code"`
Msg string `json:"msg"` Message string `json:"message"`
Data interface{} `json:"data"`
ID string `json:"id,omitempty"`
}{ }{
HttpCode: e.HttpCode, HttpCode: e.HttpCode,
BusinessCode: e.BusinessCode, BusinessCode: e.BusinessCode,
Msg: e.Msg, Message: e.Message,
Data: e.Data,
ID: e.ID,
} }
raw, _ := json.Marshal(err) raw, _ := json.Marshal(err)

46
pkg/httpclient/error.go Normal file
View File

@@ -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
}

View File

@@ -105,7 +105,11 @@ func doHTTP(ctx context.Context, method, url string, payload []byte, opt *option
}() }()
if resp.StatusCode != http.StatusOK { 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 return body, http.StatusOK, nil