Google Sheets 更新行状态
Google Sheets 更新行状态:通过 webhook 接收事件 ID 和状态,校验输入,查找 Google Sheets 中已有的行,并写入该行的状态单元格。找不到 ID 时走单独的响应分支,不执行写入。此模板不使用 AI 模型。
状态:已于 2026年10月9日 在 AI Flowi Workflow 1.9.2 上完成生产环境测试(34 个案例全部通过)
作者:Ai Flowi · 发布于 。更新于 2026年10月9日。生产环境测试日期:2026年10月9日 (MYT)。
任务
接收通过身份验证的 HTTP POST 请求,其中包含 {event_id,status}。在已有行中查找与 event_id 完全一致的值,匹配时区分大小写。实测结果为:写入状态后返回 HTTP 200 和 {"status":"updated"};查找不到行时返回 HTTP 404 和 {"status":"not-found"}。未找到行的分支不包含写入组件。
适用对象
适合在电子表格中保存事件或任务记录,并需要由发送方将已有记录标记为排队中、进行中或已完成的团队。状态由发送方提供。此工作流接受自由填写的状态文本,不推断状态,也不强制执行业务状态转换规则。
输入示例
{"event_id":"demo-event-001","status":"complete"}
两个字段都必须是字符串,长度为 1–100 个 Unicode 码点,且至少包含一个非空白字符。空白字符会保留。ID 区分大小写:001 与 1 不同。请求中的额外字段会在校验时丢弃。
设置
- 使用填有模拟数据的私有测试电子表格,以及已获授权的 Google 连接。
- 创建工作表标签页
YOUR_TAB。将 A1 设为event_id、B1 设为status,并将 C1 设为note用于测试。发送更新前先填入记录。正常使用时,ID 应为纯文本,文本单元格应有值,且 ID 应唯一。 - 将 Universal Webhook 设为 POST,身份验证方式设为 Platform API Key,响应模式设为 From Flow。发送方的密钥应保密。
- 配置 Search Row:Key Column 设为
event_id,Header Row 设为1,启用 Case Sensitive Key Match,Sheet Name 设为YOUR_TAB。将 Update Range 配置为RAW,并禁用 Include Object Headers。 - 按下表连接节点。让 JSON Operations 已选择的操作字段和 JQ 字段保持可见。将 Combine 节点的 JSON 输入设为接受列表。处理请求期间,不要移动、排序、插入或删除工作表中的行。
- 在私有标签页上重新运行本文记录的测试。每次请求后检查响应和整个工作表,再将工作流用于真实记录。
节点连接与字段映射
此流程图有 13 个节点:Universal Webhook;用于校验的 JSON Operations;用于有效输入的 Verification Router;Search Row;使用 Combine 操作的 JSON Operations;用于评估查找结果的 JSON Operations;用于已找到行的 Verification Router;Data to Message;Update Range;用于成功结果的 JSON Operations;以及分别用于 200、404 和 422 响应的三个 Webhook Response 节点。
| 源节点输出 | 目标节点输入 | 映射 |
|---|---|---|
Universal Webhook.request_data | Validation.data | 读取 .body;输入有效时,仅输出 verified、event_id 和 status。 |
Validation.data_output | Valid router.verification_result | 使用顶层布尔值 verified。 |
Valid router.verified | Search Row.row_data | 查找 event_id;此组件不执行写入。 |
Valid router.verified | Combine.data | 保留传入的 status 和 event_id。 |
Search Row.result | Combine.data | 将 found、key_value 和 row_number 加入保留的输入。 |
Combine.data_output | Lookup JSON Operations.data | 要求 found 为布尔值;匹配成功时,准备 row_number 和 values:[[status]]。 |
Lookup.data_output | Found router.verification_result | verified 表示查找结果;未找到行时携带响应定义。 |
Found router.verified | Data to Message.data | 模板:'YOUR_TAB'!B{row_number}。 |
Data to Message.text | Update Range.range_name | 由 Message 提供状态单元格的精确范围。 |
Found router.verified | Update Range.values | 使用包含一个状态值的 values 包装对象。 |
Update Range.result | Success JSON Operations.data | 要求 updatedCells == 1;仅投影出 {status:"updated"}。 |
Success.data_output | 200 response.body_data | HTTP 200 application/json。 |
Found router.rejected | 404 response.response_definition | {status_code:404,body:{status:"not-found"}};不执行写入。 |
Valid router.rejected | 422 response.body_data | 返回校验错误;不执行查找或写入。 |
Combine 节点接收两个字段名互不相同的 JSON 对象。查找投影在路由前校验匹配行的行号和键值。查找使用 event_id 表头;写入使用状态列 B。如果更改列顺序,也要更新范围模板。Update Range 只更改一个单元格,事件 ID、备注及该范围之外的其他单元格均保持不变。
JSON Operations 设置
校验、查找和成功结果处理均选择 JQ Expression。只有双输入的 Combine 节点选择 Combine。让所选字段保持可见。
相关内容:Google Sheets 定时写入:每天记录状态行。
校验查询
def valid_text: if type == "string" then length >= 1 and length <= 100 and test("\\S") else false end;
.body as $b | if ($b|type) != "object" then
{verified:false,error:"Request body must be a JSON object with event_id and status."}
else [ (if ($b.event_id|valid_text) then empty else "event_id must be non-blank text between 1 and 100 characters" end),
(if ($b.status|valid_text) then empty else "status must be non-blank text between 1 and 100 characters" end) ] as $errors
| if ($errors|length) > 0 then {verified:false,error:"Validation failed",details:$errors}
else {verified:true,event_id:$b.event_id,status:$b.status} end end
查找查询
if (.found|type) != "boolean" then error("Unexpected search result")
elif .found == false then {verified:false,status_code:404,body:{status:"not-found"}}
elif (.row_number|type) != "number" then error("Invalid matched row")
elif .row_number < 2 or (.row_number|floor) != .row_number or .key_value != .event_id then error("Invalid matched row")
else {verified:true,row_number:.row_number,values:[[.status]]} end
成功结果查询
if .updatedCells == 1 then {status:"updated"} else error("Unexpected update result") end
脱敏的请求与响应示例
这些响应示例与使用模拟数据进行的生产环境测试结果一致。
POST <production-webhook-url>
Content-Type: application/json
x-api-key: <private-platform-api-key>
{"event_id":"demo-event-001","status":"complete"}
Expected: HTTP 200
{"status":"updated"}
{"event_id":"demo-event-absent","status":"complete"}
Expected: HTTP 404; sheet unchanged
{"status":"not-found"}
{"event_id":"demo-event-001"}
Expected: HTTP 422; sheet unchanged
{"verified":false,"error":"Validation failed","details":["status must be non-blank text between 1 and 100 characters"]}
校验与失败处理行为
在生产环境测试案例中,缺失字段、null 值、类型错误、空字符串、仅含空白字符的字符串,以及超过 100 个 Unicode 码点的字符串,都会在查找或写入前返回 422。非对象请求体和格式错误的 JSON 在生产环境测试案例中也返回 422,并附带 {"verified":false,"error":"Request body must be a JSON object with event_id and status."}。平台密钥缺失或无效时返回 403;身份验证响应体的具体内容由平台控制。
只有成功执行 Search Row 且结果为 found:false 时,才会产生 not-found。Google 访问错误会中断工作流,并可能暴露未经处理的平台错误。当前配置中没有组件捕获这些异常。查找结果结构不符合预期,或更新结果并非恰好更新一个单元格时,都会抛出错误,不会声称已完成更新。
测试结果
全部 34 个生产环境 HTTP 测试案例均通过,并在请求后回读整个标签页进行验证。工作表始终保留七行数据。成功更新只更改首个匹配行的状态单元格;查找未命中和输入被拒绝时,所有值保持不变。重复发送相同更新后,值保持不变。已确认删除临时 API 密钥。
| 案例 | HTTP 结果 | 对工作表的影响 | 判定 |
|---|---|---|---|
| T1 — 有效的已有行 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T2a — 缺少 status | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T2b — 缺少 event_id | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T3a — status 为数值 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T3b — event_id 为数值 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T4 — JSON 格式错误 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T5 — 缺少 API 密钥 | 403 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T6 — API 密钥无效 | 403 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T7 — event_id 不存在 | 404 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T8a — 重复发送相同更新 | 200 | 数据行数 7 -> 7。重复更新;值不变。 | 通过 |
| T8b — 再次更改状态 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T9a — 大小写不匹配 | 404 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T9b — 前导空格不匹配 | 404 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T9c — 带前导零的文本 ID | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T10 — 忽略额外字段 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T11a — status 为 null | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T11b — status 为空字符串 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T11c — status 仅含空白字符 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T11d — event_id 为 null | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T11e — event_id 为空字符串 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T11f — event_id 仅含空白字符 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T12a — status 长度为 100 个字符 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T12b — status 长度为 101 个字符 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T12c — 预先填入的 ID 长度为 100 个字符 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T12d — ID 长度为 101 个字符 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T13a — 请求体为数组 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T13b — 请求体为 null | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T13c — 请求体为字符串 | 422 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T14 — 使用 RAW 写入类似公式的状态文本 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T15 — 保留空白字符 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T16 — 键值重复时只更新首个匹配行 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T17a — 预先填入的 ID 带空白字符 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
| T17b — 未去除首尾空白导致不匹配 | 404 | 数据行数 7 -> 7。所有值不变;不执行写入。 | 通过 |
| T18 — Unicode 状态长度边界 | 200 | 数据行数 7 -> 7。仅更新首个匹配行的状态单元格;其他单元格不变。 | 通过 |
测试的平台版本:AI Flowi Workflow 1.9.2。测试日期:2026年10月9日 (MYT)。各分支实测节点运行次数:未测量。导入及重新测试结果:所提供的证据中未执行。
下载
工作流导入文件下载:暂不可用;未测试导入后的副本。只有完成脱敏导出并测试导入后的副本,才能添加公开导入文件。上文已记录节点配置。
必需配置
- 在两个 Sheets 组件中配置已获授权的 Google 连接和 Spreadsheet ID。
- 标签页
YOUR_TAB,第 1 行的表头event_id,以及 B 列中的状态。 - 预先存在的文本 ID,以及执行期间保持稳定的行布局。
- 生产环境 webhook URL,以及发送方的 Platform API Key。
- 双输入 Combine 配置、查找结果路由,以及独立的 200、404 和 422 响应。
测试版本与日期
测试版本:AI Flowi Workflow 1.9.2。测试日期:2026年10月9日 (MYT)。所提供的证据涵盖此生产环境流程图和模拟测试数据,不能据此确认导入后副本的测试结果。
用量与费用假设
套餐:Go 年付方案每月 USD8 起(月付 USD10),Plus 年付方案每月 USD16 起(月付 USD20),Pro 100 年付方案每月 USD80 起(月付 USD100),Pro 200 年付方案每月 USD160 起(月付 USD200),Pro 500 年付方案每月 USD400 起(月付 USD500),每月包含 5,000 至 250,000 个 Flowi Tokens,并支持充值;每次节点运行消耗 1 个 token;不包含第三方 API 和模型使用费用。
此设计不使用 AI 或 LLM 组件,也不使用第三方付费 API。仍需具备 Google 账号和工作表访问权限,并受服务配额限制。以下估算假设每个经过的节点运行一次、路由器只选择一个分支、不重试,也没有回读验证流程。生产环境的实际用量统计尚待确认。
| 分支 | 每次请求的节点数 | 100 次请求 | 1,000 次请求 | 10,000 次请求 |
|---|---|---|---|---|
| 更新行 | 11 | 1,100 | 11,000 | 110,000 |
| 未找到行 | 8 | 800 | 8,000 | 80,000 |
| 校验拒绝 | 4 | 400 | 4,000 | 40,000 |
| 进入流程图前身份验证被拒绝 | 预计为 0 | 预计为 0 | 预计为 0 | 预计为 0 |
每次验证性读取都增加一次节点运行。重复投递会再次执行工作流;即使状态相同,仍会触发写入。失败尝试和重试可能消耗额外的令牌。套餐不包含第三方 API/模型用量。
限制
- 查找与写入是两次独立调用。在两次调用之间排序、插入或删除行,可能使已保存的行号指向其他行。请保持布局稳定;如果需要并发更改结构,应使用支持事务的数据存储。
- 查找选择首个匹配行。重复 ID 不会消除;正常使用时应保持 ID 唯一。
- 并发写入状态可能相互覆盖。没有冲突检测或状态转换策略。
- 状态列必须保持为 B 列。RAW 将类似公式的状态文本按原文存储,不会将其作为公式执行。
- Google 错误在工作流中没有脱敏响应。即使响应丢失,写入也可能已成功;对于结果不确定的投递,请先检查工作表再重试。
- 34 个生产环境测试案例已通过;节点用量统计和脱敏副本导入后的运行仍未经验证。回读只比较值,不比较工作表格式或权限。
后续步骤
在你自己的私有配置上重新运行所有案例,并检查完整的行内容。检查并发行更改及未经处理的 Google 错误。完成导入后副本的测试,再添加脱敏的导入文件下载。参阅 Universal Webhook、Google 连接设置和常见错误。有关 webhook 和 Google 连接设置的背景说明,请参阅通过 Webhook 写入 Google Sheets 的教程。