我做了一个飞书多维表格/钉钉 AI 表格的简单替代品

查看 16|回复 1
作者:penzi   
Github: https://github.com/autable/autable
主页以及文档: https://autable.github.io/
最近我在尝试把一些公司内部的流程迁移到钉钉 AI 表格, 然而发现并不顺利
[ol]
  • 钉钉审批流程同步到 AI 表格之后, 会产生多条相同编号的记录, 钉钉的表单和自动化没有任何办法处理这个问题, 因为钉钉并没有把 unique 的 record id 暴露出来
  • 钉钉 AI 表格的 AI 非常傻, 经常做不到一些简单的表单定义和自动化流程定义
  • nocode 的自动化对于会写代码的人完全是负生产力
  • 最大的问题, 即使付费, 默认最高也只有单表 5 万条记录
    [/ol]
    我也调研了一下开源方案, 一个问题就是 OIDC 登录基本都是付费功能, 商业版 self-host 普遍定价比钉钉/飞书的最高级别的订阅还贵
    最终, 我搓了一个符合我需求的 AI 表格
    [ol]
  • 支持表格, 自动化, 表单和字段级别的权限配置
  • 自动化, 表格都使用简单的 js 定义, AI 友好的方式
  • 支持公式字段, 公式同样使用 js 表达
  • 存储使用 sqlite, 运维成本低, 最初的容灾方案每天备份一次就行了
  • 支持 OIDC, GPL 3.0 license, 永远不用担心开原版缺失关键功能
    [/ol]
    目前刚跑通一个使用案例, 可以使用自动化同步钉钉 AI 表格的数据, 已经可以渐进式迁移, 并且可以借助这个打通钉钉内部所有数据的同步
    下面是一个周期性同步钉钉 AI 表格的 workflow.js 例子
    function instances(info) {
      return {
        timer: "time.schedule",
        dingTable: "dingtalk.notable.records.list",
        fields: "table.field.create",
        rows: "table.row.upsert"
      };
    }
    function trigger(info) {
      return {
        instance: "timer",
        params: {
          interval_ms: 5 * 60 * 1000
        }
      };
    }
    function run(info) {
      const table = "同步表";
      let nextToken = "";
      while (true) {
        const page = info.instance("dingTable").exec({
          max_results: 100,
          ...(nextToken ? { next_token: nextToken } : {})
        });
        const records = page.records || [];
        info.instance("fields").exec({
          table,
          fields: fieldsOf(records)
        });
        for (const record of records) {
          info.instance("rows").exec({
            table,
            match_field: "dingtalk_record_id",
            values: valuesOf(record)
          });
        }
        nextToken = page.next_token || "";
        if (!page.has_more || !nextToken) break;
      }
      return { ok: true, table };
    }
    function fieldsOf(records) {
      const fields = {
        dingtalk_record_id: "string"
      };
      for (const record of records) {
        for (const name of Object.keys(record.fields || {})) {
          fields[name] = "string";
        }
      }
      return fields;
    }
    function valuesOf(record) {
      return {
        dingtalk_record_id: String(record.id || ""),
        ...Object.fromEntries(
          Object.entries(record.fields || {}).map(([name, value]) => [
            name,
            stableStringify(value)
          ])
        )
      };
    }

    开源, 自动化, 表格

  • michealzh   
    我更需要飞书的文档 目前还没有看到替代品
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部