首页
/ Twenty Partners 手动工作流运行手册:为每个 Twenty 工作区重建 Cron「每日摘要」工作流

Twenty Partners 手动工作流运行手册:为每个 Twenty 工作区重建 Cron「每日摘要」工作流

2026-09-07 18:50:41作者:瞿蔚英Wynne

导读:Twenty Partners 应用把 CRM 变成了 Twenty 合作伙伴计划的运营系统(应用概览见 Twenty Partners README),但它有一个极其特殊的基础设施约束——工作流(workflow)是工作区级元数据,无法随应用清单发布。本文即官方运行手册 workflows/README.md 的完整展开:你会理解为什么 SDK 没有 defineWorkflow、为什么每个安装该应用的 workspace 都必须由管理员手工重建 cron 工作流,并得到一份可直接照做的「Daily Digest 每日摘要」五步搭建指南、本地邮件测试方案与逐工作区核对清单。

为什么这需要一份「手动」运行手册

Twenty Partners 应用的核心业务逻辑都以 logic function(逻辑函数) 的形式随应用清单发布:例如 on-opportunity-partner-won 监听数据库事件、apply-to-brief 响应命令菜单调用。但工作流(Workflow)不在这一体系内——它不是可以在清单里声明的实体,而是由运营人员在 Twenty 工作区 UI 中手工绘制的可视化流程(触发器 → 查找记录 → 迭代 → 分支 → 发邮件)。

这一点与 Twenty SDK 的实体定义清单相互印证:翻阅 twenty-sdk 的 define 目录,可以看到 defineApplicationdefineRoledefineFielddefineCommandMenuItemdefineFrontComponentdefineLogicFunction 等生成器,但没有任何 defineWorkflow。整个仓库范围内也检索不到该 API。因此:

  • 工作流不属于应用清单,不会随 deploy / install 一起迁移到目标工作区;
  • 每一个安装了 Twenty Partners 的工作区(本地 bundle、staging、生产环境)都必须执行一次本手册的重建流程;
  • 执行 yarn twenty install 之后,生产工作区在工作流重建完成前是「空跑」状态——自动提醒不会发出。

这正是 src/workflows/README.md(即本手册原文,位于 workflows 目录)存在的原因:把「需要人工完成的搭建步骤」文档化,作为版本化的配置资产随仓库一起维护。

前置条件(Prerequisites)

开始重建前,先确认以下两点:

  1. 应用已安装并同步:工作区已安装 Twenty Partners 应用,并通过 yarn twenty apply 完成同步(安装与部署命令实现见 install.tsdeploy.ts)。
  2. 不再需要 WORKFLOWS 权限标记:曾经的 WORKFLOWS permission flag 是手工开关;现在 apply(提交申请)路径已经作为**命令菜单项(command menu item)**直接随应用清单发布,管理员无需再单独授予该标记。对应的清单实体可以在源码中看到,例如 apply-to-brief.command-menu-item.ts,它定义了当用户在命令菜单中触发「Apply to brief」时应用如何响应。

版本前提:应用声明需要 twenty >= 2.31.0(见 twenty-partners 的 package.json),请确保目标工作区 / CLI 版本满足要求后再执行重建。

赢家分配:这一步不需要工作流

在进入 cron 工作流之前,先澄清一个常见误解:合作伙伴赢家(Winner)的分配不需要任何工作流

它的运转方式是纯「数据变更 + 逻辑函数」的链路:

  1. 管理员直接在关联的 Opportunity(商机)记录上设置 Partner 字段;
  2. 该写入触发数据库更新事件 opportunity.updated
  3. 应用清单中的逻辑函数 on-opportunity-partner-won 被激活。

on-opportunity-partner-won.logic-function.ts 的源码可以看到它的精确行为:处理器先检查 updatedFields 中是否包含 partnerId,只有字段确实发生变化时才继续;随后调用 syncApplicationOutcomes 去级联更新该商机下所有 Application 的状态。

级联规则在 sync-application-outcomes.service.ts 中一目了然:

  • 分配赢家时partnerId 与赢家匹配的 Application → WON;其余竞争者 → DECLINED(视为落败);
  • 撤销分配时:仅将原本为 WON 的 Application 重新打开为 APPLIED
  • 两个豁免状态BACKUP 是介绍前就设定的常备候选名单,因此不随决策及其逆转而变动;DECLINED 是终态,因为管理员也可能手工设置它,自动重开会撤销那次人工操作;
  • 整个过程以**应用身份(app identity)**运行,绕过合作伙伴的 RLS 锁。

结论:赢家分配步骤没有需要构建或重建的工作流。真正需要手工搭建的只有下面这一条 cron 工作流。

第 1 步:Daily Digest(每日摘要)cron 工作流

这条工作流的作用是:每当过去一天出现新 brief 时,向已通过验证(VALIDATED)的合作伙伴发送一次每日提醒邮件——只包含新增数量 + 链接,不含 brief 细节;如果没有新 brief(计数为 0),则不发送邮件。

因为工作流是工作区元数据、不随应用旅行,所以它必须在每个工作区手工构建。以下是 2026-08-25 在本地环境验证过的标准结构:

节点 1:触发器 —— Cron,每日 07:00

  • 新建工作流,选择 Cron 触发器,频率设置为每日 07:00
  • 测试阶段可以暂时使用**手动触发器(manual trigger)**来反复触发验证;但在正式依赖它之前,必须切回 cron 触发器。

节点 2:查找记录 Search new opp —— 找出新 brief

目标对象为 Opportunity,过滤条件组合:

字段 操作符 取值
Creation date(创建日期) 相对时间 PAST_1_DAY(过去一天内)
isListed 等于 true

其中 isListed 是「是否上架到市场」的布尔标记,其字段定义见 opportunity-is-listed.field.ts。从应用源码看,这一标记在整条匹配链路中被反复使用(submit-client-brief 提交时置 true、简介发送后 on-opportunity-intro-sent 将其置回 false),因此「过去一天新上架的 brief」用它做过滤是最准确的信号。

⚠️ 务必把记录上限(record limit)调高——编辑器默认值只有 1,若保持默认,工作流每次都只会看到一条新 brief,计数会严重失真。

节点 3:查找记录 Search Partners —— 找出所有已验证合作伙伴

  • 目标对象:Partner
  • 过滤条件:Validation Stage(验证阶段)为 VALIDATED
  • 同样把 record limit 调高,确保不会漏掉任何一个应收到提醒的合作伙伴。

节点 4:迭代器(Iterator)+ 循环体

对节点 3 返回的每个合作伙伴执行循环,循环体内包含三个子节点:

  1. 查找记录 Search people partner:查找 Person,其关联字段 Partner → Id 等于 {{currentItem.id}}(即当前迭代到的合作伙伴)。原理解释:Twenty 工作流引擎不会自动加载对象间的关联(relations),所以无法直接从 Partner 记录上读出联系邮箱——这条额外的查找就是用来把合作伙伴的联系人邮箱解析出来。
  2. If/Else 分支:仅当上一步的 Person 查找返回了邮箱时才继续;没有邮箱的合作伙伴直接跳过,避免发送失败或产生无效收件人。
  3. 发送邮件(Send Email):发件邮箱使用工作区已连接的邮箱账户(connected mailbox),收件人为上一步解析出的 Person 邮箱;邮件的主题和正文携带来自节点 2 的新 brief 计数以及生产环境市场(marketplace)链接

节点 5:发布并确认状态为 ACTIVE

保存后点击发布,然后在版本列表中确认该工作流版本显示为 ACTIVE,才算真正生效。

生产环境前置要求

在正式工作区中,Daily Digest 依赖两样东西:

  • 一个已连接的邮箱:Settings → Accounts 中完成连接(对应 SMTP 发信与 IMAP 收信);
  • 应用本身已安装(保证 Partner / Opportunity / Person 等对象与字段就绪)。

本地邮件测试方案(严禁在生产环境使用)

本地联调时不要真发邮件。官方推荐的方案是使用 smtp4dev 起一个本地假 SMTP/IMAP 服务器,把邮件「投递」进浏览器界面:

docker run --rm -d --name smtp4dev -p 8090:80 -p 2525:25 -p 1143:143 rnwood/smtp4dev

随后按以下顺序完成配置:

  1. 关闭外发安全模式:把配置变量 OUTBOUND_HTTP_SAFE_MODE_ENABLED 设为 false(位置:Settings → Admin Panel → Config Variables)。原理解释:safe mode 会有意拦截发往内网/私有主机的 IMAP/SMTP 流量,host.docker.internal 这类本地地址正是它的拦截对象。
  2. 连接一个 IMAP/SMTP 账户,参数如下:
参数
Host host.docker.internal
SMTP 端口 2525
IMAP 端口 1143
TLS 关闭
凭据 任意(smtp4dev 不做鉴权)
  1. 邮件会落在 http://localhost:8090 的 Web 界面里,可以直接查看收件箱、正文与附件渲染。

⚠️ 注意不要混淆两套邮件通道:EMAIL_DRIVER 相关环境变量属于 Twenty 服务器的系统邮件发送器(system mailer),与工作流「Send Email」节点所使用的工作区连接邮箱无关——本地测试期间保持它们原样即可,不要去改动。

每个工作区的核对清单

完成重建后,用这张清单做最终验收。每一条都应当在工作流编辑器中逐一核对:

项目 验收标准
触发器 cron,每日(daily)触发
动作 通过 Person 查找,向每个 VALIDATED 合作伙伴发送一封邮件
发布标签 发布的工作流版本标签为 Daily Digest
执行身份 以**应用角色(application role)**运行——该应用角色的 RLS 谓词定义见 partner.role.ts,并由 configure-partner-rls 脚本按角色标签定位后写入目标工作区

在每次 install 之后,对每一个目标工作区重复执行以上全部步骤。 工作流是工作区元数据——它们不会随 deploy / install 迁移;漏掉任何一个工作区(尤其是新接入的 staging / prod 环境),就意味着该环境永远不会发出每日提醒。

延伸阅读:本运行手册涉及的仓库资源

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388