本文档引用的文件
本文件面向AI有声书生成平台,系统性梳理Prisma ORM在该项目中的整体架构设计与数据模型组织方式。重点覆盖以下方面:
本项目的数据层采用Prisma Schema集中定义,配合模块化的业务服务层进行读写操作。核心文件分布如下:
业务服务:围绕模型进行CRUD与复杂事务处理
graph TB
subgraph "数据层"
PRISMA["Prisma Schema<br/>server/prisma/schema.prisma"]
MIGRATE["迁移脚本<br/>server/prisma/migrate-genstage.ts"]
SYNC["同步脚本<br/>server/prisma/sync-genstage.ts"]
end
subgraph "应用层"
MODELS["Prisma 客户端封装<br/>server/src/models/index.ts"]
AUTH["认证服务<br/>server/src/modules/auth/auth.service.ts"]
TTS["TTS服务<br/>server/src/modules/tts/tts.service.ts"]
MEMBER["会员服务<br/>server/src/modules/member/member.service.ts"]
GEN["书籍生成服务<br/>server/src/modules/book-generator/book-generator.service.ts"]
STAGE["阶段管理器<br/>server/src/modules/book-generator/stage-manager.ts"]
end
PRISMA --> MODELS
MIGRATE --> PRISMA
SYNC --> PRISMA
MODELS --> AUTH
MODELS --> TTS
MODELS --> MEMBER
MODELS --> GEN
GEN --> STAGE
图表来源
章节来源
本节从数据模型视角,介绍关键实体及其职责边界,并说明Prisma的关系映射与约束策略。
章节来源
下图展示Prisma数据模型在系统中的交互关系与典型调用链:
erDiagram
USER {
int id PK
string phone UK
string openid UK
string nickname
string avatar
int memberLevel
datetime memberExpireAt
int dailyUsage
string lastUsageDate
datetime createdAt
datetime updatedAt
}
ORDER {
int id PK
int userId FK
string orderNo UK
int planId FK
string productType
decimal amount
string status
string paymentMethod
string paymentId
datetime paidAt
datetime createdAt
datetime updatedAt
}
SUBSCRIPTION_PLAN {
int id PK
string name
int level
decimal priceMonthly
decimal priceYearly
string description
string features
boolean isRecommended
boolean isActive
int sortOrder
int dailyGenerations
int perGenerationLimit
int monthlyTokens
int monthlyMinutes
int yearlyTokens
int voiceOptions
string audioQuality
boolean apiAccess
boolean batchProcessing
boolean teamManagement
boolean overageEnabled
decimal overagePrice
datetime createdAt
datetime updatedAt
}
SUBSCRIPTION {
int id PK
int userId FK
int planId FK
datetime startDate
datetime endDate
string status
boolean autoRenew
datetime createdAt
datetime updatedAt
}
BOOK {
int id PK
int userId FK
string title
string subtitle
text description
string coverUrl
string targetAudience
string style
string bookScale
int totalChapters
int estimatedWords
int progress
boolean isPublished
longtext outlineJson
text foreword
text afterword
text errorMsg
datetime createdAt
datetime updatedAt
varchar failedStage
string genStage
string status
text bookAnalysis
}
BOOK_CHAPTER {
int id PK
int bookId FK
int parentId
int level
int number
string title
text summary
text keyPoints
int estimatedWords
longtext content
int wordCount
text contentError
datetime generatedAt
text audioUrl
int audioDuration
text videoUrl
int videoDuration
boolean isPublic
string genStage
string status
text lrcLyrics
}
PLAYLIST {
int id PK
int userId FK
string name
text description
datetime createdAt
datetime updatedAt
}
PLAYLIST_ITEM {
int id PK
int playlistId FK
int chapterId FK
string audioId
int order
}
PLAY_RECORD {
int id PK
int userId FK
int chapterId FK
float progress
float duration
datetime createdAt
datetime updatedAt
}
AUDIO_RECORD {
int id PK
int userId FK
string audioId UK
string title
longtext text
int wordCount
string voiceId
string voiceParams
text audioUrl
int audioDuration
int audioSize
string status
text errorMsg
datetime createdAt
datetime updatedAt
}
COMMENT {
int id PK
int userId FK
int chapterId FK
text content
int rating
datetime createdAt
}
DRAFT {
int id PK
int userId FK
string type
text title
longtext content
text metadata
datetime autoSavedAt
datetime createdAt
datetime updatedAt
}
TOKEN_BALANCE {
int id PK
int userId UK
int totalTokens
int usedTokens
datetime resetDate
datetime createdAt
datetime updatedAt
}
TOKEN_USAGE {
int id PK
int userId FK
string type
int amount
int contentLength
int orderId FK
text description
datetime createdAt
}
USER_PREFERENCE {
int id PK
int userId UK
float playSpeed
string quality
string theme
string defaultVoiceId
int defaultVolume
boolean autoPlayNext
boolean wifiOnlyDownload
datetime createdAt
datetime updatedAt
}
VIDEO_PROJECT {
int id PK
int userId FK
string title
text description
string coverUrl
longtext configJson
string outputUrl
int duration
int fileSize
int bookId FK
int chapterId FK
string status
int progress
text errorMsg
datetime createdAt
datetime updatedAt
}
USER ||--o{ ORDER : "拥有"
SUBSCRIPTION_PLAN ||--o{ SUBSCRIPTION : "被订阅"
USER ||--o{ SUBSCRIPTION : "订阅"
USER ||--o{ BOOK : "创作"
BOOK ||--o{ BOOK_CHAPTER : "包含"
USER ||--o{ COMMENT : "发表"
BOOK_CHAPTER ||--o{ COMMENT : "被评论"
USER ||--o{ PLAYLIST : "创建"
PLAYLIST ||--o{ PLAYLIST_ITEM : "包含"
USER ||--o{ PLAY_RECORD : "播放"
BOOK_CHAPTER ||--o{ PLAY_RECORD : "被播放"
USER ||--o{ AUDIO_RECORD : "生成"
BOOK ||--o{ VIDEO_PROJECT : "驱动"
BOOK_CHAPTER ||--o{ VIDEO_PROJECT : "驱动"
USER ||--o{ DRAFT : "保存"
USER ||--o{ TOKEN_BALANCE : "持有"
USER ||--o{ TOKEN_USAGE : "产生"
ORDER ||--o{ TOKEN_USAGE : "关联"
USER ||--o{ USER_PREFERENCE : "偏好"
图表来源
章节来源
生成阶段(genStage)与状态(status)
书籍与章节分别定义阶段枚举,确保线性演进与一致性
classDiagram
class Book {
+int id
+int userId
+string title
+string status
+string genStage
+int progress
+longtext outlineJson
+text foreword
+text afterword
+datetime createdAt
+datetime updatedAt
}
class BookChapter {
+int id
+int bookId
+int parentId
+int level
+int number
+string title
+string status
+string genStage
+text content
+text audioUrl
+text videoUrl
+text lrcLyrics
+datetime createdAt
+datetime updatedAt
}
Book "1" --> "0..*" BookChapter : "包含"
图表来源
章节来源
历史数据迁移
同步脚本基于阶段索引进行安全推进或回退
flowchart TD
Start(["开始同步"]) --> LoadChapters["加载章节/书籍"]
LoadChapters --> InferTarget["根据旧字段推导目标阶段"]
InferTarget --> Compare["比较当前阶段与目标阶段"]
Compare --> |相同| Skip["无需更新"]
Compare --> |目标更大| Advance["前进:safeTransition"]
Compare --> |目标更小| Regenerate["回退:safeTransition并清理资源"]
Advance --> Update["更新genStage并清理下游资源"]
Regenerate --> Update
Update --> Done(["完成"])
Skip --> Done
图表来源
章节来源
章节来源
章节来源
循环依赖
通过模块拆分与延迟导入避免循环依赖
graph LR
MODELS["models/index.ts"] --> AUTH["auth.service.ts"]
MODELS --> TTS["tts.service.ts"]
MODELS --> MEMBER["member.service.ts"]
MODELS --> GEN["book-generator.service.ts"]
GEN --> STAGE["stage-manager.ts"]
PRISMA["schema.prisma"] --> MODELS
图表来源
章节来源
章节来源
本项目通过Prisma Schema集中定义数据模型,结合模块化的业务服务与严格的阶段管理机制,实现了有声书生成平台的数据一致性与可扩展性。通过迁移与同步脚本,历史数据得以平滑过渡到新的线性阶段模型;通过统一的Prisma客户端封装,降低了模块间的耦合度。建议在后续迭代中持续完善索引策略、监控与告警体系,并保持阶段模型与业务流程的同步演进。