自建表與資料模型擴充

預建的 ERP 表覆蓋了通用的企業流程,但每個產業都有自己的東西——健身房要記量測數據、旅行社要記團體行程、補習班要記課堂出席。這一章說明怎麼擴充資料模型。


三條擴充路徑

路徑適合可查詢/過濾有型別約束可建關聯
custom_data JSONB少量、非結構化的附加欄位有限
延伸欄位在既有 ERP 表上加欄位
自建表全新的資料實體

判斷方式很簡單:這筆資料是既有實體的附加屬性,還是一個新實體? 客戶多一個「會員卡號」是前者(延伸欄位);「課堂出席紀錄」是後者(自建表)。


自建表:租戶層級的資源

這是自建表最重要、也最容易被誤解的特性:

自建表綁的是組織,不是應用程式。

同一個組織下的所有 Custom App、以及組織自己的資料中心介面,看到的是同一批表、同一份資料

這帶來一條必要紀律:建表前先確認有沒有可重用的既有表。同一張「客戶」表不該因為兩個應用各建一次,就分裂成兩份互不相通的資料。


雙軌命名:顯示名與實體名

每張表、每個欄位都有兩個名字:

顯示名實體名
誰決定您提供系統從顯示名自動生成,或由您顯式指定
可否修改隨時可改建立後永不可變
字元集任意(中文常見)純 ASCII
用途介面呈現API 識別、關聯目標指定

所有 API 與 SDK 在指涉既有表/欄位時一律使用實體名。 顯示名只是標籤——改顯示名不會影響任何既有的程式碼或引用。

實體名的生成會避開三個母體:同組織既有的自建表名、SQL 保留字、以及平台內建的 ERP 表名。例如顯示名「Customers」在空白環境下會得到 customers_2 而不是 customers,因為後者已被 ERP 佔用。

若您顯式指定的實體名撞到上述任一項,系統會回報衝突並明確告訴您撞到哪一類,而不是靜默改名。


欄位型別

型別說明額外契約
text文字
number數值
boolean布林
date日期
datetime日期時間
select單選必須提供選項集;值受資料庫層級約束
relation關聯見下節
json結構化資料
image圖片見下節

每張表都會自動帶系統欄位(識別碼、建立時間、更新時間)。系統欄位不可刪、不可改型別,也不計入欄位配額。

relation 欄位

relation 可以指向兩種目標:另一張自建表,或平台內建的 ERP 表。

這代表您可以建立一張「課堂出席紀錄」自建表,其中的「學員」欄位直接關聯到 ERP 的 customers 表——不需要複製一份客戶資料,也不需要自己維護對應關係。

image 欄位

image 欄位提供完整的圖片處理:上傳走平台代理(而非前端直接上傳到儲存空間),讀取時取得帶簽章的臨時網址,並受跨組織存取控制保護。圖片會計入組織的儲存配額。


誰能改結構

結構操作僅限管理員(持有 system.admin 的使用者):建表、改表、刪表、建欄位、改欄位、刪欄位、以及 ERP 延伸欄位的定義。

資料操作(記錄的增刪改查)與結構讀取維持一般開發權限(builder.access)。

管的是 schema 的形狀,不是它的使用。這道授權在入口層執行,且三個入口(REST API、SDK、AI 工具)語意一致——AI 通道不能繞過 REST 的權限閘。


刪除是兩段式的

刪表與刪欄位都需要兩步,且由伺服器端強制,不是前端的確認彈窗:

  1. 影響預覽:系統先告訴您這個刪除會影響什麼——有多少筆資料、有哪些關聯指向它
  2. 實體名強確認:您必須輸入該表/欄位的實體名才能執行

刪除不可復原。這道設計刻意讓誤刪變得困難。


三個入口,一層服務

自建表的所有操作——結構與資料——都收斂到單一服務層。三個入口共用它:

入口對面是誰身分來源
REST API組織使用者(資料中心介面)登入使用者的 session
SDK(ctx.dbCustom App 的執行期程式碼應用程式的執行身分 + Scope
Builder 工具AI 開發助手Builder session 的組織身分

這層服務是唯一的護欄所在地。 入口不重新實作驗證,也不繞過它:

  • 租戶隔離:所有操作以組織為錨;跨組織存取一律回報「不存在」,不洩漏他人資源是否存在
  • 結構操作序列化:結構變更走組織層級的鎖,併發建表不會互相撕裂
  • 配額:每組織的表數上限、每表的欄位數上限,在鎖內檢查以防併發繞過
  • 型別與值驗證:欄位型別、選項集、預設值、關聯目標的合法性
  • 錯誤反譯:資料庫層級的錯誤(唯一鍵衝突、必填為空、外鍵違反、依賴物件存在)會翻成結構化錯誤碼與可行動的訊息,不會裸拋技術訊息給使用者

任何新入口都應該打這層,而不是自己拼 SQL。


從 SDK 存取自建表

def execute(ctx):
    # 列出組織的所有自建表
    tables = ctx.db.list_tables()

    # 查詢(用實體名)
    rows = ctx.db.query_table('attendance', filters=[
        {'column': 'class_date', 'op': 'gte', 'value': '2026-08-01'}
    ])

    # 新增
    ctx.db.insert_row('attendance', {
        'student_id': ctx.params['customer_id'],   # relation 指向 ERP customers
        'class_date': '2026-08-10',
        'status': 'present',
    })

    # 更新 / 刪除
    ctx.db.update_row('attendance', row_id, {'status': 'late'})
    ctx.db.delete_row('attendance', row_id)

前端則透過內建的 src/db.ts SDK 呼叫,語意相同。詳見第 8 章。


ERP 延伸欄位

延伸欄位讓您在既有 ERP 表上新增真正的欄位——與 custom_data 不同,它可查詢、可過濾、有型別約束,也能被引用系統授權。

適用時機:這筆資料是既有實體的固有屬性,且您需要用它來篩選或排序。例如在 customers 上加「會員到期日」,之後就能查詢「本月到期的會員」。

刪除延伸欄位同樣走兩段式流程,並提供影響預覽。