改过的 schema

SalesRoomRe · 2026-08-03 · 一对多不压进列:user_channels + customer_owners + deliveries

你抓到的是同一个错,我犯了两次

把「一对多」压成主表的一列。

我写的为什么错
users.lark_open_id 一个人可以同时有 Lark 和 Teams —— 本来就是一对多
压进列的后果:每加一家 IM 就要 ALTER TABLE 一次,这才是真耦合
customers.sales_owner_id 一个客户可以两个销售一起跟,或者销售 + 产品各一个 —— 也是一对多
而且这是倒退:现有 profile.md 已经支持 sales_owner/pm_owner/fde_owner 三种角色、每种逗号分隔多人

而且 lark_open_id 直接打了我自己的脸 —— 上一轮刚说完 「接口边界不能漏任何 Lark 的东西」,转手就在核心表上加了一个叫 lark 的列。

我上一版为什么会取消 user_channels —— 那个论证是错的

我当时说:「多一张表就多一处会漂移的地方」。听起来像原则,其实是把话说错了:

真正的原则我说成了
同一个事实不要存两处不要多建表

user_channels 存的是主表没有的事实(这个人在 Lark 里的 id), 不是副本 —— 它不制造漂移。
我把「避免副本」错误地泛化成了「避免建表」,然后为此放弃了正确的结构。

改过的 schema

一人多渠道

一人跟多个客户

一个客户多个人

发给谁

RUNS_USERS

string

id

PK

= control.db UUID

int

cp_user

1=本组织成员

int

notify

收不收 · 单值 · 留主表

string

display_names

别名

USER_CHANNELS

string

id

PK

string

user_id

FK

string

channel

lark teams email

string

external_id

open_id 或 AAD id

string

bound_at

确认送达才算绑定

string

status

active broken

CUSTOMER_OWNERS

string

id

PK

string

customer_id

FK

string

user_id

FK

string

role

sales pm fde

CUSTOMERS

DELIVERIES

string

id

PK

string

user_id

FK

string

channel

实际走了哪条

string

event_type

prep summary alert

string

ref_id

meeting_id 等

string

status

sent failed

string

error

失败原因

三张表,各自解决什么

解决加 Teams 时要改什么
user_channels 「这个人在各家 IM 里分别是谁」 什么都不用改 —— 多插一行 channel='teams'
customer_owners 「这家客户谁在跟」
可多人 · 带角色
无关
deliveries 「这条消息到底送到没有」 无关 —— channel 本来就是一个值

notify 仍留在 users:「这个人在这个组织收不收消息」是单值,不是一对多。 将来若要「Lark 收、邮件不收」,那是 user_channels 上加一列的事。

你问的两个场景,现在都成立

场景怎么表达
两个销售一起跟一家客户 customer_owners 两行,role='sales'
推送时两个人都在收件人里
一个产品 + 一个销售 两行,role 分别是 pm / sales
角色不同,但都该知道
一个人同时用 Lark 和 Teams user_channels 两行。投递层按 status='active'
某人 Lark 绑定坏了 那一行 status='broken',其他人照发;deliveries 里留一条 failed

一个顺带解决的老问题

customer_owners 建起来之后,「归属」从 profile.md 的一行文本 变成了带 FK 的行。这正是 7 月客户那边做过的事:

7 月 · 客户现在 · 人
用名字引用 → アトレ / アトレ_GBaseSupport 漂移 → pipeline fail-close 用名字引用 → Alex (彦序) / Alex(彦序)20 人收不到消息
migration 0039 改用 customers.id这次改用 users.id

同一个病,同一个药。客户那边已经吃过了,人这边补上。

Generated as a visual answer · open in browser