三,用户管理微服务(library-user-service)

Posted Coding到灯火阑珊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三,用户管理微服务(library-user-service)相关的知识,希望对你有一定的参考价值。

微服务 library-user-service ,用户管理服务。提供用户管理的 Restful 接口,主要实现用户注册、根据用户ID或者 email 查询用户、查询用户所借书籍等功能。

完整代码:

https://github.com/Justin02180218/micro-kit

包结构说明

 

  • dao:数据访问层

  • dto:数据传输层

  • models:数据库表映射层

  • service:业务逻辑层

  • endpoint:go-kit 的概念,将服务提供的每一个方法封装成一个 endpoint。

  • transport:go-kit 的概念,协议传输层,支持 grpc,thrift,http 等协议,这里与gin web框架结合使用。

  • user.yaml:服务使用的配置文件

  • main.go:服务启动程序

代码实现

数据库表

首先在 library 数据库建立 user 表

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT '',
  `password` varchar(255) DEFAULT '',
  `email` varchar(255) DEFAULT '',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

models层

对应在 models 层创建 user.go,定义与表 user 对应的 User struct:

type User struct {
    ID        uint64    `gorm:"primary_key" json:"id" form:"id"`
    CreatedAt time.Time `form:"created_at" json:"created_at"`
    UpdatedAt time.Time `form:"updated_at" json:"updated_at"`
    Username  string
    Password  string
    Email     string
}

dao层

在 dao 层创建与数据库交互的 user_dao.go

定义 UserDao 接口及实现:

type UserDao interface {
    SelectByID(id uint64) (*models.User, error)
    SelectByEmail(email string) (*models.User, error)
    Save(user *models.User) error
}

type UserDaoImpl struct{}

func NewUserDaoImpl() UserDao {
    return &UserDaoImpl{}
}
  • SelectByID:根据用户ID查询用户信息

  • SelectByEail:根据用户email查询用户信息

  • Save:保存用户信息

UserDao 接口的函数实现:

func (u *UserDaoImpl) SelectByID(id uint64) (*models.User, error) {
    user := &models.User{}
    err := databases.DB.Where("id = ?", id).First(user).Error
    if err != nil {
        return nil, err
    }
    return user, nil
}

func (u *UserDaoImpl) SelectByEmail(email string) (*models.User, error) {
    user := &models.User{}
    err := databases.DB.Where("email = ?", email).First(user).Error
    if err != nil {
        return nil, err
    }
    return user, nil
}

func (u *UserDaoImpl) Save(user *models.User) error {
    return databases.DB.Create(user).Error
}

dto层

在 dto 层的 user_dto.go 中创建用于数据传输的struct UserInfo 和 RegisterUser:

type UserInfo struct {
    ID       uint64 `json:"id"`
    Username string `json:"username"`
    Email    string `json:"email"`
}

type RegisterUser struct {
    Username string
    Password string
    Email    string
}

service层

在 service 层创建 user_service.go

定义 UserService 接口及实现:

type UserService interface {
    Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error)
    FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error)
    FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error)
}

type UserServiceImpl struct {
    userDao dao.UserDao
}

func NewUserServiceImpl(userDao dao.UserDao) UserService {
    return &UserServiceImpl{
        userDao: userDao,
    }
}

UserService 接口的函数实现:

func (u *UserServiceImpl) Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error) {
    user, err := u.userDao.SelectByEmail(vo.Email)
    if user != nil {
        log.Println("User is already exist!")
        return &dto.UserInfo{}, ErrUserExisted
    }
    if err == gorm.ErrRecordNotFound || err == nil {
        newUser := &models.User{
            Username: vo.Username,
            Password: vo.Password,
            Email:    vo.Email,
        }
        err = u.userDao.Save(newUser)
        if err != nil {
            return nil, ErrRegistering
        }
        return &dto.UserInfo{
            ID:       newUser.ID,
            Username: newUser.Username,
            Email:    newUser.Email,
        }, nil
    }
    return nil, err
}

func (u *UserServiceImpl) FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error) {
    user, err := u.userDao.SelectByID(id)
    if err != nil {
        return nil, ErrNotFound
    }
    return &dto.UserInfo{
        ID:       user.ID,
        Username: user.Username,
        Email:    user.Email,
    }, nil
}

func (u *UserServiceImpl) FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error) {
    user, err := u.userDao.SelectByEmail(email)
    if err != nil {
        return nil, ErrNotFound
    }
    return &dto.UserInfo{
        ID:       user.ID,
        Username: user.Username,
        Email:    user.Email,
    }, nil
}

 

endpoint层

在 endpoint 层创建 user_endpoint.go,

定义 UserEndpoints struct,每一个请求对应一个endpoint。

type UserEndpoints struct {
    RegisterEndpoint    endpoint.Endpoint
    FindByIDEndpoint    endpoint.Endpoint
    FindByEmailEndpoint endpoint.Endpoint
}

 

创建各endpoint

func MakeRegisterEndpoint(svc service.UserService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (response interface{}, err error) {
        req := request.(*dto.RegisterUser)
        user, err := svc.Register(ctx, req)
        if err != nil {
            return nil, err
        }
        return user, nil
    }
}

func MakeFindByIDEndpoint(svc service.UserService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (response interface{}, err error) {
        id, _ := strconv.ParseUint(request.(string), 10, 64)
        user, err := svc.FindByID(ctx, id)
        if err != nil {
            return nil, err
        }
        return user, nil
    }
}

func MakeFindByEmailEndpoint(svc service.UserService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (response interface{}, err error) {
        email := request.(string)
        user, err := svc.FindByEmail(ctx, email)
        if err != nil {
            return nil, err
        }
        return user, nil
    }
}

transport层

请求转发我们采用 gin web 框架与 go-kit transport 相结合的方式,因此在 http_util.go 中定 义 Router ,返回 gin.Engine

func NewRouter(mode string) *gin.Engine {
    gin.SetMode(mode)
    r := gin.New()
    r.Use(gin.Recovery())
    return r
}

在 user_transport.go 中定义 NewHttpHandler 返回 gin.Engine,gin 的请求处理 handler 由 go-kit 的http.NewServer 处理。

func NewHttpHandler(ctx context.Context, endpoints *endpoint.UserEndpoints) *gin.Engine {
    r := utils.NewRouter(ctx.Value("ginMod").(string))

    e := r.Group("/api/v1")
    {
        e.POST("register", func(c *gin.Context) {
            kithttp.NewServer(
                endpoints.RegisterEndpoint,
                decodeRegisterRequest,
                utils.EncodeJsonResponse,
            ).ServeHTTP(c.Writer, c.Request)
        })

        e.GET("findByID", func(c *gin.Context) {
            kithttp.NewServer(
                endpoints.FindByIDEndpoint,
                decodeFindByIDRequest,
                utils.EncodeJsonResponse,
            ).ServeHTTP(c.Writer, c.Request)
        })

        e.GET("findByEmail", func(c *gin.Context) {
            kithttp.NewServer(
                endpoints.FindByEmailEndpoint,
                decodeFindByEmailRequest,
                utils.EncodeJsonResponse,
            ).ServeHTTP(c.Writer, c.Request)
        })
    }

    return r
}

 

启动服务

补全 main.go

var confFile = flag.String("f", "user.yaml", "user config file")

func main() {
    flag.Parse()

    err := configs.Init(*confFile)
    if err != nil {
        panic(err)
    }

    err = databases.Initmysql(configs.Conf.MySQLConfig)
    if err != nil {
        fmt.Println("load mysql failed")
    }

    ctx := context.Background()

    userDao := dao.NewUserDaoImpl()
    userService := service.NewUserServiceImpl(userDao)
    userEndpoints := &endpoint.UserEndpoints{
        RegisterEndpoint:    endpoint.MakeRegisterEndpoint(userService),
        FindByIDEndpoint:    endpoint.MakeFindByIDEndpoint(userService),
        FindByEmailEndpoint: endpoint.MakeFindByEmailEndpoint(userService),
    }

    ctx = context.WithValue(ctx, "ginMod", configs.Conf.ServerConfig.Mode)
    r := transport.NewHttpHandler(ctx, userEndpoints)

    errChan := make(chan error)
    go func() {
        errChan <- r.Run(fmt.Sprintf(":%s", strconv.Itoa(configs.Conf.ServerConfig.Port)))
    }()

    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
        errChan <- fmt.Errorf("%s", <-c)
    }()
    fmt.Println(<-errChan)
}

启动

进入 library-user-service 目录,执行 go run main.go 如图: 

服务成功启动,监听10086端口

接口测试

使用postman进行接口测试,在这里进行了 register 的接口测试,结果如图:

测试成功,数据库成功插入一条用户记录

 

下一篇文章,我们开始编写书籍管理微服务:library-book-service

完整代码:

https://github.com/Justin02180218/micro-kit


更多【分布式专辑】【架构实战专辑】系列文章,请关注公众号

以上是关于三,用户管理微服务(library-user-service)的主要内容,如果未能解决你的问题,请参考以下文章

基于微信小程序的校友录系统

三、微服务注册到Nacos

服务门户:Spring Cloud Gateway 如何把好微服务的大门

Spring Cloud微服务升级总结

面试官灵魂三问:什么是SOA?什么是微服务?SOA和微服务有什么区别?

每个微服务是不是应该管理自己的用户权限和用户角色?