1 Star 0 Fork 0

jason-laf / mxshop

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
user.go 7.96 KB
一键复制 编辑 原始数据 按行查看 历史
jason-laf 提交于 2024-02-24 04:59 . u
package api
import (
"context"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/mojocn/base64Captcha"
"go.uber.org/zap"
"gitee.com/jason-laf/common/global"
"gitee.com/jason-laf/common/pb/user"
"gitee.com/jason-laf/common/utils"
"gitee.com/jason-laf/mxshop/user/api/form"
"gitee.com/jason-laf/mxshop/user/api/model"
)
var store = base64Captcha.DefaultMemStore // 放在全局变量的原因是可以在其他地方使用 store 验证
func Register(c *gin.Context) {
/*
1. 解析表单
2. 验证参数
3. 校验验证码
4. 请求rpc层
是否需要考虑幂等性?每一个请求都要满足幂等性
一次和多次请求某一个资源,对资源本身所产生的的影响均与一次执行的影响相同(多次操作数据库数据是一致的)
HTTP 请求可以大致分为 GET、POST、PUT 和 DELETE
GET 和 DELETE 请求操作执行一次和执行多次不影响最终结果,天生支持幂等性
所以,需要考虑到幂等性的场景就是 PUT 和 POST
常见的解决幂等性的方式有以下:
1. 数据库唯一索引;保证插入的数据只有一条;
2. token机制(redis);服务端提供了发送token的接口。每次接口请求前先去server端获取一个唯一的token,
并将token放入reids中,客户端请求的时候加上这个token,服务端接收到请求先删除redis中的这个token,
如果删除成功,则是第一次请求,执行业务逻辑,如果删除失败,说明这个请求已经被处理过了,直接返回
3. 悲观锁或者乐观锁,悲观锁可以保证每次for update的时候其他sql无法update数据
(在数据库引擎是innodb的时候,select的条件必须是唯一索引,防止锁全表)【分布锁思路】
4. 先查询后判断,首先通过查询数据库是否存在数据,如果存在证明已经请求过了,直接拒绝该请求,
如果没有存在,就证明是第一次进来,直接放行。
PUT 请求的解决方法:唯一索引(数据库支持)
POST 请求要分情况:当设置某一个值的时候不存在幂等性的问题;但是当在某个字段上增加值的时候,就存在幂等性的问题
POST 请求的解决方法:
5. 返回token
*/
var register form.Register
if err := c.ShouldBind(&register); err != nil {
utils.HandleValidatorError(c, err)
return
}
// 短信验证码校验
result, err := global.RedisClient.Get(context.Background(), register.Mobile).Result()
if err != nil {
zap.S().Errorf(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"msg": "查询用户验证码错误",
})
return
}
if result != register.Code {
c.JSON(http.StatusBadRequest, gin.H{
"msg": "短信验证码错误",
})
return
}
// RPC调用
userRsp, err := global.UserClient.CreateUser(context.Background(), &user.CreateUserReq{
Name: register.Mobile,
Mobile: register.Mobile,
Password: register.Password,
})
if err != nil {
zap.S().Errorf(err.Error())
utils.HandleGrpcError(c, err)
return
}
// 生成JWT
j := utils.NewJWT()
claims := jwt.MapClaims{ // claims可以被base64解码,因此不能存放敏感信息
"iss": "mxshop-server", // 签发者
"sub": "all", // 该JWT所面向的用户
"aud": "user", // 接收该JWT的一方
"foo": 2,
"exp": time.Now().Add(time.Hour * time.Duration(1)).Unix(), // 过期时间
"iat": time.Now().Unix(), // 签发时间
// 可以自定义字段
"user_id": userRsp.User.Id, // 用户id
"role": userRsp.User.Role, // 用户角色
}
token, err := j.CreateToken(&claims)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"msg": fmt.Sprintf("生成token失败: %s", err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "用户创建成功",
"token": token,
})
}
func Update(c *gin.Context) {
var update form.Update
if err := c.ShouldBind(&update); err != nil {
utils.HandleValidatorError(c, err)
return
}
// 解析user_id
val, ok := c.Get("claims")
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{
"msg": fmt.Sprintf("claims不存在"),
})
return
}
claims, ok := val.(jwt.MapClaims)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{
"msg": fmt.Sprintf("token解析失败"),
})
return
}
id, ok := claims["user_id"]
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{
"msg": fmt.Sprintf("user_id不存在"),
})
return
}
// RPC调用
_, err := global.UserClient.UpdateUser(context.Background(), &user.UpdateUserReq{
Id: int32(id.(float64)),
Name: update.Name,
Age: update.Age,
Gender: user.Gender(update.Gender),
Email: update.Email,
Password: update.Password,
Birthday: update.Birthday,
})
if err != nil {
zap.S().Errorf(err.Error())
utils.HandleGrpcError(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "用户更新成功",
})
}
func List(c *gin.Context) {
var page form.Page
if err := c.ShouldBindQuery(&page); err != nil { // query 适用于绑定GET请求中的参数,使用的是 form 绑定引擎,所以tag使用form
utils.HandleValidatorError(c, err)
return
}
//pn := c.DefaultQuery("pn", "1")
//pSize := c.DefaultQuery("psize", "10")
//pnInt, err := strconv.Atoi(pn)
//psizeInt, err := strconv.Atoi(pSize)
//if err != nil {
// zap.S().Errorf(err.Error())
// c.JSON(http.StatusBadRequest, gin.H{
// "msg": "参数错误",
// })
// return
//}
rsp, err := global.UserClient.GetUserList(context.Background(), &user.GetUserListReq{Pn: page.Pn, PSize: page.PSize})
if err != nil {
zap.S().Errorf(err.Error())
utils.HandleGrpcError(c, err)
return
}
users := make([]model.UserRsp, len(rsp.Users))
for k, v := range rsp.Users {
users[k] = model.UserRsp{
Name: v.Name,
Age: int(v.Age),
Gender: v.Gender,
Mobile: v.Mobile,
Email: v.Email,
Birthday: model.FormatTime(time.Unix(v.Birthday, 0)),
}
}
c.JSON(http.StatusOK, gin.H{
"total": rsp.Total,
"users": users,
})
}
func Login(c *gin.Context) {
/*
1. 表单验证
2. 验证图形验证码
3. 查询数据库是否有这个用户
4. 判断密码是否正确
5. 返回token
*/
var login form.Login
if err := c.ShouldBind(&login); err != nil {
utils.HandleValidatorError(c, err)
return
}
// 图片验证码校验
if !store.Verify(login.CaptchaId, login.Captcha, false) { // 第三个参数为 false 时,校验传入的id的验证码后,这个ID的验证码不删除
c.JSON(http.StatusBadRequest, gin.H{
"msg": "验证码错误",
})
return
}
// 查询用户是否存在
userRsp, err := global.UserClient.GetUserByMobile(context.Background(), &user.GetUserByMobileReq{Mobile: login.Mobile})
if err != nil {
zap.S().Errorf(err.Error())
utils.HandleGrpcError(c, err)
return
}
// 检查密码
pwdRsp, err := global.UserClient.CheckPassword(context.Background(), &user.CheckPasswordReq{Password: login.Password, EncryptedPassword: userRsp.User.Password})
if err != nil {
zap.S().Errorf(err.Error())
utils.HandleGrpcError(c, err)
return
}
if !pwdRsp.IsSuccess {
c.JSON(http.StatusBadRequest, gin.H{
"msg": "账号密码错误",
})
return
}
// 生成JWT并返回
j := utils.NewJWT()
claims := jwt.MapClaims{ // claims可以被base64解码,因此不能存放敏感信息
"iss": "mxshop-server", // 签发者
"sub": "all", // 该JWT所面向的用户
"aud": "user", // 接收该JWT的一方
"foo": 2,
"exp": time.Now().Add(time.Hour * 24 * time.Duration(90)).Unix(), // 过期时间
"iat": time.Now().Unix(), // 签发时间
// 可以自定义字段
"user_id": userRsp.User.Id, // 用户id
"role": userRsp.User.Role, // 用户角色
}
token, err := j.CreateToken(&claims)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"msg": fmt.Sprintf("生成token失败: %s", err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "登陆成功",
"token": token,
})
}
1
https://gitee.com/jason-laf/mxshop.git
git@gitee.com:jason-laf/mxshop.git
jason-laf
mxshop
mxshop
d54e58f0fab9

搜索帮助