mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-14 07:04:29 +08:00
微信登录验证完成
This commit is contained in:
@@ -8,6 +8,7 @@ package handler
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/middleware"
|
||||
@@ -37,6 +38,8 @@ type UserHandler struct {
|
||||
licenseService *service.LicenseService
|
||||
captchaService *service.CaptchaService
|
||||
userService *service.UserService
|
||||
wxLoginService *service.WxLoginService
|
||||
ipSearcher *xdb.Searcher
|
||||
}
|
||||
|
||||
func NewUserHandler(
|
||||
@@ -47,6 +50,8 @@ func NewUserHandler(
|
||||
levelDB *store.LevelDB,
|
||||
captcha *service.CaptchaService,
|
||||
userService *service.UserService,
|
||||
wxLoginService *service.WxLoginService,
|
||||
ipSearcher *xdb.Searcher,
|
||||
licenseService *service.LicenseService) *UserHandler {
|
||||
return &UserHandler{
|
||||
BaseHandler: BaseHandler{DB: db, App: app},
|
||||
@@ -56,6 +61,8 @@ func NewUserHandler(
|
||||
captchaService: captcha,
|
||||
licenseService: licenseService,
|
||||
userService: userService,
|
||||
wxLoginService: wxLoginService,
|
||||
ipSearcher: ipSearcher,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +74,10 @@ func (h *UserHandler) RegisterRoutes() {
|
||||
group.POST("register", h.Register)
|
||||
group.POST("login", h.Login)
|
||||
group.POST("resetPass", h.ResetPass)
|
||||
group.GET("clogin", h.CLogin)
|
||||
group.GET("login/qrcode", h.GetWxLoginQRCode)
|
||||
group.POST("login/callback", h.WxLoginCallback)
|
||||
group.GET("login/status", h.GetWxLoginState)
|
||||
group.GET("logout", h.Logout)
|
||||
group.GET("clogin/callback", h.CLoginCallback)
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
@@ -150,30 +158,8 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// 验证邀请码
|
||||
inviteCode := model.InviteCode{}
|
||||
if data.InviteCode != "" {
|
||||
res := h.DB.Where("code = ?", data.InviteCode).First(&inviteCode)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "无效的邀请码")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
salt := utils.RandString(8)
|
||||
user := model.User{
|
||||
Username: data.Username,
|
||||
Password: utils.GenPassword(data.Password, salt),
|
||||
Avatar: "/images/avatar/user.png",
|
||||
Salt: salt,
|
||||
Status: true,
|
||||
ChatRoles: utils.JsonEncode([]string{"gpt"}), // 默认只订阅通用助手角色
|
||||
ChatConfig: "{}",
|
||||
ChatModels: "{}",
|
||||
Power: h.App.SysConfig.Base.InitPower,
|
||||
}
|
||||
|
||||
// check if the username is existing
|
||||
user := model.User{Username: data.Username, Password: data.Password}
|
||||
var item model.User
|
||||
session := h.DB.Session(&gorm.Session{})
|
||||
if data.Mobile != "" {
|
||||
@@ -193,78 +179,19 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 被邀请人也获得赠送算力
|
||||
if data.InviteCode != "" {
|
||||
user.Power += h.App.SysConfig.Base.InvitePower
|
||||
}
|
||||
|
||||
if h.licenseService.GetLicense().Configs.DeCopy {
|
||||
user.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6))
|
||||
} else {
|
||||
defaultNickname := h.App.SysConfig.Base.DefaultNickname
|
||||
if defaultNickname == "" {
|
||||
defaultNickname = "极客学长"
|
||||
}
|
||||
user.Nickname = fmt.Sprintf("%s@%d", defaultNickname, utils.RandomNumber(6))
|
||||
}
|
||||
|
||||
tx := h.DB.Begin()
|
||||
if err := tx.Create(&user).Error; err != nil {
|
||||
user, err := h.createNewUser(user, data.InviteCode)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 记录邀请关系
|
||||
if data.InviteCode != "" {
|
||||
// 增加邀请数量
|
||||
h.DB.Model(&model.InviteCode{}).Where("code = ?", data.InviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
|
||||
if h.App.SysConfig.Base.InvitePower > 0 {
|
||||
err := h.userService.IncreasePower(inviteCode.UserId, h.App.SysConfig.Base.InvitePower, model.PowerLog{
|
||||
Type: types.PowerInvite,
|
||||
Model: "Invite",
|
||||
Remark: fmt.Sprintf("邀请用户注册奖励,金额:%d,邀请码:%s,新用户:%s", h.App.SysConfig.Base.InvitePower, inviteCode.Code, user.Username),
|
||||
})
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 添加邀请记录
|
||||
err := tx.Create(&model.InviteLog{
|
||||
InviterId: inviteCode.UserId,
|
||||
UserId: user.Id,
|
||||
Username: user.Username,
|
||||
InviteCode: inviteCode.Code,
|
||||
Remark: fmt.Sprintf("奖励 %d 算力", h.App.SysConfig.Base.InvitePower),
|
||||
}).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
|
||||
_ = h.redis.Del(c, key) // 注册成功,删除短信验证码
|
||||
// 自动登录创建 token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": user.Id,
|
||||
"expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(),
|
||||
})
|
||||
tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey))
|
||||
token, err := h.doLogin(&user, c.ClientIP())
|
||||
if err != nil {
|
||||
resp.ERROR(c, "Failed to generate token, "+err.Error())
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
// 保存到 redis
|
||||
key = fmt.Sprintf("users/%d", user.Id)
|
||||
if _, err := h.redis.Set(c, key, tokenString, 0).Result(); err != nil {
|
||||
resp.ERROR(c, "error with save token: "+err.Error())
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username})
|
||||
|
||||
resp.SUCCESS(c, gin.H{"token": token, "user_id": user.Id, "username": user.Username})
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
@@ -311,35 +238,13 @@ func (h *UserHandler) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新最后登录时间和IP
|
||||
user.LastLoginIp = c.ClientIP()
|
||||
user.LastLoginAt = time.Now().Unix()
|
||||
h.DB.Model(&user).Updates(user)
|
||||
|
||||
h.DB.Create(&model.UserLoginLog{
|
||||
UserId: user.Id,
|
||||
Username: user.Username,
|
||||
LoginIp: c.ClientIP(),
|
||||
LoginAddress: utils.Ip2Region(h.searcher, c.ClientIP()),
|
||||
})
|
||||
|
||||
// 创建 token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": user.Id,
|
||||
"expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(),
|
||||
})
|
||||
tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey))
|
||||
token, err := h.doLogin(&user, c.ClientIP())
|
||||
if err != nil {
|
||||
resp.ERROR(c, "Failed to generate token, "+err.Error())
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
// 保存到 redis
|
||||
sessionKey := fmt.Sprintf("users/%d", user.Id)
|
||||
if _, err = h.redis.Set(c, sessionKey, tokenString, 0).Result(); err != nil {
|
||||
resp.ERROR(c, "error with save token: "+err.Error())
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username})
|
||||
|
||||
resp.SUCCESS(c, gin.H{"token": token, "user_id": user.Id, "username": user.Username})
|
||||
}
|
||||
|
||||
// Logout 注 销
|
||||
@@ -351,130 +256,208 @@ func (h *UserHandler) Logout(c *gin.Context) {
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
// CLogin 第三方登录请求二维码
|
||||
func (h *UserHandler) CLogin(c *gin.Context) {
|
||||
// GetWxLoginQRCode 获取微信登录二维码URL
|
||||
func (h *UserHandler) GetWxLoginQRCode(c *gin.Context) {
|
||||
if !h.wxLoginService.GetConfig().Enabled {
|
||||
resp.ERROR(c, "微信登录功能未启用")
|
||||
return
|
||||
}
|
||||
|
||||
if h.wxLoginService.GetConfig().ApiKey == "" {
|
||||
resp.ERROR(c, "微信登录服务令牌未配置")
|
||||
return
|
||||
}
|
||||
|
||||
state := utils.RandString(32)
|
||||
qrCodeURL, err := h.wxLoginService.GetLoginQrCodeUrl(state)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp.SUCCESS(c, gin.H{
|
||||
"url": qrCodeURL,
|
||||
"state": state,
|
||||
})
|
||||
}
|
||||
|
||||
// CLoginCallback 第三方登录回调
|
||||
func (h *UserHandler) CLoginCallback(c *gin.Context) {
|
||||
// loginType := c.Query("login_type")
|
||||
// code := c.Query("code")
|
||||
// userId := h.GetInt(c, "user_id", 0)
|
||||
// action := c.Query("action")
|
||||
// 查询微信登录状态
|
||||
func (h *UserHandler) GetWxLoginState(c *gin.Context) {
|
||||
state := c.Query("state")
|
||||
if state == "" {
|
||||
resp.ERROR(c, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// var res types.BizVo
|
||||
// apiURL := fmt.Sprintf("%s/api/clogin/info", h.App.Config.ApiConfig.ApiURL)
|
||||
// r, err := req.C().R().SetBody(gin.H{"login_type": loginType, "code": code}).
|
||||
// SetHeader("AppId", h.App.Config.ApiConfig.AppId).
|
||||
// SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.App.Config.ApiConfig.Token)).
|
||||
// SetSuccessResult(&res).
|
||||
// Post(apiURL)
|
||||
// if err != nil {
|
||||
// resp.ERROR(c, err.Error())
|
||||
// return
|
||||
// }
|
||||
// if r.IsErrorState() {
|
||||
// resp.ERROR(c, "error with login http status: "+r.Status)
|
||||
// return
|
||||
// }
|
||||
status, err := h.wxLoginService.GetLoginStatus(state)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// if res.Code != types.Success {
|
||||
// resp.ERROR(c, "error with http response: "+res.Message)
|
||||
// return
|
||||
// }
|
||||
if status.Status != service.LoginStatusSuccess {
|
||||
resp.SUCCESS(c, status)
|
||||
return
|
||||
}
|
||||
|
||||
// // login successfully
|
||||
// data := res.Data.(map[string]interface{})
|
||||
// var user model.User
|
||||
// if action == "bind" && userId > 0 {
|
||||
// err = h.DB.Where("openid", data["openid"]).First(&user).Error
|
||||
// if err == nil {
|
||||
// resp.ERROR(c, "该微信已经绑定其他账号,请先解绑")
|
||||
// return
|
||||
// }
|
||||
// 登录成功
|
||||
var user model.User
|
||||
h.DB.Where("openid = ?", status.OpenID).First(&user)
|
||||
if user.Id == 0 {
|
||||
// 创建新用户
|
||||
user, err = h.createNewUser(model.User{OpenId: status.OpenID}, "")
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// err = h.DB.Where("id", userId).First(&user).Error
|
||||
// if err != nil {
|
||||
// resp.ERROR(c, "绑定用户不存在")
|
||||
// return
|
||||
// }
|
||||
token, err := h.doLogin(&user, c.ClientIP())
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// err = h.DB.Model(&user).UpdateColumn("openid", data["openid"]).Error
|
||||
// if err != nil {
|
||||
// resp.ERROR(c, "更新用户信息失败,"+err.Error())
|
||||
// return
|
||||
// }
|
||||
status.Status = service.LoginStatusExpired
|
||||
h.wxLoginService.SetLoginStatus(state, *status)
|
||||
|
||||
// resp.SUCCESS(c, gin.H{"token": ""})
|
||||
// return
|
||||
// }
|
||||
status.Status = service.LoginStatusSuccess
|
||||
status.Token = token
|
||||
resp.SUCCESS(c, status)
|
||||
}
|
||||
|
||||
// session := gin.H{}
|
||||
// tx := h.DB.Where("openid", data["openid"]).First(&user)
|
||||
// if tx.Error != nil {
|
||||
// // create new user
|
||||
// var totalUser int64
|
||||
// h.DB.Model(&model.User{}).Count(&totalUser)
|
||||
// if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
|
||||
// resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
||||
// return
|
||||
// }
|
||||
// createNewUser 创建新用户
|
||||
func (h *UserHandler) createNewUser(user model.User, inviteCode string) (model.User, error) {
|
||||
if user.OpenId != "" {
|
||||
user.Platform = "wechat"
|
||||
user.Nickname = fmt.Sprintf("微信用户@%d", utils.RandomNumber(6))
|
||||
user.Username = fmt.Sprintf("wx@%d", utils.RandomNumber(8))
|
||||
user.Password = "geekai123"
|
||||
} else {
|
||||
user.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6))
|
||||
if user.Username == "" || user.Password == "" {
|
||||
return user, fmt.Errorf("用户名或密码不能为空")
|
||||
}
|
||||
}
|
||||
|
||||
// salt := utils.RandString(8)
|
||||
// password := fmt.Sprintf("%d", utils.RandomNumber(8))
|
||||
// user = model.User{
|
||||
// Username: fmt.Sprintf("%s@%d", loginType, utils.RandomNumber(10)),
|
||||
// Password: utils.GenPassword(password, salt),
|
||||
// Avatar: fmt.Sprintf("%s", data["avatar"]),
|
||||
// Salt: salt,
|
||||
// Status: true,
|
||||
// ChatRoles: utils.JsonEncode([]string{"gpt"}), // 默认只订阅通用助手角色
|
||||
// Power: h.App.SysConfig.InitPower,
|
||||
// OpenId: fmt.Sprintf("%s", data["openid"]),
|
||||
// Nickname: fmt.Sprintf("%s", data["nickname"]),
|
||||
// }
|
||||
salt := utils.RandString(8)
|
||||
user.Salt = salt
|
||||
user.Password = utils.GenPassword(user.Password, salt)
|
||||
user.Avatar = "/images/avatar/user.png"
|
||||
user.Status = true
|
||||
user.ChatRoles = utils.JsonEncode([]string{"gpt"})
|
||||
user.ChatConfig = "{}"
|
||||
user.ChatModels = "{}"
|
||||
user.Power = h.App.SysConfig.Base.InitPower
|
||||
|
||||
// tx = h.DB.Create(&user)
|
||||
// if tx.Error != nil {
|
||||
// resp.ERROR(c, "保存数据失败")
|
||||
// logger.Error(tx.Error)
|
||||
// return
|
||||
// }
|
||||
// session["username"] = user.Username
|
||||
// session["password"] = password
|
||||
// } else { // login directly
|
||||
// // 更新最后登录时间和IP
|
||||
// user.LastLoginIp = c.ClientIP()
|
||||
// user.LastLoginAt = time.Now().Unix()
|
||||
// h.DB.Model(&user).Updates(user)
|
||||
// 创建用户
|
||||
tx := h.DB.Begin()
|
||||
if err := tx.Create(&user).Error; err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
// h.DB.Create(&model.UserLoginLog{
|
||||
// UserId: user.Id,
|
||||
// Username: user.Username,
|
||||
// LoginIp: c.ClientIP(),
|
||||
// LoginAddress: utils.Ip2Region(h.searcher, c.ClientIP()),
|
||||
// })
|
||||
// }
|
||||
// 记录邀请关系
|
||||
if inviteCode != "" {
|
||||
inviteCode := model.InviteCode{}
|
||||
err := h.DB.Where("code = ?", inviteCode).First(&inviteCode).Error
|
||||
if err != nil {
|
||||
return user, fmt.Errorf("无效的邀请码")
|
||||
}
|
||||
|
||||
// // 创建 token
|
||||
// token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
// "user_id": user.Id,
|
||||
// "expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(),
|
||||
// })
|
||||
// tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey))
|
||||
// if err != nil {
|
||||
// resp.ERROR(c, "Failed to generate token, "+err.Error())
|
||||
// return
|
||||
// }
|
||||
// // 保存到 redis
|
||||
// key := fmt.Sprintf("users/%d", user.Id)
|
||||
// if _, err := h.redis.Set(c, key, tokenString, 0).Result(); err != nil {
|
||||
// resp.ERROR(c, "error with save token: "+err.Error())
|
||||
// return
|
||||
// }
|
||||
// session["token"] = tokenString
|
||||
// resp.SUCCESS(c, session)
|
||||
// 增加邀请数量
|
||||
h.DB.Model(&model.InviteCode{}).Where("code = ?", inviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
|
||||
if h.App.SysConfig.Base.InvitePower > 0 {
|
||||
err := h.userService.IncreasePower(inviteCode.UserId, h.App.SysConfig.Base.InvitePower, model.PowerLog{
|
||||
Type: types.PowerInvite,
|
||||
Model: "Invite",
|
||||
Remark: fmt.Sprintf("邀请用户注册奖励,金额:%d,邀请码:%s,新用户:%s", h.App.SysConfig.Base.InvitePower, inviteCode.Code, user.Username),
|
||||
})
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return user, err
|
||||
}
|
||||
|
||||
// 添加邀请记录
|
||||
err = tx.Create(&model.InviteLog{
|
||||
InviterId: inviteCode.UserId,
|
||||
UserId: user.Id,
|
||||
Username: user.Username,
|
||||
InviteCode: inviteCode.Code,
|
||||
Remark: fmt.Sprintf("奖励 %d 算力", h.App.SysConfig.Base.InvitePower),
|
||||
}).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return user, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// doLogin 执行登录操作
|
||||
func (h *UserHandler) doLogin(user *model.User, ip string) (string, error) {
|
||||
// 更新最后登录时间和IP
|
||||
user.LastLoginIp = ip
|
||||
user.LastLoginAt = time.Now().Unix()
|
||||
err := h.DB.Model(user).Updates(user).Error
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to update user: %v", err)
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
h.DB.Create(&model.UserLoginLog{
|
||||
UserId: user.Id,
|
||||
Username: user.Username,
|
||||
LoginIp: ip,
|
||||
LoginAddress: utils.Ip2Region(h.ipSearcher, ip),
|
||||
})
|
||||
|
||||
// 创建 token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": user.Id,
|
||||
"expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(),
|
||||
})
|
||||
tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate token: %v", err)
|
||||
}
|
||||
|
||||
// 保存到 redis
|
||||
sessionKey := fmt.Sprintf("users/%d", user.Id)
|
||||
if _, err = h.redis.Set(context.Background(), sessionKey, tokenString, 0).Result(); err != nil {
|
||||
return "", fmt.Errorf("error with save token: %v", err)
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// WxLoginCallback 微信登录回调处理
|
||||
func (h *UserHandler) WxLoginCallback(c *gin.Context) {
|
||||
var data struct {
|
||||
OpenID string `json:"openid"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
if data.OpenID == "" || data.State == "" {
|
||||
resp.ERROR(c, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置登录状态
|
||||
status := service.LoginStatus{
|
||||
Status: service.LoginStatusSuccess,
|
||||
OpenID: data.OpenID,
|
||||
}
|
||||
h.wxLoginService.SetLoginStatus(data.State, status)
|
||||
|
||||
resp.SUCCESS(c, status)
|
||||
}
|
||||
|
||||
// Session 获取/验证会话
|
||||
|
||||
Reference in New Issue
Block a user