Go-kratos 框架商城微服務實戰之使用者服務 (二)

Aliliin發表於2022-02-08

Go-kratos 框架商城微服務實戰之使用者服務 (二)

這篇主要編寫服務介面。寫的不清晰的地方可看原始碼,歡迎大佬指教。

  1. 新增 user.proto 服務介面

注:這裡的目錄有做修改,完整專案目錄為 shop/service/user/api/user/v1/user.proto , 根目錄還是指代 user 目錄

  • 編輯 user/api/user/v1/user.proto 檔案
.
.
.

service User{
  rpc CreateUser(CreateUserInfo) returns (UserInfoResponse){}; // 建立使用者
  rpc GetUserList(PageInfo) returns (UserListResponse){}; // 使用者列表
  rpc GetUserByMobile(MobileRequest) returns (UserInfoResponse){}; // 通過 mobile 查詢使用者
  rpc GetUserById(IdRequest) returns (UserInfoResponse){}; // 通過 Id 查詢使用者
  rpc UpdateUser(UpdateUserInfo) returns (google.protobuf.Empty){}; // 更新使用者
  rpc CheckPassword(PasswordCheckInfo) returns (CheckResponse){}; // 驗證使用者密碼
}

.
.
.

// 使用者列表
message UserListResponse{
  int32 total = 1;
  repeated UserInfoResponse data = 2;
}

// 分頁
message PageInfo{
  uint32 pn = 1;
  uint32 pSize = 2;
}

message MobileRequest{
  string mobile = 1;
}

message IdRequest{
  int64 id = 1;
}

message  UpdateUserInfo{
  int64 id = 1;
  string nickName = 2;
  string gender = 3;
  uint64 birthday = 4;
}

message PasswordCheckInfo{
  string password = 1;
  string encryptedPassword = 2;
}

message CheckResponse{
  bool success = 1;
}
  • 重新生成 proto ,根目錄執行命令 make api
  1. 編寫使用者列表介面
  • user/internal/data/user.go 檔案下新增兩個方法
// ListUser .
func (r *userRepo) ListUser(ctx context.Context, pageNum, pageSize int) ([]*biz.User, int, error) {
    var users []biz.User
    result := r.data.db.Find(&users)
    if result.Error != nil {
        return nil, 0, result.Error
    }
    total := int(result.RowsAffected)
    r.data.db.Scopes(paginate(pageNum, pageSize)).Find(&users)
    rv := make([]*biz.User, 0)
    for _, u := range users {
        rv = append(rv, &biz.User{
            ID:       u.ID,
            Mobile:   u.Mobile,
            Password: u.Password,
            NickName: u.NickName,
            Gender:   u.Gender,
            Role:     u.Role,
            Birthday: u.Birthday,
        })
    }
    return rv, total, nil
}

// paginate 分頁
func paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if page == 0 {
            page = 1
        }

        switch {
        case pageSize > 100:
            pageSize = 100
        case pageSize <= 0:
            pageSize = 10
        }

        offset := (page - 1) * pageSize
        return db.Offset(offset).Limit(pageSize)
    }
}
  • user/internal/biz/user.go 檔案修改
package biz

.
.
..
.
.

type UserRepo interface {
    CreateUser(context.Context, *User) (*User, error)
    ListUser(ctx context.Context, pageNum, pageSize int) ([]*User, int, error)
}

.
.
.

func (uc *UserUsecase) List(ctx context.Context, pageNum, pageSize int) ([]*User, int, error) {
    return uc.repo.ListUser(ctx, pageNum, pageSize)
}
  • user/internal/service/user.go 檔案新增程式碼

.
.
.

// GetUserList .
func (u *UserService) GetUserList(ctx context.Context, req *v1.PageInfo) (*v1.UserListResponse, error) {
    list, total, err := u.uc.List(ctx, int(req.Pn), int(req.PSize))
    if err != nil {
        return nil, err
    }
    rsp := &v1.UserListResponse{}
    rsp.Total = int32(total)

    for _, user := range list {
        userInfoRsp := UserResponse(user)
        rsp.Data = append(rsp.Data, &userInfoRsp)
    }

    return rsp, nil
}

func UserResponse(user *biz.User) v1.UserInfoResponse {
    userInfoRsp := v1.UserInfoResponse{
        Id:       user.ID,
        Mobile:   user.Mobile,
        Password: user.Password,
        NickName: user.NickName,
        Gender:   user.Gender,
        Role:     int32(user.Role),
    }
    if user.Birthday != nil {
        userInfoRsp.Birthday = uint64(user.Birthday.Unix())
    }
    return userInfoRsp
}
  1. 編寫手機號查詢使用者介面
  • user/internal/data/user.go 檔案下新增
.
.
.

// UserByMobile .
func (r *userRepo) UserByMobile(ctx context.Context, mobile string) (*biz.User, error) {
    var user biz.User
    result := r.data.db.Where(&biz.User{Mobile: mobile}).First(&user)
    if result.Error != nil {
        return nil, result.Error
    }

    if result.RowsAffected == 0 {
        return nil, status.Errorf(codes.NotFound, "使用者不存在")
    }

    return &user, nil
}
  • user/internal/biz/user.go 檔案修改
package biz
.
.
.

type UserRepo interface {
    CreateUser(context.Context, *User) (*User, error)
    ListUser(ctx context.Context, pageNum, pageSize int) ([]*User, int, error)
    UserByMobile(ctx context.Context, mobile string) (*User, error)
}

.
.
.

func (uc *UserUsecase) UserByMobile(ctx context.Context, mobile string) (*User, error) {
    return uc.repo.UserByMobile(ctx, mobile)
}
  • user/internal/service/user.go 檔案新增程式碼

    .
    .
    .
    
    // GetUserByMobile .
    func (u *UserService) GetUserByMobile(ctx context.Context, req *v1.MobileRequest) (*v1.UserInfoResponse, error) {
        user, err := u.uc.UserByMobile(ctx, req.Mobile)
        if err != nil {
            return nil, err
        }
        rsp := UserResponse(user)
        return &rsp, nil
    }
  1. 編寫ID查詢使用者介面

和手機號查詢差不多這裡就不重複寫了

  • user/internal/data/user.go 檔案下新增

  • user/internal/biz/user.go 檔案修改

  • user/internal/service/user.go 檔案新增程式碼

  1. 編寫使用者更新介面
  • user/internal/data/user.go 檔案下新增
.
.
.

// UpdateUser .
func (r *userRepo) UpdateUser(ctx context.Context, user *biz.User) (bool, error) {
    var userInfo biz.User
    result := r.data.db.Where(&biz.User{ID: user.ID}).First(&userInfo)
    if result.RowsAffected == 0 {
        return false, status.Errorf(codes.NotFound, "使用者不存在")
    }

    userInfo.NickName = user.NickName
    userInfo.Birthday = user.Birthday
    userInfo.Gender = user.Gender

    res := r.data.db.Save(&userInfo)
    if res.Error != nil {
        return false, status.Errorf(codes.Internal, res.Error.Error())
    }

    return true, nil
}
  • user/internal/biz/user.go 檔案修改
package biz
.
.
.

type UserRepo interface {
    CreateUser(context.Context, *User) (*User, error)
    ListUser(ctx context.Context, pageNum, pageSize int) ([]*User, int, error)
    UserByMobile(ctx context.Context, mobile string) (*User, error)
    UpdateUser(context.Context, *User) (bool, error)
}

.
.
.

func (uc *UserUsecase) UpdateUser(ctx context.Context, user *User) (bool, error) {
    return uc.repo.UpdateUser(ctx, user)
}
  • user/internal/service/user.go 檔案新增程式碼
.
.
.
// UpdateUser .
func (u *UserService) UpdateUser(ctx context.Context, req *v1.UpdateUserInfo) (*emptypb.Empty, error) {
    birthDay := time.Unix(int64(req.Birthday), 0)
    user, err := u.uc.UpdateUser(ctx, &biz.User{
        ID:       req.Id,
        Gender:   req.Gender,
        Birthday: &birthDay,
        NickName: req.NickName,
    })

    if err != nil {
        return nil, err
    }

    if user == false {
        return nil, err
    }

    return &empty.Empty{}, nil
}
  1. 編寫驗證使用者密碼介面
  • user/internal/data/user.go 檔案下新增方法
// import "github.com/anaskhan96/go-password-encoder"
// import "crypto/sha512"
// CheckPassword .
func (r *userRepo) CheckPassword(ctx context.Context, pwd, encryptedPassword string) (bool, error) {
    options := &password.Options{SaltLen: 16, Iterations: 10000, KeyLen: 32, HashFunction: sha512.New}
    passwordInfo := strings.Split(encryptedPassword, "$")
    check := password.Verify(pwd, passwordInfo[2], passwordInfo[3], options)
    return check, nil
}
  • user/internal/biz/user.go 檔案修改
package biz
.
.
.

type UserRepo interface {
    CreateUser(context.Context, *User) (*User, error)
    ListUser(ctx context.Context, pageNum, pageSize int) ([]*User, int, error)
    UserByMobile(ctx context.Context, mobile string) (*User, error)
    UpdateUser(context.Context, *User) (bool, error)
    CheckPassword(ctx context.Context, password, encryptedPassword string) (bool, error)
}

.
.
.

func (uc *UserUsecase) CheckPassword(ctx context.Context, password, encryptedPassword string) (bool, error) {
    return uc.repo.CheckPassword(ctx, password, encryptedPassword)
}
  • user/internal/service/user.go 檔案新增程式碼

// CheckPassword .
func (u *UserService) CheckPassword(ctx context.Context, req *v1.PasswordCheckInfo) (*v1.CheckResponse, error) {
    check, err := u.uc.CheckPassword(ctx, req.Password, req.EncryptedPassword)
    if err != nil {
        return nil, err
    }
    return &v1.CheckResponse{Success: check}, nil
}
  1. 簡單編寫測試程式碼
  • 啟動專案 krtaos run

  • 修改測試檔案 user/test/user.go 放開註釋,進行本檔案進行測試

.
.
.

func main() {
    Init()
    TestGetUserList() // 獲取使用者列表
    //TestCreateUser() // 建立使用者
    //TestUpdateUser() // 更新使用者
    //TestGetUserByMobile() // 根據手機獲取使用者
    conn.Close()
}

.
.
.

func TestGetUserList() {
    r, err := userClient.GetUserList(context.Background(), &v1.PageInfo{
        Pn:    1,
        PSize: 6,
    })

    if err != nil {
        panic("grpc get err" + err.Error())
    }

    for _, user := range r.Data {
        fmt.Println(user.Mobile, user.NickName, user.Password)
        // 驗證密碼
        checkRsp, err := userClient.CheckPassword(context.Background(), &v1.PasswordCheckInfo{
            Password:          "admin",
            EncryptedPassword: user.Password,
        })
        if err != nil {
            panic(" get check user  psw err" + err.Error())
        }
        fmt.Println(checkRsp.Success)
    }
    fmt.Println(r.Total)
}

func TestGetUserByMobile() {
    rsp, err := userClient.GetUserByMobile(context.Background(), &v1.MobileRequest{
        Mobile: "13501167228",
    })
    if err != nil {
        panic("grpc get user by mobile err" + err.Error())
    }
    fmt.Println(rsp)
}

func TestUpdateUser() {
    rsp, err := userClient.UpdateUser(context.Background(), &v1.UpdateUserInfo{
        Id:       19,
        Gender:   "female",
        NickName: fmt.Sprintf("YWW%d", 233),
    })
    if err != nil {
        panic("grpc update user err" + err.Error())
    }
    fmt.Println(rsp)
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
高永立

相關文章