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": "获取授权信息",
"responses": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$ref": "#/definitions/demo.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"
}
}
}
}
@@ -129,10 +147,22 @@ var doc = `{
],
"responses": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$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": {
"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": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$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": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
}
},
"definitions": {
"code.Failure": {
"type": "object",
"properties": {
"code": {
"description": "业务码",
"type": "integer"
},
"message": {
"description": "描述信息",
"type": "string"
}
}
},
"demo.authResponse": {
"type": "object",
"properties": {
@@ -302,7 +381,7 @@ var doc = `{
"type": "integer"
},
"mobile": {
"description": "手机号",
"description": "手机号(脱敏)",
"type": "string"
},
"nick_name": {

View File

@@ -26,10 +26,16 @@
"summary": "获取授权信息",
"responses": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$ref": "#/definitions/demo.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"
}
}
}
}
@@ -112,10 +130,22 @@
],
"responses": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$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": {
"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": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$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": {
"200": {
"description": "返回信息",
"description": "OK",
"schema": {
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
}
},
"definitions": {
"code.Failure": {
"type": "object",
"properties": {
"code": {
"description": "业务码",
"type": "integer"
},
"message": {
"description": "描述信息",
"type": "string"
}
}
},
"demo.authResponse": {
"type": "object",
"properties": {
@@ -285,7 +364,7 @@
"type": "integer"
},
"mobile": {
"description": "手机号",
"description": "手机号(脱敏)",
"type": "string"
},
"nick_name": {

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package demo
import (
"net/http"
"time"
"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/db"
"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/p"
"github.com/xinliangnote/go-gin-api/pkg/token"
@@ -49,19 +51,27 @@ func (d *Demo) Get() core.HandlerFunc {
return func(c core.Context) {
req := new(request)
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
}
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
}
c.Payload(code.OK.WithData(&response{
c.Payload(&response{
Name: "Tom",
Job: "Student",
}))
})
}
}
@@ -78,19 +88,27 @@ func (d *Demo) Post() core.HandlerFunc {
return func(c core.Context) {
req := new(request)
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
}
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
}
c.Payload(code.OK.WithData(&response{
c.Payload(&response{
Name: "Jack",
Job: "Teacher",
}))
})
}
}
@@ -110,14 +128,19 @@ type traceResponse []struct {
// @Tags Demo
// @Accept json
// @Produce json
// @Success 200 {object} authResponse "返回信息"
// @Success 200 {object} authResponse
// @Failure 400 {object} code.Failure
// @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))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err),
)
return
}
@@ -125,7 +148,7 @@ func (d *Demo) Auth() core.HandlerFunc {
res.Authorization = tokenString
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
// @Produce json
// @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]
func (d *Demo) Trace() core.HandlerFunc {
return func(c core.Context) {
@@ -151,12 +176,16 @@ func (d *Demo) Trace() core.HandlerFunc {
if err != nil {
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
}
// 调试信息
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",
@@ -169,13 +198,17 @@ func (d *Demo) Trace() core.HandlerFunc {
if err != nil {
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
}
// 调试信息
p.Println("res2.Data.Name",
res2.Data.Name,
p.Println("res2.Name",
res2.Name,
p.WithTrace(c.Trace()),
)
@@ -193,14 +226,14 @@ func (d *Demo) Trace() core.HandlerFunc {
data := &traceResponse{
{
Name: res1.Data.Name,
Job: res1.Data.Job,
Name: res1.Name,
Job: res1.Job,
},
{
Name: res2.Data.Name,
Job: res2.Data.Job,
Name: res2.Name,
Job: res2.Job,
},
}
c.Payload(code.OK.WithData(data))
c.Payload(data)
}
}

View File

@@ -2,6 +2,7 @@ package user_handler
import (
"errors"
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"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/db"
"github.com/xinliangnote/go-gin-api/pkg/ddm"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap"
)
@@ -57,30 +59,45 @@ func (u *userDemo) i() {}
// @Produce json
// @Param RequestInfo body user_model.CreateRequest 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]
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))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
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
}
id, err := u.userService.Create(c, req)
if err != nil {
c.AbortWithError(code.ErrUserCreate.WithErr(err))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserCreateError,
code.Text(code.UserCreateError)).WithErr(err),
)
return
}
res.Id = id
c.Payload(code.OK.WithData(res))
c.Payload(res)
}
}
@@ -92,25 +109,36 @@ func (u *userDemo) Create() core.HandlerFunc {
// @Produce json
// @Param RequestInfo body user_model.UpdateNickNameByIDRequest 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]
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))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
err := u.userService.UpdateNickNameByID(c, req.Id, req.NickName)
if err != nil {
c.AbortWithError(code.ErrUserUpdate.WithErr(err))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserUpdateError,
code.Text(code.UserUpdateError)).WithErr(err),
)
return
}
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
// @Param id path int true "用户ID"
// @Param Authorization header string true "签名"
// @Success 200 "返回信息"
// @Success 200
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @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))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
err := u.userService.Delete(c, req.Id)
if err != nil {
c.AbortWithError(code.ErrUserUpdate.WithErr(err))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserUpdateError,
code.Text(code.UserUpdateError)).WithErr(err),
)
return
}
c.Payload(code.OK.WithData(nil))
c.Payload("")
}
}
@@ -150,20 +188,30 @@ func (u *userDemo) Delete() core.HandlerFunc {
// @Produce json
// @Param username path 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]
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))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
user, err := u.userService.GetUserByUserName(c, req.UserName)
if err != nil {
c.AbortWithError(code.ErrUserSearch.WithErr(err))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserSearchError,
code.Text(code.UserSearchError)).WithErr(err),
)
return
}
@@ -171,6 +219,7 @@ func (u *userDemo) Detail() core.HandlerFunc {
res.UserName = user.UserName
res.NickName = user.NickName
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 (
"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
}

View File

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

View File

@@ -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",

View File

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

View File

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

View File

@@ -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,27 @@ 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()
reply := &code.Failure{
Code: businessCode,
Message: businessCodeMsg,
}
ctx.JSON(err.GetHttpCode(), reply)
}
} else {
response = context.getPayload()
if response != nil {
ctx.JSON(http.StatusOK, response)
}
}
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("")
}
businessCode = response.GetBusinessCode()
businessCodeMsg = response.GetMsg()
ctx.JSON(response.GetHttpCode(), response)
}
graphResponse = context.getGraphPayload()
@@ -464,7 +472,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 +501,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
Host: ctx.Host(),
Status: "ok",
}
ctx.Payload(code.OK.WithData(resp))
ctx.Payload(resp)
})
}

View File

@@ -3,12 +3,14 @@ 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"
@@ -53,7 +55,11 @@ func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string,
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(code.ErrServer)
coreContext.AbortWithError(errno.NewError(
http.StatusInternalServerError,
code.ServerError,
code.Text(code.ServerError)),
)
notify.OnPanicNotify(coreContext, err, stackInfo)
}
}()
@@ -62,7 +68,11 @@ func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string,
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(code.ErrServer)
coreContext.AbortWithError(errno.NewError(
http.StatusInternalServerError,
code.ServerError,
code.Text(code.ServerError)),
)
notify.OnPanicNotify(coreContext, err, stackInfo)
}

14
main.go
View File

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

View File

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

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 {
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