c1826918aa
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
9.0 KiB
9.0 KiB
日志系统 + 任务历史 + 文件管理 设计文档
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:writing-plans to create an implementation plan from this spec.
Goal: 为益选 OCR Web 系统添加持久化日志、任务历史和增强文件管理,提升生产环境可观测性和用户体验。
Architecture: 单一 SQLite 数据库 (data/web_data.db) 存储三类数据,FastAPI 中间件自动采集 HTTP 日志,TaskManager 改造为写入 DB,前端新增两个独立页面。
Tech Stack: FastAPI middleware, SQLite (via existing DBPool), Vue 3 + Element Plus, Pinia
1. 数据库设计
数据库文件: data/web_data.db,通过现有 DBPool 管理。
1.1 http_logs 表
CREATE TABLE IF NOT EXISTS http_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL, -- ISO 8601
method TEXT NOT NULL, -- GET/POST/PUT/DELETE
path TEXT NOT NULL, -- /api/memory
status_code INTEGER, -- 200, 404, 500
duration_ms REAL, -- 请求耗时(ms)
user TEXT, -- 当前用户名
ip TEXT, -- 客户端 IP
detail TEXT -- 错误详情/备注
);
CREATE INDEX IF NOT EXISTS idx_http_logs_timestamp ON http_logs(timestamp);
CREATE INDEX IF NOT EXISTS idx_http_logs_status ON http_logs(status_code);
1.2 task_history 表
CREATE TABLE IF NOT EXISTS task_history (
id TEXT PRIMARY KEY, -- 8-char UUID
name TEXT NOT NULL, -- pipeline/ocr-batch/excel/merge/sync-push/sync-pull
status TEXT NOT NULL, -- pending/running/completed/failed
progress INTEGER DEFAULT 0,
message TEXT,
result_files TEXT, -- JSON array of filenames
error TEXT,
log_lines TEXT, -- JSON array of log strings
created_at TEXT NOT NULL, -- ISO 8601
updated_at TEXT NOT NULL, -- ISO 8601
completed_at TEXT -- ISO 8601, null if not done
);
CREATE INDEX IF NOT EXISTS idx_task_history_status ON task_history(status);
CREATE INDEX IF NOT EXISTS idx_task_history_created ON task_history(created_at);
1.3 file_metadata 表
CREATE TABLE IF NOT EXISTS file_metadata (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT NOT NULL,
directory TEXT NOT NULL, -- input/output/result
size INTEGER,
action TEXT NOT NULL, -- upload/delete/clear
user TEXT,
timestamp TEXT NOT NULL, -- ISO 8601
task_id TEXT -- 关联的任务 ID (可选)
);
CREATE INDEX IF NOT EXISTS idx_file_metadata_timestamp ON file_metadata(timestamp);
1.4 自动清理
30 天过期清理,在服务器启动时执行,之后每天通过 asyncio 定时任务执行一次:
async def cleanup_old_records():
cutoff = (datetime.now() - timedelta(days=30)).isoformat()
await db_pool.execute_write("DELETE FROM http_logs WHERE timestamp < ?", cutoff)
await db_pool.execute_write("DELETE FROM task_history WHERE created_at < ?", cutoff)
await db_pool.execute_write("DELETE FROM file_metadata WHERE timestamp < ?", cutoff)
2. 后端架构
2.1 新增文件
| 文件 | 职责 |
|---|---|
web/backend/services/db_schema.py |
建表 SQL + init_db() + cleanup_old_records() |
web/backend/middleware/logging.py |
HTTP 请求日志中间件 |
web/backend/routers/logs.py |
日志查询 API |
web/backend/routers/tasks.py |
任务历史 API |
2.2 修改文件
| 文件 | 改动 |
|---|---|
web/backend/main.py |
lifespan 中调用 init_db(),挂载 logging 中间件,注册 logs/tasks 路由 |
web/backend/services/task_manager.py |
update_progress() 和 _finish() 写入 task_history 表 |
web/backend/routers/files.py |
upload/delete/clear 操作写入 file_metadata 表 |
2.3 API 端点
日志 (/api/logs)
GET /api/logs— 分页查询- 参数:
page,page_size,method,status_code,path(搜索),start_date,end_date - 返回:
{ items: [...], total: number }
- 参数:
GET /api/logs/stats— 统计概览- 返回:
{ today_count, error_count, avg_duration_ms, error_rate }
- 返回:
任务历史 (/api/tasks)
GET /api/tasks— 分页查询- 参数:
page,page_size,status,name(类型筛选),search - 返回:
{ items: [...], total: number }
- 参数:
GET /api/tasks/{task_id}— 任务详情(含完整 log_lines)POST /api/tasks/{task_id}/retry— 重试失败任务- 根据
name字段重新调用对应处理端点
- 根据
文件历史 (/api/files)
GET /api/files/history— 文件操作记录- 参数:
page,page_size,directory,action - 返回:
{ items: [...], total: number }
- 参数:
GET /api/files/stats— 存储统计- 返回:
{ directories: [{ name, file_count, total_size }] }
- 返回:
2.4 中间件设计
async def logging_middleware(request: Request, call_next):
# 跳过静态资源和 WebSocket
if request.url.path.startswith("/assets") or request.url.path.startswith("/ws"):
return await call_next(request)
start = time.time()
response = await call_next(request)
duration_ms = (time.time() - start) * 1000
# 异步写入日志(不阻塞响应)
asyncio.create_task(write_log(
method=request.method,
path=request.url.path,
status_code=response.status_code,
duration_ms=duration_ms,
user=get_current_user_from_request(request),
ip=request.client.host,
))
return response
2.5 TaskManager 改造
现有 TaskManager.update_progress() 和 _finish() 方法中增加 DB 写入:
async def update_progress(self, task_id: str, progress: int, message: str):
task = self._tasks[task_id]
task.progress = progress
task.message = message
task.log_lines.append(message)
# 新增:写入 DB
await self._db.execute_write(
"UPDATE task_history SET progress=?, message=?, log_lines=?, updated_at=? WHERE id=?",
progress, message, json.dumps(task.log_lines), datetime.now().isoformat(), task_id
)
await self._broadcast(task)
3. 前端设计
3.1 新增页面
侧边栏导航新增 2 项:
| 页面 | 路由 | 图标 | 标签 |
|---|---|---|---|
| 任务历史 | /tasks |
Timer |
- |
| 日志中心 | /logs |
Notebook |
- |
3.2 任务历史页面 (Tasks.vue)
布局:
- 顶部统计卡片行(4 卡片):总任务数 / 成功 / 失败 / 运行中
- 筛选栏:状态下拉(全部/成功/失败/运行中)+ 类型下拉(全部/pipeline/ocr/excel/merge)+ 搜索框
- 表格列:任务ID、类型、状态(彩色标签)、进度条、耗时、创建时间、操作
- 操作:查看详情(弹窗显示完整日志流)、重试(仅失败任务)
详情弹窗:
- 任务基本信息(类型/状态/耗时/结果文件)
- 终端风格日志流(复用 Dashboard 的 log-box 样式)
- 结果文件列表(可下载)
3.3 日志中心页面 (Logs.vue)
布局:
- 顶部统计卡片行(4 卡片):今日请求 / 错误数 / 平均耗时 / 错误率
- 筛选栏:时间范围选择器(今天/7天/30天)+ 方法筛选(GET/POST/PUT/DELETE)+ 状态码筛选(2xx/4xx/5xx)+ 路径搜索
- 表格列:时间、方法(彩色标签)、路径、状态码(颜色区分)、耗时、用户
- 点击行展开详情面板(IP 地址、错误信息)
3.4 Dashboard 增强
- stats-row 第三列从硬编码 "记忆库 5591" 改为动态存储统计(磁盘用量)
- 文件列表区新增「操作历史」按钮,弹窗显示该目录的 file_metadata 记录
3.5 新增文件
| 文件 | 职责 |
|---|---|
web/frontend/src/views/Tasks.vue |
任务历史页面 |
web/frontend/src/views/Logs.vue |
日志中心页面 |
web/frontend/src/stores/tasks.ts |
任务历史状态管理(可选,可用 api 直接调用) |
3.6 修改文件
| 文件 | 改动 |
|---|---|
web/frontend/src/views/Layout.vue |
navItems 新增 2 项 |
web/frontend/src/router/index.ts |
新增 2 个路由 |
web/frontend/src/views/Dashboard.vue |
stats-row 动态化 + 文件历史弹窗 |
4. 安全与性能
- 日志查询 API 仅限认证用户
- HTTP 日志不记录请求体(避免泄露敏感数据)
- 中间件使用
asyncio.create_task()异步写入,不阻塞响应 - 日志表索引:
timestamp、status_code、path - 任务表索引:
status、created_at - 自动清理 30 天前的记录,防止数据库无限增长
- 分页查询默认 page_size=50,最大 200
5. 实施顺序
-
Phase 1: 数据库 + 后端
- db_schema.py(建表 + 清理)
- logging 中间件
- task_manager 改造
- files.py 改造
- logs.py + tasks.py 路由
-
Phase 2: 前端页面
- Tasks.vue
- Logs.vue
- Layout.vue 路由注册
- Dashboard.vue 增强
-
Phase 3: 集成测试
- npm run build
- 端到端验证:操作 → 日志记录 → 任务历史 → 文件历史