手冊首頁 / 專案與任務 / 專案列表頁

專案列表頁(Project List)

功能群:專案管理|狀態機與共用概念先讀 功能群總覽

事實基準:2026-07-05 從 FE / BE code 掃出(FR-038 v2 round-based 架構);API schema 逐條對照 marshmallow serializer 與 service 組裝碼;表結構取自 db_schema.json|變更紀錄:2026-07-09 專案狀態機修復 B 案 —— 專案建立即 in_progress(「未開始(pending)」語意取消),狀態篩選由四鈕改三鈕;批次刪除補 not_found 計數(FR-047 U-4)

1. 功能描述

專案列表頁是稽核專案的入口與總覽清單:登入後看見自己有份的所有專案,提供搜尋、狀態篩選、排序、分頁,以及建立 / 編輯 / 刪除 / 同步 Drive 的操作入口。清單每一列彙總該專案的框架、最新稽核輪次狀態、輪次數、負責人;點名稱進入該專案的輪次清單頁。

主要使用者:所有登入者(清單只回自己參與的專案);建立 / 編輯 / 刪除受角色與 owner 判定。

角色速覽(完整定義與 manager 全域代行慣例見 _overview §3):

角色 一句話
owner 專案負責人(project_extensions.owner_id)— 隱含 manager 等級的編輯 / 刪除權
manager 專案經理 — 可編輯 / 刪除本專案
auditor / viewer 只能檢視,功能欄不顯示編輯 / 刪除
admin 系統管理員(is_admin)— 看得到全部專案、可刪任一專案

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

# 功能 說明 位置 詳述
1 專案清單(分頁 / 排序) 後端分頁(lazy),每頁 10 / 25 / 50;名稱、稽核輪次數可排序 資料表 UC-PL-01
2 關鍵字搜尋 依專案名稱模糊搜尋(Enter 或放大鏡觸發) 篩選列 UC-PL-01
3 狀態篩選 三鈕:全部 / 進行中(in_progress)/ 已完成(completed)(B 案後專案建立即 in_progress,pending 不再產生,故不提供「未開始」篩選) 篩選列 UC-PL-01
4 重設篩選 清空搜尋與狀態、還原分頁 篩選列 §10
5 建立專案入口 導向專案建立頁(限 CREATE 頁權限) Header 按鈕 專案建立
6 名稱連結進專案 點名稱 → 該專案輪次清單頁(project-ap-list) 名稱欄 §10
7 框架欄 顯示綁定的 module_frame 名稱(FrameworkBadge) 框架欄 §6.2
8 最新輪次狀態 有輪次顯示「第 N 輪+StatusBadge」;無輪次顯示「尚未啟動稽核」 狀態欄 §6.2
9 編輯專案 就地 dialog 改名稱 / 描述(限 owner 或 manager) 功能欄 → dialog §6.1
10 刪除專案(單筆) 軟刪除(限 owner 或 manager / admin) 功能欄 UC-PL-02
11 批次刪除 勾選多筆一次刪,逐筆檢權部分成功 Header 按鈕 UC-PL-02
12 同步專案到 Google Drive 未同步且 tenant 已連 Drive 時,手動建立專案資料夾 功能欄 UC-PL-03
13 Drive 同步徽章 名稱旁顯示「已連動 / 未同步 Google Drive」(僅 tenant 已連 Drive 時出現) 名稱欄 UC-PL-03

UC(§2)只展開 1 / 10 / 11 / 12 這些核心操作路徑;搜尋 / 篩選 / 編輯屬常規列表 CRUD,建立專案另有 專案建立 詳述。

2. Use Case

角色速覽(完整定義與 manager 全域代行慣例見 _overview §3):

角色 一句話
owner 專案負責人(project_extensions.owner_id)— 隱含 manager 等級的編輯 / 刪除權
manager 專案經理 — 可編輯 / 刪除本專案
auditor / viewer 只能檢視,功能欄不顯示編輯 / 刪除
admin 系統管理員(is_admin)— 看得到全部專案、可刪任一專案

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

UC-PL-01 瀏覽 / 搜尋 / 篩選專案清單

項目 內容
角色 任一登入者
前置條件 已登入
產出 / 後置條件 回傳分頁清單(只含自己參與的專案);查詢條件存入 queryStore,重入頁保留
pl_load_sequence mount onMounted q1 還原 queryStore (分頁/排序/篩選) mount->q1 drive GET /integrations/google-drive (tenant 是否 CONNECTED → driveConnected) mount->drive 並行 list POST /grc/projects/list (pager+sort+filters) q1->list enrich BE 逐專案 enrich: 最新輪次/完成度/owner/module_frame/drive_synced list->enrich render 渲染表格+分頁 Drive 徽章依 driveConnected 顯示 enrich->render drive->render 並行
UC-PL-01 流程:還原查詢條件→POST list(後端可見性過濾+逐專案彙總)→渲染,Drive 狀態並行載入

UC-PL-02 刪除專案(單筆 / 批次)

項目 內容
角色 owner / manager(admin 可刪任一)
前置條件 對該專案為 owner 或 manager;FE 已依此隱藏無權按鈕
產出 / 後置條件 軟刪除:project_extensions.deleted_at 標記 + projects.status='archived';批次回 {deleted, forbidden, not_found} 計數
uc_pl_02 start pick 選單筆🗑/勾選多筆後按批次刪除 (FE 已依 owner/manager 隱藏無權按鈕) start->pick confirm 確認對話框:確定刪除? pick->confirm cancel 取消 → 不動作 confirm->cancel call 單筆:DELETE /grc/project/{uid} 批次:POST /grc/projects/batch-delete confirm->call perm BE 逐筆檢查:is_admin? 或本人為 owner?或 role=manager? call->perm deny 403 PROJECT_403001 (批次:歸入 forbidden 清單) perm->deny 皆否 soft 軟刪除:project_extensions.deleted_at=now + projects.status='archived' perm->soft 任一成立 done 回列表:重新查詢 批次回 {deleted, forbidden, not_found} 計數 toast soft->done
UC-PL-02 流程:確認→呼叫刪除→BE 逐筆檢權(admin/owner/manager)→軟刪除→重查清單

UC-PL-03 同步專案到 Google Drive

項目 內容
角色 owner / manager
前置條件 tenant 已連 Drive(driveConnected)且該專案尚未同步(drive_synced=false
產出 / 後置條件 送出 init-folders 佇列任務,由 Drive sync worker 非同步建 5 層資料夾(見 §11)
uc_pl_03 start click 功能欄按「同步到 Drive」 (此鈕僅 driveConnected 且 !drive_synced 時出現) start->click call POST /integrations/google-drive/ projects/{uid}/init-folders click->call perm BE 檢查:owner 或 manager? 且 tenant 狀態為 CONNECTED? call->perm denyPerm 403 GRC_403001 (無權限) perm->denyPerm 非 owner/manager denyConn 404 GRC_404030 (tenant 未連 Drive → 提示先到整合設定) perm->denyConn tenant 未連 enqueue try_enqueue_init_project_folders (best-effort:入佇列失敗只 log 不擋操作) perm->enqueue 皆通過 worker Drive sync worker 非同步處理 建立 5 層專案資料夾 enqueue->worker 佇列 done drive_synced=true → 徽章翻「已連動 Google Drive」 (下次載入 enrich_drive_synced 反映) worker->done
UC-PL-03 流程:按同步→BE 檢權+tenant 連線檢查→try_enqueue(best-effort 不擋)→worker 非同步建 5 層資料夾→徽章翻「已連動」

3. 權限矩陣

專案層角色基調見 _overview §3。本頁的實際檢查點(FE 判定與 BE 強制分欄並列):

操作 FE 判定 BE 強制 BE 檢查位置
進入 / 讀清單 登入即可 只回自己參與的專案(owner 或任一 participant 表命中);is_admin 看全部 infra/grc/repository/grc_project_repo_impl.py::list_projects(可見性 join)
建立入口顯示 pagePermissionCREATE 建立在另頁強制(見 project-create)
編輯專案 UPDATE 權 +(本人為 owner 或該專案 manager) 需為可見專案(get_project 可見性);participant 全量覆寫時保護 owner 不被移除 app/grc/service/project_service.py::update_project
刪除(單筆) DELETE 權 +(owner 或 manager) is_admin 或 owner 或 role=manager,否則 ForbiddenError app/grc/service/project_service.py::delete_project:630-644
批次刪除 DELETE 權 + 有勾選 逐筆同單筆規則,無權者歸 forbidden 不整批失敗 同檔 batch_delete_projects:653
同步 Drive driveConnected!drive_synced +(owner 或 manager) init-folders endpoint(tenant 須 CONNECTED) api/cloud_integration/routes/google_drive_sync_route.py

⚠️ 已知不一致(維護時注意):清單可見性用「owner 或任一 participant 表命中」的寬鬆聯集(含 control / group 層參與),而編輯 / 刪除只認 owner 或專案層 manager——「看得到但不能改」是正常設計,但改權限邏輯時兩處要一起看,見 §12。

4. 狀態機與前置條件

本頁不推進稽核輪次狀態(那在總覽頁 / 規劃頁)。列表僅呈現每專案的狀態:

條件 效果 出處
專案有 ≥1 輪次 狀態欄顯示「第 {latest_round_no} 輪」+ latest_round_status 徽章 ProjectResponseSchema.latest_round_*api/grc/serializers/project.py:149-153
專案無輪次 狀態欄顯示「尚未啟動稽核」 同上(round_count=0latest_round_*=None
projects.status(粗粒度真相) B 案:專案建立即 in_progress;由輪次驅動維護(全輪 closed→completed、否則 in_progressarchived/suspended 為使用者終態不覆寫)。pending 值域於 serializer/enum 保留相容但 BE 不再產生 _sync_project_statusapp/grc/service/audit_round_app_service.py)+建立點 project_start_app_service.py
projects.status='archived' 軟刪除後不再出現於清單(deleted_at IS NULL 過濾) grc_project_repo_impl.py::list_projectsdelete 軟刪除
狀態篩選鈕 FE 只提供 in_progress / completed(B 案後 pending 不再產生故不列);BE _STATUS_VALUES 仍保留 pending/suspended/archived 值域相容,但這三值不開放為篩選鈕 FiltersSchema._STATUS_VALUESapi/grc/serializers/project.py:34)+FE ProjectListView.statusOptions

5. UI 設計

版面骨架與區塊職責(Header 動作+篩選列+資料表+分頁,就地編輯 dialog 依操作出現;彩色區塊可點跳本頁小節錨點,API 僅標代表性):

Header:建立專案(→ project-create,限 CREATE 頁權限)+ 批次刪除(勾選列時才顯示) 篩選列:關鍵字搜尋(名稱模糊)|狀態鈕 全部/待啟動/進行中/已完成|重設 跳至 §2 Use Case(UC-PL-01 瀏覽/搜尋/篩選;UC-PL-03 同步 Drive) 專案資料表(後端分頁 lazy,每頁 10/25/50;名稱、輪次數可排序) 名稱(連結進 project-ap-list + Drive 同步徽章)|框架|最新輪次狀態徽章|負責人 功能欄:編輯(→ dialog)/刪除/同步 Drive(僅已連 Drive 未同步時) POST /grc/projects/list(pager + sort + filters) GET /integrations/google-drive(→ driveConnected,控徽章/同步鈕整組顯示) POST …/init-folders(UC-PL-03 佇列建 Drive 資料夾) → 點我跳 §2 UC-PL-01/03 分頁列(頁碼/每頁筆數) 跳至 §6 API 規格(PUT/DELETE/batch-delete 寫入類 CRUD) 編輯專案 dialog(就地,限 owner/manager) 改名稱/描述;刪除=軟刪除(狀態轉 archived) PUT /grc/project/{uid}・DELETE /grc/project/{uid} POST /grc/projects/batch-delete(逐筆檢權,部分成功) 彩色=可點跳本頁小節錨點(框內標錨點+代表性 API)|灰=本頁描述|虛線框=依操作才出現的編輯 dialog API 僅列代表性 2~3 條,完整規格見 §6;清單可見性寬鬆聯集、編輯/刪除只認 owner 或專案層 manager(§12)
專案列表頁版面 wireframe:Header 建立/批次刪除、篩選列、資料表(名稱+Drive 徽章/框架/輪次狀態/負責人/功能欄)、分頁、編輯 dialog

實機截圖(STG,亮色模式,2026-07-05,帳號 blsadmin):

專案列表整頁:篩選列(搜尋/狀態)+ 資料表(合規資源庫/狀態/稽核計畫/負責人/功能)

功能欄按鈕與 Drive 徽章:編輯/刪除圖示 + 「已連動 Google Drive」徽章(此列已同步,故無「同步 Drive」按鈕——該鈕僅在已連動但尚未同步時出現)

編輯專案 dialog:專案名稱與描述欄位(僅開啟預覽,未送出儲存)

狀態呈現:DataTable lazy 後端分頁;多欄排序(sortMode="multiple" + removableSort);勾選列出現時 Header 才顯示批次刪除;同步 Drive 按鈕帶 per-列 loading(syncingProjectId);Drive 徽章與同步按鈕整組只在 driveConnected 為真時渲染。無 socket,操作後靠重新查詢刷新。

6. API 規格

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

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

分類 Method + Path 說明 完整規格
載入 POST /grc/projects/list 分頁清單(pager + sort + filters) §6.2
載入 GET /integrations/google-drive tenant Drive 連線狀態 → driveConnected GAI-SD-02
編輯 PUT /grc/project/{uid} 更新專案(名稱 / 描述 / 日期 / 狀態 / owner / participants) §6.3
刪除 DELETE /grc/project/{uid} 軟刪除單一專案 §6.3
刪除 POST /grc/projects/batch-delete 批次軟刪除,逐筆檢權 §6.3
Drive POST /integrations/google-drive/projects/{project_uid}/init-folders 佇列建立專案 Drive 資料夾 GAI-SD-02

以下 §6.2~6.3 為核心 endpoint 完整規格(欄位逐條對照 serializer / service 組裝碼,出處標在各段末);標 GAI-SD-02 者屬常規 CRUD,細節見 API 規格書或 Swagger /swagger-ui/

6.2 載入類

[POST] /grc/projects/list

分頁清單。Request(繼承 RequestMetaSchemapager / sort):

欄位 型別 必填 說明
pager object {page, page_size, with_total}
sort array [{field, order}];可排序欄位僅 name / start_date / end_date / status,其餘忽略,預設 projects.id DESC
filters.search string|null 專案名稱模糊搜尋(name ILIKE %search%
filters.status string|array|null 單值或陣列(單值 @pre_load 自動轉陣列);允許值 pending/in_progress/completed/suspended/archived

Response data(陣列,每項為 ProjectResponseSchema = GrcProjectDto)核心欄位:

欄位 型別 說明
id string 專案 uid(get_id 回傳 uid,不曝光內部 id)
name / description / status string 基本資料
completion_rate float 整體完成度 %
total_tasks / completed_tasks / task_status_breakdown int / int / object 任務統計({completed, in_progress, not_started}
control_group_count / total_controls / total_assessment_objects / reviewed_controls_count int 控制母體 in-scope 統計(由 profile resolution 推導,失敗回 0 不擋清單)
ap_count / round_count int 稽核計畫數 / 輪次數
latest_round_no / latest_round_status / latest_round_name int|null / string|null / string|null 最新一輪(狀態欄用;無輪次為 null)
start_date / end_date date|null 專案期程
framework / module_frame object|null 框架 / 範本資訊({id, name, …}
owner object|null {id, name, avatar_url}(負責人欄)
ssp / assessment_plan object|null living SSP / AP 摘要
participants array 參與者(ProjectParticipantResponse
drive_synced bool 是否已有 Drive PROJECT 對應(enrich_drive_synced 批次補;控制徽章與同步鈕)

出處:serializer api/grc/serializers/project.py:67-72(request)/ :133-253(response);組裝 app/grc/service/project_service.py::list_projects;可見性與 enrich infra/grc/repository/grc_project_repo_impl.py::list_projects

6.3 寫入類(常規 CRUD,摘要)

[PUT] /grc/project/{uid}

更新專案。全部欄位選填,只更新有送的(未送的欄位不動)。Request:

欄位 型別 必填 說明
name string 名稱變更會回報供 Drive 資料夾改名(§11)
description string 專案描述
start_date / end_date date 專案期程
status string 允許值見 _STATUS_VALUES
owner_uid string 變更負責人
participants array 送 list 即全量覆寫(含 [] 清空);null / 省略 = 不動;owner 受保護不被移除

Meta.unknown=EXCLUDE:舊 FE 送 audit_systems / devices 等未知欄位靜默忽略。

出處:api/grc/serializers/project.py:88-116app/grc/service/project_service.py::update_project

[DELETE] /grc/project/{uid} ・ [POST] /grc/projects/batch-delete

軟刪除。單筆 body 無;批次 body {uids: [...]},回 {deleted, forbidden, not_found}(部分成功)。權限:is_admin 或 owner 或 role=manager,否則 ForbiddenError(PROJECT_403001);找不到 NotFound(PROJECT_404001)。實作 = project_extensions.deleted_at=now() + projects.status='archived'grc_project_repo_impl.py 軟刪除)。出處:app/grc/service/project_service.py:609-701

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

檔案 角色
src/views/project/ProjectListView.vue 主檢視(清單、篩選、分頁、編輯 dialog、刪除、Drive 同步)
src/config/router/index.js:754-759 route project-list/project/projects
src/composables/useTableQuery.js 分頁 / 排序 / 篩選共用邏輯
src/stores/queryStore.js 依 scope 保存查詢條件(重入頁還原)
src/components/grc/StatusBadge.vue 輪次狀態徽章
src/components/grc/FrameworkBadge.vue 框架名稱徽章
src/components/grc/UserAvatar.vue 負責人頭像 + 名稱
src/service/CloudIntegrationService.js Drive 連線狀態查詢 / initProjectFolders
src/config/api/api.js endpoint 常數(GRC_PROJECTS_LIST / GRC_PROJECT / GRC_PROJECTS_BATCH_DELETE …)
src/config/locales/i18n/zh-tw/project-list.json 頁面 i18n

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

鏈路 Route App Service 底層
清單 api/grc/routes/project_route.py::ProjectListResource app/grc/service/project_service.py::list_projects(enrich participants + drive_synced) domain/grc/service/grc_project_domain_service.pyinfra/grc/repository/grc_project_repo_impl.py::list_projects(可見性 join + 批次彙總最新輪次 / 控制母體)
更新 同檔 ProjectDetailResource.put update_project(participant 全量覆寫、owner 保護、改名回報 Drive rename) jedi_project domain + project_participant_service
刪除 同檔 ProjectDetailResource.delete delete_project(逐筆檢權) 軟刪除 deleted_at + status='archived'
批次刪除 同檔 ProjectBatchDeleteResource.post batch_delete_projects(部分成功分類) 同上

DDD 提醒:可見性過濾與最新輪次彙總都在 repo 層批次做(避免 N+1),route 不碰 DB、app service @transaction——照層級走。

9. DB

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

讀/寫 說明 欄位詳述
compliance.projects 讀 / (status) 專案主檔(列表主體);刪除時改 status='archived' §9.3
compliance.project_extensions 讀 / (deleted_at, owner_id) owner / module_frame / living_ssp 錨點;軟刪除欄位在此 §9.3
compliance.project_participants 讀 / (更新時全量覆寫) 專案層角色(可見性 + 編輯 / 刪除權判定) GAI-SD-03
compliance.project_audit_rounds 每專案最新一輪 → 狀態欄 / round_count _overview §2
compliance.control_group_participants / project_group_participants / project_control_participants 可見性聯集(看得到但不一定能改) GAI-SD-03
compliance.job_executions 任務指派聯集(我有份的專案)+ 完成度統計 GAI-SD-03
compliance.module_frames 框架 / 範本名稱(框架欄) GAI-SD-03
oscal.system_security_plans living SSP → 控制母體統計 GAI-SD-03
public.users 負責人 nickname / avatar GAI-SD-03
public.drive_folder_mappings 是否已同步 Drive(drive_synced GAI-SD-03

9.2 ER 圖(本頁讀寫範圍)

pl_er proj compliance.projects uid / name / description status / start_date / end_date tenant_id / org_unit_id ext compliance.project_extensions project_id (1:1) owner_id → users module_frame_id living_ssp_id deleted_at(軟刪除) proj->ext 1:1 part compliance.project_participants project_id / user_id role(manager/reviewer/ auditor/viewer) proj->part 1:N round compliance.project_audit_rounds project_id / round_no status(最新一輪→列表狀態欄) proj->round 1:N drive public.drive_folder_mappings project → Drive 資料夾 (存在 = drive_synced) proj->drive 讀 sync 狀態 mf compliance.module_frames uid / name(→框架欄) ext->mf module_frame_id usr public.users id / uid / nickname (負責人顯示) ext->usr owner_id
專案列表讀寫範圍:藍 = 主檔與延伸(軟刪除)、綠 = 唯讀彙總來源;虛線 = soft ref / 狀態讀取

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

compliance.projects — 專案主檔

欄位 型別 說明
uid uuid 內部唯一識別碼(API 對外走 uid)
name / description varchar(255) / text 標題 / 描述
status varchar(50)|null 專案狀態(pending / in_progress / completed / suspended / archived
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)
owner_id int|null 專案負責人(users.id)— 隱含 manager 等級權限
module_frame_id int|null 啟動專案用的範本參考(clone 後即獨立,無 FK)
living_ssp_id bigint|null 專案 living SSP(soft ref)— 控制母體統計的錨點
deleted_at timestamp|null 軟刪除時間戳(NULL = 未刪除);清單以此過濾

compliance.project_participants — 專案參與者

欄位 型別 說明
project_id / user_id int 專案 / 使用者
role varchar(50) 職責角色:manager / reviewer / auditor / viewer

10. 頁面邏輯與資料對應

載入時序:見 §2 UC-PL-01 的載入時序圖(清單查詢與 Drive 狀態並行)。

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

畫面元素 FE state API 欄位
搜尋框 filterTable.global.value filters.search
狀態篩選鈕 statusFilter filters.status(單值 → BE 轉陣列)
名稱欄連結 data.id(uid)→ router project-ap-list
框架欄 data.module_frame.name
狀態欄 data.latest_round_no + data.latest_round_status(無 → 「尚未啟動稽核」)
稽核輪次欄 data.round_count
負責人欄 data.owner.name / owner.avatar_url
Drive 徽章 / 同步鈕 driveConnected data.drive_synced

儲存流程:編輯 dialog 送 PUT → 成功後重查清單;刪除 / 批次刪除確認後呼叫 → 重查。查詢條件寫入 queryStore(scope = 路由名),重入頁還原;重設按鈕清空條件並還原分頁。

錯誤對應:BE error envelope → toast;同步 Drive 常見碼 GRC_404030(tenant 未連 Drive → 提示先到整合設定)、GRC_403001(無權限);刪除 PROJECT_403001(無權)/ PROJECT_404001(不存在)。

11. 背景行為與外部依賴

類型 內容
Drive 同步 「同步到 Drive」→ try_enqueue_init_project_folders;改名 → try_enqueue_rename_folder(皆 best-effort,失敗只 log 不擋操作);實際建 / 改資料夾由 Drive sync worker 非同步做
通知 更新專案狀態轉 in_progress_notify_project_started(見 update_project)
Socket 本頁 socket / 輪詢;清單更新靠操作後重新查詢
jedi-* 套件 jedi-project(專案 domain / repo)、jedi-auth(JWT / user context / RLS session 變數)
系統參數 無 feature flag;Drive 相關 UI 由 tenant 連線狀態動態決定

12. 邊界情況與已知坑

  1. 可見性 vs 編輯權不對齊:清單用寬鬆聯集(owner 或任一 participant 表命中,含控制 / 群組層參與)決定「看得到」,但編輯 / 刪除只認 owner 或專案層 manager。使用者可能「列表看得到卻沒有功能鈕」,屬正常設計,改權限務必兩處一起看
  2. 軟刪除語意:刪除 = deleted_at + status='archived',不真正 DELETE;清單以 deleted_at IS NULL 過濾,archived 也不在狀態篩選選項。誤刪需 DB 層還原 deleted_at=NULL
  3. 批次刪除部分成功(三類計數已補齊,2026-07-09 FR-047 U-4):無權或不存在的 uid 不會讓整批失敗,分別歸 deleted / forbidden / not_found 計數;前端 toast 已呈現三類數量(原僅呈現 deleted / forbidden、漏讀 not_found,已補齊)。not_found 情境=勾選後被他人先刪、隨後 fetchProjects() 刷新自然消失,困惑度低
  4. 狀態欄讀的是輪次不是專案 status:列表狀態欄顯示最新輪次狀態(FR-038),與 projects.status(篩選用)是兩套值;別把兩者當同一欄
  5. 控制母體統計靠 SSP 鏈total_controls 等由 living SSP → profile → catalog resolution 推導,鏈斷(無 living_ssp_id)時靜默回 0,不擋清單
  6. Drive 徽章整組條件渲染driveConnected=false(tenant 未連 Drive)時,徽章與同步鈕完全不顯示——不是「未同步」狀態,是「沒有 Drive 這回事」
  7. 排序欄位有限:只有 name / start_date / end_date / status 可排序,對「稽核輪次數」欄雖 FE 標 field="round_count",BE 排序白名單未含它 → 點了不會照它排(預設 id DESC)。修此需在 repo 排序白名單補
  8. date-only 顯示start_date / end_date 為 date 型,僅在編輯 dialog / 詳情用;留意 date-only 字串顯示時區陷阱(見專案共用坑)

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
  • 前置資料:需至少一個自己有份(owner 或 participant)的專案才看得到列;Drive 相關功能需 tenant 先連 Google Drive
  • E2Ecompliance-manager-test/ repo(Cucumber + Playwright);以 專案列表 / project-list 搜尋 feature 檔
  • 相關文件:建立流程見 專案建立、單專案入口見 專案總覽頁;FR-038(v2 輪次)docs/features/FR-038-2606-oscal-redesign/;DB / API 全量見 GAI-SD-02/03