本文引用的文件
本文件面向AI有声书生成平台的数据访问层,系统性梳理Prisma ORM在本项目中的使用方式与数据模型设计,覆盖实体关系映射、查询构建、事务管理、迁移策略、版本管理与数据同步机制,并给出查询优化、索引设计、性能调优、数据验证与完整性保障、最佳实践(批量操作、分页查询、缓存策略)以及数据库设计指南。
数据访问层主要由以下部分构成:
迁移与同步脚本:历史字段到新阶段字段的迁移与一致性同步
graph TB
subgraph "Prisma"
S["schema.prisma<br/>数据模型与关系"]
C["Prisma Client 初始化<br/>models/index.ts"]
end
subgraph "业务模块"
BGS["BookGeneratorStore<br/>book-generator.store.ts"]
TYPES["类型定义<br/>book-generator.types.ts"]
TTS["TTS服务<br/>tts.service.ts"]
end
subgraph "迁移与同步"
MIG["迁移脚本<br/>migrate-genstage.ts"]
SYNC["同步脚本<br/>sync-genstage.ts"]
end
S --> C
C --> BGS
C --> TTS
BGS --> TYPES
MIG --> S
SYNC --> S
图表来源
章节来源
章节来源
数据访问层遵循“模型即服务”的思想:Prisma负责模型与SQL抽象,业务模块通过类方法暴露领域语义的操作,避免直接散布SQL与查询逻辑。
classDiagram
class PrismaClient {
+connect()
+disconnect()
}
class BookStore {
+create(data)
+getById(id, filterPublic, userId)
+getAllByUser(userId, includePublic)
+getPublicBooks()
+update(id, data)
+delete(id)
+createChapter(data)
+createChapters(chapters)
+createChapterItem(bookId, item, parentId, level)
+updateChapter(bookId, number, data)
+updateChapterById(id, data)
+getChapters(bookId)
+getChapterTree(bookId)
+countCompletedChapters(bookId)
+publishAlbum(bookId)
+unpublishAlbum(bookId)
+togglePublish(bookId)
+generateChapterAudio(bookId, chapterNumber, userId)
+generateChapterAudioById(chapterId, userId)
+generateSingleChapterContent(bookId, chapterId)
}
class TTS {
+generateAudio(userId, text, voiceId, voiceParams, onComplete, options)
+getAudioStatus(audioId)
}
PrismaClient <.. BookStore : "注入"
PrismaClient <.. TTS : "注入"
BookStore --> TTS : "调用音频生成"
图表来源
其他:播放记录、收藏、评论、通知、搜索历史、热门搜索、签到、令牌余额/使用、视频素材、视频项目、发布任务、草稿、歌单、歌单项、反馈等
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
int usedAudioMinutes
datetime subscriptionResetDate
}
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
}
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
text lrcLyrics
string status
}
PLAY_RECORD {
int id PK
int userId FK
int chapterId FK
float progress
float duration
datetime updatedAt
datetime createdAt
}
FAVORITE {
int id PK
int userId FK
int bookId FK
datetime createdAt
}
COMMENT {
int id PK
int userId FK
int chapterId FK
text content
int rating
datetime createdAt
}
SUBSCRIPTION {
int id PK
int userId FK
int planId FK
datetime startDate
datetime endDate
string status
boolean autoRenew
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
}
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
int bookId FK
}
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_PREFERENCE {
int id PK
int userId UK
float playSpeed
string quality
string theme
string defaultVoiceId
int defaultVolume
boolean autoPlayNext
boolean wifiOnlyDownload
datetime updatedAt
datetime createdAt
}
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
}
FEEDBACK {
string id PK
string type
text title
text content
text contact
text screenshotUrls
string status
datetime createdAt
datetime updatedAt
}
USER ||--o{ ORDER : "拥有"
USER ||--o{ SUBSCRIPTION : "拥有"
USER ||--o{ BOOK : "拥有"
USER ||--o{ COMMENT : "拥有"
USER ||--o{ FAVORITE : "拥有"
USER ||--o{ PLAY_RECORD : "拥有"
USER ||--o{ PLAYLIST : "拥有"
USER ||--o{ SIGN_RECORD : "拥有"
USER ||--o{ DRAFT : "拥有"
USER ||--o{ TOKEN_USAGE : "产生"
USER ||--o{ AUDIO_RECORD : "拥有"
USER ||--o{ PLATFORM_ACCOUNT : "绑定"
USER ||--o{ VIDEO_PROJECT : "创建"
ORDER }o--|| SUBSCRIPTION_PLAN : "购买"
BOOK ||--o{ BOOK_CHAPTER : "包含"
BOOK ||--o{ VIDEO_PROJECT : "关联"
BOOK_CHAPTER ||--o{ COMMENT : "被评论"
BOOK_CHAPTER ||--o{ PLAY_RECORD : "播放记录"
BOOK_CHAPTER ||--o{ PLAYLIST_ITEM : "加入"
BOOK_CHAPTER ||--o{ VIDEO_PROJECT : "关联"
PLAYLIST }o--o{ PLAYLIST_ITEM : "包含"
图表来源
章节来源
事务管理:使用$transaction包裹多表联动更新(如发布/取消发布书籍时同时更新书籍与章节公开状态)
sequenceDiagram
participant Svc as "业务服务"
participant Store as "BookStore"
participant Prisma as "Prisma Client"
participant DB as "数据库"
Svc->>Store : "发布书籍"
Store->>Prisma : "$transaction([更新书籍, 更新章节公开])"
Prisma->>DB : "BEGIN"
DB-->>Prisma : "OK"
Prisma->>DB : "UPDATE book SET isPublished=true"
DB-->>Prisma : "OK"
Prisma->>DB : "UPDATE bookChapter SET isPublic=true WHERE bookId=?"
DB-->>Prisma : "OK"
Prisma->>DB : "COMMIT"
DB-->>Prisma : "OK"
Prisma-->>Store : "事务完成"
Store-->>Svc : "完成"
图表来源
章节来源
章节来源
版本管理:Prisma迁移锁定文件与版本化SQL脚本配合,确保团队协作一致
flowchart TD
Start(["开始"]) --> LoadOld["读取历史状态字段"]
LoadOld --> ComputeNew["计算新 genStage"]
ComputeNew --> ApplyBook["应用到 Book.genStage"]
ApplyBook --> ApplyChapter["应用到 BookChapter.genStage"]
ApplyChapter --> SafeTrans["使用安全推进函数同步"]
SafeTrans --> Done(["完成"])
图表来源
章节来源
章节来源
章节来源
章节来源
章节来源
迁移与同步脚本依赖Prisma Client与状态推进工具
graph LR
ENV["环境变量 DATABASE_URL"] --> PRISMA["Prisma Client"]
PRISMA --> STORE["BookGeneratorStore"]
PRISMA --> TTS["TTS服务"]
PRISMA --> MIG["迁移脚本"]
PRISMA --> SYNC["同步脚本"]
图表来源
章节来源
[本节为通用指导,无需具体文件分析]
章节来源
本数据访问层以Prisma为核心,结合业务模块的存储类与服务层,实现了清晰的领域建模与稳定的事务一致性。通过迁移与同步脚本,历史数据得到平滑过渡;通过索引与查询优化,兼顾了读写性能。建议持续完善缓存策略与监控告警,进一步提升系统稳定性与可维护性。
[本节为总结,无需具体文件分析]
章节来源