feat: add user role menu management api
This commit is contained in:
+404
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user