Archived
迁移 0033:邮箱账号 / 入站 / 投递 / 出站四张表;mail store 与跨进程租约(两连接对抢 20 轮每轮一个赢)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
-- 邮件收发(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);
|
||||
Reference in New Issue
Block a user