FR-049 問卷流程規則(跳題)補回 + 子題 Designer 接線 — Phase 0/1 設計

建立:2026-07-18(site-regression D-3 查證後開案) 狀態:設計拍板(A 案),待 spec review → implementation plan 背景查證:D-3 調查定案(2026-07-18)——子題 pid BE 完整實作有真實資料;flowRules v2 零持久化;v1 曾有 goto 跳題機制上線(options JSONB goto_question/goto_page_1/goto_page_2,2025-05~09 真實資料)。v2 重寫斷鏈非「從未做過」。

1. 白話需求(Phase 0)

問卷設計者要能設定「依答案決定後續要答什麼」:

  1. 同題組內跳題:A-1 答選項 2 → 直接跳到第 5 題(中間題目不用答)。
  2. 跳到其他題組:A-1「系統部署環境?」答 1 地端 → 答 B 題組(地端規格);答 2 雲端 → 答 C 題組;答 3 混合 → B、C 都要答(一個選項可指多個題組)。
  3. 只有單選題可設定流程規則(與 v1 規格一致)。
  4. 受稽方填答時,被略過的題目不需作答、不算進度;稽核方檢視時能分辨「不適用(依作答略過)」與「漏答」。
  5. 順手收 D-3a:Designer 建子題存不進去(parentId 沒送出、清單不顯示子題)——BE 現成,補兩處 FE 接線。

現況問題:v2 Designer 有 flowRules 編輯 UI 殼,但設定存檔即蒸發(BE 零持久化、填答端零引擎)——使用者以為設好了,實際問卷完全不跳,比沒有功能更傷信任。

2. 方案取捨(Phase 1 已拍板)

內容 判定
A(採用) 復刻 v1 跳題語意、v2 資料形狀重做;填答端用「可見性計算」實作 ✅ 符合原規格與使用者心智模型;v1 雙 page 槽位一般化為陣列後混合情境原生支援
B 純條件顯示(Qualtrics 式) 條件掛在題/題組上 ❌ 偏離原規格與現有 UI 殼,設計者要換心智模型
C 只做題組級跳轉 同組內跳題後期補 ❌ 「A-1 跳第 5 題」是真實需求,切掉半套

關鍵設計決策:跳題語意、可見性實作。 對外語意是「跳題」(設計者設「答 X 跳第 N 題」),內部引擎是純函數可見集合 = f(全部規則, 全部當前答案),答案任何變動即全量重算。語意細則(已定案):

  • 初始/未作答:規則來源題未作答時,其規則不生效 → 預設全可見(含被規則指到的題組)。跳題語意是「答了才略過」,不是「答了才出現」——與 v1 行為一致,也保證無規則問卷 = 全可見零變化。
  • 同題組內跳:「Q1 答 2 跳 Q5」= Q2-Q4 不可見;Q5 之後照常(跳題只略過起訖之間,不影響其後)。
  • goto_page:選項設了 goto_page_uids 時,該題組集合以外、同層級的其他「被任一規則提及的題組」不可見;未被任何規則提及的題組不受影響(照常顯示)。
  • 重新進入部分作答的問卷:載入後以既有答案跑一次同一個純函數還原可見狀態(無需額外持久化「跳題進度」)。
  • 已填而後被隱藏的答案:保留資料不刪除(稽核軌跡);「不適用」是運算結果非持久化 flag——檢視端用「提交當下快照的規則+答案」重算(規則隨問卷版本走,v2 問卷發布後結構鎖定不可改,無「事後改規則」的漂移問題;此前提 implementation plan 時驗證,若發布後仍可改結構則改為提交時落 snapshot flag)。
  • 必填檢核只作用於可見題(與進度計算同源):現行填答端的 totalRequired(進度分母)與提交檢核(isRequired && !isAnswered 擋 submit/close)都是全量掃描——FR-049 後兩者一律改以可見集合為母集:被規則略過的必填題不擋提交、不算分母。同一個可見性純函數餵三個消費點(渲染/進度/必填檢核),禁止各算各的(否則會出現「畫面上看不到的必填題擋住提交」的死結)。

3. 資料模型(已定案)

沿 v1 內嵌模式:規則直接掛在 options JSONB 的每個選項物件上,換新鍵名。v1 真實資料形狀(DEV 實查)是 {key, name, content, goto_page_1: {uid,name}, goto_page_2: {uid,name}}——選項本身無 uid,內嵌設計天然迴避「option 識別」問題(獨立欄位需 option index/uid 對應,index 重排即壞)。

options JSONB 每個選項物件(v2 新鍵,僅單選題):
{
  "name": "雲端",
  ...既有鍵...,
  "goto_question_uid": "<同題組內目標題 uid>" | 缺省,
  "goto_page_uids": ["<題組 uid>", ...] | 缺省      # v1 goto_page_1/2 一般化為陣列
}
  • 無 schema migration:options 本就是 JSONB,新鍵向後相容;既有問卷無新鍵=無規則。
  • v1 遺留鍵(goto_question/goto_page_1/goto_page_2)不讀不寫不遷移——v1 問卷已退役,資料留檔即可;v2 引擎只認新鍵。
  • 屬套件 domain 資料 → 改 jedi-survey 套件本體(CLAUDE.md 套件異動規範,dev 走 path dependency)。serializer 對 options 內新鍵做白名單驗證(目標存在性、禁止自指、僅單選題允許)。

4. 四段鏈範圍

內容
① BE(jedi-survey) flow_rules 持久化 + serializer + 驗證(單選限定、目標存在性、禁止自指);v1 goto 遺留資料不遷移(v1 問卷已退役,資料留檔即可)
② Designer 既有 flowRules UI 殼接 BE(存讀);目標選擇補「題組(多選)」;子題接線(D-3a:AddQuestionDialog parentId→parentDbId 送出、清單 render subQuestions)
③ 填答端 可見性計算引擎(監聽單選答案 → 重算可見題/題組集合);進度計算只算可見題;被隱藏題答案保留標不適用
④ 稽核檢視端 略過題呈現「不適用(依 X-N 作答略過)」,與漏答視覺區分

5. 邊界與風險

  • 規則循環:A 跳 B、B 的答案又影響 A 所在題組——BE 驗證擋自指;跨題組循環由「可見性計算是純函數(從全部答案一次算出可見集合)」天然免疫遞迴爆炸,但設計 Designer 時提示使用者避免矛盾規則。
  • 多選/其他題型:不允許設規則(BE 擋 + UI 只在單選題顯示設定區)。
  • 匯入路徑:Excel 匯入問卷(import_survey)本期不支援 flow_rules 欄位(範圍控制),匯入的問卷無規則、可後續在 Designer 補設。
  • 既有 495+ 份 v2 問卷:無 flow_rules → 行為完全不變(可見性引擎對無規則問卷=全可見)。
  • task-survey(任務問卷)路徑:已盤點確認共用——FE router 實查:/survey/preview/:id(預覽)與 /survey/fill(任務抽屜開的填答頁)都掛同一個 SurveyPreview.vue。可見性引擎做在 SurveyPreview(或其 composable)一處即三入口(預覽/獨立填答/任務填答)全生效,無第二處要改。

6. 驗收標準

  1. 部署環境三情境可完整跑通:「A-1 系統部署環境?」單選【地端/雲端/混合】,規則設 地端→goto_page_uids=[B 題組]、雲端→[C 題組]、混合→[B,C 兩題組]。填答驗證:答地端只出 B、答雲端只出 C、答混合 B+C 都出。(此即 v1 goto_page_1/2 雙槽位的一般化情境,DEV 遺留資料有同款真實案例可對照)
  2. 同題組內「答 2 跳第 5 題」可跑通,Q2-4 不算進度。
  3. 回頭改 A-1 答案,可見集合即時重算,已填答案保留。
  4. Designer 建子題後重整不消失(D-3a)。
  5. 無規則的既有問卷行為零變化(site-regression survey 三頁回歸全綠)。
  6. 稽核檢視能區分「不適用」與「漏答」。

7. 測試

  • jedi-survey pytest:持久化/驗證/serializer
  • E2E(test repo site-regression/features/modules/survey/,另派):Designer 設規則→填答跳題→混合情境→改答案重算;survey-v2 既有 47 條(survey-v2-manage/designer/fill.feature)回歸不紅
  • 移除 site-regression survey-v2-designer.feature 子題 @known-bug(D-3a 修復後);flowRules 新 scenario 補進 survey-v2-designer + survey-v2-fill 兩 feature