feat: 初始化 FTB 智能项目管理系统 monorepo 项目结构
包含 Turborepo 配置、Next.js 前端骨架、NestJS 后端骨架、Prisma Schema、 共享类型包、Docker Compose 和环境变量模板。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
18
.env.example
Normal file
18
.env.example
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 数据库
|
||||||
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm
|
||||||
|
|
||||||
|
# 认证
|
||||||
|
NEXTAUTH_SECRET=change-me-to-random-secret
|
||||||
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# AI
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-xxx
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
|
# 邮件通知(可选)
|
||||||
|
SMTP_HOST=smtp.example.com
|
||||||
|
SMTP_PORT=465
|
||||||
|
SMTP_USER=noreply@example.com
|
||||||
|
SMTP_PASS=your-smtp-password
|
||||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.next/
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
*.log
|
||||||
|
.turbo/
|
||||||
|
coverage/
|
||||||
|
.DS_Store
|
||||||
188
CLAUDE.md
Normal file
188
CLAUDE.md
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
FTB 智能项目管理系统 — 一个集成 AI 能力的项目管理平台,核心目标是通过智能化手段提升项目管理效率。
|
||||||
|
|
||||||
|
### 核心功能模块
|
||||||
|
|
||||||
|
- **任务管理**:任务创建、分配、状态流转、优先级管理
|
||||||
|
- **看板视图**:可视化任务拖拽、自定义工作流列
|
||||||
|
- **甘特图**:项目时间线规划与依赖关系管理
|
||||||
|
- **AI 辅助**:任务智能分解、工期预估、风险预警
|
||||||
|
- **资源调度**:团队成员工作负载可视化、智能排期建议
|
||||||
|
- **数据看板**:项目进度、燃尽图、团队效率指标
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
| 层级 | 技术 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 前端框架 | Next.js 14+ (App Router) | SSR + 文件路由 |
|
||||||
|
| UI 组件 | Shadcn/ui + Tailwind CSS | 现代风格,完全可定制 |
|
||||||
|
| 状态管理 | Zustand | 轻量级,替代 Redux |
|
||||||
|
| 拖拽 | dnd-kit | 看板拖拽交互 |
|
||||||
|
| 图表 | Recharts | 燃尽图、数据看板 |
|
||||||
|
| 后端框架 | NestJS | 模块化架构,TypeScript |
|
||||||
|
| ORM | Prisma | 类型安全的数据库访问 |
|
||||||
|
| 数据库 | PostgreSQL | 关系型数据,适合任务依赖建模 |
|
||||||
|
| AI 集成 | Anthropic SDK | 任务分解、风险分析、智能建议 |
|
||||||
|
| 认证 | NextAuth.js | OAuth + JWT |
|
||||||
|
| 实时通信 | Socket.io | 看板实时同步、通知推送 |
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
ftb-project-management/
|
||||||
|
├── apps/
|
||||||
|
│ ├── web/ # Next.js 前端
|
||||||
|
│ │ ├── app/ # App Router 页面
|
||||||
|
│ │ ├── components/ # UI 组件
|
||||||
|
│ │ │ ├── ui/ # Shadcn 基础组件
|
||||||
|
│ │ │ ├── board/ # 看板相关组件
|
||||||
|
│ │ │ ├── gantt/ # 甘特图组件
|
||||||
|
│ │ │ └── dashboard/ # 数据看板组件
|
||||||
|
│ │ ├── hooks/ # 自定义 Hooks
|
||||||
|
│ │ ├── stores/ # Zustand stores
|
||||||
|
│ │ └── lib/ # 工具函数
|
||||||
|
│ └── server/ # NestJS 后端
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── modules/
|
||||||
|
│ │ │ ├── project/ # 项目 CRUD
|
||||||
|
│ │ │ ├── task/ # 任务管理 + 状态机
|
||||||
|
│ │ │ ├── board/ # 看板逻辑
|
||||||
|
│ │ │ ├── gantt/ # 甘特图数据
|
||||||
|
│ │ │ ├── ai/ # AI 能力封装
|
||||||
|
│ │ │ ├── user/ # 用户与权限
|
||||||
|
│ │ │ └── notify/ # 通知系统
|
||||||
|
│ │ ├── common/ # 拦截器、守卫、管道
|
||||||
|
│ │ └── prisma/ # Schema + Migrations
|
||||||
|
│ └── test/
|
||||||
|
├── packages/
|
||||||
|
│ └── shared/ # 前后端共享类型定义
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── turbo.json # Turborepo monorepo 管理
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build & Dev Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖(monorepo 根目录)
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 启动全部服务(前端 + 后端 + 数据库)
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# 单独启动
|
||||||
|
pnpm dev --filter=web # 前端 localhost:3000
|
||||||
|
pnpm dev --filter=server # 后端 localhost:3001
|
||||||
|
|
||||||
|
# 数据库
|
||||||
|
pnpm db:migrate # 执行 Prisma 迁移
|
||||||
|
pnpm db:seed # 填充测试数据
|
||||||
|
pnpm db:studio # 打开 Prisma Studio
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
pnpm test # 全部测试
|
||||||
|
pnpm test --filter=server # 仅后端测试
|
||||||
|
pnpm test -- --watch # 监听模式
|
||||||
|
pnpm test -- -t "任务创建" # 运行单个测试
|
||||||
|
|
||||||
|
# 构建 & 检查
|
||||||
|
pnpm build # 生产构建
|
||||||
|
pnpm lint # ESLint 检查
|
||||||
|
pnpm type-check # TypeScript 类型检查
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker-compose up -d # 启动 PostgreSQL + Redis
|
||||||
|
docker-compose down # 停止容器
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Model (核心实体关系)
|
||||||
|
|
||||||
|
```
|
||||||
|
User ──┬── owns ──── Project
|
||||||
|
│ │
|
||||||
|
│ contains many
|
||||||
|
│ │
|
||||||
|
└── assigned ── Task ──── depends on ──── Task
|
||||||
|
│
|
||||||
|
has many
|
||||||
|
│
|
||||||
|
Comment / Activity / Attachment
|
||||||
|
```
|
||||||
|
|
||||||
|
关键设计决策:
|
||||||
|
- 任务状态机:`todo → in_progress → in_review → done`,支持自定义列
|
||||||
|
- 任务支持无限层级子任务(parent_id 自引用)
|
||||||
|
- 权限模型:Owner > Admin > Member > Viewer(项目级 RBAC)
|
||||||
|
- AI 操作记录独立表存储,便于审计和回溯
|
||||||
|
|
||||||
|
## AI Module 设计
|
||||||
|
|
||||||
|
AI 模块作为独立 NestJS Module,对外暴露服务接口:
|
||||||
|
|
||||||
|
- `AiTaskService.decompose(description)` — 将需求描述拆解为子任务
|
||||||
|
- `AiRiskService.analyze(projectId)` — 分析项目风险并生成预警
|
||||||
|
- `AiScheduleService.suggest(projectId)` — 基于成员负载给出排期建议
|
||||||
|
|
||||||
|
所有 AI 调用走统一的 `AiGateway`,负责 prompt 管理、token 计量、降级处理。
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- 包管理器:pnpm(monorepo workspace)
|
||||||
|
- 分支命名:`feature/模块-描述`、`fix/模块-描述`、`hotfix/描述`
|
||||||
|
- Commit 格式:`类型(模块): 描述`(中文)
|
||||||
|
- 类型:feat / fix / refactor / docs / test / chore
|
||||||
|
- API 路径:`/api/v1/projects/:projectId/tasks/:taskId`(RESTful 嵌套资源)
|
||||||
|
- 前端路由:`/projects/[id]/board`、`/projects/[id]/gantt`、`/projects/[id]/dashboard`
|
||||||
|
- 数据库表名 snake_case,TypeScript 字段 camelCase(Prisma 自动映射)
|
||||||
|
- 组件文件 PascalCase,工具函数文件 camelCase
|
||||||
|
- Zustand store 按功能域拆分:`useProjectStore`、`useTaskStore`、`useBoardStore`
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
项目根目录提供 `.env.example`,开发者复制为 `.env.local` 使用。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 数据库
|
||||||
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm
|
||||||
|
|
||||||
|
# 认证
|
||||||
|
NEXTAUTH_SECRET=your-random-secret-key
|
||||||
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# AI
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-xxx
|
||||||
|
|
||||||
|
# Redis(Socket.io 适配器 + 缓存)
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
|
# 邮件通知(可选)
|
||||||
|
SMTP_HOST=smtp.example.com
|
||||||
|
SMTP_PORT=465
|
||||||
|
SMTP_USER=noreply@example.com
|
||||||
|
SMTP_PASS=your-smtp-password
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
采用 Docker Compose 部署到云服务器(推荐 2核4G),一套配置本地和线上通用。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.prod.yml 核心服务
|
||||||
|
services:
|
||||||
|
web: # Next.js 前端,端口 3000
|
||||||
|
server: # NestJS 后端,端口 3001
|
||||||
|
postgres: # PostgreSQL 数据库,端口 5432
|
||||||
|
redis: # Redis 缓存 + Socket.io,端口 6379
|
||||||
|
nginx: # 反向代理 + SSL 终止,端口 80/443
|
||||||
|
```
|
||||||
|
|
||||||
|
部署流程:
|
||||||
|
1. 服务器安装 Docker + Docker Compose
|
||||||
|
2. 配置 `.env.production` 环境变量
|
||||||
|
3. `docker-compose -f docker-compose.prod.yml up -d`
|
||||||
|
4. Nginx 配置域名 + Let's Encrypt SSL 证书
|
||||||
|
5. 数据库迁移:`docker exec server pnpm db:migrate`
|
||||||
3
apps/server/.env.example
Normal file
3
apps/server/.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-xxx
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
6
apps/server/nest-cli.json
Normal file
6
apps/server/nest-cli.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": { "deleteOutDir": true }
|
||||||
|
}
|
||||||
39
apps/server/package.json
Normal file
39
apps/server/package.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nest start --watch",
|
||||||
|
"build": "nest build",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"src/**/*.ts\"",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"db:migrate": "prisma migrate dev",
|
||||||
|
"db:seed": "ts-node prisma/seed.ts",
|
||||||
|
"db:studio": "prisma studio"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^10.0.0",
|
||||||
|
"@nestjs/core": "^10.0.0",
|
||||||
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
|
"@prisma/client": "^5.15.0",
|
||||||
|
"reflect-metadata": "^0.2.0",
|
||||||
|
"rxjs": "^7.8.0",
|
||||||
|
"@ftb/shared": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
"@nestjs/testing": "^10.0.0",
|
||||||
|
"@types/express": "^4.17.0",
|
||||||
|
"@types/jest": "^29.5.0",
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prisma": "^5.15.0",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"ts-node": "^10.9.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
169
apps/server/prisma/schema.prisma
Normal file
169
apps/server/prisma/schema.prisma
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
email String @unique
|
||||||
|
name String
|
||||||
|
avatar String?
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
createdTasks Task[] @relation("TaskCreator")
|
||||||
|
assignedTasks Task[] @relation("TaskAssignee")
|
||||||
|
projectMembers ProjectMember[]
|
||||||
|
requirements Requirement[]
|
||||||
|
watchedTasks TaskWatcher[]
|
||||||
|
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Product {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
description String @default("")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
projects Project[]
|
||||||
|
requirements Requirement[]
|
||||||
|
versions Version[]
|
||||||
|
|
||||||
|
@@map("products")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Project {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
productId String @map("product_id")
|
||||||
|
name String
|
||||||
|
description String @default("")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
sprints Sprint[]
|
||||||
|
tasks Task[]
|
||||||
|
members ProjectMember[]
|
||||||
|
|
||||||
|
@@map("projects")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Sprint {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
projectId String @map("project_id")
|
||||||
|
name String
|
||||||
|
startDate DateTime @map("start_date")
|
||||||
|
endDate DateTime @map("end_date")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
tasks Task[]
|
||||||
|
|
||||||
|
@@map("sprints")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Version {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
productId String @map("product_id")
|
||||||
|
name String
|
||||||
|
description String @default("")
|
||||||
|
releaseDate DateTime? @map("release_date")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
tasks Task[]
|
||||||
|
|
||||||
|
@@map("versions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Requirement {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
productId String @map("product_id")
|
||||||
|
title String
|
||||||
|
description String @default("")
|
||||||
|
status String @default("draft")
|
||||||
|
priority Int @default(0)
|
||||||
|
creatorId String @map("creator_id")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
creator User @relation(fields: [creatorId], references: [id])
|
||||||
|
|
||||||
|
@@map("requirements")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Task {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
projectId String @map("project_id")
|
||||||
|
sprintId String? @map("sprint_id")
|
||||||
|
versionId String? @map("version_id")
|
||||||
|
parentId String? @map("parent_id")
|
||||||
|
title String
|
||||||
|
description String @default("")
|
||||||
|
status String @default("todo")
|
||||||
|
priority Int @default(0)
|
||||||
|
assigneeId String? @map("assignee_id")
|
||||||
|
creatorId String @map("creator_id")
|
||||||
|
startDate DateTime? @map("start_date")
|
||||||
|
dueDate DateTime? @map("due_date")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
sprint Sprint? @relation(fields: [sprintId], references: [id])
|
||||||
|
version Version? @relation(fields: [versionId], references: [id])
|
||||||
|
parent Task? @relation("TaskChildren", fields: [parentId], references: [id])
|
||||||
|
children Task[] @relation("TaskChildren")
|
||||||
|
assignee User? @relation("TaskAssignee", fields: [assigneeId], references: [id])
|
||||||
|
creator User @relation("TaskCreator", fields: [creatorId], references: [id])
|
||||||
|
watchers TaskWatcher[]
|
||||||
|
comments Comment[]
|
||||||
|
|
||||||
|
@@map("tasks")
|
||||||
|
}
|
||||||
|
|
||||||
|
model TaskWatcher {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
taskId String @map("task_id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
|
||||||
|
task Task @relation(fields: [taskId], references: [id])
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@unique([taskId, userId])
|
||||||
|
@@map("task_watchers")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Comment {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
taskId String @map("task_id")
|
||||||
|
authorId String @map("author_id")
|
||||||
|
content String
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
task Task @relation(fields: [taskId], references: [id])
|
||||||
|
|
||||||
|
@@map("comments")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ProjectMember {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
projectId String @map("project_id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
role String @default("member")
|
||||||
|
|
||||||
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@unique([projectId, userId])
|
||||||
|
@@map("project_members")
|
||||||
|
}
|
||||||
8
apps/server/src/app.module.ts
Normal file
8
apps/server/src/app.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
12
apps/server/src/main.ts
Normal file
12
apps/server/src/main.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
app.setGlobalPrefix('api/v1');
|
||||||
|
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
|
||||||
|
app.enableCors();
|
||||||
|
await app.listen(3001);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
16
apps/server/tsconfig.json
Normal file
16
apps/server/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"target": "ES2021",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"paths": { "@/*": ["src/*"] }
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
3
apps/web/app/globals.css
Normal file
3
apps/web/app/globals.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
19
apps/web/app/layout.tsx
Normal file
19
apps/web/app/layout.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import './globals.css';
|
||||||
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'FTB 项目管理',
|
||||||
|
description: '智能项目管理系统',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
apps/web/app/page.tsx
Normal file
7
apps/web/app/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen items-center justify-center">
|
||||||
|
<h1 className="text-3xl font-bold">FTB 项目管理系统</h1>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
6
apps/web/next.config.js
Normal file
6
apps/web/next.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
transpilePackages: ['@ftb/shared'],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = nextConfig;
|
||||||
28
apps/web/package.json
Normal file
28
apps/web/package.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev --port 3000",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint",
|
||||||
|
"type-check": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "^14.2.0",
|
||||||
|
"react": "^18.3.0",
|
||||||
|
"react-dom": "^18.3.0",
|
||||||
|
"zustand": "^4.5.0",
|
||||||
|
"@ftb/shared": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"@types/react": "^18.3.0",
|
||||||
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"autoprefixer": "^10.4.0",
|
||||||
|
"postcss": "^8.4.0",
|
||||||
|
"tailwindcss": "^3.4.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
apps/web/postcss.config.js
Normal file
6
apps/web/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
9
apps/web/tailwind.config.ts
Normal file
9
apps/web/tailwind.config.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Config } from 'tailwindcss';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
|
||||||
|
theme: { extend: {} },
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
21
apps/web/tsconfig.json
Normal file
21
apps/web/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [{ "name": "next" }],
|
||||||
|
"paths": { "@/*": ["./*"] }
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
ports:
|
||||||
|
- '5432:5432'
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: ftb_pm
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports:
|
||||||
|
- '6379:6379'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
898
docs/superpowers/plans/2026-06-08-project-init.md
Normal file
898
docs/superpowers/plans/2026-06-08-project-init.md
Normal file
@@ -0,0 +1,898 @@
|
|||||||
|
# FTB 智能项目管理系统 — 初始化实现计划
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 初始化 monorepo 项目代码结构,包含前后端骨架、数据库 Schema、共享类型包,使项目可以运行并进入功能开发阶段。
|
||||||
|
|
||||||
|
**Architecture:** Turborepo monorepo,apps/web (Next.js 14 App Router) + apps/server (NestJS),packages/shared 共享类型。PostgreSQL + Prisma ORM,pnpm workspace 管理依赖。
|
||||||
|
|
||||||
|
**Tech Stack:** Next.js 14, Shadcn/ui, Tailwind CSS, Zustand, NestJS, Prisma, PostgreSQL, TypeScript
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: 初始化 Monorepo 根目录
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `package.json`
|
||||||
|
- Create: `pnpm-workspace.yaml`
|
||||||
|
- Create: `turbo.json`
|
||||||
|
- Create: `.gitignore`
|
||||||
|
- Create: `.nvmrc`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 初始化 git 仓库**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git init
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建根 package.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "ftb-project-management",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "turbo dev",
|
||||||
|
"build": "turbo build",
|
||||||
|
"lint": "turbo lint",
|
||||||
|
"type-check": "turbo type-check",
|
||||||
|
"test": "turbo test",
|
||||||
|
"db:migrate": "pnpm --filter=server db:migrate",
|
||||||
|
"db:seed": "pnpm --filter=server db:seed",
|
||||||
|
"db:studio": "pnpm --filter=server db:studio"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"turbo": "^2.0.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@9.4.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 创建 pnpm-workspace.yaml**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
packages:
|
||||||
|
- "apps/*"
|
||||||
|
- "packages/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 创建 turbo.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://turbo.build/schema.json",
|
||||||
|
"tasks": {
|
||||||
|
"dev": {
|
||||||
|
"cache": false,
|
||||||
|
"persistent": true
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": [".next/**", "dist/**"]
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
},
|
||||||
|
"type-check": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 创建 .gitignore**
|
||||||
|
|
||||||
|
```
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.next/
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
*.log
|
||||||
|
.turbo/
|
||||||
|
coverage/
|
||||||
|
.DS_Store
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: 创建 .nvmrc**
|
||||||
|
|
||||||
|
```
|
||||||
|
20
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 7: 安装根依赖并验证**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 8: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add package.json pnpm-workspace.yaml turbo.json .gitignore .nvmrc
|
||||||
|
git commit -m "chore: 初始化 monorepo 根目录配置"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: 初始化 packages/shared 共享类型包
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/shared/package.json`
|
||||||
|
- Create: `packages/shared/tsconfig.json`
|
||||||
|
- Create: `packages/shared/src/index.ts`
|
||||||
|
- Create: `packages/shared/src/enums.ts`
|
||||||
|
- Create: `packages/shared/src/types.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建 packages/shared/package.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@ftb/shared",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"lint": "eslint src/"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 packages/shared/tsconfig.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "./dist"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 创建 packages/shared/src/enums.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export enum TaskStatus {
|
||||||
|
TODO = 'todo',
|
||||||
|
IN_PROGRESS = 'in_progress',
|
||||||
|
IN_REVIEW = 'in_review',
|
||||||
|
DONE = 'done',
|
||||||
|
CLOSED = 'closed',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProjectRole {
|
||||||
|
OWNER = 'owner',
|
||||||
|
ADMIN = 'admin',
|
||||||
|
MEMBER = 'member',
|
||||||
|
VIEWER = 'viewer',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RequirementStatus {
|
||||||
|
DRAFT = 'draft',
|
||||||
|
REVIEWING = 'reviewing',
|
||||||
|
APPROVED = 'approved',
|
||||||
|
REJECTED = 'rejected',
|
||||||
|
DELIVERED = 'delivered',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 创建 packages/shared/src/types.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { TaskStatus, ProjectRole, RequirementStatus } from './enums';
|
||||||
|
|
||||||
|
export interface Product {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Task {
|
||||||
|
id: string;
|
||||||
|
projectId: string;
|
||||||
|
sprintId: string | null;
|
||||||
|
versionId: string | null;
|
||||||
|
parentId: string | null;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
status: TaskStatus;
|
||||||
|
assigneeId: string | null;
|
||||||
|
creatorId: string;
|
||||||
|
priority: number;
|
||||||
|
startDate: Date | null;
|
||||||
|
dueDate: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Requirement {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
status: RequirementStatus;
|
||||||
|
priority: number;
|
||||||
|
creatorId: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Version {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
releaseDate: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Sprint {
|
||||||
|
id: string;
|
||||||
|
projectId: string;
|
||||||
|
name: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 创建 packages/shared/src/index.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export * from './enums';
|
||||||
|
export * from './types';
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/shared/
|
||||||
|
git commit -m "feat(shared): 初始化共享类型包,定义核心枚举和实体类型"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: 初始化 NestJS 后端 (apps/server)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/server/package.json`
|
||||||
|
- Create: `apps/server/tsconfig.json`
|
||||||
|
- Create: `apps/server/nest-cli.json`
|
||||||
|
- Create: `apps/server/src/main.ts`
|
||||||
|
- Create: `apps/server/src/app.module.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建 apps/server/package.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nest start --watch",
|
||||||
|
"build": "nest build",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"src/**/*.ts\"",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"db:migrate": "prisma migrate dev",
|
||||||
|
"db:seed": "ts-node prisma/seed.ts",
|
||||||
|
"db:studio": "prisma studio"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^10.0.0",
|
||||||
|
"@nestjs/core": "^10.0.0",
|
||||||
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
|
"@prisma/client": "^5.15.0",
|
||||||
|
"reflect-metadata": "^0.2.0",
|
||||||
|
"rxjs": "^7.8.0",
|
||||||
|
"@ftb/shared": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
"@nestjs/testing": "^10.0.0",
|
||||||
|
"@types/express": "^4.17.0",
|
||||||
|
"@types/jest": "^29.5.0",
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prisma": "^5.15.0",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"ts-node": "^10.9.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 apps/server/tsconfig.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"target": "ES2021",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"paths": { "@/*": ["src/*"] }
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 创建 apps/server/nest-cli.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": { "deleteOutDir": true }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 创建 apps/server/src/app.module.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 创建 apps/server/src/main.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
app.setGlobalPrefix('api/v1');
|
||||||
|
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
|
||||||
|
app.enableCors();
|
||||||
|
await app.listen(3001);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/server/
|
||||||
|
git commit -m "feat(server): 初始化 NestJS 后端骨架"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: 配置 Prisma Schema
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/server/prisma/schema.prisma`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建 Prisma Schema 文件**
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
email String @unique
|
||||||
|
name String
|
||||||
|
avatar String?
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
createdTasks Task[] @relation("TaskCreator")
|
||||||
|
assignedTasks Task[] @relation("TaskAssignee")
|
||||||
|
projectMembers ProjectMember[]
|
||||||
|
requirements Requirement[]
|
||||||
|
watchedTasks TaskWatcher[]
|
||||||
|
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Product {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
description String @default("")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
projects Project[]
|
||||||
|
requirements Requirement[]
|
||||||
|
versions Version[]
|
||||||
|
|
||||||
|
@@map("products")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Project {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
productId String @map("product_id")
|
||||||
|
name String
|
||||||
|
description String @default("")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
sprints Sprint[]
|
||||||
|
tasks Task[]
|
||||||
|
members ProjectMember[]
|
||||||
|
|
||||||
|
@@map("projects")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 继续添加剩余模型到 schema.prisma**
|
||||||
|
|
||||||
|
在上面的文件末尾追加:
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
model Sprint {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
projectId String @map("project_id")
|
||||||
|
name String
|
||||||
|
startDate DateTime @map("start_date")
|
||||||
|
endDate DateTime @map("end_date")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
tasks Task[]
|
||||||
|
|
||||||
|
@@map("sprints")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Version {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
productId String @map("product_id")
|
||||||
|
name String
|
||||||
|
description String @default("")
|
||||||
|
releaseDate DateTime? @map("release_date")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
tasks Task[]
|
||||||
|
|
||||||
|
@@map("versions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Requirement {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
productId String @map("product_id")
|
||||||
|
title String
|
||||||
|
description String @default("")
|
||||||
|
status String @default("draft")
|
||||||
|
priority Int @default(0)
|
||||||
|
creatorId String @map("creator_id")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
creator User @relation(fields: [creatorId], references: [id])
|
||||||
|
|
||||||
|
@@map("requirements")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Task {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
projectId String @map("project_id")
|
||||||
|
sprintId String? @map("sprint_id")
|
||||||
|
versionId String? @map("version_id")
|
||||||
|
parentId String? @map("parent_id")
|
||||||
|
title String
|
||||||
|
description String @default("")
|
||||||
|
status String @default("todo")
|
||||||
|
priority Int @default(0)
|
||||||
|
assigneeId String? @map("assignee_id")
|
||||||
|
creatorId String @map("creator_id")
|
||||||
|
startDate DateTime? @map("start_date")
|
||||||
|
dueDate DateTime? @map("due_date")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
sprint Sprint? @relation(fields: [sprintId], references: [id])
|
||||||
|
version Version? @relation(fields: [versionId], references: [id])
|
||||||
|
parent Task? @relation("TaskChildren", fields: [parentId], references: [id])
|
||||||
|
children Task[] @relation("TaskChildren")
|
||||||
|
assignee User? @relation("TaskAssignee", fields: [assigneeId], references: [id])
|
||||||
|
creator User @relation("TaskCreator", fields: [creatorId], references: [id])
|
||||||
|
watchers TaskWatcher[]
|
||||||
|
comments Comment[]
|
||||||
|
|
||||||
|
@@map("tasks")
|
||||||
|
}
|
||||||
|
|
||||||
|
model TaskWatcher {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
taskId String @map("task_id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
|
||||||
|
task Task @relation(fields: [taskId], references: [id])
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@unique([taskId, userId])
|
||||||
|
@@map("task_watchers")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Comment {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
taskId String @map("task_id")
|
||||||
|
authorId String @map("author_id")
|
||||||
|
content String
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
task Task @relation(fields: [taskId], references: [id])
|
||||||
|
|
||||||
|
@@map("comments")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ProjectMember {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
projectId String @map("project_id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
role String @default("member")
|
||||||
|
|
||||||
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@unique([projectId, userId])
|
||||||
|
@@map("project_members")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/server/prisma/
|
||||||
|
git commit -m "feat(server): 添加 Prisma Schema,定义全部核心数据模型"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: 初始化 Next.js 前端 (apps/web)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/web/package.json`
|
||||||
|
- Create: `apps/web/tsconfig.json`
|
||||||
|
- Create: `apps/web/next.config.js`
|
||||||
|
- Create: `apps/web/tailwind.config.ts`
|
||||||
|
- Create: `apps/web/postcss.config.js`
|
||||||
|
- Create: `apps/web/app/layout.tsx`
|
||||||
|
- Create: `apps/web/app/page.tsx`
|
||||||
|
- Create: `apps/web/app/globals.css`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建 apps/web/package.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev --port 3000",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint",
|
||||||
|
"type-check": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "^14.2.0",
|
||||||
|
"react": "^18.3.0",
|
||||||
|
"react-dom": "^18.3.0",
|
||||||
|
"zustand": "^4.5.0",
|
||||||
|
"@ftb/shared": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"@types/react": "^18.3.0",
|
||||||
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"autoprefixer": "^10.4.0",
|
||||||
|
"postcss": "^8.4.0",
|
||||||
|
"tailwindcss": "^3.4.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 apps/web/tsconfig.json**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [{ "name": "next" }],
|
||||||
|
"paths": { "@/*": ["./*"] }
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 创建 apps/web/next.config.js**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
transpilePackages: ['@ftb/shared'],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = nextConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 创建 apps/web/tailwind.config.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Config } from 'tailwindcss';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
|
||||||
|
theme: { extend: {} },
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 创建 apps/web/postcss.config.js**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: 创建 apps/web/app/globals.css**
|
||||||
|
|
||||||
|
```css
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 7: 创建 apps/web/app/layout.tsx**
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import './globals.css';
|
||||||
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'FTB 项目管理',
|
||||||
|
description: '智能项目管理系统',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 8: 创建 apps/web/app/page.tsx**
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen items-center justify-center">
|
||||||
|
<h1 className="text-3xl font-bold">FTB 项目管理系统</h1>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 9: 创建前端页面目录占位**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p apps/web/app/workspace
|
||||||
|
mkdir -p apps/web/app/products/[id]/requirements
|
||||||
|
mkdir -p apps/web/app/products/[id]/versions
|
||||||
|
mkdir -p apps/web/app/projects/[id]/board
|
||||||
|
mkdir -p apps/web/app/projects/[id]/gantt
|
||||||
|
mkdir -p apps/web/app/projects/[id]/sprints
|
||||||
|
mkdir -p apps/web/app/projects/[id]/settings
|
||||||
|
mkdir -p apps/web/app/admin/members
|
||||||
|
mkdir -p apps/web/components/ui
|
||||||
|
mkdir -p apps/web/components/board
|
||||||
|
mkdir -p apps/web/components/gantt
|
||||||
|
mkdir -p apps/web/components/shared
|
||||||
|
mkdir -p apps/web/hooks
|
||||||
|
mkdir -p apps/web/stores
|
||||||
|
mkdir -p apps/web/lib
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 10: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/web/
|
||||||
|
git commit -m "feat(web): 初始化 Next.js 前端骨架,配置 Tailwind CSS"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: Docker + 环境配置
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `docker-compose.yml`
|
||||||
|
- Create: `.env.example`
|
||||||
|
- Create: `apps/server/.env.example`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建 docker-compose.yml**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
ports:
|
||||||
|
- '5432:5432'
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: ftb_pm
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports:
|
||||||
|
- '6379:6379'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 .env.example**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 数据库
|
||||||
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm
|
||||||
|
|
||||||
|
# 认证
|
||||||
|
NEXTAUTH_SECRET=change-me-to-random-secret
|
||||||
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# AI
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-xxx
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
|
# 邮件通知(可选)
|
||||||
|
SMTP_HOST=smtp.example.com
|
||||||
|
SMTP_PORT=465
|
||||||
|
SMTP_USER=noreply@example.com
|
||||||
|
SMTP_PASS=your-smtp-password
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 创建 apps/server/.env.example**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ftb_pm
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-xxx
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add docker-compose.yml .env.example apps/server/.env.example
|
||||||
|
git commit -m "chore: 添加 Docker Compose 和环境变量示例配置"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: 验证整体项目结构
|
||||||
|
|
||||||
|
- [ ] **Step 1: 安装全部依赖**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 启动数据库**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 运行 Prisma 迁移**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/server && pnpm db:migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 验证后端启动**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev --filter=server
|
||||||
|
```
|
||||||
|
|
||||||
|
预期:控制台输出 `Nest application successfully started`,监听 3001 端口。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 验证前端启动**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev --filter=web
|
||||||
|
```
|
||||||
|
|
||||||
|
预期:控制台输出 `ready started server on 0.0.0.0:3000`,浏览器打开看到 "FTB 项目管理系统"。
|
||||||
|
|
||||||
|
- [ ] **Step 6: 类型检查通过**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm type-check
|
||||||
|
```
|
||||||
|
|
||||||
|
预期:无错误输出。
|
||||||
134
docs/superpowers/specs/2026-06-08-project-structure-design.md
Normal file
134
docs/superpowers/specs/2026-06-08-project-structure-design.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# FTB 智能项目管理系统 — 项目结构设计
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
基于 AI 能力的项目管理平台,核心层级:产品 → 项目 → 迭代 → 任务。采用 Turborepo monorepo + Next.js + NestJS + PostgreSQL 技术栈。
|
||||||
|
|
||||||
|
## 数据层级
|
||||||
|
|
||||||
|
```
|
||||||
|
产品 (Product) — 顶层容器
|
||||||
|
├── 需求池 (Requirement) — 需求管理、评审流转
|
||||||
|
├── 发布版本 (Version) — v1.0/v2.0,关联多个任务追踪发布范围
|
||||||
|
└── 项目 (Project) — 归属于产品
|
||||||
|
├── 迭代 (Sprint) — 时间盒开发周期
|
||||||
|
└── 任务 (Task) — 归属迭代,可关联版本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 后端模块划分
|
||||||
|
|
||||||
|
| 模块 | 职责 | 关键实体 |
|
||||||
|
|------|------|---------|
|
||||||
|
| product | 产品 CRUD、需求池管理、需求评审状态流转 | Product, Requirement |
|
||||||
|
| version | 产品发布版本、版本规划、关联任务范围 | Version |
|
||||||
|
| project | 项目 CRUD、项目设置 | Project |
|
||||||
|
| sprint | 迭代管理、迭代规划、燃尽图数据源 | Sprint |
|
||||||
|
| task | 任务 CRUD、状态机、看板、甘特图、子任务 | Task, Comment, Attachment |
|
||||||
|
| member | 用户管理、角色分配(RBAC)、项目成员 | User, Role, ProjectMember |
|
||||||
|
| dashboard | "与我相关"聚合视图 | 无独立实体,聚合查询 |
|
||||||
|
| ai | 任务智能分解、风险预警、排期建议 | AiLog |
|
||||||
|
| common | 守卫、拦截器、管道、装饰器 | — |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ftb-project-management/
|
||||||
|
├── apps/
|
||||||
|
│ ├── web/ # Next.js 14+ (App Router)
|
||||||
|
│ │ ├── app/
|
||||||
|
│ │ │ ├── (auth)/ # 登录、注册
|
||||||
|
│ │ │ ├── workspace/ # "与我相关"
|
||||||
|
│ │ │ ├── products/ # 产品列表 + 详情
|
||||||
|
│ │ │ │ └── [id]/
|
||||||
|
│ │ │ │ ├── requirements/ # 需求池
|
||||||
|
│ │ │ │ └── versions/ # 版本管理
|
||||||
|
│ │ │ ├── projects/
|
||||||
|
│ │ │ │ └── [id]/
|
||||||
|
│ │ │ │ ├── board/ # 看板
|
||||||
|
│ │ │ │ ├── gantt/ # 甘特图
|
||||||
|
│ │ │ │ ├── sprints/ # 迭代
|
||||||
|
│ │ │ │ └── settings/ # 项目设置+成员
|
||||||
|
│ │ │ └── admin/ # 全局管理
|
||||||
|
│ │ ├── components/
|
||||||
|
│ │ │ ├── ui/ # Shadcn 基础组件
|
||||||
|
│ │ │ ├── board/ # 看板组件
|
||||||
|
│ │ │ ├── gantt/ # 甘特图组件
|
||||||
|
│ │ │ └── shared/ # 通用业务组件
|
||||||
|
│ │ ├── hooks/
|
||||||
|
│ │ ├── stores/ # Zustand stores
|
||||||
|
│ │ └── lib/ # 工具函数、API client
|
||||||
|
│ └── server/ # NestJS 后端
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── modules/
|
||||||
|
│ │ │ ├── product/
|
||||||
|
│ │ │ ├── version/
|
||||||
|
│ │ │ ├── project/
|
||||||
|
│ │ │ ├── sprint/
|
||||||
|
│ │ │ ├── task/
|
||||||
|
│ │ │ ├── member/
|
||||||
|
│ │ │ ├── dashboard/
|
||||||
|
│ │ │ └── ai/
|
||||||
|
│ │ ├── common/ # 守卫、拦截器、管道
|
||||||
|
│ │ └── prisma/ # Schema + Migrations
|
||||||
|
│ └── test/
|
||||||
|
├── packages/
|
||||||
|
│ └── shared/ # 前后端共享类型、枚举、常量
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── docker-compose.prod.yml
|
||||||
|
└── turbo.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端路由
|
||||||
|
|
||||||
|
| 路由 | 页面 |
|
||||||
|
|------|------|
|
||||||
|
| `/login` | 登录 |
|
||||||
|
| `/workspace` | "与我相关"(我的任务、我创建的、我关注的) |
|
||||||
|
| `/products` | 产品列表 |
|
||||||
|
| `/products/[id]` | 产品详情 |
|
||||||
|
| `/products/[id]/requirements` | 需求池 |
|
||||||
|
| `/products/[id]/versions` | 版本管理 |
|
||||||
|
| `/projects/[id]/board` | 看板 |
|
||||||
|
| `/projects/[id]/gantt` | 甘特图 |
|
||||||
|
| `/projects/[id]/sprints` | 迭代管理 |
|
||||||
|
| `/projects/[id]/settings` | 项目设置 + 成员管理 |
|
||||||
|
| `/admin/members` | 全局人员管理 |
|
||||||
|
|
||||||
|
## 任务状态机
|
||||||
|
|
||||||
|
```
|
||||||
|
todo → in_progress → in_review → done → closed
|
||||||
|
```
|
||||||
|
|
||||||
|
看板列对应状态,支持自定义列名但不改变底层状态枚举。
|
||||||
|
|
||||||
|
## 关键数据关系
|
||||||
|
|
||||||
|
- Product 1:N Project(产品下有多个项目)
|
||||||
|
- Product 1:N Requirement(产品有需求池)
|
||||||
|
- Product 1:N Version(产品有发布版本)
|
||||||
|
- Project 1:N Sprint(项目有多个迭代)
|
||||||
|
- Sprint 1:N Task(迭代包含多个任务)
|
||||||
|
- Task N:1 Version(任务可关联到某个发布版本)
|
||||||
|
- Task 自引用(parent_id,无限层级子任务)
|
||||||
|
- Requirement → Task(需求可一键转为任务)
|
||||||
|
- Task 有 assignee、creator、watchers
|
||||||
|
|
||||||
|
## 权限模型
|
||||||
|
|
||||||
|
项目级 RBAC,四个角色:
|
||||||
|
- **Owner** — 项目所有者,全部权限
|
||||||
|
- **Admin** — 管理成员、修改项目设置
|
||||||
|
- **Member** — 创建/编辑任务、评论
|
||||||
|
- **Viewer** — 只读访问
|
||||||
|
|
||||||
|
全局管理员可管理所有产品和人员。
|
||||||
|
|
||||||
|
## AI 模块
|
||||||
|
|
||||||
|
独立 NestJS Module,统一通过 AiGateway 调用:
|
||||||
|
- 任务智能分解:输入需求描述,输出结构化子任务列表
|
||||||
|
- 风险预警:分析项目延期风险,基于历史数据和当前进度
|
||||||
|
- 排期建议:基于成员负载和任务依赖给出时间安排
|
||||||
|
|
||||||
|
所有 AI 调用记录到 AiLog 表,支持审计和 token 用量追踪。
|
||||||
19
package.json
Normal file
19
package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "ftb-project-management",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "turbo dev",
|
||||||
|
"build": "turbo build",
|
||||||
|
"lint": "turbo lint",
|
||||||
|
"type-check": "turbo type-check",
|
||||||
|
"test": "turbo test",
|
||||||
|
"db:migrate": "pnpm --filter=server db:migrate",
|
||||||
|
"db:seed": "pnpm --filter=server db:seed",
|
||||||
|
"db:studio": "pnpm --filter=server db:studio"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"turbo": "^2.0.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@9.4.0"
|
||||||
|
}
|
||||||
14
packages/shared/package.json
Normal file
14
packages/shared/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "@ftb/shared",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"lint": "eslint src/"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
packages/shared/src/enums.ts
Normal file
22
packages/shared/src/enums.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export enum TaskStatus {
|
||||||
|
TODO = 'todo',
|
||||||
|
IN_PROGRESS = 'in_progress',
|
||||||
|
IN_REVIEW = 'in_review',
|
||||||
|
DONE = 'done',
|
||||||
|
CLOSED = 'closed',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProjectRole {
|
||||||
|
OWNER = 'owner',
|
||||||
|
ADMIN = 'admin',
|
||||||
|
MEMBER = 'member',
|
||||||
|
VIEWER = 'viewer',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RequirementStatus {
|
||||||
|
DRAFT = 'draft',
|
||||||
|
REVIEWING = 'reviewing',
|
||||||
|
APPROVED = 'approved',
|
||||||
|
REJECTED = 'rejected',
|
||||||
|
DELIVERED = 'delivered',
|
||||||
|
}
|
||||||
2
packages/shared/src/index.ts
Normal file
2
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './enums';
|
||||||
|
export * from './types';
|
||||||
68
packages/shared/src/types.ts
Normal file
68
packages/shared/src/types.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { TaskStatus, ProjectRole, RequirementStatus } from './enums';
|
||||||
|
|
||||||
|
export interface Product {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Task {
|
||||||
|
id: string;
|
||||||
|
projectId: string;
|
||||||
|
sprintId: string | null;
|
||||||
|
versionId: string | null;
|
||||||
|
parentId: string | null;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
status: TaskStatus;
|
||||||
|
assigneeId: string | null;
|
||||||
|
creatorId: string;
|
||||||
|
priority: number;
|
||||||
|
startDate: Date | null;
|
||||||
|
dueDate: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Requirement {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
status: RequirementStatus;
|
||||||
|
priority: number;
|
||||||
|
creatorId: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Version {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
releaseDate: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Sprint {
|
||||||
|
id: string;
|
||||||
|
projectId: string;
|
||||||
|
name: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
13
packages/shared/tsconfig.json
Normal file
13
packages/shared/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "./dist"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
packages:
|
||||||
|
- "apps/*"
|
||||||
|
- "packages/*"
|
||||||
22
turbo.json
Normal file
22
turbo.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://turbo.build/schema.json",
|
||||||
|
"tasks": {
|
||||||
|
"dev": {
|
||||||
|
"cache": false,
|
||||||
|
"persistent": true
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": [".next/**", "dist/**"]
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
},
|
||||||
|
"type-check": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user