feat(user): 添加QQ绑定功能并重构用户登录逻辑 - 在BaseSysUser模型中添加QQ字段,移除密码字段(暂时注释) - 移除base.bbs.go中的GetUserInfo函数,将其迁移至base_sys_user.go - 将登录服务中的外部API调用逻辑整合到BaseSysUserService - 新增BindQQ方法实现QQ号绑定功能,包含重复绑定检查 - 更新GetUserInfo方法,完善用户信息获取和同步逻辑 - 优化导入包,移除未使用的依赖项 ```
543 lines
15 KiB
Go
543 lines
15 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"net/http/cookiejar"
|
||
"net/url"
|
||
"strings"
|
||
|
||
"blazing/cool"
|
||
|
||
"blazing/modules/base/model"
|
||
|
||
"github.com/gogf/gf/v2/container/garray"
|
||
"github.com/gogf/gf/v2/container/gset"
|
||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
)
|
||
|
||
type BaseSysUserService struct {
|
||
*cool.Service
|
||
}
|
||
|
||
// Person 方法 返回不带密码的用户信息
|
||
func (s *BaseSysUserService) Person(userId uint) (res gdb.Record, err error) {
|
||
m := cool.DBM(s.Model)
|
||
res, err = m.Where("id", userId).FieldsEx("password").One()
|
||
return
|
||
}
|
||
func (s *BaseSysUserService) GetSession(email string, password string) (res *model.BaseSysUser, err error) {
|
||
m := cool.DBM(s.Model)
|
||
|
||
m.Where("email", email).Where("password", password).Where("status", 1).Scan(&res)
|
||
if res == nil {
|
||
err = gerror.New("账户或密码不正确~")
|
||
return
|
||
}
|
||
|
||
return
|
||
}
|
||
func (s *BaseSysUserService) GetPerson(userId uint32) (res *model.BaseSysUser) {
|
||
m := cool.DBM(s.Model)
|
||
m.Where("id", userId).FieldsEx("password").Scan(&res)
|
||
|
||
return
|
||
}
|
||
func (s *BaseSysUserService) SetdepartmentId(userId, departmentId uint32) (res *model.BaseSysUser) {
|
||
m := cool.DBM(s.Model)
|
||
m.Where("id", userId).Data("departmentId", departmentId).Update()
|
||
|
||
return
|
||
}
|
||
func (s *BaseSysUserService) DuihuanFreeGold(userId uint32, gold, free int64) {
|
||
m := cool.DBM(s.Model).Where("id", userId)
|
||
m.Data(g.Map{
|
||
"goldbean": gdb.Raw("goldbean-" + gconv.String(gold)),
|
||
"free_gold": gdb.Raw("free_gold+" + gconv.String(free)),
|
||
}).Update()
|
||
|
||
}
|
||
func (s *BaseSysUserService) DuihuanGold(userId uint32, gold, free int64) {
|
||
m := cool.DBM(s.Model).Where("id", userId)
|
||
m.Data(g.Map{
|
||
"goldbean": gdb.Raw("goldbean+" + gconv.String(gold)),
|
||
"free_gold": gdb.Raw("free_gold-" + gconv.String(free)),
|
||
}).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) {
|
||
|
||
m := cool.DBM(s.Model).Where("id", userId)
|
||
m.Increment("free_gold", gold)
|
||
|
||
}
|
||
|
||
// 单位是分
|
||
func (s *BaseSysUserService) UpdateGold(userId uint32, gold int64) {
|
||
|
||
if cool.Config.ServerInfo.IsVip != 0 {
|
||
cool.Logger.Info(context.TODO(), "测试服不操作金币")
|
||
return
|
||
}
|
||
m := cool.DBM(s.Model).Where("id", userId)
|
||
m.Increment("goldbean", gold)
|
||
|
||
}
|
||
func (s *BaseSysUserService) GetGold(userId uint) (res int64) {
|
||
var res1 model.BaseSysUser
|
||
m := cool.DBM(s.Model)
|
||
m.Where("id", userId).Fields("goldbean").Scan(&res1)
|
||
|
||
return res1.GoldBean
|
||
}
|
||
func (s *BaseSysUserService) GetFreeGold(userId uint) (res int64) {
|
||
var res1 model.BaseSysUser
|
||
m := cool.DBM(s.Model)
|
||
m.Where("id", userId).Fields("free_gold").Scan(&res1)
|
||
|
||
return res1.FreeGold
|
||
}
|
||
func (s *BaseSysUserService) GetEamil(userId string) (res *model.BaseSysUser) {
|
||
m := cool.DBM(s.Model)
|
||
m.Where("email", userId).FieldsEx("password").Scan(&res)
|
||
|
||
return
|
||
}
|
||
func (s *BaseSysUserService) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
|
||
if method == "Delete" {
|
||
// 禁止删除超级管理员
|
||
userIds := garray.NewIntArrayFrom(gconv.Ints(param["ids"]))
|
||
currentId, found := userIds.Get(0)
|
||
superAdminId := 10001
|
||
|
||
if userIds.Len() == 1 && found && currentId == superAdminId {
|
||
err = gerror.New("超级管理员不能删除")
|
||
return
|
||
}
|
||
|
||
// 删除超级管理员
|
||
userIds.RemoveValue(10001)
|
||
g.RequestFromCtx(ctx).SetParam("ids", userIds.Slice())
|
||
}
|
||
return
|
||
}
|
||
|
||
func (s *BaseSysUserService) ModifyAfter(ctx context.Context, method string, param g.MapStrAny) (err error) {
|
||
if method == "Delete" {
|
||
userIds := garray.NewIntArrayFrom(gconv.Ints(param["ids"]))
|
||
userIds.RemoveValue(1)
|
||
// 删除用户时删除相关数据
|
||
cool.DBM(model.NewBaseSysUserRole()).WhereIn("userId", userIds.Slice()).Delete()
|
||
}
|
||
return
|
||
}
|
||
func (s *BaseSysUserService) Gen(user UserAttributes) (data interface{}, err error) {
|
||
var (
|
||
m = cool.DBM(s.Model)
|
||
)
|
||
|
||
lastInsertId, err := m.Data(user).Data(
|
||
|
||
g.Map{
|
||
"username": strings.ToLower(user.Username),
|
||
"headImg": user.AvatarUrl,
|
||
"departmentId": 1,
|
||
},
|
||
).InsertAndGetId()
|
||
if err != nil {
|
||
return
|
||
}
|
||
data = g.Map{"id": lastInsertId}
|
||
roleArray := garray.NewArray()
|
||
roleArray.PushRight(g.Map{
|
||
"userId": gconv.Uint(lastInsertId),
|
||
"roleId": gconv.Uint(13),
|
||
})
|
||
|
||
_, err = cool.DBM(model.NewBaseSysUserRole()).Fields("userId,roleId").Insert(roleArray)
|
||
|
||
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 方法 添加用户
|
||
func (s *BaseSysUserService) ServiceAdd(ctx context.Context, req *cool.AddReq) (data interface{}, err error) {
|
||
var (
|
||
m = cool.DBM(s.Model)
|
||
r = g.RequestFromCtx(ctx)
|
||
reqmap = r.GetMap()
|
||
)
|
||
|
||
// 如果reqmap["password"]不为空,则对密码进行md5加密
|
||
if !r.Get("password").IsNil() {
|
||
reqmap["password"] = gmd5.MustEncryptString(r.Get("password").String())
|
||
}
|
||
if !r.Get("username").IsNil() {
|
||
reqmap["username"] = strings.ToLower(r.Get("username").String())
|
||
}
|
||
if s.UniqueKey != nil {
|
||
for k, v := range s.UniqueKey {
|
||
if reqmap[k] != nil {
|
||
count, err := cool.DBM(s.Model).Where(k, reqmap[k]).Count()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if count > 0 {
|
||
err = gerror.New(v)
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
}
|
||
lastInsertId, err := m.Data(reqmap).InsertAndGetId()
|
||
if err != nil {
|
||
return
|
||
}
|
||
data = g.Map{"id": lastInsertId}
|
||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||
|
||
if !r.Get("roleIdList").IsNil() {
|
||
inRoleIdSet := gset.NewFrom(r.Get("roleIdList").Ints())
|
||
roleArray := garray.NewArray()
|
||
inRoleIdSet.Iterator(func(v interface{}) bool {
|
||
roleArray.PushRight(g.Map{
|
||
"userId": gconv.Uint(lastInsertId),
|
||
"roleId": gconv.Uint(v),
|
||
})
|
||
return true
|
||
})
|
||
|
||
_, err = cool.DBM(model.NewBaseSysUserRole()).Fields("userId,roleId").Insert(roleArray)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return
|
||
})
|
||
return
|
||
}
|
||
|
||
// ServiceInfo 方法 返回服务信息
|
||
func (s *BaseSysUserService) ServiceInfo(ctx g.Ctx, req *cool.InfoReq) (data interface{}, err error) {
|
||
result, err := s.Service.ServiceInfo(ctx, req)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if result.(gdb.Record).IsEmpty() {
|
||
return nil, nil
|
||
}
|
||
// g.DumpWithType(result)
|
||
resultMap := result.(gdb.Record).Map()
|
||
|
||
// 获取角色
|
||
roleIds, err := cool.DBM(model.NewBaseSysUserRole()).Where("userId", resultMap["id"]).Fields("roleId").Array()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
resultMap["roleIdList"] = roleIds
|
||
data = resultMap
|
||
|
||
return
|
||
}
|
||
|
||
// ServiceUpdate 方法 更新用户信息
|
||
func (s *BaseSysUserService) ServiceUpdate(ctx context.Context, req *cool.UpdateReq) (data sql.Result, err error) {
|
||
var (
|
||
admin = cool.GetAdmin(ctx)
|
||
m = cool.DBM(s.Model)
|
||
)
|
||
|
||
r := g.RequestFromCtx(ctx)
|
||
rMap := r.GetMap()
|
||
if !r.Get("username").IsNil() {
|
||
rMap["username"] = strings.ToLower(r.Get("username").String())
|
||
}
|
||
// 如果不传入ID代表更新当前用户
|
||
userId := r.Get("id", admin.UserId).Uint()
|
||
userInfo, err := m.Where("id", userId).One()
|
||
|
||
if err != nil {
|
||
return
|
||
}
|
||
if userInfo.IsEmpty() {
|
||
err = gerror.New("用户不存在")
|
||
return
|
||
}
|
||
|
||
// 禁止禁用超级管理员
|
||
if userId == 10001 && (!r.Get("status").IsNil() && r.Get("status").Int() == 0) {
|
||
err = gerror.New("禁止禁用超级管理员")
|
||
return
|
||
}
|
||
if s.UniqueKey != nil {
|
||
for k, v := range s.UniqueKey {
|
||
if rMap[k] != nil {
|
||
count, err := cool.DBM(s.Model).Where(k, rMap[k]).WhereNot("id", userId).Count()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if count > 0 {
|
||
err = gerror.New(v)
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 如果请求的password不为空并且密码加密后的值有变动,说明要修改密码
|
||
var rPassword = r.Get("password", "").String()
|
||
if rPassword != "" && rPassword != userInfo["password"].String() {
|
||
rMap["password"], _ = gmd5.Encrypt(rPassword)
|
||
rMap["passwordV"] = userInfo["passwordV"].Int() + 1
|
||
cool.CacheManager.Set(ctx, fmt.Sprintf("admin:passwordVersion:%d", userId), rMap["passwordV"], 0)
|
||
} else {
|
||
delete(rMap, "password")
|
||
}
|
||
delete(rMap, "goldbean")
|
||
|
||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||
roleModel := cool.DBM(model.NewBaseSysUserRole()).TX(tx).Where("userId", userId)
|
||
roleIds, err := roleModel.Fields("roleId").Array()
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
// 如果请求参数中不包含roleIdList说明不修改角色信息
|
||
if !r.Get("roleIdList").IsNil() {
|
||
inRoleIdSet := gset.NewFrom(r.Get("roleIdList").Ints())
|
||
roleIdsSet := gset.NewFrom(gconv.Ints(roleIds))
|
||
|
||
// 如果请求的角色信息未发生变化则跳过更新逻辑
|
||
if roleIdsSet.Diff(inRoleIdSet).Size() != 0 || inRoleIdSet.Diff(roleIdsSet).Size() != 0 {
|
||
roleArray := garray.NewArray()
|
||
inRoleIdSet.Iterator(func(v interface{}) bool {
|
||
roleArray.PushRight(g.Map{
|
||
"userId": gconv.Uint(userId),
|
||
"roleId": gconv.Uint(v),
|
||
})
|
||
return true
|
||
})
|
||
|
||
_, err = roleModel.Delete()
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
_, err = roleModel.Fields("userId,roleId").Insert(roleArray)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
|
||
_, err = m.TX(tx).Update(rMap)
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return
|
||
})
|
||
return
|
||
}
|
||
|
||
// Move 移动用户部门
|
||
func (s *BaseSysUserService) Move(ctx g.Ctx) (err error) {
|
||
request := g.RequestFromCtx(ctx)
|
||
departmentId := request.Get("departmentId").Int()
|
||
userIds := request.Get("userIds").Slice()
|
||
|
||
_, err = cool.DBM(s.Model).Where("id IN(?)", userIds).Data(g.Map{"departmentId": departmentId}).Update()
|
||
|
||
return
|
||
}
|
||
|
||
// NewBaseSysUserService 创建一个新的BaseSysUserService实例
|
||
func NewBaseSysUserService() *BaseSysUserService {
|
||
return &BaseSysUserService{
|
||
Service: &cool.Service{
|
||
Model: model.NewBaseSysUser(),
|
||
InfoIgnoreProperty: "password",
|
||
UniqueKey: map[string]string{
|
||
"username": "用户名不能重复",
|
||
"email": "邮箱不能重复",
|
||
},
|
||
PageQueryOp: &cool.QueryOp{
|
||
Select: `
|
||
base_sys_user.*,
|
||
STRING_AGG(role.name, ', ') AS roleName`,
|
||
Join: []*cool.JoinOp{
|
||
{
|
||
Model: model.NewBaseSysDepartment(),
|
||
Alias: "dept",
|
||
Type: "LeftJoin",
|
||
Condition: `base_sys_user."departmentId" = dept.id`,
|
||
},
|
||
{
|
||
Model: model.NewBaseSysUserRole(),
|
||
Alias: "user_role",
|
||
Type: "LeftJoin",
|
||
Condition: `base_sys_user.id = user_role."userId"`,
|
||
},
|
||
{
|
||
Model: model.NewBaseSysRole(),
|
||
Alias: "role",
|
||
Type: "LeftJoin",
|
||
Condition: `role.id = user_role."roleId"`,
|
||
},
|
||
},
|
||
Where: func(ctx context.Context) []g.Array {
|
||
r := g.RequestFromCtx(ctx).GetMap()
|
||
return []g.Array{
|
||
{"id != ?", g.Slice{"10001"}, true}, //排除管理员
|
||
{`("departmentId" IN (?))`, gconv.SliceStr(r["departmentIds"])},
|
||
}
|
||
},
|
||
Extend: func(ctx g.Ctx, m *gdb.Model) *gdb.Model {
|
||
return m.Group(`base_sys_user.id`)
|
||
},
|
||
KeyWordField: []string{"username", "email"},
|
||
FieldEQ: []string{"id"},
|
||
},
|
||
},
|
||
}
|
||
}
|