本卡屬 FR-161(母卡見「母卡」欄),第 1 棒:建資料表與大類 API。後續五張卡都吃這張的表。

這張卡做什麼(白話)

「事前資料」是客戶在顧問訪談前上傳資料的地方,資料分成幾個大類(網路架構、組織架構……)。這張卡做地基:建三張資料表(大類、資料、資料的各個版本),出貨帶四個起始大類,開兩顆權限,在側選單登記兩個入口,並做大類的新增、改名、排序、刪除 API。畫面在卡 4 做,這張只做後端。

怎麼做

① Migration(主線、phase=active、envs=*):新檔 scripts/sql/2026-10-2x-fr161-pre-engagement-tables.sql,在 scripts/sql/manifest.tsv 末尾加一列。三張表都在 compliance schema:

compliance.pre_engagement_categories
  id BIGSERIAL PK, uid VARCHAR(36) UNIQUE, name VARCHAR(100) NOT NULL, description TEXT,
  sort INT DEFAULT 0, seed_key VARCHAR(50),          -- 從 system_menus 帶進來的才有值
  tenant_id BIGINT NOT NULL REFERENCES public.tenants(id),
  org_unit_id INTEGER REFERENCES public.org_units(id),
  created_at/updated_at timestamptz, created_user/updated_user VARCHAR(255)
  UNIQUE (tenant_id, name)

compliance.pre_engagement_files
  id, uid, category_id FK→pre_engagement_categories(id), title VARCHAR(255) NOT NULL,
  is_obsolete BOOLEAN DEFAULT false, tenant_id, org_unit_id, 審計四欄

compliance.pre_engagement_file_versions
  id, uid, pre_engagement_file_id FK ON DELETE CASCADE, version_no INT NOT NULL,
  file_id FK→public.upload_files(id), created_user, created_at
  UNIQUE (pre_engagement_file_id, version_no)

② 起始值:另開一支 seed migration(phase=seed),system_menus group pre_engagement_category 四筆,ON CONFLICT ("group", key) DO NOTHING:

key=network   value=網路架構          sort=1
key=org       value=組織架構          sort=2
key=asset     value=資產盤點          sort=3
key=policy    value=政策文件與程序書  sort=4

先例:scripts/sql/2026-10-22-fr158-seed-pdf-parser-menu.sql。i18n 標籤走 lang.pre_engagement_category.<key>。

③ 權限與路由(併在 ① 那支或另開一支 active migration):抄 scripts/sql/2026-10-22-fr157-agent-console-capabilities.sql 的三段式。

④ BE 模組:新模組 pre_engagement,結構照既有模組(參考 governance):domain/pre_engagement/(entity、repo interface、domain service)、infra/pre_engagement/(model 繼承 TenantScopedMixinModel、mapper、repo impl)、app/pre_engagement/、api/pre_engagement/、di_containers/pre_engagement/,並在 config/app_modules.py 註冊。這張只做大類;資料與版本的 model、repo 也建好,給卡 2 用。

⑤ 大類 API(守門用 common.authz 的 capability decorator):

GET    /pre-engagement/categories            upload 或 manage-category 都可讀;回每類資料數
POST   /pre-engagement/categories            manage-category;{name, description}
PUT    /pre-engagement/categories/<uid>      manage-category;改 name、description
DELETE /pre-engagement/categories/<uid>      manage-category;底下有資料回 PRE_409002
PUT    /pre-engagement/categories/order      manage-category;[uid, ...] 依序寫 sort