```
feat(user): 添加QQ绑定功能并重构用户登录逻辑 - 在BaseSysUser模型中添加QQ字段,移除密码字段(暂时注释) - 移除base.bbs.go中的GetUserInfo函数,将其迁移至base_sys_user.go - 将登录服务中的外部API调用逻辑整合到BaseSysUserService - 新增BindQQ方法实现QQ号绑定功能,包含重复绑定检查 - 更新GetUserInfo方法,完善用户信息获取和同步逻辑 - 优化导入包,移除未使用的依赖项 ```
This commit is contained in:
@@ -11,18 +11,18 @@ type BaseSysUser struct {
|
|||||||
*cool.Model
|
*cool.Model
|
||||||
DepartmentID uint `gorm:"column:departmentId;type:bigint;index" json:"departmentId"` // 部门ID
|
DepartmentID uint `gorm:"column:departmentId;type:bigint;index" json:"departmentId"` // 部门ID
|
||||||
|
|
||||||
Username string `gorm:"column:username;type:varchar(100);not null;Index" json:"username"` // 用户名
|
Username string `gorm:"column:username;type:varchar(100);not null;Index" json:"username"` // 用户名
|
||||||
Password string `gorm:"column:password;type:varchar(255);" json:"password"` // 密码
|
//Password string `gorm:"column:password;type:varchar(255);" json:"password"` // 密码
|
||||||
PasswordV *int32 `gorm:"column:passwordV;type:int;not null;default:1" json:"passwordV"` // 密码版本, 作用是改完密码,让原来的token失效
|
PasswordV *int32 `gorm:"column:passwordV;type:int;not null;default:1" json:"passwordV"` // 密码版本, 作用是改完密码,让原来的token失效
|
||||||
|
|
||||||
HeadImg *string `gorm:"column:headImg;type:varchar(255)" json:"headImg"` // 头像
|
HeadImg *string `gorm:"column:headImg;type:varchar(255)" json:"headImg"` // 头像
|
||||||
|
QQ int64 `gorm:"column:qq;type:bigint" json:"qq"`
|
||||||
|
|
||||||
Email *string `gorm:"column:email;type:varchar(255)" json:"email"` // 邮箱
|
|
||||||
Status *int32 `gorm:"column:status;not null;default:1" json:"status"` // 状态 0:禁用 1:启用
|
Status *int32 `gorm:"column:status;not null;default:1" json:"status"` // 状态 0:禁用 1:启用
|
||||||
GoldBean int64 `gorm:"column:goldbean;type:bigint;default:0" json:"goldbean"`
|
GoldBean int64 `gorm:"column:goldbean;type:bigint;default:0" json:"goldbean"`
|
||||||
FreeGold int64 `gorm:"column:free_gold;type:bigint;default:0" json:"free_gold"` //集市金豆
|
FreeGold int64 `gorm:"column:free_gold;type:bigint;default:0" json:"free_gold"` //集市金豆
|
||||||
Remark *string `gorm:"column:remark;type:varchar(255)" json:"remark"` // 备注
|
Remark *string `gorm:"column:remark;type:varchar(255)" json:"remark"` // 备注
|
||||||
//Debug int32 `gorm:"column:debug;type:int;not null;default:0" json:"debug"` // 是否可以进入2服 测试服
|
|
||||||
Maxts uint32 `gorm:"column:max_ts;type:int;not null;default:0" json:"max_ts"` //最后生成的时间记录表
|
Maxts uint32 `gorm:"column:max_ts;type:int;not null;default:0" json:"max_ts"` //最后生成的时间记录表
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/http/cookiejar"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TokenResponse 用来解析第一次请求返回的 JSON
|
// TokenResponse 用来解析第一次请求返回的 JSON
|
||||||
type TokenResponse struct {
|
type TokenResponse struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -101,73 +91,3 @@ type GroupAttributes struct {
|
|||||||
var bbsurl = "http://43.248.3.21:45632"
|
var bbsurl = "http://43.248.3.21:45632"
|
||||||
|
|
||||||
// GetUserInfo 输入用户名和密码,返回用户信息结构体
|
// GetUserInfo 输入用户名和密码,返回用户信息结构体
|
||||||
func GetUserInfo(username, password string) (*UserResponse, error) {
|
|
||||||
// 创建带 Cookie 存储的 HTTP 客户端
|
|
||||||
jar, err := cookiejar.New(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("创建 CookieJar 失败: %w", err)
|
|
||||||
}
|
|
||||||
client := &http.Client{Jar: jar}
|
|
||||||
|
|
||||||
// 1. POST 获取 token
|
|
||||||
tokenURL := bbsurl + "/api/token"
|
|
||||||
formData := url.Values{}
|
|
||||||
formData.Set("identification", username)
|
|
||||||
formData.Set("password", password)
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(formData.Encode()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("发送请求失败: %w", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("读取响应失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析 token
|
|
||||||
var tokenResp TokenResponse
|
|
||||||
err = json.Unmarshal(body, &tokenResp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("解析 token 失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取 CSRF Token
|
|
||||||
csrfToken := resp.Header.Get("X-CSRF-Token")
|
|
||||||
|
|
||||||
// 2. GET 获取该用户的详细信息
|
|
||||||
usersURL := bbsurl + fmt.Sprintf("/api/users/%d", tokenResp.UserID)
|
|
||||||
req2, err := http.NewRequest("GET", usersURL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
|
||||||
}
|
|
||||||
req2.Header.Set("Authentication", tokenResp.Token)
|
|
||||||
req2.Header.Set("X-CSRF-Token", csrfToken)
|
|
||||||
|
|
||||||
resp2, err := client.Do(req2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("发送请求失败: %w", err)
|
|
||||||
}
|
|
||||||
defer resp2.Body.Close()
|
|
||||||
|
|
||||||
body2, err := io.ReadAll(resp2.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("读取响应失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析用户信息
|
|
||||||
var userResp UserResponse
|
|
||||||
err = json.Unmarshal(body2, &userResp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("解析用户信息失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &userResp, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -15,7 +13,6 @@ import (
|
|||||||
"blazing/modules/base/config"
|
"blazing/modules/base/config"
|
||||||
"blazing/modules/base/model"
|
"blazing/modules/base/model"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
|
||||||
"github.com/gogf/gf/v2/encoding/gbase64"
|
"github.com/gogf/gf/v2/encoding/gbase64"
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
@@ -38,11 +35,10 @@ type TokenResult struct {
|
|||||||
// Login 登录
|
// Login 登录
|
||||||
func (s *BaseSysLoginService) Login(ctx context.Context, req *v1.BaseOpenLoginReq) (result *TokenResult, err error) {
|
func (s *BaseSysLoginService) Login(ctx context.Context, req *v1.BaseOpenLoginReq) (result *TokenResult, err error) {
|
||||||
var (
|
var (
|
||||||
captchaId = req.CaptchaId
|
captchaId = req.CaptchaId
|
||||||
verifyCode = req.VerifyCode
|
verifyCode = req.VerifyCode
|
||||||
password = req.Password
|
password = req.Password
|
||||||
username = req.Username
|
username = req.Username
|
||||||
baseSysUser = model.NewBaseSysUser()
|
|
||||||
)
|
)
|
||||||
username = strings.ToLower(username)
|
username = strings.ToLower(username)
|
||||||
vcode, _ := cool.CacheManager.Get(ctx, "login:"+captchaId)
|
vcode, _ := cool.CacheManager.Get(ctx, "login:"+captchaId)
|
||||||
@@ -51,62 +47,11 @@ func (s *BaseSysLoginService) Login(ctx context.Context, req *v1.BaseOpenLoginRe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 调用方法
|
// 调用方法
|
||||||
userInfo, err := GetUserInfo(username, password)
|
user, err := NewBaseSysUserService().GetUserInfo(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = gerror.New("用户系统错误")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 输出结果
|
|
||||||
// fmt.Printf("用户名: %s\n", userInfo.Data.Attributes.Username)
|
|
||||||
// fmt.Printf("显示名: %s\n", userInfo.Data.Attributes.DisplayName)
|
|
||||||
// fmt.Printf("头像: %s\n", userInfo.Data.Attributes.AvatarUrl)
|
|
||||||
// fmt.Printf("加入时间: %s\n", userInfo.Data.Attributes.JoinTime)
|
|
||||||
// fmt.Printf("金钱: %.2f\n", userInfo.Data.Attributes.Money)
|
|
||||||
// fmt.Printf("用户组: %s\n", userInfo.Included[0].Attributes.NameSingular)
|
|
||||||
md5password, _ := gmd5.Encrypt(password)
|
|
||||||
var user *model.BaseSysUser
|
|
||||||
|
|
||||||
if !strings.EqualFold(userInfo.Data.Attributes.Username, username) { //说明没查找到用户
|
|
||||||
//后端添加的账户
|
|
||||||
cool.DBM(baseSysUser).Where("username=?", username).Where("password=?", md5password).Where("status=?", 1).Scan(&user)
|
|
||||||
if user == nil {
|
|
||||||
|
|
||||||
err = gerror.New("账户或密码不正确~")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
m := cool.DBM(baseSysUser).Where("username=?", username)
|
|
||||||
m.Scan(&user)
|
|
||||||
if user == nil {
|
|
||||||
//这里实现注册用户
|
|
||||||
//err = gerror.New("账户或密码不正确~")
|
|
||||||
// return
|
|
||||||
|
|
||||||
NewBaseSysUserService().Gen(userInfo.Data.Attributes)
|
|
||||||
m := cool.DBM(baseSysUser).Where("username=?", username)
|
|
||||||
m.Scan(&user)
|
|
||||||
} else {
|
|
||||||
if *user.Status == 0 {
|
|
||||||
|
|
||||||
err = gerror.New("账户被封禁~")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.HeadImg = &userInfo.Data.Attributes.AvatarUrl
|
|
||||||
var ttt = *user.PasswordV + 1
|
|
||||||
user.PasswordV = &ttt
|
|
||||||
_, err := m.Save(user)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cool.CacheManager.Set(ctx, fmt.Sprintf("admin:passwordVersion:%d", user.ID), ttt, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err = s.generateTokenByUser(ctx, user)
|
result, err = s.generateTokenByUser(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3,7 +3,13 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
@@ -68,6 +74,35 @@ func (s *BaseSysUserService) DuihuanGold(userId uint32, gold, free int64) {
|
|||||||
}).Update()
|
}).Update()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BaseSysUserService) BindQQ(username string, passworid string, qq int64) (err error) {
|
||||||
|
var res *model.BaseSysUser
|
||||||
|
cool.DBM(s.Model).Where("qq", qq).Scan(&res)
|
||||||
|
|
||||||
|
if res != nil {
|
||||||
|
err = gerror.New("该QQ已绑定其他账号" + gconv.String(res.ID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var res1 *model.BaseSysUser
|
||||||
|
cool.DBM(s.Model).Where("id", username).Scan(&res1)
|
||||||
|
if res1 == nil {
|
||||||
|
return gerror.New("用户未激活")
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := s.GetUserInfo(username, passworid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r == nil {
|
||||||
|
err = gerror.New("用户不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = cool.DBM(s.Model).Where("id", username).Data("qq", qq).Update()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
func (s *BaseSysUserService) UpdateFreeGold(userId uint32, gold int64) {
|
func (s *BaseSysUserService) UpdateFreeGold(userId uint32, gold int64) {
|
||||||
|
|
||||||
m := cool.DBM(s.Model).Where("id", userId)
|
m := cool.DBM(s.Model).Where("id", userId)
|
||||||
@@ -162,6 +197,108 @@ func (s *BaseSysUserService) Gen(user UserAttributes) (data interface{}, err err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BaseSysUserService) GetUserInfo(username, password string) (*model.BaseSysUser, error) {
|
||||||
|
// 创建带 Cookie 存储的 HTTP 客户端
|
||||||
|
jar, err := cookiejar.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建 CookieJar 失败: %w", err)
|
||||||
|
}
|
||||||
|
client := &http.Client{Jar: jar}
|
||||||
|
|
||||||
|
// 1. POST 获取 token
|
||||||
|
tokenURL := bbsurl + "/api/token"
|
||||||
|
formData := url.Values{}
|
||||||
|
formData.Set("identification", username)
|
||||||
|
formData.Set("password", password)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(formData.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("发送请求失败: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取响应失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 token
|
||||||
|
var tokenResp TokenResponse
|
||||||
|
err = json.Unmarshal(body, &tokenResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析 token 失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取 CSRF Token
|
||||||
|
csrfToken := resp.Header.Get("X-CSRF-Token")
|
||||||
|
|
||||||
|
// 2. GET 获取该用户的详细信息
|
||||||
|
usersURL := bbsurl + fmt.Sprintf("/api/users/%d", tokenResp.UserID)
|
||||||
|
req2, err := http.NewRequest("GET", usersURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||||
|
}
|
||||||
|
req2.Header.Set("Authentication", tokenResp.Token)
|
||||||
|
req2.Header.Set("X-CSRF-Token", csrfToken)
|
||||||
|
|
||||||
|
resp2, err := client.Do(req2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("发送请求失败: %w", err)
|
||||||
|
}
|
||||||
|
defer resp2.Body.Close()
|
||||||
|
|
||||||
|
body2, err := io.ReadAll(resp2.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取响应失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析用户信息
|
||||||
|
var userResp UserResponse
|
||||||
|
err = json.Unmarshal(body2, &userResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析用户信息失败: %w", err)
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(userResp.Data.Attributes.Username, username) { //说明没查找到用户
|
||||||
|
|
||||||
|
return nil, gerror.New("账户或密码不正确~")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var user *model.BaseSysUser
|
||||||
|
m := cool.DBM(s.Model).Where("username=?", username)
|
||||||
|
m.Scan(&user)
|
||||||
|
if user == nil {
|
||||||
|
|
||||||
|
s.Gen(userResp.Data.Attributes)
|
||||||
|
m := cool.DBM(s.Model).Where("username=?", username)
|
||||||
|
m.Scan(&user)
|
||||||
|
} else {
|
||||||
|
if *user.Status == 0 {
|
||||||
|
|
||||||
|
err = gerror.New("账户被封禁~")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user.HeadImg = &userResp.Data.Attributes.AvatarUrl
|
||||||
|
var ttt = *user.PasswordV + 1
|
||||||
|
user.PasswordV = &ttt
|
||||||
|
_, err := m.Save(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cool.CacheManager.Set(context.Background(), fmt.Sprintf("admin:passwordVersion:%d", user.ID), ttt, 0)
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceAdd 方法 添加用户
|
// ServiceAdd 方法 添加用户
|
||||||
func (s *BaseSysUserService) ServiceAdd(ctx context.Context, req *cool.AddReq) (data interface{}, err error) {
|
func (s *BaseSysUserService) ServiceAdd(ctx context.Context, req *cool.AddReq) (data interface{}, err error) {
|
||||||
var (
|
var (
|
||||||
|
|||||||
Reference in New Issue
Block a user