手把手开发Admin 系列二(统一格式篇)

164 阅读4分钟

文档:docs.arklnk.com

前端:github.com/arklnk/ark-…

后端:github.com/arklnk/ark-…

go-zero:go-zero.dev/cn

演示: arkadmin.si-yee.com

账号密码备注
demo123456演示账号

统一错误码

返回格式

{
  "code": 1000,
  "msg": "服务繁忙,请稍后重试"
}

封装

package errorx
​
type CodeError struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}
​
type CodeErrorResponse struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}
​
func NewCodeError(code int, msg string) error {
    return &CodeError{Code: code, Msg: msg}
}
​
func NewDefaultError(code int) error {
    return NewCodeError(code, MapErrMsg(code))
}
​
func NewHandlerError(code int, msg string) error {
    return NewCodeError(code, msg)
}
​
func (e *CodeError) Error() string {
    return e.Msg
}
​
func (e *CodeError) Data() *CodeErrorResponse {
    return &CodeErrorResponse{
        Code: e.Code,
        Msg:  e.Msg,
    }
}

自定义错误码

package errorx
​
var errorMsg map[int]string
​
const (
    ServerErrorCode              = 1000
    ParamErrorCode               = 1001
    CaptchaErrorCode             = 1002
    AccountErrorCode             = 1003
    PasswordErrorCode            = 1004
    NotPermMenuErrorCode         = 1005
    DeletePermMenuErrorCode      = 1006
    ParentPermMenuErrorCode      = 1007
    AddRoleErrorCode             = 1008
    DeleteRoleErrorCode          = 1009
    AddDeptErrorCode             = 1010
    DeleteDeptErrorCode          = 1011
    AddJobErrorCode              = 1012
    DeleteJobErrorCode           = 1013
    AddProfessionErrorCode       = 1014
    DeleteProfessionErrorCode    = 1015
    AddUserErrorCode             = 1016
    DeptHasUserErrorCode         = 1017
    RoleIsUsingErrorCode         = 1018
    ParentRoleErrorCode          = 1019
    ParentDeptErrorCode          = 1020
    AccountDisableErrorCode      = 1021
    SetParentIdErrorCode         = 1022
    SetParentTypeErrorCode       = 1023
    AddConfigErrorCode           = 1024
    AddDictionaryErrorCode       = 1025
    AuthErrorCode                = 1026
    DeleteDictionaryErrorCode    = 1027
    JobIsUsingErrorCode          = 1028
    ProfessionIsUsingErrorCode   = 1029
    ForbiddenErrorCode           = 1030
    UpdateRoleUniqueKeyErrorCode = 1031
    UpdateDeptUniqueKeyErrorCode = 1032
    AssigningRolesErrorCode      = 1033
    DeptIdErrorCode              = 1034
    ProfessionIdErrorCode        = 1035
    JobIdErrorCode               = 1036
    ParentRoleIdErrorCode        = 1037
    ParentDeptIdErrorCode        = 1038
    ParentPermMenuIdErrorCode    = 1039
    ParentDictionaryIdErrorCode  = 1040
    DictionaryIdErrorCode        = 1041
    PermMenuIdErrorCode          = 1042
    RoleIdErrorCode              = 1043
    UserIdErrorCode              = 1044
)
​
func init() {
    errorMsg = make(map[int]string)
    errorMsg[ServerErrorCode] = "服务繁忙,请稍后重试"
    errorMsg[CaptchaErrorCode] = "验证码错误"
    errorMsg[AccountErrorCode] = "账号错误"
    errorMsg[PasswordErrorCode] = "密码错误"
    errorMsg[NotPermMenuErrorCode] = "权限不足"
    errorMsg[DeletePermMenuErrorCode] = "该权限菜单存在子级权限菜单"
    errorMsg[ParentPermMenuErrorCode] = "父级菜单不能为自己"
    errorMsg[AddRoleErrorCode] = "角色已存在"
    errorMsg[DeleteRoleErrorCode] = "该角色存在子角色"
    errorMsg[AddDeptErrorCode] = "部门已存在"
    errorMsg[DeleteDeptErrorCode] = "该部门存在子部门"
    errorMsg[AddJobErrorCode] = "岗位已存在"
    errorMsg[DeleteJobErrorCode] = "该岗位正在使用中"
    errorMsg[AddProfessionErrorCode] = "职称已存在"
    errorMsg[DeleteProfessionErrorCode] = "该职称正在使用中"
    errorMsg[AddUserErrorCode] = "账号已存在"
    errorMsg[DeptHasUserErrorCode] = "该部门正在使用中"
    errorMsg[RoleIsUsingErrorCode] = "该角色正在使用中"
    errorMsg[ParentRoleErrorCode] = "父级角色不能为自己"
    errorMsg[ParentDeptErrorCode] = "父级部门不能为自己"
    errorMsg[AccountDisableErrorCode] = "账号已禁用"
    errorMsg[SetParentIdErrorCode] = "不能设置子级为自己的父级"
    errorMsg[SetParentTypeErrorCode] = "权限类型不能作为父级菜单"
    errorMsg[AddConfigErrorCode] = "配置已存在"
    errorMsg[AddDictionaryErrorCode] = "字典已存在"
    errorMsg[AuthErrorCode] = "授权已失效,请重新登录"
    errorMsg[DeleteDictionaryErrorCode] = "该字典集存在配置项"
    errorMsg[JobIsUsingErrorCode] = "该岗位正在使用中"
    errorMsg[ProfessionIsUsingErrorCode] = "该职称正在使用中"
    errorMsg[ForbiddenErrorCode] = "禁止操作"
    errorMsg[UpdateRoleUniqueKeyErrorCode] = "角色标识已存在"
    errorMsg[UpdateDeptUniqueKeyErrorCode] = "部门标识已存在"
    errorMsg[AssigningRolesErrorCode] = "角色不在可控范围"
    errorMsg[DeptIdErrorCode] = "部门不存在"
    errorMsg[ProfessionIdErrorCode] = "职称不存在"
    errorMsg[JobIdErrorCode] = "岗位不存在"
    errorMsg[ParentRoleIdErrorCode] = "父级角色不存在"
    errorMsg[ParentDeptIdErrorCode] = "父级部门不存在"
    errorMsg[ParentPermMenuIdErrorCode] = "父级菜单不存在"
    errorMsg[ParentDictionaryIdErrorCode] = "字典集不存在"
    errorMsg[DictionaryIdErrorCode] = "字典不存在"
    errorMsg[PermMenuIdErrorCode] = "权限菜单不存在"
    errorMsg[RoleIdErrorCode] = "角色不存在"
    errorMsg[UserIdErrorCode] = "用户不存在"
}
​
func MapErrMsg(errCode int) string {
    if msg, ok := errorMsg[errCode]; ok {
        return msg
    } else {
        return "服务繁忙,请稍后重试"
    }
}

开启自定义错误码

路径:app/core/cmd/api/core.go

package main
​
import (
    "flag"
    "fmt"
    "net/http""ark-admin-zero/app/core/cmd/api/internal/config"
    "ark-admin-zero/app/core/cmd/api/internal/handler"
    "ark-admin-zero/app/core/cmd/api/internal/svc"
    "ark-admin-zero/common/errorx""github.com/zeromicro/go-zero/core/conf"
    "github.com/zeromicro/go-zero/core/logx"
    "github.com/zeromicro/go-zero/rest"
    "github.com/zeromicro/go-zero/rest/httpx"
)
​
var configFile = flag.String("f", "etc/core-api.yaml", "the config file")
​
func main() {
    flag.Parse()
​
    var c config.Config
    conf.MustLoad(*configFile, &c)
​
    server := rest.MustNewServer(c.RestConf)
    defer server.Stop()
​
    ctx := svc.NewServiceContext(c)
    handler.RegisterHandlers(server, ctx)
​
    // 自定义错误
    httpx.SetErrorHandler(func(err error) (int, interface{}) {
        switch e := err.(type) {
        case *errorx.CodeError:
            return http.StatusOK, e.Data()
        default:
            return http.StatusInternalServerError, nil
        }
    })
​
    if c.Mode == "dev" {
        logx.DisableStat()
    }
​
    fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
    server.Start()
}

统一返回值

返回格式

{
  "code": 200,
  "msg": "success",
  "data": {
    ....
  }
}

封装

package response
​
import (
    "net/http""github.com/zeromicro/go-zero/rest/httpx"
)
​
type Body struct {
    Code int         `json:"code"`
    Msg  string      `json:"msg"`
    Data interface{} `json:"data,omitempty"`
}
​
func Response(w http.ResponseWriter, resp interface{}, err error) {
    var body Body
    if err != nil {
        body.Code = 0
        body.Msg = err.Error()
    } else {
        body.Code = 200
        body.Msg = "success"
        body.Data = resp
    }
    httpx.OkJson(w, body)
}

使用统一返回值

以登录接口为例,在登录接口的LoginHandler中使用response.Response(w, resp, err)作为统一的返回格式

路径:app/core/cmd/api/internal/handler/user/loginhandler.go

package user
​
import (
    "errors"
    "net/http"
    "reflect""ark-admin-zero/app/core/cmd/api/internal/logic/user"
    "ark-admin-zero/app/core/cmd/api/internal/svc"
    "ark-admin-zero/app/core/cmd/api/internal/types"
    "ark-admin-zero/common/errorx"
    "ark-admin-zero/common/response""github.com/go-playground/locales/zh"
    ut "github.com/go-playground/universal-translator"
    "github.com/go-playground/validator/v10"
    translations "github.com/go-playground/validator/v10/translations/zh"
    "github.com/zeromicro/go-zero/rest/httpx"
)
​
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req types.LoginReq
        if err := httpx.Parse(r, &req); err != nil {
            httpx.Error(w, errorx.NewHandlerError(errorx.ParamErrorCode, err.Error()))
            return
        }
​
        validate := validator.New()
        validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
            name := fld.Tag.Get("label")
            return name
        })
​
        trans, _ := ut.New(zh.New()).GetTranslator("zh")
        validateErr := translations.RegisterDefaultTranslations(validate, trans)
        if validateErr = validate.StructCtx(r.Context(), req); validateErr != nil {
            for _, err := range validateErr.(validator.ValidationErrors) {
                httpx.Error(w, errorx.NewHandlerError(errorx.ParamErrorCode, errors.New(err.Translate(trans)).Error()))
                return
            }
        }
​
        l := user.NewLoginLogic(r.Context(), svcCtx)
        resp, err := l.Login(&req, r)
        if err != nil {
            httpx.Error(w, err)
            return
        }
​
        response.Response(w, resp, err)
    }
}