# 三层架构实施指南 **创建时间**: 2026-10-12 **预计工作量**: 3-5 天 --- ## 🎯 实施目标 将现有的单层架构升级为三层架构: - **Admin Layer** - 系统管理 - **Group Layer** - 群组管理 - **User Layer** - 用户数据 --- ## 📋 实施步骤 ### Phase 1: 数据库准备 (1 天) #### 1.1 创建目录结构 ```bash cd /Users/accusys/momentry_studio # 创建新目录 mkdir -p data/admin data/groups data/users # 备份现有数据 mv data/users/demo.sqlite data/users/demo.sqlite.backup ``` --- #### 1.2 运行初始化脚本 ```bash # 初始化三层架构数据库 ./scripts/init_three_layer_db.sh ``` **输出**: - `data/admin/admin.sqlite` - Admin 数据库 - `data/groups/group_marketing.sqlite` - 示例群组 - `data/groups/group_development.sqlite` - 示例群组 - `data/users/demo.sqlite` - Demo 用户数据库 --- #### 1.3 验证初始化 ```bash # 检查三层架构 ./scripts/check_three_layer.sh ``` **预期输出**: ``` ✅ Admin Layer: 存在 ✅ Group Layer: 2 个群组 ✅ User Layer: 1 个用户 ``` --- ### Phase 2: 代码实现 (2-3 天) #### 2.1 修改 db.rs **添加新的路径函数**: ```rust // src-tauri/src/db.rs /// Admin 数据库路径 pub fn admin_db_path() -> PathBuf { let base = get_data_dir(); std::fs::create_dir_all(format!("{}/admin", base)).ok(); PathBuf::from(base).join("admin/admin.sqlite") } /// Group 数据库路径 pub fn group_db_path(group_id: &str) -> PathBuf { let base = get_data_dir(); std::fs::create_dir_all(format!("{}/groups", base)).ok(); PathBuf::from(base).join(format!("groups/group_{}.sqlite", group_id)) } /// User 数据库路径 pub fn user_db_path(username: &str) -> PathBuf { let base = get_data_dir(); std::fs::create_dir_all(format!("{}/users", base)).ok(); PathBuf::from(base).join(format!("users/{}.sqlite", username)) } fn get_data_dir() -> String { std::env::var("MOMENTRY_DATA_DIR") .unwrap_or_else(|_| { let home = dirs::home_dir() .expect("Failed to get home directory"); format!("{}/momentry_studio/data", home.display()) }) } ``` --- #### 2.2 创建 admin.rs **创建新文件**: `src-tauri/src/admin.rs` ```rust use rusqlite::Connection; use crate::db::admin_db_path; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct User { pub id: String, pub username: String, pub email: Option, pub display_name: Option, pub role: String, pub status: String, pub group_ids: Option>, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Group { pub id: String, pub name: String, pub description: Option, pub owner_id: String, pub members: Vec, pub status: String, } // Admin 数据库初始化 pub fn init_admin_db() -> Result<(), String> { let path = admin_db_path(); let conn = Connection::open(&path) .map_err(|e| format!("Failed to open admin db: {}", e))?; // 创建表 (参考架构文档) conn.execute_batch(include_str!("../sql/admin_tables.sql")) .map_err(|e| format!("Failed to create tables: {}", e))?; Ok(()) } // 获取系统配置 pub fn get_system_config(key: &str) -> Result, String> { let conn = Connection::open(admin_db_path()) .map_err(|e| format!("Failed to open db: {}", e))?; let value = conn.query_row( "SELECT value FROM system_config WHERE key = ?1", [key], |row| row.get(0) ).ok(); Ok(value) } // 设置系统配置 pub fn set_system_config(key: &str, value: &str, updated_by: &str) -> Result<(), String> { let conn = Connection::open(admin_db_path()) .map_err(|e| format!("Failed to open db: {}", e))?; conn.execute( "INSERT OR REPLACE INTO system_config (key, value, updated_by, updated_at) VALUES (?1, ?2, ?3, datetime('now'))", rusqlite::params![key, value, updated_by] ).map_err(|e| format!("Failed to set config: {}", e))?; Ok(()) } // 创建用户 pub fn create_user( id: &str, username: &str, password: &str, email: Option<&str>, role: &str ) -> Result<(), String> { let conn = Connection::open(admin_db_path()) .map_err(|e| format!("Failed to open db: {}", e))?; let password_hash = crate::db::hash_password(password); conn.execute( "INSERT INTO users (id, username, email, password_hash, role, status, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, 'active', datetime('now'), datetime('now'))", rusqlite::params![id, username, email, password_hash, role] ).map_err(|e| format!("Failed to create user: {}", e))?; Ok(()) } // 创建群组 pub fn create_group( id: &str, name: &str, description: &str, owner_id: &str ) -> Result<(), String> { // 1. 在 admin 数据库创建记录 let admin_conn = Connection::open(admin_db_path()) .map_err(|e| format!("Failed to open admin db: {}", e))?; admin_conn.execute( "INSERT INTO groups (id, name, description, owner_id, members, status, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, '[]', 'active', datetime('now'), datetime('now'))", rusqlite::params![id, name, description, owner_id] ).map_err(|e| format!("Failed to create group: {}", e))?; // 2. 创建群组数据库 let group_path = crate::db::group_db_path(id); let group_conn = Connection::open(&group_path) .map_err(|e| format!("Failed to create group db: {}", e))?; group_conn.execute_batch(include_str!("../sql/group_tables.sql")) .map_err(|e| format!("Failed to create group tables: {}", e))?; Ok(()) } // 添加用户到群组 pub fn add_user_to_group(user_id: &str, group_id: &str) -> Result<(), String> { let conn = Connection::open(admin_db_path()) .map_err(|e| format!("Failed to open db: {}", e))?; // 1. 更新用户的 group_ids conn.execute( "UPDATE users SET group_ids = json_insert(group_ids, '$[#]', ?1) WHERE id = ?2", rusqlite::params![group_id, user_id] ).map_err(|e| format!("Failed to update user groups: {}", e))?; // 2. 更新群组的 members conn.execute( "UPDATE groups SET members = json_insert(members, '$[#]', ?1) WHERE id = ?2", rusqlite::params![user_id, group_id] ).map_err(|e| format!("Failed to update group members: {}", e))?; // 3. 在群组数据库添加成员记录 let group_conn = Connection::open(crate::db::group_db_path(group_id)) .map_err(|e| format!("Failed to open group db: {}", e))?; group_conn.execute( "INSERT OR IGNORE INTO group_members (user_id, username, role, joined_at) SELECT ?1, username, 'member', datetime('now') FROM users WHERE id = ?1", [user_id] ).ok(); // 忽略错误,因为可能已存在 Ok(()) } ``` --- #### 2.3 创建 group.rs **创建新文件**: `src-tauri/src/group.rs` ```rust use rusqlite::Connection; use crate::db::group_db_path; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct GroupFile { pub file_uuid: String, pub filename: String, pub file_path: String, pub file_size: i64, pub mime_type: Option, pub uploaded_by: String, pub access_level: String, } // 获取群组文件列表 pub fn get_group_files(group_id: &str) -> Result, String> { let conn = Connection::open(group_db_path(group_id)) .map_err(|e| format!("Failed to open group db: {}", e))?; let mut stmt = conn.prepare( "SELECT file_uuid, filename, file_path, file_size, mime_type, uploaded_by, access_level FROM group_files ORDER BY created_at DESC" ).map_err(|e| format!("Failed to prepare statement: {}", e))?; let files = stmt.query_map([], |row| { Ok(GroupFile { file_uuid: row.get(0)?, filename: row.get(1)?, file_path: row.get(2)?, file_size: row.get(3)?, mime_type: row.get(4)?, uploaded_by: row.get(5)?, access_level: row.get(6)?, }) }).map_err(|e| format!("Failed to query files: {}", e))?; let mut result = Vec::new(); for file in files { result.push(file.map_err(|e| format!("Failed to get file: {}", e))?); } Ok(result) } // 上传文件到群组 pub fn upload_file_to_group( group_id: &str, file_uuid: &str, filename: &str, file_path: &str, file_size: i64, mime_type: &str, uploaded_by: &str ) -> Result<(), String> { let conn = Connection::open(group_db_path(group_id)) .map_err(|e| format!("Failed to open group db: {}", e))?; conn.execute( "INSERT INTO group_files (file_uuid, filename, file_path, file_size, mime_type, uploaded_by, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, datetime('now'), datetime('now'))", rusqlite::params![file_uuid, filename, file_path, file_size, mime_type, uploaded_by] ).map_err(|e| format!("Failed to insert file: {}", e))?; Ok(()) } ``` --- #### 2.4 修改 main.rs **添加新的 Tauri 命令**: ```rust // src-tauri/src/main.rs mod admin; mod group; // Admin 命令 #[tauri::command] fn get_system_config(key: String) -> Result, String> { admin::get_system_config(&key) } #[tauri::command] fn set_system_config(key: String, value: String, updated_by: String) -> Result<(), String> { admin::set_system_config(&key, &value, &updated_by) } #[tauri::command] fn create_user(id: String, username: String, password: String, email: Option, role: String) -> Result<(), String> { admin::create_user(&id, &username, &password, email.as_deref(), &role) } #[tauri::command] fn create_group(id: String, name: String, description: String, owner_id: String) -> Result<(), String> { admin::create_group(&id, &name, &description, &owner_id) } // Group 命令 #[tauri::command] fn get_group_files(group_id: String) -> Result, String> { group::get_group_files(&group_id) } #[tauri::command] fn upload_file_to_group( group_id: String, file_uuid: String, filename: String, file_path: String, file_size: i64, mime_type: String, uploaded_by: String ) -> Result<(), String> { group::upload_file_to_group(&group_id, &file_uuid, &filename, &file_path, file_size, &mime_type, &uploaded_by) } // 在 invoke_handler 中注册 fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![ // ... 现有命令 get_system_config, set_system_config, create_user, create_group, get_group_files, upload_file_to_group, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` --- #### 2.5 创建 SQL 文件 **创建**: `src-tauri/sql/admin_tables.sql` ```sql -- 系统配置表 CREATE TABLE IF NOT EXISTS system_config ( key TEXT PRIMARY KEY, value TEXT NOT NULL, description TEXT, updated_by TEXT, updated_at TEXT DEFAULT (datetime('now')) ); -- 用户表 CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, email TEXT UNIQUE, password_hash TEXT NOT NULL, display_name TEXT, role TEXT DEFAULT 'user', status TEXT DEFAULT 'active', group_ids TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), last_login_at TEXT ); -- 群组表 CREATE TABLE IF NOT EXISTS groups ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT, owner_id TEXT NOT NULL, members TEXT, permissions TEXT, storage_quota TEXT, status TEXT DEFAULT 'active', created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ); -- 权限表 CREATE TABLE IF NOT EXISTS permissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, resource_type TEXT NOT NULL, resource_id TEXT NOT NULL, permission TEXT NOT NULL, granted_by TEXT NOT NULL, granted_at TEXT DEFAULT (datetime('now')), expires_at TEXT, UNIQUE(user_id, resource_type, resource_id, permission) ); -- 审计日志表 CREATE TABLE IF NOT EXISTS audit_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, action TEXT NOT NULL, resource_type TEXT, resource_id TEXT, details TEXT, ip_address TEXT, user_agent TEXT, created_at TEXT DEFAULT (datetime('now')) ); ``` **创建**: `src-tauri/sql/group_tables.sql` ```sql -- 群组配置表 CREATE TABLE IF NOT EXISTS group_config ( key TEXT PRIMARY KEY, value TEXT NOT NULL, description TEXT, updated_by TEXT, updated_at TEXT DEFAULT (datetime('now')) ); -- 群组成员表 CREATE TABLE IF NOT EXISTS group_members ( user_id TEXT PRIMARY KEY, username TEXT NOT NULL, role TEXT DEFAULT 'member', permissions TEXT, joined_at TEXT DEFAULT (datetime('now')), invited_by TEXT ); -- 群组文件表 CREATE TABLE IF NOT EXISTS group_files ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_uuid TEXT NOT NULL UNIQUE, filename TEXT NOT NULL, file_path TEXT NOT NULL, file_size INTEGER, mime_type TEXT, uploaded_by TEXT NOT NULL, access_level TEXT DEFAULT 'group', tags TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ); -- 群组权限表 CREATE TABLE IF NOT EXISTS group_permissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, permission TEXT NOT NULL, resource_type TEXT, resource_id TEXT, granted_by TEXT NOT NULL, granted_at TEXT DEFAULT (datetime('now')), UNIQUE(user_id, permission, resource_type, resource_id) ); -- 群组共享表 CREATE TABLE IF NOT EXISTS group_shares ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_uuid TEXT NOT NULL, shared_by TEXT NOT NULL, shared_with TEXT, share_type TEXT, permissions TEXT, expires_at TEXT, created_at TEXT DEFAULT (datetime('now')) ); ``` --- ### Phase 3: 前端实现 (1-2 天) #### 3.1 创建 Admin 管理页面 **创建**: `src/views/AdminView.vue` ```vue ``` --- #### 3.2 更新 API 调用 **修改**: `src/api/index.ts` ```typescript // Admin API export async function getSystemConfig(key: string) { return await invoke('get_system_config', { key }) } export async function setSystemConfig(key: string, value: string, updatedBy: string) { return await invoke('set_system_config', { key, value, updatedBy }) } export async function createUser(id: string, username: string, password: string, email?: string, role: string = 'user') { return await invoke('create_user', { id, username, password, email, role }) } export async function createGroup(id: string, name: string, description: string, ownerId: string) { return await invoke('create_group', { id, name, description, ownerId: ownerId }) } // Group API export async function getGroupFiles(groupId: string) { return await invoke('get_group_files', { groupId }) } export async function uploadFileToGroup( groupId: string, fileUuid: string, filename: string, filePath: string, fileSize: number, mimeType: string, uploadedBy: string ) { return await invoke('upload_file_to_group', { groupId, fileUuid, filename, filePath, fileSize, mimeType, uploadedBy }) } ``` --- ### Phase 4: 测试与部署 (1 天) #### 4.1 功能测试 ```bash # 1. 初始化数据库 ./scripts/init_three_layer_db.sh # 2. 检查架构 ./scripts/check_three_layer.sh # 3. 启动应用 ./start.sh # 4. 测试功能 # - 创建用户 # - 创建群组 # - 添加用户到群组 # - 上传文件到群组 # - 检查权限隔离 ``` --- #### 4.2 数据迁移 ```bash # 1. 备份现有数据 cp data/users/demo.sqlite data/users/demo.sqlite.backup # 2. 迁移到新架构 # (手动或脚本迁移) # 3. 验证数据完整性 ./scripts/check_three_layer.sh ``` --- #### 4.3 部署到生产 ```bash # 1. 在生产环境运行初始化 ./scripts/init_three_layer_db.sh # 2. 修改默认密码 export MOMENTRY_DEFAULT_PASSWORD="secure_password" ./src-tauri/target/release/user_tool init-demo # 3. 配置系统设置 # 通过 Admin 界面或 API 设置 # 4. 启动服务 ./start.sh ``` --- ## 📊 进度检查清单 ### Phase 1: 数据库准备 - [ ] 创建目录结构 - [ ] 运行初始化脚本 - [ ] 验证初始化结果 ### Phase 2: 代码实现 - [ ] 修改 db.rs - [ ] 创建 admin.rs - [ ] 创建 group.rs - [ ] 修改 main.rs - [ ] 创建 SQL 文件 - [ ] 编译测试 ### Phase 3: 前端实现 - [ ] 创建 Admin 管理页面 - [ ] 创建 Group 管理页面 - [ ] 更新 API 调用 - [ ] 测试 UI 交互 ### Phase 4: 测试与部署 - [ ] 功能测试 - [ ] 数据迁移 - [ ] 生产部署 - [ ] 验证检查 --- ## ⚠️ 注意事项 ### 1. 数据库路径 确保所有路径函数使用动态路径: ```rust // ✅ 正确 pub fn user_db_path(username: &str) -> PathBuf { PathBuf::from(base).join(format!("users/{}.sqlite", username)) } // ❌ 错误 pub fn user_db_path() -> PathBuf { PathBuf::from(base).join("users/demo.sqlite") // 硬编码 } ``` --- ### 2. 权限检查 所有操作前检查权限: ```rust pub fn upload_file_to_group(...) -> Result<(), String> { // 1. 检查用户是否有权限 if !check_permission(user_id, "group", group_id, "write")? { return Err("Permission denied".to_string()); } // 2. 执行操作 // ... } ``` --- ### 3. 审计日志 记录所有关键操作: ```rust pub fn create_user(...) -> Result<(), String> { // ... 创建用户 // 记录审计日志 add_audit_log( admin_id, "create", "user", &user_id, &json!({"username": username}) )?; Ok(()) } ``` --- ## 🎯 验证标准 ### 功能验证 - [ ] Admin 可以管理系统配置 - [ ] Admin 可以创建/删除用户 - [ ] Admin 可以创建/删除群组 - [ ] Group Owner 可以管理群组成员 - [ ] Group Member 可以上传文件 - [ ] User 数据完全隔离 - [ ] 权限检查生效 --- ### 性能验证 - [ ] 数据库查询 < 100ms - [ ] 文件上传响应及时 - [ ] 多用户并发正常 --- ### 安全验证 - [ ] 无硬编码路径 - [ ] 无硬编码密码 - [ ] 权限检查严格 - [ ] 审计日志完整 --- ## 📚 相关文档 1. **MARKBASE_THREE_LAYER_ARCHITECTURE.md** - 架构设计 2. **DATABASE_DEPENDENCIES.md** - 数据库依赖 3. **USER_DATABASE_ISOLATION_FIX.md** - 用户隔离修复 --- **预计工作量**: 3-5 天 **优先级**: 高 **状态**: ✅ 实施指南完成,待执行