最近有一个任务,需要实现一个能效平台:把 GitLab、飞书通讯录、飞书项目 Meego、飞书多维表串起来,让gitlab的每次 push 最终都变成研发能效指标。
一、为什么需要这个平台
简单来说,管理层想看,官方点就是研发过程里的数据天然分散,代码在 GitLab,人员信息在飞书通讯录,需求状态和周期在 Meego(我们公司使用的飞书项目),分析看板在飞书多维表。这样的话,管理者看到的是就是一堆碎片了,而这个平台不需要让人重复填数据,而是从研发动作本身采集数据。
当开发者推送代码时,GitLab System Hook 会把 push 事件送进平台。平台再拉取 commit 明细,解析 commit message 和分支名,识别需求 ID、提交类型和 AI 标记。随后,它用飞书通讯录补齐人员和部门,用 Meego 补齐需求名称、状态与周期,最后把结果落到 MySQL,并同步到飞书多维表。
二、总体架构
平台采用 Koa 2 + CommonJS JavaScript 实现,运行在 Node.js 18 以上,数据存储使用 MySQL 5.7,测试体系是 Mocha + Supertest。
整体分层:
| 层级 | 目录 | 主要职责 |
|---|---|---|
| HTTP | app/router/、app/controllers/ |
路由、鉴权、请求入口、统一响应 |
| Service | app/services/ |
编排业务流程、后台处理、同步任务 |
| Biz | app/biz/ |
commit 解析、分支解析、指标聚合、周期计算 |
| DAO | app/dao/、app/common/mysql.dao.js |
MySQL 表访问 |
| Clients | app/clients/ |
GitLab、飞书、Meego、多维表 OpenAPI |
| Jobs | app/jobs/ |
cron 调度 |
Controller 不写业务逻辑,Biz 保持纯函数,外部 API 被收敛到 Client,Service 负责把各个环节串起来。后续无论是扩展指标,还是替换某个外部系统,都不需要把 HTTP 细节、数据库细节和计算逻辑混在一起改。
核心链路可以概括为:
三、Hook 入口
平台的第一个入口是:
http
POST /api/gitlab/system-hooks
校验 X-Gitlab-Token,把原始 payload 写入 dep_gitlab_hook_events。
去重键来自 project_id + ref + before + after。也就是说,同一个项目、同一个分支、同一段 commit 范围的重复 hook,不会被反复入队。
这里有一个重要设计取舍:Hook 入口不直接做所有外部 API 查询。
GitLab 回调应该尽快返回。如果在回调线程里同步拉 GitLab commit、查飞书通讯录、查 Meego、写多维表,一旦某个外部系统慢下来,GitLab hook 就会被拖住。平台选择先入队,再由后台任务处理,入口负责"可靠接收",后台负责"慢慢加工"。
dep_gitlab_hook_events 表承担了这个队列角色。它保存原始 payload、状态和错误信息,状态包括 pending、processing、done、failed。即使处理失败,事件也不会丢,后续扫描还能继续重试。
四、后台处理
后台处理有三种触发方式:
| 触发方式 | 入口 |
|---|---|
| Hook 入库后异步触发 | 内部 runner |
| 定时兜底 | HOOK_PROCESS_CRON,默认每分钟 |
| 手动触发 | POST /api/jobs/process-hooks?limit=50 |
处理流程从 serv.hook-processing.js 开始。服务会扫描 pending 或 failed 事件,将其标记为 processing,然后判断是否为 push 事件,非 push 事件会被跳过。
对于 push 事件,平台先解析 Git ref 得到分支名,再通过 GitLab API 拉取 push 对应的 commit 和 diff 统计。随后每条 commit 会被标准化成统一结构,写入 dep_gitlab_commits。
这张 commit 明细表保留了几个关键字段:
| 字段 | 意义 |
|---|---|
commit_sha、project_id、branch_name |
定位一次提交 |
author_email、author_name |
关联人员 |
requirement_id、commit_type |
关联需求和提交类型 |
ai_generated、ai_additions |
AI 代码识别结果 |
additions、deletions |
代码增删行统计 |
valid_message、invalid_reason |
commit message 合规性 |
它的唯一键是 project_id + commit_sha + branch_name,避免同一分支上的同一次提交重复写入。
五、Commit 规范
平台能跑起来,commit message 必须有规则
当前支持的格式类似:
text
feat: [123456] 完成登录页面
fix: [123456] 修复登录错误
docs: 更新 README
feat 和 fix 必须携带需求 ID,其他类型允许不携带。
系统 merge 和 revert 信息会被识别为 skipped,不参与需求指标。
六、指标聚合
平台的最终指标不是按单个 commit 展示,而是按下面这个维度聚合:
text
人员邮箱 + 需求 ID + GitLab project ID + GitLab 分支
聚合后的数据写入 dep_branch_metrics,其中包含人员、需求、项目、分支、代码量、AI 代码量、AI 占比、需求周期、开发周期和 Meego 状态等字段。
其中人员和需求信息来自外部系统补齐:
| 补齐来源 | 补齐内容 |
|---|---|
| 飞书通讯录 | 工号、姓名、部门 |
| Meego OpenAPI | 需求名称、开始时间、结束时间、开发周期、状态 |
| 飞书多维表 | 人员和部门兜底资料 |
七、多维表同步
从MySQL同步到飞书多维表
同步入口有两个:
http
POST /api/jobs/sync-base
以及定时任务,默认每天 02:00 执行。
同步时,平台会创建一条 dep_sync_runs 记录,用来追踪本次同步的状态、记录数、错误信息和完成时间。随后读取 dep_branch_metrics 全量指标,通过人员邮箱、需求 ID、GitLab project ID、GitLab 分支查找目标多维表中的已有记录,存在则更新,不存在则创建。