From 93185369b99c39b2c569b020e40ad0c5988aeb2c Mon Sep 17 00:00:00 2001 From: Yuuuu <1569752928@qq.com> Date: Tue, 10 Mar 2026 16:24:42 +0800 Subject: [PATCH] refactor: modularize SSTC workspace panels and dialogs --- AGENTS.md | 42 + CLAUDE.md | 283 +- .../components/sstc/SSTCCharterStagePanel.tsx | 598 ++++ .../sstc/SSTCExecutionBoardPanel.tsx | 133 + .../sstc/SSTCReleaseCreateDialog.tsx | 123 + .../components/sstc/SSTCReleaseStagePanel.tsx | 270 ++ .../sstc/SSTCReviewCreateDialog.tsx | 112 + .../components/sstc/SSTCReviewStagePanel.tsx | 303 ++ .../components/sstc/SSTCSignalStagePanel.tsx | 409 +++ .../components/sstc/SSTCTaskCreateDialog.tsx | 109 + .../components/sstc/SSTCTaskStagePanel.tsx | 399 +++ .../src/components/sstc/SSTCWorkspace.tsx | 2747 +---------------- frontend/src/components/sstc/sstcUtils.ts | 214 ++ .../sstc/useSSTCWorkspaceOrchestration.ts | 639 ++++ frontend/tsconfig.tsbuildinfo | 2 +- 15 files changed, 3559 insertions(+), 2824 deletions(-) create mode 100644 frontend/src/components/sstc/SSTCCharterStagePanel.tsx create mode 100644 frontend/src/components/sstc/SSTCExecutionBoardPanel.tsx create mode 100644 frontend/src/components/sstc/SSTCReleaseCreateDialog.tsx create mode 100644 frontend/src/components/sstc/SSTCReleaseStagePanel.tsx create mode 100644 frontend/src/components/sstc/SSTCReviewCreateDialog.tsx create mode 100644 frontend/src/components/sstc/SSTCReviewStagePanel.tsx create mode 100644 frontend/src/components/sstc/SSTCSignalStagePanel.tsx create mode 100644 frontend/src/components/sstc/SSTCTaskCreateDialog.tsx create mode 100644 frontend/src/components/sstc/SSTCTaskStagePanel.tsx create mode 100644 frontend/src/components/sstc/sstcUtils.ts create mode 100644 frontend/src/components/sstc/useSSTCWorkspaceOrchestration.ts diff --git a/AGENTS.md b/AGENTS.md index d6f50a7c..3faff990 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,3 +70,45 @@ frontend/ # React 前端 ## 执行要求 根据用户的具体需求,生成高质量、可直接运行的代码。 + +--- + +## 8. 服务边界约束(禁止越界) + +- 数据库写操作必须通过 Alembic migration,禁止直接 `ALTER TABLE` +- 新增接口必须遵循响应包络:`{code, message, data}`,不得裸返回 +- 禁止在 API 层写业务逻辑,业务逻辑归属 `services/` +- 禁止在前端组件内直接调用 `fetch`,必须通过 `frontend/src/api/` 封装层 +- 前端状态管理:服务端数据用 React Query,本地 UI 状态用 `useState` + +## 9. 新增代码的参考模式 + +- 新增后端接口:参考 `backend/app/api/sstc.py` 的结构 +- 新增前端 API 调用:参考 `frontend/src/api/sstc.ts` +- 新增数据库表:参考 `alembic/versions/` 最新 migration +- 新增飞书事件处理:参考 `backend/app/services/feishu_integration.py` + +## 10. 幂等与事务约束 + +- 所有入站事件必须经过 `ingest_events` 幂等去重 +- 飞书回复必须经过 `feishu_reply_outbox`,禁止同步直接发送 +- 跨表写操作必须在同一个数据库事务内完成 + +## 11. 已知 BOUNDARY(不要幻构行为) + +- 飞书回调字段可能缺失文件名/大小,仅保证 `attachment_id/key/type` 可用 +- `agents.defaults.timeoutSeconds` 在 OpenClaw 网关行为未验证,当前以 `LLM_REQUEST_TIMEOUT` 环境变量为准 +- World Model 每次全量构建,高负载场景下存在性能风险,暂未缓存,改动前需评估影响 + +## 12. 前端组件约束 + +- `ui/` 目录下组件禁止修改,仅使用 +- `sstc/` 目录下组件已模块化拆分,修改前必须执行 `FIND_USAGE` +- 新增业务组件放入对应功能目录,禁止在 `pages/` 内写内联组件 +- 样式只用 Tailwind utility class,禁止内联 `style` + +## 13. 测试与验证 + +- 后端改动:`python -m py_compile` 通过 + 相关单测通过 +- 前端改动:`npm run build` 通过 + `tsc --noEmit` 零错误 +- 新增接口:必须在 `backend/tests/` 补充对应单测 diff --git a/CLAUDE.md b/CLAUDE.md index b35d2c02..ab4a195d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,226 +1,89 @@ -# CLAUDE.md +# PMFlow 项目结构说明(CLAUDE.md) -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +## 1. Agent 入口文件(你关心的“进项目先读什么”) +- `AGENTS.md`:Codex/Coding Agent 的主规则入口(优先级最高的项目级约束) +- `CLAUDE.md`:面向 Claude 风格的项目说明文档(补充背景) +- 建议:规则放 `AGENTS.md`,架构/目录说明放 `CLAUDE.md` -## Project Overview +## 2. 项目定位 +- 项目名:`PMFlow / Neural Sync` +- 形态:AI 原生项目管理系统(多租户 + 角色权限 + SSTC 工作流) +- 技术栈: + - 后端:`FastAPI + SQLAlchemy + Alembic` + - 前端:`React + TypeScript + Vite + Tailwind + Radix + React Query` -**PMFlow / Neural Sync** is an AI-native project management platform designed for small (10-50 person) AI/ML technical teams. The system has evolved into a multi-tenant, role-based PM operating system with: - -- **Multi-tenant Architecture**: Complete data isolation with tenant_id filtering -- **RBAC**: Role-based access control with JWT authentication -- **Event-Driven Architecture**: Event bus with WebSocket real-time updates -- **AI-Native Features**: LangChain integration, skill management, intelligent task routing - -### Dual Frontend Architecture - -The project maintains two frontends: -- **Vue 3** (`frontend/`): Legacy Signal Dashboard with Naive UI -- **React 18** (`frontend-react/`): New PMFlow interface with shadcn/ui (recommended for new features) - -## Common Development Commands - -### Quick Start / Stop - -```bash -# Start all services (backend + React frontend) -./start.sh - -# Or use Python launcher (cross-platform) -python launcher.py - -# Stop all services -./stop.sh +## 3. 顶层目录(精简版) +```text +AIProjectManger/ +├── AGENTS.md # Agent 执行规则(建议优先维护) +├── CLAUDE.md # 现有 Claude 说明文档 +├── start.sh / stop.sh # 一键启动/停止入口 +├── docker-compose*.yml # 容器编排 +├── backend/ # FastAPI 后端 +├── frontend/ # React 前端 +├── scripts/ # 根目录运维脚本 +├── tests/ # 根级测试(集成/脚本) +└── docs/ # 额外文档 ``` -### Backend Development - -```bash -cd backend - -# Install dependencies (use project venv) -source ../venv/bin/activate # On Windows: ../venv/Scripts/activate -pip install -r requirements.txt - -# Run database migrations -alembic upgrade head - -# Seed initial data (development) -python -m seed_projects.py - -# Start backend server (dev mode with hot reload) -uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 - -# Run specific file -python -m app.main +## 4. 后端结构(`backend/`) +```text +backend/ +├── app/ +│ ├── api/ # 路由层(含 endpoints) +│ ├── core/ # 配置、日志、核心能力 +│ ├── dependencies/ # 鉴权/租户等依赖注入 +│ ├── models/ # SQLAlchemy 模型 +│ ├── schemas/ # Pydantic 模型 +│ ├── services/ # 业务服务(AI/SSTC/集成) +│ ├── config/ # 配置与 prompt +│ └── utils/ # 工具函数 +├── alembic/ # 数据库迁移 +├── requirements.txt # Python 依赖 +├── Dockerfile +└── tests/ ``` -### Frontend Development - -**React (PMFlow - Recommended)**: -```bash -cd frontend-react -npm install -npm run dev # Port 5174 -npm run build -npm run preview +## 5. 前端结构(`frontend/`) +```text +frontend/ +├── src/ +│ ├── api/ # 接口封装 +│ ├── pages/ # 页面级组件 +│ ├── components/ +│ │ ├── ui/ # 基础 UI 组件 +│ │ └── sstc/ # SSTC 工作流组件(已模块化拆分) +│ ├── hooks/ # 自定义 hooks +│ ├── lib/ # 通用工具 +│ ├── stores/ # 状态管理 +│ ├── router/ # 路由 +│ └── types/ # TS 类型定义 +├── package.json +└── vite.config.ts ``` -**Vue 3 (Legacy)**: +## 6. 运行命令(高频) +### 根目录 +```bash +./start.sh dev # 推荐:docker-dev 热更新模式 +./stop.sh dev +``` + +### 前端 ```bash cd frontend -npm install -npm run dev -- --host # Port 5174 +npm run dev npm run build ``` -## Architecture - -### Backend Structure ([`backend/app/`](backend/app/)) - -**Core Architecture**: -- [`api/`](backend/app/api/) - API routes organized by module (auth, signals, users, sstc, etc.) -- [`core/`](backend/app/core/) - Configuration, logging, response formatting -- [`models/`](backend/app/models/) - SQLAlchemy ORM with multi-tenant base classes -- [`schemas/`](backend/app/schemas/) - Pydantic request/response models -- [`services/`](backend/app/services/) - Business logic layer -- [`dependencies/`](backend/app/dependencies/) - FastAPI dependency injection (auth, tenant filtering) -- [`config/`](backend/app/config/) - Configuration and AI prompts -- [`main.py`](backend/app/main.py) - FastAPI application entry point - -**Key Systems**: - -1. **Multi-Tenancy** ([`models/base.py`](backend/app/models/base.py)): - - All models inherit from `Base` (includes `tenant_id`, `created_at`, `updated_at`) - - `Tenant` model uses `BaseNoTenant` (no tenant_id) - - All queries must filter by tenant_id via dependency injection - -2. **Authentication** ([`services/auth.py`](backend/app/services/auth.py), [`dependencies/auth.py`](backend/app/dependencies/auth.py)): - - JWT-based authentication with 24-hour expiration - - RBAC with roles: BOSS, ADMIN, AI, FE, BE - - Permission matrix for fine-grained access control - - Use `require_roles()` and `get_current_user` dependencies - -3. **Event System** ([`events.py`](backend/app/events.py), [`websocket.py`](backend/app/websocket.py)): - - Event bus for pub/sub messaging - - WebSocket manager with real-time broadcasting - - Event types: signals, tasks, code reviews, audits, integrations - -4. **Role Dispatcher** ([`dispatcher.py`](backend/app/dispatcher.py)): - - Routes signals to appropriate roles (Boss, AI Engineer, Backend, Frontend) - - Keyword and pattern-based routing with confidence scoring - - Escalation logic for high-impact decisions - -5. **AI Services**: - - [`ai_service.py`](backend/app/services/ai_service.py) - LangChain integration - - [`llm.py`](backend/app/services/llm.py) - Ollama/OpenAI client wrapper - - [`skill_service.py`](backend/app/services/skill_service.py) - User skill management - - [`config/ai_prompts.py`](backend/app/config/ai_prompts.py) - Centralized prompt templates - -6. **Integration Services**: - - [`plane.py`](backend/app/services/plane.py) - Plane PM tool integration - - [`lark.py`](backend/app/services/lark.py) - Lark/Feishu messaging - - [`auditor.py`](backend/app/services/auditor.py) - Code audit workflow - - [`auto_merger.py`](backend/app/services/auto_merger.py) - Auto-merge logic - -### Frontend Structure - -**React** ([`frontend-react/src/`](frontend-react/src/)): -- [`pages/`](frontend-react/src/pages/) - Page components (Login, Dashboard, Projects, Users, Skills) -- [`components/`](frontend-react/src/components/) - Reusable UI components -- [`api/`](frontend-react/src/api/) - API client wrappers -- [`hooks/`](frontend-react/src/hooks/) - Custom React hooks (useUserRole) -- Uses React Router with HashRouter, Tailwind CSS, Zustand for state - -**Vue 3** ([`frontend/src/`](frontend/src/)): -- [`pages/`](frontend/src/pages/) - Vue pages -- [`components/`](frontend/src/components/) - Vue components with Naive UI -- [`api/`](frontend/src/api/) - API client -- [`composables/`](frontend/src/composables/) - Vue composables -- [`stores/`](frontend/src/stores/) - Pinia stores - -### Database Schema - -**Multi-Tenant Models**: -- `User` - User accounts with roles (BOSS, ADMIN, AI, FE, BE) -- `Tenant` - Tenant isolation -- `Project` - Projects with tenant_id -- `Signal` - External signals with strategic analysis -- `Task` - Tasks linked to signals and projects -- `Skill` - User skills with proficiency levels -- `UserSkillBinding` - Many-to-many user-skill relationships -- `UserWorkload` - Real-time workload tracking - -**Type Compatibility** ([`models/types.py`](backend/app/models/types.py)): -- PostgreSQL: JSONB, ARRAY, Vector (pgvector) -- SQLite: Fallback to JSON/text storage for development - -## Key Integration Points - -### Authentication Flow -1. Login via `/auth/login` returns JWT token -2. Include token in Authorization header: `Bearer ` -3. Use `get_current_user` dependency for protected routes -4. Use `require_roles("BOSS", "ADMIN")` for role-gated endpoints - -### Multi-Tenant Queries -```python -from app.dependencies import get_tenant_id - -@router.get("/items") -async def list_items( - tenant_id: int = Depends(get_tenant_id), - db: AsyncSession = Depends(get_db) -): - result = await db.execute( - select(Item).where(Item.tenant_id == tenant_id) - ) +### 后端(本地直跑) +```bash +cd backend +pip install -r requirements.txt +uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 ``` -### Event Broadcasting -```python -from app.events import get_event_bus, EventType - -event_bus = get_event_bus() -await event_bus.publish( - EventType.TASK_ASSIGNED, - {"task_id": 123, "user_id": 456} -) -``` - -### WebSocket Events -- Events are automatically bridged to WebSocket clients -- Clients subscribe to event types for filtering -- Message format: `{"type": "event", "event_type": "...", "data": {...}, "timestamp": ...}` - -## Development Notes - -- **Database**: PostgreSQL with pgvector recommended; SQLite fallback for development -- **CORS**: Currently allows all origins (`*`) - restrict for production -- **Logging**: Structured logging via [`core/logging.py`](backend/app/core/logging.py) -- **API Response**: Standardized format via [`core/response.py`](backend/app/core/response.py) -- **AI Caching**: Redis-based caching for AI call results -- **AI Logging**: All AI calls logged to `ai_call_logs` table - -## Ports and Endpoints - -- **Backend API**: http://127.0.0.1:8000 -- **API Docs**: http://127.0.0.1:8000/docs (Swagger UI) -- **React Frontend**: http://127.0.0.1:5174 -- **Vue Frontend**: http://127.0.0.1:5173 - -## Environment Variables - -Key environment variables (see [`.env.example`](frontend/.env.example)): -- `DATABASE_URL` - PostgreSQL/SQLite connection string -- `SECRET_KEY` - JWT signing key -- `OPENAI_API_KEY` - OpenAI API key for LangChain -- `REDIS_URL` - Redis connection for caching -- `PLANE_API_*` - Plane PM tool integration - -## File Conventions - -- **Pydantic Schemas**: Request/Response models in [`schemas/`](backend/app/schemas/) -- **SQLAlchemy Models**: Database models in [`models/`](backend/app/models/) -- **API Routes**: Route handlers in [`api/`](backend/app/api/) -- **Business Logic**: Services in [`services/`](backend/app/services/) -- **AI Prompts**: Centralized in [`config/ai_prompts.py`](backend/app/config/ai_prompts.py) +## 7. 团队维护建议(简版) +- 规则变更:只改 `AGENTS.md` +- 结构变更:同步改 `CLAUDE.md` +- API/契约变更:后端 `schemas` + 前端 `types` 同步更新 diff --git a/frontend/src/components/sstc/SSTCCharterStagePanel.tsx b/frontend/src/components/sstc/SSTCCharterStagePanel.tsx new file mode 100644 index 00000000..49682625 --- /dev/null +++ b/frontend/src/components/sstc/SSTCCharterStagePanel.tsx @@ -0,0 +1,598 @@ +import { PlayCircle, Sparkles } from "lucide-react"; +import type { + SignalAttachment, + SSTCCharter, + SSTCSignal, + SSTCTask, +} from "@/api/sstc"; +import { CHARTER_MODULE_SCHEMAS, type CharterModuleKey } from "@/components/sstc/charterModules"; +import { + FIXED_ALIGNMENT_QA_QUESTIONS, + statusBadgeClass, +} from "@/components/sstc/sstcUtils"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { toZhPriority, toZhSignalType, toZhStatus } from "@/lib/display"; +import { compactTitle } from "@/lib/title"; + +interface CharterActionMutationLike { + isPending: boolean; + mutate: (charterId: string) => void; +} + +interface UpdateCharterMutationLike { + isPending: boolean; + mutate: (payload: { charterId: string; title: string; content: Record }) => void; +} + +interface AIPrefillCharterMutationLike { + isPending: boolean; + mutate: (payload: { charterId: string; force?: boolean }) => void; +} + +interface CharterDetailShape extends SSTCCharter { + signals: SSTCSignal[]; + source_signal?: SSTCSignal | null; + tasks: SSTCTask[]; + alignment_records: Array<{ + user_id: string; + score: number; + confirmed: boolean; + comments?: string | null; + }>; +} + +interface ModuleFieldEditState { + aiGenerated: boolean; + edited: boolean; +} + +interface SSTCCharterStagePanelProps { + charters: SSTCCharter[]; + selectedCharter: SSTCCharter | null; + charterDetail?: CharterDetailShape; + charterDraftTitle: string; + charterModuleDraft: { + modules: Record>; + }; + activeModule: { + key: CharterModuleKey; + title: string; + shortTitle: string; + fields: Array<{ + key: string; + label: string; + editable?: boolean; + multiline?: boolean; + rows?: number; + placeholder?: string; + }>; + }; + activeModuleIndex: number; + charterProgress: { + completedModules: number; + moduleProgress: Record< + CharterModuleKey, + { + done: boolean; + requiredTotal: number; + requiredDone: number; + } + >; + }; + serializedCharterContent: Record; + sourceSignalOpen: boolean; + showAutoFields: boolean; + showAllAlignmentQa: boolean; + editingAlignmentQaIndex: number | null; + alignmentQaAnswers: string[]; + downloadingAttachmentKey: string | null; + setCharterDraftTitle: (value: string) => void; + setSourceSignalOpen: (open: boolean) => void; + setShowAutoFields: (show: boolean | ((prev: boolean) => boolean)) => void; + setShowAllAlignmentQa: (show: boolean | ((prev: boolean) => boolean)) => void; + setEditingAlignmentQaIndex: (index: number | null) => void; + setActiveModuleKey: (key: CharterModuleKey) => void; + onSelectCharter: (charterId: string) => void; + updateModuleField: (moduleKey: CharterModuleKey, fieldKey: string, value: string) => void; + updateAlignmentQaAnswer: (answerIndex: number, value: string) => void; + getFieldEditState: (moduleKey: CharterModuleKey, fieldKey: string) => ModuleFieldEditState; + getModuleStepStatus: (moduleKey: CharterModuleKey) => string; + updateCharterMutation: UpdateCharterMutationLike; + startAlignmentMutation: CharterActionMutationLike; + confirmAlignmentMutation: CharterActionMutationLike; + aiPrefillCharterMutation: AIPrefillCharterMutationLike; + handleDownloadSignalAttachment: (signalId: string, attachment: SignalAttachment) => Promise; +} + +export function SSTCCharterStagePanel({ + charters, + selectedCharter, + charterDetail, + charterDraftTitle, + charterModuleDraft, + activeModule, + activeModuleIndex, + charterProgress, + serializedCharterContent, + sourceSignalOpen, + showAutoFields, + showAllAlignmentQa, + editingAlignmentQaIndex, + alignmentQaAnswers, + downloadingAttachmentKey, + setCharterDraftTitle, + setSourceSignalOpen, + setShowAutoFields, + setShowAllAlignmentQa, + setEditingAlignmentQaIndex, + setActiveModuleKey, + onSelectCharter, + updateModuleField, + updateAlignmentQaAnswer, + getFieldEditState, + getModuleStepStatus, + updateCharterMutation, + startAlignmentMutation, + confirmAlignmentMutation, + aiPrefillCharterMutation, + handleDownloadSignalAttachment, +}: SSTCCharterStagePanelProps) { + const sourceSignals = charterDetail?.signals || []; + const sourceNames = Array.from( + new Set( + sourceSignals + .map((signal) => toZhSignalType(signal.source || signal.signal_type || "other")) + .filter((name) => !!name && name !== "-") + ) + ); + const sourceSummary = sourceNames.length > 0 ? sourceNames.join("/") : "未绑定"; + const autoFields = activeModule.fields.filter((field) => field.editable === false); + const prevModule = activeModuleIndex > 0 ? CHARTER_MODULE_SCHEMAS[activeModuleIndex - 1] : null; + const nextModule = + activeModuleIndex < CHARTER_MODULE_SCHEMAS.length - 1 + ? CHARTER_MODULE_SCHEMAS[activeModuleIndex + 1] + : null; + const hasMultipleCharters = charters.length > 1; + const filledModulePercent = Math.round( + (charterProgress.completedModules / Math.max(CHARTER_MODULE_SCHEMAS.length, 1)) * 100 + ); + const aiReferenceText = + sourceSignals.length > 0 + ? `参考了 ${sourceSignals + .slice(0, 2) + .map((signal, signalIndex) => `信号 #${signal.id.match(/\d+/)?.[0] || signalIndex + 1}`) + .join(" 和 ")} 与项目上下文资料` + : "参考了项目上下文资料"; + + return ( +
+ {hasMultipleCharters && ( +
+
+

宪章列表

+ + {charters.length} 份 + +
+
+ {charters.map((c) => ( + + ))} + {charters.length === 0 &&

暂无宪章

} +
+
+ )} + +
+
+
+
+
+

+ {compactTitle(selectedCharter?.title || "未选择宪章", 24)} +

+

+ v{selectedCharter?.version || 0} · {toZhStatus(selectedCharter?.status)} +

+ {!hasMultipleCharters && ( +

单宪章模式:左侧列表已自动折叠

+ )} +
+
+
+
+
+ + +
+
+ +
+ ↑ 信号源: {sourceSummary} +
+ + +
+
+ + {aiPrefillCharterMutation.isPending && ( +
+ AI 正在根据信号源自动生成 6 模块草稿,请稍候… +
+ )} +
+
+ +
+
+ + +
+ setCharterDraftTitle(e.target.value)} + placeholder="宪章标题(自动精简)" + className="border-0 bg-[#F8F9FA] shadow-[inset_0_0_0_1px_rgba(148,163,184,0.28)]" + /> + +
+
+

{activeModule.title}

+

+ {activeModuleIndex + 1}/{CHARTER_MODULE_SCHEMAS.length} +

+
+ +
+ {activeModule.fields + .filter( + (field) => + field.editable !== false && + !(activeModule.key === "module_2" && field.key === "alignment_qa") + ) + .map((field) => { + const state = getFieldEditState(activeModule.key, field.key); + const fieldTone = + state.aiGenerated && !state.edited ? "bg-[#F4F7FF]" : "bg-white"; + return ( +
+

{field.label}

+ {field.multiline ? ( +