本卡屬 FR-161(母卡見「母卡」欄),第 2 棒:事前資料本身的 API。接在卡 1 之後。
客戶在事前資料頁上傳檔案、上傳新版、看舊版、刪除、標記過時、一次下載全部,這張卡做這些動作背後的 API。畫面在卡 4。這張先不碰資源庫的程序書池;把事前資料同步進程序書池是卡 3 的事。
~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-161-2610-pre-engagement-materials/design.md(決策表 D1~D10、詳細設計、拆分表);雛形:~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-161-2610-pre-engagement-materials/prototype/index.html(三頁,雙擊即可開)。本卡已帶足開工所需細節,不必先讀完 design。在卡 1 建好的 pre_engagement 模組裡補資料與版本的 app service 與 API。上傳流程沿用既有做法:FE 先 POST /file/upload 拿到 file_uid,再呼叫業務 API(參考 api/oscal/routes/ssp/ssp_document_pool_route.py 與 app/oscal/service/ssp_document_pool_service.py:add_to_pool 如何把 file_uid 換成 upload_files.id,找不到時的錯誤處理也照抄)。
GET /pre-engagement/files upload;依大類分組
POST /pre-engagement/files upload;{category_uid, file_uid, title?},建資料+v1
POST /pre-engagement/files/check-duplicate upload;{category_uid, file_name}
POST /pre-engagement/files/<uid>/versions upload;{file_uid},version_no=目前最大+1
PUT /pre-engagement/files/<uid> upload;{title?, category_uid?, is_obsolete?}
DELETE /pre-engagement/files/<uid> upload
GET /pre-engagement/files/archive upload;所有未過時資料的最新版打成 zip
uid、title、category_uid、is_obsolete、is_stale(最新版 created_at 距今超過 365 天)、is_locked(卡 3 才會有實值,這張先固定回 false 並留一個函式掛點)、latest(version_no、file_uid、檔名、大小、created_user、created_user_name、created_at)、versions(全部版本,新到舊)。title 等於傳入檔名(去副檔名)的資料,或任一版本的原始檔名(去副檔名)相同的資料。回一個清單(通常 0 或 1 筆),FE 據此跳「當成新版/另存一份」。比對不分大小寫、去頭尾空白。<大類名>/<檔名>,同名加 (2) 後綴。檔案讀取走既有的檔案儲存服務(grep upload_files 的讀取 canonical,不要直接讀磁碟)。created_user_name/updated_user_name 一律用 enrich_audit_nickname_objs。POST /file/upload 拿一個 file_uid,再 POST /pre-engagement/files 建一份,GET 看到它在對應大類、versions 一筆。check-duplicate 傳同檔名回那一筆;換大類回空。POST .../versions 後 GET 看到 latest.version_no=2、versions 兩筆。created_at 改成 400 天前,GET 回 is_stale=true。PUT 標 is_obsolete=true、搬到別的大類,GET 跟著變;archive 下載的 zip 打得開、不含過時那份。