對應 design:design-C4.md 級別:中 預估工時:BE 1 天
# 1. 確認 C3 已 ship(C4 嚴格依賴 C3 — Response Schema 已改 derived)
git log --oneline | grep -E "C3|2026.*range-migration" | head -3
# 2. 確認 OscalProjectStartRequest 仍在預期位置
grep -n "class OscalProjectStartRequest\|class ProjectUpdateRequestSchema" \
api/project/serializers/project.py api/grc/serializers/project.py
# 3. 確認 start_oscal_project Step C 仍在 line 456-461
grep -n "Step C\|project_device_mapping_service" \
app/project/service/oscal_project_service.py | head -5
# 4. 確認 update_project Section 3/5 仍在 line 367 / 432
grep -n "audit_systems\|devices.*替換\|稽核系統替換\|設備替換" \
app/grc/service/project_service.py | head -10
# 5. 確認 DI 注入點
grep -rn "project_device_mapping_service\|project_information_system_domain_service" \
di_containers/ --include="*.py" | head -10OscalProjectStartRequest 拿掉 audit_systems + devices 欄位(BE)檔案:api/project/serializers/project.py:30
動作:
Meta.unknown = EXCLUDE 維持(向後相容)對應 DTO 變動:
StartProjectDTO(或對應 dataclass)有 audit_systems / devices 屬性 → 同步拿掉Commit message:refactor(project-api): C4 拿掉 OscalProjectStartRequest 內 audit_systems + devices
ProjectUpdateRequestSchema 拿掉 audit_systems + devices 欄位(BE)檔案:api/grc/serializers/project.py:77
動作:
Commit message:refactor(grc-api): C4 拿掉 ProjectUpdateRequestSchema 內 audit_systems + devices
oscal_project_service.start_oscal_project 移除 Step C(BE)檔案:app/project/service/oscal_project_service.py
動作:
__init__ 拿掉 project_device_mapping_service 參數self.project_device_mapping_service 屬性 + 所有引用 grep + 移除delete_by_project_id(line 1100)也要移除(刪專案 cascade,C3 應已搬到 SSP CASCADE,C4 確認清理)DI 改動:
di_containers/project/project_containers.py — 移除 project_device_mapping_service wiringCommit message:refactor(oscal-project): C4 移除 start_oscal_project Step C device/audit_systems 寫入邏輯
GrcProjectService.update_project 移除 Section 3 + 5(BE)檔案:app/grc/service/project_service.py
動作:
__init__ 拿掉 project_device_mapping_service + project_information_system_domain_service 參數self._device_mapping_service + self._pis_domain_service 屬性 + 所有引用 grep + 移除dto.audit_systems 組裝邏輯由 C3 改 derived,C4 不動 (Response 不變)DI 改動:
di_containers/grc/grc_containers.py — 移除兩個 service wiringCommit message:refactor(grc-project): C4 移除 update_project audit_systems + devices 替換邏輯
檔案:api/project/serializers/project.py:19
改動前:
class ParticipantInputSchema(Schema):
user_uid = fields.String(required=True)
role = fields.String(load_default="viewer")改動後:
from marshmallow import validate
class ParticipantInputSchema(Schema):
user_uid = fields.String(required=True)
role = fields.String(
load_default="viewer",
validate=validate.OneOf(["manager", "reviewer", "auditor", "viewer"]),
)Commit message:tweak(project-api): C4 ParticipantInputSchema.role 加 OneOf validation
可能受影響的測試:
grep -rln "audit_systems\|devices=\|StartProjectDTO" tests/ | head預期受影響:
tests/test_oscal_project_service.pytests/test_grc_project_service.pytests/test_project_routes.pyaudit_systems / devices 的測試 case動作:
dto.devices == [] dto.audit_systems == [](依賴 C3 改 derive)Commit message:test(project): C4 移除 audit_systems / devices 相關 test fixture + 加 derived empty case
檔案:docs/changelog/YYYY-MM-DD-refactor-project-crud-drop-scope.md
---
type: tweak
breaking: true
modules: [project, grc]
---
## 需求說明
C4:拿掉專案建立/編輯 API 內的 audit_systems / devices 欄位。
受評範圍改在 SSP 編輯頁維護(per-AP SSP),見 docs/features/FR-011.3-2605-ssp-edit-in-project/
## 變更範圍
### BE
- `api/project/serializers/project.py`:OscalProjectStartRequest 拿掉 audit_systems + devices
- `api/grc/serializers/project.py`:ProjectUpdateRequestSchema 拿掉 audit_systems + devices
- `app/project/service/oscal_project_service.py`:start_oscal_project Step C 移除
- `app/grc/service/project_service.py`:update_project Section 3+5 移除
- DI containers:拿掉 project_device_mapping_service / project_information_system_domain_service 注入
## 行為差異
| 情境 | 改前 | 改後 |
|------|------|------|
| POST /oscal-project/start 含 devices | 寫入 project_device_mapping | unknown=EXCLUDE 忽略,建專案成功但不寫入 |
| PUT /grc/project/<uid> 含 audit_systems | 替換 mappings | EXCLUDE 忽略 |
| GET /grc/project/<uid> | devices/audit_systems from project tables | 由 C3 改 derived from SSP(C4 不變動 response)|
## Breaking change 警示
- FE 必須同步 deploy 更新表單(C6)
- 既有用 audit_systems / devices field 的 caller 改用 SSP-scoped endpoints(C2)| 測試 | 內容 |
|---|---|
| Unit | OscalProjectStartRequest 不接受 audit_systems / devices(unknown=EXCLUDE 忽略) |
| Unit | start_oscal_project 不再呼叫 project_device_mapping_service |
| Unit | update_project 不再呼叫 device / pis service |
| Unit | DI container resolve OscalProjectService 不需要 project_device_mapping_service |
| Integration | E2E:POST /oscal-project/start 不含 devices → 建專案 OK + Response devices=[] |
| Integration | E2E:建專案後 → 進 SSP tab 加 device → 再 GET 專案 → Response devices 從 SSP derive 出來 |
| 風險 | 緩解 |
|---|---|
| FE 沒同步 deploy 仍送 audit_systems / devices | unknown=EXCLUDE 容錯 + log warn |
| 既有 caller (gh stat / dashboard) 依賴 Response 內 audit_systems | C3 已改 derived,Response shape 不變,caller 透明 |
| DI 解依賴後其他 service 還有引用 | pre-flight grep -rn project_device_mapping_service 找全 |
Rollback:純程式碼改動,git revert 即可。但若已 deploy 且 FE 表單同步更新後,rollback 會撞 FE 不送 fields 但 BE 不接受新 schema 的 mismatch。建議:FE deploy 前先 deploy BE。