深入解析go-zero-looklook项目中的用户服务架构与实现

深入解析go-zero-looklook项目中的用户服务架构与实现

go-zero-looklook 🔥基于go-zero(go zero) 微服务全技术栈开发最佳实践项目。Develop best practice projects based on the full technology stack of go zero (go zero) microservices. go-zero-looklook 项目地址: https://gitcode.com/gh_mirrors/go/go-zero-looklook

用户服务架构概述

go-zero-looklook项目中的用户服务采用了清晰的分层架构设计,将业务逻辑合理分布在API层和RPC层。这种架构设计充分体现了微服务的思想,使得系统各组件职责分明,便于维护和扩展。

架构图解

用户服务的整体架构如下图所示:

用户服务架构图

架构主要分为两层:

  1. API层:负责处理HTTP请求,对外提供RESTful接口
  2. RPC层:实现核心业务逻辑,为API层提供服务

服务依赖关系

在go-zero-looklook项目中,usercenter-api(用户中心API服务)依赖于usercenter-rpc(用户中心RPC服务)。这种设计使得业务逻辑集中在RPC服务中,API层只需关注接口适配和请求转发。

用户注册功能实现详解

API层实现

1. 接口定义

在usercenter.api文件中定义用户注册接口:

@server(
 prefix: usercenter/v1
 group: user
)
service usercenter {
  @doc "register"
  @handler register
  post /user/register (RegisterReq) returns (RegisterResp)
  ...
}
2. 请求响应结构

在user.api文件中定义注册请求和响应结构:

type (
  RegisterReq {
    Mobile   string `json:"mobile"`
    Password string `json:"password"`
  }
  RegisterResp {
    AccessToken  string `json:"accessToken"`
    AccessExpire int64  `json:"accessExpire"`
    RefreshAfter int64  `json:"refreshAfter"`
  }
)
3. 代码生成与逻辑实现

使用goctl工具生成代码后,在register.go文件中实现注册逻辑:

func (l *RegisterLogic) Register(req *types.RegisterReq) (*types.RegisterResp, error) {
    // 调用RPC服务完成注册
    resp, err := l.svcCtx.UserRpc.Register(l.ctx, &usercenter.RegisterReq{
        Mobile:   req.Mobile,
        Password: req.Password,
    })
    if err != nil {
        return nil, err
    }
    
    // 返回注册结果
    return &types.RegisterResp{
        AccessToken:  resp.AccessToken,
        AccessExpire: resp.AccessExpire,
        RefreshAfter: resp.RefreshAfter,
    }, nil
}

RPC层实现

1. Protobuf定义

在usercenter.proto文件中定义RPC服务和方法:

message RegisterReq {
  string mobile = 1;
  string nickname = 2;
  string password = 3;
  string authKey = 4;
  string authType = 5;
}

message RegisterResp {
  string accessToken = 1;
  int64 accessExpire = 2;
  int64 refreshAfter = 3;
}

service usercenter {
  rpc register(RegisterReq) returns(RegisterResp);
  ...
}
2. 注册逻辑实现

注册逻辑主要处理以下业务:

  1. 检查联系方式是否已注册
  2. 创建用户记录
  3. 创建用户认证记录
  4. 生成访问令牌
func (l *RegisterLogic) Register(in *usercenter.RegisterReq) (*usercenter.RegisterResp, error) {
    // 1. 检查用户是否已存在
    user, err := l.svcCtx.UserModel.FindOneByMobile(l.ctx, in.Mobile)
    if err != nil && err != model.ErrNotFound {
        return nil, err
    }
    if user != nil {
        return nil, errors.New("用户已存在")
    }
    
    var userId int64
    // 2. 使用事务处理用户注册
    if err := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
        // 创建用户记录
        user := new(model.User)
        user.Mobile = in.Mobile
        user.Nickname = tool.Krand(8, tool.KC_RAND_KIND_ALL)
        if len(in.Password) > 0 {
            user.Password = tool.Md5ByString(in.Password)
        }
        
        // 插入用户记录
        insertResult, err := l.svcCtx.UserModel.Insert(ctx, session, user)
        if err != nil {
            return err
        }
        
        // 获取用户ID
        userId, err = insertResult.LastInsertId()
        if err != nil {
            return err
        }
        
        // 创建用户认证记录
        userAuth := new(model.UserAuth)
        userAuth.UserId = userId
        userAuth.AuthKey = in.AuthKey
        userAuth.AuthType = in.AuthType
        if _, err := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); err != nil {
            return err
        }
        
        return nil
    }); err != nil {
        return nil, err
    }
    
    // 3. 生成访问令牌
    tokenResp, err := NewGenerateTokenLogic(l.ctx, l.svcCtx).GenerateToken(&usercenter.GenerateTokenReq{
        UserId: userId,
    })
    if err != nil {
        return nil, err
    }
    
    return &usercenter.RegisterResp{
        AccessToken:  tokenResp.AccessToken,
        AccessExpire: tokenResp.AccessExpire,
        RefreshAfter: tokenResp.RefreshAfter,
    }, nil
}
3. 事务处理

注册过程中涉及用户表和用户认证表的操作,需要使用事务保证数据一致性。项目中通过在Model层暴露Trans方法,使得业务逻辑中可以方便地使用事务:

func (m *defaultUserModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
    return m.conn.TransactCtx(ctx, fn)
}

获取登录用户ID

在业务逻辑中,经常需要获取当前登录用户的ID。项目中提供了统一的方法从上下文中获取用户ID:

func GetUidFromCtx(ctx context.Context) int64 {
    var uid int64
    if jsonUid, ok := ctx.Value(CtxKeyJwtUserId).(json.Number); ok {
        if int64Uid, err := jsonUid.Int64(); err == nil {
            uid = int64Uid
        } else {
            logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err)
        }
    }
    return uid
}

使用时只需调用:

userId := ctxdata.GetUidFromCtx(l.ctx)

设计亮点

  1. 分层清晰:API层和RPC层职责分明,API层处理HTTP协议相关逻辑,RPC层专注业务实现
  2. 事务封装:将事务操作封装在Model层,业务逻辑中可方便使用
  3. 统一认证:提供统一的用户ID获取方式,简化业务开发
  4. 灵活扩展:支持多种注册方式(联系方式、小程序等),通过authType区分

通过这种架构设计,go-zero-looklook项目的用户服务实现了高内聚、低耦合,为其他业务模块提供了清晰、稳定的用户管理能力。

go-zero-looklook 🔥基于go-zero(go zero) 微服务全技术栈开发最佳实践项目。Develop best practice projects based on the full technology stack of go zero (go zero) microservices. go-zero-looklook 项目地址: https://gitcode.com/gh_mirrors/go/go-zero-looklook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

劳丽娓Fern

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值