# 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
```

预期：无错误输出。
