專案建立(Project Create)
事實基準:2026-07-05 從 FE / BE code 掃出(FR-038 Wave 2B「選 published 資源庫 → clone 三件組」模型);API schema 逐條對照 marshmallow serializer 與
project_start_app_service.start_project;表結構取自db_schema.json
1. 功能描述
專案建立頁是成立一個稽核專案的三步精靈。使用者選一個已發布的合規資源庫(module_frame
三件組)與稽核流程範本、填基本資料與參與人員,送出後 BE
於單一交易內把資源庫的 catalog / profile / SSP 各 clone
一份脫鉤公版,建立專案主檔、project_extensions(記
living_ssp_id 錨點)、參與人員,並以 best-effort
生成準備期收證據任務與第一輪稽核(round_type='initial')。成功後導向該專案的總覽頁(輪次變體)。
主要使用者:任一登入者(BE 僅要求登入,無角色守門);建立者自動成為專案 manager。
角色速覽(完整定義見 _overview §3):
| 角色 | 一句話 |
|---|---|
| 任一登入者 | 可建立專案(BE 無角色守門) |
| 建立者 | 送出後自動成為該專案 manager(_add_participants
fallback) |
受評範圍不在此頁(C6, 2026-05-23 決策):
audit_systems/devices已從建立流程移除,改在建立後的 SSP 編輯頁維護;本頁 Step 2 有提示導引。
1.1 功能總覽(本頁全部功能)
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 步驟指示器 | 三步:基本資訊 / 參與人員 / 確認送出,可回改 | 頁首下方 | §5 |
| 2 | 專案名稱 / 描述 | 名稱必填(BE 檢查唯一);描述選填 | Step 0 | UC-PC-01 |
| 3 | 開始 / 結束日期 | 皆必填;結束須晚於開始(FE 驗證) | Step 0 | UC-PC-01 |
| 4 | 選合規資源庫 | 下拉選 published module_frame;選後自動帶入已連結帳號的人員 | Step 0 | UC-PC-01 |
| 5 | 選稽核流程範本 | 下拉選 flow_template(第一輪稽核流程)+預覽 dialog(BPMN) | Step 0 | UC-PC-01 |
| 6 | 參與人員挑選 | 搜尋加入使用者、設角色、移除;自動帶入者預設 viewer | Step 1 | UC-PC-01 |
| 7 | 確認摘要卡 | 基本資訊 / 參與人員兩張唯讀卡,各有「回改」鈕 | Step 2 | §5 |
| 8 | 受評範圍提示 | 提示建立後至 SSP 頁籤填系統安全計畫 | Step 2 | §12 |
| 9 | 建立專案送出 | POST /oscal-project/start → clone +
bootstrap(交易) |
Step 2 按鈕 | UC-PC-01 |
| 10 | 導航分流 | 第一輪成功 → 總覽頁(round 變體);失敗 → 輪次清單頁 | 送出後 | §10 |
UC(§2)只展開一條核心路徑 UC-PC-01(完整三步 + 送出 + BE bootstrap),因為本頁的價值幾乎全在這條建立鏈;其餘為表單互動細節。
2. Use Case
角色速覽(完整定義見 _overview §3):
| 角色 | 一句話 |
|---|---|
| 任一登入者 | 可建立專案(BE 無角色守門) |
| 建立者 | 送出後自動成為該專案 manager(_add_participants
fallback) |
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外 / 擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-PC-01 建立稽核專案
| 項目 | 內容 |
|---|---|
| 角色 | 任一登入者 |
| 前置條件 | 已登入;系統有 ≥1 個 published 合規資源庫與 flow_template |
| 產出 / 後置條件 | clone 三件組 + compliance.projects /
project_extensions /
project_participants;best-effort 生成準備 job
與第一輪;導向總覽頁 |
3. 權限矩陣
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 進入建立頁 | route 無 requiresAdmin;由 project-list 的
CREATE 頁權限導入 |
— | src/config/router/index.js:761-765 |
| 送出建立 | 表單驗證通過 | 僅
@jwt_required(),無角色守門(任一登入者可建) |
api/project/routes/project_route.py::OscalProjectStartRoute.post |
| 成為 manager | — | 建立者若未在 participants 名單,自動補一筆
role='manager' |
app/project/service/project_start_app_service.py::_add_participants |
⚠️ 無角色守門是刻意的現況:建立專案不限角色,靠 FE 的
CREATE頁權限控制入口。若日後要收緊(例如限特定角色才能開專案),需在 route / app service 補檢查——不要假設 BE 已擋。
4. 狀態機與前置條件
本頁不涉輪次狀態機(輪次在建立之後才有)。建立當下的前置條件(BE 強制):
| 條件 | 效果 | 出處 |
|---|---|---|
| 專案名稱唯一(排除 archived) | 重複 → ConflictError(GRC_409005) |
project_start_app_service.start_project(名稱檢查段) |
資源庫已發布(module_frames.enable/published) |
未發布 → PreconditionFailedError(GRC_412031) |
_resolve_source_triple:152-154 |
| 資源庫存在且有 catalog / SSP | 缺 → NotFound(GRC_404021 /
GRC_404037 / GRC_404012) |
_resolve_source_triple:152-167 |
建立成功 → 專案 status='pending' |
尚未啟動稽核;第一輪為 initial(best-effort) |
start_project 回傳 +
_create_first_round |
5. UI 設計
版面骨架與步驟區塊(三步 wizard:基本資訊 → 參與人員 → 確認送出;彩色區塊可點跳本頁小節錨點,API 僅標代表性):
實機截圖(STG,亮色模式,2026-07-05,帳號 blsadmin;填完 Step 2 後中止未送出,STG 未產生測試專案):



狀態呈現:步驟圓點(已完成打勾 / 當前外框 / 未達灰底);選資源庫時
Step 1 顯示自動帶入 spinner 與「已帶入 N 位」訊息;流程範本預覽走
dialog;建立按鈕帶 createLoading。日期以
%Y-%m-%d 送出(FE formatDate)。
6. API 規格
Envelope:成功 {"status": true, "data": …}、失敗
{"error_code": "…", "msg": "…"}(HTTP 4xx/5xx)。
6.1 總清單(本頁呼叫的全部 endpoint)
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 載入 | GET /module-frame(filter published) |
合規資源庫下拉來源 | GAI-SD-02 |
| 載入 | GET /users/menu |
參與人員挑選來源 | GAI-SD-02 |
| 載入 | GET /module-frame/{uid}/parties |
選資源庫後自動帶入已連結帳號人員 | GAI-SD-02 |
| 載入 | GET /flow-engine/...(flow template 清單 / 明細) |
流程範本下拉 + 預覽 | GAI-SD-02 |
| 送出 | POST /oscal-project/start |
建立專案(clone 三件組 + bootstrap) | §6.2 |
6.2 核心 endpoint
[POST] /oscal-project/start
建立專案。Request(OscalProjectStartRequest):
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| resource_library_uid | string | 是 | published 合規資源庫 UID(→ clone catalog / profile / SSP 三件組脫鉤公版) |
| name | string | 是 | 專案名稱(BE 檢查唯一,排除 archived) |
| description | string|null | 否 | 專案描述 |
| start_date / end_date | date(%Y-%m-%d)|null |
否 | 專案期程(FE 驗證結束>開始) |
| owner_uid | string|null | 否 | 專案負責人 user uid;不傳預設建立者 |
| participants | array|null | 否 | [{user_uid(必填), role(manager/reviewer/auditor/viewer,預設 viewer)}];建立者自動補
manager |
| flow_template_uid | string|null | 否 | 第一輪稽核流程範本 UID(=
compliance.flow_templates.uid);不傳則回退資源庫的
audit_flow_template_uid |
Response data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| project_uid | string | 新專案 uid(導航用) |
| catalog_uid / profile_uid / ssp_uid | string|null | clone 出的三件組 uid(ssp_uid = living SSP) |
| control_count | int | 初始化的控制項數 |
| job_count | int | 準備期 per-AO 收證據 job 數(best-effort,可能 0) |
| ap_created / ar_created | bool | 皆 false(AP / AR 延到輪次啟動 B3+,見 _overview §2) |
| first_round_uid / first_round_created | string|null / bool | 第一輪 uid(best-effort,失敗為 null) |
| participants | array | 實際寫入的參與者 {user_uid, user_id, role} |
| created_user | string | 建立者 login_name |
出處:serializer
api/project/serializers/project.py:44-66;組裝與回傳
app/project/service/project_start_app_service.py::start_project(return
dict);route
api/project/routes/project_route.py::OscalProjectStartRoute
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/views/project/ProjectCreateView.vue |
主檢視(三步精靈、驗證、送出、導航分流) |
src/components/grc/ProjectBasicInfoForm.vue |
Step 0 基本資訊表單(名稱 / 描述 / 日期 + slot 放資源庫 / 流程範本下拉) |
src/components/grc/ParticipantPicker.vue |
Step 1 參與人員挑選(搜尋 / 角色 / 移除) |
src/views/flow-template/components/FlowTemplatePreviewDialog.vue |
流程範本 BPMN 預覽 dialog |
src/service/FlowTemplateService.js |
流程範本清單 / 明細 API |
src/stores/menuStore.js |
資源庫(module_frame)/ 使用者下拉資料 |
src/config/router/index.js:761-765 |
route
project-create(/project/projects/new) |
src/config/api/api.js |
endpoint 常數(OSCAL_PROJECT_START 等) |
src/config/locales/i18n/zh-tw/project-create.json |
頁面 i18n(標籤 / 驗證訊息 / toast) |
8. 後端檔案地圖(本頁核心鏈路)
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 建立專案 | api/project/routes/project_route.py::OscalProjectStartRoute.post |
app/project/service/project_start_app_service.py::start_project(@transaction;名稱檢查
→ clone → 建檔 → 參與人員 → SSP 初始化 → best-effort 準備 job /
第一輪) |
OscalSnapshotService.clone_resource_library(三件組
clone)、project_service.create_project、project_extension_domain_service、project_participant_domain_service、audit_round_app_service.create_round(第一輪) |
| Drive 初始化 | 同 route(交易外) | drive_sync_orchestration_service.try_enqueue_init_project_folders |
best-effort 佇列,失敗只 log |
DDD 提醒:start_project 是唯一
@transaction 開啟點,內部
helper(_resolve_source_triple /
_add_participants / _generate_prep_jobs /
_create_first_round)都在同一 scope 內、不各自加
@transaction(_create_first_round 呼叫的
create_round 是 nested @transaction 重用同一
session)。
9. DB
9.1 資料表總清單(本頁寫入的全部表)
| 表 | 讀/寫 | 說明 | 欄位詳述 |
|---|---|---|---|
| compliance.module_frames | 讀 | clone 來源(published 資源庫) | §9.3 |
| compliance.projects | 寫 | 專案主檔(status=pending) | §9.3 |
| compliance.project_extensions | 寫 | module_frame_id / owner_id / living_ssp_id 錨點 | §9.3 |
| compliance.project_participants | 寫 | 參與者(建立者補 manager) | §9.3 |
| oscal.catalogs / catalog_controls / catalog_control_parts | 寫 | clone 控制母體三層 | GAI-SD-03 |
| oscal.profiles / profile_imports | 寫 | clone 基準線 + catalog 導航 | GAI-SD-03 |
| oscal.system_security_plans | 寫 | clone 出的 living SSP | GAI-SD-03 |
| oscal.ssp_implemented_requirements / ssp_statements | 寫 | 控制項 / AO 現況(空白初始化) | GAI-SD-03 |
| oscal.ssp_reference_documents | 寫(best-effort) | 程序書池 + 對應 clone | GAI-SD-03 |
| compliance.workflow_templates / workflow_executions / job_executions | 寫(best-effort) | 準備期 per-AO 收證據 job | GAI-SD-03 |
| compliance.project_audit_rounds | 寫(best-effort) | 第一輪 round_type='initial' |
_overview §2 |
9.2 ER 圖(本頁寫入範圍)
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)
compliance.projects — 專案主檔
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | uuid | 內部唯一識別碼(回傳 project_uid 導航用) |
| name / description | varchar(255) / text | 標題 / 描述 |
| status | varchar(50)|null | 建立後為 pending |
| start_date / end_date | timestamp|null | 專案期程 |
| tenant_id / org_unit_id | int|null | 租戶 / 組織單位(RLS) |
compliance.project_extensions — 專案延伸(1:1)
| 欄位 | 型別 | 說明 |
|---|---|---|
| project_id | int | → compliance.projects.id(1:1) |
| module_frame_id | int|null | 啟動專案用的 module_frame 參考(clone 後即獨立,無 FK) |
| owner_id | int|null | 專案負責人(users.id);不傳預設建立者 |
| living_ssp_id | bigint|null | clone 出的 SSP(soft ref)— 唯一錨點;profile 由
ssp.import_profile_id、catalog 由
profile_imports.source_catalog_id 導航 |
compliance.project_participants — 參與者
project_id / user_id /
role(manager / reviewer /
auditor / viewer);建立者若未在名單自動補
manager(_add_participants fallback,in-memory
dedup)。
compliance.module_frames — 合規資源庫(clone 來源)
published 的 module_frame 是 clone 的來源;解析出其 catalog / profile
/ SSP / audit_flow_template_uid(flow_template
未指定時的回退值)。
10. 頁面邏輯與資料對應
BE 寫入時序(本頁最關鍵的邏輯):
關鍵欄位對應(畫面 ↔︎ payload):
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 合規資源庫下拉 | step0.moduleFrameId(取所選 module_frame 的 uid) |
resource_library_uid |
| 稽核流程範本下拉 | step0.flowTemplateUid |
flow_template_uid |
| 名稱 / 描述 | step0.name / step0.description |
name / description |
| 開始 / 結束日期 | step0.startDate /
endDate(formatDate→YYYY-MM-DD) |
start_date / end_date |
| 參與人員清單 | selectedParticipants[{uid, role}] |
participants[{user_uid, role}] |
送出後導航分流:first_round_uid 有值 →
router.push('project-auditor-overview-round', {id: project_uid, roundUid: first_round_uid});為
null(第一輪 best-effort 失敗)→ 降級到
project-ap-list。
錯誤對應:BE error envelope → toast;常見碼
GRC_409005(名稱重複)、GRC_412031(資源庫未發布)、GRC_404021(資源庫不存在)。FE
端另有必填 / 日期順序驗證(Vuelidate)在送出前擋。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| Drive 同步 | 建立成功後 route 層 best-effort 佇列
INIT_PROJECT_FOLDERS(tenant 未連 Drive 則靜默跳過;失敗只
log,不 rollback 專案),由 worker 非同步建 5
層資料夾 |
| 準備 job | _generate_prep_jobs 為每 in-scope 控制的每個 AO part
clone 專屬流程範本並建 job_execution(指派由 PM
之後手動),best-effort |
| BPMN | 第一輪綁
flow_template_uid(未指定回退資源庫預設);輪次狀態後續由
BPMN 驅動 |
| Socket | 本頁無 socket;建立為一次 request/response |
| jedi-* 套件 | jedi-oscal-v2(clone 三件組 / SSP
repo)、jedi-flow-engine(準備 job / 範本
clone)、jedi-project(專案
domain)、jedi-auth(JWT) |
| 系統參數 | 無 feature flag;資源庫 / 流程範本清單由 published 狀態決定 |
12. 邊界情況與已知坑
- 交易邊界:atomic 核心 vs best-effort 尾段:名稱檢查
/ clone 三件組 / 建專案 / extension / participants / SSP 初始化屬
@transaction內,任一失敗全 rollback;但程序書 clone、準備 job、第一輪是 try/except 尾段,失敗只 log 不擋專案成立——所以會出現「專案建好但沒第一輪 / 沒準備 job」的合法狀態(FE 導航已針對第一輪缺席降級到輪次清單頁) - 建立無角色守門:任一登入者都能打
oscal-project/start;限制只在 FE 的CREATE頁權限。安全評估勿假設 BE 已擋 - 受評範圍已移出建立流程(C6):
audit_systems/devices不在 payload;建立後必須到 SSP 編輯頁補系統特性 / 元件 / 盤點,否則控制母體雖有、受評標的空 - 自動帶入的人員是 viewer 不是 OSCAL
role:選資源庫時從 module_frame parties
帶入「已連結帳號」的人員,一律給
viewer(SSP 角色 ≠ 專案角色);使用者可再調整 - owner_uid 預設建立者:不傳 owner_uid 時 owner = 建立者;且建立者一定被加為 manager participant(即使 owner 是別人)
- 名稱唯一性排除 archived:軟刪除(archived)的同名專案不算衝突,可重用名稱
- living SSP 是唯一錨點:不另存 catalog_id /
profile_uid,全靠
living_ssp_id → import_profile_id → source_catalog_id導航(避免 desync);改動這條鏈要整條一起看 - flow_template_uid 回退:不指定則用資源庫
audit_flow_template_uid;兩者皆無時第一輪可能無流程綁定(best-effort 失敗,降級導航) - date-only 陷阱:日期以
%Y-%m-%d傳輸,顯示時留意時區(見專案共用坑)
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 環境
blsadmin(manual 測試用;密碼見.env/ 部署文件) - 導航路徑:專案列表 →
「建立專案」(
/project/projects/new) - 前置資料:系統需至少一個 published 合規資源庫(module_frame 三件組)與一個 flow_template;否則兩個下拉為空無法送出
- E2E:
compliance-manager-test/repo(Cucumber + Playwright);以專案建立/project-create搜尋 feature 檔 - 相關文件:建立後入口見 專案總覽頁、清單見 專案列表頁;FR-038 Wave 2B 設計
docs/features/FR-038-2606-oscal-redesign/;prompt/oscal_project_start_flow.md(啟動寫入順序);DB / API 全量見 GAI-SD-02/03