feat(user): 实现基础用户系统
- 实现用户手机验证码、密码登录功能,支持 JWT 认证 - 实现用户注册功能 - 实现jwt中获取用户信息
This commit is contained in:
parent
cca66faed5
commit
d09e7a08cf
@ -10,7 +10,7 @@ info (
|
||||
type (
|
||||
sendSmsReq {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
actionType string `json:"actionType" validate:"required"`
|
||||
ActionType string `json:"actionType" validate:"required,oneof=loginCode registerCode QueryCode"`
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -24,9 +24,13 @@ service user {
|
||||
@handler register
|
||||
post /user/register (RegisterReq) returns (RegisterResp)
|
||||
|
||||
@doc "login"
|
||||
@handler login
|
||||
post /user/login (LoginReq) returns (LoginResp)
|
||||
@doc "mobile login"
|
||||
@handler mobileLogin
|
||||
post /user/mobileLogin (MobileLoginReq) returns (MobileLoginResp)
|
||||
|
||||
@doc "mobile code login"
|
||||
@handler mobileCodeLogin
|
||||
post /user/mobileCodeLogin (MobileCodeLoginReq) returns (MobileCodeLoginResp)
|
||||
}
|
||||
|
||||
//need login
|
||||
|
@ -10,12 +10,14 @@ info (
|
||||
type User {
|
||||
Id int64 `json:"id"`
|
||||
Mobile string `json:"mobile"`
|
||||
NickName string `json:"nickName"`
|
||||
}
|
||||
|
||||
type (
|
||||
RegisterReq {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Password string `json:"password" validate:"required,min=11,max=11,password"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
RegisterResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
@ -25,11 +27,23 @@ type (
|
||||
)
|
||||
|
||||
type (
|
||||
LoginReq {
|
||||
Mobile string `json:"mobile"`
|
||||
Password string `json:"password"`
|
||||
MobileLoginReq {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
LoginResp {
|
||||
MobileLoginResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
MobileCodeLoginReq {
|
||||
Mobile string `json:"mobile"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
MobileCodeLoginResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
|
@ -1,10 +1,10 @@
|
||||
Name: user
|
||||
Host: 0.0.0.0
|
||||
Port: 8888
|
||||
DataSource: "qnc:V3j@K9!qL5#wX4$p@tcp(127.0.0.1:20001)/qnc?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
DataSource: "qnc:5vg67b3UNHu8@tcp(127.0.0.1:20001)/qnc?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
CacheRedis:
|
||||
- Host: "127.0.0.1:20002"
|
||||
Pass: "A!02Myl>H}ZQfpxC" # Redis 密码,如果未设置则留空
|
||||
Pass: "3m3WsgyCKWqz" # Redis 密码,如果未设置则留空
|
||||
Type: "node" # 单节点模式
|
||||
JwtAuth:
|
||||
AccessSecret: "WUvoIwL-FK0qnlxhvxR9tV6SjfOpeJMpKmY2QvT99lA"
|
||||
|
@ -1,29 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"qnc-server/app/user/cmd/api/internal/logic/auth"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
)
|
||||
|
||||
func SendSmsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.SendSmsReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := auth.NewSendSmsLogic(r.Context(), svcCtx)
|
||||
err := l.SendSms(&req)
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
@ -27,10 +27,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// login
|
||||
// mobile code login
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/login",
|
||||
Handler: user.LoginHandler(serverCtx),
|
||||
Path: "/user/mobileCodeLogin",
|
||||
Handler: user.MobileCodeLoginHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// mobile login
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/mobileLogin",
|
||||
Handler: user.MobileLoginHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// register
|
||||
|
@ -1,29 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"qnc-server/app/user/cmd/api/internal/logic/user"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
)
|
||||
|
||||
func DetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UserInfoReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := user.NewDetailLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Detail(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"qnc-server/app/user/cmd/api/internal/logic/user"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
)
|
||||
|
||||
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.LoginReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := user.NewLoginLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Login(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"qnc-server/app/user/cmd/api/internal/logic/user"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
)
|
||||
|
||||
func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.RegisterReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := user.NewRegisterLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Register(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"qnc-server/app/user/cmd/api/internal/logic/user"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
)
|
||||
|
||||
func WxMiniAuthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.WXMiniAuthReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := user.NewWxMiniAuthLogic(r.Context(), svcCtx)
|
||||
resp, err := l.WxMiniAuth(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
@ -37,12 +37,12 @@ func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
||||
redisKey := fmt.Sprintf("%s:%s", req.ActionType, req.Mobile)
|
||||
exists, err := l.svcCtx.Redis.Exists(redisKey)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", req.Mobile)
|
||||
}
|
||||
|
||||
if exists {
|
||||
// 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误
|
||||
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "手机号1分钟内重复请求发送二维码, 手机号: %s", req.Mobile)
|
||||
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "短信发送, 手机号1分钟内重复请求发送二维码: %s", req.Mobile)
|
||||
}
|
||||
|
||||
code := fmt.Sprintf("%06d", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000))
|
||||
@ -50,21 +50,21 @@ func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
||||
// 发送短信
|
||||
smsResp, err := l.sendSmsRequest(req.Mobile, code)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送失败, 调用阿里客户端失败: %+v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 调用阿里客户端失败: %+v", err)
|
||||
}
|
||||
if *smsResp.Body.Code != "OK" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送失败, 阿里客户端响应失败: %s", *smsResp.Body.Message)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message)
|
||||
}
|
||||
|
||||
// 将验证码保存到 Redis,设置过期时间
|
||||
err = l.svcCtx.Redis.Setex(req.Mobile, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "缓存失败, 验证码设置过期时间失败: %+v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置过期时间失败: %+v", err)
|
||||
}
|
||||
// 在 Redis 中设置 1 分钟的标记,限制重复请求
|
||||
err = l.svcCtx.Redis.Setex(redisKey, code, 60) // 标记 1 分钟内不能重复请求
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "缓存失败, 验证码设置限制重复请求失败: %+v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置限制重复请求失败: %+v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
@ -24,7 +27,20 @@ func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DetailLogi
|
||||
}
|
||||
|
||||
func (l *DetailLogic) Detail(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %+v", err)
|
||||
}
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户信息, 数据库查询用户信息失败, %+v", err)
|
||||
}
|
||||
var userInfo types.User
|
||||
err = copier.Copy(&userInfo, user)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 用户信息结构体复制失败, %+v", err)
|
||||
}
|
||||
return &types.UserInfoResp{
|
||||
UserInfo: userInfo,
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type LoginLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
|
||||
return &LoginLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return
|
||||
}
|
@ -2,7 +2,9 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"qnc-server/app/user/cmd/api/internal/svc"
|
||||
"qnc-server/app/user/cmd/api/internal/types"
|
||||
@ -14,8 +16,6 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
var ErrUserAlreadyRegisterError = xerr.NewErrMsg("该手机号码已注册")
|
||||
|
||||
type RegisterLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
@ -31,30 +31,42 @@ func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Register
|
||||
}
|
||||
|
||||
func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterResp, err error) {
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "registerCode", req.Mobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "手机注册, 验证码过期: %s", req.Mobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 读取验证码redis缓存失败, mobile: %s, err: %+v", req.Mobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机注册, 验证码不正确: %s", req.Mobile)
|
||||
}
|
||||
hasUser, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, req.Mobile)
|
||||
if findUserErr != nil && findUserErr != model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "mobile:%s,err:%v", req.Mobile, err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 读取数据库获取用户失败, mobile%s, err: %+v", req.Mobile, err)
|
||||
}
|
||||
if hasUser != nil {
|
||||
return nil, errors.Wrapf(ErrUserAlreadyRegisterError, "Register user exists mobile:%s,err:%v", req.Mobile, err)
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该手机号码已注册"), "手机注册, 手机号码已注册, mobile:%s", req.Mobile)
|
||||
}
|
||||
var userId int64
|
||||
if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
user := new(model.User)
|
||||
user.Mobile = req.Mobile
|
||||
if len(user.Nickname) == 0 {
|
||||
user.Nickname = tool.Krand(8, tool.KC_RAND_KIND_ALL)
|
||||
user.Nickname = req.Mobile
|
||||
}
|
||||
if len(req.Password) > 0 {
|
||||
user.Password = tool.Md5ByString(req.Password)
|
||||
}
|
||||
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
||||
if userInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Register db user Insert err:%v,user:%+v", userInsertErr, user)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入新用户失败, mobile%s, err: %+v", req.Mobile, err)
|
||||
}
|
||||
lastId, lastInsertIdErr := insertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Register db user insertResult.LastInsertId err:%v,user:%+v", lastInsertIdErr, user)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 获取新用户ID失败, err:%+v, user:%+v", lastInsertIdErr, user)
|
||||
}
|
||||
userId = lastId
|
||||
|
||||
@ -62,8 +74,8 @@ func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterRe
|
||||
userAuth.UserId = lastId
|
||||
userAuth.AuthKey = req.Mobile
|
||||
userAuth.AuthType = model.UserAuthTypeAppMobile
|
||||
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Register db user_auth Insert err:%v,userAuth:%v", err, userAuthInsertErr)
|
||||
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); userAuthInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入用户认证失败, err:%+v", userAuthInsertErr)
|
||||
}
|
||||
return nil
|
||||
}); transErr != nil {
|
||||
@ -72,9 +84,8 @@ func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterRe
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userId, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "GenerateToken error userId : %d", userId)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机注册, 生成jwt token失败, userid: %d, err:%+v", userId, generaErr)
|
||||
}
|
||||
|
||||
return &types.RegisterResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
|
@ -1,12 +1,23 @@
|
||||
// Code generated by goctl. DO NOT EDIT.
|
||||
package types
|
||||
|
||||
type LoginReq struct {
|
||||
type MobileCodeLoginReq struct {
|
||||
Mobile string `json:"mobile"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
type MobileCodeLoginResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
|
||||
type MobileLoginReq struct {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
type MobileLoginResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
@ -14,7 +25,8 @@ type LoginResp struct {
|
||||
|
||||
type RegisterReq struct {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Password string `json:"password" validate:"required,min=11,max=11,password"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type RegisterResp struct {
|
||||
@ -26,6 +38,7 @@ type RegisterResp struct {
|
||||
type User struct {
|
||||
Id int64 `json:"id"`
|
||||
Mobile string `json:"mobile"`
|
||||
NickName string `json:"nickName"`
|
||||
}
|
||||
|
||||
type UserInfoReq struct {
|
||||
@ -49,5 +62,5 @@ type WXMiniAuthResp struct {
|
||||
|
||||
type SendSmsReq struct {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
ActionType string `json:"actionType" validate:"required"`
|
||||
ActionType string `json:"actionType" validate:"required,oneof=loginCode registerCode QueryCode"`
|
||||
}
|
||||
|
@ -3,20 +3,23 @@ package ctxdata
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CtxKeyJwtUserId get uid from ctx
|
||||
var CtxKeyJwtUserId = "jwtUserId"
|
||||
const CtxKeyJwtUserId = "userId"
|
||||
|
||||
func GetUidFromCtx(ctx context.Context) int64 {
|
||||
var uid int64
|
||||
if jsonUid, ok := ctx.Value(CtxKeyJwtUserId).(json.Number); ok {
|
||||
if int64Uid, err := jsonUid.Int64(); err == nil {
|
||||
uid = int64Uid
|
||||
} else {
|
||||
logx.WithContext(ctx).Errorf("GetUidFromCtx err : %+v", err)
|
||||
// GetUidFromCtx 从 context 中获取用户 ID
|
||||
func GetUidFromCtx(ctx context.Context) (int64, error) {
|
||||
// 尝试从上下文中获取 jwtUserId
|
||||
jsonUid, ok := ctx.Value(CtxKeyJwtUserId).(json.Number)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("无法获取用户 ID, CtxKeyJwtUserId = %v", CtxKeyJwtUserId)
|
||||
}
|
||||
// 转换为 int64
|
||||
uid, err := jsonUid.Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("用户 ID 转换失败: %+v", err)
|
||||
}
|
||||
return uid
|
||||
|
||||
return uid, nil
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ services:
|
||||
# 时区上海 - Time zone Shanghai (Change if needed)
|
||||
TZ: Asia/Shanghai
|
||||
# root 密码 - root password
|
||||
MYSQL_ROOT_PASSWORD: eh:GiX%cWyiV$WF_
|
||||
MYSQL_ROOT_PASSWORD: yfg87gyuYiy1
|
||||
MYSQL_DATABASE: qnc
|
||||
MYSQL_USER: qnc
|
||||
MYSQL_PASSWORD: V3j@K9!qL5#wX4$p
|
||||
MYSQL_PASSWORD: 5vg67b3UNHu8
|
||||
ports:
|
||||
- "20001:3306"
|
||||
volumes:
|
||||
@ -34,7 +34,7 @@ services:
|
||||
|
||||
#redis容器 - Redis container
|
||||
redis:
|
||||
image: redis:6.2.5
|
||||
image: redis:7.4.0
|
||||
container_name: qnc_redis
|
||||
ports:
|
||||
- "20002:6379"
|
||||
@ -44,7 +44,7 @@ services:
|
||||
volumes:
|
||||
# 数据文件 - data files
|
||||
- ./data/redis/data:/data:rw
|
||||
command: "redis-server --requirepass A!02Myl>H}ZQfpxC --appendonly yes"
|
||||
command: "redis-server --requirepass 3m3WsgyCKWqz --appendonly yes"
|
||||
privileged: true
|
||||
restart: always
|
||||
networks:
|
||||
|
1
go.mod
1
go.mod
@ -40,6 +40,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -134,6 +134,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
|
@ -11,6 +11,9 @@ var customMessages = map[string]string{
|
||||
"Mobile.mobile": "电话号码必须为有效的中国电话号码",
|
||||
"IDCard.required": "身份证号是必填项",
|
||||
"IDCard.idCard": "无效的身份证号码",
|
||||
"Password.min": "密码不能少于8位数",
|
||||
"Password.max": "密码不能超过32位数",
|
||||
"password.password": "密码强度太弱",
|
||||
}
|
||||
|
||||
// 获取自定义错误消息
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var validate *validator.Validate
|
||||
@ -43,6 +44,16 @@ func init() {
|
||||
if err := validate.RegisterValidation("mobileType", validMobileType); err != nil {
|
||||
panic(fmt.Sprintf("注册 mobileType 验证器时发生错误: %v", err))
|
||||
}
|
||||
if err := validate.RegisterValidation("password", validatePassword); err != nil {
|
||||
panic(fmt.Sprintf("注册 password 验证器时发生错误: %v", err))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 弱口令列表
|
||||
var weakPasswords = []string{
|
||||
"12345678", "password", "123456789", "qwerty", "123456", "letmein",
|
||||
"1234567", "welcome", "abc123", "password1", "1234", "111111", "admin",
|
||||
}
|
||||
|
||||
// Validate 校验参数逻辑
|
||||
@ -127,3 +138,17 @@ func validMobileType(fl validator.FieldLevel) bool {
|
||||
|
||||
return validTypes[mobileType]
|
||||
}
|
||||
|
||||
// 自定义密码强度校验函数
|
||||
func validatePassword(fl validator.FieldLevel) bool {
|
||||
password := fl.Field().String()
|
||||
|
||||
// 检查密码是否在弱口令列表中
|
||||
for _, weakPwd := range weakPasswords {
|
||||
if strings.ToLower(password) == weakPwd {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
exit status 1exit status 1exit status 1exit status 1exit status 1
|
Loading…
Reference in New Issue
Block a user