feat: add user role menu management api

This commit is contained in:
yangshangbo
2025-07-16 18:09:52 +08:00
parent 441a8f114d
commit 38a55c41ab
5 changed files with 1569 additions and 0 deletions
+404
View File
@@ -0,0 +1,404 @@
module hono
import crypto.sha256
import crypto.rand
import time
import json
// 用户角色枚举
pub enum UserRole {
admin
manager
user
guest
}
// 用户结构
pub struct User {
pub:
id int
username string
email string
password_hash string
role UserRole
status bool // true: active, false: inactive
created_at int
updated_at int
}
// 菜单项结构
pub struct MenuItem {
pub:
id int
name string
path string
icon string
parent_id int
sort_order int
permissions []string
status bool
created_at int
updated_at int
mut:
children []MenuItem
}
// 用户会话结构
pub struct UserSession {
pub:
user_id int
token string
expires_at int
created_at int
}
// 认证管理器
pub struct AuthManager {
mut:
db DatabaseManager
}
// 创建认证管理器
pub fn new_auth_manager(db DatabaseManager) AuthManager {
return AuthManager{
db: db
}
}
// 初始化认证相关表
pub fn (mut auth AuthManager) init_tables() ! {
// 创建用户表
auth.db.db.exec('CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT NOT NULL,
status BOOLEAN DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);') or {
return error('Failed to create users table: $err')
}
// 创建菜单表
auth.db.db.exec('CREATE TABLE IF NOT EXISTS menu_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
path TEXT NOT NULL,
icon TEXT,
parent_id INTEGER DEFAULT 0,
sort_order INTEGER DEFAULT 0,
permissions TEXT,
status BOOLEAN DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);') or {
return error('Failed to create menu_items table: $err')
}
// 创建用户会话表
auth.db.db.exec('CREATE TABLE IF NOT EXISTS user_sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at INTEGER NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
);') or {
return error('Failed to create user_sessions table: $err')
}
// 创建索引
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);') or {
return error('Failed to create index: $err')
}
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);') or {
return error('Failed to create index: $err')
}
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_menu_parent ON menu_items(parent_id);') or {
return error('Failed to create index: $err')
}
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_token ON user_sessions(token);') or {
return error('Failed to create index: $err')
}
}
// 密码哈希
fn hash_password(password string) string {
return sha256.sum(password.bytes()).hex()
}
// 生成随机令牌
fn generate_token() string {
random_bytes := rand.bytes(32) or { return '' }
mut token := ''
for byte in random_bytes {
token += '${byte:02x}'
}
return token
}
// 创建用户
pub fn (mut auth AuthManager) create_user(username string, email string, password string, role UserRole) !User {
now := int(time.now().unix())
password_hash := hash_password(password)
role_str := role.str()
auth.db.db.exec('INSERT INTO users (username, email, password_hash, role, status, created_at, updated_at) VALUES ("$username", "$email", "$password_hash", "$role_str", 1, $now, $now)') or {
return error('Failed to create user: $err')
}
// 获取插入的用户ID
rows := auth.db.db.exec('SELECT last_insert_rowid()') or {
return error('Failed to get user ID: $err')
}
user_id := rows[0].vals[0].int()
return User{
id: user_id
username: username
email: email
password_hash: password_hash
role: role
status: true
created_at: now
updated_at: now
}
}
// 用户登录
pub fn (mut auth AuthManager) login(username string, password string) !UserSession {
password_hash := hash_password(password)
rows := auth.db.db.exec('SELECT id, username, email, password_hash, role, status FROM users WHERE username = "$username" AND password_hash = "$password_hash" AND status = 1') or {
return error('Failed to query user: $err')
}
if rows.len == 0 {
return error('Invalid username or password')
}
user := rows[0]
user_id := user.vals[0].int()
// 生成会话令牌
token := generate_token()
now := int(time.now().unix())
expires_at := now + (24 * 60 * 60) // 24小时后过期
// 保存会话
auth.db.db.exec('INSERT INTO user_sessions (user_id, token, expires_at, created_at) VALUES ($user_id, "$token", $expires_at, $now)') or {
return error('Failed to create session: $err')
}
return UserSession{
user_id: user_id
token: token
expires_at: expires_at
created_at: now
}
}
// 验证令牌
pub fn (auth AuthManager) verify_token(token string) !User {
rows := auth.db.db.exec('SELECT u.id, u.username, u.email, u.password_hash, u.role, u.status FROM users u JOIN user_sessions s ON u.id = s.user_id WHERE s.token = "$token" AND s.expires_at > ${int(time.now().unix())} AND u.status = 1') or {
return error('Failed to verify token: $err')
}
if rows.len == 0 {
return error('Invalid or expired token')
}
user := rows[0]
role_str := user.vals[4]
role := match role_str {
'admin' { UserRole.admin }
'manager' { UserRole.manager }
'user' { UserRole.user }
'guest' { UserRole.guest }
else { UserRole.guest }
}
return User{
id: user.vals[0].int()
username: user.vals[1]
email: user.vals[2]
password_hash: user.vals[3]
role: role
status: user.vals[5].int() == 1
created_at: 0
updated_at: 0
}
}
// 注销会话
pub fn (mut auth AuthManager) logout(token string) ! {
auth.db.db.exec('DELETE FROM user_sessions WHERE token = "$token"') or {
return error('Failed to logout: $err')
}
}
// 创建菜单项
pub fn (mut auth AuthManager) create_menu_item(name string, path string, icon string, parent_id int, sort_order int, permissions []string) !MenuItem {
now := int(time.now().unix())
permissions_json := json.encode(permissions)
permissions_sql := permissions_json.replace('"', "''")
auth.db.db.exec('INSERT INTO menu_items (name, path, icon, parent_id, sort_order, permissions, status, created_at, updated_at) VALUES ("$name", "$path", "$icon", $parent_id, $sort_order, "$permissions_sql", 1, $now, $now)')
or {
eprintln('菜单插入SQL错误: $err')
return error('Failed to create menu item: $err')
}
// 获取插入的菜单ID
rows := auth.db.db.exec('SELECT last_insert_rowid()') or {
return error('Failed to get menu ID: $err')
}
menu_id := rows[0].vals[0].int()
return MenuItem{
id: menu_id
name: name
path: path
icon: icon
parent_id: parent_id
sort_order: sort_order
permissions: permissions
status: true
created_at: now
updated_at: now
}
}
// 获取所有菜单项
pub fn (auth AuthManager) get_all_menu_items() ![]MenuItem {
rows := auth.db.db.exec('SELECT id, name, path, icon, parent_id, sort_order, permissions, status FROM menu_items ORDER BY parent_id, sort_order') or {
return error('Failed to query all menu items: $err')
}
mut menus := []MenuItem{}
for row in rows {
permissions_str := row.vals[6]
permissions := json.decode([]string, permissions_str) or { []string{} }
menus << MenuItem{
id: row.vals[0].int()
name: row.vals[1]
path: row.vals[2]
icon: row.vals[3]
parent_id: row.vals[4].int()
sort_order: row.vals[5].int()
permissions: permissions
status: row.vals[7].int() == 1
created_at: 0
updated_at: 0
}
}
return menus
}
// 获取用户菜单
pub fn (auth AuthManager) get_user_menus(user User) ![]MenuItem {
// 根据用户角色获取菜单
role_permissions := match user.role {
.admin { ['*'] } // 管理员拥有所有权限
.manager { ['read', 'write', 'manage'] }
.user { ['read', 'write'] }
.guest { ['read'] }
}
// 构建权限查询条件
mut permission_conditions := []string{}
for permission in role_permissions {
if permission == '*' {
permission_conditions << 'permissions LIKE "%*%"'
} else {
permission_conditions << 'permissions LIKE "%$permission%"'
}
}
permission_where := permission_conditions.join(' OR ')
rows := auth.db.db.exec('SELECT id, name, path, icon, parent_id, sort_order, permissions, status FROM menu_items WHERE status = 1 AND ($permission_where) ORDER BY parent_id, sort_order') or {
return error('Failed to query menu items: $err')
}
mut menus := []MenuItem{}
for row in rows {
permissions_str := row.vals[6]
permissions := json.decode([]string, permissions_str) or { []string{} }
menus << MenuItem{
id: row.vals[0].int()
name: row.vals[1]
path: row.vals[2]
icon: row.vals[3]
parent_id: row.vals[4].int()
sort_order: row.vals[5].int()
permissions: permissions
status: row.vals[7].int() == 1
created_at: 0
updated_at: 0
}
}
return menus
}
// 构建菜单树
pub fn (auth AuthManager) build_menu_tree(menus []MenuItem) []MenuItem {
mut menu_map := map[int]MenuItem{}
mut root_menus := []MenuItem{}
// 创建菜单映射
for menu in menus {
menu_map[menu.id] = menu
}
// 构建树结构
for menu in menus {
if menu.parent_id == 0 {
// 根菜单
mut root_menu := menu
root_menu.children = auth.get_children(menu.id, menu_map)
root_menus << root_menu
}
}
return root_menus
}
// 获取子菜单
fn (auth AuthManager) get_children(parent_id int, menu_map map[int]MenuItem) []MenuItem {
mut children := []MenuItem{}
for _, menu in menu_map {
if menu.parent_id == parent_id {
mut child := menu
child.children = auth.get_children(menu.id, menu_map)
children << child
}
}
return children
}
// 检查用户权限
pub fn (auth AuthManager) check_permission(user User, required_permission string) bool {
return match user.role {
.admin { true } // 管理员拥有所有权限
.manager { required_permission in ['read', 'write', 'manage'] }
.user { required_permission in ['read', 'write'] }
.guest { required_permission == 'read' }
}
}
// 获取菜单id(通过path)
pub fn (auth AuthManager) get_menu_id_by_path(path string) int {
rows := auth.db.db.exec('SELECT id FROM menu_items WHERE path = "$path"') or { return 0 }
if rows.len > 0 {
return rows[0].vals[0].int()
}
return 0
}