An overview,

First do a simple user system, mainly including registration, login, authentication functions.

The code has been placedGitHubOn, synchronous update, you can view.

Second, micro services

Writing microservices using the Go-Zero framework (see my Basics column if you don’t know)

1. Writeuser.protoFile, protobuf syntax self – learning
syntax = "proto3";

package user;

option go_package = "user";

message Request{
  int64 ping = 1;
}

message Reply{
  bool ok = 1;
  string code = 2;
}

message IdReq{
  int64 id = 1;
}

message UsernameReq{
  string username = 1;
}

message UserInfoReply{
  int64 id = 1;
  string username = 2;
  string password = 3;
  string nickname = 4;
  string phone = 5;
  string email = 6;
}

message UsersInfoReply{
  repeated UserInfoReply usersInfo = 1;
}

message RegisterReq{
  string username = 1;
  string password = 2;
  string nickname = 3;
  string phone = 4;
  string email = 5;
  string repeatPassword = 6;
}

message LoginReq{
  string username= 1;
  string email = 2;
  string phone = 3;
  string password = 4;
}

message UpdateUserReq{
  int64 id = 1;
  string username = 2;
  string password = 3;
  string nickname = 4;
  string phone = 5;
  string email = 6;
}

service User {
  rpc FindOneUserById(IdReq) returns(UserInfoReply);
  rpc FindOneUserByUsername(UsernameReq) returns(UserInfoReply);
  rpc FindAllUser(Request) returns(UsersInfoReply);

  rpc Register(RegisterReq) returns(Reply);
  rpc Login(LoginReq) returns(UserInfoReply);

  rpc UpdateUser(UpdateUserReq) returns(Reply);
}
Copy the code
2. Run the goctl command to generate the framework file
book-store@ubuntu:~/BookStoreProject/Grpc/User$ goctl rpc proto -src user.proto -dir .
Copy the code
Book - store @ ubuntu: ~ / BookStoreProject Grpc/User $tree. ├ ─ ─ etc │ └ ─ ─ the User. The yaml ├ ─ ─goThe mod ├ ─ ─go.├ ── ├─ config │ ├─ ├─.├ ─ ├─ ├─go│ ├─ Logic │ ├─ FindallUserLogic.go│ │ ├ ─ ─ findoneuserbyidlogic.go│ │ ├ ─ ─ findoneuserbyusernamelogic.go│ │ ├ ─ ─ loginlogic.go│ │ ├ ─ ─ registerlogic.go│ │ └ ─ ─ updateuserlogic.go│ ├─ ├─ ├─ ├─ ├─go│ ├ ─ garbage ─ garbagego├ ─ ─ model// Generated by goctl from the database, as described in 2│ ├ ─ ─ userinfoextramodel.go│ ├ ─ ─ userinfomodel.go│ └ ─ ─ vars.go├── ├─ ├.pb.go├─ userbase │ ├─go├ ─ ─ userclient.go├ ─ ─ the user.go└ ─ ─ user. Proto9 directories, 20 files
Copy the code
2. Run goctl to generate the database model file
book-store@ubuntu:~/BookStoreProject/Grpc/User$ goctl model mysql datasource -url="Bookstore, bookstore @ TCP (172.20.3.12:3306)/bs - user" -table="user_info"  -dir="./model" -cache=true
Copy the code
  • Database table DDL
CREATE TABLE `user_info` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`) USING BTREE,
  UNIQUE KEY `nickname` (`nickname`) USING BTREE,
  UNIQUE KEY `phone` (`phone`) USING BTREE,
  UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Copy the code
3. Modify the configuration fileetc/user.yaml, according to their own environment to configure
Name: user.rpc
ListenOn: 0.0. 0. 0:8080
Mysql:
  DataSource: bookstore:bookstore@tcp(172.203.12.:3306)/bs-user? charset=utf8&parseTime=true
CacheRedis:
  - Host: 172.203.13.:30020
    Pass: '123456'
    Type: node
Copy the code
4. Perfect logicinternal/logicFolders and servicesinternal/svcFolder of files, complete can goGitHubIn the search.

loginlogic.go

func (l *LoginLogic) Login(in *user.LoginReq) (*user.UserInfoReply, error) {
	var err error
	var rep = new(model.UserInfo)
	ifin.Username ! ="" {
		rep, err = l.svcCtx.Model.FindOneByUsername(in.Username)
	} else ifin.Email ! ="" {
		rep, err = l.svcCtx.Model.FindOneByEmail(in.Email)
	} else ifin.Phone ! ="" {
		rep, err = l.svcCtx.Model.FindOneByPhone(in.Phone)
	} else {
		return &user.UserInfoReply{}, nil
	}
	iferr ! =nil {
		return nil, err
	}
	if rep.Password == in.Password {
		return &user.UserInfoReply{
			Id:       rep.Id,
			Username: rep.Username,
			Nickname: rep.Nickname,
			//Password: rep.Password,
			Email: rep.Email,
			Phone: rep.Phone,
		}, nil
	}
	return &user.UserInfoReply{}, nil
}
Copy the code

servicecontext.go

type ServiceContext struct {
	Config config.Config
	Model  model.UserInfoModel
}

func NewServiceContext(c config.Config) *ServiceContext {
	con := sqlx.NewMysql(c.Mysql.DataSource)
	return &ServiceContext{
		Config: c,
		Model:  model.NewUserInfoModel(con, c.CacheRedis),
	}
}
Copy the code
5. Write a client for verificationuserclient.go
func main(a) {
	// Connect to the server
	conn, err := grpc.Dial("172.20.3.111:8080", grpc.WithInsecure())
	iferr ! =nil {
		fmt.Println("Failed to connect to server:", err)
		return
	}
	defer conn.Close()

	// Create a new client
	c := user.NewUserClient(conn)

	// Call the server function
	r, err := c.FindAllUser(context.Background(), &user.Request{})
	iferr ! =nil {
		fmt.Println("Calling server code failed:", err)
		return
	}

	fmt.Println("Call successful:", r)
}
Copy the code
6. Test run

WebApi gateway

The Gin framework is used for the design of the gateway, which is briefly described in the registration sectionGitHubTo view the complete file

1. File structure, similar to the basic column, a new onePbFolder is used to store GRPC files andEtcThe folder is used to store configuration files
book-store@ubuntu:~/BookStoreProject/WebApi$tree - d. ├── Apps │ ├── ├── Databases ├─ Etc ├─ Middlewares ├ ─ ─ Models ├ ─ ─ Pb │ └ ─ ─ the user ├ ─ ─ the Router ├ ─ ─ Services └ ─ ─ Utils12 directories
Copy the code
In 2.Services/grpc.goTo connect to the Grpc microserver
func GrpcInit(a) error {
	conn, err := grpc.Dial(C.UserRpc.Host, grpc.WithInsecure())
	iferr ! =nil {
		return err
	}
	UserGrpc = user.NewUserClient(conn)
	return nil
}
Copy the code
In 3.Apps/user/login_handler.goIs written to process registration request logic
func LoginHandler(c *gin.Context) {
	username := c.DefaultPostForm("username"."")
	password := c.DefaultPostForm("password"."")
	email := c.DefaultPostForm("email"."")
	phone := c.DefaultPostForm("phone"."")

	ctx := context.Background()
	rep, err := Services.UserGrpc.Login(ctx, &user.LoginReq{
		Username: username,
		Password: password,
		Email:    email,
		Phone:    phone})
	iferr ! =nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	} else {
		if rep.Username == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "No user found. Please register first."})
			return
		}
	}

	now := time.Now().Unix()

	jwtToken, err := getJwtToken(Services.C.Jwt.Secret, strconv.FormatInt(now, 10), strconv.FormatInt(Services.C.Jwt.Expire, 10), rep.Username)
	iferr ! =nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, struct {
		Name         string
		NickName     string
		AccessToken  string
		AccessExpire int64
		RefreshAfter int64
	}{
		Name:         rep.Username,
		NickName:     rep.Nickname,
		AccessToken:  jwtToken,
		AccessExpire: now + Services.C.Jwt.Expire,
		RefreshAfter: now + Services.C.Jwt.Expire/2})}Copy the code
In 4.Router/routers.goWrite routing in
func Init(a) *gin.Engine {
	r := gin.Default()
	//r. Use(Middlewares.Cors())
	r.Static("/Assets"."./Assets")
	r.StaticFile("/favicon.ico"."./Assets/favicon.ico")

	userGroup := r.Group("/user")
	{
		userGroup.POST("/login", user.LoginHandler)
		userGroup.POST("/register", user.RegisterHandler)
		userGroup.GET("/",Middlewares.JWTSuperuserMiddleware(), user.GetAllUsersHandler)
	}

	return r
}
Copy the code
5. Run, you can see the result on postman

Iv. Vue front-end

Simple implementation using Vue + Element-UI component, inGitHubIn theFrontDirectory to view the complete file

1. ShowLogin.vue
<template>
  <div id="login" style="">
    <el-row :gutter="20">
      <el-col :span="8" :offset="8">
        <div class="grid-content bg-purple">
          <el-form ref="form" :model="form" label-width="140px">
            <el-form-item label="Username or email address or phone number">
              <el-input le v-model="form.username"></el-input>
            </el-form-item>
            <el-form-item label="Password">
              <el-input v-model="form.password"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="onSubmit('form')"> login < / el - button > < el - button > cancel < / el - button > < / el - form - item > < / el - form > < / div > < / el - col > < / el - row > < / div > < / template > <script> exportdefault {
  name: 'Login',
  data () {
    return {
      form: {
        username: ' ',
        password: ' '
      }
    }
  },
  mounted () {
  },
  methods: {
    onSubmit (form) {
      this.$refs[form].validate((valid) => {
        if (valid) {
          let that = this
          let username = ' '
          let email = ' '
          let phone = ' '
          if(/ ^1\d{10}$/.test(that.form['username'])) {
            phone = that.form['username']}else if(/^(\w-*\.*)+@(\w-?) +(\.\w{2,})+$/.test(that.form['username'])) {
            email = that.form['username']}else {
            username = that.form['username']
          }

          let formData = new FormData()
          formData.append('username', username)
          formData.append('email', email)
          formData.append('phone', phone)
          formData.append('password', that.form['password'])

          that.$axios({
            method: 'post',
            url: '/api/user/login',
            data: formData
          }).then(function (response) {
            const res = response.data
            console.log(res)
            localStorage.setItem('Token', res['AccessToken'])
            localStorage.setItem('Username', res['Name'])
            localStorage.setItem('Nickname', res['NickName'])
            that.$message({message: 'Login successful', duration: 1000})
            setTimeout(function () {
              that.$router.push({path: '/'})
              window.location.reload()
            }, 1000)
          }).catch(function (error) {
            console.log(error)
            alert('User does not exist')})}else {
          console.log('error submit!! ')
          return false
        }
      })
    }
  }
}
</script>

<style scoped>

</style>
Copy the code
2. Display running results

Five, the trailer

In the next chapter, the project will be deployed on Kubernets. If you are interested, please pay attention to it. Thank you. Also welcome everybody to put forward opinion or suggestion, common progress.