← 目录
otter-coordinator · 设计文档 · 定稿 v1 · 2026-07-27

Coordinator vNext

达人是唯一主档(Engagement),一次合作是达人 × 活动方向(Collaboration), 交付物逐条追踪(泳道)。功能分两半:CRM 聊天(零 LLM 依赖的地板)与 Studio 制片(L0→L3 渐进自动)。 本文是唯一权威设计;迁移见姊妹篇《迁移计划》

01
词表

九个词,全部语义在此冻结

定义关键规则
Engagement
达人主档
一个达人在系统里的唯一记录议价成功、运营 pick 后建档;也可手动建/存量迁入。名下挂社媒组、联系方式组、Collaboration 组
社媒组
social profiles
该达人的全部社媒账号支持采集(tt/yt/ig/x)带指标(粉丝、近 12 条中位播放),可出建议报价;不支持的(snapchat 等)只记 handle+平台,供分发
联系方式组
contact methods
达人本人 + 所属经纪的手机/邮箱每条标 personal / agency;经纪方式可同时指派给旗下多个达人,不作身份合并键
Collaboration
一次合作
达人 × Campaign 的一轮合作复购(同 Campaign)、复用(跨 Campaign)都开新条,可并行;建档即快照(权益/整价/要求/限制),支持达人级字段 override
Deliverable
泳道
一条交付物,Studio 追踪的最小单位阶段 = 全局超集 × Campaign 模板;挂具体社媒账号;全程消息引用可跳原文;创建 Collaboration 时至少播种一条
Campaign
活动方向
合作的稳定归属容器边界 = 提报总表/进度总表(8、9 月同表即同一个);有起止但时间不是边界;变更走「发布 → 订阅方更新」
Scouting
招募批次
Java 侧 campaign 的新名分只读镜像,允许继续随意开;只用于溯源「这个人是哪一批找来的」,不参与业务判断
CRM运营与达人的聊天工具threads 按 Campaign 分组;完全不依赖 LLM 也可用(翻译/润色类轻功能允许)
Studio
制片
智能与做片推进管理泳道组界面 + 快捷操作;自动化 L0→L3 全局爬梯;高危/高难度由 LLM 判定转人工

库表命名:界面词 Engagement,数据库表暂名 coordinator_creator_relationships (旧 coordinator_engagements 迁移期仍存活,同库同词双义是事故源;旧表退役后再改名)。

02
实体模型

结构与落库

Engagement(达人主档)
│
├─ 社媒组 ────────── 复用平台已有 creator_profiles(445 万行,含粉丝/中位播放,勿新建第三套)
├─ 联系方式组 ────── 新建 contact_methods + 指派表(personal / agency)
└─ Collaboration[](并行)
     ├─ 快照 + override(权益 · 整价 · 要求 · 限制)
     ├─ Deliverable[](泳道,挂具体社媒账号)
     │    └─ 阶段事件(带消息引用)· artifacts · 分发 · 付款
     └─ 对价与实付(账本)

Campaign ←(发布变更)── 订阅 ──→ 各 Collaboration
Scouting(旧 campaigns 表,只读)──→ collaboration.scouting_batch_id 溯源

身份层:复用,不新建

平台已有 contacts(247 万,含 kind = person | agency 与合并机制)、 creator_profiles(445 万,指标齐全)、contact_relationships (15 万条经纪↔达人)。vNext 不再新建身份体系,只做接线 —— 这是全部工程量估算「20% 新模型、80% 接线」的来源。

-- 达人主档(界面词 Engagement)
create table coordinator_creator_relationships (
  id                 uuid primary key default gen_random_uuid(),
  creator_contact_id uuid not null references contacts(id),
  status             text not null default 'active'
                       check (status in ('active','paused','blocked','closed')),
  env                text not null default 'live' check (env in ('live','sim')),
  created_at         timestamptz not null default now(),
  unique (creator_contact_id, env),
  unique (id, creator_contact_id)
);

-- 联系方式:方式归其真正主人(经纪邮箱归经纪 contact)
create table contact_methods (
  id uuid primary key default gen_random_uuid(),
  contact_id uuid not null references contacts(id),
  method_type text not null check (method_type in
    ('email','phone','whatsapp','wechat','telegram','other')),
  raw_value text not null, normalized_value text not null, label text,
  verification_status text not null default 'unverified',
  mergeable boolean not null default true,     -- agency/共享信箱 false:不作身份合并键
  valid_during tstzrange not null default tstzrange(now(),null,'[)'),
  unique (contact_id, method_type, normalized_value),
  unique (id, contact_id)
);

-- 指派:这个方式「用于」哪个达人;personal / agency 标在这里
create table contact_method_assignments (
  id uuid primary key default gen_random_uuid(),
  creator_contact_id uuid not null references contacts(id),
  contact_method_id uuid not null, method_owner_contact_id uuid not null,
  assignment_type text not null check (assignment_type in ('personal','agency')),
  source_relationship_id uuid references contact_relationships(id),
  valid_during tstzrange not null default tstzrange(now(),null,'[)'),
  is_primary boolean not null default false,
  foreign key (contact_method_id, method_owner_contact_id)
    references contact_methods(id, contact_id),
  check ((assignment_type='personal' and method_owner_contact_id = creator_contact_id
            and source_relationship_id is null)
      or (assignment_type='agency' and method_owner_contact_id <> creator_contact_id
            and source_relationship_id is not null))
);

Campaign 与 Collaboration

-- Campaign:提报总表边界;运营本地建,不碰 Java
create table coordinator_campaigns (
  id uuid primary key default gen_random_uuid(),
  label text not null, client_name text not null, product_line text not null,
  starts_at date, ends_at date,               -- 生命周期存在,但不定义边界
  stage_template jsonb not null,              -- 从全局阶段超集中启用哪些(§04)
  pricing_rules  jsonb,                       -- 指标 → 建议起价(决定 11)
  benefits jsonb, requirements jsonb, restrictions jsonb,   -- 统一管理的三类内容
  created_by text not null, created_at timestamptz not null default now(),
  unique (client_name, product_line, label)
);

-- Campaign 变更:保存 ≠ 生效,必须「发布」;订阅方逐条收敛
create table coordinator_campaign_releases (
  id uuid primary key default gen_random_uuid(),
  campaign_id uuid not null references coordinator_campaigns(id),
  note text not null, diff jsonb not null,
  published_by text not null, published_at timestamptz not null default now()
);
create table coordinator_collaboration_release_states (
  collaboration_id uuid not null, release_id uuid not null references coordinator_campaign_releases(id),
  status text not null check (status in ('applied','pending_review','rejected')),
  resolved_by text, resolved_at timestamptz,
  primary key (collaboration_id, release_id)
);

-- 一次合作:达人 × Campaign(无多达人打包 — 决定 1)
create table coordinator_collaborations (
  id uuid primary key default gen_random_uuid(),
  engagement_id     uuid not null,            -- → coordinator_creator_relationships
  creator_contact_id uuid not null,
  campaign_id       uuid references coordinator_campaigns(id),   -- 可空:临时合作/待归类
  scouting_batch_id int  references campaigns(id),               -- 溯源,只读
  lifecycle_status  text not null check (lifecycle_status in
                      ('draft','delivering','closed','dropped')),
  env               text not null default 'live' check (env in ('live','sim')),
  snapshot          jsonb not null,           -- 建档快照:权益/整价/要求/限制
  overrides         jsonb not null default '{}',  -- 达人级字段覆盖(如特殊发布要求)
  renewal_of_collaboration_id uuid references coordinator_collaborations(id),
  agreed_at timestamptz, closed_at timestamptz,
  legacy_engagement_id uuid unique,           -- 存量迁入回指(NOT VALID FK)
  created_at timestamptz not null default now(),
  foreign key (engagement_id, creator_contact_id)
    references coordinator_creator_relationships(id, creator_contact_id),
  unique (id, engagement_id)
);

Deliverable 与追踪

-- 泳道:挂具体社媒账号;创建 Collaboration 时至少播种一条(provisional)
create table coordinator_deliverables_v2 (
  id uuid primary key default gen_random_uuid(),
  collaboration_id uuid not null, engagement_id uuid not null,
  creator_contact_id uuid not null,
  creator_profile_id uuid,                    -- 发布账号;签约后再定允许 null
  sequence int not null check (sequence > 0),
  deliverable_type text not null,             -- 含 'provisional' 占位型
  current_stage text not null,                -- §04 超集之一
  needs_script boolean not null default true,
  due_at timestamptz,
  foreign key (collaboration_id, engagement_id)
    references coordinator_collaborations(id, engagement_id),
  foreign key (creator_profile_id, creator_contact_id)
    references creator_profiles(id, creator_id),   -- 前置:unique(id, creator_id)
  unique (collaboration_id, sequence),
  unique (id, collaboration_id)
);

-- 阶段事件:每次推进一条,消息引用在此(hover/点击跳原文)
create table coordinator_stage_events (
  id uuid primary key default gen_random_uuid(),
  deliverable_id uuid not null references coordinator_deliverables_v2(id),
  stage text not null, action text not null,   -- entered | revised | approved | blocked …
  waiting_on text check (waiting_on in ('us','kol','brand')),  -- 等我们/等达人/等品牌
  note text,
  source_message_ids uuid[] not null default '{}',   -- 引用的聊天消息
  actor text not null,                        -- operator: | studio:L1 …
  created_at timestamptz not null default now()
);

-- artifacts:版本化,只存引用(脚本 v1/v2、样片、发布链接、物流单号)
create table coordinator_deliverable_artifacts (
  id uuid primary key default gen_random_uuid(),
  deliverable_id uuid not null references coordinator_deliverables_v2(id),
  kind text not null check (kind in
    ('product_pick','tracking_no','script','cut','publish_url','distribute_url','other')),
  version int not null default 1, uri text, content text, review_state text,
  source_message_id uuid,
  created_at timestamptz not null default now(),
  unique (deliverable_id, kind, version)
);

-- 对价(含谈判中报价)与实付(append-only,幂等)
create table coordinator_collaboration_considerations (
  id uuid primary key default gen_random_uuid(),
  collaboration_id uuid not null references coordinator_collaborations(id),
  consideration_type text not null check (consideration_type in
    ('cash_package','cash_fee','barter','gift','commission','other')),
  side text not null default 'agreed' check (side in ('asked','offered','agreed')),
  amount numeric(19,4), currency char(3), description text,
  observed_at timestamptz not null default now(), source_message_id uuid,
  unique (id, collaboration_id),
  check ((amount is null) = (currency is null)
     and (amount is null or (amount >= 0 and currency::text ~ '^[A-Z]{3}$')))
);
create unique index one_agreed_package on coordinator_collaboration_considerations(collaboration_id)
  where consideration_type='cash_package' and side='agreed';

create table coordinator_collaboration_payments (
  id uuid primary key default gen_random_uuid(),
  collaboration_id uuid not null references coordinator_collaborations(id),
  deliverable_id uuid, consideration_id uuid,
  payee_contact_id uuid not null references contacts(id),   -- 可为经纪
  amount numeric(19,4) not null check (amount >= 0),
  currency char(3) not null check (currency::text ~ '^[A-Z]{3}$'),
  direction text not null default 'outbound'
    check (direction in ('outbound','refund','chargeback')),
  source_kind text not null,   -- chat_capture_confirmed | operator | payment_system(决定 9)
  source_ref  text not null,   -- 消息 id / 回执号
  paid_at timestamptz not null,
  unique (source_kind, source_ref, direction),   -- 幂等,回调重试不重复入账
  foreign key (deliverable_id, collaboration_id)
    references coordinator_deliverables_v2(id, collaboration_id),
  foreign key (consideration_id, collaboration_id)
    references coordinator_collaboration_considerations(id, collaboration_id)
);

CRM 侧:thread 与绑定

-- thread 归属 = 可见性授予;可多条并存(决定 4/5)
create table coordinator_thread_bindings (
  id uuid primary key default gen_random_uuid(),
  thread_key text not null,                   -- channel:identity:conversation
  collaboration_id uuid not null references coordinator_collaborations(id),
  granted_from timestamptz not null default now(),  -- 自绑定时刻起可见,不回溯
  bound_by text not null,                     -- auto:new_email_thread | operator:
  released_at timestamptz,
  unique (thread_key, collaboration_id)
);
03
功能规格 · 一

CRM:聊天,系统的地板

是什么
  • threads 矩阵:联系方式组 × 我们的 sender 邮箱/账号
  • threads 按 Campaign 分组,运营只看自己活动的一组即可跟进
  • 通常各活动用不同 sender 建联 —— 达人视角是「对接了两个人」;但 sender 不与 Campaign 绑定,归组只靠会话绑定
  • 消息去重、投递回执、已读状态、内部备注、@提及
  • 入库规范化:剥追踪链接/UI 状态,群发-自动回复分类(纯规则,非 LLM)
最小可用形态:运营只用 CRM 聊天,就能把片做完
零 LLM 依赖(决定 12)
  • 收发、分组、搜索、绑定 —— 没有任何一步需要模型
  • 翻译/润色是可选增强:LLM 全挂时按钮变灰,聊天照常
  • Studio 读 CRM,CRM 永不反向依赖 Studio
  • L3 全自动出问题时,逐级降回 —— CRM 是所有等级共同的回退终点
判定标准:拔掉所有 LLM key,CRM 功能 100% 可用

会话归组规则(按渠道)

渠道归组方式多活动同人
邮件新活动开新 thread,建 thread 即绑定,零人工天然分开(两个 thread)
WhatsApp / XDM未归属会话进「待绑定」,运营一键绑定到 Collaboration,自绑定时刻起生效一个会话可绑多条,两边都显示
经纪信箱邮件:请其按活动开多 thread;WA:同上绑定同上;经纪方式 mergeable=false,永不触发身份合并
04
功能规格 · 二

Studio:制片

界面:Collaboration 组 × 泳道

阶段超集(全局唯一,Campaign 模板启停)

product_pick 选品 → sampling 寄样 → logistics 物流签收
  → script 脚本* → script_rev 脚本修改*(多轮,每轮一条 stage_event)
  → cut 样片 → cut_rev 样片修改(多轮)
  → publish 发布 → distribute 分发(不支持采集的平台在此登记)
  → payment 付款

* needs_script=false 的泳道自动跳过;纯 gifting 模板可停用 payment;
  软件类模板可停用 sampling/logistics。模板配置在 Campaign 上,泳道不允许自定义。

报价建议(决定 11)

Campaign 上配 pricing_rules(平台 × 粉丝/中位播放区间 → 金额)。 泳道挂哪个社媒账号,就按那个账号的指标算 —— 同一个人 YT 与 IG 各算各的。 结果只作为建议起价展示;是否直接采用由运营(或 L2+ 的自动化)决定。

自动化爬梯(全局设置 — 决定 10)

L0纯人工。运营用 CRM 聊天 + Studio 手动看板推进,所有状态人手写
L1只读理解。每条新消息读一次:刷新泳道阶段建议、记录 issues、采集付款/发布信号 —— 全部落为「待确认」,不直接改状态、不发一个字
L2起草待审。阶段变化自动生成催办草稿(附原始意见);回复消息进审核队列,运营点发
L3全自动推进。常规消息直发、状态直改;高危/高难度由 LLM 判定后转人工 —— 运营只处理异常与高危
05
关键流程

四条主线

① 建档(决定 6)

谈判上游(现系统,暂不动)──议价成功──▶ 运营 pick
   │                                        也可:手动建档 / 存量 delivery 迁入
   ▼
若首次合作:建 Engagement(接线 contacts + creator_profiles + 联系方式组)
建 Collaboration:campaign_id + 快照(整价来自谈判结果,权益/要求/限制来自 Campaign)
按 Campaign 模板播种泳道(至少一条;数量按谈定的交付物拆)
   ▼
Studio 制片开始

② Campaign 变更(决定 7)

编辑 Campaign(如:产品发货延误两周)→ 保存 ≠ 生效
   ▼
发布 release(note + diff)
   ▼
订阅的每条 Collaboration 生成 release_state:
   ├─ 与达人 override 无冲突 → applied:快照更新、泳道排期顺延、时间线留痕
   └─ 有冲突 → pending_review:待运营逐条确认

③ 会话绑定(决定 4/5)

邮件:开新 thread ─▶ 生来绑定(零人工)
WhatsApp:未归属会话 ─▶ 运营绑定 ─▶ granted_from 起对该合作可见
         同一会话双活动 ─▶ 两条 binding,两边都显示

④ 付款(决定 9)

L1+ 从聊天里采集付款信号(金额/币种/凭证)─▶ 生成待确认付款记录(带消息引用)
运营确认 ─▶ 落 payments(source_kind='chat_capture_confirmed')
将来接支付系统 ─▶ source_kind='payment_system',按实付自动对账「谈成 vs 实付」
06
边界

非目标与留白(同样是决定)


定稿依据:12 项决定(2026-07-27 逐条拍板)。词表见 §01,冲突处以本文为准。
迁移路径:《迁移计划》 · 周会版:presentation