手把手开发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

账号密码备注
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)
	}
}
下载方式:https://pan.quark.cn/s/a4b39357ea24 布线问题(分支限界算法)是计算机科学和电子工程领域中一个广为人知的议题,它主要探讨如何在印刷电路板上定位两个节点间最短的连接路径。 在这一议题中,电路板被构建为一个包含 n×m 个方格的矩阵,每个方格能够被界定为可通行或不可通行,其核心任务是定位从初始点到最终点的最短路径。 分支限界算法是处理布线问题的一种常用策略。 该算法与回溯法有相似之处,但存在差异,分支限界法仅需获取满足约束条件的一个最优路径,并按照广度优先或最小成本优先的原则来探索解空间树。 树 T 被构建为子集树或排列树,在探索过程中,每个节点仅被赋予一次成为扩展节点的机会,且会一次性生成其全部子节点。 针对布线问题的解决,队列式分支限界法可以被采用。 从起始位置 a 出发,将其设定为首个扩展节点,并将与该扩展节点相邻且可通行的方格加入至活跃节点队列中,将这些方格标记为 1,即从起始方格 a 到这些方格的距离为 1。 随后,从活跃节点队列中提取队首节点作为下一个扩展节点,并将与当前扩展节点相邻且未标记的方格标记为 2,随后将这些方格存入活跃节点队列。 这一过程将持续进行,直至算法探测到目标方格 b 或活跃节点队列为空。 在实现上述算法时,必须定义一个类 Position 来表征电路板上方格的位置,其成员 row 和 col 分别指示方格所在的行和列。 在方格位置上,布线能够沿右、下、左、上四个方向展开。 这四个方向的移动分别被记为 0、1、2、3。 下述表格中,offset[i].row 和 offset[i].col(i=0,1,2,3)分别提供了沿这四个方向前进 1 步相对于当前方格的相对位移。 在 Java 编程语言中,可以使用维数组...
源码来自:https://pan.quark.cn/s/a4b39357ea24 在VC++开发过程中,对话框(CDialog)作为典型的用户界面组件,承担着与用户进行信息交互的重要角色。 在VS2008SP1的开发环境中,常常需要满足为对话框配置个性化背景图片的需求,以此来优化用户的操作体验。 本案例将系统性地阐述在CDialog框架下如何达成这一功能。 首先,需要在资源设计工具中构建一个新的对话框资源。 具体操作是在Visual Studio平台中,进入资源视图(Resource View)界面,定位到对话框(Dialog)分支,通过右键选择“插入对话框”(Insert Dialog)选项。 完成对话框内控件的布局设计后,对对话框资源进行保存。 随后,将着手进行背景图片的载入工作。 通常有两种主要的技术路径:1. **运用位图控件(CStatic)**:在对话框界面中嵌入一个CStatic控件,并将其属性设置为BST_OWNERDRAW,从而具备自主控制绘制过程的权限。 在对话框的类定义中,需要重写OnPaint()函数,负责调用图片资源并借助CDC对象将其渲染到对话框表面。 此外,必须合理处理WM_CTLCOLORSTATIC消息,确保背景图片的展示不会受到其他界面元素的干扰。 ```cppvoid CMyDialog::OnPaint(){ CPaintDC dc(this); // 生成设备上下文对象 CBitmap bitmap; bitmap.LoadBitmap(IDC_BITMAP_BACKGROUND); // 获取背景图片资源 CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = m...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值