go-micro微服务实战-创建用户服务

go-micro微服务实战-创建用户服务

网上按照一些微服务实战项目学习,以此深入学习微服务,这里我选择go-micro

用户服务

用户服务提供用户注册、用户登录、修改密码等功能

新建服务

go-micro new service user

整理目录结构

增加model目录和repository目录(MVC),删掉proto里默认内容,创建config目录。

开发步骤:1.定义接口(proto) -> 2.生成接口代码 -> 3.编写model层代码 -> 4.编写repository数据操作代码 -> 5.实现接口 -> 6.修改main.go

定义用户服务接口

user.proto

syntax = "proto3";

package user;

option go_package = "./proto;user";

service UserService {
	rpc Register (RegisterRequest) returns (Response){}
	rpc Login (LoginRequest) returns (Response){}
	rpc UpdatePassword (UpdatePasswordRequest) returns (Response){}
}

message User {
	uint32 id = 1;
	string name = 2;
	string phone = 3;
	string password = 4;
}

message RegisterRequest{
	User user = 1;
}

message LoginRequest{
	string phone = 1;
	string password = 2;
}

message UpdatePasswordRequest{
	uint32 uid = 1;
	string oldPassword = 2;
	string newPassword = 3;
	string confirmPassword = 4;
}

message Response {
	string code = 1;
	string msg = 2;
}

生成接口代码

protoc --proto_path=. --micro_out=. --go_out=:. proto/user.proto

编写model.user

model/user.go

package model

var Models = []interface{}{
	&User{},
}

type Model struct {
	Id uint32 `gorm:"primaryKey;autoIncrement" json:"id" form:"id"`
}

type User struct {
	Model
	Name string `gorm:"size:32;unique;" json:"name" form:"name"`
	Phone string `gorm:"size:32;unique;" json:"phone" form:"phone"`
	Password string `gorm:"size:512" json:"password" form:"password"`
}

编写repository.user

repository/user.go

package repository

import (
	"gorm.io/gorm"
	"shopping/user/model"
)

type Repository interface {
	Find(id int32) (*model.User, error)
	Create(*model.User) error
	Update(*model.User, int64) (*model.User, error)
	FindByField(string, string, string) (*model.User, error)
}

type User struct {
	Db *gorm.DB
}

func (repo *User) Find(id uint32) (*model.User, error) {
	user :=  &model.User{}
	user.Id = id
	if err := repo.Db.First(user).Error; err != nil {
		return nil, err
	}
	return user, nil
}

func (repo *User) Create(user *model.User) error {
	if err := repo.Db.Create(user).Error; err != nil {
		return err
	}
	return nil
}

func (repo *User) Update(user *model.User) (*model.User, error) {
	if err := repo.Db.Model(user).Updates(&user).Error; err != nil {
		return nil, err
	}
	return user, nil
}

func (repo *User) FindByField(key string, value string, fields string) (*model.User, error) {
	if len(fields) == 0 {
		fields = "*"
	}
	user :=  &model.User{}
	if err := repo.Db.Select(fields).Where(key+" = ?", value).First(user).Error; err != nil {
		return nil, err
	}
	return user, nil
}

实现接口handler

handler/user.go

package handler
import (
	"context"
	"fmt"
	"go-micro.dev/v4/errors"
	log "go-micro.dev/v4/logger"
	"golang.org/x/crypto/bcrypt"
	"shopping/user/model"
	"shopping/user/repository"

	userpb "shopping/user/proto"
)

type User struct{
	Repo *repository.User
}

// Call is a single request handler called via client.Call or the generated client code
func (e *User) Register(ctx context.Context, req *userpb.RegisterRequest, rsp *userpb.Response) error {
	hashedPwd, err := bcrypt.GenerateFromPassword([]byte(req.User.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}

	user := &model.User{
		Name:req.User.Name,
		Phone:req.User.Phone,
		Password:string(hashedPwd),
	}

	if err := e.Repo.Create(user);err != nil{
		log.Log(4, "create error")
		return err
	}

	rsp.Code = "200"
	rsp.Msg = "注册成功"

	return nil
}

func (e *User)Login(ctx context.Context, req *userpb.LoginRequest, rsp *userpb.Response) error {
	user , err := e.Repo.FindByField("phone" , req.Phone , "id , password")
	if err != err{
		return err
	}

	if user == nil{
		return errors.Unauthorized("go.micro.srv.user.login", "该手机号不存在")
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil {
		return errors.Unauthorized("go.micro.srv.user.login", "密码错误")
	}

	rsp.Code = "200"
	rsp.Msg = "登录成功"

	return nil
}

func (e *User)UpdatePassword(ctx context.Context, req *userpb.UpdatePasswordRequest, rsp *userpb.Response) error {
	user,err := e.Repo.Find(req.Uid)

	if user == nil{
		return errors.Unauthorized("go.micro.srv.user.login", "该用户不存在")
	}

	if err != nil {
		return err
	}
	//验证老密码是否正常
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.OldPassword)); err != nil {
		return errors.Unauthorized("go.micro.srv.user.login", "旧密码认证失败")
	}

	//验证通过后,对新密码hash存下来
	hashedPwd, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	user.Password = string(hashedPwd)
	e.Repo.Update(user)

	rsp.Code = "200"
	rsp.Msg =user.Name+",您的密码更新成功"

	return nil
}

修改main.go

package main

import (
	"flag"
	"shopping/user/config"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"shopping/user/handler"
	"shopping/user/model"
	"github.com/mlogclub/simple/sqls"
	userpb "shopping/user/proto"
	"shopping/user/repository"

	"go-micro.dev/v4"
	log "go-micro.dev/v4/logger"

)

var (
	service = "user"
	version = "latest"
	configFile = flag.String("config", "\xxxx\config\config.yml", "配置文件路径")

)

func main()  {
	flag.Parse()

	// 初始化配置
	conf := config.Init(*configFile)

	// gorm配置
	gormConf := &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true, // 单数形式,User表名为user 复数形式 User的表名应该是users
			TablePrefix:   "t_", //表名前缀 User的表名应该是t_user
		},
	}

	// 连接数据库
	if err := sqls.Open(conf.DB.Url, gormConf, conf.DB.MaxIdleConns, conf.DB.MaxOpenConns, model.Models...); err != nil {
		log.Fatal(err)
	}

	// Create service
	srv := micro.NewService(
		micro.Name(service),
		micro.Version(version),
	)
	srv.Init()
	repo := &repository.User{sqls.DB()}

	// Register handler
	userpb.RegisterUserServiceHandler(srv.Server(), &handler.User{repo})
	// Run service
	if err := srv.Run(); err != nil {
		log.Fatal(err)
	}


}

补充下config

config/config.yml

# 数据库配置
DB:
  Url: user:password!@#@tcp(xx.xx.xx.xx:3306)/shopping?charset=utf8mb4&parseTime=True&loc=Local
  MaxIdleConns: 50
  MaxOpenConns: 200

config/config.go

package config

import (
	"fmt"
	"io/ioutil"
	"gopkg.in/yaml.v2"
	log "go-micro.dev/v4/logger"
)

var Instance *Config

type Config struct {
	// 数据库配置
	DB struct {
		Url          string `yaml:"Url"`
		MaxIdleConns int    `yaml:"MaxIdleConns"`
		MaxOpenConns int    `yaml:"MaxOpenConns"`
	} `yaml:"DB"`
}

func Init(filename string) *Config {
	Instance = &Config{}
	if yamlFile, err := ioutil.ReadFile(filename); err != nil {
		fmt.Println(err)
		log.Fatal(err)
	} else if err = yaml.Unmarshal(yamlFile, Instance); err != nil {
		fmt.Println(err)
		log.Fatal(err)
	}
	return Instance
}

运行验证

服务端

go run main.go

调用

> go-micro call user UserService.Register "{\"user\":{\"id\":1,\"name\": \"liaolin\",\"phone\":\"13218289117\",\"password\":\"123456\"}}"
{"code":"200","msg":"注册成功"}

> go-micro call user UserService.Login "{\"phone\":\"13218289117\",\"password\":\"123456\"}"
{"code":"200","msg":"登录成功"}

> go-micro call user UserService.UpdatePassword "{\"uid\":1,\"oldPassword\": \"123456\",\"newPassword\":\"654321\",\"confirmPassword\":\"654321\"}"
{"code":"200","msg":"liaolin,您的密码更新成功"}

> go-micro call user UserService.Login "{\"phone\":\"13218289117\",\"password\":\"123456\"}"
{"id":"go.micro.srv.user.login","code":401,"detail":"密码错误","status":"Unauthorized"}

> go-micro call user UserService.Login "{\"phone\":\"13218289117\",\"password\":\"654321\"}"
{"code":"200","msg":"登录成功"}
posted @ 2022-09-17 08:02  村口王铁匠  阅读(251)  评论(0)    收藏  举报