阅读 121

租售同体的书屋项目——用户系统(一)

租售同体的书屋项目——用户系统(一)

一、概述

先做一个简单的用户系统,主要包括注册,登录,鉴权功能。

 

代码已经放在GitHub上,同步更新,可自行查看。

二、微服务

使用Go-zero框架进行微服务的编写(不知道的可以去看我的基础专栏里面讲过的)

1.编写user.proto文件,protobuf语法自行学习
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); } 复制代码
2.使用goctl生成框架文件
book-store@ubuntu:~/BookStoreProject/Grpc/User$ goctl rpc proto -src user.proto -dir . 复制代码
book-store@ubuntu:~/BookStoreProject/Grpc/User$ tree . ├── etc │   └── user.yaml ├── go.mod ├── go.sum ├── internal │   ├── config │   │   └── config.go │   ├── logic │   │   ├── findalluserlogic.go │   │   ├── findoneuserbyidlogic.go │   │   ├── findoneuserbyusernamelogic.go │   │   ├── loginlogic.go │   │   ├── registerlogic.go │   │   └── updateuserlogic.go │   ├── server │   │   └── userserver.go │   └── svc │       └── servicecontext.go ├── model //由goctl根据数据库生成,后面2.会描述 │   ├── userinfoextramodel.go  │   ├── userinfomodel.go │   └── vars.go ├── user │   └── user.pb.go ├── userclient │   └── user.go ├── userclient.go ├── user.go └── user.proto 9 directories, 20 files 复制代码
2.使用goctl生成数据库模型文件
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 复制代码
  • 数据库表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; 复制代码
3.修改配置文件etc/user.yaml,根据自己的环境去配置
Name: user.rpc ListenOn: 0.0.0.0:8080 Mysql:   DataSource: bookstore:bookstore@tcp(172.20.3.12:3306)/bs-user?charset=utf8&parseTime=true CacheRedis:   - Host: 172.20.3.13:30020     Pass: '123456'     Type: node 复制代码
4.完善逻辑internal/logic文件夹和服务internal/svc文件夹中的文件,完整的可去GitHub上搜寻。

loginlogic.go

func (l *LoginLogic) Login(in *user.LoginReq) (*user.UserInfoReply, error) { var err error var rep = new(model.UserInfo) if in.Username != "" { rep, err = l.svcCtx.Model.FindOneByUsername(in.Username) } else if in.Email != "" { rep, err = l.svcCtx.Model.FindOneByEmail(in.Email) } else if in.Phone != "" { rep, err = l.svcCtx.Model.FindOneByPhone(in.Phone) } else { return &user.UserInfoReply{}, nil } if err != 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 } 复制代码

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), } } 复制代码
5.编写一个客户端进行验证userclient.go
func main() { // 连接服务器 conn, err := grpc.Dial("172.20.3.111:8080", grpc.WithInsecure()) if err != nil { fmt.Println("连接服务端失败: ", err) return } defer conn.Close() // 新建一个客户端 c := user.NewUserClient(conn) // 调用服务端函数 r, err := c.FindAllUser(context.Background(), &user.Request{}) if err != nil { fmt.Println("调用服务端代码失败: ", err) return } fmt.Println("调用成功: ", r) } 复制代码
6.测试运行

user rpc.png

三、WebApi网关

使用Gin框架进行网关的设计,用注册部分简单说一下,其它类似,在GitHub中查看完整文件

1.文件结构,同基础专栏类似,新增了一个Pb文件夹用于放grpc的文件和Etc文件夹用于存放配置文件
book-store@ubuntu:~/BookStoreProject/WebApi$ tree -d . ├── Apps │   └── user ├── Assets ├── Databases ├── Etc ├── Middlewares ├── Models ├── Pb │   └── user ├── Router ├── Services └── Utils 12 directories 复制代码
2.在Services/grpc.go中连接Grpc微服务端
func GrpcInit() error { conn, err := grpc.Dial(C.UserRpc.Host, grpc.WithInsecure()) if err != nil { return err } UserGrpc = user.NewUserClient(conn) return nil } 复制代码
3.在Apps/user/login_handler.go中编写处理注册请求逻辑
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}) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } else { if rep.Username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "没有找到用户,请先注册"}) 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) if err != 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}) } 复制代码
4.在Router/routers.go中编写路由
func Init() *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 } 复制代码
5.运行,可以在postman上看到结果

user webApi.png

四、Vue前端

使用Vue + Element-UI组件进行简单实现,在GitHub中Front目录查看完整文件

1.展示Login.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="用户名或邮箱或电话">               <el-input le v-model="form.username"></el-input>             </el-form-item>             <el-form-item label="密码">               <el-input v-model="form.password"></el-input>             </el-form-item>             <el-form-item>               <el-button type="primary" @click="onSubmit('form')">登录</el-button>               <el-button>取消</el-button>             </el-form-item>           </el-form>         </div>       </el-col>     </el-row>   </div> </template> <script> export default {   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: '登录成功', duration: 1000})             setTimeout(function () {               that.$router.push({path: '/'})               window.location.reload()             }, 1000)           }).catch(function (error) {             console.log(error)             alert('用户不存在')           })         } else {           console.log('error submit!!')           return false         }       })     }   } } </script> <style scoped> </style> 复制代码
2.运行结果展示

user vue login2.png

五、预告

本专栏持续更新中,下一章将把项目部署在Kubernets上,请感兴趣的同学收藏关注,谢谢。也欢迎大家提出意见或者建议,共同进步。

来源https://juejin.cn/post/7018427464225407012

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐