手冊首頁 / 溝通協作 / 問卷管理頁

問卷管理頁(Survey Manage)

功能群:問卷|資料模型與雙生命週期先讀 功能群總覽

事實基準:2026-07-05 從 FE(views/survey-v2/SurveyManage.vue 等)/ BE(api/survey、jedi-survey 套件)code 掃出;表結構取自 db_schema.jsonsurvey schema);error code 對 jedi_survey/common/enum/error_code.pycommon/code/grc_error_code.py

1. 功能描述

問卷管理頁是作者端問卷範本的入口與總覽:左側資料夾樹分類、主區以卡片或表格列出問卷,提供建立 / 複製 / 另存新版 / 發布 / 移動 / 刪除 / 批量匯入。點問卷進問卷設計器編輯題目結構;問卷發布後才能被專案規劃頁的稽核任務選用(綁定時取快照,見 _overview §4)。

主要使用者:問卷範本作者(以選單能力控管,非專案層角色,見 _overview §5)。清單僅顯示範本(is_snapshot=0),看不到消費端快照。

角色速覽(作者端以選單能力控管,非專案 manager/auditor,見 _overview §5):

角色 一句話
問卷作者 有問卷管理選單能力者——本頁全部操作(實際 endpoint 僅 jwt_required,無角色細分,見 §3)

1.1 功能總覽(本頁全部功能)

# 功能 說明 位置 詳述
1 資料夾樹 樹狀分類、搜尋、「所有資料夾」;每夾顯示問卷計數 左側邊欄 UC-SVM-01
2 資料夾 CRUD 新增 / 編輯 / 刪除資料夾 左側邊欄 dialog 新增 / 更名 UC-SVM-05;刪除 UC-SVM-03
3 批次刪除資料夾 勾選多夾一次刪 左側邊欄 §6.2
4 統計腳 已發布 / 草稿 / 範本 / 全部計數,可點擊切篩選 左側邊欄底 §10
5 問卷清單(grid / table) 卡片或表格;後端分頁;顯示狀態、版本、頁 / 題數、建立者 主內容區 UC-SVM-01
6 關鍵字搜尋 依問卷名稱模糊搜尋 工具列 UC-SVM-01
7 狀態篩選 全部 / 已發布 / 草稿 / 範本 工具列 UC-SVM-01
8 檢視切換 grid ⇄ table 工具列 §5
9 建立問卷 SplitButton:新增空白 / 從樣板複製 / 批量匯入 工具列 UC-SVM-02
10 進設計器 點名稱 / 卡片 menu「編輯」→ 問卷設計器 名稱 / menu 問卷設計器
11 預覽 卡片 menu「預覽」→ 唯讀預覽 卡片 menu 問卷填寫 / 預覽
12 複製問卷 深度複製(含頁 / 題 / 翻譯)到指定資料夾 卡片 menu / SurveyPickerDialog UC-SVM-02
13 移動問卷 改所屬資料夾 卡片 menu → dialog UC-SVM-05
14 發布 / 取消發布 切換 release;發布後才能被任務選用 設計器 / 狀態 UC-SVM-02
15 刪除問卷 軟刪除(FE 依 hasAnswer 擋,BE 無答案守門) 卡片 menu / table UC-SVM-03
16 批次刪除問卷 勾選多筆一次刪 工具列 UC-SVM-03
17 批量匯入 Excel 三 sheet 匯入(可更新既有問卷) BatchImportDialog UC-SVM-04

UC(§2)已展開本頁所有具實質後端邏輯的功能:瀏覽 / 搜尋 / 篩選(1/5/6/7)、建立 / 複製 / 另存新版 / 發布(9/12/14)、刪除問卷 / 資料夾(15/16/2 刪除路徑)、批量匯入(17)、資料夾建立 / 更名 + 問卷移動(2 建立更名 / 13)。純顯示 / 導覽類(4 統計腳、8 檢視切換)見 §5 / §10;進設計器 / 預覽(10/11)另屬所屬頁面 spec,於此只交叉連結不重複。

2. Use Case

角色速覽(作者端以選單能力控管,非專案 manager/auditor,見 _overview §5):

角色 一句話
問卷作者 有問卷管理選單能力者——本頁全部操作(實際 endpoint 僅 jwt_required,無角色細分,見 §3)

流程圖視覺慣例:菱形 = 判斷、橘底 = 例外 / 擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。

UC-SVM-01 瀏覽 / 搜尋 / 篩選問卷

項目 內容
角色 問卷作者
前置條件 已登入且有問卷管理選單能力
產出 / 後置條件 資料夾樹 + 分頁問卷清單(僅範本 is_snapshot=0is_delete=0);切資料夾以 reqId 防亂序覆寫
uc_svm_01_load UC-SVM-01 載入問卷管理頁(資料夾 + 清單並行) start 進入 /survey/manage f1 fetchFolders() POST /survey-folders {is_delete:0} start->f1 s1 fetchSurveys() POST /surveys {pager, sort, filters:{is_delete:0}} start->s1 f2 渲染資料夾樹 + 統計 f1->f2 s2 BE 過濾 is_snapshot=0(只回範本、不回快照) 每列 enrich has_answer / 建立者暱稱 s1->s2 race 切資料夾連發多次 fetchSurveys? s1->race s3 mapSurveyListFromApi → grid/table 渲染 s2->s3 race->s3 否(最新請求才寫入 state) drop 舊 reqId ≠ 最新 → 丟棄過期回應 race->drop 是(回應亂序)
UC-SVM-01:資料夾與清單並行載入,切資料夾以遞增 reqId 丟棄過期回應

UC-SVM-02 建立 / 複製 / 另存新版 / 發布

項目 內容
角色 問卷作者
前置條件 目標資料夾存在(複製 / 匯入需選資料夾)
產出 / 後置條件 新增空白→建 survey(含初始頁)並導向設計器;複製→深度 clone;另存新版→同 group_idversion_no 列;發布→release=1
uc_svm_02 UC-SVM-02 建立 / 複製 / 另存新版 / 發布 pick 新增問卷 ▾ blank 新增空白問卷 POST /survey(含初始 page) group_id = 新 survey.id pick->blank tmpl 從樣板複製 / 複製問卷 POST /survey/{uid}/clone {name, folder_uid} pick->tmpl imp 批量匯入 POST /survey/excel-import (FormData) 讀 Info/Pages/Questions 三 sheet pick->imp goto 導向問卷設計器 /survey/designer/{uid} blank->goto done clone 名稱重複? tmpl->done ver (設計器中)另存新版 POST /survey as_new_version=true + group_id 保留舊版、新增 version_no 列 goto->ver rel 發布 / 取消發布 PUT / DELETE /survey/{uid}/release goto->rel done->goto err SURVEY_409002 問卷已存在 done->err
UC-SVM-02:新增空白 / clone / 匯入 / 另存新版 / 發布 各路徑,clone 同名擋 SURVEY_409002

UC-SVM-03 刪除問卷 / 資料夾

項目 內容
角色 問卷作者
前置條件 問卷:FE 依 hasAnswer 決定是否允許刪(有答案則前端擋)
產出 / 後置條件 問卷 / 資料夾皆軟刪除(is_delete=1);⚠ BE 端 DELETE /survey/{uid} 不再檢查答案、資料夾刪除不檢查是否含問卷(見 §12)
uc_svm_03 UC-SVM-03 刪除問卷 / 資料夾(守門在 FE,BE 無答案守門) del 刪除問卷(卡片 menu / table 操作) chk FE 檢查:survey.hasAnswer? del->chk block FE toast 阻止、不送請求 (此問卷已有填寫資料) chk->block send DELETE /survey/{uid} chk->send soft BE 軟刪除 is_delete=1 (BE 不再檢查 has_answer) send->soft note ⚠ BE 無答案守門 GRC_SURVEY_HAS_ANSWERS 只在 規劃頁任務移除問卷時擋(§12) soft->note folder 刪除資料夾 fsoft DELETE /survey-folder/{uid} 無條件軟刪除 is_delete=1 (不檢查是否含問卷,§12) folder->fsoft
UC-SVM-03:刪除守門只在 FE,BE 軟刪除無答案 / 非空守門

UC-SVM-04 批量匯入問卷(Excel 三 sheet)

項目 內容
角色 問卷作者
前置條件 目標資料夾存在(folder_uid);xlsx 含 Info / Pages / Questions 三 sheet
產出 / 後置條件 解析後寫入 surveys / survey_pages / survey_questions(含 locale 時寫翻譯表);帶 survey_uid 則更新既有問卷、否則建立新問卷;缺欄擋 TASK_SURVEY_400001
uc_svm_04 UC-SVM-04 批量匯入問卷(Excel 三 sheet;可更新既有) open 開 BatchImportDialog 選檔案 + 目標資料夾 folder_uid mode 更新既有問卷? open->mode withuid 帶 survey_uid(更新既有) mode->withuid nouid 不帶 survey_uid(建立新問卷) mode->nouid post POST /survey/excel-import (FormData) file + folder_uid (+ survey_uid, locale) withuid->post nouid->post parse BE 讀 xlsx 三 sheet Info / Pages / Questions post->parse chk Excel 欄位齊全?(含 No / Question) parse->chk err TASK_SURVEY_400001 Excel 必含 No / Question 欄 chk->err write 寫入 surveys / survey_pages / survey_questions (含 locale 時寫翻譯表) chk->write done 重載 folders + surveys 清單 write->done
UC-SVM-04:上傳 Excel → 解析三 sheet → 缺 No/Question 欄擋 TASK_SURVEY_400001 → 寫入(帶 survey_uid 則更新既有)→ 重載清單

UC-SVM-05 資料夾建立 / 更名 + 問卷移動

項目 內容
角色 問卷作者(寫入僅 @jwt_required(),無角色守門,見 §3 / §12)
前置條件 建立 / 更名:提供 name;移動:目標資料夾存在
產出 / 後置條件 survey_folders 增改(同名擋 SURVEY_409007;含 localesurvey_folders_trans);移動改 surveys.folder_id。資料夾刪除見 UC-SVM-03
uc_svm_05 UC-SVM-05 資料夾建立 / 更名 + 問卷移動(刪除見 UC-SVM-03) fnew 新增資料夾 POST /survey-folder {name, description} fchk 資料夾名稱重複? fnew->fchk fedit 編輯資料夾 PUT /survey-folder/{uid} {name, description} fedit->fchk ferr SURVEY_409007 資料夾已存在 fchk->ferr fwrite 寫入 survey_folders (含 locale 時寫 survey_folders_trans) fchk->fwrite reload 重載資料夾樹 / 清單 fwrite->reload mv 移動問卷(卡片 menu → dialog) mpost POST /survey/move 改所屬 folder_id mv->mpost mwrite 寫入 surveys.folder_id mpost->mwrite mwrite->reload note ⚠ 寫入只有 @jwt_required() 無角色守門(§3 / §12) reload->note
UC-SVM-05:新增 / 編輯資料夾(同名擋 SURVEY_409007)與問卷移動(POST /survey/move 改 folder_id);寫入無角色守門

3. 權限矩陣

作者端角色基調見 _overview §5。本頁實際檢查點(FE 判定與 BE 強制分欄並列):

操作 FE 判定 BE 強制 BE 檢查位置
進入 / 讀清單 需 route meta.requiresAuthconfig/router/index.js:710 @jwt_required();無角色細分 api/survey/routes/survey_route.py::SurveysRoute
資料夾 / 問卷 CRUD 選單能力顯示按鈕 @jwt_required()無 manager / 角色守門 api/survey/routes/survey_route.pysurvey_folder_route.py(全部僅 jwt)
刪除問卷 survey.hasAnswer 隱藏 / 阻止 僅驗證存在→軟刪除,不檢查答案 jedi_survey/app/service/survey.py::delete_survey:197(無 guard)
刪除資料夾 僅驗證存在→軟刪除,不檢查是否含問卷 jedi_survey/domain/service/survey_folder_domain_service.py::delete_folder:116

⚠️ 已知安全缺口(維護時注意):作者端問卷 / 資料夾的寫入與刪除只有 JWT、無角色守門——任何登入者只要能到此頁就能改 / 刪任一問卷。清單另附的 GRC_SURVEY_HAS_ANSWERS 守門只保護規劃頁任務移除問卷的路徑app/grc/service/job_service.py:374),不保護本頁刪除。收進 §12。

4. 狀態機與前置條件

本頁的問卷狀態是作者端發布生命週期surveys.release,見 _overview §3);消費端填答狀態機不在此頁。

條件 效果 出處
release=0 草稿;統計 / 篩選歸「草稿」 surveys.release(db_schema)
release=1 + release_at + release_target 已發布;可被規劃頁任務選用(/surveys/menu PUT /survey/{uid}/releasesurvey_route.py
is_sample=1 標為範本;「從樣板複製」清單來源(/surveys/menu?type=1 surveys.is_sample
is_delete=1 軟刪除,清單以 is_delete=0 過濾不再出現 delete_survey(軟刪除)
is_snapshot=0 清單只回範本列;快照列(消費端)永不出現在管理頁 survey_route.py:71 filters

5. UI 設計

📸 實際畫面截圖待補(一律亮色模式拍攝;截圖另有專責 session)。預計補到 assets/img/: ① 整頁(左資料夾樹 + grid 卡片)manage-overview.png ② 卡片三點選單(編輯 / 預覽 / 複製 / 移動 / 刪除)manage-card-menu.png ③ 建立問卷 dialog manage-create-dialog.png ④ BatchImportDialog manage-import-dialog.png

版面骨架與區塊職責(工具列、左資料夾樹+統計腳選夾 → 右問卷清單分頁,建立/匯入 dialog;彩色區塊可點跳對應小節錨點,API 僅標代表性):

工具列:關鍵字搜尋 ・ 狀態篩選(全部/已發布/草稿/範本)・ grid ⇄ table ・ [建立問卷 ▾] ・ 批次刪除 POST /surveys(pager + filters,enrich has_answer;§6 載入類) 左資料夾樹選夾 → 右問卷清單分頁載入(reqId 丟棄過期回應) 跳至 §2 Use Case(UC-SVM-05 資料夾建立/更名/移動・UC-SVM-01 瀏覽搜尋) 左資料夾樹 + 統計腳 樹狀分類/搜尋/計數 資料夾 CRUD・批次刪除 腳:已發布/草稿/範本 篩選 POST /survey-folders POST/PUT/DELETE /survey-folder → 點我跳 §2 UC-SVM-05/01 跳至 §2 Use Case(UC-SVM-02 建立/複製/另存新版/發布・UC-SVM-03 刪除) 問卷清單(grid 卡片 ⇄ table,後端分頁) 顯示狀態/版本/頁題數/建立者 卡片 menu:編輯/預覽/複製/移動/刪除 POST /survey/{uid}/clone ・ POST /survey/move PUT /survey/{uid}/release ・ DELETE /survey/{uid} → 點我跳 §2 UC-SVM-02/03 建立問卷 dialog(SplitButton) 新增空白/從樣板複製 POST /survey(含初始頁/clone) BatchImportDialog Excel 三 sheet 匯入(可更新既有) POST /survey/excel-import 彩色=可點跳本頁小節錨點(框內標錨點+代表性 API)|灰=本頁描述|虛線框=依操作才開的 dialog 層 API 僅列代表性 2~3 條,完整規格見 §6
問卷管理頁版面 wireframe:工具列、左資料夾樹+統計腳、grid/table 問卷清單、建立問卷與 BatchImportDialog 彈窗群

狀態呈現:grid 卡片頂色條依狀態(發布 / 草稿 / 範本);grid / table 由 viewMode 切換;勾選列出現時工具列才顯示批次刪除;卡片 menu 的「刪除」對 hasAnswer=true 的問卷 toast 阻止。切資料夾時多次 fetchSurveys 以遞增 reqId 只保留最新回應(useSurveyApi.js:103-124)。無 socket(作者端管理頁不即時協作;即時協作只在填寫頁)。

6. API 規格

Envelope:成功 {"status": true, "data": …, "meta": …}(清單帶分頁 meta)、失敗 {"error_code": "…", "msg": "…"}

6.1 總清單(本頁呼叫的全部 endpoint)

分類 Method + Path 說明 完整規格
資料夾 POST /survey-folders 資料夾清單({is_delete:0},直接回陣列無 meta) §6.2
資料夾 GET /survey-folders/menu 輕量資料夾下拉 GAI-SD-02
資料夾 POST /survey-folder・PUT /survey-folder/{uid}・DELETE /survey-folder/{uid} 資料夾 CRUD §6.2
清單 POST /surveys 分頁問卷清單(pager + sort + filters;enrich has_answer) §6.2
選單 GET /surveys/menu?type=&release=&is_sample= 輕量問卷下拉(樣板清單來源) GAI-SD-02
詳情 GET /survey/{uid} 單一問卷(含 pages / questions,供設計器 / 預覽) designer
建立 POST /survey 建立(含初始頁);as_new_version=true+group_id = 另存新版 §6.2
更新 PUT /survey/{uid} 全量更新(含 pages 結構) designer
刪除 DELETE /survey/{uid} 軟刪除(無答案守門) §6.2
移動 POST /survey/move 改所屬資料夾 §6.2
複製 POST /survey/{uid}/clone 深度複製(頁 / 題 / 翻譯) §6.2
發布 PUT /survey/{uid}/release・DELETE /survey/{uid}/release 發布 / 取消發布 §6.2
匯入 POST /survey/excel-import Excel 三 sheet 匯入(FormData) §6.2
匯入 GET /survey/import-template 下載匯入 Excel 範本 GAI-SD-02

6.2 核心 endpoint(欄位對照 serializer / service,出處標段末)

[POST] /surveys — 分頁清單

Request:

{
  "pager": {
    "page": 0,
    "page_size": 0,
    "with_total": true
  },
  "sort": [
    {
      "field": "string",
      "order": "string"
    }
  ],
  "filters": {
    "is_delete": 0  // 其他 filter 條件
  }
}

BE 強制 is_snapshot=0(只回範本)。

Response data(陣列,每項 = 問卷 DTO)核心欄位:

欄位 型別 說明
uid string 問卷 UUID
name string 名稱
description string 描述
folder_id int 所屬資料夾
version_no int 版本號
group_id int 問卷群組 ID(版本序列)
release int 發布旗標
release_at timestamp 發布時間
release_target string 發布對象
is_sample int 是否為範本
page_count int 頁數(meta 彙總)
question_count int 題數(meta 彙總)
has_answer bool 是否有填答(question_answer_service.survey_has_answer(survey.id) enrich)
created_user(_name) string 建立者 login_name(_name = nickname)
updated_user(_name) string 更新者 login_name(_name = nickname)

出處:api/survey/routes/survey_route.py:54-96SurveysRoute + has_answer enrich)、DTO jedi_survey SurveyEntity。

[POST] /survey ・ [PUT] /survey/{uid} — 建立 / 更新

Request(建立):

欄位 型別 必填 說明
name string 問卷名稱
folder_uid string 所屬資料夾
description string 描述
release_target string 發布對象
release_at date YYYY-MM-DD
release int 0/1
is_sample int 0/1
pages[] array 建立時 FE 送單一初始頁 {name:'page_structure', no:'#'}
as_new_version bool 另存新版:加 as_new_version=true + group_id(保留舊版、產生新 version_no 列)
group_id int 另存新版時指定的問卷群組 ID

release_at 於 route 層 datetime.strptime(...[:10]) 只取日期。出處:api/survey/routes/survey_route.py:99-141jedi_survey/app/service/survey.py::add_survey

[POST] /survey/{uid}/clone — 深度複製

Request:

{
  "name": "string",
  "folder_uid": "string"  // 選填
}

深度複製 pages / questions / _trans 翻譯,新問卷 group_id = 新 survey.id(獨立版本序列)。同名擋 SURVEY_409002(問卷已存在)。出處:app/survey/service/survey_clone_service.py

[PUT] /survey/{uid}/release ・ [DELETE] .../release — 發布 / 取消

PUT body {release_target}release=1 + release_at;DELETE → release=0。出處:api/survey/routes/survey_route.py::SurveyReleaseRoute

[DELETE] /survey/{uid} — 軟刪除

無 body。BE 僅驗證存在後 is_delete=1不檢查 has_answer(FE 才擋)。出處:api/survey/routes/survey_route.py:145-149jedi_survey/app/service/survey.py::delete_survey:197

資料夾 CRUD

POST /survey-folder {name, description};PUT /survey-folder/{uid} {name, description}(同名擋 SURVEY_409007);DELETE /survey-folder/{uid} 無條件軟刪除(不檢查含問卷)。審計欄位 enrich created_user_name / updated_user_name。出處:api/survey/routes/survey_folder_route.pyjedi_survey/app/service/survey_folder.py

[POST] /survey/excel-import — 批量匯入

FormData:

欄位 型別 必填 說明
file file xlsx,三 sheet:Info / Pages / Questions
folder_uid string 匯入目標資料夾
survey_uid string 選填,更新既有問卷
locale string 語言

缺欄擋 TASK_SURVEY_400001(Excel 必含 No / Question 欄——共用匯入驗證)。出處:api/survey/routes/survey_route.py::SurveyExcelImportRoute

7. 前端檔案地圖(compliance-manager-fe/)

檔案 角色
src/views/survey-v2/SurveyManage.vue 主檢視(資料夾樹 + 清單 + 建立 / 移動 / 匯入彈窗)
src/views/survey-v2/components/FolderTree.vueFolderTreeNode.vue 資料夾樹(CRUD / 批次刪 / 右鍵選單)
src/views/survey-v2/components/SurveyPickerDialog.vue 從樣板複製 / 複製問卷選擇器(template / copy mode)
src/views/survey-v2/components/BatchImportDialog.vue Excel 批量匯入(檔案 / 資料夾 / 語言 / 更新既有)
src/views/survey-v2/composables/useSurveyApi.js 作者端 API composable(singleton state:folders / surveys;reqId 防亂序)
src/views/survey-v2/utils/survey-mapper.js API ↔︎ FE 欄位轉換(mapSurveyListFromApi 等)
src/stores/surveyStore.js 問卷相關 store
src/config/router/index.js:713-716 route survey-v2-manage/survey/manage
src/config/api/api.js endpoint 常數(SURVEY / SURVEYS / SURVEY_FOLDER(S) / SURVEY_MOVE / SURVEY_EXCEL_IMPORT …)
src/config/locales/i18n/*/survey-v2.json 頁面 i18n

8. 後端檔案地圖(本頁核心鏈路)

鏈路 Route App Service 底層
清單 api/survey/routes/survey_route.py::SurveysRoute app/survey/service/survey_app_service.py(enrich 暱稱 + has_answer) jedi_survey SurveyService → repo
建立 / 更新 / 刪除 同檔 SurveyRoute jedi_survey/app/service/survey.py jedi_survey domain / repo
複製 SurveyCloneRoute app/survey/service/survey_clone_service.py 深度複製 pages / questions / trans
資料夾 survey_folder_route.py jedi_survey/app/service/survey_folder.py survey_folder_domain_service
has_answer app/task_survey/service/question_answer_service.py::survey_has_answer survey.question_answers 查詢

DDD 提醒:問卷 domain / repo 在 jedi_survey 外部套件;主專案 route 只做 HTTP 邊界 + audit 欄位 enrich。作者端 route 全部僅 @jwt_required()未加角色守門(見 §3 缺口)——新增寫入端點時勿比照,應補角色檢查。

9. DB

9.1 資料表總清單(本頁讀寫的全部表)

讀/寫 說明 欄位詳述
survey.surveys 讀 / 問卷主檔(範本列 is_snapshot=0);刪除改 is_delete=1 §9.3
survey.survey_folders 讀 / 資料夾(樹 pid);刪除改 is_delete=1 §9.3
survey.survey_pages 建立時初始頁;複製 / 匯入時整批寫 designer
survey.survey_questions 複製 / 匯入時整批寫 designer
survey.surveys_trans / survey_folders_trans 翻譯表(含 locale 時) §9.3
survey.question_answers has_answer 判定(survey_has_answer fill

9.2 ER 圖(本頁讀寫範圍)

svm_er 問卷管理頁讀寫範圍(survey schema) folders survey.survey_folders 讀 / 寫(樹 pid) surveys survey.surveys 讀 / 寫 folder_id·group_id·version_no release·is_sample·is_delete folders->surveys folder_id pages survey.survey_pages 寫(建立時初始頁) surveys->pages survey_id ftrans survey.survey_folders_trans survey.surveys_trans 寫(含 locale 時) surveys->ftrans 翻譯 qa survey.question_answers 讀(has_answer 判定) surveys->qa survey_id survey_has_answer() questions survey.survey_questions 寫(複製 / 匯入) pages->questions page_id
問卷管理讀寫範圍:藍 = 主檔與資料夾(軟刪除)、綠 = has_answer 唯讀來源、黃 = 翻譯表

9.3 核心表欄位(取自 DEV DB dump,含 DB comment)

survey.surveys — 問卷主檔(20 欄)

欄位 型別 說明
uid varchar(36) 問卷唯一識別碼 UUID
folder_id int 所屬資料夾(→ survey_folders.id,ON DELETE CASCADE
group_id int|null 問卷群組 ID(版本控制:同 group 為同問卷多版本)
name / description varchar(255) / text 名稱 / 描述
version_no int|null 版本號
release int|null 是否已發布(1 已發布 / 0 未發布)
release_at / release_target timestamp / varchar(500) 發布時間 / 發布對象
is_sample int|null 是否為範本(1 是 / 0 否)
is_snapshot int NOT NULL 是否為快照(1 快照 / 0 範本)——管理頁只查 0
source_survey_id int|null 來源問卷 ID(快照專用)
is_delete int|null 軟刪除(1 已刪 / 0 未刪)
tenant_id / org_unit_id int|null 租戶 / 組織單位(RLS + FK RESTRICT)

survey.survey_folders — 資料夾(樹)

欄位 型別 說明
uid varchar(36) 資料夾 UUID
pid int|null 父資料夾(→ survey_folders.id 自參照)
name / description varchar(255) / varchar(500) 名稱 / 描述(description NOT NULL)
is_delete int|null 軟刪除
tenant_id / org_unit_id int|null 租戶 / 組織單位

翻譯表 survey.surveys_trans / survey_folders_trans

surveys_transsurvey_id / name / description / release_target / language_codesurvey_folders_transsurvey_folder_id / name / description / language_code。寫入含翻譯欄位要傳 locale(見 _overview §6.3 / docs/claude/translatable-fields.md)。

10. 頁面邏輯與資料對應

載入時序:見 §2 UC-SVM-01(資料夾與清單並行,reqId 防亂序)。

關鍵欄位對應(API ↔︎ 畫面):

畫面元素 FE state API 欄位
資料夾樹節點 folders / folderTree /survey-folders 陣列(mapFolderFromApi
選中資料夾篩選 selectedFolder filters.folder_id(切夾重查)
搜尋框 filterText filters(名稱模糊)
狀態篩選鈕 filterTab 前端依 release / is_sample 分類
卡片 / 列狀態 release(發布 / 草稿)、is_sample(範本)
版本標籤 version_no
頁 / 題數 page_count / question_count
刪除可否 survey.hasAnswer has_answer

儲存流程:建立 dialog → POST /survey → 成功導向設計器;複製 / 移動 / 發布 / 刪除確認後呼叫對應 endpoint → 重查清單。批量匯入成功後重載 folders + surveys。

錯誤對應:BE error envelope → toast。常見碼:SURVEY_404002(問卷不存在)、SURVEY_409002(複製同名)、SURVEY_409007(資料夾同名)、TASK_SURVEY_400001(匯入 Excel 缺 No/Question 欄)。

11. 背景行為與外部依賴

類型 內容
通知 本頁通知(通知在填寫頁狀態轉換時發,見 fill §11
Job 無背景 job;匯入為同步解析
Socket 本頁 socket / 輪詢;清單更新靠操作後重查
jedi-* 套件 jedi-survey(問卷 domain / repo / 錯誤碼 / 匯入解析)、jedi-auth(JWT / RLS session 變數)
系統參數 無 feature flag;grid / table 檢視為前端偏好

12. 邊界情況與已知坑

  1. 作者端寫入無角色守門(安全缺口):問卷 / 資料夾的建立 / 更新 / 刪除 route 只有 @jwt_required(),無 manager / 能力層強制——任何能到此頁者皆可改 / 刪任一問卷。修法應在 route 或 app service 補角色 / 能力檢查。
  2. 刪除問卷 BE 無答案守門DELETE /survey/{uid} 只軟刪除,不檢查 has_answer;守門只在 FE(survey.hasAnswer 擋)。GRC_SURVEY_HAS_ANSWERS(GRC_409033)只保護規劃頁任務移除問卷的路徑(job_service.py:374),與本頁刪除無關。繞過 FE 直接呼叫可刪掉已有填答資料的範本。
  3. 刪資料夾不檢查是否含問卷delete_folder 無條件軟刪除;夾內問卷 folder_id 仍指向已軟刪除的資料夾(孤兒)。清單以資料夾 is_delete=0 過濾,被孤立問卷可能在樹上找不到入口。
  4. 範本 vs 快照同表survey.surveys 同時放範本列(is_snapshot=0)與消費端快照列(is_snapshot=1);管理頁一律 is_snapshot=0,別在此頁查 / 改到快照。
  5. 版本語意:「另存新版」= 保留舊版 + 同 group_idversion_no 列;「複製」= 全新 group_id(獨立序列)。兩者都不影響已綁定任務的快照(快照綁定當下已脫鉤)。
  6. 切資料夾亂序fetchSurveys 以遞增 reqId 保證只有最新請求寫入 state(useSurveyApi.js:103-124);慢回的舊請求丟棄——維護分頁 / 篩選邏輯時勿破壞此保護。
  7. 可翻譯欄位:改 name / description / release_target 未傳 locale 只更新主表;讀取走 failover 優先翻譯表 → 前端可能看不到主表變更(見 docs/claude/translatable-fields.md)。

13. 開發與驗證

  • 跑起來:BE python main_socketio.py(port 8000,log 在 log/app.log);FE 在 compliance-manager-fe/ 起 Vite dev server。BE 改 service code 後必須重啟(無 hot reload)
  • 測試帳號:dev 環境登入帳號密碼見 .env / 部署文件
  • 導航路徑:登入 → 側邊選單「問卷」→ 問卷管理(/survey/manage
  • 前置資料:至少一個資料夾才能建問卷;「從樣板複製」需有 is_sample=1 的範本
  • E2Ecompliance-manager-test/ repo(Cucumber + Playwright);以 問卷 / survey 搜尋 feature 檔
  • 相關文件:題目編輯見 問卷設計器、填答 / 預覽見 問卷填寫 / 預覽、任務綁定見 project-task-edit.md §6.2;DB / API 全量見 GAI-SD-02/03