背景
业务上需要将用户注册行为(账号、来源、状态等)自动记录到飞书多维表格中,方便运营团队实时查看注册数据,无需登录后台系统。
本文基于 Egg.js + 飞书开放平台 Node SDK,实现了一个完整的注册数据上报功能。
技术栈
- Egg.js(Node.js 企业级框架)
- @larksuiteoapi/node-sdk ^1.60.0(飞书开放平台官方 Node SDK)
- 飞书多维表格(Bitable)
整体架构
客户端注册请求
↓
POST /doc-api/register/report
↓
RegisterReportController(参数校验)
↓
FeishuBitableService(调用飞书 SDK 写入多维表)
↓
飞书多维表格(数据落地)
实现步骤
1. 安装依赖
npm install @larksuiteoapi/node-sdk --save
2. 配置飞书应用凭证
在 config/config.default.js 中添加飞书多维表配置:
// 飞书多维表配置
feishuBitable: {
appId: "your_app_id", // 飞书应用 App ID
appSecret: "your_app_secret", // 飞书应用 App Secret
appToken: "your_app_token", // 多维表格的 App Token
tableId: "your_table_id", // 数据表 ID
userAccessToken: "your_token", // 用户访问令牌
},
获取方式: 在 飞书开放平台 创建自建应用,开通多维表格权限后,在 API 调试台获取以上参数。
3. 编写 Service 层 —— 封装飞书 SDK 调用
新建 app/service/feishuBitable.js:
const Service = require("egg").Service;
const lark = require("@larksuiteoapi/node-sdk");
class FeishuBitableService extends Service {
// 创建飞书客户端
createClient() {
const { appId, appSecret } = this.ctx.app.config.feishuBitable;
return new lark.Client({ appId, appSecret });
}
// 向多维表添加注册记录
async addRegisterRecord(data) {
const { ctx, logger } = this;
const config = ctx.app.config.feishuBitable;
try {
const client = this.createClient();
// 构建字段数据
const fields = {
"注册账号": data.account,
"注册时间": Date.now(),
"utmurl": data.utmurl || "",
"注册前向地址": data.cl_referrer || "",
"code": data.code,
"成功/失败原因": data.fail_reason || "成功",
};
const result = await client.bitable.v1.appTableRecord.create(
{
path: {
app_token: config.appToken,
table_id: config.tableId,
},
data: { fields },
},
lark.withUserAccessToken(config.userAccessToken),
);
logger.info("[FeishuBitable] 添加注册记录成功", {
account: data.account,
recordId: result?.data?.record?.record_id,
});
return { success: true, data: result.data };
} catch (error) {
logger.error("[FeishuBitable] 添加注册记录失败", {
account: data.account,
error: error.message,
response: error.response?.data,
});
return { success: false, error: error.message };
}
}
}
module.exports = FeishuBitableService;
4. 编写 Controller 层 —— 接口入口与参数校验
新建 app/controller/user/registerReport.js:
const Controller = require("egg").Controller;
class RegisterReportController extends Controller {
async report() {
const { ctx, logger } = this;
const { account, utmurl, cl_referrer, code, fail_reason } = ctx.request.body;
// 参数校验
if (!account) {
ctx.body = { code: "000001", message: "注册账号不能为空" };
return;
}
if (!code) {
ctx.body = { code: "000001", message: "code不能为空" };
return;
}
try {
const result = await ctx.service.feishuBitable.addRegisterRecord({
account, utmurl, cl_referrer, code, fail_reason,
});
if (!result.success) {
ctx.body = { code: "000002", message: "上报失败" };
return;
}
ctx.body = { code: "000000", message: "成功", data: result.data };
} catch (error) {
logger.error("[RegisterReport] 上报异常", error);
ctx.body = { code: "000500", message: "网络错误" };
}
}
}
module.exports = RegisterReportController;
5. 注册路由
在 app/router/router_user.js 中添加:
router.post("/doc-api/register/report", controller.user.registerReport.report);
接口文档
POST /doc-api/register/report
请求参数(JSON Body):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| account | string | 是 | 注册账号(手机号) |
| code | string | 是 | 状态码,如 "000000" |
| utmurl | string | 否 | utm 来源 URL |
| cl_referrer | string | 否 | 注册前向地址(Referrer) |
| fail_reason | string | 否 | 失败原因,不传默认为 "成功" |
请求示例:
{
"account": "18330825660",
"utmurl": "https://www.baidu.com",
"cl_referrer": "https://www.google.com",
"code": "000000",
"fail_reason": "成功"
}
响应示例:
{
"code": "000000",
"message": "成功",
"data": {
"record": {
"record_id": "recxxxxxx",
"fields": { ... }
}
}
}
踩坑记录
Map 不能作为飞书 SDK 的 fields 参数
飞书官方示例代码使用了 new Map() 来构建 fields:
// 官方示例(存在问题)
data: {
fields: new Map([
['注册账号', '18330825660'],
['code', '000000'],
]),
}
但实际使用中,这会导致写入空记录。 原因是 SDK 内部通过 JSON.stringify 序列化请求体,而 Map 无法被正确序列化:
JSON.stringify(new Map([["key", "value"]]))
// 输出: "{}" ← 数据全部丢失!
解决方案: 使用普通对象替代 Map:
// 正确写法
data: {
fields: {
"注册账号": "18330825660",
"code": "000000",
},
}
多维表格字段名必须精确匹配
fields 中的 key 必须和飞书多维表格中的列名完全一致(包括中文、标点、空格),否则该字段会被静默忽略,不会报错。
涉及文件清单
| 文件 | 操作 | 说明 |
|---|---|---|
package.json | 修改 | 新增 @larksuiteoapi/node-sdk 依赖 |
config/config.default.js | 修改 | 添加飞书多维表配置项 |
config/config.local.js | 修改 | 添加本地环境飞书配置 + MySQL charset |
app/service/feishuBitable.js | 新增 | 飞书多维表 Service 封装 |
app/controller/user/registerReport.js | 新增 | 注册上报 Controller |
app/router/router_user.js | 修改 | 注册上报路由 |
多维表格字段映射
| 多维表列名 | 数据来源 | 说明 |
|---|---|---|
| 注册账号 | account | 用户手机号 |
| 注册时间 | Date.now() | 接口调用时的时间戳(毫秒) |
| utmurl | utmurl | 广告追踪来源 |
| 注册前向地址 | cl_referrer | 用户注册前的页面地址 |
| code | code | 注册结果状态码 |
| 成功/失败原因 | fail_reason | 注册结果描述 |
关注前端求生录微信公众号,了解更多