使用者服務
使用者服務,提供登入、注冊、修改密碼等功能。
建立服務
micro new shopping/user
整理結構
增加model目錄和repository目錄,删掉proto裡預設的example檔案夾,建立user檔案夾。
開發步驟:1.定義接口 -> 2.生成接口代碼 -> 3.編寫model層代碼 -> 4.編寫repository資料操作代碼 -> 5.實作接口 -> 6.修改main.go
因為這是第一個微服務,是以會盡可能地較長的描述建立微服務的過程
定義使用者服務接口
syntax = "proto3";
package go.micro.srv.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/user.proto
編寫model.user
package model
import "github.com/jinzhu/gorm"
type User struct {
gorm.Model
Name string
Phone string `gorm:"type:char(11);`
Password string
}
編寫repository.user
package repository
import (
"github.com/jinzhu/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 = uint(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.user
package handler
import (
"context"
"github.com/micro/go-log"
"github.com/micro/go-micro/errors"
"golang.org/x/crypto/bcrypt"
"shopping/user/model"
"shopping/user/repository"
user "shopping/user/proto/user"
)
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 *user.RegisterRequest, rsp *user.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("create error")
return err
}
rsp.Code = "200"
rsp.Msg = "注冊成功"
return nil
}
func (e *User)Login(ctx context.Context, req *user.LoginRequest, rsp *user.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 *user.UpdatePasswordRequest, rsp *user.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
因為需要在main.go裡建立資料庫連接配接,在根目錄下建立database.go
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
func CreateConnection() (*gorm.DB, error) {
host := "192.168.0.111"
user := "mytestroot"
dbName := "shopping"
password := "mytestroot"
return gorm.Open("mysql", fmt.Sprintf(
"%s:%[email protected](%s:3306)/%s?charset=utf8&parseTime=True&loc=Local",
user, password, host, dbName,
),
)
}
然後修改main.go
package main
import (
"github.com/micro/go-log"
"github.com/micro/go-micro"
"github.com/micro/go-grpc"
"shopping/user/handler"
"shopping/user/model"
"shopping/user/repository"
user "shopping/user/proto/user"
)
func main() {
db,err := CreateConnection()
defer db.Close()
db.AutoMigrate(&model.User{})
if err != nil {
log.Fatalf("connection error : %v \n" , err)
}
repo := &repository.User{db}
// New Service
service := grpc.NewService(
micro.Name("go.micro.srv.user"),
micro.Version("latest"),
)
// Initialise service
service.Init()
// Register Handler
user.RegisterUserServiceHandler(service.Server(), &handler.User{repo})
// Register Struct as Subscriber
//micro.RegisterSubscriber("go.micro.srv.user", service.Server(), new(subscriber.Example))
// Register Function as Subscriber
//micro.RegisterSubscriber("go.micro.srv.user", service.Server(), subscriber.Handler)
// Run service
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
啟動服務,驗證接口
go run main.go database.go
和
micro api --namespace=go.micro.srv
注冊接口
登入接口
修改密碼接口