admin_backend/model/admin.go
2026-05-07 14:39:24 +08:00

324 lines
10 KiB
Go
Raw Permalink 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 model
import (
"backend/store"
"backend/util"
"fmt"
"strings"
"github.com/gin-gonic/gin"
)
type Admin struct {
ID int `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Password string `json:"password" db:"password"`
RealName string `json:"real_name" db:"real_name"`
Nickname string `json:"nickname" db:"nickname"`
Phone string `json:"phone" db:"phone"`
Email string `json:"email" db:"email"`
Token string `json:"token" db:"token"`
Group string `json:"group" db:"group"`
Status int `json:"status" db:"status"`
Expires int `json:"expires" db:"expires"`
Role int `json:"role" db:"role"`
LastLoginTime int `json:"lastLoginTime" db:"lastLoginTime"`
LastLoginIP string `json:"lastLoginIp" db:"lastLoginIp"`
CreateTime int `json:"createTime" db:"createTime"`
UpdateTime int `json:"updateTime" db:"updateTime"`
Remark string `json:"remark" db:"remark"`
}
type AdminLog struct {
ID int `json:"id" db:"id"`
Admin string `json:"admin" db:"admin"`
Action string `json:"action" db:"action"`
Params string `json:"params" db:"params"`
IP string `json:"ip" db:"ip"`
CreateTime int `json:"createTime" db:"createTime"`
}
func (a *Admin) List() (*Result, error) {
var admins []Admin
db := util.MPool.GetGameDB()
defer db.Close()
if db == nil {
return nil, fmt.Errorf("failed to get database connection")
}
err := db.Select(&admins, "SELECT `id`,`username`,`real_name`,`nickname`,`phone`,`email`,`group`,`status`,`role`,`lastLoginTime`,`lastLoginIp`,`createTime`,`updateTime`,`remark` FROM admin ORDER BY id DESC")
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
var total int
db.Get(&total, "SELECT COUNT(*) FROM admin")
return &Result{
Data: admins,
Total: total,
}, nil
}
func (a *Admin) Add() (*Result, error) {
// 这里可以添加逻辑来保存管理员信息到数据库
// 假设我们将管理员信息插入到数据库中
db := util.MPool.GetGameDB() // 假设使用默认的AppConfig和ServerId为0
defer db.Close()
if db == nil {
return nil, fmt.Errorf("failed to get database connection")
}
defer db.Close()
a.CreateTime = int(util.Now())
a.UpdateTime = a.CreateTime
if a.Status == 0 {
a.Status = 1 // 默认启用
}
a.Password, _ = util.Encrypt(a.Password)
_, err := db.NamedExec("INSERT INTO admin (`username`, `password`, `real_name`, `nickname`, `phone`, `email`, `token`, `group`, `status`, `role`, `createTime`, `updateTime`, `expires`, `remark`) VALUES (:username, :password, :real_name, :nickname, :phone, :email, :token, :group, :status, :role, :createTime, :updateTime, :expires, :remark)", a)
if err != nil {
return nil, fmt.Errorf("failed to add admin: %v", err)
}
var total int
db.Get(&total, "SELECT COUNT(*) FROM admin")
return &Result{
Data: a,
Total: total,
}, nil
}
func (a *Admin) Login() (*Admin, error) {
// 这里可以添加逻辑来验证用户名和密码
// 假设我们从数据库中查询到管理员信息并返回
db := util.MPool.GetGameDB() // 假设使用默认的AppConfig和ServerId为0
defer db.Close()
if db == nil {
return nil, fmt.Errorf("failed to get database connection")
}
var admin Admin
err := db.Get(&admin, "SELECT * FROM admin WHERE username = ?", a.Username)
admin.Password, _ = util.Decrypt(admin.Password) // 假设有一个解密函数Login
if err != nil {
return nil, fmt.Errorf("账号不存在")
}
if admin.Password != a.Password {
return nil, fmt.Errorf("密码错误")
}
if (admin.Expires > 0 && admin.Expires < int(util.Now())) || admin.Token == "" || admin.Expires == 0 {
admin.CreateToken() // 如果令牌过期,重新生成令牌
}
return &Admin{
ID: admin.ID,
Username: admin.Username,
Token: admin.Token,
}, nil
}
func (a *Admin) LoginByCode(phone, code string) (*Admin, error) {
db := util.MPool.GetGameDB() // 假设使用默认的AppConfig和ServerId为0
defer db.Close()
if db == nil {
return nil, fmt.Errorf("failed to get database connection")
}
var admin Admin
err := db.Get(&admin, "SELECT * FROM admin WHERE phone = ?", phone)
if (admin.Expires > 0 && admin.Expires < int(util.Now())) || admin.Token == "" || admin.Expires == 0 {
admin.CreateToken() // 如果令牌过期,重新生成令牌
}
if err != nil {
return nil, fmt.Errorf("账号不存在")
}
if code == "" {
return nil, fmt.Errorf("验证码不能为空")
}
storedCode, exists := store.GetCode(phone)
if !exists || storedCode != code {
return nil, fmt.Errorf("验证码错误")
}
return &Admin{
ID: admin.ID,
Username: admin.Username,
Token: admin.Token,
}, nil
}
func (a *Admin) CreateToken() error {
// 这里可以添加逻辑来生成令牌
// 假设我们生成一个新的令牌并更新到数据库中
a.Token = util.GenerateToken() // 假设有一个生成令牌的函数
db := util.MPool.GetGameDB() // 假设使用默认的AppConfig和ServerId为0
defer db.Close()
if db == nil {
return fmt.Errorf("failed to get database connection")
}
a.Expires = int(util.Now()) + 604800
store.AddToken(a.Token, int64(a.Expires), a.Username, util.GetRole(a.Role)) // 添加到TokenList
_, err := db.NamedExec("UPDATE admin SET token = :token, expires = :expires WHERE id = :id", a)
if err != nil {
return fmt.Errorf("failed to update token: %v", err)
}
return nil
}
func (a *Admin) GetAdmin(username string) error {
db := util.MPool.GetGameDB() // 假设使用默认的AppConfig和ServerId为0
defer db.Close()
if db == nil {
return fmt.Errorf("failed to get database connection")
}
return db.Get(a, "SELECT `username`, `role`, `password`, `token`, `group`, `remark` FROM admin WHERE username = ?", username)
}
func InitToken() {
// 初始化TokenList可以从数据库中加载现有的令牌
db := util.MPool.GetGameDB() // 假设使用默认的AppConfig和ServerId为0
defer db.Close()
if db == nil {
return
}
var tokens []struct {
Token string `db:"token"`
Expires int64 `db:"expires"`
UserName string `db:"username"`
Role int `db:"role"` // 假设有一个角色字段
}
err := db.Select(&tokens, "SELECT username, token, expires, role FROM admin WHERE token IS NOT NULL")
if err != nil {
return // 处理错误
}
for _, t := range tokens {
store.AddToken(t.Token, t.Expires, t.UserName, util.GetRole(t.Role))
}
}
func (a *Admin) Edit() error {
db := util.MPool.GetGameDB()
defer db.Close()
if db == nil {
return fmt.Errorf("failed to get database connection")
}
a.UpdateTime = int(util.Now())
if a.Password != "" {
a.Password, _ = util.Encrypt(a.Password)
_, err := db.NamedExec("UPDATE admin SET `real_name`=:real_name, `nickname`=:nickname, `phone`=:phone, `email`=:email, `group`=:group, `role`=:role, `status`=:status, `remark`=:remark, `password`=:password, `updateTime`=:updateTime WHERE id=:id", a)
return err
}
_, err := db.NamedExec("UPDATE admin SET `real_name`=:real_name, `nickname`=:nickname, `phone`=:phone, `email`=:email, `group`=:group, `role`=:role, `status`=:status, `remark`=:remark, `updateTime`=:updateTime WHERE id=:id", a)
return err
}
func (a *Admin) Delete() error {
db := util.MPool.GetGameDB()
defer db.Close()
if db == nil {
return fmt.Errorf("failed to get database connection")
}
_, err := db.Exec("DELETE FROM admin WHERE id = ?", a.ID)
return err
}
func SendPhoneCode(phone string) error {
// 这里可以添加逻辑来发送验证码到手机
// 假设我们使用一个外部服务发送验证码
Code, err := util.GeneratedCode(phone) // 假设有一个发送短信的函数
store.AddCode(phone, Code) // 将验证码存储到CodeList
if err != nil {
return fmt.Errorf("failed to send code: %v", err)
}
return nil
}
// CheckUserPermission 基于 admin_permission 表进行动态 DB 鉴权。
// 鉴权优先级super 全放行 → 路径无受控权限点放行 →
// 用户直接权限(显式允许/拒绝)→ 用户组→角色→权限链路 → 默认拒绝。
func CheckUserPermission(username, path, method string) bool {
db := util.MPool.GetGameDB()
if db == nil {
return false
}
defer db.Close()
// 查询用户 ID 和角色
var adminInfo struct {
ID int `db:"id"`
Role int `db:"role"`
}
if err := db.Get(&adminInfo, "SELECT id, role FROM admin WHERE username = ?", username); err != nil {
return false
}
// super 超级管理员跳过所有权限检查
if util.GetRole(adminInfo.Role) == util.RoleSuper {
return true
}
// 用户通过用户组继承到 R_SUPER 角色时放行
var superRoleCount int
if err := db.Get(&superRoleCount,
`SELECT COUNT(*)
FROM admin_user_group_rel ugr
INNER JOIN admin_group_role_rel grr ON grr.group_id = ugr.group_id
INNER JOIN admin_role r ON r.id = grr.role_id
WHERE ugr.admin_id = ? AND r.role_code = 'R_SUPER' AND r.status = 1`,
adminInfo.ID,
); err == nil && superRoleCount > 0 {
return true
}
// 查找匹配路径的受控权限点http_method 为空视为不限方法)
var permIDs []int
if err := db.Select(&permIDs,
"SELECT id FROM admin_permission WHERE api_path = ? AND status = 1 AND (http_method = '' OR http_method = ?)",
path, method,
); err != nil || len(permIDs) == 0 {
// 路径未被任何权限点管控 → 放行
return true
}
// 检查用户直接权限(优先级最高)
for _, pid := range permIDs {
var grantType int
err := db.Get(&grantType,
"SELECT grant_type FROM admin_user_permission_rel WHERE admin_id = ? AND permission_id = ?",
adminInfo.ID, pid,
)
if err == nil {
if grantType == 2 {
return false // 显式拒绝
}
if grantType == 1 {
return true // 显式允许
}
}
}
// 检查 用户 → 用户组 → 角色 → 权限 链路
placeholders := make([]string, len(permIDs))
pidArgs := make([]interface{}, 1+len(permIDs))
pidArgs[0] = adminInfo.ID
for i, pid := range permIDs {
placeholders[i] = "?"
pidArgs[i+1] = pid
}
var count int
q := fmt.Sprintf(
`SELECT COUNT(*) FROM admin_role_permission_rel rpr
INNER JOIN admin_group_role_rel grr ON grr.role_id = rpr.role_id
INNER JOIN admin_user_group_rel ugr ON ugr.group_id = grr.group_id
WHERE ugr.admin_id = ? AND rpr.permission_id IN (%s)`,
strings.Join(placeholders, ","),
)
if err := db.Get(&count, q, pidArgs...); err == nil && count > 0 {
return true
}
return false
}
func GetAdminRole(c *gin.Context) (string, error) {
username := c.GetString("admin")
Admin := &Admin{}
err := Admin.GetAdmin(username)
if err != nil {
return "", err
}
return util.GetRole(Admin.Role), nil
}