From 7a729211d13dd9fdc4e259fc2b35f1f2db45d321 Mon Sep 17 00:00:00 2001 From: Echo <1711788888@qq.com> Date: Wed, 23 Jul 2025 02:33:39 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E4=BC=98=E5=8C=96=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=B5=81=E7=A8=8B&=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=92=8C=E5=BE=AE=E4=BF=A1=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/app/user.go | 92 +++++++++++++++++++++++++++++++++++--- api/v1/user/user.go | 17 +++++++ initialize/router.go | 32 ++++++++----- model/user/request/user.go | 8 ++-- router/app/user.go | 2 + router/user/user.go | 15 ++++--- service/app/user.go | 46 +++++++++++++++++++ service/user/user.go | 36 ++++++++++++--- utils/wechat/wechat.go | 2 +- 9 files changed, 218 insertions(+), 32 deletions(-) diff --git a/api/v1/app/user.go b/api/v1/app/user.go index 2a5178b..f2c8fc0 100644 --- a/api/v1/app/user.go +++ b/api/v1/app/user.go @@ -3,6 +3,9 @@ package app import ( "errors" "fmt" + "strconv" + "time" + "git.echol.cn/loser/lckt/global" "git.echol.cn/loser/lckt/model/app" r "git.echol.cn/loser/lckt/model/common/response" @@ -13,7 +16,6 @@ import ( "github.com/gin-gonic/gin" "github.com/redis/go-redis/v9" "go.uber.org/zap" - "time" ) type AppUserApi struct{} @@ -63,14 +65,14 @@ func (*AppUserApi) Login(ctx *gin.Context) { return } - if _, err = global.GVA_REDIS.Get(ctx, user.Phone).Result(); errors.Is(err, redis.Nil) { + if _, err = global.GVA_REDIS.Get(ctx, strconv.Itoa(int(user.ID))).Result(); errors.Is(err, redis.Nil) { // 此处过期时间等于jwt过期时间 dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) if err != nil { return } timer := dr - if err := global.GVA_REDIS.Set(ctx, user.Phone, token, timer).Err(); err != nil { + if err := global.GVA_REDIS.Set(ctx, strconv.Itoa(int(user.ID)), token, timer).Err(); err != nil { global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err)) r.FailWithMessage("设置登录状态失败", ctx) return @@ -115,19 +117,26 @@ func (*AppUserApi) WechatLogin(ctx *gin.Context) { r.FailWithMessage("获取token失败", ctx) return } - if _, err = global.GVA_REDIS.Get(ctx, user.Phone).Result(); errors.Is(err, redis.Nil) { + if _, err = global.GVA_REDIS.Get(ctx, strconv.Itoa(int(user.ID))).Result(); errors.Is(err, redis.Nil) { // 此处过期时间等于jwt过期时间 dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) if err != nil { return } timer := dr - if err := global.GVA_REDIS.Set(ctx, user.Phone, token, timer).Err(); err != nil { + if err := global.GVA_REDIS.Set(ctx, strconv.Itoa(int(user.ID)), token, timer).Err(); err != nil { global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err)) r.FailWithMessage("设置登录状态失败", ctx) return } user_jwt.SetToken(ctx, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix())) + + result := map[string]interface{}{} + result["User"] = user + result["Token"] = token + result["ExpiresAt"] = claims.RegisteredClaims.ExpiresAt.Unix() * 1000 + fmt.Println(result) + r.OkWithDetailed(gin.H{ "User": user, "Token": token, @@ -182,7 +191,7 @@ func (*AppUserApi) PwdLogin(ctx *gin.Context) { return } timer := dr - if err := global.GVA_REDIS.Set(ctx, user.Phone, token, timer).Err(); err != nil { + if err := global.GVA_REDIS.Set(ctx, strconv.Itoa(int(user.ID)), token, timer).Err(); err != nil { global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err)) r.FailWithMessage("设置登录状态失败", ctx) return @@ -289,3 +298,74 @@ func (a *AppUserApi) GetTeacherApply(context *gin.Context) { r.OkWithDetailed(status, "获取教师申请状态成功", context) } + +// BindWechat 手机登录用户绑定微信 +func (a *AppUserApi) BindWechat(context *gin.Context) { + var p request.BindWechatReq + if err := context.ShouldBind(&p); err != nil { + global.GVA_LOG.Error("绑定微信请求参数有误") + r.FailWithMessage("绑定微信请求参数有误", context) + return + } + + user, err := appUserService.BindWechat(p) + if err != nil { + global.GVA_LOG.Error("绑定微信失败", zap.Error(err)) + } + + // 生成新token + token, claims, err := user_jwt.LoginToken(*user) + if err != nil { + global.GVA_LOG.Error("获取token失败!", zap.Error(err)) + r.FailWithMessage("获取token失败", context) + return + } + + // 此处过期时间等于jwt过期时间 + dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime) + if err != nil { + global.GVA_LOG.Error("解析JWT过期时间失败", zap.Error(err)) + r.FailWithMessage("解析JWT过期时间失败", context) + return + } + + timer := dr + if err := global.GVA_REDIS.Set(context, user.Phone, token, timer).Err(); err != nil { + global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err)) + r.FailWithMessage("设置登录状态失败", context) + return + } + + user_jwt.SetToken(context, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix())) + r.OkWithDetailed(gin.H{ + "User": user, + "Token": token, + "ExpiresAt": claims.RegisteredClaims.ExpiresAt.Unix() * 1000, + }, "绑定微信成功", context) +} + +// BindPhone 微信登录用户绑定手机号s +func (a *AppUserApi) BindPhone(context *gin.Context) { + var p request.BindPhoneReq + if err := context.ShouldBind(&p); err != nil { + global.GVA_LOG.Error("绑定手机号请求参数有误") + r.FailWithMessage("绑定手机号请求参数有误", context) + return + } + + // 验证码检查 + if result, _ := global.GVA_REDIS.Get(context, fmt.Sprintf("VerifyCode:%s", p.Phone)).Result(); result != p.Code { + global.GVA_LOG.Error("验证码错误", zap.String("phone", p.Phone)) + r.FailWithMessage("验证码错误", context) + return + } + + user, err := appUserService.BindPhone(p) + if err != nil { + global.GVA_LOG.Error("绑定手机号失败", zap.Error(err)) + r.FailWithMessage("绑定手机号失败", context) + return + } + + r.OkWithDetailed(user, "绑定手机号成功", context) +} diff --git a/api/v1/user/user.go b/api/v1/user/user.go index a75b130..b488918 100644 --- a/api/v1/user/user.go +++ b/api/v1/user/user.go @@ -2,6 +2,7 @@ package user import ( "git.echol.cn/loser/lckt/global" + "git.echol.cn/loser/lckt/model/app" common "git.echol.cn/loser/lckt/model/common/request" r "git.echol.cn/loser/lckt/model/common/response" "git.echol.cn/loser/lckt/model/user/request" @@ -138,3 +139,19 @@ func (a *UserApi) GetTeacherApplyList(context *gin.Context) { PageSize: p.PageSize, }, "获取教师申请列表成功", context) } + +// UpdateTeacherApplyStatus 更新教师申请状态 +func (a *UserApi) UpdateTeacherApplyStatus(context *gin.Context) { + var p app.TeacherApply + if err := context.ShouldBind(&p); err != nil { + r.FailWithMessage(err.Error(), context) + global.GVA_LOG.Error("参数错误,更新教师申请状态失败", zap.Error(err)) + return + } + + if err := userService.UpdateTeacherApplyStatus(p); err != nil { + r.FailWithMessage("更新教师申请状态失败", context) + return + } + r.OkWithMessage("更新教师申请状态成功", context) +} diff --git a/initialize/router.go b/initialize/router.go index 6848292..41ecd2f 100644 --- a/initialize/router.go +++ b/initialize/router.go @@ -1,18 +1,19 @@ package initialize import ( - "git.echol.cn/loser/lckt/plugin/customerservice" - "git.echol.cn/loser/lckt/plugin/picturelibrary" - "net/http" - "os" - "git.echol.cn/loser/lckt/docs" "git.echol.cn/loser/lckt/global" "git.echol.cn/loser/lckt/middleware" + "git.echol.cn/loser/lckt/plugin/customerservice" + "git.echol.cn/loser/lckt/plugin/picturelibrary" "git.echol.cn/loser/lckt/router" + gc "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" + "net/http" + "os" + "time" ) type justFilesFilesystem struct { @@ -38,6 +39,17 @@ func (fs justFilesFilesystem) Open(name string) (http.File, error) { func Routers() *gin.Engine { Router := gin.New() Router.Use(gin.Recovery()) + Router.Use(middleware.AllCors()) + + Router.Use(gc.New(gc.Config{ + AllowAllOrigins: true, // 允许所有来源 + AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, + AllowHeaders: []string{"*"}, // 允许所有自定义header + ExposeHeaders: []string{"Content-Length", "Content-Type"}, + AllowCredentials: true, + MaxAge: 12 * time.Hour, + })) + if gin.Mode() == gin.DebugMode { Router.Use(gin.Logger()) } @@ -55,7 +67,7 @@ func Routers() *gin.Engine { Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{http.Dir(global.GVA_CONFIG.Local.StorePath)}) // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件") // 跨域,如需跨域可以打开下面的注释 - // Router.Use(middleware.Cors()) // 直接放行全部跨域请求 + // Router.Use(middleware.AllCors()) // 直接放行全部跨域请求 // Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求 // global.GVA_LOG.Info("use middleware cors") docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix @@ -64,13 +76,13 @@ func Routers() *gin.Engine { // 方便统一添加路由组前缀 多服务器上线使用 PublicGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix) - PublicGroup.Use(middleware.Cors()) // 直接放行全部跨域请求 + PublicGroup.Use(middleware.AllCors()) // 直接放行全部跨域请求 PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix) - PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).Use(middleware.Cors()) + PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).Use(middleware.AllCors()) AppAuthGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix) - AppAuthGroup.Use(middleware.Cors()) - //AppAuthGroup.Use(middleware.UserJWTAuth()).Use(middleware.Cors()) + AppAuthGroup.Use(middleware.AllCors()) + //AppAuthGroup.Use(middleware.UserJWTAuth()).Use(middleware.AllCors()) { // 健康监测 PublicGroup.GET("/health", func(c *gin.Context) { diff --git a/model/user/request/user.go b/model/user/request/user.go index de4ad7e..dd71995 100644 --- a/model/user/request/user.go +++ b/model/user/request/user.go @@ -7,7 +7,7 @@ type SendCodeReq struct { } type CodeLoginReq struct { - Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'请输入手机号码'"` + Phone string `json:"phone" form:"phone"` Code string `json:"code" form:"code" vd:"@:len($)>0; msg:'请输入短信验证码'"` } @@ -35,8 +35,10 @@ type BindWechatReq struct { } type BindPhoneReq struct { - Id int `json:"id" form:"id" vd:"@:len($)>0; msg:'用户ID不能为空'"` - Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'手机号码不能为空'"` + Id int `json:"id" form:"id" vd:"@:len($)>0; msg:'用户ID不能为空'"` + Phone string `json:"phone" form:"phone" vd:"@:len($)>0; msg:'手机号码不能为空'"` + Code string `json:"code" form:"code" vd:"@:len($)>0; msg:'验证码不能为空'"` + PassWord string `json:"password" form:"password" vd:"@:len($)>0; msg:'密码不能为空'"` } type GetUserListReq struct { diff --git a/router/app/user.go b/router/app/user.go index 006d1b4..a8cef60 100644 --- a/router/app/user.go +++ b/router/app/user.go @@ -17,6 +17,8 @@ func (s *UserRouter) InitAppUserRouter(AppAuthGroup, PublicRouter *gin.RouterGro } { publicRouter.POST("wxLogin", userApi.WechatLogin) // 微信登录 + publicRouter.POST("bindWX", userApi.BindWechat) // 绑定微信 + publicRouter.POST("bindPhone", userApi.BindPhone) // 获取用户信息 publicRouter.POST("pwdlogin", userApi.PwdLogin) // 密码登录 publicRouter.POST("sms/send", userApi.SendCode) // 发送短信验证码 publicRouter.POST("login", userApi.Login) // 短信验证码登录 diff --git a/router/user/user.go b/router/user/user.go index f2ecacc..664713d 100644 --- a/router/user/user.go +++ b/router/user/user.go @@ -11,12 +11,13 @@ type UserRouter struct{} func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) { userRouter := Router.Group("app_user").Use(middleware.OperationRecord()) { - userRouter.GET("list", userApi.GetUserList) // 获取用户列表 - userRouter.PUT("setBalance", userApi.SetBalance) // 更新用户余额 - userRouter.POST("register", userApi.Register) // 注册 - userRouter.PUT("status/:id", userApi.SetUserStatus) // 更新用户状态 - userRouter.GET(":id", userApi.GetUserById) // 获取用户信息 - userRouter.GET("/teachers", userApi.GetTeachers) // 获取教师列表 - userRouter.GET("/teacherApplyList", userApi.GetTeacherApplyList) // 获取教师信息 + userRouter.GET("list", userApi.GetUserList) // 获取用户列表 + userRouter.PUT("setBalance", userApi.SetBalance) // 更新用户余额 + userRouter.POST("register", userApi.Register) // 注册 + userRouter.PUT("status/:id", userApi.SetUserStatus) // 更新用户状态 + userRouter.GET(":id", userApi.GetUserById) // 获取用户信息 + userRouter.GET("/teachers", userApi.GetTeachers) // 获取教师列表 + userRouter.GET("/teacherApplyList", userApi.GetTeacherApplyList) // 获取教师信息 + userRouter.PUT("/teacherApply/status", userApi.UpdateTeacherApplyStatus) // 更新教师申请状态 } } diff --git a/service/app/user.go b/service/app/user.go index 7e2c1f8..ac74208 100644 --- a/service/app/user.go +++ b/service/app/user.go @@ -175,3 +175,49 @@ func (u *AppUserService) GetTeacherApplyStatus(id uint) (teacherApply app.Teache return } + +// BindWechat 手机登录用户绑定微信 +func (u *AppUserService) BindWechat(p request.BindWechatReq) (*user.User, error) { + var userInfo user.User + err := global.GVA_DB.Model(&user.User{}).Where("id = ?", p.Id).First(&userInfo).Error + if err != nil { + global.GVA_LOG.Error("查询用户失败", zap.Error(err)) + return nil, err + } + + if userInfo.OpenId != "" { + global.GVA_LOG.Error("用户已绑定微信") + return nil, fmt.Errorf("用户已绑定微信") + } + + userInfo.OpenId = p.Openid + + err = global.GVA_DB.Save(&userInfo).Error + if err != nil { + global.GVA_LOG.Error("绑定微信失败", zap.Error(err)) + return nil, err + } + + return &userInfo, nil +} + +// BindPhone 微信登录用户绑定手机号 +func (u *AppUserService) BindPhone(p request.BindPhoneReq) (*user.User, error) { + var userInfo user.User + err := global.GVA_DB.Model(&user.User{}).Where("id = ?", p.Id).First(&userInfo).Error + if err != nil { + global.GVA_LOG.Error("查询用户失败", zap.Error(err)) + return nil, err + } + + userInfo.Phone = p.Phone + pwd, _ := bcrypt.GenerateFromPassword([]byte(p.PassWord), bcrypt.DefaultCost) + userInfo.Password = string(pwd) + err = global.GVA_DB.Save(&userInfo).Error + if err != nil { + global.GVA_LOG.Error("绑定微信失败", zap.Error(err)) + return nil, err + } + + return &userInfo, nil +} diff --git a/service/user/user.go b/service/user/user.go index abb33f8..0c0a2cb 100644 --- a/service/user/user.go +++ b/service/user/user.go @@ -9,6 +9,7 @@ import ( common "git.echol.cn/loser/lckt/model/common/request" "git.echol.cn/loser/lckt/model/user" "git.echol.cn/loser/lckt/model/user/request" + "git.echol.cn/loser/lckt/utils/sms" "go.uber.org/zap" "golang.org/x/crypto/bcrypt" "math/rand" @@ -43,11 +44,11 @@ func (u *UserService) SendCode(req request.SendCodeReq) (err error) { rand.New(rand.NewSource(time.Now().UnixNano())) verifyCode := fmt.Sprintf("%06v", rand.Int31n(1000000)) - //if ok := sms.SendSMS(req.Phone, verifyCode); !ok { - // global.GVA_LOG.Error("发送验证码失败") - // return - //} - // + if ok := sms.DxbSendSMS(req.Phone, verifyCode); !ok { + global.GVA_LOG.Error("发送验证码失败") + return + } + if err = rdb.Set(context.Background(), key, verifyCode, 5*time.Minute).Err(); err != nil { global.GVA_LOG.Error("设置验证码缓存失败", zap.Error(err)) return @@ -202,3 +203,28 @@ func (u *UserService) GetTeacherApplyList(p request.GetTeacherApplyListReq) (lis } return } + +func (u *UserService) UpdateTeacherApplyStatus(p app.TeacherApply) (err error) { + err = global.GVA_DB.Save(&p).Error + if err != nil { + global.GVA_LOG.Error("更新教师申请状态失败", zap.Error(err)) + return + } + + // 如果申请通过,更新用户类型为教师 + if p.Status == 1 { + var user user.User + err = global.GVA_DB.Model(&user).Where("id = ?", p.UserId).First(&user).Error + if err != nil { + global.GVA_LOG.Error("查询用户信息失败", zap.Error(err)) + return + } + user.UserType = 2 // 设置为教师 + err = global.GVA_DB.Save(&user).Error + if err != nil { + global.GVA_LOG.Error("更新用户类型失败", zap.Error(err)) + return + } + } + return +} diff --git a/utils/wechat/wechat.go b/utils/wechat/wechat.go index 9e715a8..c4f26a9 100644 --- a/utils/wechat/wechat.go +++ b/utils/wechat/wechat.go @@ -17,7 +17,7 @@ var WechatPay *payment.Payment func InitWeOfficial() { OfficialAccountApp, err := officialAccount.NewOfficialAccount(&officialAccount.UserConfig{ AppID: "wx3d21df18d7f8f9fc", // 公众号、小程序的appid - Secret: "ffce59a9a9272c1aaee53950e96617d8", // + Secret: "3ab19e9b6a5e155c25ac6457be650047", // Log: officialAccount.Log{ Level: "debug",