• go-zero微服务入门教程


    go-zero微服务入门教程

    本教程主要模拟实现用户注册用户信息查询两个接口。

    准备工作

    安装基础环境

    • 安装etcd, mysql,redis,建议采用docker安装。

    MySQL安装好之后,新建数据库dsms_admin,并新建表sys_user,建表语句见后文。

    安装插件

    这里采用GoLand开发工具,请自行搜索安装插件Goctl。

    在这里插入图片描述

    创建工程

    这里采用开发工具GoLand,File > New > Project

    在这里插入图片描述

    创建api目录、rpc目录、common目录。
    在这里插入图片描述
    在这里插入图片描述

    编写API Gateway代码

    编写api文件

    在api目录下创建新目录doc/sys。

    在这里插入图片描述

    在api/doc/sys下创建user.api。

    在这里插入图片描述

    user.api文件内容如下:

    syntax = "v1"
    
    info(
    	title: "用户相关"
    	desc: "用户相关"
    	author: "宋发元"
    )
    
    type (
    	UserInfoResp {
    		Code    int64        `json:"code"`
    		Message string       `json:"message"`
    		Data    UserInfoData `json:"data"`
    	}
    
    	UserInfoData {
    		Avatar      string             `json:"avatar"`
    		Name        string             `json:"name"`
    		MenuTree    []*ListMenuTree    `json:"menuTree"`
    		MenuTreeVue []*ListMenuTreeVue `json:"menuTreeVue"`
    		ResetPwd    bool               `json:"resetPwd,default=false"`
    	}
    
    	ListMenuTree {
    		Id       int64  `json:"id"`
    		Path     string `json:"path"`
    		Name     string `json:"name"`
    		ParentId int64  `json:"parentId"`
    		Icon     string `json:"icon"`
    	}
    
    	ListMenuTreeVue {
    		Id           int64        `json:"id"`
    		ParentId     int64        `json:"parentId"`
    		Title        string       `json:"title"`
    		Path         string       `json:"path"`
    		Name         string       `json:"name"`
    		Icon         string       `json:"icon"`
    		VueRedirent  string       `json:"vueRedirent"`
    		VueComponent string       `json:"vueComponent"`
    		Meta         MenuTreeMeta `json:"meta"`
    	}
    
    	MenuTreeMeta {
    		Title string `json:"title"`
    		Icon  string `json:"icon"`
    	}
    
    	AddUserReq {
    		Name     string `json:"name"`
    		NickName string `json:"nickName"`
    		Password string `json:"password,optional"`
    		Email    string `json:"email"`
    		RoleId   int64  `json:"roleId"`
    		Status   int64  `json:"status,default=1"`
    	}
    
    	AddUserResp {
    		Code    int64           `json:"code"`
    		Message string          `json:"message"`
    		Data    ReceiptUserData `json:"data"`
    	}
    
    	ReceiptUserData {
    		Id int64 `json:"id"`
    	}
    )
    
    @server (
    	group : sys/user
    	prefix : /sys/user
    )
    
    service admin-api{
    	@doc(
    		summary : "用户管理-获取当前用户信息"
    	)
    	@handler UserInfo
    	get /currentUser returns (UserInfoResp)
    
    	@doc(
    		summary : "用户管理-新增用户"
    	)
    	@handler UserAdd
    	post /add(AddUserReq)returns(AddUserResp)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    用goctl生成API Gateway代码

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    生成的文件结构如下:

    api
    ├── admin.go    //main入口定义
    ├── doc
    │   └── sys
    │       └── user.api    //api定义文件
    ├── etc
    │   └── admin-api.yaml    //配置文件
    └── internal
        ├── config
        │   └── config.go    //定义配置
        ├── handler
        │   ├── routes.go   //定义路由处理
        │   └── sys
        │       └── user
        │           ├── useraddhandler.go    //实现addhandler
        │           └── userinfohandler.go    //实现infohandler
        ├── logic
        │   └── sys
        │       └── user
        │           ├── useraddlogic.go    //实现addlogic
        │           └── userinfologic.go    //实现infologic
        ├── svc
        │   └── servicecontext.go    //定义ServiceContext
        └── types
            └── types.go    //定义请求、返回结构体
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    编写rpc服务

    编写sys.proto文件

    在rpc下创建新目录sys。
    在这里插入图片描述

    在rpc/sys目录下创建sys.proto文件。

    在这里插入图片描述

    sys.proto文件内容如下:

    syntax = "proto3";
    
    package sysclient;
    
    option go_package = "./sysclient";
    
    message InfoReq{
      int64 UserId = 1;
    }
    message InfoResp{
      string avatar =1;
      string name = 2;
      repeated MenuListTree menuListTree = 3;
      repeated string backgroundUrls=4;
      bool resetPwd=5;
    }
    
    message MenuListTree{
      int64 id=1;
      string name=2;
      string icon=3;
      int64 parentId=4;
      string path=5;
      string vuePath=6;
      string vueComponent=7;
      string vueIcon=8;
      string vueRedirect=9;
      string backgroundUrl=10;
    }
    
    message UserAddReq{
      string name=1;
      string nickName=2;
      string password=3;
      string email=4;
      int64 roleId=5;
      int64 status=6;
      string createBy=7;
    }
    
    message  UserAddResp{
      int64 id=1;
    }
    
    service Sys{
      rpc UserInfo(InfoReq)returns(InfoResp);
      rpc UserAdd(UserAddReq)returns(UserAddResp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    用goctl生成rpc代码

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    生成的文件结构如下:

    sys
    ├── etc
    │   └── sys.yaml                     //yaml配置文件
    ├── internal
    │   ├── config
    │   │   └── config.go                //yaml配置文件对应的结构体定义
    │   ├── logic                        //业务逻辑
    │   │   ├── useraddlogic.go
    │   │   └── userinfologic.go
    │   ├── server                       //rpc server
    │   │   └── sysserver.go
    │   └── svc                          //资源依赖
    │       └── servicecontext.go
    ├── sys                              //rpc client call entry
    │   └── sys.go
    ├── sys.go                           //main函数入口
    ├── sys.proto                        //proto源文件
    └── sysclient                        //pb.go
        ├── sys.pb.go
        └── sys_grpc.pb.go
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    修改API Gateway代码调用rpc服务

    admin-api.yaml

    • 修改配置文件admin-api.yaml,增加如下内容,这里的192.168.2.204为基础环境服务器IP。
    Timeout: 60000
    
    Mysql:
      Datasource: root:123456@tcp(192.168.2.204:3306)/dsms_admin?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
    
    CacheRedis:
      - Host: 192.168.2.204:6379
        Pass: qkgxChxNkCwK
        Type: node
    
    Redis:
      Address: 192.168.2.204:6379
      Pass: qkgxChxNkCwK
    
    SysRpc:
      Timeout: 30000
      Etcd:
        Hosts:
          - 192.168.2.204:2379
        Key: sysa.rpc
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    通过etcd自动去发现可用的rpc服务。

    config.go

    • 修改api/internal/config/config.go如下,增加rpc服务依赖。
    	SysRpc zrpc.RpcClientConf
    
    	CacheRedis cache.ClusterConf
    
    	Redis struct {
    		Address string
    		Pass    string
    	}
    
    	Mysql struct {
    		Datasource string
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    servicecontext.go

    • 修改api/internal/svc/servicecontext.go,如下:
    type ServiceContext struct {
    	Config config.Config
    
    	Sys   sys.Sys
    	Redis *redis.Redis
    }
    
    func NewServiceContext(c config.Config) *ServiceContext {
    	newRedis := redis.New(c.Redis.Address, redisConfig(c))
    	return &ServiceContext{
    		Config: c,
    		Sys:    sys.NewSys(zrpc.MustNewClient(c.SysRpc, zrpc.WithUnaryClientInterceptor(interceptor))),
    		Redis:  newRedis,
    	}
    }
    
    func redisConfig(c config.Config) redis.Option {
    	return func(r *redis.Redis) {
    		r.Type = redis.NodeType
    		r.Pass = c.Redis.Pass
    	}
    }
    
    func interceptor(ctx context.Context, method string, req any, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
    	md := metadata.New(map[string]string{"x": "xx"})
    	ctx = metadata.NewOutgoingContext(ctx, md)
    	// logx.Debug("调用rpc服务前")
    	err := invoker(ctx, method, req, reply, cc)
    	if err != nil {
    		return err
    	}
    	// logx.Debug("调用rpc服务后")
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    通过ServiceContext在不同业务逻辑之间传递依赖。

    useraddlogic.go

    • 修改api/internal/logic/useraddlogic.go里的UserAdd方法,如下:
    func (l *UserAddLogic) UserAdd(req *types.AddUserReq) (resp *types.AddUserResp, err error) {
    	res, err := l.svcCtx.Sys.UserAdd(l.ctx, &sysclient.UserAddReq{
    		Name:     req.Name,
    		NickName: req.NickName,
    		Password: req.Password,
    		Email:    req.Email,
    		RoleId:   req.RoleId,
    		Status:   req.Status,
    		//CreateBy: l.ctx.Value(cache.JwtFieldUserName).(string),
    		CreateBy: "songfayuan",
    	})
    
    	if err != nil {
    		reqJson, _ := json.Marshal(req)
    		logx.WithContext(l.ctx).Errorf("添加用户信息失败,请求参数:%s,异常信息:%s", reqJson, err.Error())
    		return nil, rpcerror.New(err)
    	}
    
    	return &types.AddUserResp{
    		Code:    200,
    		Message: "添加用户成功",
    		Data:    types.ReceiptUserData{Id: res.Id},
    	}, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    userinfologic.go

    • 修改api/internal/logic/userinfologic.go里的UserInfo方法,如下:
    
    func (l *UserInfoLogic) UserInfo() (*types.UserInfoResp, error) {
    	//这里的key和生成jwt token时传入的key一致
    	//userId, _ := l.ctx.Value(cache.JwtFieldUserId).(json.Number).Int64()
    
    	var userId int64 = 1
    	resp, err := l.svcCtx.Sys.UserInfo(l.ctx, &sysclient.InfoReq{
    		UserId: userId,
    	})
    
    	if err != nil {
    		logx.WithContext(l.ctx).Errorf("根据userId:%s, 查询用户异常:%s", strconv.FormatInt(userId, 10), err.Error())
    		return nil, rpcerror.New(err)
    	}
    
    	var MenuTree []*types.ListMenuTree
    
    	//组装ant ui中的菜单
    	for _, item := range resp.MenuListTree {
    		MenuTree = append(MenuTree, &types.ListMenuTree{
    			Id:       item.Id,
    			Path:     item.Path,
    			Name:     item.Name,
    			ParentId: item.ParentId,
    			Icon:     item.Icon,
    		})
    	}
    
    	if MenuTree == nil {
    		MenuTree = make([]*types.ListMenuTree, 0)
    	}
    
    	//组装element ui中的菜单
    	var MenuTreeVue []*types.ListMenuTreeVue
    
    	for _, item := range resp.MenuListTree {
    		if len(strings.TrimSpace(item.VuePath)) != 0 {
    			MenuTreeVue = append(MenuTreeVue, &types.ListMenuTreeVue{
    				Id:           item.Id,
    				ParentId:     item.ParentId,
    				Title:        item.Name,
    				Path:         item.VuePath,
    				Name:         item.Name,
    				Icon:         item.VueIcon,
    				VueRedirent:  item.VueRedirect,
    				VueComponent: item.VueComponent,
    				Meta: types.MenuTreeMeta{
    					Title: item.Name,
    					Icon:  item.VueIcon,
    				},
    			})
    		}
    	}
    	if MenuTreeVue == nil {
    		MenuTreeVue = make([]*types.ListMenuTreeVue, 0)
    	}
    
    	err = l.svcCtx.Redis.Set(strconv.FormatInt(userId, 10), strings.Join(resp.BackgroundUrls, ","))
    	if err != nil {
    		logx.Errorf("设置用户:%s, 权限到Redis异常:%+v", resp.Name, err)
    	}
    
    	return &types.UserInfoResp{
    		Code:    200,
    		Message: "成功",
    		Data: types.UserInfoData{
    			Avatar:      resp.Avatar,
    			Name:        resp.Name,
    			MenuTree:    MenuTree,
    			MenuTreeVue: MenuTreeVue,
    			ResetPwd:    resp.ResetPwd,
    		},
    	}, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    定义数据库表结构,并生成CRUD代码

    • 在rpc目录下创建model/sysmodel目录,在rpc目录下创建doc/sql/sys目录。

    在这里插入图片描述

    创建sys_user.sql

    • 在rpc/doc/sql/sys目录下编写sql文件sys_user.sql,如下:
    -- goctl model mysql ddl -src doc/sql/sys/sys_user.sql -dir ./model/sysmodel
    
    -- ----------------------------
    -- Table structure for sys_user
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_user`;
    CREATE TABLE `sys_user`
    (
        `id`          bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
        `name`        varchar(128) NOT NULL DEFAULT '' COMMENT '账号',
        `nick_name`   varchar(128) NOT NULL DEFAULT '' COMMENT '名称',
        `avatar`      varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
        `password`    varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
        `salt`        varchar(40)  NOT NULL DEFAULT '' COMMENT '加密盐',
        `email`       varchar(128) NOT NULL DEFAULT '' COMMENT '邮箱',
        `mobile`      varchar(32)  NOT NULL DEFAULT '' COMMENT '手机号',
        `status`      tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态  -1:禁用   1:正常',
        `create_by`   varchar(128) NOT NULL DEFAULT '' COMMENT '创建人',
        `create_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
        `update_by`   varchar(128) NOT NULL DEFAULT '' COMMENT '更新人',
        `update_time` datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
        `del_flag`    tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除  1:已删除  0:正常',
        PRIMARY KEY (`id`),
        KEY           `name` (`name`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户管理';
    -- ----------------------------
    -- Records of sys_user
    -- ----------------------------
    INSERT INTO `sys_user` VALUES (1, 'admin', 'admin', '', '$2a$10$hDlSis2/3IPGNYQhFlFfK.Wmi7iH9/jr6wcN.5c.rh7fc/uUnCo4S', '', 'admin@dsms.com', '13612345678', 1, 'admin', '2018-08-14 11:11:11', '', '2023-01-04 10:17:30', 0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    生成CRUD代码

    方法一

    通过工具生成,这种方式生成带缓存的代码。(本文采用方法二生成)

    在这里插入图片描述

    选择代码位置。
    在这里插入图片描述

    生成的代码。

    在这里插入图片描述

    方法二(采纳)

    在rpc路径下执行如下命令,生成不带缓存的代码。

    goctl model mysql ddl -src doc/sql/sys/sys_user.sql -dir ./model/sysmodel
    
    • 1

    完善CRUD代码

    sysusermodel.go

    在model/sysmodel/sysusermodel.go文件中添加常用crud的代码,完整代码如下。

    package sysmodel
    
    import (
    	"context"
    	"errors"
    	"github.com/zeromicro/go-zero/core/stores/sqlx"
    	"time"
    )
    import sq "github.com/Masterminds/squirrel"
    
    var _ SysUserModel = (*customSysUserModel)(nil)
    
    type (
    	// SysUserModel is an interface to be customized, add more methods here,
    	// and implement the added methods in customSysUserModel.
    	SysUserModel interface {
    		sysUserModel
    		withSession(session sqlx.Session) SysUserModel
    
    		Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
    
    		UpdateBuilder() sq.UpdateBuilder
    		UpdateByQuery(ctx context.Context, updateBuilder sq.UpdateBuilder) error
    
    		RowBuilder() sq.SelectBuilder
    		FindOneByQuery(ctx context.Context, rowBuilder sq.SelectBuilder) (*SysUser, error)
    		FindRowsByQuery(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUser, error)
    
    		CountBuilder(field string) sq.SelectBuilder
    		FindCount(ctx context.Context, countBuilder sq.SelectBuilder) (int64, error)
    
    		FindAll(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUserList, error)
    
    		TableName() string
    	}
    
    	customSysUserModel struct {
    		*defaultSysUserModel
    	}
    
    	SysUserList struct {
    		Id         int64     `db:"id"`          // 编号
    		Name       string    `db:"name"`        // 账号
    		NickName   string    `db:"nick_name"`   // 名称
    		Avatar     string    `db:"avatar"`      // 头像
    		Password   string    `db:"password"`    // 密码
    		Salt       string    `db:"salt"`        // 加密盐
    		Email      string    `db:"email"`       // 邮箱
    		Mobile     string    `db:"mobile"`      // 手机号
    		Status     int64     `db:"status"`      // 状态  -1:禁用   1:正常
    		CreateBy   string    `db:"create_by"`   // 创建人
    		CreateTime time.Time `db:"create_time"` // 创建时间
    		UpdateBy   string    `db:"update_by"`   // 更新人
    		UpdateTime time.Time `db:"update_time"` // 更新时间
    		DelFlag    int64     `db:"del_flag"`    // 是否删除  1:已删除  0:正常
    		RoleId     int64     `db:"role_id"`
    		RoleName   string    `db:"role_name"`
    	}
    )
    
    func (m *customSysUserModel) UpdateByQuery(ctx context.Context, updateBuilder sq.UpdateBuilder) error {
    	query, values, err := updateBuilder.Where("del_flag = ?", 0).ToSql()
    	if err != nil {
    		return err
    	}
    	_, err = m.conn.ExecCtx(ctx, query, values...)
    	return err
    }
    
    func (m *customSysUserModel) UpdateBuilder() sq.UpdateBuilder {
    	return sq.Update(m.table)
    }
    
    func (m *customSysUserModel) Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error {
    	return m.conn.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
    		return fn(ctx, session)
    	})
    }
    
    func (m *customSysUserModel) TableName() string {
    	return m.table
    }
    
    func (m *customSysUserModel) FindAll(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUserList, error) {
    	if orderBy == "" {
    		rowBuilder = rowBuilder.OrderBy("id AEC")
    	} else {
    		rowBuilder = rowBuilder.OrderBy(orderBy)
    	}
    
    	query, values, err := rowBuilder.Where("del_flag = ?", 0).ToSql()
    	if err != nil {
    		return nil, err
    	}
    
    	var resp []*SysUserList
    	err = m.conn.QueryRowsCtx(ctx, &resp, query, values...)
    	switch err {
    	case nil:
    		return resp, nil
    	case sqlx.ErrNotFound:
    		return nil, errors.New("查询记录为空")
    	default:
    		return nil, err
    	}
    }
    
    func (m *customSysUserModel) FindCount(ctx context.Context, countBuilder sq.SelectBuilder) (int64, error) {
    	query, values, err := countBuilder.Where("del_flag = ?", 0).ToSql()
    	if err != nil {
    		return 0, err
    	}
    
    	var resp int64
    	err = m.conn.QueryRowCtx(ctx, &resp, query, values...)
    	switch err {
    	case nil:
    		return resp, nil
    	default:
    		return 0, err
    	}
    }
    
    func (m *customSysUserModel) CountBuilder(field string) sq.SelectBuilder {
    	return sq.Select("COUNT(" + field + ")").From(m.table)
    }
    
    func (m *customSysUserModel) FindRowsByQuery(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUser, error) {
    	if orderBy == "" {
    		rowBuilder = rowBuilder.OrderBy("id DESC")
    	} else {
    		rowBuilder = rowBuilder.OrderBy(orderBy)
    	}
    
    	query, values, err := rowBuilder.Where("del_flag = ?", 0).ToSql()
    	if err != nil {
    		return nil, err
    	}
    
    	var resp []*SysUser
    	err = m.conn.QueryRowCtx(ctx, &resp, query, values...)
    	switch err {
    	case nil:
    		return resp, nil
    	case sqlx.ErrNotFound:
    		return nil, errors.New("查询记录为空")
    	default:
    		return nil, err
    	}
    }
    
    func (m *customSysUserModel) FindOneByQuery(ctx context.Context, rowBuilder sq.SelectBuilder) (*SysUser, error) {
    	query, values, err := rowBuilder.Where("del_flag = ?", 0).Limit(1).ToSql()
    	if err != nil {
    		return nil, err
    	}
    
    	var resp SysUser
    	err = m.conn.QueryRowCtx(ctx, &resp, query, values...)
    	switch err {
    	case nil:
    		return &resp, nil
    	default:
    		return nil, err
    	}
    }
    
    func (m *customSysUserModel) RowBuilder() sq.SelectBuilder {
    	return sq.Select(sysUserRows).From(m.table)
    }
    
    // NewSysUserModel returns a model for the database table.
    func NewSysUserModel(conn sqlx.SqlConn) SysUserModel {
    	return &customSysUserModel{
    		defaultSysUserModel: newSysUserModel(conn),
    	}
    }
    
    func (m *customSysUserModel) withSession(session sqlx.Session) SysUserModel {
    	return NewSysUserModel(sqlx.NewSqlConnFromSession(session))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181

    修改rpc代码调用crud代码

    sys.yaml

    • 修改rpc/sys/etc/sys.yaml,如下内容:
    Name: sys.rpc
    ListenOn: 0.0.0.0:8080
    Timeout: 10000
    Etcd:
      Hosts:
      - 192.168.2.204:2379
      Key: sysa.rpc
    
    Mysql:
      Datasource: root:123456@tcp(192.168.2.204:3306)/dsms_admin?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
    
    CacheRedis:
      - Host: 192.168.2.204:6379
        Pass: qkgxChxNkCwK
        Type: node
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    config.go

    • 修改rpc/sys/internal/config/config.go,如下:
    type Config struct {
    	zrpc.RpcServerConf
    
    	Mysql struct {
    		Datasource string
    	}
    
    	CacheRedis cache.ClusterConf
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    servicecontext.go

    • 修改rpc/sys/internal/svc/servicecontext.go,如下:
    type ServiceContext struct {
    	Config config.Config
    	Cache  cache.Cache
    	Redis  *redis.Redis
    
    	UserModel sysmodel.SysUserModel
    }
    
    func NewServiceContext(c config.Config) *ServiceContext {
    	sqlConn := sqlx.NewMysql(c.Mysql.Datasource)
    
    	ca := cache.New(c.CacheRedis, syncx.NewSingleFlight(), cache.NewStat("dc"), errors.New("data not find"))
    	rConn := redis.New(c.CacheRedis[0].Host, func(r *redis.Redis) {
    		r.Type = c.CacheRedis[0].Type
    		r.Pass = c.CacheRedis[0].Pass
    	})
    
    	return &ServiceContext{
    		Config: c,
    		Cache:  ca,
    		Redis:  rConn,
    
    		UserModel: sysmodel.NewSysUserModel(sqlConn),
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    useraddlogic.go

    • 修改rpc/sys/internal/logic/useraddlogic.go,如下:
    func (l *UserAddLogic) UserAdd(in *sysclient.UserAddReq) (*sysclient.UserAddResp, error) {
    	if in.Name == "" {
    		return nil, errors.New("账号不能为空")
    	}
    	if in.NickName == "" {
    		return nil, errors.New("姓名不能为空")
    	}
    	if in.Email == "" {
    		return nil, errors.New("邮箱不能为空")
    	}
    
    	//校验账号是否已存在
    	selectBuilder := l.svcCtx.UserModel.CountBuilder("id").Where(sq.Eq{"name": in.Name})
    	count, _ := l.svcCtx.UserModel.FindCount(l.ctx, selectBuilder)
    	if count > 0 {
    		logx.WithContext(l.ctx).Errorf("账号已存在,添加失败,userName = %s", in.Name)
    		return nil, errors.New("账号已存在")
    	}
    
    	if in.Password == "" {
    		in.Password = "123456"
    	}
    	hashedPassword, err := utils.GenerateFromPassword(in.Password)
    	if err != nil {
    		return nil, errors.New("密码加密出错")
    	}
    
    	//插入数据
    	result, err := l.svcCtx.UserModel.Insert(l.ctx, &sysmodel.SysUser{
    		Name:       in.Name,
    		NickName:   in.NickName,
    		Avatar:     "",
    		Password:   hashedPassword,
    		Salt:       "",
    		Email:      in.Email,
    		Mobile:     "",
    		Status:     0,
    		CreateBy:   in.CreateBy,
    		UpdateTime: time.Time{},
    		DelFlag:    0,
    	})
    	if err != nil {
    		return nil, err
    	}
    	insertId, err := result.LastInsertId()
    	if err != nil {
    		return nil, err
    	}
    
    	return &sysclient.UserAddResp{Id: insertId}, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    userinfologic.go

    • 修改rpc/sys/internal/logic/userinfologic.go,如下:
    func (l *UserInfoLogic) UserInfo(in *sysclient.InfoReq) (*sysclient.InfoResp, error) {
    	rowBuilder := l.svcCtx.UserModel.RowBuilder().Where(sq.Eq{"id": in.UserId})
    	userInfo, err := l.svcCtx.UserModel.FindOneByQuery(l.ctx, rowBuilder)
    
    	switch err {
    	case nil:
    	case sqlx.ErrNotFound:
    		logx.WithContext(l.ctx).Infof("用户不存在userId:%s", in.UserId)
    		return nil, fmt.Errorf("用户不存在userId:%s", strconv.FormatInt(in.UserId, 10))
    	default:
    		return nil, err
    	}
    
    	//var list []*sys.MenuListTree
    	//var listUrls []string
    
    	return &sysclient.InfoResp{
    		Avatar:         "11111",
    		Name:           userInfo.Name,
    		MenuListTree:   nil,
    		BackgroundUrls: nil,
    		ResetPwd:       false,
    	}, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    common目录

    common目录下为通用工具,直接拷贝进去即可。

    bcrypt.go

    • common/utils/bcrypt.go
    package utils
    
    import (
    	"bytes"
    	"crypto/md5"
    	"crypto/rand"
    	"crypto/rsa"
    	"crypto/x509"
    	"encoding/base64"
    	"encoding/hex"
    	"encoding/pem"
    	"fmt"
    	"log"
    
    	"github.com/tjfoc/gmsm/sm2"
    	x509g "github.com/tjfoc/gmsm/x509"
    	"golang.org/x/crypto/bcrypt"
    )
    
    func GenerateFromPassword(pwd string) (hashedPassword string, err error) {
    	password := []byte(pwd)
    	// Hashing the password with the default cost of 10
    	hashedPasswordBytes, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
    	hashedPassword = string(hashedPasswordBytes)
    	return
    }
    
    func CompareHashAndPassword(hashedPwd, plainPwd string) bool {
    	byteHash := []byte(hashedPwd)
    	err := bcrypt.CompareHashAndPassword(byteHash, []byte(plainPwd))
    	if err != nil {
    		return false
    	}
    	return true
    }
    
    // EncryptSm2 加密
    func EncryptSm2(privateKey, content string) string {
    	// 从十六进制导入公私钥
    	priv, err := x509g.ReadPrivateKeyFromHex(privateKey)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// 公钥加密部分
    	msg := []byte(content)
    	pub := &priv.PublicKey
    	cipherTxt, err := sm2.Encrypt(pub, msg, rand.Reader, sm2.C1C2C3) // sm2加密
    	if err != nil {
    		log.Fatal(err)
    	}
    	// fmt.Printf("加密文字:%s\n加密结果:%x\n", msg, cipherTxt)
    	encodeRes := fmt.Sprintf("%x", cipherTxt)
    	return encodeRes
    }
    
    // DecryptSm2 解密
    func DecryptSm2(privateKey, encryptData string) (string, error) {
    	// 从十六进制导入公私钥
    	priv, err := x509g.ReadPrivateKeyFromHex(privateKey)
    	if err != nil {
    		return "", err
    	}
    	// 私钥解密部分
    	hexData, err := hex.DecodeString(encryptData)
    	if err != nil {
    		return "", err
    	}
    	plainTxt, err := sm2.Decrypt(priv, hexData, sm2.C1C2C3) // sm2解密
    	if err != nil {
    		return "", err
    	}
    	// fmt.Printf("解密后的明文:%s\n私钥:%s \n 匹配一致", plainTxt, x509.WritePrivateKeyToHex(priv))
    	return string(plainTxt), nil
    }
    
    // EncryptAndDecrypt 加密/解密
    func EncryptAndDecrypt(privateKey, content string) {
    	// 从十六进制导入公私钥
    	priv, err := x509g.ReadPrivateKeyFromHex(privateKey)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// 公钥加密部分
    	msg := []byte(content)
    	pub := &priv.PublicKey
    	cipherTxt, err := sm2.Encrypt(pub, msg, rand.Reader, sm2.C1C2C3) // sm2加密
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Printf("加密文字:%s\n加密结果:%x\n", msg, cipherTxt)
    
    	// 私钥解密部分
    	plainTxt, err := sm2.Decrypt(priv, cipherTxt, sm2.C1C2C3) // sm2解密
    	if err != nil {
    		log.Fatal(err)
    	}
    	if !bytes.Equal(msg, plainTxt) {
    		log.Fatal("原文不匹配:", msg)
    	}
    	fmt.Printf("解密后的明文:%s\n私钥:%s \n 匹配一致", plainTxt, x509g.WritePrivateKeyToHex(priv))
    }
    
    // EncryptRSA 加密
    func EncryptRSA(content, publicKey string) (encryptStr string, err error) {
    	// 	var publicKey = `-----BEGIN PUBLIC KEY-----
    	// MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaIWAL13RU+bJN2hfmTSyOBotf
    	// 71pq8jc2ploPBHtN3smTUkYPbX2MIbO9TrRj3u67s/kGQZrz6tyQ68oexpukPN4/
    	// ypzp64UA5CQENSA41ZxTpYADbFQsiX9Spv6aDHhHzUlZtWRru9ptcFO3tDKq0ACT
    	// OAR1ZEHFwQGhzwaAowIDAQAB
    	// -----END PUBLIC KEY-----`
    	block, _ := pem.Decode([]byte(publicKey))
    	if block == nil {
    		return "", fmt.Errorf("failed to parse public key PEM")
    	}
    	publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
    	if err != nil {
    		return "", err
    	}
    	//  类型断言
    	rsaPublicKey := publicKeyInterface.(*rsa.PublicKey)
    	// 加密数据
    	encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(content))
    	if err != nil {
    		return "", fmt.Errorf("error encrypting data:%v", err)
    	}
    
    	return base64.StdEncoding.EncodeToString(encryptedData), err
    
    }
    
    // DecryptRSA 解密
    func DecryptRSA(encryptStr, privateKey string) (content string, err error) {
    	// 	var privateKey = `-----BEGIN PRIVATE KEY-----
    	// MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANohYAvXdFT5sk3a
    	// F+ZNLI4Gi1/vWmryNzamWg8Ee03eyZNSRg9tfYwhs71OtGPe7ruz+QZBmvPq3JDr
    	// yh7Gm6Q83j/KnOnrhQDkJAQ1IDjVnFOlgANsVCyJf1Km/poMeEfNSVm1ZGu72m1w
    	// U7e0MqrQAJM4BHVkQcXBAaHPBoCjAgMBAAECgYA/aJJN/uyvQwKlBPALn4WDJ73e
    	// PmrvScfpGAR39xqM8WVxcOoy0+Y6FRX1wupHWefWIqQSQIH1w+EoM5LGzX8yflSo
    	// lG3E0mgJzrMAOTs5FVkdN4tV6rKYq/vA9R67AD0a9nq7yOFeTqjGzWj4l7Vptvu4
    	// prK5GWV+i0+mpB2kKQJBAP0n1EMAHQSW38zOngfaqC6cvnjEbX4NnhSPRZVzlu3y
    	// ZkitiA/Y96yCCybCWD0TkF43Z1p0wIGuXSJ1Igku6bcCQQDclMziUz1RnQDl7RIN
    	// 449vbmG2mGLoXp5HTD9QP0NB46w64WwXIX7IZL2GubndTRFUFTTPLZZ80XbhFtp6
    	// 19B1AkEAnIgjJGaOisbrjQz5BCw8r821rKDwfu/WninUwcteOLUYb7n1Fq92vZEP
    	// aiDjRKizLL6fRnxIiCcTaXn52KnMUwJBAJaKOxYPRx8G7tD8rcCq2H5tL+TFNWNv
    	// B8iTAfbLZiR2tFlu9S0IIBW1ox9qa63b5gKjgmoOq9C9x8swpKUH2u0CQAKDHqwh
    	// aH6lVtV8cw55Ob8Dsh3PgFUazuM1+e5PjmZku3/2jeQQJrecu/S6LooPdeUf+EtV
    	// OB/5HvFhGpEu2/E=
    	// -----END PRIVATE KEY-----`
    	block, _ := pem.Decode([]byte(privateKey))
    	if block == nil {
    		return "", fmt.Errorf("failed to parse private key PEM")
    	}
    	privateKeyData, err := x509.ParsePKCS8PrivateKey(block.Bytes)
    	if err != nil {
    		return "", err
    	}
    	privateKeyInterface := privateKeyData.(*rsa.PrivateKey)
    
    	// 解密数据
    
    	byt, err := base64.StdEncoding.DecodeString(encryptStr)
    	if err != nil {
    		return "", fmt.Errorf("base64 DecodeString err:%v", err)
    	}
    
    	decryptedData, err := rsa.DecryptPKCS1v15(rand.Reader, privateKeyInterface, byt)
    	if err != nil {
    		return "", fmt.Errorf("error decrypting data:%v", err)
    	}
    
    	return string(decryptedData), nil
    
    }
    
    func Md5(s []byte) string {
    	m := md5.New()
    	m.Write(s)
    
    	return hex.EncodeToString(m.Sum(nil))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182

    code.go

    • common/errors/code.go
    package errors
    
    const BaseCode = 50000
    
    const RpcCode = 51000
    
    const MustUpdatePwdCode = 50005
    
    const LoginExpired = 50001
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    base.go

    • common/errors/base.go
    package errors
    
    type CommonError interface {
    	Error() string
    	ErrorType() string
    
    	Data() *CommonErrorResp
    }
    
    type CommonErrorResp struct {
    	Code    int    `json:"code"`
    	Message string `json:"message"`
    	Type    string `json:"error"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    errorx.go

    • common/errors/errorx/errorx.go
    package errorx
    
    import "go-zero-test/common/errors"
    
    var _ errors.CommonError = (*ErrorX)(nil)
    
    type ErrorX struct {
    	Code    int    `json:"code"`
    	Message string `json:"message"`
    	Type    string `json:"error"`
    }
    
    func (e *ErrorX) Error() string {
    	return e.Message
    }
    
    func (e *ErrorX) ErrorType() string {
    	return e.Type
    }
    
    func (e *ErrorX) Data() *errors.CommonErrorResp {
    	return &errors.CommonErrorResp{
    		Code:    e.Code,
    		Message: e.Message,
    		Type:    e.Type,
    	}
    }
    
    func New(s string) error {
    	return &ErrorX{Code: errors.BaseCode, Message: s, Type: "base error"}
    }
    
    func NewCodeErr(code int, s string) error {
    	return &ErrorX{Code: code, Message: s, Type: "base error"}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    rpcerror.go

    • common/errors/rpcerror/rpcerror.go
    package rpcerror
    
    import "go-zero-test/common/errors"
    
    var _ errors.CommonError = (*RpcError)(nil)
    
    type RpcError struct {
    	Code    int    `json:"code"`
    	Message string `json:"message"`
    	Type    string `json:"error"`
    }
    
    func (e *RpcError) Error() string {
    	return e.Message
    }
    
    func (e *RpcError) ErrorType() string {
    	return e.Type
    }
    
    func (e *RpcError) Data() *errors.CommonErrorResp {
    	return &errors.CommonErrorResp{
    		Code:    e.Code,
    		Message: e.Message,
    		Type:    e.Type,
    	}
    }
    
    // New rpc返回错误
    func New(e error) error {
    	msg := e.Error()[len("rpc error: code = Unknown desc = "):]
    	return &RpcError{Code: errors.RpcCode, Message: msg, Type: "rpc error"}
    }
    
    // NewError 返回自定义错误,rpc返回错误
    func NewError(s string, err error) error {
    	msgType := err.Error()[len("rpc error: code = Unknown desc = "):]
    	return &RpcError{Code: errors.RpcCode, Message: s, Type: msgType}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    完整调用演示

    最后,在根目录go-zero-test执行下命令。

    go mod tidy
    
    • 1

    运行rpc服务

    在这里插入图片描述

    修改路径。

    在这里插入图片描述

    之后直接启动即可。

    在这里插入图片描述

    运行api

    在这里插入图片描述

    修改路径。

    在这里插入图片描述

    之后直接启动即可。

    在这里插入图片描述

    api调用

    命令请求:

    curl -i "localhost:8888/sys/user/currentUser"
    
    • 1

    返回结果:

    HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    Traceparent: 00-7cf8f53fe7009655963024f44767cd53-67d21fe606d82a15-00
    Date: Thu, 22 Feb 2024 06:27:28 GMT
    Content-Length: 120
    
    {"code":200,"message":"成功","data":{"avatar":"11111","name":"admin","menuTree":[],"menuTreeVue":[],"resetPwd":false}}%
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    或者postman调用也行。

    在这里插入图片描述

    后续研发

    后续新增服务、新增接口流程同编写rpc服务模块。

    源码

    上面的源码在这里…
    源码包

  • 相关阅读:
    BUUCTF pwn1_sctf_2016 1
    当语文课本上的古诗词遇上拓世AI,文生图绘就东方美学画卷
    远程连接服务器的方法(VScode、Xshell、Ubuntu)
    RNN/LSTM (二) 实践案例
    玩转 PI 系列-如何在 Rockchip Arm 开发板上安装 Docker Tailscale K3s Cilium?
    【kubernetes】使用virtual-kubelet扩展k8s
    【重学前端】003-JavaScript:正统的面向对象语言
    小程序广告变现的收益怎么样?
    幂级数和幂级数的和函数有什么关系?
    navicat 导出数据库的表结构
  • 原文地址:https://blog.csdn.net/u011019141/article/details/136233473