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.proto
File, 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/logic
Folders and servicesinternal/svc
Folder 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 onePb
Folder is used to store GRPC files andEtc
The 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.go
To 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.go
Is 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.go
Write 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 theFront
Directory 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