mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-25 20:15:37 +02:00
refactor(channel): defer lark_* table drop to a follow-up migration
Preflight deploy review: dropping lark_* in the same release that cuts over (old migration 125) is not rollback/rolling-safe — the v0.3.27 release still reads lark_*, so a rolling deploy or a post-deploy code rollback would hit "relation does not exist". Remove the drop and keep the old tables for one release (standard expand/contract): migration 124 already backfilled lark_* -> channel_*, the new code reads/writes only channel_*, and the physical drop moves to a separate cleanup migration once this ships and is observed. The lark_* tables remain in the schema, so sqlc regenerates the (now unused) db.Lark* models; queries/lark.sql stays deleted (the new code uses channel_*). No code path reads lark_* — only the destructive drop is deferred, keeping the design's no-compat-layer / no-dual-write rule while being deploy-safe. MUL-3515 Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -1,194 +0,0 @@
|
||||
-- Recreate the lark_* tables dropped by 125_drop_lark_tables.up.sql
|
||||
-- (structure only — the data lives in channel_* after migration 124). This is
|
||||
-- the authoritative pre-drop schema: lark_installation plus the deltas from
|
||||
-- migrations 112 (bot_union_id), 113 (per-installation dedup), 116 (region),
|
||||
-- and 122 (thread-reply columns). Foreign keys are added after all tables
|
||||
-- exist, so table order does not matter.
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_binding_token (
|
||||
token_hash text NOT NULL,
|
||||
workspace_id uuid NOT NULL,
|
||||
installation_id uuid NOT NULL,
|
||||
lark_open_id text NOT NULL,
|
||||
expires_at timestamp with time zone NOT NULL,
|
||||
consumed_at timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT lark_binding_token_ttl_cap CHECK ((expires_at <= (created_at + '00:15:00'::interval)))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_chat_session_binding (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
chat_session_id uuid NOT NULL,
|
||||
installation_id uuid NOT NULL,
|
||||
lark_chat_id text NOT NULL,
|
||||
lark_chat_type text NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
last_lark_message_id text,
|
||||
last_lark_thread_id text,
|
||||
CONSTRAINT lark_chat_session_binding_lark_chat_type_check CHECK ((lark_chat_type = ANY (ARRAY['p2p'::text, 'group'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_inbound_audit (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
installation_id uuid,
|
||||
lark_chat_id text,
|
||||
event_type text NOT NULL,
|
||||
lark_event_id text,
|
||||
lark_message_id text,
|
||||
drop_reason text NOT NULL,
|
||||
received_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_inbound_message_dedup (
|
||||
installation_id uuid NOT NULL,
|
||||
message_id text NOT NULL,
|
||||
received_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
processed_at timestamp with time zone,
|
||||
claim_token uuid DEFAULT gen_random_uuid() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_installation (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
workspace_id uuid NOT NULL,
|
||||
agent_id uuid NOT NULL,
|
||||
app_id text NOT NULL,
|
||||
app_secret_encrypted bytea NOT NULL,
|
||||
tenant_key text,
|
||||
bot_open_id text NOT NULL,
|
||||
installer_user_id uuid NOT NULL,
|
||||
status text DEFAULT 'active'::text NOT NULL,
|
||||
ws_lease_token text,
|
||||
ws_lease_expires_at timestamp with time zone,
|
||||
installed_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
bot_union_id text,
|
||||
region text DEFAULT 'feishu'::text NOT NULL,
|
||||
CONSTRAINT lark_installation_region_check CHECK ((region = ANY (ARRAY['feishu'::text, 'lark'::text]))),
|
||||
CONSTRAINT lark_installation_status_check CHECK ((status = ANY (ARRAY['active'::text, 'revoked'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_outbound_card_message (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
chat_session_id uuid NOT NULL,
|
||||
task_id uuid,
|
||||
lark_chat_id text NOT NULL,
|
||||
lark_card_message_id text NOT NULL,
|
||||
status text DEFAULT 'pending'::text NOT NULL,
|
||||
last_patched_at timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT lark_outbound_card_message_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'streaming'::text, 'final'::text, 'error'::text])))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lark_user_binding (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
workspace_id uuid NOT NULL,
|
||||
multica_user_id uuid NOT NULL,
|
||||
installation_id uuid NOT NULL,
|
||||
lark_open_id text NOT NULL,
|
||||
union_id text,
|
||||
bound_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY lark_binding_token
|
||||
ADD CONSTRAINT lark_binding_token_pkey PRIMARY KEY (token_hash);
|
||||
|
||||
ALTER TABLE ONLY lark_chat_session_binding
|
||||
ADD CONSTRAINT lark_chat_session_binding_chat_session_id_key UNIQUE (chat_session_id);
|
||||
|
||||
ALTER TABLE ONLY lark_chat_session_binding
|
||||
ADD CONSTRAINT lark_chat_session_binding_installation_id_lark_chat_id_key UNIQUE (installation_id, lark_chat_id);
|
||||
|
||||
ALTER TABLE ONLY lark_chat_session_binding
|
||||
ADD CONSTRAINT lark_chat_session_binding_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY lark_inbound_audit
|
||||
ADD CONSTRAINT lark_inbound_audit_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY lark_inbound_message_dedup
|
||||
ADD CONSTRAINT lark_inbound_message_dedup_pkey PRIMARY KEY (installation_id, message_id);
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_app_id_key UNIQUE (app_id);
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_id_workspace_id_key UNIQUE (id, workspace_id);
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_workspace_id_agent_id_key UNIQUE (workspace_id, agent_id);
|
||||
|
||||
ALTER TABLE ONLY lark_outbound_card_message
|
||||
ADD CONSTRAINT lark_outbound_card_message_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY lark_user_binding
|
||||
ADD CONSTRAINT lark_user_binding_installation_id_lark_open_id_key UNIQUE (installation_id, lark_open_id);
|
||||
|
||||
ALTER TABLE ONLY lark_user_binding
|
||||
ADD CONSTRAINT lark_user_binding_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_binding_token_installation ON lark_binding_token USING btree (installation_id, expires_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_chat_session_binding_session ON lark_chat_session_binding USING btree (chat_session_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_inbound_audit_installation ON lark_inbound_audit USING btree (installation_id, received_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_inbound_audit_reason ON lark_inbound_audit USING btree (drop_reason, received_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_inbound_dedup_received ON lark_inbound_message_dedup USING btree (received_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_installation_agent ON lark_installation USING btree (agent_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_installation_lease ON lark_installation USING btree (ws_lease_expires_at) WHERE (status = 'active'::text);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_installation_workspace ON lark_installation USING btree (workspace_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_outbound_card_session ON lark_outbound_card_message USING btree (chat_session_id, created_at DESC);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_lark_outbound_card_task ON lark_outbound_card_message USING btree (task_id) WHERE (task_id IS NOT NULL);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_user_binding_user ON lark_user_binding USING btree (multica_user_id, workspace_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lark_user_binding_workspace_open ON lark_user_binding USING btree (workspace_id, lark_open_id);
|
||||
|
||||
ALTER TABLE ONLY lark_binding_token
|
||||
ADD CONSTRAINT lark_binding_token_installation_id_fkey FOREIGN KEY (installation_id) REFERENCES lark_installation(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_binding_token
|
||||
ADD CONSTRAINT lark_binding_token_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspace(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_chat_session_binding
|
||||
ADD CONSTRAINT lark_chat_session_binding_chat_session_id_fkey FOREIGN KEY (chat_session_id) REFERENCES chat_session(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_chat_session_binding
|
||||
ADD CONSTRAINT lark_chat_session_binding_installation_id_fkey FOREIGN KEY (installation_id) REFERENCES lark_installation(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_inbound_audit
|
||||
ADD CONSTRAINT lark_inbound_audit_installation_id_fkey FOREIGN KEY (installation_id) REFERENCES lark_installation(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY lark_inbound_message_dedup
|
||||
ADD CONSTRAINT lark_inbound_message_dedup_installation_id_fkey FOREIGN KEY (installation_id) REFERENCES lark_installation(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agent(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_installer_user_id_fkey FOREIGN KEY (installer_user_id) REFERENCES "user"(id) ON DELETE RESTRICT;
|
||||
|
||||
ALTER TABLE ONLY lark_installation
|
||||
ADD CONSTRAINT lark_installation_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspace(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_outbound_card_message
|
||||
ADD CONSTRAINT lark_outbound_card_message_chat_session_id_fkey FOREIGN KEY (chat_session_id) REFERENCES chat_session(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_outbound_card_message
|
||||
ADD CONSTRAINT lark_outbound_card_message_task_id_fkey FOREIGN KEY (task_id) REFERENCES agent_task_queue(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY lark_user_binding
|
||||
ADD CONSTRAINT lark_user_binding_installation_fk FOREIGN KEY (installation_id, workspace_id) REFERENCES lark_installation(id, workspace_id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lark_user_binding
|
||||
ADD CONSTRAINT lark_user_binding_member_fk FOREIGN KEY (workspace_id, multica_user_id) REFERENCES member(workspace_id, user_id) ON DELETE CASCADE;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- Drop the lark_* tables. MUL-3515 generalized them into channel_* in
|
||||
-- migration 124 and the Go cutover has landed (the lark package now reads and
|
||||
-- writes only channel_*), so the old tables are dead. Per the design (§5:
|
||||
-- replace, do not keep both) the old path is removed rather than left in place.
|
||||
--
|
||||
-- Dropped in dependency order — every other lark_* table has a foreign key to
|
||||
-- lark_installation, so it goes last. The down migration recreates the schema
|
||||
-- (structure only; the data already lives in channel_*).
|
||||
DROP TABLE IF EXISTS lark_binding_token;
|
||||
DROP TABLE IF EXISTS lark_outbound_card_message;
|
||||
DROP TABLE IF EXISTS lark_inbound_audit;
|
||||
DROP TABLE IF EXISTS lark_inbound_message_dedup;
|
||||
DROP TABLE IF EXISTS lark_user_binding;
|
||||
DROP TABLE IF EXISTS lark_chat_session_binding;
|
||||
DROP TABLE IF EXISTS lark_installation;
|
||||
@@ -523,6 +523,86 @@ type IssueToLabel struct {
|
||||
LabelID pgtype.UUID `json:"label_id"`
|
||||
}
|
||||
|
||||
type LarkBindingToken struct {
|
||||
TokenHash string `json:"token_hash"`
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
InstallationID pgtype.UUID `json:"installation_id"`
|
||||
LarkOpenID string `json:"lark_open_id"`
|
||||
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
|
||||
ConsumedAt pgtype.Timestamptz `json:"consumed_at"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type LarkChatSessionBinding struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ChatSessionID pgtype.UUID `json:"chat_session_id"`
|
||||
InstallationID pgtype.UUID `json:"installation_id"`
|
||||
LarkChatID string `json:"lark_chat_id"`
|
||||
LarkChatType string `json:"lark_chat_type"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
LastLarkMessageID pgtype.Text `json:"last_lark_message_id"`
|
||||
LastLarkThreadID pgtype.Text `json:"last_lark_thread_id"`
|
||||
}
|
||||
|
||||
type LarkInboundAudit struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
InstallationID pgtype.UUID `json:"installation_id"`
|
||||
LarkChatID pgtype.Text `json:"lark_chat_id"`
|
||||
EventType string `json:"event_type"`
|
||||
LarkEventID pgtype.Text `json:"lark_event_id"`
|
||||
LarkMessageID pgtype.Text `json:"lark_message_id"`
|
||||
DropReason string `json:"drop_reason"`
|
||||
ReceivedAt pgtype.Timestamptz `json:"received_at"`
|
||||
}
|
||||
|
||||
type LarkInboundMessageDedup struct {
|
||||
InstallationID pgtype.UUID `json:"installation_id"`
|
||||
MessageID string `json:"message_id"`
|
||||
ReceivedAt pgtype.Timestamptz `json:"received_at"`
|
||||
ProcessedAt pgtype.Timestamptz `json:"processed_at"`
|
||||
ClaimToken pgtype.UUID `json:"claim_token"`
|
||||
}
|
||||
|
||||
type LarkInstallation struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
AgentID pgtype.UUID `json:"agent_id"`
|
||||
AppID string `json:"app_id"`
|
||||
AppSecretEncrypted []byte `json:"app_secret_encrypted"`
|
||||
TenantKey pgtype.Text `json:"tenant_key"`
|
||||
BotOpenID string `json:"bot_open_id"`
|
||||
InstallerUserID pgtype.UUID `json:"installer_user_id"`
|
||||
Status string `json:"status"`
|
||||
WsLeaseToken pgtype.Text `json:"ws_lease_token"`
|
||||
WsLeaseExpiresAt pgtype.Timestamptz `json:"ws_lease_expires_at"`
|
||||
InstalledAt pgtype.Timestamptz `json:"installed_at"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
BotUnionID pgtype.Text `json:"bot_union_id"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type LarkOutboundCardMessage struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ChatSessionID pgtype.UUID `json:"chat_session_id"`
|
||||
TaskID pgtype.UUID `json:"task_id"`
|
||||
LarkChatID string `json:"lark_chat_id"`
|
||||
LarkCardMessageID string `json:"lark_card_message_id"`
|
||||
Status string `json:"status"`
|
||||
LastPatchedAt pgtype.Timestamptz `json:"last_patched_at"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type LarkUserBinding struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
MulticaUserID pgtype.UUID `json:"multica_user_id"`
|
||||
InstallationID pgtype.UUID `json:"installation_id"`
|
||||
LarkOpenID string `json:"lark_open_id"`
|
||||
UnionID pgtype.Text `json:"union_id"`
|
||||
BoundAt pgtype.Timestamptz `json:"bound_at"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
|
||||
Reference in New Issue
Block a user