← 目录
otter-coordinator · Campaign 统一 · 线上数据核实 · 2026-07-25

你的判断成立。
而且比你说的更严重。

我去线上把 455 条 campaign 全拉出来看了。你说的四种滥用——按月、找不到人、换平台、换国家—— 全都能在真实数据里指出来。但真正的问题不是滥用本身,而是: Java 那边从上到下三层,没有任何一层是稳定的。

455campaign 记录
87campaign group
112product
(= scheduler project)
~49真正的品牌
64单个 group 最多
重开次数
01
线上实况

四种滥用,在真实 label 里一眼可见

455 条 campaign 里,402 条(88%)不是第一次尝试,平均 attempts = 14.2。 下面是 capcut-yt-2605-53 这一个 group 的真实记录,按 attempts 排序:

group_slug = capcut-yt-2605-53 · 10 条 campaign

attempts 1 → 12
#1CapCut Web Promotion May YT
#2CapCut Web Promotion May-copy找不到人
#3Capcut PC Promotion 2606换平台
#4CapCut Web Promotion June YT换月份
#5CapCut PC Promotion 2606 YT Shrots换平台
#6CapCut Web Promotion June YT Top Search Result换找人策略
#9CapCut PC Promotion 2606 June YT Shorts no limit放宽条件
#10CapCut YouTube 2026 July Promotion-3D换月份换角度
#11Capcut Instagram 2026 July Promotion换平台
#12CapCut YouTube 2026 July Promotion换平台

换国家那一类在 MiniMax 上最典型——同一个活动,一个国家一条记录:

group_slug = minimax-11 · 25 条 campaign

attempts 25 → 49
#26MiniMax AI Spanish YouTube Apr Promotional Event换语种
#27MiniMax AI Portuguese YouTube Apr Promotional Event换语种
#28MiniMax AI ME YouTube Apr Promotional Event换地区
#30MiniMax AI South Korea Instagram Apr Promotional Event换国家换平台
#31MiniMax AI South Korea TikTok Apr Promotional Event换平台
#32MiniMax AI South Korea YouTube Apr Promotional Event换平台
#33MiniMax AI Spain Instagram Apr Promotional Event换国家
#34MiniMax AI Spain TikTok Apr Promotional Event换国家换平台

这不是有人乱来。「换一个国家/平台就得开一条新 campaign」是那个系统的建模方式决定的—— 因为 campaign 同时承担了「投放对象定义」和「一次招募尝试」两个职责,而这两件事的变更频率差一个数量级。

02
关键发现

上面本来有两层,但两层都被同一种病侵蚀了

Coordinator 的 schema 其实已经预留了分层: products(= scheduler 的 project)→ campaign_groupscampaigns, 而且 campaigns.attempts 就是重试计数器。设计者预见到了这件事。

问题是上面两层同样在按地区/品类/平台裂开。 以 Temu 为例:

product(= scheduler project)campaign 数裂开的维度
Temu ME April48中东 + 月份
Temu-东南亚24地区
Temu日本IG17国家 + 平台
Temu-中欧13地区
Temu-美国-USTT12国家 + 平台
Temu-科技10品类
Temu-玩具10品类
Temu-IG女装9平台 + 品类
…以及另外 20 多个 Temu 相关 group
合计:一个客户 Temu21632 个 group · 8+ 个 product
campaign(招募尝试) 455 按月/平台/国家/重试 裂开
campaign_group 87 也按地区/品类裂开
product / project 112 与 group 几乎 1:1,同样裂开
真正稳定的东西 ~49 品牌/客户 —— 只存在于运营脑子里
这才是必须在 Coordinator 这边统一的真正理由

不是「上游数据脏、我们清洗一下」。而是——那个稳定实体在上游任何一层都不存在。 product 和 group 看起来像分层,实际上是同一种裂变的另外两个副本。 所以这不是同步策略问题,是一个上游根本没有建模的概念,只能由我们建。

03
这个背景改变了一个数字

19% 跨活动的达人,是复购还是同一个活动重开?

上一版我说「19% 的达人同时在 2+ 活动上,跨活动身份冲突是常态」。 但你这个背景一说,我立刻意识到这个数字可能是假的——如果 5 月找不到人 6 月重开一条, 同一个人被重新触达一次,那不是复购,是重投。所以我把它拆开算了。

同一品牌重复触达
2,134

占多活动达人的 30.7%。同一个客户,不同 campaign 记录,同一个人被再次触达。 这是滥用造成的真实伤害——对达人是骚扰,对我们是重复成本。

真正的跨品牌复购
4,819

69.3%。不同客户、不同商业意图,同一个达人。 这是真资产,复购的故事成立——只是今天没有任何实体把它表达出来。

口径:按 group_slug 归一到品牌前缀后统计(455 campaign → 49 brand)。 以 group_slug 原样统计则是 1,144 / 5,809,结论方向一致。

两个可执行的结论

复购是真的,4,819 人。上一版关于 creator-as-record 的结论不变。
但顺带发现了一个今天没人看得见的问题:2,134 个达人被同一个客户重复触达过。 统一之后,「这个人这个客户已经聊过了」变成一次 SELECT,而今天它需要人去记。

04
方案

统一成什么,以及成本比想象的低

拆开被 campaign 混在一起的两个职责

是什么变更频率谁拥有
Program
合作项目
客户 × 产品 × 商业意图。稳定,是「我们和 Temu 在做的事」 季度级Coordinator 本地,运营维护
Sourcing batch
招募批次
一次找人尝试:某月、某国、某平台、放宽条件重试 周级,可以随便开Java campaign 的只读镜像

关键:不要试图去修 Java 那边。 让他们继续随便开 campaign—— 那本来就是招募批次该有的行为。我们只是不再假装它是「合作项目」。

program(id, label, client, product, intent,     -- 本地,稳定,~49 条
        created_by)                          -- 运营建,不碰 Java

sourcing_batch(id, program_id,                 -- 多对一映射到 program
               external_campaign_id,         -- Java campaign 的镜像
               attempt_no, region, platform, period,
               synced_at)                    -- 只读,永不回写

collaboration.program_id          ← 稳定的归属。复购、金额、看板都按它汇总
collaboration.sourcing_batch_id  ← 来源,只用于追溯「这个人是哪一批找来的」

归类成本:112 条,不是 455 条

好消息

productcampaign_group 几乎是 1:1 (112 product / 87 有 campaign 的 group)。所以归类可以在 product 这一层做,一条 product 带走它下面全部 campaign。 112 行人工归类到 ~49 个 program——半天的活,不是一个项目。

动作工作量谁做
存量:112 个 product → ~49 个 program112 行运营一次性,可用品牌前缀预填后人工确认
增量:新同步进来的 campaign每条 1 次按 product 自动继承;product 是新的才需要人点一下
未归类的兜底必须允许存在。归到 program = null,不阻塞收信,进待归类队列

最后一行很重要:归类不能是入库的前置条件。今天 "Legacy (Scheduler)" 这个占位 product 下面挂着 24 条 campaign, 就是因为上游没给 project 时系统必须编一个出来。新模型里那种情况直接进待归类队列,不用编。

05
你补的那点

默认永远至少一条泳道

你说得对,而且这正好补上了我上一版指出的那个洞: 8,586 个进行中的交付合作里,4,461 个(52%)一条 deliverable 记录都没有。 原因就是交付物要等合同签署后才播种,而在那之前合作是「无形」的。

采纳:建合作时即播种一条

创建 collaboration 时立刻建一条 provisional 交付物 (渠道待定、数量 1)。谈判期它显示谈判状态,条款谈成时再按价格条目细化成 N 条。 合作从第一天起就有形、可推进、可被催办。

量级没问题:51,175 条合作 × 至少 1 条 = 5 万多行,今天是 4,740 行。对 Postgres 不算什么。 真正的收益是看板从第一天起就是完整的,而不是等签约后才突然出现。

@laura_makes · program: Temu 北美 · 3 个批次找来过
交付物 1谈判中 待确认价格 —— 对方 $1,800 / 我方 $1,200 (创建时就有这条泳道) 6 天
交付物 2–3条款谈成后细化 按谈成的价格条目自动展开
⚠ 重复触达统一后可见 此人 5 月已在 Temu-科技 批次被触达过并拒绝 —— 今天这条信息看不到

最后那一行是统一后白送的能力:2,134 个被重复触达的达人, 在按 program 汇总之后自动可见。

06
需要你定的

三个口径问题

  1. Program 的粒度是「客户」还是「客户 × 产品线」? Temu 有 216 条 campaign、8 个 product,涵盖科技/玩具/女装等不同品类。 一个 Temu program,还是 Temu-科技 / Temu-玩具 分开?
    我倾向后者——因为「同一品类是否已触达过」比「同一客户是否已触达过」更有业务意义。但这取决于你们内部是按客户还是按品类结算。
  2. 还要不要继续同步 Java campaign? 我建议保留只读同步作为 sourcing_batch(它是真实的招募事实,有溯源价值), 但 campaign 不再参与任何业务判断——不做路由键、不做范围过滤、不做归属。
  3. 存量 51,175 条 collaboration 怎么补 program_id?campaign → product → program 回填即可,一次 SQL。 前提是第 1 条的粒度定了。

数据取自生产库直查(2026-07-25):campaigns / campaign_groups / products / coordinator_engagements
品牌归一口径为 group_slug 去尾号 + 去地区/平台/月份后缀,455 → 49。
相关:Research/coordinator-vnext/04-vnext-architecture.md