手把手开发Admin 系列二(统一格式篇)
文档:https://docs.arklnk.com
前端:https://github.com/arklnk/ark-admin-vuenext
后端:https://github.com/arklnk/ark-admin-zero
go-zero:https://go-zero.dev/cn
演示: http://arkadmin.si-yee.com
账号 密码 备注 demo 123456 演示账号
统一错误码
返回格式
{
"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)
}
}