AO 任務配置與啟動(Task Config)
功能群:專案管理|上層見 專案規劃頁(本頁=其 Tab0「控制項實作」的 AO 任務配置,規劃頁功能總覽第 4/5/6 項)
事實基準:2026-07-06 從 FE / BE code 掃出;表結構對
db_schema.json、endpoint 對routes.json、欄位對 marshmallow serializer + service 組裝碼逐條核|變更紀錄:2026-07-06 FR-047 從 project-planning.md 拆出並納入快速配置 / 開始執行
1. 功能描述
AO 任務(prep-job)是「為每個控制項評估目標(AO)蒐集證據」的工作項。本頁涵蓋任務域的三件事:①單一任務逐欄配置(inline 編輯)、②快速配置(一鍵把多位人員批次併入所有可編輯任務)、③開始執行(PM 把已指派任務批次轉 PROCESSING 並通知)。
規劃頁 Tab0 的左樹選到某個 AO 後,右面板下方列出該 AO
的任務卡;每張卡片直接 inline 編輯(非獨立
Dialog):任務名稱 / 描述 / 指南、任務類型(一般 general /
問卷
survey)、指派人員(可標審核人)、部門、設備、問卷(可標審批要求)。儲存以
PUT job 一次寫回,關聯欄位採
full-replace 語意(送 [] 清空、省略 =
不動)。快速配置與開始執行是 Header
的批次動作:前者 FE 端逐一 PUT 合併、後者呼叫專屬
start-task-execution endpoint。
任務真相來自範本 BPMN 的
userTask(見規劃頁背景);儲存時 BE 會在同一 transaction
同步回寫 BPMN userTask,讓 BPMN 編輯器與規劃頁兩端一致。與 SSP
編輯不同,任務指派不受 SSP 凍結影響,只要輪次在
planning 階段、使用者對該節點有三層遞進的 manager
權即可編。
主要使用者:對該控制節點有 manager 權者(專案 / 群組 / 控制項任一層 manager)。
角色速覽(完整定義見 _overview §3):
| 角色 | 一句話 |
|---|---|
| manager(三層任一) | 專案 / 群組 / 控制項 manager —
可編此節點下的任務(canEdit 遞進,見規劃頁 §3) |
| 參與者 | auditor / reviewer / viewer — 可讀不可寫 |
1.1 功能總覽(本頁全部功能)
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 編輯任務基本文案 | 名稱 / 描述 / 指南 + 類型(general / survey) | 任務卡 inline | UC-TE-01 |
| 2 | 指派人員(含審核人) | MultiSelect 指派;survey
型可標審核人(is_approver) |
任務卡 | UC-TE-01 |
| 3 | 部門 / 設備關聯 | MultiSelect 綁部門(org_unit)/ 設備(device) | 任務卡 | UC-TE-01 |
| 4 | 問卷關聯(含審批) | 僅 survey 型;MultiSelect 選問卷,每張可標「審批要求」 | 任務卡 | UC-TE-01 / §12 |
| 5 | 儲存任務 | 一次 PUT,關聯 full-replace,同步 BPMN | 每卡「儲存」 | §6.2 |
| 6 | 快速配置 | 多選人員一鍵 additive 併入所有可編輯 AO 的任務(FE 逐一 PUT) | Header 按鈕 → dialog | UC-PP-02 / §12 |
| 7 | 開始執行任務 | PM 把「已指派且 TODO」的任務批次轉 PROCESSING 並通知(idempotent) | Header 按鈕(限 PM) | UC-PP-02 |
UC(§2)展開兩條核心路徑:UC-TE-01(單一任務逐欄 inline 編輯,含 M2M / 審核人坑)與 UC-PP-02(快速配置 → 開始執行的批次流)。快速配置 ⚠️ 有清空關聯的坑(見 §12)。
2. Use Case
角色速覽(同 §1)。流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-TE-01 編輯 AO 任務
| 項目 | 內容 |
|---|---|
| 角色 | 對該節點有 manager 權者 |
| 前置條件 | 輪次 AP=planning;已載入
control-tree(任務內嵌於樹);canEdit 為真 |
| 產出 / 後置條件 | 寫 job_executions(本體)+ reconcile 四關聯表(指派 /
部門 / 設備 / 問卷);同步 BPMN userTask;名稱變更 best-effort 觸發
Drive 改名 |
UC-PP-02 快速配置與開始執行
| 項目 | 內容 |
|---|---|
| 角色 | 快速配置:有編輯權者;開始執行:專案 manager |
| 前置條件 | 輪次 = planning;AO 下已有任務(BPMN 範本衍生的 prep-job) |
| 產出 / 後置條件 | 快速配置:對每個可編輯任務 additive 併入指派人員後逐一
PUT job;開始執行:compliance.task_assignees
等已備妥後 job_executions.status
TODO→PROCESSING、站內通知送出 |
3. 權限矩陣
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 讀任務 | 參與者即可(隨 control-tree 一起載) | require_participant(讀樹) |
規劃頁 control-tree 路徑 |
| 編輯 / 儲存任務 | 三層遞進 canEdit(專案 → 群組 → 控制項
manager,ProjectPlanningView.vue:663-679) |
⚠️ 無角色守門(route 僅
@jwt_required()) |
api/grc/routes/job_route.py:98(route);service
app/grc/service/job_service.py |
| 快速配置 | 同編輯(逐一 PUT job) | 同上(每筆 PUT 無角色守門) | FE
executeQuickConfig(ProjectPlanningView.vue:1192) |
| 開始執行任務 | isProjectManager 才顯示按鈕 |
_check_manager(須專案 manager,否則
GRC_403002) |
app/grc/service/task_execution_service.py(_check_manager:8-17) |
⚠️ FE / BE 權限不對齊:FE 用三層遞進的
canEdit擋按鈕,但PUT jobroute 層只有 JWT 登入、無任何角色檢查——任一登入者繞過 FE 直接打 API 都能改任務指派。與規劃頁 §3 / §12 記載的同一 finding。唯有「開始執行」有 BE manager 守門(GRC_403002)。
4. 狀態機與前置條件
承接輪次階段 gate(見 規劃頁 §4):
| 條件 | 效果 | 出處 |
|---|---|---|
apStatus === 'planning' |
任務可編(不受 SSP 編輯凍結影響) | 規劃頁 FE 行為 |
!canEdit(非三層 manager) |
任務卡全部欄位 disabled | ProjectPlanningView.vue(canEdit 綁
:disabled,約 :1953-2029) |
類型 = survey |
才顯示問卷 MultiSelect + 審批 / 審核人 switch | 同檔(jobType==='survey' gate,約 :1985/:2015) |
5. UI 設計
版面骨架(Header 批次動作 + AO 任務卡 inline 編輯,含 survey 型條件層 + 代表性 API):
📸 截圖待補(亮色模式):AO 面板 + 任務卡展開(general 型)
taskedit-general.png、survey 型(含問卷 + 審批 + 審核人 switch)taskedit-survey.png。(規劃頁 §5 已有planning-ao-tasks.png概覽可參考)
任務卡 inline 表單(ProjectPlanningView.vue Panel
卡片,無獨立 Dialog)。欄位:
| # | i18n label | 型別 | 必填 | 列舉 / 來源 | payload 欄位 | disabled | 備註 |
|---|---|---|---|---|---|---|---|
| 1 | task_name_label 任務名稱 |
InputText | 否 | — | name |
!canEdit |
|
| 2 | task_desc_label 描述 |
Textarea(autoResize) | 否 | — | description |
!canEdit |
|
| 3 | task_guide_label 指南 |
Textarea(autoResize) | 否 | — | guide |
!canEdit |
|
| 4 | 任務類型 | SelectButton | 否 | general / survey |
job_type |
!canEdit |
切 survey 才顯示問卷區 |
| 5 | assignees_label 指派人員 |
MultiSelect(chip) | 否 | /users/menu({id,name}) |
assignees[{uid,is_approver}] |
!canEdit |
dataKey=id |
| 5a | is_approver_label 審核人 |
InputSwitch(每位人員) | 否 | — | assignees[].is_approver |
!canEdit |
僅 survey 型且有問卷開審批時顯示 |
| 6 | departments_label 部門 |
MultiSelect | 否 | /org-units/menu({id,name}) |
department_uids[] |
!canEdit |
|
| 7 | devices_label 設備 |
MultiSelect | 否 | POST /devices({id,name}) |
device_uids[] |
!canEdit |
|
| 8 | surveys_label 問卷 |
MultiSelect | 否 | /surveys/menu({id,name}) |
surveys[{uid,approval_required}] |
!canEdit |
僅 survey 型顯示 |
| 8a | approval_required_label 審批 |
InputSwitch(每張問卷) | 否 | — | surveys[].approval_required |
!canEdit |
與問卷名並列 |
FE 別名坑:指派人員的審核旗標,FE 早期送
is_admin,serializerAssigneeInputSchema以@post_loadcoalesce 進is_approver(api/grc/serializers/job.py:105-114)。新資料一律is_approver。
狀態呈現:無 socket;儲存中 per-任務
spinner(taskSavingSet Set);儲存後前端 state
更新(不整頁重載)。
6. API 規格
Envelope:成功 {"status": true, "data": …}、失敗
{"error_code": "…", "msg": "…"}。
6.1 總清單
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 任務 | PUT /grc/project/{project_uid}/job/{job_uid} |
任務更新(關聯 full-replace) | §6.2 |
| picker | GET
/users/menu・/org-units/menu・/surveys/menu、POST
/devices |
指派 / 部門 / 設備 / 問卷下拉 | GAI-SD-02 |
批次啟動
POST /grc/project/{uid}/start-task-execution屬規劃頁功能 6,見 規劃頁 §6.4。
6.2 核心 endpoint
[PUT] /grc/project/{project_uid}/job/{job_uid}
任務更新。全部欄位選填(load_default=None);關聯陣列
full-replace(送 [] 清空、null/省略 =
不動)。Request(JobUpdateRequestSchema,Meta.unknown=EXCLUDE):
{
"name": "訪談紀錄蒐集",
"description": "…",
"guide": "…",
"job_type": "survey",
"control_uid": null,
"assignees": [{ "uid": "<user uid>", "is_approver": true }],
"department_uids": ["<org_unit uid>"],
"device_uids": ["<device uid>"],
"surveys": [{ "uid": "<survey uid>", "approval_required": true }]
}| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| name / description / guide | string|null | 否 | 任務文案 → job_executions |
| job_type | string|null | 否 | OneOf GrcJobType(general /
survey) |
| control_uid | string|null | 否 | 控制層 workflow_execution.uid;可省略,BE 由 job 的 workflow 階層自動解析 |
| assignees | array|null | 否 | [{uid(必填), is_approver(預設 false;相容 is_admin 別名)}]
→ task_assignees |
| department_uids | array[string]|null | 否 | 部門 org_unit uid → job_execution_org_units |
| device_uids | array[string]|null | 否 | 設備 uid → job_execution_devices |
| surveys | array|null | 否 | [{uid(必填), approval_required(預設 null)}] →
survey.task_surveys;送 null
保留既有審批旗標(BPMN
編輯器同步不誤蓋,SurveyInputSchema:117-125) |
出處:request api/grc/serializers/job.py:128-151、route
api/grc/routes/job_route.py:100-136、service
app/grc/service/job_service.py(update_job +
_reconcile_task_surveys:228)
Response data(JobResponseSchema,形狀同
control-tree 的 Job 節點):
| 欄位 | 型別 | 說明 |
|---|---|---|
| id / name / description / guide | string | 任務基本資料(id=job uid) |
| job_type / status | string | 類型 / 狀態 |
| template_job_id | string|null | 範本任務來源 |
| can_revert_job | bool | 可否退回 |
| assignees / departments / devices / surveys | array | 關聯子物件(形狀同 control-tree Job 節點) |
出處:api/grc/serializers/job.py:128-157(JobUpdateResponseSchema
→ JobResponseSchema)
6.3 批次動作
快速配置(無專屬 endpoint,FE 逐一 PUT)
FE-only 編排:多選人員(來源 = 專案參與人員)→
迴圈所有可編輯(canEditCtrl)控制項底下的任務
→ 每個 job 把選取人員 additive
併入既有指派(已存在者跳過)→ 逐一
PUT job。⚠️ 每筆 PUT 送
department_uids: [] / device_uids: [] /
surveys: []——因 full-replace
語意,會清空該任務既有的部門 / 設備 / 問卷關聯(見 §12
坑)。出處:ProjectPlanningView.vue:executeQuickConfig:1192-1230。
[POST] /grc/project/{project_uid}/start-task-execution
無 body。把該專案「已指派且 TODO」的 prep-job 批次轉
PROCESSING 並發站內通知。限專案
manager(_check_manager,否則
GRC_403002);idempotent 可重按。Response
data:{"activated_count": int}(實際轉
PROCESSING 的任務數)。出處:service
app/grc/service/task_execution_service.py(_check_manager:8-17、start_task_execution:22-30)、serializer
api/grc/serializers/task_execution.py:5-8。
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/views/project/ProjectPlanningView.vue |
任務卡 inline 編輯(Panel 卡片、四關聯 MultiSelect、審核人 / 審批
switch、saveTask PUT) |
src/service/BaseService.js |
put(...) 打
/grc/project/{uid}/job/{jobId} |
8. 後端檔案地圖(本頁核心鏈路)
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 任務更新 | api/grc/routes/job_route.py(PUT,僅 JWT) |
JobService.update_job(full-replace reconcile + BPMN
同步,同一 transaction) |
compliance.job_executions + reconcile
task_assignees / job_execution_org_units /
job_execution_devices +
survey.task_surveys(_reconcile_task_surveys) |
⚠️ DDD 提醒:
PUT jobroute 層無角色守門(別比照為權限典範);問卷關聯跨到surveyschema 的task_surveys(非compliance.job_execution_surveys),維護留意 schema 邊界。
9. DB
9.1 資料表總清單(本頁讀寫的全部表)
| 表 | 讀寫 | 說明 | 詳述 |
|---|---|---|---|
| compliance.job_executions | 讀 / 寫 | 任務本體(name / description / guide / job_type) | §9.3 |
| compliance.task_assignees | 讀 / 寫(reconcile) | 指派人員(含 is_approver) |
§9.3 |
| compliance.job_execution_org_units | 讀 / 寫(全刪重建) | 任務↔︎部門 | §9.3 |
| compliance.job_execution_devices | 讀 / 寫(全刪重建) | 任務↔︎設備 | §9.3 |
| survey.task_surveys | 讀 / 寫(reconcile) | 任務↔︎問卷(含 approval_required) |
§9.3 |
9.2 ER 圖
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)
compliance.job_executions — 任務本體
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(50) | 任務 UUID(對外 job id) |
| name | varchar(500) | 任務名稱 ← name |
| description / guide | text | 描述 / 指南 |
| job_type | varchar(50) | general / survey ←
job_type(另有 type jobtype enum 欄) |
| status | jobstatus | 任務狀態(TODO / PROCESSING…) |
| org_unit_id / tenant_id | integer | 租戶 / 組織(RLS) |
compliance.task_assignees — 指派人員
| 欄位 | 型別 | 說明 |
|---|---|---|
| project_id / control_id / task_id | integer | 複合定位 |
| task_uid / task_template_id | varchar | 任務 uid / 範本任務 id |
| user_id | integer | 指派人 |
| role | varchar(50) | 角色 |
| is_approver | integer | 是否審核者(0/1)← assignees[].is_approver |
compliance.job_execution_org_units / job_execution_devices — 部門 / 設備關聯
| 欄位 | 型別 | 說明 |
|---|---|---|
| job_execution_id | integer | → job_executions |
| org_unit_id / device_id | integer | 部門 / 設備(全刪重建 reconcile) |
survey.task_surveys — 問卷關聯(跨 schema)
| 欄位 | 型別 | 說明 |
|---|---|---|
| task_id / survey_id | integer | 任務 / 問卷 |
| approval_required | boolean | 是否需審核 ← surveys[].approval_required |
| device_id / department_id | integer | 問卷施測的設備 / 部門 |
| job_evidence_id | integer | soft ref → job_evidences.id(標記 GRC 建立) |
| is_delete | boolean | 軟刪旗標 |
⚠️ schema 註:問卷關聯在
surveyschema 的task_surveys(job_service._reconcile_task_surveys管理),不是compliance.job_execution_surveys(那是另一張簡單映射表,本更新路徑不寫)。欄位一律取自db_schema.json。
10. 頁面邏輯與資料對應
任務隨 control-tree 一次載入(內嵌於 AO 節點)。欄位對應:
| 畫面元素 | FE state | payload 欄位 → DB |
|---|---|---|
| 名稱 / 描述 / 指南 | task.name / .description /
.guide |
同名 → job_executions |
| 類型 | task.jobType |
job_type → job_executions.job_type |
| 指派人員(含審核人) | task.assignees[{id,name,is_approver}] |
assignees[{uid,is_approver}] →
task_assignees |
| 部門 / 設備 | task.departments / .devices |
department_uids / device_uids → org_units
/ devices 關聯 |
| 問卷(含審批) | task.surveys[{id,name,approval_required}] |
surveys[{uid,approval_required}] →
survey.task_surveys |
儲存流程:saveTask 先 client
檢查「survey 有審批要求但無審核人 → toast warn
不送」(ProjectPlanningView.vue:918-923)→ PUT → 前端 state
更新。錯誤對應:GRC_404005(任務不存在)/
GRC_409033(問卷已有填答無法移除)→ toast。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| socket | 無(儲存後前端 state 更新) |
| BPMN 同步 | update_job 在同一 transaction 回寫 BPMN userTask(name
/ description / guide / job_type / 問卷 actionInfo),維持 BPMN
編輯器一致 |
| Drive | 任務名稱變更 → best-effort 觸發 Drive 資料夾改名(無 Drive 整合則靜默跳過) |
| 依賴 | /users/menu(指派)/
/org-units/menu(部門)/
/surveys/menu(問卷)/ POST
/devices(設備) |
12. 邊界情況與已知坑
PUT job無 BE 角色守門:route 僅@jwt_required(),FE 靠三層canEdit擋;繞過 FE 直打 API 任一登入者都能改任務指派(與規劃頁 §12 同一 finding)。- 審核人檢查只在 FE:「survey
有審批要求則至少一審核人」只在
saveTask前 client 檢查(toast 不送),BE 無此驗證——直打 API 可存出「有審批要求卻無審核人」的任務。 - 問卷關聯跨 schema、非直覺:任務問卷走
survey.task_surveys(_reconcile_task_surveys),不是compliance.job_execution_surveys;後者是另一映射表,本路徑不動。 surveys[].approval_required送 null 保留既有值:SurveyInputSchema的approval_requiredload_default=None——BPMN 編輯器同步問卷時不帶此欄(無此 UI),送 null →_reconcile_task_surveys保留既有審批旗標,不誤蓋規劃頁設定;規劃頁顯式送 True/False 行為不變。is_admin→is_approver別名:FE 早期送is_admin,serializer@post_loadcoalesce 進is_approver;下游只讀is_approver。- 關聯 full-replace
語意:
assignees/department_uids/device_uids/surveys送[]清空、null/省略不動;部門 / 設備關聯是全刪重建(reconcile)。 - 問卷已有填答不可移除:移除已有答案的問卷 evidence →
GRC_409033(GRC_SURVEY_HAS_ANSWERS)。 - ⚠️ 快速配置會清空部門 / 設備 /
問卷關聯:
executeQuickConfig對每個 job 只 additive 合併指派人員,但送department_uids: []/device_uids: []/surveys: []——因 full-replace 語意([]=清空),會把該任務既有的部門 / 設備 / 問卷關聯一併清掉。若已配置這些關聯,用快速配置補人員前要留意會被清空(ProjectPlanningView.vue:1218)。 - survey × device 笛卡爾積:任務掛多問卷多設備時
survey.task_surveys列數會乘開(每問卷 × 每設備一列),指派前想清楚資料量。 - date-only 顯示 08:00 陷阱:任務時間相關欄位 BE
慣例吐
YYYY-MM-DDTHH:MM:00無 tz,前端 date-only 顯示要留意時區偏移(見 _overview 時區慣例)。
13. 開發與驗證
- 跑起來:起 BE + FE;登入見
.env/ 部署文件測試帳號(需該節點 manager;開始執行需專案 manager) - 導航:規劃頁 Tab0 → 左樹選 AO → 右面板任務卡展開編輯;快速配置 / 開始執行在 Header
- 前置資料:需 AP=planning 的專案 + 至少一個控制項有 AO 任務;備 user / org_unit / device / survey master 測四關聯;切 survey 型測問卷 + 審批 + 審核人 switch 連動;測快速配置後檢查部門/設備是否被清空(坑 8)
- 相關:現況編輯 / 程序書見 現況編輯;批次匯入見 批次維護;任務真相=範本 BPMN userTask;問卷設計見 survey 功能群