@allbluecn/database 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/migrations/20260804174501_init/migration.sql +1764 -0
  2. package/migrations/20260805174545_add_story_activity_is_ai/migration.sql +2 -0
  3. package/migrations/20260806035202_story_archived/migration.sql +6 -0
  4. package/migrations/20260806061157_builtin_independent_project/migration.sql +26 -0
  5. package/migrations/20260806144250_tanstack_ai_persistence/migration.sql +129 -0
  6. package/migrations/20260809080004_add_monitoring_observability/migration.sql +75 -0
  7. package/migrations/20260811112502_add_workspace/migration.sql +89 -0
  8. package/migrations/20260811153719_add_hosted_ai_tables/migration.sql +119 -0
  9. package/migrations/20260812043944_add_billing_tables/migration.sql +86 -0
  10. package/migrations/20260813040200_sub3_stripe_subscription_unique/migration.sql +6 -0
  11. package/migrations/20260813061345_add_enterprise_license_sso_audit_permissions/migration.sql +169 -0
  12. package/migrations/20260813132026_add_user_locale/migration.sql +2 -0
  13. package/migrations/20260814000000_add_story_composite_index/migration.sql +2 -0
  14. package/migrations/20260814161622_add_user_default_workspace_id/migration.sql +2 -0
  15. package/migrations/20260815025746_p2_seat_addon/migration.sql +15 -0
  16. package/migrations/20260815120000_creem_billing_neutralization/migration.sql +122 -0
  17. package/migrations/20260816212027_compliance_content/migration.sql +3 -0
  18. package/migrations/20260817204928_p6_user_agents_skills/migration.sql +57 -0
  19. package/migrations/20260818094327_user_level_subscription/migration.sql +43 -0
  20. package/migrations/20260819014924_team_membership_invitation/migration.sql +65 -0
  21. package/migrations/migration_lock.toml +3 -0
  22. package/package.json +8 -3
  23. package/prisma.config.ts +18 -0
  24. package/schema.prisma +2014 -0
  25. package/schema.sqlite.prisma +1584 -0
package/schema.prisma ADDED
@@ -0,0 +1,2014 @@
1
+ generator client {
2
+ provider = "prisma-client"
3
+ output = "./generated"
4
+ }
5
+
6
+ datasource db {
7
+ provider = "postgresql"
8
+ }
9
+
10
+ // 系统配置表
11
+ model SystemSetting {
12
+ id String @id @default(cuid())
13
+ key String @unique
14
+ value String @db.Text
15
+ category String @default("general") // 配置分类
16
+ desc String? // 说明
17
+ createdAt DateTime @default(now())
18
+ updatedAt DateTime @updatedAt
19
+
20
+ @@index([category])
21
+ @@map("system_settings")
22
+ }
23
+
24
+ // 用户表
25
+ model User {
26
+ id String @id @default(cuid())
27
+ email String @unique
28
+ username String @unique
29
+ password String
30
+ emailVerified DateTime?
31
+ image String?
32
+ role Role @default(USER)
33
+ status UserStatus @default(ACTIVE)
34
+ createdAt DateTime @default(now())
35
+ updatedAt DateTime @updatedAt
36
+ lastLoginAt DateTime?
37
+
38
+ accounts Account[]
39
+
40
+ avatarConfig Json?
41
+ bgShape String?
42
+ bgColor String?
43
+
44
+ aiProvider String? // "openrouter" | "openai"
45
+ aiApiKey String? // 用户 AI API Key(TODO: 加密存储)
46
+ aiModel String? // 默认 "openai/gpt-4o-mini"
47
+ locale String? // 用户语言偏好(zh / en),null 表示未设置
48
+
49
+ storyDisplayOptions Json? // Story Display Options 个人偏好(URL 无参数时的默认值,跨设备持久化)
50
+
51
+ sketchApiToken String?
52
+ timezone String? // 用户全局时区,如 "Asia/Shanghai"。null = 浏览器时区
53
+
54
+ /// 用户偏好的计费币种(子项目 3 新增)
55
+ preferredCurrency String @default("usd")
56
+
57
+ /// 用户默认 workspace ID(B3 精确化:多 workspace 用户权益判定确定性)
58
+ defaultWorkspaceId String?
59
+
60
+ invitationUses InvitationUse[]
61
+ loginLogs LoginLog[]
62
+ passwordResetTokens PasswordResetToken[]
63
+ prds PRD[]
64
+ tags Tag[]
65
+ knowledgeBases KnowledgeBase[]
66
+ knowledgeDocuments KnowledgeDocument[]
67
+ knowledgeShares KnowledgeShareRecord[]
68
+ legalRegulations LegalRegulation[]
69
+ industryCompliances IndustryCompliance[]
70
+ dictionaryTerms DictionaryTerm[]
71
+ websiteBookmarks WebsiteBookmark[]
72
+ bookmarkCategories BookmarkCategory[]
73
+ articleFavorites ArticleFavorite[]
74
+ anyhubApps AnyHubApp[]
75
+ anyhubMarketApps AnyHubMarketApp[]
76
+ reviewedAnyhubRecommendations AnyHubRecommendation[] @relation("reviewedAnyhubRecommendations")
77
+ anyhubRecommenders AnyHubRecommender[]
78
+ prdVersions PRDVersion[]
79
+ reviewComments ReviewComment[]
80
+ comments Comment[]
81
+ commentIgnores CommentIgnore[]
82
+ reviews Review[] @relation("reviewer")
83
+ designProjects DesignProject[]
84
+ storiesCreated Story[] @relation("storyCreator")
85
+ storiesAssigned Story[] @relation("storyAssignee")
86
+ storiesEdited Story[] @relation("storyEditor")
87
+ labelsCreated Label[] @relation("labelCreator")
88
+ storyActivities StoryActivity[] @relation("storyActivityUser")
89
+ storyComments StoryComment[] @relation("storyCommentUser")
90
+ storyReactions StoryReaction[] @relation("storyReactionUser")
91
+ commentReactions CommentReaction[] @relation("commentReactionUser")
92
+ storyFilterViews StoryFilterView[]
93
+ chatThreads ChatThread[]
94
+ aiProviderConfigs AiProviderConfig[]
95
+ agentConversations AgentConversation[]
96
+ ownedProjects Project[] @relation("projectOwner")
97
+ projectMemberships ProjectMember[]
98
+ projectActivities ProjectActivity[]
99
+ ownedWorkspaces Workspace[] @relation("workspaceOwner")
100
+ workspaceMemberships TeamMembership[] @relation("teamOwner")
101
+ teamJoined TeamMembership? @relation("teamMember")
102
+ sentInvitations TeamInvitation[] @relation("invitationOwner")
103
+ aiUsageLogs AiUsageLog[]
104
+ agents UserAgent[]
105
+ skills UserSkill[]
106
+
107
+ issuedLicenses LicenseKey[] @relation("LicenseIssuer")
108
+ ssoMappings SsoMapping[]
109
+ roleAssignments RoleAssignment[]
110
+ subscription Subscription?
111
+
112
+ @@map("users")
113
+ }
114
+
115
+ // OAuth 账号关联
116
+ model Account {
117
+ id String @id @default(cuid())
118
+ userId String
119
+ type String
120
+ provider String
121
+ providerAccountId String
122
+ refresh_token String? @db.Text
123
+ access_token String? @db.Text
124
+ expires_at Int?
125
+ token_type String?
126
+ scope String?
127
+ id_token String? @db.Text
128
+ session_state String?
129
+
130
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
131
+
132
+ @@unique([provider, providerAccountId])
133
+ @@index([userId])
134
+ @@map("accounts")
135
+ }
136
+
137
+ // 项目
138
+ model Project {
139
+ id String @id @default(cuid())
140
+ displayId String? // 用户层项目ID,全大写字母数字,1-10位
141
+ name String
142
+ description String?
143
+ icon String @default("folder")
144
+ timezone String? // 项目时区,null = 跟随用户全局时区
145
+ ownerId String
146
+ owner User @relation("projectOwner", fields: [ownerId], references: [id])
147
+ workspaceId String?
148
+ workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: SetNull)
149
+ members ProjectMember[]
150
+ activities ProjectActivity[]
151
+ stories Story[]
152
+ createdAt DateTime @default(now())
153
+ updatedAt DateTime @updatedAt
154
+
155
+ @@unique([ownerId, displayId])
156
+ @@index([ownerId])
157
+ @@index([workspaceId])
158
+ @@map("projects")
159
+ }
160
+
161
+ // 项目成员
162
+ model ProjectMember {
163
+ id String @id @default(cuid())
164
+ projectId String
165
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
166
+ userId String
167
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
168
+ role String @default("member") // owner | admin | member | viewer
169
+ status String @default("active") // active | invited | suspended
170
+ joinedAt DateTime @default(now())
171
+
172
+ @@unique([projectId, userId])
173
+ @@index([projectId])
174
+ @@map("project_members")
175
+ }
176
+
177
+ // 工作空间(项目的上一级容器)
178
+ model Workspace {
179
+ id String @id @default(cuid())
180
+ name String
181
+ icon String @default("folder")
182
+ description String?
183
+ ownerId String
184
+ owner User @relation("workspaceOwner", fields: [ownerId], references: [id])
185
+ projects Project[]
186
+ usageRecords UsageRecord[]
187
+ aiUsageLogs AiUsageLog[]
188
+ createdAt DateTime @default(now())
189
+ updatedAt DateTime @updatedAt
190
+
191
+ ssoProviders SsoProvider[]
192
+ customRoles CustomRole[]
193
+ auditLogs AuditLog[]
194
+
195
+ @@index([ownerId])
196
+ @@map("workspaces")
197
+ }
198
+
199
+ // 团队隶属关系
200
+ model TeamMembership {
201
+ id String @id @default(cuid())
202
+ ownerId String
203
+ owner User @relation("teamOwner", fields: [ownerId], references: [id], onDelete: Cascade)
204
+ memberId String
205
+ member User @relation("teamMember", fields: [memberId], references: [id], onDelete: Cascade)
206
+ seatType String @default("member")
207
+ joinedAt DateTime @default(now())
208
+ @@unique([memberId])
209
+ @@index([ownerId])
210
+ @@map("team_memberships")
211
+ }
212
+
213
+ // 团队邀请
214
+ model TeamInvitation {
215
+ id String @id @default(cuid())
216
+ ownerId String
217
+ owner User @relation("invitationOwner", fields: [ownerId], references: [id], onDelete: Cascade)
218
+ email String
219
+ seatType String @default("member")
220
+ token String @unique
221
+ channel String @default("email")
222
+ status String @default("pending")
223
+ expiresAt DateTime
224
+ createdAt DateTime @default(now())
225
+ acceptedBy String?
226
+ @@unique([ownerId, email])
227
+ @@index([ownerId])
228
+ @@index([email])
229
+ @@map("team_invitations")
230
+ }
231
+
232
+ // 项目活动记录
233
+ model ProjectActivity {
234
+ id String @id @default(cuid())
235
+ projectId String
236
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
237
+ userId String
238
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
239
+ action String // created | updated | deleted | member_invited | member_joined | member_removed | role_changed | module_viewed
240
+ targetType String? // project | member | requirement | prd | design | testcase | bug | acceptance
241
+ targetId String?
242
+ metadata Json?
243
+ createdAt DateTime @default(now())
244
+
245
+ @@index([projectId, createdAt])
246
+ @@map("project_activities")
247
+ }
248
+
249
+ // 对话线程(作为 TanStack AI persistence threadId)
250
+ model ChatThread {
251
+ id String @id @default(cuid())
252
+ userId String
253
+ title String?
254
+ isArchived Boolean @default(false)
255
+ createdAt DateTime @default(now())
256
+ updatedAt DateTime @updatedAt
257
+
258
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
259
+ messages ChatMessage[]
260
+ runs ChatRun[]
261
+ interrupts ChatInterrupt[]
262
+ generations GenerationRun[]
263
+ agentId String?
264
+ agent UserAgent? @relation(fields: [agentId], references: [id])
265
+
266
+ @@index([userId, updatedAt])
267
+ @@map("chat_threads")
268
+ }
269
+
270
+ // 对话消息(TanStack AI messages store)
271
+ model ChatMessage {
272
+ id String @id @default(cuid())
273
+ threadId String
274
+ role String // "user" | "assistant" | "tool"
275
+ content String @db.Text
276
+ parts Json?
277
+ thinking String? @db.Text
278
+ citations Json?
279
+ createdAt DateTime @default(now())
280
+
281
+ thread ChatThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
282
+
283
+ @@index([threadId, createdAt])
284
+ @@map("chat_messages")
285
+ }
286
+
287
+ // 运行记录(TanStack AI runs store)
288
+ model ChatRun {
289
+ id String @id @default(cuid())
290
+ threadId String
291
+ userId String
292
+ status String // running | completed | failed | aborted | interrupted
293
+ startedAt DateTime @default(now())
294
+ finishedAt DateTime?
295
+ detachedSince DateTime?
296
+ usage Json?
297
+ error String? @db.Text
298
+
299
+ thread ChatThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
300
+ interrupts ChatInterrupt[]
301
+
302
+ @@index([threadId, startedAt])
303
+ @@index([userId, status])
304
+ @@map("chat_runs")
305
+ }
306
+
307
+ // 中断记录(TanStack AI interrupts store)
308
+ model ChatInterrupt {
309
+ id String @id @default(cuid())
310
+ runId String
311
+ threadId String
312
+ userId String
313
+ kind String // tool-approval | generic
314
+ toolName String?
315
+ state String // pending | resolved | cancelled
316
+ payload Json?
317
+ resolution Json?
318
+ createdAt DateTime @default(now())
319
+ resolvedAt DateTime?
320
+
321
+ run ChatRun @relation(fields: [runId], references: [id], onDelete: Cascade)
322
+ thread ChatThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
323
+
324
+ @@index([runId])
325
+ @@index([threadId, state])
326
+ @@map("chat_interrupts")
327
+ }
328
+
329
+ // 媒体生成记录(占位,本轮建表不接入业务)
330
+ model GenerationRun {
331
+ id String @id @default(cuid())
332
+ threadId String
333
+ userId String
334
+ kind String // image | video | speech | transcription
335
+ status String // running | completed | failed | interrupted
336
+ input Json?
337
+ result Json?
338
+ artifactId String?
339
+ error String? @db.Text
340
+ startedAt DateTime @default(now())
341
+ finishedAt DateTime?
342
+
343
+ thread ChatThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
344
+
345
+ @@index([threadId, startedAt])
346
+ @@index([userId, status])
347
+ @@map("generation_runs")
348
+ }
349
+
350
+ // 登录日志
351
+ model LoginLog {
352
+ id String @id @default(cuid())
353
+ userId String
354
+ ip String?
355
+ userAgent String?
356
+ location String? // 地理位置,如 "北京, 中国";开发环境为 "本地"
357
+ success Boolean @default(true)
358
+ message String? // 失败原因(中文),成功时为 null
359
+ createdAt DateTime @default(now())
360
+
361
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
362
+
363
+ @@map("login_logs")
364
+ }
365
+
366
+ // 邀请码
367
+ model InvitationCode {
368
+ id String @id @default(cuid())
369
+ code String @unique
370
+ maxUses Int @default(1)
371
+ usedCount Int @default(0)
372
+ expiresAt DateTime
373
+ createdAt DateTime @default(now())
374
+
375
+ uses InvitationUse[]
376
+
377
+ @@map("invitation_codes")
378
+ }
379
+
380
+ // 邀请码使用记录
381
+ model InvitationUse {
382
+ id String @id @default(cuid())
383
+ codeId String
384
+ userId String
385
+ usedAt DateTime @default(now())
386
+
387
+ code InvitationCode @relation(fields: [codeId], references: [id], onDelete: Cascade)
388
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
389
+
390
+ @@map("invitation_uses")
391
+ }
392
+
393
+ // 密码重置令牌
394
+ model PasswordResetToken {
395
+ id String @id @default(cuid())
396
+ token String @unique
397
+ userId String
398
+ expiresAt DateTime
399
+ usedAt DateTime?
400
+ createdAt DateTime @default(now())
401
+
402
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
403
+
404
+ @@map("password_reset_tokens")
405
+ }
406
+
407
+ enum Role {
408
+ SUPER_ADMIN
409
+ ADMIN
410
+ USER
411
+ GUEST
412
+ }
413
+
414
+ enum UserStatus {
415
+ ACTIVE
416
+ INACTIVE
417
+ BANNED
418
+ }
419
+
420
+ enum IconType {
421
+ IMAGE
422
+ ICON
423
+ }
424
+
425
+ enum MarketplaceAppStatus {
426
+ DRAFT
427
+ PUBLISHED
428
+ UNPUBLISHED
429
+ }
430
+
431
+ enum ReviewStatus {
432
+ PENDING
433
+ APPROVED
434
+ REJECTED
435
+ }
436
+
437
+ enum ShareStatus {
438
+ ACTIVE
439
+ EXPIRED
440
+ DISABLED
441
+ }
442
+
443
+ // 知识库
444
+ model KnowledgeBase {
445
+ id String @id @default(cuid())
446
+ name String
447
+ description String? @db.Text
448
+ icon String @default("notebook")
449
+ tags Json @default("[]")
450
+ visibility String @default("private")
451
+ userId String
452
+ isDeleted Boolean @default(false)
453
+ createdAt DateTime @default(now())
454
+ updatedAt DateTime @updatedAt
455
+
456
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
457
+ documents KnowledgeDocument[]
458
+
459
+ @@index([userId])
460
+ @@map("knowledge_bases")
461
+ }
462
+
463
+ model KnowledgeDocument {
464
+ id String @id @default(cuid())
465
+ kbId String
466
+ parentId String?
467
+ title String
468
+ type String @default("doc")
469
+ content Json @default("{}")
470
+ icon String?
471
+ isHomepage Boolean @default(false)
472
+ sortOrder Int @default(0)
473
+ isDeleted Boolean @default(false)
474
+ createdBy String
475
+ tags Json @default("[]")
476
+ properties Json @default("{}")
477
+ createdAt DateTime @default(now())
478
+ updatedAt DateTime @updatedAt
479
+
480
+ kb KnowledgeBase @relation(fields: [kbId], references: [id], onDelete: Cascade)
481
+ parent KnowledgeDocument? @relation("DocumentTree", fields: [parentId], references: [id], onDelete: SetNull)
482
+ children KnowledgeDocument[] @relation("DocumentTree")
483
+ creator User @relation(fields: [createdBy], references: [id], onDelete: Cascade)
484
+ linkedStories StoryDocLink[] @relation("DocStoryLinks")
485
+
486
+ @@index([kbId])
487
+ @@index([parentId])
488
+ @@index([kbId, isHomepage])
489
+ @@map("knowledge_documents")
490
+ }
491
+
492
+ // PRD 文档表
493
+ model PRD {
494
+ id String @id @default(cuid())
495
+ icon String @default("file") // 图标名称
496
+ name String // PRD 名称
497
+ description String? // 介绍
498
+ settings Json? // 文档设置(布局、主题等)
499
+ userId String // 所属用户
500
+ isDeleted Boolean @default(false)
501
+ deletedAt DateTime?
502
+ createdAt DateTime @default(now())
503
+ updatedAt DateTime @updatedAt
504
+
505
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
506
+ pages PRDPage[]
507
+ versions PRDVersion[]
508
+ reviews Review[]
509
+ comments Comment[]
510
+ shares Share[]
511
+ prdTags PRDTag[]
512
+ agentConversations AgentConversation[]
513
+ linkedStories StoryPrdLink[] @relation("PrdStoryLinks")
514
+
515
+ @@index([userId])
516
+ @@map("prds")
517
+ }
518
+
519
+ // Agent 原型生成会话
520
+ model AgentConversation {
521
+ id String @id @default(cuid())
522
+ userId String
523
+ prdId String
524
+ title String @default("新会话")
525
+ createdAt DateTime @default(now())
526
+ updatedAt DateTime @updatedAt
527
+
528
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
529
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
530
+ messages AgentMessage[]
531
+
532
+ @@index([userId, prdId])
533
+ @@map("agent_conversations")
534
+ }
535
+
536
+ // Agent 会话消息
537
+ model AgentMessage {
538
+ id String @id @default(cuid())
539
+ conversationId String
540
+ role String // "user" | "assistant"
541
+ content String // 消息文本
542
+ components Json? // AI 生成的组件数据(assistant 消息)
543
+ isError Boolean @default(false)
544
+ createdAt DateTime @default(now())
545
+
546
+ conversation AgentConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
547
+
548
+ @@index([conversationId])
549
+ @@map("agent_messages")
550
+ }
551
+
552
+ // 基础组件表(后台维护)
553
+ model Component {
554
+ id String @id @default(cuid())
555
+ name String
556
+ description String? // 组件描述
557
+ type String // "component" | "block" | "template"
558
+ category String? // 分类
559
+ subCategory String? // 子分类
560
+ path String? // 树形分组路径(如 "基础组件/表单/输入框")
561
+ defaultWidth Int @default(200)
562
+ defaultHeight Int @default(40)
563
+ prototypeKey String? // unique key for registry lookup (e.g. "text", "button")
564
+ config Json // 属性描述 schema(props定义、slots、events)
565
+ defaultProps Json? // 默认属性值
566
+ defaultStyle Json? // 默认样式
567
+ preview String? // 预览图URL
568
+ version Int @default(1)
569
+ sortOrder Int @default(0) // 排序权重(越大越前)
570
+ source String @default("manual") // "npm" | "file" | "manual"
571
+ sourceId String? // npm 包名 / 文件名
572
+ isHidden Boolean @default(false) // 隐藏/显示
573
+ isDeleted Boolean @default(false)
574
+ createdAt DateTime @default(now())
575
+ updatedAt DateTime @updatedAt
576
+
577
+ blockItems BlockComponent[] @relation("componentRef")
578
+ instances ComponentInstance[] @relation("componentRef")
579
+ componentTags ComponentTag[]
580
+ tags String[] @default([])
581
+
582
+ @@index([type, category])
583
+ @@index([isDeleted])
584
+ @@index([isHidden])
585
+ @@index([sortOrder])
586
+ @@map("components")
587
+ }
588
+
589
+ // 区块定义表(后台维护)
590
+ model Block {
591
+ id String @id @default(cuid())
592
+ name String
593
+ description String?
594
+ category String?
595
+ isDeleted Boolean @default(false)
596
+ createdAt DateTime @default(now())
597
+ updatedAt DateTime @updatedAt
598
+
599
+ items BlockComponent[]
600
+ instances BlockInstance[] @relation("blockRef")
601
+ templateItems TemplateBlock[] @relation("templateBlockRef")
602
+
603
+ @@index([isDeleted])
604
+ @@map("blocks")
605
+ }
606
+
607
+ // 区块内组件布局
608
+ model BlockComponent {
609
+ id String @id @default(cuid())
610
+ blockId String
611
+ componentId String
612
+ x Float @default(0)
613
+ y Float @default(0)
614
+ width Float @default(100)
615
+ height Float @default(100)
616
+ zIndex Int @default(0)
617
+ propsOverride Json? // 局部覆写属性
618
+ styleOverride Json? // 局部覆写样式
619
+
620
+ block Block @relation(fields: [blockId], references: [id], onDelete: Cascade)
621
+ component Component @relation("componentRef", fields: [componentId], references: [id])
622
+
623
+ @@map("block_components")
624
+ }
625
+
626
+ // 模板定义表(后台维护)
627
+ model Template {
628
+ id String @id @default(cuid())
629
+ name String
630
+ description String?
631
+ category String?
632
+ isDeleted Boolean @default(false)
633
+ createdAt DateTime @default(now())
634
+ updatedAt DateTime @updatedAt
635
+
636
+ items TemplateBlock[]
637
+
638
+ @@index([isDeleted])
639
+ @@map("templates")
640
+ }
641
+
642
+ // 模板内区块排布
643
+ model TemplateBlock {
644
+ id String @id @default(cuid())
645
+ templateId String
646
+ blockId String
647
+ order Int @default(0) // 排布顺序
648
+ x Float? @default(0)
649
+ y Float? @default(0)
650
+ width Float?
651
+ height Float?
652
+
653
+ template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
654
+ block Block @relation("templateBlockRef", fields: [blockId], references: [id])
655
+
656
+ @@map("template_blocks")
657
+ }
658
+
659
+ // PRD 页面/目录树节点
660
+ model PRDPage {
661
+ id String @id @default(cuid())
662
+ prdId String
663
+ parentId String? // 父节点,构建树形结构(null = 根级)
664
+ name String
665
+ order Int @default(0) // 排序
666
+ type String @default("page") // "page" | "folder"
667
+ canvas Json? // 原型画板数据
668
+ content Json? // PRD 文档内容
669
+ version Int @default(1) // 页面版本号
670
+ createdAt DateTime @default(now())
671
+ updatedAt DateTime @updatedAt
672
+
673
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
674
+ parent PRDPage? @relation("PageHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
675
+ children PRDPage[] @relation("PageHierarchy")
676
+ componentInstances ComponentInstance[]
677
+ blockInstances BlockInstance[]
678
+ pageVersions PageVersion[]
679
+
680
+ @@index([prdId, order])
681
+ @@map("prd_pages")
682
+ }
683
+
684
+ // 组件实例(画布上)
685
+ model ComponentInstance {
686
+ id String @id @default(cuid())
687
+ pageId String
688
+ componentId String? // null = 已解绑的游离副本
689
+ blockInstanceId String? // 所属区块实例
690
+ name String? // 实例别名
691
+ x Float @default(0)
692
+ y Float @default(0)
693
+ width Float @default(100)
694
+ height Float @default(100)
695
+ zIndex Int @default(0)
696
+ props Json? // 局部覆写属性
697
+ style Json? // 局部覆写样式
698
+ createdAt DateTime @default(now())
699
+ updatedAt DateTime @updatedAt
700
+
701
+ page PRDPage @relation(fields: [pageId], references: [id], onDelete: Cascade)
702
+ component Component? @relation("componentRef", fields: [componentId], references: [id])
703
+ blockInstance BlockInstance? @relation(fields: [blockInstanceId], references: [id])
704
+
705
+ @@index([pageId])
706
+ @@map("component_instances")
707
+ }
708
+
709
+ // 区块实例(画布上)
710
+ model BlockInstance {
711
+ id String @id @default(cuid())
712
+ pageId String
713
+ blockId String? // null = 已解绑的游离副本
714
+ name String? // 实例别名
715
+ x Float @default(0)
716
+ y Float @default(0)
717
+ width Float @default(400)
718
+ height Float @default(300)
719
+ zIndex Int @default(0)
720
+ props Json? // 局部覆写属性
721
+ style Json? // 局部覆写样式
722
+ createdAt DateTime @default(now())
723
+ updatedAt DateTime @updatedAt
724
+
725
+ page PRDPage @relation(fields: [pageId], references: [id], onDelete: Cascade)
726
+ block Block? @relation("blockRef", fields: [blockId], references: [id])
727
+ instances ComponentInstance[]
728
+
729
+ @@index([pageId])
730
+ @@map("block_instances")
731
+ }
732
+
733
+ // PRD 版本(手动发布创建)
734
+ model PRDVersion {
735
+ id String @id @default(cuid())
736
+ prdId String
737
+ major Int @default(1)
738
+ minor Int @default(0)
739
+ patch Int @default(0)
740
+ versionLabel String // 如 "v1", "v1.1", "v1.1.1"
741
+ parentVersion String? // 上一版本ID(版本链)
742
+ pageVersions Json // [{ pageId, pageName, version, canvas, content }]
743
+ notes String? // 发版说明
744
+ userId String
745
+ createdAt DateTime @default(now())
746
+ updatedAt DateTime?
747
+
748
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
749
+ user User @relation(fields: [userId], references: [id])
750
+ reviews Review[]
751
+ shareVersions ShareVersion[]
752
+
753
+ @@index([prdId, createdAt])
754
+ @@index([userId])
755
+ @@map("prd_versions")
756
+ }
757
+
758
+ // 页面版本(独立的页面级别版本历史)
759
+ model PageVersion {
760
+ id String @id @default(cuid())
761
+ pageId String
762
+ version Int // 整数版本号,自增
763
+ notes String? // 版本说明(用户可编辑)
764
+ canvas Json? // 原型快照
765
+ content Json? // 文档快照
766
+ restoredFromVersion Int? // 从哪个版本号还原而来(null 表示非还原版本)
767
+ fromPRDVersionId String? // 来源:从哪个 PRD 版本创建(可选标记)
768
+ createdAt DateTime @default(now())
769
+
770
+ page PRDPage @relation(fields: [pageId], references: [id], onDelete: Cascade)
771
+
772
+ @@index([pageId, createdAt])
773
+ @@index([pageId, version])
774
+ @@map("page_versions")
775
+ }
776
+
777
+ // 评审工作流
778
+ model Review {
779
+ id String @id @default(cuid())
780
+ prdId String
781
+ versionId String
782
+ status ReviewStatus @default(PENDING)
783
+ reviewerId String?
784
+ notes String?
785
+ createdAt DateTime @default(now())
786
+ completedAt DateTime?
787
+
788
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
789
+ version PRDVersion @relation(fields: [versionId], references: [id])
790
+ reviewer User? @relation("reviewer", fields: [reviewerId], references: [id])
791
+ comments ReviewComment[]
792
+
793
+ @@unique([prdId, versionId])
794
+ @@index([prdId])
795
+ @@index([versionId])
796
+ @@index([reviewerId])
797
+ @@map("reviews")
798
+ }
799
+
800
+ // 评审留言
801
+ model ReviewComment {
802
+ id String @id @default(cuid())
803
+ reviewId String
804
+ userId String
805
+ content String @db.Text
806
+ pageId String? // 关联页面
807
+ elementId String? // 关联具体元素
808
+ parentId String? // 回复上级留言
809
+ createdAt DateTime @default(now())
810
+
811
+ review Review @relation(fields: [reviewId], references: [id], onDelete: Cascade)
812
+ user User @relation(fields: [userId], references: [id])
813
+ parent ReviewComment? @relation("ReviewCommentReplies", fields: [parentId], references: [id])
814
+ replies ReviewComment[] @relation("ReviewCommentReplies")
815
+
816
+ @@index([reviewId])
817
+ @@index([userId])
818
+ @@index([parentId])
819
+ @@map("review_comments")
820
+ }
821
+
822
+ // 评论(PRD 级别 / 页面级别 / 元素级别)
823
+ model Comment {
824
+ id String @id @default(cuid())
825
+ prdId String
826
+ pageId String? // 可选:针对某页面
827
+ elementId String? // 可选:针对某元素(组件/区块实例ID)
828
+ userId String
829
+ content String @db.Text
830
+ parentId String? // 回复上级评论
831
+ position Json? // 文本选区位置 { from: number, to: number }
832
+ resolved Boolean @default(false)
833
+ createdAt DateTime @default(now())
834
+ updatedAt DateTime @updatedAt
835
+
836
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
837
+ user User @relation(fields: [userId], references: [id])
838
+ parent Comment? @relation("CommentReplies", fields: [parentId], references: [id])
839
+ replies Comment[] @relation("CommentReplies")
840
+ ignores CommentIgnore[]
841
+
842
+ @@index([prdId])
843
+ @@index([userId])
844
+ @@index([parentId])
845
+ @@index([pageId])
846
+ @@map("comments")
847
+ }
848
+
849
+ model CommentIgnore {
850
+ id String @id @default(cuid())
851
+ userId String
852
+ commentId String
853
+ createdAt DateTime @default(now())
854
+
855
+ user User @relation(fields: [userId], references: [id])
856
+ comment Comment @relation(fields: [commentId], references: [id])
857
+
858
+ @@unique([userId, commentId])
859
+ @@index([userId])
860
+ @@index([commentId])
861
+ @@map("comment_ignores")
862
+ }
863
+
864
+ // 分享链接
865
+ model Share {
866
+ id String @id @default(cuid())
867
+ prdId String
868
+ token String @unique
869
+ type String @default("prd") // "prd" | 预留扩展
870
+ status ShareStatus @default(ACTIVE)
871
+ password String? // 加密分享密码(4位,数字+大写+小写字母)
872
+ hasPassword Boolean @default(false)
873
+ expiresAt DateTime?
874
+ createdAt DateTime @default(now())
875
+ updatedAt DateTime @updatedAt
876
+
877
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
878
+ shareVersions ShareVersion[]
879
+
880
+ @@index([token])
881
+ @@index([prdId, type, status])
882
+ @@index([status])
883
+ @@index([expiresAt])
884
+ @@map("shares")
885
+ }
886
+
887
+ // 标签表
888
+ model Tag {
889
+ id String @id @default(cuid())
890
+ name String
891
+ color Int @default(0)
892
+ sortOrder Int @default(0)
893
+ isHidden Boolean @default(false)
894
+ userId String
895
+ createdAt DateTime @default(now())
896
+ updatedAt DateTime @updatedAt
897
+
898
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
899
+ prdTags PRDTag[]
900
+
901
+ @@unique([name, userId])
902
+ @@index([sortOrder])
903
+ @@index([userId])
904
+ @@map("tags")
905
+ }
906
+
907
+ // PRD-标签多对多
908
+ model PRDTag {
909
+ id String @id @default(cuid())
910
+ prdId String
911
+ tagId String
912
+ createdAt DateTime @default(now())
913
+
914
+ prd PRD @relation(fields: [prdId], references: [id], onDelete: Cascade)
915
+ tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
916
+
917
+ @@unique([prdId, tagId])
918
+ @@index([prdId])
919
+ @@index([tagId])
920
+ @@map("prd_tags")
921
+ }
922
+
923
+ // 组件-标签多对多
924
+ // TODO(ComponentTag 重新调整): Tag 已加 userId,ComponentTag 业务待后续调整。
925
+ // DB 端 FK 已 drop(迁移同步);component_tags 表保留,tagId 列 + 唯一约束 (componentId, tagId) 保留;
926
+ // 移除 Prisma 关系以避免与新 Tag 语义冲突。
927
+ model ComponentTag {
928
+ id String @id @default(cuid())
929
+ componentId String
930
+ tagId String
931
+
932
+ component Component @relation(fields: [componentId], references: [id], onDelete: Cascade)
933
+
934
+ @@unique([componentId, tagId])
935
+ @@map("component_tags")
936
+ }
937
+
938
+ model Reference {
939
+ id String @id @default(uuid())
940
+ sourceModule String
941
+ sourceEntityId String
942
+ targetModule String
943
+ targetEntityId String
944
+ createdAt DateTime @default(now())
945
+
946
+ @@index([sourceModule, sourceEntityId])
947
+ @@index([targetModule, targetEntityId])
948
+ @@map("references")
949
+ }
950
+
951
+ model ShareVersion {
952
+ id String @id @default(cuid())
953
+ shareId String
954
+ versionId String
955
+
956
+ share Share @relation(fields: [shareId], references: [id], onDelete: Cascade)
957
+ version PRDVersion @relation(fields: [versionId], references: [id], onDelete: Cascade)
958
+
959
+ @@unique([shareId, versionId])
960
+ @@index([shareId])
961
+ @@index([versionId])
962
+ @@map("share_versions")
963
+ }
964
+
965
+ // ========== AnyHub 模型 ==========
966
+
967
+ model AnyHubApp {
968
+ id String @id @default(cuid())
969
+ name String @unique
970
+ displayName String
971
+ description String? @db.Text
972
+ version String @default("0.1.0")
973
+ source String
974
+ url String?
975
+ localPath String?
976
+ proxyBasePath String?
977
+ iconUrl String?
978
+ pinned Boolean @default(false)
979
+ status String @default("installing")
980
+ userId String
981
+ githubUrl String?
982
+ createdAt DateTime @default(now())
983
+ updatedAt DateTime @updatedAt
984
+
985
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
986
+ permissions AnyHubPermission[]
987
+
988
+ @@index([userId])
989
+ @@index([pinned])
990
+ }
991
+
992
+ model AnyHubPermission {
993
+ id String @id @default(cuid())
994
+ appId String
995
+ capability String
996
+ granted Boolean @default(false)
997
+ userId String
998
+ createdAt DateTime @default(now())
999
+ updatedAt DateTime @updatedAt
1000
+
1001
+ app AnyHubApp @relation(fields: [appId], references: [id], onDelete: Cascade)
1002
+
1003
+ @@unique([appId, capability, userId])
1004
+ @@index([appId])
1005
+ }
1006
+
1007
+ model AnyHubMarketApp {
1008
+ id String @id @default(cuid())
1009
+ name String
1010
+ displayName String
1011
+ description String? @db.Text
1012
+ entryUrl String
1013
+ icon String?
1014
+ iconType IconType @default(ICON)
1015
+ logo String?
1016
+ category String?
1017
+ tags String? @db.Text
1018
+ downloads Int @default(0)
1019
+ capabilities Json
1020
+ status MarketplaceAppStatus @default(DRAFT)
1021
+ publishedAt DateTime?
1022
+ userId String
1023
+ recommendationId String? @unique
1024
+ createdAt DateTime @default(now())
1025
+ updatedAt DateTime @updatedAt
1026
+
1027
+ user User @relation(fields: [userId], references: [id])
1028
+ recommendation AnyHubRecommendation? @relation("anyhubRecommendation", fields: [recommendationId], references: [id])
1029
+
1030
+ @@index([status])
1031
+ @@index([category])
1032
+ }
1033
+
1034
+ model AnyHubRecommendation {
1035
+ id String @id @default(cuid())
1036
+ githubUrl String @unique
1037
+ name String?
1038
+ displayName String?
1039
+ description String? @db.Text
1040
+ status String @default("pending")
1041
+ reviewedBy String?
1042
+ reviewedAt DateTime?
1043
+ marketplaceAppId String?
1044
+ createdAt DateTime @default(now())
1045
+ updatedAt DateTime @updatedAt
1046
+
1047
+ marketplaceApp AnyHubMarketApp? @relation("anyhubRecommendation")
1048
+ reviewer User? @relation("reviewedAnyhubRecommendations", fields: [reviewedBy], references: [id])
1049
+ recommenders AnyHubRecommender[]
1050
+
1051
+ @@index([status])
1052
+ }
1053
+
1054
+ model AnyHubRecommender {
1055
+ id String @id @default(cuid())
1056
+ recommendationId String
1057
+ userId String
1058
+ reason String?
1059
+ createdAt DateTime @default(now())
1060
+
1061
+ recommendation AnyHubRecommendation @relation(fields: [recommendationId], references: [id], onDelete: Cascade)
1062
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1063
+
1064
+ @@unique([recommendationId, userId])
1065
+ }
1066
+
1067
+ // 设计稿
1068
+ model DesignProject {
1069
+ id String @id @default(cuid())
1070
+ name String
1071
+ documentId String?
1072
+ sketchName String?
1073
+ version Int @default(1)
1074
+ userId String
1075
+ user User @relation(fields: [userId], references: [id])
1076
+ artboards Artboard[]
1077
+ createdAt DateTime @default(now())
1078
+ updatedAt DateTime @updatedAt
1079
+
1080
+ @@index([userId])
1081
+ @@map("design_projects")
1082
+ }
1083
+
1084
+ model Artboard {
1085
+ id String @id @default(cuid())
1086
+ projectId String
1087
+ project DesignProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
1088
+ name String
1089
+ pageName String?
1090
+ width Int
1091
+ height Int
1092
+ pngPath String
1093
+ svgPath String?
1094
+ annotationJson Json?
1095
+ layerCount Int?
1096
+ sortOrder Int @default(0)
1097
+ createdAt DateTime @default(now())
1098
+ exports ArtboardExport[]
1099
+
1100
+ @@index([projectId])
1101
+ @@map("design_artboards")
1102
+ }
1103
+
1104
+ model ArtboardExport {
1105
+ id String @id @default(cuid())
1106
+ artboardId String
1107
+ artboard Artboard @relation(fields: [artboardId], references: [id], onDelete: Cascade)
1108
+ layerName String
1109
+ layerId String
1110
+ scale Int
1111
+ format String @default("png")
1112
+ filePath String
1113
+ width Int
1114
+ height Int
1115
+
1116
+ @@unique([artboardId, layerId, scale])
1117
+ @@map("design_artboard_exports")
1118
+ }
1119
+
1120
+ // AI 服务商元数据(admin 维护)
1121
+ model AiProvider {
1122
+ id String @id @default(cuid())
1123
+ providerId String @unique // 业务标识 "openai" "anthropic",用于 adapter 路由与 AiProviderConfig 软关联
1124
+ displayName String
1125
+ brandName String
1126
+ description String @db.Text
1127
+ category String @default("popular") // recommended|popular|local
1128
+ adapterKind String // openai|anthropic|gemini|groq|grok|openrouter|ollama|openai-compatible
1129
+ defaultBaseUrl String?
1130
+ keyUrl String?
1131
+ docsUrl String?
1132
+ keyPlaceholder String?
1133
+ iconSlug String?
1134
+ brandColor String @default("#6467F2")
1135
+ badge String? // 卡片角标文案(如「推荐」「热门」「NEW」),admin 可配
1136
+ isLocal Boolean @default(false)
1137
+ enabled Boolean @default(true) // 平台级开关:停用后未配置用户看不到,已配置用户继续可用
1138
+ models Json // AiModelMeta[](admin 可编辑)
1139
+ sortOrder Int @default(0)
1140
+ createdAt DateTime @default(now())
1141
+ updatedAt DateTime @updatedAt
1142
+
1143
+ @@map("ai_providers")
1144
+ }
1145
+
1146
+ // 用户级 AI 服务商配置
1147
+ model AiProviderConfig {
1148
+ id String @id @default(cuid())
1149
+ userId String
1150
+ providerId String // 业务 providerId,软关联 AiProvider.providerId(不设外键 cascade,避免 admin 删除丢失用户配置)
1151
+ apiKey String? @db.Text // 加密存储 TODO
1152
+ baseUrl String?
1153
+ enabled Boolean @default(false) // 用户级开关:是否在对话模型选择器中暴露
1154
+ models Json? // string[] 用户启用的模型 ID(有序,顺序 = 模型选择器展示顺序)
1155
+ customModels Json? // AiModelMeta[] 用户私有的自定义/刷新获取的模型元数据,与 AiProvider.models 取并集按 id 去重
1156
+ createdAt DateTime @default(now())
1157
+ updatedAt DateTime @updatedAt
1158
+
1159
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1160
+
1161
+ @@unique([userId, providerId])
1162
+ @@index([userId])
1163
+ @@index([providerId])
1164
+ @@map("ai_provider_configs")
1165
+ }
1166
+
1167
+ // 第三方依赖版本管理(admin 决策是否升级)
1168
+ // currentVersion 实时读 monorepo 各 package.json,不存 DB;latestVersion 缓存自 npm registry
1169
+ model ThirdPartyDependency {
1170
+ id String @id @default(cuid())
1171
+ name String @unique // npm 包名,如 "@tiptap/core" / "mind-elixir"
1172
+ displayName String? // 展示名(默认取 name)
1173
+ description String? @db.Text // 依赖用途说明(admin 可编辑)
1174
+ homepage String? // 官网 / 仓库
1175
+ category String @default("other") // framework|ui|editor|ai|state|tool|other
1176
+ isWatched Boolean @default(true) // 是否置顶「关注列表」
1177
+ priority String @default("normal") // high|normal|low
1178
+ latestVersion String? // 缓存的 npm registry latest 版本
1179
+ latestChangelog String? @db.Text // 最新版本的更新日志(来自 GitHub Releases,markdown)
1180
+ lastSyncAt DateTime? // 最后一次成功同步 npm registry 的时间
1181
+ notes String? @db.Text // 备注(升级注意事项等)
1182
+ sortOrder Int @default(0)
1183
+ createdAt DateTime @default(now())
1184
+ updatedAt DateTime @updatedAt
1185
+
1186
+ @@index([category])
1187
+ @@index([isWatched])
1188
+ @@map("third_party_dependencies")
1189
+ }
1190
+
1191
+ // ============ 公共知识库:法律法规 ============
1192
+ model LegalRegulation {
1193
+ id String @id @default(cuid())
1194
+ canonicalId String
1195
+ title String
1196
+ jurisdiction String
1197
+ region String?
1198
+ country String?
1199
+ category String
1200
+ issuingAuthority String?
1201
+ effectiveDate DateTime?
1202
+ summary String? @db.Text
1203
+ content String? @db.Text
1204
+ sourceUrl String?
1205
+ likeCount Int @default(0)
1206
+ tags Json @default("[]")
1207
+ visibility String @default("private")
1208
+ userId String
1209
+ isDeleted Boolean @default(false)
1210
+ createdAt DateTime @default(now())
1211
+ updatedAt DateTime @updatedAt
1212
+
1213
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1214
+
1215
+ @@index([canonicalId, visibility])
1216
+ @@index([userId, visibility])
1217
+ @@index([jurisdiction, category])
1218
+ @@map("legal_regulations")
1219
+ }
1220
+
1221
+ model IndustryCompliance {
1222
+ id String @id @default(cuid())
1223
+ canonicalId String
1224
+ title String
1225
+ industry String
1226
+ regulatoryBody String?
1227
+ region String?
1228
+ summary String? @db.Text
1229
+ content String? @db.Text
1230
+ requirements Json @default("[]")
1231
+ sourceUrl String?
1232
+ likeCount Int @default(0)
1233
+ tags Json @default("[]")
1234
+ visibility String @default("private")
1235
+ userId String
1236
+ isDeleted Boolean @default(false)
1237
+ createdAt DateTime @default(now())
1238
+ updatedAt DateTime @updatedAt
1239
+
1240
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1241
+
1242
+ @@index([canonicalId, visibility])
1243
+ @@index([userId, visibility])
1244
+ @@index([industry])
1245
+ @@map("industry_compliances")
1246
+ }
1247
+
1248
+ model DictionaryTerm {
1249
+ id String @id @default(cuid())
1250
+ canonicalId String
1251
+ title String
1252
+ industry String
1253
+ definition String? @db.Text
1254
+ aliases Json @default("[]")
1255
+ examples Json @default("[]")
1256
+ referenceUrl String?
1257
+ tags Json @default("[]")
1258
+ visibility String @default("private")
1259
+ userId String
1260
+ isDeleted Boolean @default(false)
1261
+ createdAt DateTime @default(now())
1262
+ updatedAt DateTime @updatedAt
1263
+
1264
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1265
+
1266
+ @@index([canonicalId, visibility])
1267
+ @@index([userId, visibility])
1268
+ @@index([title])
1269
+ @@map("dictionary_terms")
1270
+ }
1271
+
1272
+ model WebsiteBookmark {
1273
+ id String @id @default(cuid())
1274
+ canonicalId String
1275
+ title String
1276
+ url String
1277
+ categoryId String?
1278
+ summary String? @db.Text
1279
+ faviconUrl String?
1280
+ faviconSyncedAt DateTime?
1281
+ isGithub Boolean @default(false)
1282
+ githubOwner String?
1283
+ githubRepo String?
1284
+ githubStars Int?
1285
+ githubLatestVersion String?
1286
+ githubVersionDate DateTime?
1287
+ githubRepoUpdatedAt DateTime?
1288
+ githubSyncedAt DateTime?
1289
+ tags Json @default("[]")
1290
+ visibility String @default("private")
1291
+ userId String
1292
+ isDeleted Boolean @default(false)
1293
+ createdAt DateTime @default(now())
1294
+ updatedAt DateTime @updatedAt
1295
+
1296
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1297
+ category BookmarkCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
1298
+
1299
+ @@index([canonicalId, visibility])
1300
+ @@index([userId, visibility])
1301
+ @@index([categoryId])
1302
+ @@index([isGithub])
1303
+ @@map("website_bookmarks")
1304
+ }
1305
+
1306
+ model BookmarkCategory {
1307
+ id String @id @default(cuid())
1308
+ name String
1309
+ icon String @default("Globe")
1310
+ sortOrder Int @default(0)
1311
+ isDefault Boolean @default(false)
1312
+ userId String?
1313
+ createdAt DateTime @default(now())
1314
+ updatedAt DateTime @updatedAt
1315
+
1316
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
1317
+ bookmarks WebsiteBookmark[]
1318
+
1319
+ @@unique([userId, name])
1320
+ @@index([userId])
1321
+ @@map("bookmark_categories")
1322
+ }
1323
+
1324
+ model KnowledgeShareRecord {
1325
+ id String @id @default(cuid())
1326
+ entityType String
1327
+ canonicalId String
1328
+ userId String
1329
+ sharedAt DateTime @default(now())
1330
+
1331
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1332
+
1333
+ @@unique([entityType, canonicalId, userId])
1334
+ @@index([entityType, canonicalId])
1335
+ @@map("knowledge_share_records")
1336
+ }
1337
+
1338
+ model ArticleFavorite {
1339
+ id String @id @default(cuid())
1340
+ entityType String
1341
+ entityId String
1342
+ userId String
1343
+ createdAt DateTime @default(now())
1344
+
1345
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1346
+
1347
+ @@unique([entityType, entityId, userId])
1348
+ @@index([userId, entityType])
1349
+ @@map("article_favorites")
1350
+ }
1351
+
1352
+ // 需求
1353
+ model Story {
1354
+ id String @id @default(cuid())
1355
+ sequenceId Int
1356
+ boardOrder Int @default(0)
1357
+ identifier String
1358
+ projectId String
1359
+ project Project @relation(fields: [projectId], references: [id], onDelete: Restrict)
1360
+ name String
1361
+ description String? @db.Text
1362
+ state String @default("backlog")
1363
+ priority String @default("none")
1364
+ archived Boolean @default(false)
1365
+ archivedAt DateTime?
1366
+ assigneeId String?
1367
+ assignee User? @relation("storyAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
1368
+ startDate DateTime?
1369
+ targetDate DateTime?
1370
+ createdById String
1371
+ createdBy User @relation("storyCreator", fields: [createdById], references: [id])
1372
+ updatedById String?
1373
+ updatedBy User? @relation("storyEditor", fields: [updatedById], references: [id], onDelete: SetNull)
1374
+ labels StoryLabel[]
1375
+ activities StoryActivity[]
1376
+ comments StoryComment[]
1377
+ reactions StoryReaction[]
1378
+ createdAt DateTime @default(now())
1379
+ updatedAt DateTime @updatedAt
1380
+
1381
+ parentId String?
1382
+ parent Story? @relation("StoryHierarchy", fields: [parentId], references: [id], onDelete: SetNull)
1383
+ children Story[] @relation("StoryHierarchy")
1384
+ dependenciesAsSource StoryDependency[] @relation("DependencySource")
1385
+ dependenciesAsTarget StoryDependency[] @relation("DependencyTarget")
1386
+ links StoryLink[]
1387
+ attachments StoryAttachment[]
1388
+ linkedPrds StoryPrdLink[] @relation("StoryPrdLinks")
1389
+ linkedDocs StoryDocLink[] @relation("StoryDocLinks")
1390
+
1391
+ @@unique([identifier])
1392
+ @@index([projectId, sequenceId])
1393
+ @@index([state])
1394
+ @@index([archived])
1395
+ @@index([archived, state, createdAt])
1396
+ @@index([assigneeId])
1397
+ @@index([createdById])
1398
+ @@map("stories")
1399
+ }
1400
+
1401
+ // 全局标签
1402
+ model Label {
1403
+ id String @id @default(cuid())
1404
+ name String @unique
1405
+ color String @default("#6b7280")
1406
+ createdById String
1407
+ createdBy User @relation("labelCreator", fields: [createdById], references: [id])
1408
+ stories StoryLabel[]
1409
+ createdAt DateTime @default(now())
1410
+
1411
+ @@map("labels")
1412
+ }
1413
+
1414
+ model StoryLabel {
1415
+ storyId String
1416
+ labelId String
1417
+ story Story @relation(fields: [storyId], references: [id], onDelete: Cascade)
1418
+ label Label @relation(fields: [labelId], references: [id], onDelete: Cascade)
1419
+
1420
+ @@id([storyId, labelId])
1421
+ @@index([labelId])
1422
+ @@map("story_labels")
1423
+ }
1424
+
1425
+ model StoryActivity {
1426
+ id String @id @default(cuid())
1427
+ storyId String
1428
+ story Story @relation(fields: [storyId], references: [id], onDelete: Cascade)
1429
+ userId String
1430
+ user User @relation("storyActivityUser", fields: [userId], references: [id])
1431
+ action String
1432
+ field String?
1433
+ oldValue String? @db.Text
1434
+ newValue String? @db.Text
1435
+ isAI Boolean @default(false)
1436
+ metadata Json?
1437
+ createdAt DateTime @default(now())
1438
+
1439
+ @@index([storyId, createdAt])
1440
+ @@map("story_activities")
1441
+ }
1442
+
1443
+ model SequenceCounter {
1444
+ id String @id @default(cuid())
1445
+ scope String @unique
1446
+ seq Int @default(0)
1447
+
1448
+ @@map("sequence_counters")
1449
+ }
1450
+
1451
+ model StoryComment {
1452
+ id String @id @default(cuid())
1453
+ storyId String
1454
+ story Story @relation(fields: [storyId], references: [id], onDelete: Cascade)
1455
+ userId String
1456
+ user User @relation("storyCommentUser", fields: [userId], references: [id])
1457
+ parentId String?
1458
+ parent StoryComment? @relation("CommentReply", fields: [parentId], references: [id], onDelete: Cascade)
1459
+ replies StoryComment[] @relation("CommentReply")
1460
+ content String @db.Text
1461
+ reactions CommentReaction[]
1462
+ createdAt DateTime @default(now())
1463
+ updatedAt DateTime @updatedAt
1464
+
1465
+ @@index([storyId, createdAt])
1466
+ @@index([parentId])
1467
+ @@map("story_comments")
1468
+ }
1469
+
1470
+ model StoryReaction {
1471
+ id String @id @default(cuid())
1472
+ storyId String
1473
+ story Story @relation(fields: [storyId], references: [id], onDelete: Cascade)
1474
+ userId String
1475
+ user User @relation("storyReactionUser", fields: [userId], references: [id])
1476
+ emoji String
1477
+ createdAt DateTime @default(now())
1478
+
1479
+ @@unique([storyId, userId, emoji])
1480
+ @@index([storyId])
1481
+ @@map("story_reactions")
1482
+ }
1483
+
1484
+ model CommentReaction {
1485
+ id String @id @default(cuid())
1486
+ commentId String
1487
+ comment StoryComment @relation(fields: [commentId], references: [id], onDelete: Cascade)
1488
+ userId String
1489
+ user User @relation("commentReactionUser", fields: [userId], references: [id])
1490
+ emoji String
1491
+ createdAt DateTime @default(now())
1492
+
1493
+ @@unique([commentId, userId, emoji])
1494
+ @@index([commentId])
1495
+ @@map("comment_reactions")
1496
+ }
1497
+
1498
+ model StoryDependency {
1499
+ id String @id @default(cuid())
1500
+ sourceId String
1501
+ targetId String
1502
+ type DependencyType
1503
+ createdAt DateTime @default(now())
1504
+ updatedAt DateTime @updatedAt
1505
+
1506
+ source Story @relation("DependencySource", fields: [sourceId], references: [id], onDelete: Cascade)
1507
+ target Story @relation("DependencyTarget", fields: [targetId], references: [id], onDelete: Cascade)
1508
+
1509
+ @@unique([sourceId, targetId, type])
1510
+ @@index([sourceId])
1511
+ @@index([targetId])
1512
+ @@map("story_dependencies")
1513
+ }
1514
+
1515
+ model StoryLink {
1516
+ id String @id @default(cuid())
1517
+ storyId String
1518
+ url String
1519
+ title String?
1520
+ description String?
1521
+ createdAt DateTime @default(now())
1522
+ updatedAt DateTime @updatedAt
1523
+
1524
+ story Story @relation(fields: [storyId], references: [id], onDelete: Cascade)
1525
+
1526
+ @@index([storyId])
1527
+ @@map("story_links")
1528
+ }
1529
+
1530
+ model StoryAttachment {
1531
+ id String @id @default(cuid())
1532
+ storyId String
1533
+ fileName String
1534
+ fileSize BigInt
1535
+ mimeType String
1536
+ storageUrl String
1537
+ createdAt DateTime @default(now())
1538
+
1539
+ story Story @relation(fields: [storyId], references: [id], onDelete: Cascade)
1540
+
1541
+ @@index([storyId])
1542
+ @@map("story_attachments")
1543
+ }
1544
+
1545
+ model StoryPrdLink {
1546
+ id String @id @default(cuid())
1547
+ storyId String
1548
+ prdId String
1549
+ createdAt DateTime @default(now())
1550
+
1551
+ story Story @relation("StoryPrdLinks", fields: [storyId], references: [id], onDelete: Cascade)
1552
+ prd PRD @relation("PrdStoryLinks", fields: [prdId], references: [id], onDelete: Cascade)
1553
+
1554
+ @@unique([storyId, prdId])
1555
+ @@index([storyId])
1556
+ @@index([prdId])
1557
+ @@map("story_prd_links")
1558
+ }
1559
+
1560
+ model StoryDocLink {
1561
+ id String @id @default(cuid())
1562
+ storyId String
1563
+ docId String
1564
+ createdAt DateTime @default(now())
1565
+
1566
+ story Story @relation("StoryDocLinks", fields: [storyId], references: [id], onDelete: Cascade)
1567
+ doc KnowledgeDocument @relation("DocStoryLinks", fields: [docId], references: [id], onDelete: Cascade)
1568
+
1569
+ @@unique([storyId, docId])
1570
+ @@index([storyId])
1571
+ @@index([docId])
1572
+ @@map("story_doc_links")
1573
+ }
1574
+
1575
+ enum DependencyType {
1576
+ BLOCKS
1577
+ BLOCKED_BY
1578
+ RELATES_TO
1579
+ }
1580
+
1581
+ // 需求模块保存视图(per-user 私有)
1582
+ model StoryFilterView {
1583
+ id String @id @default(cuid())
1584
+ userId String
1585
+ name String
1586
+ description String?
1587
+ icon String @default("list")
1588
+ projectId String?
1589
+ filters Json
1590
+ displayOptions Json
1591
+ sortOrder Int @default(0)
1592
+ createdAt DateTime @default(now())
1593
+ updatedAt DateTime @updatedAt
1594
+
1595
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1596
+
1597
+ @@index([userId])
1598
+ @@map("story_filter_views")
1599
+ }
1600
+
1601
+ // ==================== 监控可观测性模块 ====================
1602
+
1603
+ model MonitoringRequestLog {
1604
+ id String @id @default(cuid())
1605
+ timestamp DateTime @default(now())
1606
+ method String @db.VarChar(10)
1607
+ path String @db.VarChar(512)
1608
+ status Int
1609
+ duration Int
1610
+ ip String? @db.VarChar(45)
1611
+ userAgent String? @db.Text
1612
+
1613
+ @@index([timestamp], map: "idx_request_log_timestamp")
1614
+ @@index([path, timestamp], map: "idx_request_log_path_ts")
1615
+ @@map("monitoring_request_logs")
1616
+ }
1617
+
1618
+ model MonitoringErrorReport {
1619
+ id String @id @default(cuid())
1620
+ timestamp DateTime @default(now())
1621
+ errorName String @db.VarChar(128)
1622
+ errorMessage String @db.Text
1623
+ stackTrace String? @db.Text
1624
+ count Int @default(1)
1625
+ firstSeen DateTime @default(now())
1626
+ lastSeen DateTime @default(now())
1627
+ context Json @default("{}")
1628
+
1629
+ @@index([errorName], map: "idx_error_report_name")
1630
+ @@index([lastSeen], map: "idx_error_report_last_seen")
1631
+ @@map("monitoring_error_reports")
1632
+ }
1633
+
1634
+ model MonitoringMetricsSnapshot {
1635
+ id String @id @default(cuid())
1636
+ recordedAt DateTime @default(now())
1637
+ metricName String @db.VarChar(256)
1638
+ count Int
1639
+ avg Float
1640
+ p50 Float
1641
+ p95 Float
1642
+ p99 Float
1643
+ min Float
1644
+ max Float
1645
+
1646
+ @@index([metricName, recordedAt], map: "idx_metrics_name_ts")
1647
+ @@map("monitoring_metrics_snapshots")
1648
+ }
1649
+
1650
+ model MonitoringHealthSnapshot {
1651
+ id String @id @default(cuid())
1652
+ timestamp DateTime @default(now())
1653
+ status String @db.VarChar(16)
1654
+ uptimeSeconds Float
1655
+ memoryUsage Json
1656
+ dbStatus String @db.VarChar(16)
1657
+ dbLatencyMs Int?
1658
+
1659
+ @@index([timestamp], map: "idx_health_snapshot_ts")
1660
+ @@map("monitoring_health_snapshots")
1661
+ }
1662
+
1663
+ // ── 子项目 2:托管 AI 与 BYOK 统一层 ──
1664
+
1665
+ /// 订阅计划定义(seed 数据)
1666
+ model Plan {
1667
+ id String @id @default(cuid())
1668
+ name String @unique
1669
+ displayName String
1670
+ description String?
1671
+ priceMonthly Int @default(0)
1672
+ priceYearly Int?
1673
+ currency String @default("usd")
1674
+ maxSeats Int?
1675
+ maxGuestSeats Int?
1676
+ maxWorkspaces Int?
1677
+ aiQuotaMonthly Int?
1678
+ features Json
1679
+ isPublic Boolean @default(true)
1680
+ sortOrder Int @default(0)
1681
+ createdAt DateTime @default(now())
1682
+ updatedAt DateTime @updatedAt
1683
+
1684
+ /// Provider 产品与价格同步(子项目 3 新增)
1685
+ /// ⚠ stripeProductId 已删除:Creem 无独立产品概念,一个价格点 = 一个产品
1686
+ providerPriceIdUsd String?
1687
+ providerPriceIdCny String?
1688
+ providerPriceIdUsdYearly String?
1689
+ providerPriceIdCnyYearly String?
1690
+
1691
+ /// 席位加购(P2 新增):每席位/月单价(分),null = 不可加购
1692
+ seatPriceMonthly Int?
1693
+
1694
+ /// 席位加购 Provider Price(P2 新增,per-seat/monthly interval)
1695
+ providerSeatPriceIdUsd String?
1696
+ providerSeatPriceIdCny String?
1697
+
1698
+ agentQuota Int?
1699
+ skillQuota Int?
1700
+
1701
+ subscriptions Subscription[]
1702
+
1703
+ @@map("plans")
1704
+ }
1705
+
1706
+ /// 订阅记录(User 级,一人一订阅)
1707
+ model Subscription {
1708
+ id String @id @default(cuid())
1709
+ userId String @unique
1710
+ user User @relation(fields: [userId], references: [id])
1711
+ planId String
1712
+ plan Plan @relation(fields: [planId], references: [id])
1713
+ status SubscriptionStatus @default(ACTIVE)
1714
+
1715
+ /// 席位加购数量(账号级)。实际上限 = plan.maxSeats + addonSeats。
1716
+ addonSeats Int @default(0)
1717
+
1718
+ provider String @default("creem")
1719
+ providerCustomerId String?
1720
+ providerSubscriptionId String? @unique
1721
+ providerPriceId String?
1722
+ providerSeatSubscriptionId String?
1723
+
1724
+ currentPeriodStart DateTime?
1725
+ currentPeriodEnd DateTime?
1726
+ cancelAtPeriodEnd Boolean @default(false)
1727
+ canceledAt DateTime?
1728
+ trialEndsAt DateTime?
1729
+
1730
+ createdAt DateTime @default(now())
1731
+ updatedAt DateTime @updatedAt
1732
+
1733
+ /// 关联发票(子项目 3 新增)
1734
+ invoices Invoice[]
1735
+
1736
+ @@map("subscriptions")
1737
+ }
1738
+
1739
+ enum SubscriptionStatus {
1740
+ TRIALING
1741
+ ACTIVE
1742
+ PAST_DUE
1743
+ CANCELED
1744
+ EXPIRED
1745
+ UNPAID
1746
+ }
1747
+
1748
+ /// 用量记录(按月聚合,用于额度判断)
1749
+ model UsageRecord {
1750
+ id String @id @default(cuid())
1751
+ workspaceId String
1752
+ workspace Workspace @relation(fields: [workspaceId], references: [id])
1753
+ metric UsageMetric
1754
+ period String
1755
+ count Int @default(0)
1756
+ resetAt DateTime
1757
+
1758
+ createdAt DateTime @default(now())
1759
+ updatedAt DateTime @updatedAt
1760
+
1761
+ @@unique([workspaceId, metric, period])
1762
+ @@index([workspaceId, metric])
1763
+ @@map("usage_records")
1764
+ }
1765
+
1766
+ enum UsageMetric {
1767
+ AI_CALL
1768
+ STORAGE_MB
1769
+ SEAT
1770
+ }
1771
+
1772
+ /// AI 用量明细(按次扣减 + 成本审计)
1773
+ model AiUsageLog {
1774
+ id String @id @default(cuid())
1775
+ workspaceId String
1776
+ workspace Workspace @relation(fields: [workspaceId], references: [id])
1777
+ userId String
1778
+ user User @relation(fields: [userId], references: [id])
1779
+ source String @default("byok")
1780
+ provider String
1781
+ model String
1782
+ inputTokens Int @default(0)
1783
+ outputTokens Int @default(0)
1784
+ costUsd Decimal? @db.Decimal(10, 6)
1785
+ success Boolean @default(true)
1786
+ errorMessage String?
1787
+ durationMs Int?
1788
+
1789
+ createdAt DateTime @default(now())
1790
+
1791
+ @@index([workspaceId, createdAt])
1792
+ @@index([userId, createdAt])
1793
+ @@index([workspaceId, source, createdAt])
1794
+ @@index([workspaceId, success, createdAt])
1795
+ @@map("ai_usage_logs")
1796
+ }
1797
+
1798
+ // ── 子项目 3:计费与订阅系统 ──
1799
+
1800
+ /// 账单记录(Webhook 同步)
1801
+ model Invoice {
1802
+ id String @id @default(cuid())
1803
+ subscriptionId String
1804
+ subscription Subscription @relation(fields: [subscriptionId], references: [id])
1805
+ providerInvoiceId String @unique
1806
+ providerEventId String?
1807
+ amountDue Int
1808
+ amountPaid Int
1809
+ currency String @default("usd")
1810
+ status String
1811
+ pdfUrl String?
1812
+ hostedInvoiceUrl String?
1813
+ periodStart DateTime
1814
+ periodEnd DateTime
1815
+ paidAt DateTime?
1816
+
1817
+ createdAt DateTime @default(now())
1818
+
1819
+ @@index([subscriptionId, createdAt])
1820
+ @@map("invoices")
1821
+ }
1822
+
1823
+ /// Provider Webhook 事件幂等记录
1824
+ model ProviderEvent {
1825
+ id String @id @default(cuid())
1826
+ providerEventId String @unique
1827
+ type String
1828
+ status String @default("processing")
1829
+ payload Json?
1830
+ errorMessage String?
1831
+ processedAt DateTime? @db.Timestamptz
1832
+
1833
+ createdAt DateTime @default(now()) @db.Timestamptz
1834
+
1835
+ @@index([type, createdAt])
1836
+ @@map("provider_events")
1837
+ }
1838
+
1839
+ // ==================== 子项目 5:企业治理模型 ====================
1840
+
1841
+ // 企业版 License(自部署企业版验证)
1842
+ model LicenseKey {
1843
+ id String @id @default(cuid())
1844
+ key String @unique
1845
+ signature String
1846
+ payload Json
1847
+ customerId String
1848
+ customerName String
1849
+ customerEmail String
1850
+ plan String @default("enterprise")
1851
+ seats Int
1852
+ features Json
1853
+ issuedAt DateTime @default(now())
1854
+ expiresAt DateTime?
1855
+ revokedAt DateTime?
1856
+ issuedById String?
1857
+ issuedBy User? @relation("LicenseIssuer", fields: [issuedById], references: [id])
1858
+ activations LicenseActivation[]
1859
+
1860
+ @@map("license_keys")
1861
+ }
1862
+
1863
+ model LicenseActivation {
1864
+ id String @id @default(cuid())
1865
+ licenseId String
1866
+ license LicenseKey @relation(fields: [licenseId], references: [id])
1867
+ instanceId String @unique
1868
+ fingerprint String
1869
+ activatedAt DateTime @default(now())
1870
+ lastSeenAt DateTime?
1871
+ revokedAt DateTime?
1872
+
1873
+ @@map("license_activations")
1874
+ }
1875
+
1876
+ // 审计日志
1877
+ model AuditLog {
1878
+ id String @id @default(cuid())
1879
+ workspaceId String?
1880
+ workspace Workspace? @relation(fields: [workspaceId], references: [id])
1881
+ userId String?
1882
+ action String
1883
+ resourceType String
1884
+ resourceId String?
1885
+ metadata Json
1886
+ ip String?
1887
+ userAgent String?
1888
+ createdAt DateTime @default(now())
1889
+
1890
+ @@index([workspaceId, createdAt])
1891
+ @@index([userId, createdAt])
1892
+ @@index([action])
1893
+ @@map("audit_logs")
1894
+ }
1895
+
1896
+ // SSO Provider 配置
1897
+ model SsoProvider {
1898
+ id String @id @default(cuid())
1899
+ workspaceId String
1900
+ workspace Workspace @relation(fields: [workspaceId], references: [id])
1901
+ type String
1902
+ name String
1903
+ config Json
1904
+ domain String?
1905
+ enabled Boolean @default(true)
1906
+ createdAt DateTime @default(now())
1907
+ updatedAt DateTime @updatedAt
1908
+ mappings SsoMapping[]
1909
+
1910
+ @@unique([workspaceId, type, domain])
1911
+ @@map("sso_providers")
1912
+ }
1913
+
1914
+ model SsoMapping {
1915
+ id String @id @default(cuid())
1916
+ providerId String
1917
+ provider SsoProvider @relation(fields: [providerId], references: [id])
1918
+ externalId String
1919
+ userId String
1920
+ user User @relation(fields: [userId], references: [id])
1921
+ email String?
1922
+ createdAt DateTime @default(now())
1923
+
1924
+ @@unique([providerId, externalId])
1925
+ @@map("sso_mappings")
1926
+ }
1927
+
1928
+ // 自定义角色
1929
+ model CustomRole {
1930
+ id String @id @default(cuid())
1931
+ workspaceId String
1932
+ workspace Workspace @relation(fields: [workspaceId], references: [id])
1933
+ name String
1934
+ permissions Json
1935
+ isSystem Boolean @default(false)
1936
+ createdAt DateTime @default(now())
1937
+ updatedAt DateTime @updatedAt
1938
+ assignments RoleAssignment[]
1939
+
1940
+ @@unique([workspaceId, name])
1941
+ @@map("custom_roles")
1942
+ }
1943
+
1944
+ model RoleAssignment {
1945
+ id String @id @default(cuid())
1946
+ roleId String
1947
+ role CustomRole @relation(fields: [roleId], references: [id])
1948
+ userId String
1949
+ user User @relation(fields: [userId], references: [id])
1950
+ createdAt DateTime @default(now())
1951
+
1952
+ @@unique([roleId, userId])
1953
+ @@map("role_assignments")
1954
+ }
1955
+
1956
+ // 合规导出任务
1957
+ model ComplianceExport {
1958
+ id String @id @default(cuid())
1959
+ type String
1960
+ scope String
1961
+ targetId String
1962
+ status String @default("pending")
1963
+ downloadUrl String?
1964
+ expiresAt DateTime?
1965
+ error String?
1966
+ content String?
1967
+ requestedBy String?
1968
+ createdAt DateTime @default(now())
1969
+ completedAt DateTime?
1970
+
1971
+ @@map("compliance_exports")
1972
+ }
1973
+
1974
+ // P6 用户自定义 Agent(人设/工具/模型/温度/开场白/建议问题/知识库绑定)
1975
+ model UserAgent {
1976
+ id String @id @default(cuid())
1977
+ userId String
1978
+ name String
1979
+ description String?
1980
+ avatarEmoji String?
1981
+ systemPrompt String @db.Text
1982
+ allowedTools Json @default("[]")
1983
+ model String?
1984
+ temperature Float?
1985
+ greeting String? @db.Text
1986
+ suggestedPrompts Json?
1987
+ knowledgeBaseIds Json @default("[]")
1988
+ createdAt DateTime @default(now())
1989
+ updatedAt DateTime @updatedAt
1990
+ isDeleted Boolean @default(false)
1991
+
1992
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1993
+ threads ChatThread[]
1994
+
1995
+ @@index([userId])
1996
+ @@map("user_agents")
1997
+ }
1998
+
1999
+ // P6 用户自定义 Skill(斜杠指令模板)
2000
+ model UserSkill {
2001
+ id String @id @default(cuid())
2002
+ userId String
2003
+ name String
2004
+ title String
2005
+ template String @db.Text
2006
+ createdAt DateTime @default(now())
2007
+ updatedAt DateTime @updatedAt
2008
+ isDeleted Boolean @default(false)
2009
+
2010
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
2011
+
2012
+ @@unique([userId, name])
2013
+ @@map("user_skills")
2014
+ }