乐趣区

关于go-zero:gozero-dockercompose-搭建课件服务五完善user服务

0、索引

go-zero docker-compose 搭建课件服务(一):编写服务 api 和 proto

go-zero docker-compose 搭建课件服务(二):编写 courseware rpc 服务

go-zero docker-compose 搭建课件服务(三):编写 courseware api 服务

go-zero docker-compose 搭建课件服务(四):生成 Dockerfile 并在 docker-compose 中启动

go-zero docker-compose 搭建课件服务(五):欠缺 user 服务

go-zero docker-compose 搭建课件服务(六):欠缺 jwt 鉴权和返回构造

go-zero docker-compose 搭建课件服务(七):prometheus+grafana 服务监控

0.1 源码地址

https://github.com/liuyuede123/go-zero-courseware

1、生成 model

到我的项目根目录下创立 model 目录,并新建 user.sql

mkdir user/rpc/model
touch user/rpc/model/user.sql
CREATE TABLE `user`
(
    `id`          bigint unsigned NOT NULL AUTO_INCREMENT,
    `login_name`  varchar(255) NOT NULL DEFAULT ''COMMENT' 登录名 ',
    `username`        varchar(255) NOT NULL DEFAULT ''COMMENT' 用户姓名 ',
    `sex`      tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '用户性别',
    `password`    varchar(255) NOT NULL DEFAULT ''COMMENT' 用户明码 ',
    `is_delete`   tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否删除 0- 未删除 1- 已删除',
    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `udx_login_name` (`login_name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;

到 model 目录下生成 model

cd user/rpc/model
goctl model mysql ddl -src="./*.sql" -dir="./" -c

2、生成 rpc 文件

到 user/rpc 目录下生成 rpc 文件

goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.

3、减少 mysql 配置

user 目录下初始化 module

go mod init
go mod tidy

user/rpc/etc/user.yaml 中减少数据源和缓存配置

Name: user.rpc
ListenOn: 127.0.0.1:8300
Etcd:
  Hosts:
  - etcd:2379
  Key: user.rpc

# mysql 数据源
Mysql:
  DataSource: root:liufutian@tcp(192.168.0.110:3306)/go_zero_courseware?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai

# redis 缓存
CacheRedis:
  - Host: 192.168.0.110:6379
    Pass:

批改 user/rpc/internal/config/config.go 中配置

package config

import (
    "github.com/zeromicro/go-zero/core/stores/cache"
    "github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
    zrpc.RpcServerConf

    Mysql struct {DataSource string}

    CacheRedis cache.CacheConf
}

批改 courseware/rpc/internal/svc/servicecontext.go 相干配置

package svc

import (
    "github.com/zeromicro/go-zero/core/stores/sqlx"
    "go-zero-courseware/user/rpc/internal/config"
    "go-zero-courseware/user/rpc/model"
)

type ServiceContext struct {
    Config config.Config

    UserModel model.UserModel
}

func NewServiceContext(c config.Config) *ServiceContext {conn := sqlx.NewMysql(c.Mysql.DataSource)
    return &ServiceContext{
        Config: c,
        UserModel: model.NewUserModel(conn, c.CacheRedis),
    }
}

4、增加用户逻辑

先走通,后续会优化用户逻辑

user/rpc/internal/logic/registerlogic.go 减少注册逻辑

package logic

import (
    "context"
    "go-zero-courseware/user/rpc/internal/svc"
    "go-zero-courseware/user/rpc/model"
    "go-zero-courseware/user/rpc/user"
    "google.golang.org/grpc/status"

    "github.com/zeromicro/go-zero/core/logx"
)

type RegisterLogic struct {
    ctx    context.Context
    svcCtx *svc.ServiceContext
    logx.Logger
}

func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
    return &RegisterLogic{
        ctx:    ctx,
        svcCtx: svcCtx,
        Logger: logx.WithContext(ctx),
    }
}

func (l *RegisterLogic) Register(in *user.RegisterRequest) (*user.RegisterResponse, error) {_, err := l.svcCtx.UserModel.FindOneByLoginName(l.ctx, in.LoginName)
    if err == nil {return nil, status.Error(5000, "登录名已存在")
    }

    if err != model.ErrNotFound {return nil, status.Error(500, err.Error())
    }
    newUser := model.User{
        LoginName: in.LoginName,
        Username:  in.Username,
        Sex:       in.Sex,
        Password:  in.Password,
    }
    _, err = l.svcCtx.UserModel.Insert(l.ctx, &newUser)
    if err != nil {return nil, status.Error(500, err.Error())
    }

    return &user.RegisterResponse{}, nil}

user/rpc/internal/logic/loginlogic.go 减少登录逻辑

package logic

import (
    "context"
    "go-zero-courseware/user/rpc/model"
    "google.golang.org/grpc/status"

    "go-zero-courseware/user/rpc/internal/svc"
    "go-zero-courseware/user/rpc/user"

    "github.com/zeromicro/go-zero/core/logx"
)

type LoginLogic struct {
    ctx    context.Context
    svcCtx *svc.ServiceContext
    logx.Logger
}

func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
    return &LoginLogic{
        ctx:    ctx,
        svcCtx: svcCtx,
        Logger: logx.WithContext(ctx),
    }
}

func (l *LoginLogic) Login(in *user.LoginRequest) (*user.LoginResponse, error) {userInfo, err := l.svcCtx.UserModel.FindOneByLoginName(l.ctx, in.LoginName)
    if err == model.ErrNotFound {return nil, status.Error(5000, "用户不存在")
    }
    if err != nil {return nil, status.Error(500, err.Error())
    }

    if in.Password != userInfo.Password {return nil, status.Error(5000, "明码谬误")
    }

    return &user.LoginResponse{
        Id:    userInfo.Id,
        Token: "a.b.c",
    }, nil
}

减少用户信息逻辑

package logic

import (
    "context"
    "go-zero-courseware/user/rpc/model"
    "google.golang.org/grpc/status"

    "go-zero-courseware/user/rpc/internal/svc"
    "go-zero-courseware/user/rpc/user"

    "github.com/zeromicro/go-zero/core/logx"
)

type UserInfoLogic struct {
    ctx    context.Context
    svcCtx *svc.ServiceContext
    logx.Logger
}

func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic {
    return &UserInfoLogic{
        ctx:    ctx,
        svcCtx: svcCtx,
        Logger: logx.WithContext(ctx),
    }
}

func (l *UserInfoLogic) UserInfo(in *user.UserInfoRequest) (*user.UserInfoResponse, error) {userInfo, err := l.svcCtx.UserModel.FindOne(l.ctx, in.Id)
    if err == model.ErrNotFound {return nil, status.Error(5000, "用户不存在")
    }
    if err != nil {return nil, status.Error(500, err.Error())
    }

    return &user.UserInfoResponse{
        Id:        userInfo.Id,
        Username:  userInfo.Username,
        LoginName: userInfo.LoginName,
        Sex:       userInfo.Sex,
    }, nil
}

5、欠缺 api 代码

到 user/api 目录下,生成 api 端代码

goctl api go -api user.api -dir . -style gozero

user/api/etc/user.yaml 配置

Name: user
Host: 0.0.0.0
Port: 8300

UserRpc:
  Etcd:
    Hosts:
      - etcd:2379
    Key: user.rpc

user/api/internal/config/config.go 配置

package config

import (
    "github.com/zeromicro/go-zero/rest"
    "github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
    rest.RestConf

    UserRpc zrpc.RpcClientConf
}

user/api/internal/svc/servicecontext.go 配置

package svc

import (
    "github.com/zeromicro/go-zero/zrpc"
    "go-zero-courseware/user/api/internal/config"
    "go-zero-courseware/user/rpc/userclient"
)

type ServiceContext struct {
    Config config.Config

    UserRpc userclient.User
}

func NewServiceContext(c config.Config) *ServiceContext {
    return &ServiceContext{
        Config:  c,
        UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
    }
}

user/api/internal/logic/userregisterlogic.go 减少注册逻辑

package logic

import (
    "context"
    "go-zero-courseware/user/rpc/userclient"
    "google.golang.org/grpc/status"

    "go-zero-courseware/user/api/internal/svc"
    "go-zero-courseware/user/api/internal/types"

    "github.com/zeromicro/go-zero/core/logx"
)

type UserRegisterLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewUserRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRegisterLogic {
    return &UserRegisterLogic{Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *UserRegisterLogic) UserRegister(req *types.RegisterRequest) (resp *types.RegisterResponse, err error) {
    _, err = l.svcCtx.UserRpc.Register(l.ctx, &userclient.RegisterRequest{
        LoginName: req.LoginName,
        Username:  req.Username,
        Password:  req.Password,
        Sex:       req.Sex,
    })
    if err != nil {return nil, status.Error(500, err.Error())
    }

    return &types.RegisterResponse{}, nil}

user/api/internal/logic/userloginlogic.go 减少登录逻辑

package logic

import (
    "context"
    "go-zero-courseware/user/rpc/userclient"

    "go-zero-courseware/user/api/internal/svc"
    "go-zero-courseware/user/api/internal/types"

    "github.com/zeromicro/go-zero/core/logx"
)

type UserLoginLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLoginLogic {
    return &UserLoginLogic{Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *UserLoginLogic) UserLogin(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
    login, err := l.svcCtx.UserRpc.Login(l.ctx, &userclient.LoginRequest{
        LoginName: req.LoginName,
        Password:  req.Password,
    })
    if err != nil {return nil, err}

    return &types.LoginResponse{
        Id:    login.Id,
        Token: login.Token,
    }, nil
}

user/api/internal/logic/userinfologic.go 减少用户信息逻辑

package logic

import (
    "context"
    "go-zero-courseware/user/rpc/userclient"
    "google.golang.org/grpc/status"

    "go-zero-courseware/user/api/internal/svc"
    "go-zero-courseware/user/api/internal/types"

    "github.com/zeromicro/go-zero/core/logx"
)

type UserInfoLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic {
    return &UserInfoLogic{Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *UserInfoLogic) UserInfo(req *types.UserInfoRequest) (resp *types.UserInfoResponse, err error) {
    info, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &userclient.UserInfoRequest{Id: req.Id,})
    if err != nil {return nil, status.Error(500, err.Error())
    }

    return &types.UserInfoResponse{
        Id:        info.Id,
        Username:  info.Username,
        LoginName: info.LoginName,
        Sex:       info.Sex,
    }, nil
}

6、docker-compose 减少配置

user/rpc 目录下生成 rpc 的 Dockerfile

goctl docker -go user.go

user/api 目录下生成 api 的 Dockerfile

goctl docker -go user.go

根目录下 docker-compose.yml 减少用户服务 api 和 rpc 配置

version: '3.5'
# 网络配置
networks:
  backend:
    driver: bridge

# 服务容器配置
services:
  etcd:                                  # 自定义容器名称
    build:
      context: etcd                    # 指定构建应用的 Dockerfile 文件
    environment:
      - TZ=Asia/Shanghai
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
    ports:                               # 设置端口映射
      - "2379:2379"
    networks:
      - backend
    restart: always

  etcd-manage:
    build:
      context: etcd-manage
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "7000:8080"                    # 设置容器 8080 端口映射指定宿主机端口,用于宿主机拜访可视化 web
    depends_on:                                       # 依赖容器
      - etcd                                          # 在 etcd 服务容器启动后启动
    networks:
      - backend
    restart: always

  courseware-rpc: # 自定义容器名称
    build:
      context: courseware                 # 指定构建应用的 Dockerfile 文件
      dockerfile: rpc/Dockerfile
    environment: # 设置环境变量
      - TZ=Asia/Shanghai
    privileged: true
    ports: # 设置端口映射
      - "9400:9400"  # 课件服务 rpc 端口
    stdin_open: true                     # 关上规范输出,能够承受内部输出
    tty: true
    networks:
      - backend
    restart: always                      # 指定容器退出后的重启策略为始终重启

  courseware-api: # 自定义容器名称
    build:
      context: courseware                  # 指定构建应用的 Dockerfile 文件
      dockerfile: api/Dockerfile
    environment: # 设置环境变量
      - TZ=Asia/Shanghai
    privileged: true
    ports: # 设置端口映射
      - "8400:8400"  # 课件服务 api 端口
    stdin_open: true                     # 关上规范输出,能够承受内部输出
    tty: true
    networks:
      - backend
    restart: always                      # 指定容器退出后的重启策略为始终重启

  user-rpc: # 自定义容器名称
    build:
      context: user                 # 指定构建应用的 Dockerfile 文件
      dockerfile: rpc/Dockerfile
    environment: # 设置环境变量
      - TZ=Asia/Shanghai
    privileged: true
    ports: # 设置端口映射
      - "9300:9300"  # 课件服务 rpc 端口
    stdin_open: true                     # 关上规范输出,能够承受内部输出
    tty: true
    networks:
      - backend
    restart: always                      # 指定容器退出后的重启策略为始终重启

  user-api: # 自定义容器名称
    build:
      context: user                  # 指定构建应用的 Dockerfile 文件
      dockerfile: api/Dockerfile
    environment: # 设置环境变量
      - TZ=Asia/Shanghai
    privileged: true
    ports: # 设置端口映射
      - "8300:8300"  # 课件服务 api 端口
    stdin_open: true                     # 关上规范输出,能够承受内部输出
    tty: true
    networks:
      - backend
    restart: always                      # 指定容器退出后的重启策略为始终重启 

6、运行 user 服务

user 目录下

go mod tidy

到我的项目根目录

docker-compose up -d

7、测试接口

http://localhost:8300/api/user/register
{
    "loginName": "liuyuede",
    "username": "liuyuede",
    "sex": 1,
    "password": "123"
}
http://localhost:8300/api/user/login
{
    "loginName": "liuyuede",
    "password": "123"
}
http://localhost:8300/api/user/userInfo
{"id": 2
退出移动版