Files
momentry_studio/docs/THREE_LAYER_IMPLEMENTATION_GUIDE.md
Momentry Studio be3cef2e0d feat: MarkBase 三层架构设计
架构设计:
- Admin Layer: 系统管理 (admin.sqlite)
  * system_config - 系统配置
  * users - 用户管理
  * groups - 群组管理
  * permissions - 权限管理
  * audit_logs - 审计日志

- Group Layer: 群组管理 (group_{id}.sqlite)
  * group_config - 群组配置
  * group_members - 群组成员
  * group_files - 群组文件
  * group_permissions - 群组权限
  * group_shares - 群组共享

- User Layer: 用户数据 ({username}.sqlite)
  * user_config - 用户配置
  * user_files - 用户文件
  * user_bookmarks - 用户书签
  * search_history - 搜索历史
  * video_marks - 视频标记

目录结构:
data/
├── admin/admin.sqlite
├── groups/group_{id}.sqlite
└── users/{username}.sqlite

权限模型:
- Admin: 系统管理
- Group Owner: 群组管理
- Group Member: 群组访问
- User: 个人数据

文档:
- MARKBASE_THREE_LAYER_ARCHITECTURE.md - 完整架构设计
- THREE_LAYER_IMPLEMENTATION_GUIDE.md - 实施指南

脚本:
- init_three_layer_db.sh - 数据库初始化
- check_three_layer.sh - 架构检查

预计工作量: 3-5 天
优先级: 高
2026-10-12 15:08:48 +08:00

19 KiB

三层架构实施指南

创建时间: 2026-10-12
预计工作量: 3-5 天


🎯 实施目标

将现有的单层架构升级为三层架构:

  • Admin Layer - 系统管理
  • Group Layer - 群组管理
  • User Layer - 用户数据

📋 实施步骤

Phase 1: 数据库准备 (1 天)

1.1 创建目录结构

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 运行初始化脚本

# 初始化三层架构数据库
./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 验证初始化

# 检查三层架构
./scripts/check_three_layer.sh

预期输出:

✅ Admin Layer: 存在
✅ Group Layer: 2 个群组
✅ User Layer: 1 个用户

Phase 2: 代码实现 (2-3 天)

2.1 修改 db.rs

添加新的路径函数:

// 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

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<String>,
    pub display_name: Option<String>,
    pub role: String,
    pub status: String,
    pub group_ids: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Group {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub owner_id: String,
    pub members: Vec<String>,
    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<Option<String>, 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

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<String>,
    pub uploaded_by: String,
    pub access_level: String,
}

// 获取群组文件列表
pub fn get_group_files(group_id: &str) -> Result<Vec<GroupFile>, 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 命令:

// src-tauri/src/main.rs

mod admin;
mod group;

// Admin 命令
#[tauri::command]
fn get_system_config(key: String) -> Result<Option<String>, 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<String>, 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<Vec<group::GroupFile>, 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

-- 系统配置表
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

-- 群组配置表
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

<template>
  <div class="admin-view">
    <h1>系统管理</h1>
    
    <div class="admin-tabs">
      <button @click="activeTab = 'users'">用户管理</button>
      <button @click="activeTab = 'groups'">群组管理</button>
      <button @click="activeTab = 'config'">系统配置</button>
    </div>
    
    <div v-if="activeTab === 'users'" class="users-panel">
      <!-- 用户列表和管理 -->
    </div>
    
    <div v-if="activeTab === 'groups'" class="groups-panel">
      <!-- 群组列表和管理 -->
    </div>
    
    <div v-if="activeTab === 'config'" class="config-panel">
      <!-- 系统配置 -->
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { invoke } from '@tauri-apps/api/core'

const activeTab = ref('users')

// ... 更多实现
</script>

3.2 更新 API 调用

修改: src/api/index.ts

// 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 功能测试

# 1. 初始化数据库
./scripts/init_three_layer_db.sh

# 2. 检查架构
./scripts/check_three_layer.sh

# 3. 启动应用
./start.sh

# 4. 测试功能
# - 创建用户
# - 创建群组
# - 添加用户到群组
# - 上传文件到群组
# - 检查权限隔离

4.2 数据迁移

# 1. 备份现有数据
cp data/users/demo.sqlite data/users/demo.sqlite.backup

# 2. 迁移到新架构
# (手动或脚本迁移)

# 3. 验证数据完整性
./scripts/check_three_layer.sh

4.3 部署到生产

# 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. 数据库路径

确保所有路径函数使用动态路径:

// ✅ 正确
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. 权限检查

所有操作前检查权限:

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. 审计日志

记录所有关键操作:

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 天
优先级: 高
状态: ✅ 实施指南完成,待执行