library-picturebook-activity/.cursor/rules/database-design.mdc
2025-12-09 11:10:36 +08:00

279 lines
5.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: Prisma 数据库设计规范和最佳实践
globs:
- "backend/prisma/**/*.prisma"
alwaysApply: false
---
# 数据库设计规范
## Prisma Schema 规范
### 表结构要求
所有业务表必须包含以下字段:
```prisma
model YourModel {
id Int @id @default(autoincrement())
tenantId Int @map("tenant_id") /// 租户ID必填
// 业务字段...
name String
description String?
// 审计字段
validState Int @default(1) @map("valid_state") /// 1-有效2-失效
creator Int? /// 创建人ID
modifier Int? /// 修改人ID
createTime DateTime @default(now()) @map("create_time")
modifyTime DateTime @updatedAt @map("modify_time")
// 关系
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
creatorUser User? @relation("YourModelCreator", fields: [creator], references: [id], onDelete: SetNull)
modifierUser User? @relation("YourModelModifier", fields: [modifier], references: [id], onDelete: SetNull)
@@map("your_table_name")
}
```
### 字段命名规范
- Prisma 模型使用 camelCase`tenantId`, `createTime`
- 数据库列使用 snake_case`tenant_id`, `create_time`
- 使用 `@map()` 映射字段名
- 使用 `@@map()` 映射表名
### 状态字段
使用 `validState` 表示数据有效性:
- `1` - 有效
- `2` - 失效(软删除)
```prisma
validState Int @default(1) @map("valid_state")
```
## 关系设计
### 一对多关系
```prisma
model Tenant {
id Int @id @default(autoincrement())
users User[]
}
model User {
id Int @id @default(autoincrement())
tenantId Int @map("tenant_id")
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
}
```
### 多对多关系
使用显式中间表:
```prisma
model User {
id Int @id @default(autoincrement())
roles UserRole[]
}
model Role {
id Int @id @default(autoincrement())
users UserRole[]
}
model UserRole {
userId Int @map("user_id")
roleId Int @map("role_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
@@id([userId, roleId])
@@map("user_roles")
}
```
### 一对一关系
```prisma
model User {
id Int @id @default(autoincrement())
teacher Teacher?
}
model Teacher {
id Int @id @default(autoincrement())
userId Int @unique @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
### 级联删除规则
- 强依赖关系:`onDelete: Cascade`
- 弱依赖关系:`onDelete: SetNull`(字段必须可选)
- 保护性关系:`onDelete: Restrict`
## 索引设计
### 自动索引
- 主键自动创建索引
- 外键字段自动创建索引
- `@unique` 字段自动创建唯一索引
### 复合索引
```prisma
model User {
tenantId Int
username String
@@unique([tenantId, username])
@@index([tenantId, validState])
}
```
### 性能优化索引
为频繁查询的字段添加索引:
```prisma
model Contest {
tenantId Int
status Int
startTime DateTime
@@index([tenantId, status])
@@index([tenantId, startTime])
}
```
## Prisma 查询最佳实践
### 使用 include 预加载关联
避免 N+1 查询问题:
```typescript
// ✅ 好的做法 - 使用 include 预加载
const users = await prisma.user.findMany({
where: { tenantId },
include: {
roles: {
include: {
role: true,
},
},
},
});
// ❌ 不好的做法 - N+1 查询
const users = await prisma.user.findMany({
where: { tenantId },
});
for (const user of users) {
user.roles = await prisma.userRole.findMany({
where: { userId: user.id },
});
}
```
### 使用 select 精简字段
只查询需要的字段:
```typescript
const users = await prisma.user.findMany({
where: { tenantId },
select: {
id: true,
username: true,
nickname: true,
// 不查询 password 等敏感字段
},
});
```
### 分页查询
```typescript
const users = await prisma.user.findMany({
where: { tenantId, validState: 1 },
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { createTime: "desc" },
});
const total = await prisma.user.count({
where: { tenantId, validState: 1 },
});
```
### 事务处理
使用 `$transaction` 确保数据一致性:
```typescript
await prisma.$transaction(async (tx) => {
// 创建用户
const user = await tx.user.create({
data: {
username: "test",
tenantId,
},
});
// 创建用户角色关联
await tx.userRole.createMany({
data: roleIds.map((roleId) => ({
userId: user.id,
roleId,
})),
});
});
```
## 数据迁移
### 创建迁移
```bash
# 开发环境 - 创建并应用迁移
pnpm prisma:migrate:dev
# 生产环境 - 只应用迁移
pnpm prisma:migrate:deploy
```
### 迁移命名
使用描述性的迁移名称:
```bash
prisma migrate dev --name add_contest_module
prisma migrate dev --name add_user_avatar_field
```
### 迁移注意事项
- 迁移前备份数据库
- 测试迁移在开发环境的执行
- 生产环境使用 `migrate deploy` 而不是 `migrate dev`
- 不要手动修改已应用的迁移文件
## 性能优化清单
- [ ] 频繁查询的字段添加了索引
- [ ] 使用 `include` 预加载关联数据
- [ ] 使用 `select` 只查询需要的字段
- [ ] 实现了分页查询
- [ ] 复杂操作使用了事务
- [ ] 避免了 N+1 查询问题