This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-TMP-REPO/db/migrations/0033_mail.sql
T

93 lines
4.4 KiB
SQL

-- 邮件收发(ADR-013):账号(配置 + 密封凭证 + 游标 + 跨进程租约)、入站留痕、逐订阅方投递留痕、出站留痕。
--
-- 腾讯企业邮箱两条接入路(IMAP/SMTP · 企业微信邮件 API)都没有带内容的推送,只能轮询拉:
-- 两个后端连同一个库时,一个信箱同一时刻只许一个进程在拉 —— `polling_until` 与
-- `sys.task.advancing_until` 同款(一条 SQL 判 + 占,带过期时间,进程崩了不锁死)。
-- 入站两把唯一键:重复拉到(时钟偏 / UIDVALIDITY 重锚 / wecom 十分钟重叠窗)一律静默跳过。
-- 入站 / 出站表对账号**不设外键**:账号删了留痕还在。
create table if not exists sys.mail_account (
id text primary key,
name text not null,
kind text not null check (kind in ('imap-smtp', 'wecom')),
address text not null default '',
display_name text not null default '',
enabled boolean not null default true,
is_default boolean not null default false,
poll_interval_sec integer not null default 30 check (poll_interval_sec between 10 and 3600),
channel_id text,
backfill_from timestamptz,
settings jsonb not null default '{}'::jsonb,
secret_cipher text,
cursor jsonb,
last_polled_at timestamptz,
last_poll_error text,
last_inbound_at timestamptz,
polling_until timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create unique index if not exists mail_account_name_key on sys.mail_account (name);
-- 默认发件账号至多一个 —— 用例先把别的置 false,这道索引兜底
create unique index if not exists mail_account_one_default on sys.mail_account ((true)) where is_default;
create index if not exists mail_account_due_idx on sys.mail_account (enabled, last_polled_at);
create table if not exists sys.mail_inbound (
id text primary key,
account_id text not null,
message_id text not null,
provider_ref text not null,
in_reply_to text,
reference_ids jsonb not null default '[]'::jsonb,
from_addr text not null default '',
from_name text not null default '',
to_list jsonb not null default '[]'::jsonb,
cc_list jsonb not null default '[]'::jsonb,
subject text not null default '',
text_body text,
html_body text,
sent_at timestamptz,
received_at timestamptz not null,
attachments jsonb not null default '[]'::jsonb,
raw bytea,
raw_note text not null default '',
size integer not null default 0,
created_at timestamptz not null default now()
);
create unique index if not exists mail_inbound_message_key on sys.mail_inbound (account_id, message_id);
create unique index if not exists mail_inbound_provider_key on sys.mail_inbound (account_id, provider_ref);
create index if not exists mail_inbound_time_idx on sys.mail_inbound (received_at desc);
create table if not exists sys.mail_inbound_delivery (
mail_id text not null references sys.mail_inbound(id) on delete cascade,
subscriber text not null,
state text not null check (state in ('dispatching', 'done', 'failed', 'skipped')),
error text,
started_at timestamptz not null default now(),
finished_at timestamptz,
primary key (mail_id, subscriber)
);
create table if not exists sys.mail_outbound (
id text primary key,
account_id text not null,
message_id text not null,
idempotency_key text,
to_list jsonb not null,
cc_list jsonb not null default '[]'::jsonb,
bcc_list jsonb not null default '[]'::jsonb,
subject text not null,
text_body text,
html_body text,
in_reply_to text,
reference_ids jsonb not null default '[]'::jsonb,
attachments jsonb not null default '[]'::jsonb,
state text not null check (state in ('sending', 'sent', 'failed')),
error text,
provider_ref text,
requested_by jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now(),
sent_at timestamptz
);
create unique index if not exists mail_outbound_idem_key on sys.mail_outbound (idempotency_key) where idempotency_key is not null;
create index if not exists mail_outbound_time_idx on sys.mail_outbound (created_at desc);