Files
qitongxue-api/internal/logic/admin_account.go
2026-09-29 10:57:01 +08:00

194 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"strconv"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"golang.org/x/crypto/bcrypt"
v1 "tool-api/api/admin/v1"
"tool-api/internal/consts"
)
// ============================================================================
// 管理员账号管理(T17)
// ============================================================================
// AdminList 管理员列表
func AdminList(ctx context.Context) (*v1.AdminListRes, error) {
records, err := g.Model(consts.TableAdminUsers).OrderAsc("id").All()
if err != nil {
return nil, err
}
list := make([]v1.AdminItem, 0, len(records))
for _, r := range records {
list = append(list, v1.AdminItem{
Id: r["id"].Int64(),
Account: r["account"].String(),
NickName: r["nick_name"].String(),
RoleName: r["role_name"].String(),
RoleValue: r["role_value"].String(),
Status: r["status"].Int(),
CreatedAt: r["created_at"].String(),
})
}
return &v1.AdminListRes{List: list}, nil
}
// lastSuperGuard 保护「最后一个启用中的超级管理员」:
// 若该管理员当前是启用中的超管,而改动后会失去「启用中的超管」身份(降角色或停用),
// 且库中仅剩这一个,则拒绝——避免把自己锁在门外。
func lastSuperGuard(ctx context.Context, id int64, newRole string, newStatus int) error {
rec, err := g.Model(consts.TableAdminUsers).Where("id", id).One()
if err != nil {
return err
}
if rec.IsEmpty() {
return nil
}
isEnabledSuper := rec["role_value"].String() == consts.AdminRoleSuper &&
rec["status"].Int() == consts.AdminStatusEnabled
if !isEnabledSuper {
return nil
}
stillSuper := newRole == consts.AdminRoleSuper && newStatus == consts.AdminStatusEnabled
if stillSuper {
return nil
}
count, err := g.Model(consts.TableAdminUsers).
Where("role_value", consts.AdminRoleSuper).
Where("status", consts.AdminStatusEnabled).
Count()
if err != nil {
return err
}
if count <= 1 {
return gerror.New("至少保留一个启用中的超级管理员")
}
return nil
}
// AdminSave 新增/修改管理员。编辑不清空密码(除非显式传入新密码)。
func AdminSave(ctx context.Context, req *v1.AdminSaveReq) (*v1.AdminSaveRes, error) {
if req.Id > 0 {
rec, err := g.Model(consts.TableAdminUsers).Where("id", req.Id).One()
if err != nil {
return nil, err
}
if rec.IsEmpty() {
return nil, gerror.New("管理员不存在")
}
if err = lastSuperGuard(ctx, req.Id, req.RoleValue, rec["status"].Int()); err != nil {
return nil, err
}
data := g.Map{
"nick_name": req.NickName,
"role_name": roleNameOf(req.RoleValue),
"role_value": req.RoleValue,
}
pwdChanged := strings.TrimSpace(req.Password) != ""
if pwdChanged {
hash, herr := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if herr != nil {
return nil, herr
}
data["password"] = string(hash)
}
if _, err = g.Model(consts.TableAdminUsers).Where("id", req.Id).Data(data).Update(); err != nil {
return nil, err
}
WriteAudit(ctx, AuditEntry{
Action: "admin.update",
TargetType: "admin",
TargetId: strconv.FormatInt(req.Id, 10),
Before: g.Map{
"nick_name": rec["nick_name"].String(),
"role_value": rec["role_value"].String(),
},
After: g.Map{
"nick_name": req.NickName,
"role_value": req.RoleValue,
"pwd_changed": pwdChanged,
},
Result: consts.AuditResultSuccess,
})
return &v1.AdminSaveRes{}, nil
}
account := strings.TrimSpace(req.Account)
if account == "" {
return nil, gerror.New("请填写登录账号")
}
if strings.TrimSpace(req.Password) == "" {
return nil, gerror.New("请填写登录密码")
}
count, err := g.Model(consts.TableAdminUsers).Where("account", account).Count()
if err != nil {
return nil, err
}
if count > 0 {
return nil, gerror.New("账号已存在")
}
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
newId, err := g.Model(consts.TableAdminUsers).Data(g.Map{
"account": account,
"password": string(hash),
"nick_name": req.NickName,
"role_name": roleNameOf(req.RoleValue),
"role_value": req.RoleValue,
"status": consts.AdminStatusEnabled,
"created_at": gtime.Now(),
}).InsertAndGetId()
if err != nil {
return nil, err
}
WriteAudit(ctx, AuditEntry{
Action: "admin.create",
TargetType: "admin",
TargetId: strconv.FormatInt(newId, 10),
After: g.Map{
"account": account,
"nick_name": req.NickName,
"role_value": req.RoleValue,
},
Result: consts.AuditResultSuccess,
})
return &v1.AdminSaveRes{}, nil
}
// AdminStatus 启停管理员
func AdminStatus(ctx context.Context, id int64, status int) (*v1.AdminStatusRes, error) {
if status != consts.AdminStatusEnabled && status != consts.AdminStatusDisabled {
return nil, gerror.New("状态非法")
}
rec, err := g.Model(consts.TableAdminUsers).Where("id", id).One()
if err != nil {
return nil, err
}
if rec.IsEmpty() {
return nil, gerror.New("管理员不存在")
}
if err = lastSuperGuard(ctx, id, rec["role_value"].String(), status); err != nil {
return nil, err
}
if _, err = g.Model(consts.TableAdminUsers).Where("id", id).Data(g.Map{"status": status}).Update(); err != nil {
return nil, err
}
WriteAudit(ctx, AuditEntry{
Action: "admin.status",
TargetType: "admin",
TargetId: strconv.FormatInt(id, 10),
Before: g.Map{"status": rec["status"].Int()},
After: g.Map{"status": status},
Result: consts.AuditResultSuccess,
})
return &v1.AdminStatusRes{}, nil
}