架构设计:
- 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 天
优先级: 高
823 lines
19 KiB
Markdown
823 lines
19 KiB
Markdown
# 三层架构实施指南
|
|
|
|
**创建时间**: 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<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`
|
|
|
|
```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<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 命令**:
|
|
|
|
```rust
|
|
// 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`
|
|
|
|
```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
|
|
<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`
|
|
|
|
```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 天
|
|
**优先级**: 高
|
|
**状态**: ✅ 实施指南完成,待执行 |