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:
Script Generator
2026-06-08 10:31:35 +08:00
commit 8de1f93fd3
30 changed files with 1785 additions and 0 deletions

3
apps/server/.env.example Normal file
View 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

View 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
View 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"
}
}

View 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")
}

View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

19
apps/web/app/layout.tsx Normal file
View 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
View 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
View File

@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['@ftb/shared'],
};
module.exports = nextConfig;

28
apps/web/package.json Normal file
View 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"
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View 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
View 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"]
}