2026-02-11 22:40:35 +08:00
|
|
|
|
# RustFS 对象存储集成说明
|
|
|
|
|
|
|
2026-09-01 18:05:18 +08:00
|
|
|
|
> 文档定位:**RustFS / 对象存储集成的专题说明文档**。
|
|
|
|
|
|
> 本文解释对象存储侧的接口与集成思路,不作为当前部署入口文档。当前部署方式见 [../../DEPLOYMENT.md](../../DEPLOYMENT.md) 与 [../../deployment/LINUX_SETUP.md](../../deployment/LINUX_SETUP.md),当前状态见 [../../STATUS.md](../../STATUS.md)。
|
2026-02-11 22:40:35 +08:00
|
|
|
|
## 架构概述
|
|
|
|
|
|
|
|
|
|
|
|
本项目采用 **RustFS** 作为对象存储和 **PostgreSQL** 作为元数据存储的双层存储架构。
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 应用层 (FastAPI) │
|
|
|
|
|
|
└──────────────────────┬──────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
┌──────────────┴──────────────┐
|
|
|
|
|
|
│ │
|
|
|
|
|
|
┌───────▼────────┐ ┌─────────▼─────────┐
|
|
|
|
|
|
│ PostgreSQL │ │ RustFS │
|
|
|
|
|
|
│ (元数据) │ │ (对象存储) │
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
│ - users │ │ - stp-files │
|
|
|
|
|
|
│ - stp_files │ │ - geometry │
|
|
|
|
|
|
│ - geometry_data│ │ - mold-cavities │
|
|
|
|
|
|
│ - mold_cavity │ │ - html-files │
|
|
|
|
|
|
│ - features │ │ - user-files │
|
|
|
|
|
|
│ - logs │ │ │
|
|
|
|
|
|
└────────────────┘ └──────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## RustFS API 端点
|
|
|
|
|
|
|
|
|
|
|
|
假设 RustFS 运行在 `http://localhost:8080`,需要实现以下 REST API:
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 健康检查
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /health
|
|
|
|
|
|
返回: 200 OK
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 初始化上传
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/v1/upload/init
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
请求体:
|
|
|
|
|
|
{
|
|
|
|
|
|
"namespace": "moldinsight/stp-files",
|
|
|
|
|
|
"key": "stp-files/abc123.stp",
|
|
|
|
|
|
"file_size": 1234567,
|
|
|
|
|
|
"file_hash": "sha256_hash",
|
|
|
|
|
|
"metadata": {
|
|
|
|
|
|
"original_filename": "model.stp",
|
|
|
|
|
|
"user_id": "1"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
{
|
|
|
|
|
|
"upload_id": "unique-upload-id",
|
|
|
|
|
|
"upload_url": "https://rustfs/upload/xyz",
|
|
|
|
|
|
"expires_at": "2024-01-01T00:00:00Z"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 上传文件内容
|
|
|
|
|
|
```
|
|
|
|
|
|
PUT <upload_url>
|
|
|
|
|
|
Content-Type: application/octet-stream
|
|
|
|
|
|
Content-Length: 1234567
|
|
|
|
|
|
X-File-Hash: sha256_hash
|
|
|
|
|
|
|
|
|
|
|
|
请求体: <文件二进制数据>
|
|
|
|
|
|
|
|
|
|
|
|
响应: 201 Created
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 完成上传
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/v1/upload/complete
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
请求体:
|
|
|
|
|
|
{
|
|
|
|
|
|
"upload_id": "unique-upload-id",
|
|
|
|
|
|
"namespace": "moldinsight/stp-files",
|
|
|
|
|
|
"key": "stp-files/abc123.stp"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
{
|
|
|
|
|
|
"object_key": "moldinsight/stp-files/abc123.stp",
|
|
|
|
|
|
"etag": "d41d8cd98f00b204e9800998ecf8427e",
|
|
|
|
|
|
"created_at": "2024-01-01T00:00:00Z"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5. 上传 JSON(小文件,直接上传)
|
|
|
|
|
|
```
|
|
|
|
|
|
PUT /api/v1/objects/<namespace>/<key>
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
X-File-Hash: sha256_hash
|
|
|
|
|
|
|
|
|
|
|
|
请求体: <JSON字符串>
|
|
|
|
|
|
|
|
|
|
|
|
响应: 201 Created
|
|
|
|
|
|
{
|
|
|
|
|
|
"object_key": "moldinsight/geometry/abc123.json",
|
|
|
|
|
|
"etag": "d41d8cd98f00b204e9800998ecf8427e",
|
|
|
|
|
|
"created_at": "2024-01-01T00:00:00Z"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6. 下载文件
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/v1/objects/<namespace>/<key>
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
响应: <文件二进制数据>
|
|
|
|
|
|
Content-Type: <原上传时的内容类型>
|
|
|
|
|
|
Content-Length: <文件大小>
|
|
|
|
|
|
ETag: <文件etag>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7. 获取文件信息
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/v1/objects/<namespace>/<key>/info
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
{
|
|
|
|
|
|
"object_key": "moldinsight/stp-files/abc123.stp",
|
|
|
|
|
|
"namespace": "moldinsight/stp-files",
|
|
|
|
|
|
"file_size": 1234567,
|
|
|
|
|
|
"file_hash": "sha256_hash",
|
|
|
|
|
|
"content_type": "application/octet-stream",
|
|
|
|
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
|
|
|
|
"last_modified": "2024-01-01T00:00:00Z",
|
|
|
|
|
|
"metadata": {
|
|
|
|
|
|
"original_filename": "model.stp",
|
|
|
|
|
|
"user_id": "1"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8. 删除文件
|
|
|
|
|
|
```
|
|
|
|
|
|
DELETE /api/v1/objects/<namespace>/<key>
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
响应: 204 No Content
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 9. 列出文件
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/v1/buckets/<namespace>/objects?prefix=stp-files
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
{
|
|
|
|
|
|
"namespace": "moldinsight/stp-files",
|
|
|
|
|
|
"prefix": "stp-files",
|
|
|
|
|
|
"objects": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"object_key": "moldinsight/stp-files/abc123.stp",
|
|
|
|
|
|
"file_size": 1234567,
|
|
|
|
|
|
"file_hash": "sha256_hash",
|
|
|
|
|
|
"created_at": "2024-01-01T00:00:00Z"
|
|
|
|
|
|
},
|
|
|
|
|
|
...
|
|
|
|
|
|
],
|
|
|
|
|
|
"is_truncated": false,
|
|
|
|
|
|
"next_marker": null
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 10. 生成预签名 URL
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/v1/presigned-url
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
请求体:
|
|
|
|
|
|
{
|
|
|
|
|
|
"namespace": "moldinsight/stp-files",
|
|
|
|
|
|
"key": "stp-files/abc123.stp",
|
|
|
|
|
|
"expires": 3600,
|
|
|
|
|
|
"method": "GET"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
{
|
|
|
|
|
|
"url": "https://rustfs/objects/moldinsight/stp-files/abc123.stp?signature=xyz&expires=123",
|
|
|
|
|
|
"expires_at": "2024-01-01T01:00:00Z"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 11. 存储统计
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/v1/stats
|
|
|
|
|
|
Authorization: Bearer <api_key>
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
{
|
|
|
|
|
|
"total_objects": 1234,
|
|
|
|
|
|
"total_size": 1234567890,
|
|
|
|
|
|
"namespace_stats": {
|
|
|
|
|
|
"moldinsight/stp-files": {
|
|
|
|
|
|
"object_count": 100,
|
|
|
|
|
|
"total_size": 123456789
|
|
|
|
|
|
},
|
|
|
|
|
|
"moldinsight/geometry": {
|
|
|
|
|
|
"object_count": 200,
|
|
|
|
|
|
"total_size": 234567890
|
|
|
|
|
|
},
|
|
|
|
|
|
...
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 存储桶(命名空间)
|
|
|
|
|
|
|
|
|
|
|
|
| 命名空间 | 用途 | 存储内容 |
|
|
|
|
|
|
|---------|------|---------|
|
|
|
|
|
|
| `moldinsight/stp-files` | STP/STEP文件 | 用户上传的原始3D模型文件 |
|
|
|
|
|
|
| `moldinsight/geometry` | 几何数据 | 几何分析结果的JSON数据 |
|
|
|
|
|
|
| `moldinsight/mold-cavities` | 模具型腔数据 | 模具设计的详细JSON数据 |
|
|
|
|
|
|
| `moldinsight/html` | HTML文件 | 生成的HTML报告文件 |
|
|
|
|
|
|
| `moldinsight/user-files` | 用户文件 | 其他用户上传的文件 |
|
|
|
|
|
|
|
|
|
|
|
|
## 快速开始
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 配置环境变量
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 复制示例配置
|
|
|
|
|
|
cp .env.example .env
|
|
|
|
|
|
|
|
|
|
|
|
# 编辑 .env 文件
|
|
|
|
|
|
nano .env
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
设置 RustFS 相关配置:
|
|
|
|
|
|
```env
|
|
|
|
|
|
RUSTFS_ENDPOINT=http://localhost:8080
|
|
|
|
|
|
RUSTFS_API_KEY=your-rustfs-api-key
|
|
|
|
|
|
RUSTFS_TIMEOUT=30
|
|
|
|
|
|
RUSTFS_PRESIGNED_URL_EXPIRES=3600
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 启动 RustFS 服务
|
|
|
|
|
|
|
|
|
|
|
|
假设你已经有 RustFS 服务,如果没有,可以按照以下方式启动:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 使用 Docker(如果提供了 Docker 镜像)
|
|
|
|
|
|
docker run -d \
|
|
|
|
|
|
--name rustfs \
|
|
|
|
|
|
-p 8080:8080 \
|
|
|
|
|
|
-e RUSTFS_API_KEY=your-rustfs-api-key \
|
|
|
|
|
|
-e RUSTFS_STORAGE_PATH=/data \
|
|
|
|
|
|
-v rustfs_data:/data \
|
|
|
|
|
|
your-registry/rustfs:latest
|
|
|
|
|
|
|
|
|
|
|
|
# 或直接运行编译好的二进制文件
|
|
|
|
|
|
./rustfs-server --port 8080 --api-key your-rustfs-api-key --storage-path ./rustfs-data
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 初始化存储
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 初始化 RustFS 连接
|
|
|
|
|
|
python src/storage/init_storage.py
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 使用存储集成服务
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from services.storage_integration_rustfs import storage_integration
|
|
|
|
|
|
from database.database import db_manager
|
|
|
|
|
|
|
|
|
|
|
|
async def upload_file(file_path: str):
|
|
|
|
|
|
async with db_manager.get_session() as session:
|
|
|
|
|
|
stp_file = await storage_integration.save_stp_file(
|
|
|
|
|
|
session=session,
|
|
|
|
|
|
file_path=Path(file_path),
|
|
|
|
|
|
original_filename="model.stp",
|
|
|
|
|
|
user_id=1
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f"文件已保存到 RustFS,ID: {stp_file.id}")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Python 客户端使用
|
|
|
|
|
|
|
|
|
|
|
|
### 上传文件
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from storage.rustfs_storage import rustfs_manager
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
# 先连接
|
|
|
|
|
|
await rustfs_manager.connect(
|
|
|
|
|
|
endpoint="http://localhost:8080",
|
|
|
|
|
|
api_key="your-api-key"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 上传 STP 文件
|
|
|
|
|
|
result = await rustfs_manager.upload_file(
|
|
|
|
|
|
bucket_type='stp_files',
|
|
|
|
|
|
file_path=Path('model.stp'),
|
|
|
|
|
|
original_filename='model.stp'
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f"上传成功: {result['object_key']}")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 上传 JSON 数据
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
geometry_data = {
|
|
|
|
|
|
"volume": 5061079.99,
|
|
|
|
|
|
"surface_area": 640037.28,
|
|
|
|
|
|
"bounding_box": {...}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result = await rustfs_manager.upload_json_data(
|
|
|
|
|
|
bucket_type='geometry_data',
|
|
|
|
|
|
json_data=geometry_data,
|
|
|
|
|
|
file_hash='sha256-hash'
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f"JSON 上传成功: {result['object_key']}")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 下载文件
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
data = await rustfs_manager.download_file(
|
|
|
|
|
|
bucket_type='geometry_data',
|
|
|
|
|
|
object_key='geometry_data/abc123.json'
|
|
|
|
|
|
)
|
|
|
|
|
|
json_data = json.loads(data.decode('utf-8'))
|
|
|
|
|
|
print(json_data)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 生成预签名 URL
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
url = await rustfs_manager.generate_presigned_url(
|
|
|
|
|
|
bucket_type='stp_files',
|
|
|
|
|
|
object_key='stp-files/abc123.stp',
|
|
|
|
|
|
expires=3600 # 1小时
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f"临时访问链接: {url}")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 列出文件
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
files = await rustfs_manager.list_files(
|
|
|
|
|
|
bucket_type='stp_files',
|
|
|
|
|
|
prefix='stp-files'
|
|
|
|
|
|
)
|
|
|
|
|
|
for f in files:
|
|
|
|
|
|
print(f"{f['object_key']}: {f['file_size']} bytes")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## RustFS 服务端实现参考
|
|
|
|
|
|
|
|
|
|
|
|
如果你需要实现 RustFS 服务端,以下是一个简单的 Rust 实现框架:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use actix_web::{web, App, HttpServer};
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
|
|
struct UploadInitRequest {
|
|
|
|
|
|
namespace: String,
|
|
|
|
|
|
key: String,
|
|
|
|
|
|
file_size: u64,
|
|
|
|
|
|
file_hash: String,
|
|
|
|
|
|
metadata: Option<serde_json::Value>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
|
|
struct UploadInitResponse {
|
|
|
|
|
|
upload_id: String,
|
|
|
|
|
|
upload_url: String,
|
|
|
|
|
|
expires_at: String,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn upload_init(
|
|
|
|
|
|
req: web::Json<UploadInitRequest>,
|
|
|
|
|
|
path: web::Data<PathBuf>
|
|
|
|
|
|
) -> impl web::Responder {
|
|
|
|
|
|
// 验证 API Key
|
|
|
|
|
|
// 生成 upload_id
|
|
|
|
|
|
// 返回上传 URL
|
|
|
|
|
|
web::Json(UploadInitResponse { ... })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[actix_web::main]
|
|
|
|
|
|
async fn main() -> std::io::Result<()> {
|
|
|
|
|
|
HttpServer::new(|| {
|
|
|
|
|
|
App::new()
|
|
|
|
|
|
.route("/health", web::get().to(health_check))
|
|
|
|
|
|
.route("/api/v1/upload/init", web::post().to(upload_init))
|
|
|
|
|
|
// ... 其他路由
|
|
|
|
|
|
})
|
|
|
|
|
|
.bind("0.0.0.0:8080")?
|
|
|
|
|
|
.run()
|
|
|
|
|
|
.await
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 故障排除
|
|
|
|
|
|
|
|
|
|
|
|
### 连接 RustFS 失败
|
|
|
|
|
|
```
|
|
|
|
|
|
错误: RustFS 连接失败
|
|
|
|
|
|
解决:
|
|
|
|
|
|
1. 检查 RUSTFS_ENDPOINT 是否正确
|
|
|
|
|
|
2. 检查 RustFS 服务是否运行
|
|
|
|
|
|
3. 检查网络连接
|
|
|
|
|
|
4. 验证 API Key
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 文件上传失败
|
|
|
|
|
|
```
|
|
|
|
|
|
错误: RustFS 上传失败
|
|
|
|
|
|
解决:
|
|
|
|
|
|
1. 检查磁盘空间
|
|
|
|
|
|
2. 检查网络连接
|
|
|
|
|
|
3. 检查文件大小限制
|
|
|
|
|
|
4. 查看服务端日志
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 预签名 URL 失败
|
|
|
|
|
|
```
|
|
|
|
|
|
错误: RustFS 生成预签名URL失败
|
|
|
|
|
|
解决:
|
|
|
|
|
|
1. 检查 expires 参数是否有效
|
|
|
|
|
|
2. 确认服务端支持预签名 URL
|
|
|
|
|
|
3. 检查权限配置
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 性能优化建议
|
|
|
|
|
|
|
|
|
|
|
|
### 客户端
|
|
|
|
|
|
- 使用连接池(aiohttp 默认支持)
|
|
|
|
|
|
- 实现上传重试机制
|
|
|
|
|
|
- 对大文件使用分片上传
|
|
|
|
|
|
- 并行上传多个小文件
|
|
|
|
|
|
|
|
|
|
|
|
### 服务端
|
|
|
|
|
|
- 实现缓存层
|
|
|
|
|
|
- 支持范围请求(断点续传)
|
|
|
|
|
|
- 压缩存储
|
|
|
|
|
|
- CDN 分发静态文件
|
|
|
|
|
|
|
|
|
|
|
|
## 安全建议
|
|
|
|
|
|
|
|
|
|
|
|
1. **更改默认 API Key**:生产环境必须使用强密钥
|
|
|
|
|
|
2. **启用 HTTPS**:生产环境使用 TLS
|
|
|
|
|
|
3. **访问控制**:配置命名空间权限
|
|
|
|
|
|
4. **数据加密**:敏感数据加密存储
|
|
|
|
|
|
5. **日志监控**:记录所有访问和操作
|