Files
hive-manage-v3/docs/superpowers/plans/2026-07-22-hive-manage-v3-p0.md
T

1041 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hive Manage v3 — P0 核心闭环实施计划
> **对于 agentic workers:** 使用 superpowers:executing-plans 或直接逐 task 执行。所有代码由 Claude Code CLI (`--output-format stream-json --include-partial-messages --verbose`) 编写,工头负责 QC。Steps 使用 checkbox (`- [ ]`) 语法追踪进度。
**目标:** 从零构建蜂群运维中台 v3 的 P0 核心闭环——7 个模块覆盖 58 个 Hermes Agent Profile 的批量管控、健康监控、密钥管理、Kanban 任务追踪、Cron 定时任务管理、事件档案、用户鉴权。
**架构:** FastAPI async 后端 + React 19 TypeScript 前端,PostgreSQL 持久层,Hermes CLI 作为蜂群数据源(低侵入只读),JWT 鉴权,CSS 变量双模(暗/浅)。
**技术栈:** Python 3.12, FastAPI, SQLAlchemy 2.0 (async), PostgreSQL, Alembic, Pydantic v2, React 19, TypeScript 5, Tailwind CSS 3, Vite 5, Lucide Icons, JWT (python-jose), bcrypt, pytest, Vitest.
## 全局约束
- 不复用 v1 任何代码,全部从零重写
- 所有 API 路径前缀 `/api/v1/`
- 所有 API 请求/响应用 Pydantic 模型校验
- 四态覆盖:Loading (Skeleton) / Empty (引导文案) / Error (重试按钮) / Normal
- 暗色模式为默认主题,跟随系统并可手动切换(localStorage `hive-theme`)
- 移动端响应式:768px 断点,按钮触控 ≥ 44×44px
- 鉴权:所有 `/api/v1/` 除 `/api/v1/auth/*` 外需 JWT Bearer Token
- 审计:所有写操作(POST/PUT/PATCH/DELETE)自动记录到 `audit_logs` 表
- Hermes CLI 调用只读,不修改蜂巢核心代码
- 密钥值始终脱敏显示(前 3 + 后 4 字符)+ 不写入审计日志原文
- 模型退役 warning 可忽略(底层走 DeepSeek v4 Pro 映射)
- Nginx 反代:`/api/` → FastAPI:8000,其余 → Vite dist
---
## 目录结构
```
/home/enzo/projects/hive-manage-v3/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI 应用工厂 + 中间件注册
│ │ ├── config.py # Settings (pydantic-settings),读 .env
│ │ ├── database.py # async engine + session + Base
│ │ ├── models/
│ │ │ ├── __init__.py
│ │ │ ├── user.py # User ORM
│ │ │ └── audit.py # AuditLog ORM
│ │ ├── schemas/
│ │ │ ├── __init__.py
│ │ │ ├── auth.py # Login/Register/Token 请求响应
│ │ │ ├── user.py # User CRUD
│ │ │ ├── gateway.py # Gateway 状态/事件
│ │ │ ├── health.py # 健康分
│ │ │ ├── cron.py # Cron Job
│ │ │ ├── kanban.py # Kanban 任务
│ │ │ ├── keys.py # 密钥
│ │ │ ├── dashboard.py # Dashboard 聚合
│ │ │ ├── event.py # 事件档案
│ │ │ └── audit.py # 审计日志
│ │ ├── api/
│ │ │ ├── __init__.py
│ │ │ ├── auth.py # /api/v1/auth/*
│ │ │ ├── users.py # /api/v1/users/*
│ │ │ ├── gateway.py # /api/v1/gateway/*
│ │ │ ├── health.py # /api/v1/health/*
│ │ │ ├── cron.py # /api/v1/cron/*
│ │ │ ├── kanban.py # /api/v1/kanban/*
│ │ │ ├── keys.py # /api/v1/keys/*
│ │ │ ├── dashboard.py # /api/v1/dashboard/*
│ │ │ ├── events.py # /api/v1/events/*
│ │ │ └── audit.py # /api/v1/audit/*
│ │ ├── services/
│ │ │ ├── __init__.py
│ │ │ ├── hermes_cli.py # hermes gateway/cron/kanban CLI 封装
│ │ │ ├── health_scorer.py # 健康分计算引擎
│ │ │ ├── key_manager.py # .env 读写 + 脱敏
│ │ │ └── event_crawler.py # hm.zhiweidata.ai/events 爬取
│ │ └── middleware/
│ │ ├── __init__.py
│ │ ├── auth.py # JWT 鉴权依赖
│ │ └── audit.py # 审计日志中间件
│ ├── alembic/
│ │ ├── env.py
│ │ └── versions/
│ ├── tests/
│ │ ├── conftest.py # async test fixtures
│ │ ├── test_auth.py
│ │ ├── test_gateway.py
│ │ ├── test_health.py
│ │ ├── test_cron.py
│ │ ├── test_kanban.py
│ │ ├── test_keys.py
│ │ ├── test_dashboard.py
│ │ └── test_events.py
│ ├── requirements.txt
│ ├── .env.example
│ └── pyproject.toml
├── frontend/
│ ├── src/
│ │ ├── main.tsx # React 入口
│ │ ├── App.tsx # 路由 + 布局
│ │ ├── types/
│ │ │ └── index.ts # 全局 TS 类型
│ │ ├── styles/
│ │ │ └── tokens.css # CSS 变量 (暗/浅双模)
│ │ ├── components/
│ │ │ ├── ui/
│ │ │ │ ├── Button.tsx
│ │ │ │ ├── Card.tsx
│ │ │ │ ├── Table.tsx
│ │ │ │ ├── Badge.tsx
│ │ │ │ ├── Modal.tsx
│ │ │ │ ├── Drawer.tsx
│ │ │ │ ├── Toast.tsx
│ │ │ │ ├── Skeleton.tsx
│ │ │ │ ├── Toggle.tsx
│ │ │ │ ├── Timeline.tsx
│ │ │ │ └── SearchBox.tsx
│ │ │ └── layout/
│ │ │ ├── Sidebar.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── MobileNav.tsx
│ │ │ ├── TabBar.tsx
│ │ │ └── AppLayout.tsx
│ │ ├── pages/
│ │ │ ├── LoginPage.tsx
│ │ │ ├── DashboardPage.tsx
│ │ │ ├── GatewayPage.tsx
│ │ │ ├── HealthPage.tsx
│ │ │ ├── CronPage.tsx
│ │ │ ├── KanbanPage.tsx
│ │ │ ├── KeysPage.tsx
│ │ │ ├── EventArchivePage.tsx
│ │ │ ├── UserManagementPage.tsx
│ │ │ └── AuditLogPage.tsx
│ │ ├── hooks/
│ │ │ ├── usePolling.ts
│ │ │ ├── useAuth.ts
│ │ │ └── useTheme.ts
│ │ └── api/
│ │ ├── client.ts # axios 实例 + JWT 拦截器
│ │ ├── auth.ts
│ │ ├── gateway.ts
│ │ ├── health.ts
│ │ ├── cron.ts
│ │ ├── kanban.ts
│ │ ├── keys.ts
│ │ ├── dashboard.ts
│ │ ├── events.ts
│ │ ├── users.ts
│ │ └── audit.ts
│ ├── index.html
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── tailwind.config.ts
│ └── postcss.config.js
├── deploy/
│ └── nginx-hive-manage-v3.conf
├── docs/
│ └── superpowers/
│ └── plans/
├── .gitignore
└── README.md
```
---
## Phase 0:基础设施
> 所有后续 Phase 的前置依赖。无业务逻辑,纯骨架。
### Task 0.1:项目目录 + 基础文件
**文件:**
- Create: `.gitignore`, `README.md`, `backend/.env.example`, `backend/pyproject.toml`
- [ ] **Step 1: 创建 .gitignore**
```
__pycache__/
*.pyc
.env
node_modules/
dist/
.pytest_cache/
*.egg-info/
venv/
.DS_Store
```
- [ ] **Step 2: 创建 README.md**
```markdown
# Hive Manage v3
蜂群运维中台 — 58 个 Hermes Agent Profile 的批量管控面板。
## 技术栈
- 后端: FastAPI + SQLAlchemy async + PostgreSQL
- 前端: React 19 + TypeScript + Tailwind CSS + Vite
## 快速开始
见 deploy/ 目录下的部署文档。
```
- [ ] **Step 3: 创建 backend/pyproject.toml**
```toml
[project]
name = "hive-manage-v3"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi[standard]>=0.115",
"sqlalchemy[asyncio]>=2.0",
"asyncpg>=0.30",
"alembic>=1.14",
"pydantic>=2.10",
"pydantic-settings>=2.7",
"python-jose[cryptography]>=3.3",
"passlib[bcrypt]>=1.7",
"httpx>=0.28",
"uvicorn[standard]>=0.34",
]
[project.optional-dependencies]
dev = [
"pytest>=8.3",
"pytest-asyncio>=0.25",
"httpx>=0.28",
]
```
- [ ] **Step 4: 创建 backend/.env.example**
```
DATABASE_URL=postgresql+asyncpg://hive_admin:changeme@localhost:5432/hive_manage_v3
JWT_SECRET=change-me-to-random-string
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=1440
HERMES_CLI_PATH=/home/enzo/.local/bin/hermes
HERMES_PROFILES_ROOT=/home/enzo/.hermes/profiles
ADMIN_EMAIL=admin@zhiwei.local
ADMIN_PASSWORD=admin123
```
- [ ] **Step 5: 提交**
```bash
git add -A && git commit -m "chore: project scaffold with .gitignore, README, pyproject.toml"
```
---
### Task 0.2:Git 初始化 + Gitea 建库 + Mirror
> 此 Task 委托 ops-engineer(工蜂)执行。以下为指令规格,由 ops 自行完成。
**要求:**
- 在 `/home/enzo/projects/hive-manage-v3/` 执行 `git init`
- 在主 Gitea (`192.168.50.97:3002`, 用户 `enzo`) 创建仓库 `hive-manage-v3`
- 设置 remote origin 指向主 Gitea
- 首次 commit + push
- 备份 Gitea (`192.168.50.92:3030`) 的 mirror 由现有 cron 自动发现(每小时),无需手动操作
- 验证:`git remote -v` 指向正确地址,Gitea Web UI 可见仓库
**ops 执行命令参考:**
```bash
cd /home/enzo/projects/hive-manage-v3
git init
git remote add origin http://enzo:zhiwei2026!@192.168.50.97:3002/enzo/hive-manage-v3.git
git add -A && git commit -m "chore: initial commit"
git push -u origin main
```
---
### Task 0.3:FastAPI 骨架
**文件:**
- Create: `backend/app/__init__.py`
- Create: `backend/app/main.py`
- Create: `backend/app/config.py`
- Create: `backend/app/database.py`
- [ ] **Step 1: 创建 config.py**
```python
"""应用配置 — 从环境变量 / .env 读取。"""
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str = "postgresql+asyncpg://hive_admin:changeme@localhost:5432/hive_manage_v3"
jwt_secret: str = "change-me"
jwt_algorithm: str = "HS256"
jwt_expire_minutes: int = 1440
hermes_cli_path: str = "/home/enzo/.local/bin/hermes"
hermes_profiles_root: str = "/home/enzo/.hermes/profiles"
admin_email: str = "admin@zhiwei.local"
admin_password: str = "admin123"
model_config = {"env_file": ".env"}
settings = Settings()
```
- [ ] **Step 2: 创建 database.py**
```python
"""异步 SQLAlchemy 引擎 + session 工厂。"""
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase
from app.config import settings
engine = create_async_engine(settings.database_url, echo=False)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
class Base(DeclarativeBase):
pass
async def get_db() -> AsyncSession:
async with async_session() as session:
yield session
```
- [ ] **Step 3: 创建 main.py**
```python
"""FastAPI 应用入口。"""
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.database import engine, Base
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时创建表(开发阶段;生产用 Alembic)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
await engine.dispose()
app = FastAPI(
title="Hive Manage v3",
version="0.1.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/v1/health")
async def health_check():
return {"status": "ok"}
```
- [ ] **Step 4: 提交**
```bash
git add backend/app/ && git commit -m "feat: FastAPI skeleton with config, database, health endpoint"
```
---
### Task 0.4:Alembic 初始化 + User 基础 Schema
**文件:**
- Create: `backend/alembic/` (通过 `alembic init`)
- Modify: `backend/alembic/env.py`
- Create: `backend/app/models/__init__.py`
- Create: `backend/app/models/user.py`
- [ ] **Step 1: 初始化 Alembic + 创建 User 模型**
```bash
cd backend && alembic init alembic
```
- [ ] **Step 2: 配置 alembic/env.py**
将 `target_metadata` 指向 `Base.metadata`,配置异步引擎。关键改动:
```python
from app.database import Base, engine
from app.models import * # noqa: F401,F403 — 确保所有模型被导入
target_metadata = Base.metadata
def run_migrations_offline():
...
context.configure(url=settings.database_url, target_metadata=target_metadata)
def do_run_migrations(connection):
context.configure(connection=connection, target_metadata=target_metadata)
async def run_async_migrations():
async with engine.begin() as conn:
await conn.run_sync(do_run_migrations)
def run_migrations_online():
import asyncio
asyncio.run(run_async_migrations())
```
- [ ] **Step 3: 创建 User 模型 backend/app/models/user.py**
```python
"""用户 ORM 模型。"""
import uuid
from datetime import datetime
from sqlalchemy import String, Boolean, DateTime, Enum as SAEnum
from sqlalchemy.orm import Mapped, mapped_column
import enum
from app.database import Base
class UserRole(str, enum.Enum):
OWNER = "owner"
OPERATOR = "operator"
VIEWER = "viewer"
class User(Base):
__tablename__ = "users"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, index=True)
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
role: Mapped[UserRole] = mapped_column(SAEnum(UserRole), default=UserRole.VIEWER, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
is_approved: Mapped[bool] = mapped_column(Boolean, default=False)
login_attempts: Mapped[int] = mapped_column(default=0)
locked_until: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
```
- [ ] **Step 4: 生成初始迁移**
```bash
cd backend && alembic revision --autogenerate -m "create users table"
alembic upgrade head
```
- [ ] **Step 5: 创建 backend/app/models/audit.py**
```python
"""审计日志 ORM 模型。"""
import uuid
from datetime import datetime
from sqlalchemy import String, DateTime, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class AuditLog(Base):
__tablename__ = "audit_logs"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
username: Mapped[str] = mapped_column(String(50), nullable=False)
action: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
target: Mapped[str] = mapped_column(String(255), nullable=False)
detail: Mapped[str | None] = mapped_column(Text, nullable=True)
ip_address: Mapped[str | None] = mapped_column(String(45), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
```
- [ ] **Step 6: 生成 AuditLog 迁移并提交**
```bash
cd backend && alembic revision --autogenerate -m "create audit_logs table"
alembic upgrade head
git add backend/alembic/ backend/app/models/ && git commit -m "feat: Alembic setup, User and AuditLog models"
```
---
### Task 0.5:前端 Vite + React + Tailwind 脚手架
**文件:**
- Create: `frontend/package.json`, `frontend/tsconfig.json`, `frontend/vite.config.ts`, `frontend/tailwind.config.ts`, `frontend/postcss.config.js`, `frontend/index.html`, `frontend/src/main.tsx`, `frontend/src/App.tsx`
- [ ] **Step 1: 创建 package.json**
```json
{
"name": "hive-manage-v3-frontend",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.0",
"react-dom": "^19.0",
"react-router-dom": "^7.0",
"axios": "^1.7",
"lucide-react": "^0.460"
},
"devDependencies": {
"@types/react": "^19.0",
"@types/react-dom": "^19.0",
"@vitejs/plugin-react": "^4.3",
"autoprefixer": "^10.4",
"postcss": "^8.4",
"tailwindcss": "^3.4",
"typescript": "^5.6",
"vite": "^6.0"
}
}
```
- [ ] **Step 2: 创建 vite.config.ts**
```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
proxy: {
'/api': 'http://127.0.0.1:8000',
},
},
})
```
- [ ] **Step 3: 创建 tailwind.config.ts**
```typescript
import type { Config } from 'tailwindcss'
export default {
content: ['./index.html', './src/**/*.{ts,tsx}'],
darkMode: 'class',
theme: {
extend: {
colors: {
honey: { DEFAULT: '#f0b90b', dim: '#b8970e' },
},
},
},
plugins: [],
} satisfies Config
```
- [ ] **Step 4: 创建 index.html + main.tsx + App.tsx(最小骨架)**
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Hive Manage v3</title></head>
<body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body>
</html>
```
```tsx
// main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './styles/tokens.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode><App /></React.StrictMode>
)
```
```tsx
// App.tsx
export default function App() {
return <div className="min-h-screen bg-[var(--bg-root)] text-[var(--text)]">Hive Manage v3</div>
}
```
- [ ] **Step 5: 安装依赖 + 验证**
```bash
cd frontend && npm install && npm run dev
# 访问 http://localhost:5173 应显示 "Hive Manage v3"
```
- [ ] **Step 6: 提交**
```bash
git add frontend/ && git commit -m "feat: Vite + React 19 + Tailwind + TypeScript scaffold"
```
---
### Task 0.6:CSS 变量双模体系
**文件:**
- Create: `frontend/src/styles/tokens.css`
**根据 UI 设计规范 §2.1 精确实现:**
```css
/* ===== 暗色模式(默认) ===== */
:root {
--bg-root: #0f1117; --bg-nav: #161822; --bg-card: #1a1d2e;
--bg-hover: #1e2134; --bg-elevated: #222639;
--border: #2a2d3e; --border-light: rgba(42,45,62,.5);
--honey: #f0b90b; --honey-dim: #b8970e; --honey-glow: rgba(240,185,11,.12);
--green: #00c853; --red: #ff3d3d; --yellow: #ffab00;
--blue: #448aff; --orange: #ff6d00;
--text: #e0e0e0; --text-dim: #8b8d9a; --text-muted: #5a5d6a;
--bg-success: #0d2818; --bg-error: #2d0f0f; --bg-warn: #2d2000; --bg-info: #0d1b2d;
--radius: 8px; --radius-sm: 4px; --radius-lg: 12px; --radius-full: 9999px;
--shadow: 0 2px 8px rgba(0,0,0,.35); --shadow-lg: 0 12px 32px rgba(0,0,0,.55);
--shadow-modal: 0 24px 64px rgba(0,0,0,.65);
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: "SF Mono", "Fira Code", Consolas, monospace;
--header-h: 48px; --sidebar-w: 220px;
}
/* ===== 浅色模式 ===== */
[data-theme="light"] {
--bg-root: #f5f6fa; --bg-nav: #ffffff; --bg-card: #ffffff;
--bg-hover: #edf0f5; --bg-elevated: #ffffff;
--border: #e2e5ee; --border-light: rgba(0,0,0,.06);
--honey: #e5a800; --honey-dim: #c48f00; --honey-glow: rgba(229,168,0,.08);
--green: #008c3a; --red: #d32f2f; --yellow: #e65100;
--blue: #1565c0; --orange: #d84315;
--text: #1a1d2e; --text-dim: #555a6e; --text-muted: #8b8f9e;
--bg-success: #e8f5e9; --bg-error: #fdecea; --bg-warn: #fff3e0; --bg-info: #e3f2fd;
--shadow: 0 1px 4px rgba(0,0,0,.08); --shadow-lg: 0 8px 24px rgba(0,0,0,.10);
--shadow-modal: 0 16px 48px rgba(0,0,0,.14);
}
/* ===== 全局基础 ===== */
* { box-sizing: border-box; }
body {
margin: 0; font-family: var(--font); font-size: 13px;
background: var(--bg-root); color: var(--text);
}
/* 主题切换过渡 */
* { transition: background-color .15s, border-color .15s, color .15s; }
```
- [ ] **Step: 提交**
```bash
git add frontend/src/styles/tokens.css && git commit -m "feat: CSS variable dual-theme system (dark default + light)"
```
---
### Task 0.7:共享 UI 组件库
**文件:** 全部在 `frontend/src/components/ui/` 下创建。
> 每个组件遵循 UI 设计规范 §4 规格。以下列出关键组件及核心实现要点,完整代码由 Claude Code 根据原型 HTML 和设计规范实现。
| 组件 | 文件 | 规范参考 | 关键要点 |
|------|------|----------|----------|
| Button | `Button.tsx` | §4.4 | 4 变体 (primary/danger/outline/ghost) + 2 尺寸 + loading/disabled 态 |
| Card | `Card.tsx` | §4.1 | header(label+action) + value(彩色) + change(↑↓) |
| Table | `Table.tsx` | §4.3 | sticky 表头 + hover 高亮 + row-danger 样式 + 移动端卡片化 |
| Badge | `Badge.tsx` | §4.5 | 6 变体 (success/danger/warning/info/honey/outline) + 胶囊形 |
| Modal | `Modal.tsx` | §4.8 | 居中弹窗 + 遮罩 + Esc 关闭 + 确认短语输入 |
| Drawer | `Drawer.tsx` | §4.9 | 右侧滑入 480px + 遮罩 + Esc 关闭 |
| Toast | `Toast.tsx` | §4.10 | 4 类型 (success/error/warn/info) + 顶部居中 + 自动消失 |
| Skeleton | `Skeleton.tsx` | §4.14 | shimmer 动画 + 形状匹配 (card/table/text) |
| Toggle | `Toggle.tsx` | §4.7 | 44×24px + 滑块动画 + 标签 |
| Timeline | `Timeline.tsx` | §4.13 | 横向时间线 + 点 hover tooltip |
| SearchBox | `SearchBox.tsx` | §4.11 | 输入框 + 下拉面板 + 分组结果 + debounce 300ms |
**测试要求:** 每个组件提供基础渲染测试(Vitest + React Testing Library),验证所有变体和状态。
- [ ] **Step: 逐个组件实现(Claude Code 编写 + 工头 QC)**
```bash
git add frontend/src/components/ui/ && git commit -m "feat: shared UI component library (11 components)"
```
---
### Task 0.8:布局组件
**文件:** `frontend/src/components/layout/`
| 组件 | 文件 | 规范参考 | 关键要点 |
|------|------|----------|----------|
| Sidebar | `Sidebar.tsx` | §3.5 | 220px 固定 + 5 Section + NavItem active/hover + Badge |
| Header | `Header.tsx` | §3.4 | 48px + 面包屑 + 搜索框 + 主题切换 + 更新指示 |
| MobileNav | `MobileNav.tsx` | §3.2 | 汉堡菜单 → 左侧抽屉 280px + 遮罩 |
| TabBar | `TabBar.tsx` | §3.6 | 底部固定 56px + 4 核心入口 (总览/Gateway/Kanban/Cron) |
| AppLayout | `AppLayout.tsx` | §3.1 | CSS Grid: Sidebar + Header + Main,响应式 768px 断点切换移动布局 |
- [ ] **Step 1: 实现 AppLayout.tsx(核心布局)**
```tsx
// 桌面: CSS Grid (sidebar 220px + header 48px + main)
// 平板 (768-1279px): sidebar 180px
// 移动 (<768px): Header + TabBar + 汉堡抽屉
// 使用 CSS 媒体查询 + data-viewport 属性切换
```
- [ ] **Step 2: 实现路由占位**
```tsx
// App.tsx 中使用 react-router-dom
// / → Dashboard
// /login → LoginPage
// /gateway → GatewayPage
// ...
// 未登录重定向 /login,已登录根据 JWT 存在与否判断
```
- [ ] **Step 3: 提交**
```bash
git add frontend/src/components/layout/ frontend/src/App.tsx && git commit -m "feat: layout components (Sidebar, Header, MobileNav, TabBar, AppLayout)"
```
---
## Phase 1:用户中心 + 鉴权(P0-6)
### Task 1.1:JWT 鉴权服务 + 中间件
**文件:**
- Create: `backend/app/services/auth_service.py`
- Create: `backend/app/middleware/auth.py`
- Create: `backend/app/schemas/auth.py`
- [ ] **Step 1: 创建 auth schema**
```python
# backend/app/schemas/auth.py
from pydantic import BaseModel, EmailStr
class LoginRequest(BaseModel):
username: str
password: str
class RegisterRequest(BaseModel):
username: str
email: EmailStr
password: str
class TokenResponse(BaseModel):
access_token: str
token_type: str = "bearer"
expires_in: int
user: "UserResponse" # 引用 user schema
class ChangePasswordRequest(BaseModel):
old_password: str
new_password: str
```
- [ ] **Step 2: 创建 auth_service.py**
核心功能:
- `hash_password(password: str) -> str` — bcrypt
- `verify_password(plain: str, hashed: str) -> bool`
- `create_access_token(user_id: str, role: str) -> str` — JWT 编码
- `decode_access_token(token: str) -> dict` — JWT 解码 + 过期检查
- `get_current_user(token: str, db: AsyncSession) -> User` — FastAPI Depends
- [ ] **Step 3: 创建 auth 中间件**
```python
# backend/app/middleware/auth.py
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.services.auth_service import decode_access_token, get_user_by_id
security = HTTPBearer()
async def require_auth(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: AsyncSession = Depends(get_db),
):
"""解析 JWT → 查询 User → 返回 User ORM 对象。失败抛 401。"""
payload = decode_access_token(credentials.credentials)
user = await get_user_by_id(db, payload["sub"])
if not user or not user.is_active:
raise HTTPException(status_code=401, detail="Invalid or inactive user")
return user
def require_role(*roles: str):
"""角色守卫工厂。用法: Depends(require_role("owner", "operator"))"""
async def role_check(user = Depends(require_auth)):
if user.role.value not in roles:
raise HTTPException(status_code=403, detail="Insufficient permissions")
return user
return role_check
```
- [ ] **Step 4: 编写测试**
```python
# backend/tests/test_auth.py
# 测试用例:
# - 注册成功 → 201
# - 重复注册 → 409
# - 登录成功 → 200 + JWT
# - 错误密码 → 401
# - 登录锁定(5 次失败) → 423
# - Token 过期 → 401
# - 无 Token 访问受保护路由 → 401
# - Viewer 角色访问写操作 → 403
```
- [ ] **Step 5: 提交**
```bash
git add backend/app/services/auth_service.py backend/app/middleware/ backend/app/schemas/auth.py backend/tests/test_auth.py && git commit -m "feat: JWT auth service, middleware, and role guards"
```
---
### Task 1.2:Auth API 路由
**文件:**
- Create: `backend/app/api/auth.py`
- [ ] **Step 1: 实现路由**
```python
# backend/app/api/auth.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.schemas.auth import LoginRequest, RegisterRequest, TokenResponse
from app.services.auth_service import *
from app.models.user import User, UserRole
router = APIRouter(prefix="/api/v1/auth", tags=["auth"])
@router.post("/register", status_code=201)
async def register(body: RegisterRequest, db: AsyncSession = Depends(get_db)):
"""新用户注册。默认 Viewer 角色 + 待审批。首个用户自动设为 Owner + 已审批。"""
...
@router.post("/login", response_model=TokenResponse)
async def login(body: LoginRequest, db: AsyncSession = Depends(get_db)):
"""用户名 + 密码登录。失败 5 次锁定 15 分钟。成功返回 JWT。"""
...
@router.post("/refresh")
async def refresh_token(user = Depends(require_auth)):
"""刷新 Token(重置过期时间)。"""
...
@router.post("/change-password")
async def change_password(body: ChangePasswordRequest, user = Depends(require_auth), db = Depends(get_db)):
"""修改当前用户密码。"""
...
```
- [ ] **Step 2: 在 main.py 注册路由**
```python
from app.api import auth
app.include_router(auth.router)
```
- [ ] **Step 3: 编写集成测试并提交**
---
### Task 1.3:用户管理 API
**文件:**
- Create: `backend/app/api/users.py`
- Create: `backend/app/schemas/user.py`
路由:`GET /api/v1/users`(列表) / `GET /api/v1/users/{id}`(详情) / `PATCH /api/v1/users/{id}`(改角色) / `POST /api/v1/users/{id}/approve`(审批) / `DELETE /api/v1/users/{id}`(停用)。
- [ ] **Step 1: 实现 + 测试 + 提交**
---
### Task 1.4:审计日志中间件 + API
**文件:**
- Modify: `backend/app/middleware/audit.py`(自动记录写操作)
- Create: `backend/app/api/audit.py`
核心逻辑:中间件监听所有 POST/PUT/PATCH/DELETE 请求,提取 `user_id`、`action`、`target`、`ip_address`,写入 `audit_logs`。审计日志不可删除(API 只提供 GET 查询)。
- [ ] **Step: 实现 + 测试 + 提交**
---
### Task 1.5-1.9:前端鉴权页面
**文件:**
- `frontend/src/pages/LoginPage.tsx`
- `frontend/src/pages/UserManagementPage.tsx`
- `frontend/src/pages/AuditLogPage.tsx`
- `frontend/src/hooks/useAuth.ts`
- `frontend/src/api/auth.ts`, `users.ts`, `audit.ts`
- `frontend/src/api/client.ts`(axios 实例 + JWT 拦截器 + 401 自动跳转登录)
- [ ] **Step: 逐页面实现(Claude Code 编写 + 工头 QC)**
```bash
git add frontend/src/pages/LoginPage.tsx frontend/src/pages/UserManagementPage.tsx frontend/src/pages/AuditLogPage.tsx frontend/src/hooks/useAuth.ts frontend/src/api/ && git commit -m "feat: login, user management, audit log pages with JWT auth flow"
```
---
## Phase 2-8:业务模块
> 每个模块遵循相同模式:**Hermes CLI 封装服务 → Pydantic Schema → API 路由 → 前端页面**。以下为模块规格摘要,完整实现由 Claude Code 按此模式执行。
### 通用实现模式(以 Gateway 为例)
```
1. backend/app/services/hermes_cli.py — 添加 gateway_status(), gateway_start(), gateway_stop()
2. backend/app/schemas/gateway.py — GatewayStatus, GatewayEvent, ToggleRequest
3. backend/app/api/gateway.py — router + require_auth + 角色守卫
4. backend/tests/test_gateway.py — pytest + httpx AsyncClient
5. frontend/src/api/gateway.ts — axios 调用封装
6. frontend/src/pages/GatewayPage.tsx — 完整页面(四态 + 轮询 + 操作)
```
### Phase 2:Dashboard 增强(P0-3)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 2.1 | `GET /api/v1/dashboard/summary` — 在线/离线/任务/Cron 计数 | DashboardPage 统计卡片 | 2h |
| 2.2 | `GET /api/v1/dashboard/health-distribution` — A/B/C/D 四级计数 | 健康分布柱状图 | 1.5h |
| 2.3 | `GET /api/v1/dashboard/gateway-trend` — 24h 异常事件按小时 | Gateway 异常趋势折线图 | 1.5h |
| 2.4 | `GET /api/v1/dashboard/kanban-summary` — 活跃看板概览 | Kanban 概览卡片 | 1h |
| 2.5 | `GET /api/v1/dashboard/cron-recent` — 最近 Cron 执行 | Cron 最近执行卡片 | 1h |
| 2.6 | `GET /api/v1/search?q=` — 全局搜索 | SearchBox 组件集成 | 1.5h |
### Phase 3:Gateway 健康监控(P0-1)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 3.1 | `GET /api/v1/gateway/status` — 全部 Profile 状态列表 | GatewayPage 状态表(10s 轮询) | 3h |
| 3.2 | `GET /api/v1/gateway/events` — 24h 异常事件时间线 | 异常时间线组件 | 2h |
| 3.3 | `POST /api/v1/gateway/{profile}/start\|stop\|restart` | 启停按钮 + 二次确认弹窗 | 2.5h |
| 3.4 | 筛选 + 蜂型分类 | 前端筛选器(即时生效) | 1.5h |
### Phase 4:Profile 健康分(P0-2)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 4.1 | `GET /api/v1/health/scores` — 排行列表 | HealthPage 列表 + 展开详情 | 3h |
| 4.2 | `GET /api/v1/health/scores/{profile}` — 单 Profile 详情 | 四维度扣分明细面板 | 2h |
| 4.3 | `POST /api/v1/health/compare` — 批量对比 | 批量对比视图 | 2h |
| 4.4 | `POST /api/v1/health/calculate` — 手动触发计算 | 凌晨定时 + 手动触发按钮 | 2h |
### Phase 5:Cron 管理(P0-4b)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 5.1 | `GET /api/v1/cron/jobs` — 跨 Profile Cron 列表(含系统 Cron) | CronPage 列表 + 筛选 | 3h |
| 5.2 | `GET /api/v1/cron/jobs/{id}/logs` — 最近 8 条日志 | 日志面板(遮罩 + 居中弹窗) | 2h |
| 5.3 | `POST /api/v1/cron/jobs/{id}/run\|pause\|resume` | 操作按钮 + Toast | 2h |
| 5.4 | 失败告警联动 | Dashboard Badge + 导航栏红色标记 | 1.5h |
### Phase 6:密钥批量管理(P0-5)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 6.1 | `GET /api/v1/keys` — 密钥字段列表 | KeysPage 列表 | 2h |
| 6.2 | `POST /api/v1/keys/batch-overwrite` — 批量覆盖 | 批量覆盖弹窗(确认短语) | 2h |
| 6.3 | `POST /api/v1/keys/batch-scan` — 批量检查 | 缺失扫描结果 + CSV 导出 | 1.5h |
| 6.4 | `GET /api/v1/keys/diff` — 差异视图 | 字段对比表 | 1.5h |
### Phase 7:Kanban 增强(P0-4)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 7.1 | `GET /api/v1/kanban/tasks` — 跨 Profile 五列聚合 | KanbanPage 五列看板 | 4h |
| 7.2 | `GET /api/v1/kanban/tasks/{id}` — 任务详情 | 详情抽屉 | 2h |
| 7.3 | `PATCH /api/v1/kanban/tasks/{id}/move` — 拖拽移动 | 拖拽 + 异常标记 | 3h |
| 7.4 | 异常检测(阻塞超时/长期无主/失败重试) | 卡片标红/警告 | 2h |
| 7.5 | Worker 筛选 + 看板筛选 + 异常筛选 | 筛选器 | 2h |
### Phase 8:事件档案管理(P0-7)
| Task | 后端 API | 前端页面 | 估时 |
|------|----------|----------|:--:|
| 8.1 | `GET /api/v1/events` — 事件卡片列表(三维筛选) | EventArchivePage 卡片网格 | 2h |
| 8.2 | 数据爬取 → PG(`hm.zhiweidata.ai/events`) | — | 2h |
| 8.3 | 超期标记 + Toast 详情 | 超期红色标记 + 点击 Toast | 1.5h |
---
## Phase 9:集成测试 + 上线
| Task | 内容 | 估时 |
|------|------|:--:|
| 9.1 | 全量 API 集成测试(httpx + pytest-asyncio) | 3h |
| 9.2 | 前端冒烟测试(Vitest + Testing Library) | 2h |
| 9.3 | Nginx 部署配置 + 静态文件服务 | 1h |
| 9.4 | 部署文档 + API 文档(FastAPI 自动生成) | 0.5h |
---
## 执行说明
- **所有代码由 Claude Code 编写**,命令格式:
```bash
cd /home/enzo/projects/hive-manage-v3 && \
ANTHROPIC_API_KEY="sk-4eem6h6DfcPIT7EbY3tLhtd7oepyw9GaqgbSUHmkv3shmNym" \
ANTHROPIC_BASE_URL="http://127.0.0.1:3000" \
ANTHROPIC_MODEL="claude-3-opus-20240229" \
claude --model claude-3-opus-20240229 \
--dangerously-skip-permissions \
--output-format stream-json --include-partial-messages --verbose \
-p "<完整任务描述 + 上下文 + 验收标准>"
```
- **每个 Task 完成后**:工头审查产出(代码质量 + 测试覆盖 + 是否符合设计规范),通过则提交,不通过则反馈 Claude Code 修改。
- **提交粒度**:一个 Task 一个 commit,commit message 格式 `feat:` / `fix:` / `test:` / `chore:` + 简短描述。
---
*计划版本: v1.0 · 2026-07-22 · 开发工程*