You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model MailboxAccount {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
name String @db.VarChar(128)
host String @default("imap.qq.com") @db.VarChar(255)
port Int @default(993) @db.UnsignedInt
username String @db.VarChar(320)
/// AES-GCM ciphertext (base64); empty when OAuth-only
passwordEnc String @default("") @map("password_enc") @db.VarChar(1024)
/// PASSWORD = 授权码;OAUTH = XOAUTH2
authType String @default("PASSWORD") @map("auth_type") @db.VarChar(16)
oauthProvider String? @map("oauth_provider") @db.VarChar(32)
oauthRefreshEnc String? @map("oauth_refresh_enc") @db.VarChar(2048)
oauthAccessEnc String? @map("oauth_access_enc") @db.VarChar(2048)
oauthExpiresAt DateTime? @map("oauth_expires_at") @db.DateTime(3)
idleEnabled Boolean @default(true) @map("idle_enabled")
folder String @default("INBOX") @db.VarChar(64)
enabled Boolean @default(true)
lastTestAt DateTime? @map("last_test_at") @db.DateTime(3)
lastTestOk Boolean? @map("last_test_ok")
lastTestError String? @map("last_test_error") @db.VarChar(500)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
mails MailMessage[]
@@unique([username], map: "uk_mailbox_username")
@@index([enabled], map: "idx_mailbox_enabled")
@@map("mailbox_account")
}
model MailMessage {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
// utf8mb4 unique key limit: keep message_id ≤ 191 chars for UNIQUE
messageId String? @unique @map("message_id") @db.VarChar(191)
mailboxAccountId BigInt? @map("mailbox_account_id") @db.UnsignedBigInt
imapUid BigInt? @map("imap_uid") @db.UnsignedBigInt
folder String @default("INBOX") @db.VarChar(64)
subject String @default("(无主题)") @db.VarChar(512)
fromAddr String @default("") @map("from_addr") @db.VarChar(320)
receivedAt DateTime? @map("received_at") @db.DateTime(3)
bodyText String? @map("body_text") @db.MediumText
/// OCR 抽取文本(权威解析辅助)
ocrText String? @map("ocr_text") @db.MediumText
/// 线程根 Message-ID(规范化)
threadId String? @map("thread_id") @db.VarChar(191)
inReplyTo String? @map("in_reply_to") @db.VarChar(191)
referencesHeader String? @map("references_header") @db.Text
isThreadRoot Boolean @default(true) @map("is_thread_root")
mailType String @default("UNKNOWN") @map("mail_type") @db.VarChar(32)
status String @default("FETCHED") @db.VarChar(32)
typeEvidence Json? @map("type_evidence")
snapshotPath String? @map("snapshot_path") @db.VarChar(512)
rawHash String? @unique @map("raw_hash") @db.Char(64)
lastError String? @map("last_error") @db.VarChar(1000)
version Int @default(1) @db.UnsignedInt
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
mailboxAccount MailboxAccount? @relation(fields: [mailboxAccountId], references: [id], onDelete: SetNull)
attachments MailAttachment[]
parseResult ParseResult?
imports ContainerImport[]
@@unique([mailboxAccountId, folder, imapUid], map: "uk_mailbox_folder_uid")
@@index([status, receivedAt], map: "idx_status_received")
@@index([mailType], map: "idx_mail_type")
@@index([mailboxAccountId], map: "idx_mailbox")
@@index([threadId], map: "idx_thread")
@@map("mail_message")
}
model MailAttachment {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
mailId BigInt @map("mail_id") @db.UnsignedBigInt
filename String @db.VarChar(512)
contentType String? @map("content_type") @db.VarChar(128)
sha256 String @db.Char(64)
path String @db.VarChar(512)
size Int @db.UnsignedInt
rejected Boolean @default(false)
templateId String? @map("template_id") @db.VarChar(64)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
mail MailMessage @relation(fields: [mailId], references: [id])
@@index([mailId], map: "idx_mail")
@@map("mail_attachment")
}
model ParseResult {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
mailId BigInt @unique @map("mail_id") @db.UnsignedBigInt
containerHeader Json @map("container_header")
shipments Json
lineage Json?
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
mail MailMessage @relation(fields: [mailId], references: [id])
@@map("parse_result")
}
model ContainerImport {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
mailId BigInt @map("mail_id") @db.UnsignedBigInt
containerNo String @map("container_no") @db.VarChar(50)
externalId String? @map("external_id") @db.VarChar(64)
requestBody String? @map("request_body") @db.MediumText
responseBody String? @map("response_body") @db.MediumText
status String @db.VarChar(32)
lastError String? @map("last_error") @db.VarChar(1000)
shipmentsHash String @map("shipments_hash") @db.Char(64)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
mail MailMessage @relation(fields: [mailId], references: [id])
compensations ImportCompensation[]
@@unique([mailId, containerNo, shipmentsHash], map: "uk_mail_container_shiphash")
@@index([mailId], map: "idx_mail")
@@index([containerNo], map: "idx_container")
@@map("container_import")
}
model ContainerActiveLock {
containerNo String @id @map("container_no") @db.VarChar(50)
mailId BigInt @map("mail_id") @db.UnsignedBigInt
importId BigInt? @map("import_id") @db.UnsignedBigInt
lockedAt DateTime @map("locked_at") @db.DateTime(3)
@@map("container_active_lock")
}
model ImportCompensation {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
importId BigInt @map("import_id") @db.UnsignedBigInt
retryCount Int @default(0) @map("retry_count") @db.UnsignedInt
maxRetry Int @default(3) @map("max_retry") @db.UnsignedInt
nextRetryAt DateTime? @map("next_retry_at") @db.DateTime(3)
reason String @db.VarChar(64)
status String @default("OPEN") @db.VarChar(32)
retryToken String @unique @map("retry_token") @db.Char(36)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
import ContainerImport @relation(fields: [importId], references: [id])
@@index([status, nextRetryAt], map: "idx_open")
@@map("import_compensation")
}
model CcTokenCache {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
loginMark String @unique @map("login_mark") @db.Char(36)
token String @db.VarChar(128)
expireAt DateTime @map("expire_at") @db.DateTime(3)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
@@map("cc_token_cache")
}
/// Singleton CC 连接配置(id 固定为 1);优先于 .env,密码 AES 加密
model CcSettings {
id Int @id @default(1)
mock Boolean @default(true)
apiBase String @map("api_base") @db.VarChar(512)
saasHeader String @map("saas_header") @db.VarChar(64)
loginMark String @map("login_mark") @db.Char(36)
username String @default("") @db.VarChar(128)
/// AES-GCM;空表示沿用 .env 密码或未配置
passwordEnc String? @map("password_enc") @db.VarChar(1024)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
@@map("cc_settings")
}
/// CC 写能力门禁:live | mock | off
model CcWriteCapability {
id String @id @db.VarChar(32)
mode String @default("mock") @db.VarChar(16)
endpoint String? @db.VarChar(512)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
@@map("cc_write_capability")
}
/// Singleton OAuth 应用凭证(id=1);优先于 .env,secret AES 加密
model OauthSettings {
id Int @id @default(1)
publicBaseUrl String @default("http://localhost:3000") @map("public_base_url") @db.VarChar(512)
googleClientId String @default("") @map("google_client_id") @db.VarChar(256)
googleClientSecretEnc String? @map("google_client_secret_enc") @db.VarChar(1024)
msClientId String @default("") @map("ms_client_id") @db.VarChar(256)
msClientSecretEnc String? @map("ms_client_secret_enc") @db.VarChar(1024)
msTenant String @default("common") @map("ms_tenant") @db.VarChar(64)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
@@map("oauth_settings")
}
model SenderCustomerMap {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
matchType String @map("match_type") @db.VarChar(32)
matchValue String @map("match_value") @db.VarChar(320)
customerCode String @map("customer_code") @db.VarChar(64)
enabled Boolean @default(true)
@@unique([matchType, matchValue], map: "uk_match")
@@map("sender_customer_map")
}
model AppUser {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
username String @unique @db.VarChar(64)
passwordHash String @map("password_hash") @db.VarChar(128)
role String @default("ops") @db.VarChar(16)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
@@map("app_user")
}
/// Singleton IMAP 自动拉取配置(id=1);poll_interval_ms 优先于 .env POLL_INTERVAL_MS
model ImapSettings {
id Int @id @default(1)
/// 默认 30min;合法范围 3min~7d
pollIntervalMs Int @default(1800000) @map("poll_interval_ms")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.DateTime(3)
@@map("imap_settings")
}
/// 拉取过滤规则:关键词 / 发件人白名单 / 发件人黑名单 / 关键词黑名单
model ImapFilterRule {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
kind String @db.VarChar(32)
value String @db.VarChar(256)
enabled Boolean @default(true)
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
@@unique([kind, value], map: "uk_imap_filter_kind_value")
@@index([kind, enabled], map: "idx_imap_filter_kind")
@@map("imap_filter_rule")
}
/// 拉取记录(每封一条;最多保留 1000,超则删最旧)
model ImapPullLog {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
pulledAt DateTime @default(now()) @map("pulled_at") @db.DateTime(3)
mailboxAccountId BigInt? @map("mailbox_account_id") @db.UnsignedBigInt
mailboxName String @default("") @map("mailbox_name") @db.VarChar(128)
mailboxUsername String @default("") @map("mailbox_username") @db.VarChar(320)
fromAddr String @default("") @map("from_addr") @db.VarChar(320)
subject String @default("") @db.VarChar(512)
mailType String? @map("mail_type") @db.VarChar(32)
mailId BigInt? @map("mail_id") @db.UnsignedBigInt
result String @db.VarChar(16)
reason String? @db.VarChar(64)
@@index([pulledAt], map: "idx_imap_pull_at")
@@map("imap_pull_log")
}