新增角色大廳(lobby)邀約與通話、附身(possession)、時鐘(clock)抽象、 時間流與睡眠負債、後日談進度、行為選擇與旁觀反應等模組,並補齊對應冒煙 測試(R~V)與單元測試;同步調整 web/mobile 對應頁面與元件。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1017 lines
42 KiB
Plaintext
1017 lines
42 KiB
Plaintext
// Kokorone 資料模型
|
||
// R-2 會將 provider 由 sqlite 切換為 postgresql,並為 MemoryRetriever 加上 pgvector 語意檢索。
|
||
|
||
generator client {
|
||
provider = "prisma-client"
|
||
output = "../packages/db/generated"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "sqlite"
|
||
}
|
||
|
||
enum CharacterSource {
|
||
ORIGINAL // 原創
|
||
EXISTING_WORK // 既有作品
|
||
}
|
||
|
||
enum CharacterBuildStatus {
|
||
CANDIDATE // 候補
|
||
BUILT // 已建置
|
||
}
|
||
|
||
enum MemorySource {
|
||
INTERACTION // 互動
|
||
OFFLINE_GENERATED // 離線生成
|
||
SOURCE_EXTRACTION // 原作萃取
|
||
CORRECTION // M-10 回溯修正:發現記錯了,以「想起來其實不是這樣」的新事件消化,不竄改既有的 INTERACTION 記憶
|
||
}
|
||
|
||
// 情緒標籤類型:對應情緒子系統六種狀態(見 D 群組)
|
||
enum EmotionTag {
|
||
CALM // 平靜
|
||
JOY // 愉悅
|
||
SAD // 低落
|
||
ALERT // 警戒
|
||
SHY // 害羞
|
||
GRUMPY // 彆扭
|
||
}
|
||
|
||
enum RelationshipStage {
|
||
STRANGER // 陌生 0-19
|
||
ACQUAINTANCE // 認識 20-39
|
||
FRIEND // 朋友 40-59
|
||
CLOSE_AMBIGUOUS // 摯友-曖昧 60-79
|
||
BONDED // 羈絆 80-100
|
||
}
|
||
|
||
// L-5 戀愛階段:與上面的 RelationshipStage(友誼軸分層)是完全獨立的兩套狀態機。
|
||
enum RomanceStage {
|
||
NONE // 戀愛軸未啟動(朋友)
|
||
INTERESTED // 在意
|
||
AMBIGUOUS // 曖昧
|
||
PARTNER // 戀人
|
||
DEEP_PARTNER // 深度伴侶
|
||
CRISIS // 關係危機(戀人階段的重大失望,需修復事件)
|
||
}
|
||
|
||
// L-6 角色年齡證據:證據強度依序為 官方明示(決定性)>作中身分/作中經過時間(強)>外觀與社會呈現(僅輔助,
|
||
// 不可單獨採信為成年)。isEpilogue 標記此證據屬於後日談推演區間、還是正史(已出版內容錨定點內)。
|
||
enum CharacterAgeEvidenceType {
|
||
OFFICIAL_STATED
|
||
IN_STORY_STATUS
|
||
ELAPSED_TIME
|
||
APPEARANCE
|
||
}
|
||
|
||
// L-5 心動事件類型與 L-2/L-4 的告白/里程碑事件,統一記在同一張表,供曲線與稀有度判斷共用。
|
||
enum RomanceEventType {
|
||
SHARED_HIGH_EMOTION_EXPERIENCE // 共同經歷高情緒事件(大幅上升)
|
||
UNDERSTOOD_MOMENT // 被理解的瞬間(大幅上升)
|
||
ADMIRED_TRAIT // 展現她欣賞的特質(中幅上升)
|
||
PERFECT_DISTANCE // 恰到好處的距離感(緩升)
|
||
DISAPPOINTMENT // 失望事件(回落)
|
||
CONFESSION_ACCEPTED
|
||
CONFESSION_REJECTED
|
||
MILESTONE_REACHED
|
||
MILESTONE_REJECTED
|
||
}
|
||
|
||
// I-1 週期表:平日/週末/假期各一份時段表
|
||
enum ScheduleDayType {
|
||
WEEKDAY // 平日
|
||
WEEKEND // 週末
|
||
HOLIDAY // 假期/特殊日
|
||
}
|
||
|
||
enum ScheduleSlotStatus {
|
||
SLEEP // 睡眠
|
||
BUSY // 忙碌
|
||
SEMI_BUSY // 半忙碌
|
||
FREE // 空閒
|
||
}
|
||
|
||
// J-1 委託類型
|
||
enum TaskType {
|
||
ONE_TIME // 單次提醒
|
||
RECURRING // 週期提醒
|
||
CONDITIONAL // 條件提醒
|
||
QUERY_ORGANIZE // 查詢整理
|
||
TODO_TRACKING // 代辦追蹤
|
||
}
|
||
|
||
enum TaskStatus {
|
||
PENDING // 尚未觸發
|
||
TRIGGERED // 已觸發,等候使用者回應
|
||
COMPLETED // 已完成
|
||
FAILED // 執行失敗
|
||
CANCELLED // 已取消
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(cuid())
|
||
name String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// T43 人格套用:使用者目前套用中的角色(null=未套用任何角色,以自己身分發話)。
|
||
possessingCharacterId String?
|
||
possessingCharacter Character? @relation("Possession", fields: [possessingCharacterId], references: [id], onDelete: SetNull)
|
||
|
||
relationships Relationship[]
|
||
relatedMemories EpisodicMemory[]
|
||
tasks Task[]
|
||
adultModeConsent AdultModeConsent?
|
||
romanceEventLogs RomanceEventLog[]
|
||
adultContentAttempts AdultContentAttemptLog[]
|
||
pushTokens PushToken[]
|
||
pushNotificationLogs PushNotificationLog[]
|
||
rooms Room[]
|
||
roomReadStates RoomReadState[]
|
||
callAttemptLogs CallAttemptLog[]
|
||
pendingLobbyCalls PendingLobbyCall[]
|
||
|
||
@@map("users")
|
||
}
|
||
|
||
// T43:大廳呼叫(CallService.say())辨識到的角色目前正被使用者套用時,不能讓人格系統代她判斷
|
||
// 接受/婉拒(那正是套用要避免的事),改記一筆待決,等使用者以她的身分明確決定。
|
||
model PendingLobbyCall {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
text String // 使用者原本喊的那句話,接受時用同一句話開房,維持與一般呼叫一致的行為
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("pending_lobby_calls")
|
||
}
|
||
|
||
model Work {
|
||
id String @id @default(cuid())
|
||
title String
|
||
volumeProgress String? // 卷數進度
|
||
worldview String? // 世界觀
|
||
progressAnchor String? // 作品進度錨定點(M-9/N 群組使用;描述作品實際描寫過的時期,人類可讀)
|
||
anchorStoryOrder Int @default(0) // M-9 錨定點的數值刀口:對應 Scene.storyOrder,之前是親身經歷,之後不存在
|
||
// N-1 後日談區間:錨定點抵達已出版內容盡頭後切入,時間繼續流動。
|
||
inEpilogue Boolean @default(false)
|
||
epilogueStartStoryOrder Int? // 切入後日談當下的錨定點位置,供 N-2 性格漂移計算「已經推演多久」
|
||
timeFlowMode String @default("REALTIME") // FAST/REALTIME/SLOW/FROZEN(N-1;後日談成年角色不可設 FAST)
|
||
// T34:推演進度時鐘的最後結算時間點(僅供 EpilogueProgressService 內部換算「現實流逝了多久」用,
|
||
// 與 updatedAt 分開,避免匯入、改標題等其他寫入動作干擾這個累積換算)。
|
||
epilogueProgressCheckpointAt DateTime?
|
||
branchedFromCanon Boolean @default(false) // N-5 分支保留:一旦分岔,之後官方新內容不再匯入這個作品實例
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
characters Character[]
|
||
scenes Scene[]
|
||
illustrations Illustration[]
|
||
contradictions SceneContradiction[]
|
||
|
||
@@map("works")
|
||
}
|
||
|
||
model Character {
|
||
id String @id @default(cuid())
|
||
workId String?
|
||
work Work? @relation(fields: [workId], references: [id], onDelete: SetNull)
|
||
|
||
source CharacterSource
|
||
buildStatus CharacterBuildStatus @default(CANDIDATE)
|
||
|
||
// 角色設定表七欄位
|
||
basicInfo String // 基本資料:名字、年齡、外型、出身
|
||
backgroundStory String // 背景故事:成長經歷、重大事件
|
||
personalityArchetype String // 性格原型:傲嬌/冷淡/天然呆/元氣/大小姐/三無
|
||
likesDislikes String // 喜好/厭惡
|
||
goalsObsessions String // 目標/執念
|
||
speechStyle String // 說話方式:口癖、語尾詞、第一/第二人稱
|
||
initialRelationValue String? // 人際初始值:對誰親近、對誰有心結
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
aliases CharacterAlias[]
|
||
episodicMemories EpisodicMemory[]
|
||
semanticMemories SemanticMemory[]
|
||
proceduralRules ProceduralRule[]
|
||
emotionState EmotionState?
|
||
sleepDebt SleepDebt?
|
||
relationships Relationship[]
|
||
contrastTriggerLogs ContrastTriggerLog[]
|
||
scheduleSlots ScheduleSlot[]
|
||
specialDays SpecialDay[]
|
||
scheduleExceptions ScheduleExceptionLog[]
|
||
tasks Task[]
|
||
proactiveCareLogs ProactiveCareLog[]
|
||
relationshipsToOthers CharacterRelationship[] @relation("RelationshipFrom")
|
||
relationshipsFromOthers CharacterRelationship[] @relation("RelationshipTo")
|
||
ageEvidence CharacterAgeEvidence[]
|
||
romanceEventLogs RomanceEventLog[]
|
||
adultContentAttempts AdultContentAttemptLog[]
|
||
scenePresences ScenePresence[]
|
||
spokenLines SceneLine[]
|
||
costumeCatalogEntries CostumeCatalogEntry[]
|
||
poseVocabularyEntries PoseVocabularyEntry[]
|
||
languageCorpusEntries LanguageCorpusEntry[]
|
||
personalityTraitSnapshots PersonalityTraitSnapshot[]
|
||
relationshipEventLogsFrom CharacterRelationshipEventLog[] @relation("EventLogFrom")
|
||
relationshipEventLogsTo CharacterRelationshipEventLog[] @relation("EventLogTo")
|
||
epilogueMilestoneLogs EpilogueMilestoneLog[]
|
||
voiceSheet VoiceSheet?
|
||
roomMemberships RoomMember[]
|
||
roomTurns RoomTurn[]
|
||
callAttemptsAsCandidate CallAttemptLog[] @relation("CallAttemptCandidate")
|
||
callAttemptsAsDelegate CallAttemptLog[] @relation("CallAttemptDelegate")
|
||
roomInvitationsReceived RoomInvitation[] @relation("RoomInvitationInvitee")
|
||
roomInvitationsSent RoomInvitation[] @relation("RoomInvitationInviter")
|
||
emotionBaselines CharacterEmotionBaseline[]
|
||
possessedByUsers User[] @relation("Possession")
|
||
pendingLobbyCallsAsTarget PendingLobbyCall[]
|
||
|
||
@@map("characters")
|
||
}
|
||
|
||
// P-1 角色音色設定:語音子系統的角色專屬設定,同 EmotionState 走 1:1 關聯。
|
||
model VoiceSheet {
|
||
id String @id @default(cuid())
|
||
characterId String @unique
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
baseTimbre String // 基礎音色描述,例如「清亮少女音」「低沉御姐音」「軟糯蘿莉音」
|
||
baseRate Float @default(1.0) // 預設語速倍率:性格決定的基準(元氣快、冷淡慢)
|
||
pitchRangeSemitones Float @default(4) // 音域幅度(半音數):語調起伏大小,元氣大、三無近乎單音
|
||
verbalTicsJson String @default("[]") // 口頭聲響庫(JSON 字串陣列):SQLite 不支援原生字串陣列欄位
|
||
forbiddenSoundsJson String @default("[]") // 禁則(JSON 字串陣列):此角色不會有的聲音表現
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("voice_sheets")
|
||
}
|
||
|
||
// L-6 角色年齡判定的原始證據登錄;由 AgeDeterminationService 依證據優先序算出年齡狀態,不直接存最終結論
|
||
// ——結論永遠是「依目前登錄的證據重新算出來」,避免證據更新後忘記同步結論欄位。
|
||
model CharacterAgeEvidence {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
evidenceType CharacterAgeEvidenceType
|
||
impliesAdult Boolean
|
||
isEpilogue Boolean @default(false) // true=後日談推演區間的證據,非正史(已出版內容)
|
||
description String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("character_age_evidence")
|
||
}
|
||
|
||
// L-5 心動事件與 L-2/L-4 的告白/里程碑,統一記錄,供戀愛軸曲線計算、稀有度節流、里程碑追蹤共用。
|
||
model RomanceEventLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
eventType RomanceEventType
|
||
delta Float @default(0)
|
||
detail String? // 例如里程碑類型(HAND_HOLDING/HUG/FOREHEAD_KISS…)
|
||
occurredAt DateTime @default(now())
|
||
|
||
@@map("romance_event_logs")
|
||
}
|
||
|
||
// L-7 使用者是否已完成成年驗證+明確 opt-in 開啟成人模式;預設全關,且僅影響「使用者分級」這一層邊界,
|
||
// 角色資格與硬邊界仍需獨立通過才會真的放行(見 RomanceEligibilityService)。
|
||
model AdultModeConsent {
|
||
id String @id @default(cuid())
|
||
userId String @unique
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
verifiedAdult Boolean @default(false)
|
||
optedIn Boolean @default(false)
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("adult_mode_consents")
|
||
}
|
||
|
||
// L-7 驗收:任何嘗試(無論是否放行)都留下記錄,證明繞過嘗試會被引擎層攔截並記錄,不會被悄悄放過或悄悄擋掉。
|
||
model AdultContentAttemptLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
allowed Boolean
|
||
reason String
|
||
attemptedAt DateTime @default(now())
|
||
|
||
@@map("adult_content_attempt_logs")
|
||
}
|
||
|
||
// K-2/K-4 角色間關係:群聊中「角色間關係上場」與發言權分配的「與發言者的關係」都吃這張表。
|
||
// 有向邊(characterId 對 otherCharacterId 的觀感),故同作品兩角色互相的觀感可以不對稱。
|
||
model CharacterRelationship {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation("RelationshipFrom", fields: [characterId], references: [id], onDelete: Cascade)
|
||
otherCharacterId String
|
||
otherCharacter Character @relation("RelationshipTo", fields: [otherCharacterId], references: [id], onDelete: Cascade)
|
||
affinity Int @default(50) // 0~100,越高越要好;50 為中性
|
||
dynamic String? // 互動基調描述性標籤,例如 loyal-friend/rival/protective/crush
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([characterId, otherCharacterId])
|
||
@@map("character_relationships")
|
||
}
|
||
|
||
// G-4 反差萌稀有度控制:記錄每次反差行為觸發的時間,供冷卻期判定。
|
||
model ContrastTriggerLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
triggeredAt DateTime @default(now())
|
||
|
||
@@map("contrast_trigger_logs")
|
||
}
|
||
|
||
// I-1 作息表:平日/週末/假期各一份時段表,每個時段一列。
|
||
model ScheduleSlot {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
dayType ScheduleDayType
|
||
startMinute Int // 當天 00:00 起算的分鐘數(0~1440),跨午夜的時段請拆成兩列
|
||
endMinute Int
|
||
status ScheduleSlotStatus
|
||
label String? // 固定行程名稱,例如「社團活動」
|
||
|
||
@@map("schedule_slots")
|
||
}
|
||
|
||
// I-1 特殊日:考試週、生日、原作紀念日——當天整天視為 HOLIDAY 作息。
|
||
model SpecialDay {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
date DateTime // 只使用日期部分(比對年/月/日)
|
||
label String
|
||
|
||
@@map("special_days")
|
||
}
|
||
|
||
// I-4 破例規則:記錄每種破例最後一次觸發的時間,供稀有度冷卻判定。
|
||
model ScheduleExceptionLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
exceptionType String // LATE_NIGHT_CHAT/SNEAK_REPLY_IN_CLASS/ADJUST_SCHEDULE
|
||
triggeredAt DateTime @default(now())
|
||
|
||
@@map("schedule_exception_logs")
|
||
}
|
||
|
||
model CharacterAlias {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
formalName String // 正式名
|
||
nickname String? // 暱稱
|
||
calledByOthers String? // 他人稱呼(作品中其他角色如何稱呼)
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("character_aliases")
|
||
}
|
||
|
||
model EpisodicMemory {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
content String
|
||
occurredAt DateTime // 發生時間
|
||
emotionTag EmotionTag
|
||
emotionIntensity Float // 情緒強度
|
||
retrievalCount Int @default(0) // 提取次數
|
||
lastRetrievedAt DateTime?
|
||
source MemorySource
|
||
weight Float @default(1) // 權重(供遺忘/檢索排序使用)
|
||
relatedUserId String? // 這段記憶與哪位使用者相關(E-5 關係加權檢索用)
|
||
relatedUser User? @relation(fields: [relatedUserId], references: [id], onDelete: SetNull)
|
||
isPrivate Boolean @default(false) // K-3 隱私邊界:一對一聊過的私密內容,群聊中不主動洩漏(口風不緊原型除外)
|
||
// M-7/M-10 可追溯性:這筆記憶是否由某個場景投影而來。只有這裡非 null 的記錄會被「重新投影」刪除重建;
|
||
// 使用者互動記憶(source=INTERACTION)永遠不會設這個欄位,因此重新投影永遠不會動到使用者互動記錄。
|
||
sourceSceneId String?
|
||
sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull)
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("episodic_memories")
|
||
}
|
||
|
||
model SemanticMemory {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
fact String // 去情境化事實
|
||
about String // 對象(關於誰/什麼)
|
||
sourceSceneId String? // M-4/M-7 可追溯性:由哪個場景萃取而來(新登場設定)
|
||
sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull)
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("semantic_memories")
|
||
}
|
||
|
||
model ProceduralRule {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
situation String // 情境
|
||
responsePattern String // 回應模式
|
||
weight Float @default(1)
|
||
// T25:最近一次新增或強化這條規則的來源場景(章節匯入觸發時才會設值;F-5 對話中即時強化/糾正
|
||
// 觸發的規則永遠不設這個欄位)。同一 (situation, responsePattern) 若先後被多個場景強化,這裡只記錄
|
||
// 最近一次,是已知的簡化——重投影時只能整條清掉重建,無法只回退「這個場景那一份增量」。
|
||
sourceSceneId String?
|
||
sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("procedural_rules")
|
||
}
|
||
|
||
model EmotionState {
|
||
id String @id @default(cuid())
|
||
characterId String @unique
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
|
||
calm Float @default(100) // 平靜
|
||
joy Float @default(0) // 愉悅
|
||
sad Float @default(0) // 低落
|
||
alert Float @default(0) // 警戒
|
||
shy Float @default(0) // 害羞
|
||
grumpy Float @default(0) // 彆扭
|
||
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("emotion_states")
|
||
}
|
||
|
||
// T32:睡眠債——SLEEP 時段內若仍在房間活動(沒有真的睡),累積該時段時長為債務;某次 SLEEP
|
||
// 時段完全沒有活動(真的補眠了)即歸零。影響三處:動態間隔懲罰(回覆變慢)、ACTION 權重提高
|
||
// (打瞌睡演出)、LEAVE 動因升級為「撐不住想睡」;另加重行程情緒基線的「累」傾向。
|
||
model SleepDebt {
|
||
id String @id @default(cuid())
|
||
characterId String @unique
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
accumulatedMinutes Float @default(0)
|
||
lastReconciledDate DateTime? // 台北當地日期(僅比對年月日),避免同一天重複計入同一段 SLEEP 時段
|
||
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("sleep_debts")
|
||
}
|
||
|
||
model Relationship {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
intimacy Int @default(0) // 親密度 0~100(友誼軸:穩定累積、衰減慢)
|
||
trust Int @default(0) // 信任度 0~100
|
||
firstMet DateTime?
|
||
lastInteraction DateTime?
|
||
interactionCount Int @default(0)
|
||
stage RelationshipStage @default(STRANGER)
|
||
|
||
// L-1 戀愛軸:與友誼軸(intimacy)獨立,事件驅動、衰減快、易回落。僅限成年角色資格生效後才會被寫入非零值。
|
||
romanceMeter Int @default(0) // 心動值 0~100
|
||
romanceStage RomanceStage @default(NONE)
|
||
lastRomanceEventAt DateTime?
|
||
awkwardUntil DateTime? // L-2 告白被拒後的尷尬期:互動變客氣,直到此時間點
|
||
consecutiveRejections Int @default(0) // L-2 連續告白失敗次數:累積到門檻會真的傷關係
|
||
partnerSince DateTime? // L-4 進入戀人階段的時間點,用於判斷戀人初期/穩定期
|
||
romanceEventsInStage Int @default(0) // L-4 進入目前階段後累積的正向心動事件數,同樣用於判斷穩定期
|
||
|
||
ledgerEntries SentimentLedgerEntry[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([characterId, userId])
|
||
@@map("relationships")
|
||
}
|
||
|
||
model SentimentLedgerEntry {
|
||
id String @id @default(cuid())
|
||
relationshipId String
|
||
relationship Relationship @relation(fields: [relationshipId], references: [id], onDelete: Cascade)
|
||
date DateTime
|
||
event String
|
||
weight Float // 允許正負:正向事件為正值,負向事件為負值
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("sentiment_ledger_entries")
|
||
}
|
||
|
||
// J-1 委託:使用者請角色做的事。執行(觸發準時度)是引擎責任,演出(接案/觸發/追擊的台詞)是角色責任。
|
||
model Task {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
type TaskType
|
||
content String // 委託內容(去除時間片語後的純委託描述,例如「開會」)
|
||
rawText String // 使用者原始輸入,供回顧/記憶引用
|
||
|
||
triggerAt DateTime? // 單次提醒的觸發時間;週期提醒改用 cronExpression
|
||
cronExpression String? // 週期提醒(node-cron 表達式)
|
||
condition String? // 條件提醒的條件描述(MVP 為文字紀錄,不做真實環境條件監測)
|
||
|
||
status TaskStatus @default(PENDING)
|
||
triggeredAt DateTime? // 最近一次實際觸發的時間
|
||
triggerMessage String? // 最近一次觸發演出的訊息內容(供前端/測試輪詢)
|
||
|
||
overdueNudgeCount Int @default(0) // J-4 逾期追擊次數
|
||
lastNudgeAt DateTime?
|
||
|
||
sourceRoomId String? // T7:房內自走委託的來源房間,供追溯到來源房間與該則發言
|
||
sourceRoom Room? @relation(fields: [sourceRoomId], references: [id], onDelete: SetNull)
|
||
|
||
completedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@map("tasks")
|
||
}
|
||
|
||
// J-6 主動關照:記錄每次主動關心的觸發時間與內容,供稀有度節流與「不重複台詞」判定。
|
||
model ProactiveCareLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
message String
|
||
triggeredAt DateTime @default(now())
|
||
|
||
@@map("proactive_care_logs")
|
||
}
|
||
|
||
// M-1 插圖類型:對立繪的用途不同——封面定基礎氣質、彩頁給服裝差分、黑白插圖給表情/姿勢差分。
|
||
enum IllustrationType {
|
||
COVER
|
||
COLOR_PAGE
|
||
BW_ILLUSTRATION
|
||
}
|
||
|
||
// M-1 插圖索引:不儲存受版權保護的原圖本身,只登錄「畫面描述」供服裝目錄/姿勢語彙/表情差分參考使用。
|
||
model Illustration {
|
||
id String @id @default(cuid())
|
||
workId String
|
||
work Work @relation(fields: [workId], references: [id], onDelete: Cascade)
|
||
volume Int
|
||
page Int?
|
||
type IllustrationType
|
||
sceneId String? // 對應場景(插圖與文本的交叉索引)
|
||
scene Scene? @relation(fields: [sceneId], references: [id], onDelete: SetNull)
|
||
description String // 畫面描述:表情/姿勢/服裝等文字記錄
|
||
createdAt DateTime @default(now())
|
||
|
||
costumeCatalogEntries CostumeCatalogEntry[]
|
||
poseVocabularyEntries PoseVocabularyEntry[]
|
||
|
||
@@map("illustrations")
|
||
}
|
||
|
||
// M-1 服裝目錄:逐卷登錄角色出現過的服裝,立繪服裝層只能從這個目錄取用,不可自創。
|
||
model CostumeCatalogEntry {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
illustrationId String?
|
||
illustration Illustration? @relation(fields: [illustrationId], references: [id], onDelete: SetNull)
|
||
label String // 例如「制服」「便服」「禮服」
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("costume_catalog_entries")
|
||
}
|
||
|
||
// M-1 姿勢語彙:繪師畫該角色時慣用的身體語言(總是抱著書、習慣性歪頭),納入立繪姿勢差分的官方認證來源。
|
||
model PoseVocabularyEntry {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
illustrationId String?
|
||
illustration Illustration? @relation(fields: [illustrationId], references: [id], onDelete: SetNull)
|
||
label String // 例如「總是抱著書」「習慣性歪頭」
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("pose_vocabulary_entries")
|
||
}
|
||
|
||
// M-4 場景內逐行的類型:對應五類萃取產物裡「事件」以外,需要逐行分類才能各自導向正確目的地的部分。
|
||
enum SceneLineType {
|
||
DIALOGUE // 台詞 → 語言風格語料
|
||
ACTION // 動作描寫 → 事件的一部分
|
||
MONOLOGUE // 內心獨白/心理描寫 → 性格參數證據
|
||
NARRATION // 敘述/新登場設定 → 語意記憶
|
||
}
|
||
|
||
enum SceneLineStatus {
|
||
PENDING // 尚待歸屬判定
|
||
CONFIRMED // 信心分數達門檻且指紋一致,已寫入場景資料庫
|
||
QUARANTINED // 低信心或指紋矛盾,隔離、不寫入任何角色
|
||
}
|
||
|
||
// M-3 共用場景資料庫:章節文本以場景為單位切分後的客觀記錄單位,角色中立,不含主觀詮釋。
|
||
// M-8/M-9 的共用年表以 storyOrder 排序——同作品所有場景共用一條數線,錨定點即在此數線上切一刀。
|
||
model Scene {
|
||
id String @id @default(cuid())
|
||
workId String
|
||
work Work @relation(fields: [workId], references: [id], onDelete: Cascade)
|
||
volume Int
|
||
chapterLabel String
|
||
storyOrder Int // 全作品共用年表排序鍵
|
||
sourceType String @default("NOVEL") // M-8 矛盾裁決優先序用:NOVEL/OFFICIAL_SETTEI/ANIME_ADAPTATION
|
||
summary String // 場景摘要(客觀記錄:在場者、誰說了哪句、誰做了什麼)
|
||
rawText String // 匯入的原始場景文本(僅供測試/自製文本使用,不得為受版權保護原文)
|
||
projected Boolean @default(false) // M-9:是否已投影進角色記憶(只有 storyOrder <= 錨定點的場景會被投影)
|
||
createdAt DateTime @default(now())
|
||
|
||
participants ScenePresence[]
|
||
lines SceneLine[]
|
||
illustrations Illustration[]
|
||
facts SceneFact[]
|
||
episodicMemories EpisodicMemory[]
|
||
semanticMemories SemanticMemory[]
|
||
relationshipEventLogs CharacterRelationshipEventLog[]
|
||
personalityTraitSnapshots PersonalityTraitSnapshot[]
|
||
interactions SceneInteraction[]
|
||
endingDetectionLogs EndingDetectionLog[]
|
||
emotionBaselines CharacterEmotionBaseline[]
|
||
proceduralRules ProceduralRule[]
|
||
|
||
@@unique([workId, storyOrder])
|
||
@@map("scenes")
|
||
}
|
||
|
||
// M-3/M-5 場景名冊(硬約束):不在場的角色不可能說話,歸屬候選只限在場者。
|
||
model ScenePresence {
|
||
id String @id @default(cuid())
|
||
sceneId String
|
||
scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade)
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([sceneId, characterId])
|
||
@@map("scene_presences")
|
||
}
|
||
|
||
// M-4/M-5/M-6 場景內逐行記錄:每一行都有獨立的歸屬信心分數與狀態,是隔離區機制運作的最小單位。
|
||
model SceneLine {
|
||
id String @id @default(cuid())
|
||
sceneId String
|
||
scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade)
|
||
lineIndex Int
|
||
lineType SceneLineType
|
||
rawText String
|
||
hasExplicitMarker Boolean @default(false) // 是否有「○○說道」等明示標記(決定性證據)
|
||
markerCharacterId String? // 明示標記指向的角色(若有)
|
||
speakerCharacterId String? // 目前歸屬(可能是明示、也可能是推論後確認的結果)
|
||
speakerCharacter Character? @relation(fields: [speakerCharacterId], references: [id], onDelete: SetNull)
|
||
confidence Float @default(0)
|
||
status SceneLineStatus @default(PENDING)
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("scene_lines")
|
||
}
|
||
|
||
// M-8 場景事實:客觀、可檢查的事實標記(例如天氣),跨場景比對同一事件標籤下的事實是否矛盾。
|
||
model SceneFact {
|
||
id String @id @default(cuid())
|
||
sceneId String
|
||
scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade)
|
||
eventLabel String // 事件標籤:同一事件標籤下的事實理應一致(例如「秋季運動會」)
|
||
factKey String // 例如「天氣」
|
||
factValue String // 例如「雨天」
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("scene_facts")
|
||
}
|
||
|
||
// M-4 互動的中立記錄:場景資料庫階段只記錄「發生了什麼互動」,尚未套用到任何角色的關係帳本——
|
||
// 套用(寫入 CharacterRelationshipEventLog)是投影階段的職責,一樣受 M-9 錨定點知識邊界節制。
|
||
model SceneInteraction {
|
||
id String @id @default(cuid())
|
||
sceneId String
|
||
scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade)
|
||
characterId String
|
||
otherCharacterId String
|
||
delta Float
|
||
description String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("scene_interactions")
|
||
}
|
||
|
||
enum ContradictionStatus {
|
||
PENDING_HUMAN_REVIEW
|
||
RESOLVED
|
||
}
|
||
|
||
// M-8 矛盾偵測:不同場景描寫衝突時標記待人工確認;裁決優先序見 ContradictionDetectionService。
|
||
model SceneContradiction {
|
||
id String @id @default(cuid())
|
||
workId String
|
||
work Work @relation(fields: [workId], references: [id], onDelete: Cascade)
|
||
eventLabel String
|
||
factKey String
|
||
description String
|
||
status ContradictionStatus @default(PENDING_HUMAN_REVIEW)
|
||
resolution String?
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("scene_contradictions")
|
||
}
|
||
|
||
// M-4/M-6 語言風格語料:已確認歸屬的台詞樣本,供語言指紋統計(人稱/語尾/口癖頻率)使用。
|
||
model LanguageCorpusEntry {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
sceneId String?
|
||
text String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("language_corpus_entries")
|
||
}
|
||
|
||
// M-8 性格演變追蹤:參數不是單一值而是帶時間戳的序列,依錨定點取用當時的性格參數。
|
||
model PersonalityTraitSnapshot {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
sourceSceneId String? // M-4/M-7 可追溯性:由哪個場景的獨白/心理描寫萃取而來
|
||
sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull)
|
||
atStoryOrder Int // 對應的年表位置
|
||
note String // 描述性文字:這個時間點角色參數上的變化說明
|
||
expressivenessOverride Float? // 可選:覆寫 G-1 外顯度參數,模擬成長軌跡
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("personality_trait_snapshots")
|
||
}
|
||
|
||
// M-4 互動→關係帳本(角色對角色版):每次原作互動評正負向權重,累積出她對每個角色的信任/親密度,
|
||
// 可追溯到來源場景;同一事件寫入雙方視角,權重可以不對稱。
|
||
model CharacterRelationshipEventLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation("EventLogFrom", fields: [characterId], references: [id], onDelete: Cascade)
|
||
otherCharacterId String
|
||
otherCharacter Character @relation("EventLogTo", fields: [otherCharacterId], references: [id], onDelete: Cascade)
|
||
sceneId String?
|
||
scene Scene? @relation(fields: [sceneId], references: [id], onDelete: SetNull)
|
||
delta Float
|
||
description String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("character_relationship_event_logs")
|
||
}
|
||
|
||
// N-4 人生階段推演:記錄已生成過的里程碑,供頻率節流(低頻)與避免重複生成同一件人生大事。
|
||
model EpilogueMilestoneLog {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
storyOrder Int
|
||
label String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("epilogue_milestone_logs")
|
||
}
|
||
|
||
// Q-3 推播通知:使用者的裝置推播 token(一個使用者可以有多台裝置,各自登記一筆)。
|
||
model PushToken {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
token String @unique
|
||
platform String // 例如 "expo"
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("push_tokens")
|
||
}
|
||
|
||
// Q-3 每次嘗試送出推播都留一筆記錄(成功或失敗皆記),方便之後追查「提醒有沒有真的送達」。
|
||
model PushNotificationLog {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
title String
|
||
body String
|
||
success Boolean
|
||
errorText String?
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("push_notification_logs")
|
||
}
|
||
|
||
// R1~R8 房間持久化地基(T2)。房間全面持久化到 DB,破壞性取代既有記憶體實作,不留相容層。
|
||
|
||
enum RoomMode {
|
||
LOBBY // 大廳:每位使用者恆有唯一一間,status 永久 ACTIVE
|
||
SOLO // 一對一
|
||
GROUP // 多人房(含既有 K-2/K-4 互動群聊)
|
||
SELF_CHAT // T3:既有 K-6 角色自聊(自走閒聊,使用者插話後改為 GROUP),沿用既有語意直接持久化
|
||
}
|
||
|
||
enum RoomStatus {
|
||
ACTIVE
|
||
CLOSED
|
||
}
|
||
|
||
enum RoomTurnKind {
|
||
TALK // 對話
|
||
ACTION // 動作演出
|
||
DELEGATE // 委託
|
||
LEAVE // 離開
|
||
SYSTEM // 系統提示
|
||
}
|
||
|
||
enum RoomInvitationStatus {
|
||
PENDING
|
||
ACCEPTED
|
||
DECLINED
|
||
}
|
||
|
||
enum EndingDetectionResolution {
|
||
PENDING
|
||
CONFIRMED
|
||
REJECTED
|
||
}
|
||
|
||
model Room {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
mode RoomMode
|
||
status RoomStatus @default(ACTIVE)
|
||
|
||
topicSeed String? // 開房緣由摘要,供自走判斷話題方向
|
||
autoAdvanceEnabled Boolean @default(true)
|
||
muted Boolean @default(false)
|
||
lastAdvancedAt DateTime?
|
||
closedAt DateTime?
|
||
closeReason String? // 例如 ALL_LEFT;沿用既有 schema 慣例(如 ScheduleExceptionLog.exceptionType)以開放字串記錄分類,不預先窮舉封閉列舉
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
members RoomMember[]
|
||
turns RoomTurn[]
|
||
readStates RoomReadState[]
|
||
usageLogs RoomUsageLog[]
|
||
callAttemptLogs CallAttemptLog[]
|
||
invitations RoomInvitation[]
|
||
tasks Task[]
|
||
|
||
@@map("rooms")
|
||
}
|
||
|
||
// RoomMember:房內在場名冊。activeCharacterId 於在場(leftAt 為 null)時等於 characterId、
|
||
// 離場時設回 null——SQL 唯一索引忽略 NULL,因此「未離場時 characterId 唯一」(R5:角色不可跨房)
|
||
// 直接由資料庫唯一索引擋下,不需仰賴應用層才能保證。
|
||
model RoomMember {
|
||
id String @id @default(cuid())
|
||
roomId String
|
||
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
|
||
activeCharacterId String? @unique
|
||
|
||
joinedAt DateTime @default(now())
|
||
leftAt DateTime?
|
||
leaveReason String? // 例如 ENTER_ROOM;開放字串記錄動因分類,理由同 Room.closeReason
|
||
|
||
@@map("room_members")
|
||
}
|
||
|
||
// RoomTurn:房間逐輪訊息紀錄,取代既有記憶體對話陣列;seq 為房內單調遞增序號供分頁與 SSE Last-Event-ID 使用。
|
||
model RoomTurn {
|
||
id String @id @default(cuid())
|
||
roomId String
|
||
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
|
||
seq Int
|
||
kind RoomTurnKind @default(TALK)
|
||
|
||
speakerCharacterId String? // null 表示使用者本人發話
|
||
speakerCharacter Character? @relation(fields: [speakerCharacterId], references: [id], onDelete: SetNull)
|
||
|
||
content String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([roomId, seq])
|
||
@@map("room_turns")
|
||
}
|
||
|
||
// RoomReadState:每位使用者在每間房間的已讀游標,lastReadSeq 單調遞增、不可回退。
|
||
model RoomReadState {
|
||
id String @id @default(cuid())
|
||
roomId String
|
||
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
lastReadSeq Int @default(0)
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([roomId, userId])
|
||
@@map("room_read_states")
|
||
}
|
||
|
||
// RoomUsageLog:每次生成後的 LLM 用量紀錄,僅供觀察,不做自動熔斷。
|
||
model RoomUsageLog {
|
||
id String @id @default(cuid())
|
||
roomId String
|
||
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
|
||
model String
|
||
promptTokens Int
|
||
completionTokens Int
|
||
turnSeq Int
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("room_usage_logs")
|
||
}
|
||
|
||
// CallAttemptLog:大廳對話式呼叫(T5)對每位候選角色的判定紀錄,含代答者(T23)。
|
||
model CallAttemptLog {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
characterId String
|
||
character Character @relation("CallAttemptCandidate", fields: [characterId], references: [id], onDelete: Cascade)
|
||
|
||
accepted Boolean
|
||
reason String? // 判定依據摘要(行程/情緒/關係/已在其他房間等)
|
||
|
||
delegatedByCharacterId String?
|
||
delegatedByCharacter Character? @relation("CallAttemptDelegate", fields: [delegatedByCharacterId], references: [id], onDelete: SetNull)
|
||
|
||
resultingRoomId String?
|
||
resultingRoom Room? @relation(fields: [resultingRoomId], references: [id], onDelete: SetNull)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("call_attempt_logs")
|
||
}
|
||
|
||
// RoomInvitation:使用者呼叫與房內角色邀請共用同一套裁決機制;invitedByCharacterId 為 null 表示
|
||
// 使用者從大廳直接呼叫,非 null 表示由房內某角色發出邀請。
|
||
model RoomInvitation {
|
||
id String @id @default(cuid())
|
||
roomId String
|
||
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
|
||
characterId String
|
||
character Character @relation("RoomInvitationInvitee", fields: [characterId], references: [id], onDelete: Cascade)
|
||
|
||
invitedByCharacterId String?
|
||
invitedByCharacter Character? @relation("RoomInvitationInviter", fields: [invitedByCharacterId], references: [id], onDelete: SetNull)
|
||
|
||
status RoomInvitationStatus @default(PENDING)
|
||
createdAt DateTime @default(now())
|
||
respondedAt DateTime?
|
||
|
||
@@map("room_invitations")
|
||
}
|
||
|
||
// EndingDetectionLog:匯入投影完成後,每章由 LLM 判定是否為結局的偵測結果,待使用者確認才會真的切日後談。
|
||
model EndingDetectionLog {
|
||
id String @id @default(cuid())
|
||
sceneId String
|
||
scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade)
|
||
evidence String // LLM 判定理由
|
||
resolution EndingDetectionResolution @default(PENDING)
|
||
createdAt DateTime @default(now())
|
||
respondedAt DateTime?
|
||
|
||
@@map("ending_detection_logs")
|
||
}
|
||
|
||
// CharacterEmotionBaseline:章節匯入(T17)累積出的情緒基線,與行程基線(ScheduleEmotionBaselineService,
|
||
// 純即時計算不落地)在 emotion.service 取 baselineSignal 時同層合成;不得直接寫 EmotionState 六維本身。
|
||
model CharacterEmotionBaseline {
|
||
id String @id @default(cuid())
|
||
characterId String
|
||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
||
emotionTag EmotionTag
|
||
intensity Float
|
||
sourceSceneId String?
|
||
sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull)
|
||
createdAt DateTime @default(now())
|
||
|
||
@@map("character_emotion_baselines")
|
||
}
|