|
|
@@ -0,0 +1,925 @@
|
|
|
+# shadcn全栈管理后台启动模板 Fullstack Architecture Document
|
|
|
+
|
|
|
+## 1. Introduction
|
|
|
+
|
|
|
+本文档概述了shadcn全栈管理后台启动模板的完整全栈架构,包括后端系统、前端实现及其集成。它作为AI驱动开发的单一事实来源,确保整个技术栈的一致性。
|
|
|
+
|
|
|
+这种统一方法将传统上分离的后端和前端架构文档结合起来,为现代全栈应用程序简化了开发流程,这些应用程序中这些关注点日益交织在一起。
|
|
|
+
|
|
|
+## 2. High Level Architecture
|
|
|
+
|
|
|
+### 2.1 Technical Summary
|
|
|
+
|
|
|
+shadcn全栈管理后台启动模板采用现代化的Jamstack架构风格,结合React前端和Hono后端框架。前端基于Vite构建工具和Tailwind CSS样式方案,后端使用TypeORM进行数据库操作和MySQL作为主数据库。架构采用单体部署方式,通过清晰的代码分离结构确保前后端开发一致性。该架构通过RESTful API规范、OpenAPI文档生成和类型安全的客户端集成,实现了PRD中要求的完整用户认证、权限管理和生产就绪状态。
|
|
|
+
|
|
|
+### 2.2 Platform and Infrastructure Choice
|
|
|
+
|
|
|
+基于PRD要求和开发环境配置,采用多八多云端开发容器环境作为部署平台:
|
|
|
+
|
|
|
+**平台选择:多八多云端容器环境**
|
|
|
+- **前端部署:** 同一容器内的Node.js服务
|
|
|
+- **后端部署:** 同一容器内的Node.js服务
|
|
|
+- **数据库:** MySQL 8.0.36(容器内,默认数据库d8dai)
|
|
|
+- **缓存:** Redis 7(容器内)
|
|
|
+- **文件存储:** MinIO(容器内)
|
|
|
+- **连接方式:** 所有服务使用默认参数连接,正式环境参数相同
|
|
|
+
|
|
|
+### 2.3 Repository Structure
|
|
|
+
|
|
|
+**结构:** Monorepo结构
|
|
|
+**Monorepo工具:** Turborepo
|
|
|
+**包组织策略:**
|
|
|
+- apps/admin - 管理后台React应用(基于shadcn/ui)
|
|
|
+- apps/web - 面向用户的Web端React应用
|
|
|
+- apps/api - 后端Hono API
|
|
|
+- packages/shared - 共享类型和工具
|
|
|
+- packages/ui - 共享UI组件库
|
|
|
+- packages/config - 共享配置(ESLint、TypeScript等)
|
|
|
+
|
|
|
+### 2.4 High Level Architecture Diagram
|
|
|
+
|
|
|
+```mermaid
|
|
|
+graph TB
|
|
|
+ User[用户] --> Web[Web前端<br/>React + Vite]
|
|
|
+ User --> Mobile[移动端<br/>响应式设计]
|
|
|
+
|
|
|
+ Web --> API[API网关<br/>Hono框架]
|
|
|
+ Mobile --> API
|
|
|
+
|
|
|
+ API --> Auth[认证服务<br/>JWT + bcrypt]
|
|
|
+ API --> UserService[用户服务]
|
|
|
+ API --> DataService[数据服务]
|
|
|
+
|
|
|
+ Auth --> DB[(MySQL数据库)]
|
|
|
+ UserService --> DB
|
|
|
+ DataService --> DB
|
|
|
+
|
|
|
+ API --> Cache[Redis缓存]
|
|
|
+ API --> Storage[MinIO存储]
|
|
|
+
|
|
|
+ API --> External[外部服务<br/>OpenAI等]
|
|
|
+```
|
|
|
+
|
|
|
+### 2.5 Architectural Patterns
|
|
|
+
|
|
|
+**Jamstack架构:** 静态站点生成与无服务器API结合 - *理由:* 为内容密集型应用提供最佳性能和可扩展性
|
|
|
+**组件化UI:** 基于TypeScript的可复用React组件 - *理由:* 在大型代码库中保持可维护性和类型安全
|
|
|
+**仓库模式:** 抽象数据访问逻辑 - *理由:* 支持测试和未来数据库迁移的灵活性
|
|
|
+**API网关模式:** 所有API调用的单一入口点 - *理由:* 集中化认证、速率限制和监控
|
|
|
+
|
|
|
+## 3. Tech Stack
|
|
|
+
|
|
|
+这是项目的**最终技术选择**。与用户合作确定所有选择。此表是单一事实来源 - 所有开发必须使用这些确切版本。
|
|
|
+
|
|
|
+### Technology Stack Table
|
|
|
+
|
|
|
+| 类别 | 技术 | 版本 | 用途 | 理由 |
|
|
|
+|------|------|------|------|------|
|
|
|
+| 前端语言 | TypeScript | 5.0+ | 类型安全的React开发 | 提供完整的类型安全和开发体验 |
|
|
|
+| 前端框架 | React | 19 | 用户界面构建 | 现代React特性,更好的性能 |
|
|
|
+| UI组件库 | shadcn/ui | 最新 | 管理后台UI组件 | 基于Tailwind的专业设计系统 |
|
|
|
+| 状态管理 | React Query (TanStack) | 5 | 服务器状态管理 | 简化API数据管理和缓存 |
|
|
|
+| 后端语言 | TypeScript | 5.0+ | 类型安全的Node.js开发 | 前后端类型一致性 |
|
|
|
+| 后端框架 | Hono | 4 | Web框架和API开发 | 轻量级、高性能、TypeScript友好 |
|
|
|
+| API风格 | REST | - | API接口规范 | 简单、通用、工具生态丰富 |
|
|
|
+| 数据库 | MySQL | 8.0.36 | 主数据存储 | 关系型数据库,成熟稳定 |
|
|
|
+| 缓存 | Redis | 7 | 会话和缓存 | 高性能内存数据库 |
|
|
|
+| 文件存储 | MinIO | 最新 | 对象存储 | S3兼容,自托管方案 |
|
|
|
+| 认证 | JWT + bcrypt | 最新 | 用户认证和授权 | 无状态认证,密码安全加密 |
|
|
|
+| 前端测试 | Vitest | 最新 | 单元和组件测试 | Vite生态,快速测试运行 |
|
|
|
+| 后端测试 | Vitest | 最新 | API和单元测试 | 统一测试工具链 |
|
|
|
+| E2E测试 | Playwright | 最新 | 端到端测试 | 跨浏览器测试支持 |
|
|
|
+| 构建工具 | Vite | 7 | 前端构建和开发服务器 | 快速冷启动,优秀开发体验 |
|
|
|
+| 打包工具 | Vite | 7 | 代码打包和优化 | 统一构建工具链 |
|
|
|
+| IaC工具 | Docker Compose | 最新 | 本地环境编排 | 容器化开发环境 |
|
|
|
+| CI/CD | GitHub Actions | - | 自动化部署 | 与GitHub生态集成 |
|
|
|
+| 监控 | - | - | 应用监控 | 后续阶段实现 |
|
|
|
+| 日志 | - | - | 应用日志 | 后续阶段实现 |
|
|
|
+| CSS框架 | Tailwind CSS | 4 | 样式方案 | 实用优先,设计系统友好 |
|
|
|
+
|
|
|
+## 4. Data Models
|
|
|
+
|
|
|
+基于PRD需求,定义将在前端和后端之间共享的核心数据模型/实体:
|
|
|
+
|
|
|
+### 4.1 User(用户模型)
|
|
|
+
|
|
|
+**用途:** 管理系统用户账户信息,支持认证和权限管理
|
|
|
+
|
|
|
+**关键属性:**
|
|
|
+- id: string - 用户唯一标识符(UUID)
|
|
|
+- email: string - 用户邮箱,用于登录和通信
|
|
|
+- password: string - 加密后的密码(bcrypt哈希)
|
|
|
+- name: string - 用户显示名称
|
|
|
+- role: string - 用户角色(admin, user等)
|
|
|
+- status: string - 用户状态(active, inactive等)
|
|
|
+- createdAt: Date - 创建时间
|
|
|
+- updatedAt: Date - 最后更新时间
|
|
|
+
|
|
|
+**TypeScript接口:**
|
|
|
+```typescript
|
|
|
+interface User {
|
|
|
+ id: string;
|
|
|
+ email: string;
|
|
|
+ password: string;
|
|
|
+ name: string;
|
|
|
+ role: UserRole;
|
|
|
+ status: UserStatus;
|
|
|
+ createdAt: Date;
|
|
|
+ updatedAt: Date;
|
|
|
+}
|
|
|
+
|
|
|
+type UserRole = 'admin' | 'user';
|
|
|
+type UserStatus = 'active' | 'inactive' | 'suspended';
|
|
|
+```
|
|
|
+
|
|
|
+**关系:**
|
|
|
+- 一个用户可以创建多个内容条目
|
|
|
+- 一个用户可以属于多个角色(如需扩展)
|
|
|
+
|
|
|
+### 4.2 Role(角色模型)
|
|
|
+
|
|
|
+**用途:** 管理系统角色和权限配置
|
|
|
+
|
|
|
+**关键属性:**
|
|
|
+- id: string - 角色唯一标识符
|
|
|
+- name: string - 角色名称
|
|
|
+- permissions: string[] - 权限列表
|
|
|
+- description: string - 角色描述
|
|
|
+- createdAt: Date - 创建时间
|
|
|
+
|
|
|
+**TypeScript接口:**
|
|
|
+```typescript
|
|
|
+interface Role {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ permissions: Permission[];
|
|
|
+ description: string;
|
|
|
+ createdAt: Date;
|
|
|
+}
|
|
|
+
|
|
|
+type Permission = 'user:read' | 'user:write' | 'content:read' | 'content:write' | 'settings:read' | 'settings:write';
|
|
|
+```
|
|
|
+
|
|
|
+**关系:**
|
|
|
+- 一个角色可以分配给多个用户
|
|
|
+
|
|
|
+### 会话管理策略
|
|
|
+**会话存储:** Redis
|
|
|
+**存储内容:** JWT令牌黑名单、用户会话状态
|
|
|
+**过期策略:** 基于JWT过期时间自动清理
|
|
|
+
|
|
|
+## 5. API Specification
|
|
|
+
|
|
|
+基于技术栈中选择的REST API风格,创建OpenAPI 3.0规范:
|
|
|
+
|
|
|
+### OpenAPI 3.0规范(修订)
|
|
|
+
|
|
|
+```yaml
|
|
|
+openapi: 3.0.0
|
|
|
+info:
|
|
|
+ title: shadcn管理后台API
|
|
|
+ version: 1.0.0
|
|
|
+ description: 基于Hono框架的RESTful API,支持用户认证和权限管理
|
|
|
+servers:
|
|
|
+ - url: http://localhost:8080/api
|
|
|
+ description: 本地开发环境(8080端口)
|
|
|
+ - url: https://your-domain.com:8080/api
|
|
|
+ description: 生产环境(8080端口)
|
|
|
+
|
|
|
+components:
|
|
|
+ securitySchemes:
|
|
|
+ bearerAuth:
|
|
|
+ type: http
|
|
|
+ scheme: bearer
|
|
|
+ bearerFormat: JWT
|
|
|
+
|
|
|
+ schemas:
|
|
|
+ User:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ id:
|
|
|
+ type: string
|
|
|
+ format: uuid
|
|
|
+ email:
|
|
|
+ type: string
|
|
|
+ format: email
|
|
|
+ name:
|
|
|
+ type: string
|
|
|
+ role:
|
|
|
+ type: string
|
|
|
+ enum: [admin, user]
|
|
|
+ status:
|
|
|
+ type: string
|
|
|
+ enum: [active, inactive, suspended]
|
|
|
+ createdAt:
|
|
|
+ type: string
|
|
|
+ format: date-time
|
|
|
+ updatedAt:
|
|
|
+ type: string
|
|
|
+ format: date-time
|
|
|
+
|
|
|
+ AuthResponse:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ user:
|
|
|
+ $ref: '#/components/schemas/User'
|
|
|
+ token:
|
|
|
+ type: string
|
|
|
+ expiresIn:
|
|
|
+ type: integer
|
|
|
+
|
|
|
+ ErrorResponse:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ error:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ code:
|
|
|
+ type: string
|
|
|
+ message:
|
|
|
+ type: string
|
|
|
+ details:
|
|
|
+ type: object
|
|
|
+
|
|
|
+paths:
|
|
|
+ /auth/login:
|
|
|
+ post:
|
|
|
+ summary: 用户登录
|
|
|
+ tags: [认证]
|
|
|
+ requestBody:
|
|
|
+ required: true
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ email:
|
|
|
+ type: string
|
|
|
+ format: email
|
|
|
+ password:
|
|
|
+ type: string
|
|
|
+ responses:
|
|
|
+ '200':
|
|
|
+ description: 登录成功
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ $ref: '#/components/schemas/AuthResponse'
|
|
|
+ '401':
|
|
|
+ description: 认证失败
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ $ref: '#/components/schemas/ErrorResponse'
|
|
|
+
|
|
|
+ /auth/register:
|
|
|
+ post:
|
|
|
+ summary: 用户注册
|
|
|
+ tags: [认证]
|
|
|
+ requestBody:
|
|
|
+ required: true
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ email:
|
|
|
+ type: string
|
|
|
+ format: email
|
|
|
+ password:
|
|
|
+ type: string
|
|
|
+ name:
|
|
|
+ type: string
|
|
|
+ responses:
|
|
|
+ '201':
|
|
|
+ description: 注册成功
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ $ref: '#/components/schemas/AuthResponse'
|
|
|
+ '400':
|
|
|
+ description: 请求参数错误
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ $ref: '#/components/schemas/ErrorResponse'
|
|
|
+
|
|
|
+ /users:
|
|
|
+ get:
|
|
|
+ summary: 获取用户列表
|
|
|
+ tags: [用户管理]
|
|
|
+ security:
|
|
|
+ - bearerAuth: []
|
|
|
+ parameters:
|
|
|
+ - name: page
|
|
|
+ in: query
|
|
|
+ schema:
|
|
|
+ type: integer
|
|
|
+ default: 1
|
|
|
+ - name: limit
|
|
|
+ in: query
|
|
|
+ schema:
|
|
|
+ type: integer
|
|
|
+ default: 20
|
|
|
+ responses:
|
|
|
+ '200':
|
|
|
+ description: 用户列表
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ type: object
|
|
|
+ properties:
|
|
|
+ users:
|
|
|
+ type: array
|
|
|
+ items:
|
|
|
+ $ref: '#/components/schemas/User'
|
|
|
+ total:
|
|
|
+ type: integer
|
|
|
+ page:
|
|
|
+ type: integer
|
|
|
+ limit:
|
|
|
+ type: integer
|
|
|
+ '401':
|
|
|
+ description: 未授权
|
|
|
+ content:
|
|
|
+ application/json:
|
|
|
+ schema:
|
|
|
+ $ref: '#/components/schemas/ErrorResponse'
|
|
|
+```
|
|
|
+
|
|
|
+### 基于hono/client的RPC方式
|
|
|
+
|
|
|
+**后端API路由定义:**
|
|
|
+```typescript
|
|
|
+// apps/api/src/routes/index.ts
|
|
|
+export const app = new Hono()
|
|
|
+ .route('/auth', authRoutes)
|
|
|
+ .route('/users', userRoutes);
|
|
|
+
|
|
|
+export type AppType = typeof app;
|
|
|
+```
|
|
|
+
|
|
|
+**前端类型安全调用:**
|
|
|
+```typescript
|
|
|
+// apps/admin/src/lib/api-client.ts
|
|
|
+import { hc } from 'hono/client';
|
|
|
+import type { AppType } from '@api/routes'; // 直接从后端导入类型
|
|
|
+
|
|
|
+export const apiClient = hc<AppType>('http://localhost:8080');
|
|
|
+
|
|
|
+// 类型安全的API调用
|
|
|
+const response = await apiClient.auth.login.$post({
|
|
|
+ json: { email: 'user@example.com', password: 'password123' }
|
|
|
+});
|
|
|
+const data = await response.json(); // 自动类型推断
|
|
|
+```
|
|
|
+
|
|
|
+### Zod Schema独立配置
|
|
|
+
|
|
|
+**共享Zod Schema定义:**
|
|
|
+```typescript
|
|
|
+// packages/shared/src/schemas/index.ts
|
|
|
+export const userSchema = {
|
|
|
+ create: z.object({
|
|
|
+ email: z.string().email('请输入有效的邮箱地址'),
|
|
|
+ name: z.string().min(1, '姓名不能为空'),
|
|
|
+ password: z.string().min(6, '密码至少6位'),
|
|
|
+ role: z.enum(['admin', 'user']).default('user'),
|
|
|
+ }),
|
|
|
+
|
|
|
+ update: z.object({
|
|
|
+ email: z.string().email('请输入有效的邮箱地址').optional(),
|
|
|
+ name: z.string().min(1, '姓名不能为空').optional(),
|
|
|
+ role: z.enum(['admin', 'user']).optional(),
|
|
|
+ status: z.enum(['active', 'inactive', 'suspended']).optional(),
|
|
|
+ }).partial(),
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+## 6. Components
|
|
|
+
|
|
|
+基于架构模式、技术栈和数据模型,识别全栈中的主要逻辑组件/服务:
|
|
|
+
|
|
|
+### 6.1 认证服务 (AuthService)
|
|
|
+
|
|
|
+**责任:** 处理用户认证、注册、JWT令牌生成和验证
|
|
|
+**关键接口:**
|
|
|
+- `login(email: string, password: string): Promise<AuthResponse>`
|
|
|
+- `register(userData: RegisterData): Promise<AuthResponse>`
|
|
|
+- `verifyToken(token: string): Promise<User>`
|
|
|
+- `logout(token: string): Promise<void>`
|
|
|
+**依赖:** UserService、Redis(令牌管理)
|
|
|
+**技术栈:** Hono中间件、bcrypt、jsonwebtoken、Redis客户端
|
|
|
+
|
|
|
+### 6.2 用户服务 (UserService)
|
|
|
+
|
|
|
+**责任:** 管理用户CRUD操作、权限验证和用户状态管理
|
|
|
+**关键接口:**
|
|
|
+- `getUsers(query: UserQuery): Promise<User[]>`
|
|
|
+- `getUserById(id: string): Promise<User>`
|
|
|
+- `createUser(userData: CreateUserData): Promise<User>`
|
|
|
+- `updateUser(id: string, updates: Partial<User>): Promise<User>`
|
|
|
+- `deleteUser(id: string): Promise<void>`
|
|
|
+**依赖:** 数据库(TypeORM)、RoleService
|
|
|
+**技术栈:** TypeORM Repository模式、数据验证
|
|
|
+
|
|
|
+### 6.3 通用CRUD服务 (GenericCRUDService)
|
|
|
+
|
|
|
+**责任:** 提供通用的CRUD操作基类,支持快速创建实体服务
|
|
|
+**关键接口:**
|
|
|
+- `findAll(query: PaginationQuery): Promise<Entity[]>`
|
|
|
+- `findById(id: string): Promise<Entity>`
|
|
|
+- `create(data: CreateDTO): Promise<Entity>`
|
|
|
+- `update(id: string, data: UpdateDTO): Promise<Entity>`
|
|
|
+- `delete(id: string): Promise<void>`
|
|
|
+**依赖:** TypeORM Repository、Zod验证
|
|
|
+**技术栈:** TypeORM泛型、类继承、装饰器模式
|
|
|
+
|
|
|
+### 6.4 通用CRUD路由 (GenericCRUDRouter)
|
|
|
+
|
|
|
+**责任:** 自动生成标准RESTful路由,支持通用CRUD操作
|
|
|
+**关键接口:**
|
|
|
+- `GET /entity` - 获取列表(支持分页、过滤、排序)
|
|
|
+- `GET /entity/:id` - 获取单个实体
|
|
|
+- `POST /entity` - 创建实体
|
|
|
+- `PUT /entity/:id` - 更新实体
|
|
|
+- `DELETE /entity/:id` - 删除实体
|
|
|
+**依赖:** GenericCRUDService、Zod验证
|
|
|
+**技术栈:** Hono路由工厂、OpenAPI自动生成
|
|
|
+
|
|
|
+### 6.5 组件关系图
|
|
|
+
|
|
|
+```mermaid
|
|
|
+flowchart TB
|
|
|
+ Frontend[前端应用] --> APIGateway[API网关]
|
|
|
+
|
|
|
+ APIGateway --> AuthService[认证服务]
|
|
|
+ APIGateway --> UserService[用户服务]
|
|
|
+ APIGateway --> RoleService[角色服务]
|
|
|
+
|
|
|
+ AuthService --> Redis[Redis缓存]
|
|
|
+ UserService --> DB[(MySQL数据库)]
|
|
|
+ RoleService --> DB
|
|
|
+
|
|
|
+ AuthService --> UserService
|
|
|
+ UserService --> RoleService
|
|
|
+```
|
|
|
+
|
|
|
+## 7. Core Workflows
|
|
|
+
|
|
|
+使用序列图说明关键系统工作流:
|
|
|
+
|
|
|
+### 7.1 用户登录工作流
|
|
|
+
|
|
|
+```mermaid
|
|
|
+sequenceDiagram
|
|
|
+ participant User as 用户
|
|
|
+ participant Frontend as 前端应用
|
|
|
+ participant AuthService as 认证服务
|
|
|
+ participant UserService as 用户服务
|
|
|
+ participant Redis as Redis缓存
|
|
|
+ participant DB as MySQL数据库
|
|
|
+
|
|
|
+ User->>Frontend: 输入邮箱密码
|
|
|
+ Frontend->>AuthService: POST /auth/login
|
|
|
+ AuthService->>UserService: 验证用户凭证
|
|
|
+ UserService->>DB: 查询用户信息
|
|
|
+ DB-->>UserService: 返回用户数据
|
|
|
+ UserService-->>AuthService: 验证结果
|
|
|
+
|
|
|
+ alt 验证成功
|
|
|
+ AuthService->>AuthService: 生成JWT令牌
|
|
|
+ AuthService->>Redis: 存储令牌信息
|
|
|
+ AuthService-->>Frontend: 返回用户信息和令牌
|
|
|
+ Frontend->>Frontend: 存储令牌到localStorage
|
|
|
+ Frontend-->>User: 登录成功,跳转首页
|
|
|
+ else 验证失败
|
|
|
+ AuthService-->>Frontend: 返回错误信息
|
|
|
+ Frontend-->>User: 显示错误提示
|
|
|
+ end
|
|
|
+```
|
|
|
+
|
|
|
+### 7.2 用户注册工作流
|
|
|
+
|
|
|
+```mermaid
|
|
|
+sequenceDiagram
|
|
|
+ participant User as 用户
|
|
|
+ participant Frontend as 前端应用
|
|
|
+ participant AuthService as 认证服务
|
|
|
+ participant UserService as 用户服务
|
|
|
+ participant DB as MySQL数据库
|
|
|
+
|
|
|
+ User->>Frontend: 填写注册信息
|
|
|
+ Frontend->>Frontend: Zod表单验证
|
|
|
+ Frontend->>AuthService: POST /auth/register
|
|
|
+ AuthService->>UserService: 创建新用户
|
|
|
+ UserService->>DB: 检查邮箱是否已存在
|
|
|
+ DB-->>UserService: 返回检查结果
|
|
|
+
|
|
|
+ alt 邮箱可用
|
|
|
+ UserService->>UserService: 加密密码
|
|
|
+ UserService->>DB: 插入用户记录
|
|
|
+ DB-->>UserService: 返回创建的用户
|
|
|
+ UserService-->>AuthService: 用户创建成功
|
|
|
+ AuthService->>AuthService: 生成JWT令牌
|
|
|
+ AuthService-->>Frontend: 返回用户信息和令牌
|
|
|
+ Frontend-->>User: 注册成功,自动登录
|
|
|
+ else 邮箱已存在
|
|
|
+ UserService-->>AuthService: 返回错误
|
|
|
+ AuthService-->>Frontend: 返回错误信息
|
|
|
+ Frontend-->>User: 显示邮箱已存在提示
|
|
|
+ end
|
|
|
+```
|
|
|
+
|
|
|
+## 8. Database Schema
|
|
|
+
|
|
|
+将概念数据模型转换为具体的数据库Schema:
|
|
|
+
|
|
|
+### 8.1 MySQL数据库Schema
|
|
|
+
|
|
|
+```sql
|
|
|
+-- 用户表
|
|
|
+CREATE TABLE users (
|
|
|
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
|
|
|
+ email VARCHAR(255) UNIQUE NOT NULL,
|
|
|
+ password VARCHAR(255) NOT NULL COMMENT 'bcrypt加密后的密码',
|
|
|
+ name VARCHAR(100) NOT NULL,
|
|
|
+ role ENUM('admin', 'user') NOT NULL DEFAULT 'user',
|
|
|
+ status ENUM('active', 'inactive', 'suspended') NOT NULL DEFAULT 'active',
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
+ INDEX idx_email (email),
|
|
|
+ INDEX idx_role (role),
|
|
|
+ INDEX idx_status (status)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
+
|
|
|
+-- 角色表(支持权限扩展)
|
|
|
+CREATE TABLE roles (
|
|
|
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
|
|
|
+ name VARCHAR(50) UNIQUE NOT NULL,
|
|
|
+ permissions JSON NOT NULL COMMENT '权限列表JSON数组',
|
|
|
+ description TEXT,
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
+ INDEX idx_name (name)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
+
|
|
|
+-- 用户角色关联表(多对多关系)
|
|
|
+CREATE TABLE user_roles (
|
|
|
+ user_id CHAR(36) NOT NULL,
|
|
|
+ role_id CHAR(36) NOT NULL,
|
|
|
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ PRIMARY KEY (user_id, role_id),
|
|
|
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
|
+ FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
|
|
|
+ INDEX idx_user_id (user_id),
|
|
|
+ INDEX idx_role_id (role_id)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
+```
|
|
|
+
|
|
|
+### 8.2 TypeORM实体定义
|
|
|
+
|
|
|
+```typescript
|
|
|
+// apps/api/src/entities/User.entity.ts
|
|
|
+@Entity('users')
|
|
|
+export class User {
|
|
|
+ @PrimaryGeneratedColumn('uuid')
|
|
|
+ id: string;
|
|
|
+
|
|
|
+ @Column({ unique: true })
|
|
|
+ email: string;
|
|
|
+
|
|
|
+ @Column()
|
|
|
+ password: string;
|
|
|
+
|
|
|
+ @Column()
|
|
|
+ name: string;
|
|
|
+
|
|
|
+ @Column({ type: 'enum', enum: ['admin', 'user'], default: 'user' })
|
|
|
+ role: string;
|
|
|
+
|
|
|
+ @Column({ type: 'enum', enum: ['active', 'inactive', 'suspended'], default: 'active' })
|
|
|
+ status: string;
|
|
|
+
|
|
|
+ @CreateDateColumn()
|
|
|
+ createdAt: Date;
|
|
|
+
|
|
|
+ @UpdateDateColumn()
|
|
|
+ updatedAt: Date;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 9. Frontend Architecture
|
|
|
+
|
|
|
+定义前端特定的架构细节:
|
|
|
+
|
|
|
+### 9.1 组件架构
|
|
|
+
|
|
|
+#### 组件组织
|
|
|
+```
|
|
|
+src/
|
|
|
+├── components/
|
|
|
+│ ├── ui/ # 基础UI组件(可复用)
|
|
|
+│ ├── forms/ # 表单组件
|
|
|
+│ ├── layout/ # 布局组件
|
|
|
+│ └── features/ # 功能组件
|
|
|
+```
|
|
|
+
|
|
|
+#### 组件模板
|
|
|
+```typescript
|
|
|
+// components/ui/button/Button.tsx
|
|
|
+export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
+ variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
|
|
|
+ size?: 'default' | 'sm' | 'lg' | 'icon';
|
|
|
+}
|
|
|
+
|
|
|
+const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant = 'default', size = 'default', ...props }, ref) => {
|
|
|
+ return (
|
|
|
+ <button
|
|
|
+ className={cn(
|
|
|
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors',
|
|
|
+ // ...样式类
|
|
|
+ )}
|
|
|
+ ref={ref}
|
|
|
+ {...props}
|
|
|
+ />
|
|
|
+ );
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### 9.2 状态管理架构
|
|
|
+
|
|
|
+#### 状态结构
|
|
|
+```typescript
|
|
|
+// stores/auth-store.ts
|
|
|
+export const useAuthStore = create<AuthState>((set, get) => ({
|
|
|
+ user: null,
|
|
|
+ token: localStorage.getItem('token'),
|
|
|
+ isLoading: false,
|
|
|
+
|
|
|
+ login: async (email: string, password: string) => {
|
|
|
+ set({ isLoading: true });
|
|
|
+ try {
|
|
|
+ const response = await apiClient.auth.login.$post({ json: { email, password } });
|
|
|
+ const { user, token } = await response.json();
|
|
|
+
|
|
|
+ localStorage.setItem('token', token);
|
|
|
+ set({ user, token, isLoading: false });
|
|
|
+ } catch (error) {
|
|
|
+ set({ isLoading: false });
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ },
|
|
|
+}));
|
|
|
+```
|
|
|
+
|
|
|
+### 9.3 路由架构
|
|
|
+
|
|
|
+#### 路由组织
|
|
|
+```typescript
|
|
|
+// routes/index.tsx
|
|
|
+export const router = createBrowserRouter([
|
|
|
+ {
|
|
|
+ path: '/auth',
|
|
|
+ element: <AuthLayout />,
|
|
|
+ children: [
|
|
|
+ { path: 'login', element: <LoginPage /> },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ path: '/',
|
|
|
+ element: <AppLayout />,
|
|
|
+ children: [
|
|
|
+ { index: true, element: <DashboardPage /> },
|
|
|
+ { path: 'users', element: <UsersPage /> },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+]);
|
|
|
+```
|
|
|
+
|
|
|
+#### 保护路由模式
|
|
|
+```typescript
|
|
|
+// components/auth/ProtectedRoute.tsx
|
|
|
+export function ProtectedRoute({ children }: ProtectedRouteProps) {
|
|
|
+ const { user, isLoading, initialize } = useAuthStore();
|
|
|
+ const navigate = useNavigate();
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (!isLoading && !user) {
|
|
|
+ navigate('/auth/login');
|
|
|
+ }
|
|
|
+ }, [user, isLoading, navigate]);
|
|
|
+
|
|
|
+ return user ? <>{children}</> : null;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 9.4 前端服务层
|
|
|
+
|
|
|
+#### API客户端设置
|
|
|
+```typescript
|
|
|
+// lib/api-client.ts
|
|
|
+export const apiClient = hc<AppType>(import.meta.env.VITE_API_URL || 'http://localhost:8080');
|
|
|
+
|
|
|
+// 请求拦截器 - 自动添加token
|
|
|
+apiClient._fetch = async (input, init) => {
|
|
|
+ const token = localStorage.getItem('token');
|
|
|
+ const headers = new Headers(init?.headers);
|
|
|
+
|
|
|
+ if (token) {
|
|
|
+ headers.set('Authorization', `Bearer ${token}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ return fetch(input, { ...init, headers });
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+## 10. Backend Architecture
|
|
|
+
|
|
|
+定义后端特定的架构细节:
|
|
|
+
|
|
|
+### 10.1 服务架构
|
|
|
+
|
|
|
+#### 控制器/路由组织
|
|
|
+```
|
|
|
+src/
|
|
|
+├── routes/ # API路由
|
|
|
+├── services/ # 业务服务
|
|
|
+├── middleware/ # 中间件
|
|
|
+├── entities/ # TypeORM实体
|
|
|
+└── lib/ # 工具库
|
|
|
+```
|
|
|
+
|
|
|
+#### 控制器模板
|
|
|
+```typescript
|
|
|
+// routes/users.ts
|
|
|
+export const userRoutes = new Hono()
|
|
|
+ .use('*', authMiddleware)
|
|
|
+
|
|
|
+ .get('/', async (c) => {
|
|
|
+ const { page, limit } = c.req.query();
|
|
|
+ const users = await userService.getUsers({
|
|
|
+ page: page ? parseInt(page) : 1,
|
|
|
+ limit: limit ? parseInt(limit) : 20
|
|
|
+ });
|
|
|
+ return c.json(users);
|
|
|
+ })
|
|
|
+
|
|
|
+ .get('/:id', validationMiddleware(userSchema.idParam, 'param'), async (c) => {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const user = await userService.getUserById(id);
|
|
|
+ return c.json(user);
|
|
|
+ });
|
|
|
+```
|
|
|
+
|
|
|
+### 10.2 数据库架构
|
|
|
+
|
|
|
+#### Schema设计
|
|
|
+```typescript
|
|
|
+// entities/User.entity.ts
|
|
|
+@Entity('users')
|
|
|
+export class User {
|
|
|
+ @PrimaryGeneratedColumn('uuid')
|
|
|
+ id: string;
|
|
|
+
|
|
|
+ @Column({ unique: true })
|
|
|
+ email: string;
|
|
|
+
|
|
|
+ @Column()
|
|
|
+ password: string;
|
|
|
+
|
|
|
+ @BeforeInsert()
|
|
|
+ @BeforeUpdate()
|
|
|
+ async hashPassword() {
|
|
|
+ if (this.password) {
|
|
|
+ this.password = await bcrypt.hash(this.password, 12);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 数据访问层
|
|
|
+```typescript
|
|
|
+// services/GenericCRUDService.ts
|
|
|
+export abstract class GenericCRUDService<T extends BaseEntity> {
|
|
|
+ constructor(protected repository: Repository<T>) {}
|
|
|
+
|
|
|
+ async findAll(query: any): Promise<{ data: T[]; total: number }> {
|
|
|
+ const [data, total] = await this.repository.findAndCount({
|
|
|
+ skip: (query.page - 1) * query.limit,
|
|
|
+ take: query.limit,
|
|
|
+ });
|
|
|
+ return { data, total };
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 10.3 认证架构
|
|
|
+
|
|
|
+#### 认证流程
|
|
|
+```mermaid
|
|
|
+sequenceDiagram
|
|
|
+ participant Client as 客户端
|
|
|
+ participant Middleware as 认证中间件
|
|
|
+ participant Redis as Redis
|
|
|
+ participant Service as 用户服务
|
|
|
+
|
|
|
+ Client->>Middleware: 请求携带JWT
|
|
|
+ Middleware->>Middleware: 解析JWT令牌
|
|
|
+ Middleware->>Redis: 检查令牌是否在黑名单
|
|
|
+ Redis-->>Middleware: 返回检查结果
|
|
|
+
|
|
|
+ alt 令牌有效
|
|
|
+ Middleware->>Service: 根据用户ID获取用户信息
|
|
|
+ Service-->>Middleware: 返回用户数据
|
|
|
+ Middleware-->>Client: 认证通过,继续处理
|
|
|
+ else 令牌无效或过期
|
|
|
+ Middleware-->>Client: 返回401错误
|
|
|
+ end
|
|
|
+```
|
|
|
+
|
|
|
+#### 中间件/守卫
|
|
|
+```typescript
|
|
|
+// middleware/auth.ts
|
|
|
+export const authMiddleware = createMiddleware(async (c, next) => {
|
|
|
+ const authHeader = c.req.header('Authorization');
|
|
|
+
|
|
|
+ if (!authHeader?.startsWith('Bearer ')) {
|
|
|
+ return c.json({ error: 'Unauthorized' }, 401);
|
|
|
+ }
|
|
|
+
|
|
|
+ const token = authHeader.slice(7);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 检查Redis黑名单
|
|
|
+ const isBlacklisted = await redis.get(`blacklist:${token}`);
|
|
|
+ if (isBlacklisted) {
|
|
|
+ return c.json({ error: 'Token revoked' }, 401);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证JWT
|
|
|
+ const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string };
|
|
|
+ c.set('userId', decoded.userId);
|
|
|
+
|
|
|
+ await next();
|
|
|
+ } catch (error) {
|
|
|
+ return c.json({ error: 'Invalid token' }, 401);
|
|
|
+ }
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+## 11. Unified Project Structure
|
|
|
+
|
|
|
+创建包含前端和后端的Monorepo结构:
|
|
|
+
|
|
|
+### 项目目录结构
|
|
|
+```
|
|
|
+shadcn-admin-template/
|
|
|
+├── .github/ # CI/CD工作流
|
|
|
+├── apps/ # 应用包
|
|
|
+│ ├── admin/ # 管理后台前端
|
|
|
+│ ├── web/ # 用户Web端
|
|
|
+│ └── api/ # 后端API应用
|
|
|
+├── packages/ # 共享包
|
|
|
+│ ├── shared/ # 共享类型/工具
|
|
|
+│ ├── ui/ # 共享UI组件
|
|
|
+│ └── config/ # 共享配置
|
|
|
+├── infrastructure/ # 基础设施即代码
|
|
|
+├── scripts/ # 构建/部署脚本
|
|
|
+├── docs/ # 文档
|
|
|
+├── .env.example # 环境变量模板
|
|
|
+├── package.json # 根package.json
|
|
|
+├── turbo.json # Turborepo配置
|
|
|
+└── README.md
|
|
|
+```
|
|
|
+
|
|
|
+### Monorepo配置 (turbo.json)
|
|
|
+```json
|
|
|
+{
|
|
|
+ "$schema": "https://turbo.build/schema.json",
|
|
|
+ "pipeline": {
|
|
|
+ "build": {
|
|
|
+ "dependsOn": ["^build"],
|
|
|
+ "outputs": ["dist/**", ".next/**"]
|
|
|
+ },
|
|
|
+ "dev": {
|
|
|
+ "cache": false,
|
|
|
+ "persistent": true
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 根package.json脚本
|
|
|
+```json
|
|
|
+{
|
|
|
+ "scripts": {
|
|
|
+ "dev": "turbo run dev",
|
|
|
+ "build": "turbo run build",
|
|
|
+ "lint": "turbo run lint",
|
|
|
+ "test": "turbo run test",
|
|
|
+ "dev:admin": "turbo run dev --filter=admin",
|
|
|
+ "dev:api": "turbo run dev --filter=api"
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|