11 KiB
11 KiB
sub-implementer-backend-s171 — nhật-ký lane BE (Nhóm 1, BE-1 → BE-7)
Run:
2026-08-04-S171-khkk-ui-mirror-pe· vaiimplementer-backend· spec §②-D + §③ Nhóm 1 Luật ghi: sau MỖI file sửa xong ⇒ append ngay 1 dòng<path> — <đã làm gì> — <acceptance nào phủ>.
0. Trạng thái
- BE-1 DetailDto +5 field
- BE-1b FinalizeStepName/FinalizeLevelName DẪN XUẤT + 3-ngả (in-memory thay 3 subquery — khai ở §2, và SỬA công thức con-trỏ cho đúng KHKK)
- BE-2 ContractSigningPlanApprovalDto (đẻ mới) + projection
- BE-3 tên người duyệt cho Approval (join
Users— đường thứ ba, lý do §2) - BE-4 WorkflowStepDto +DepartmentName
- BE-5 changelogs endpoint policy KeHoachKyKet.Read (+ rào hàng
EnsureCanViewAsync) - BE-6 map Cấp cho Approvals (StepOrder/StepName/LevelOrder/LevelName)
- BE-7 enum +ApprovalAttachment=3 + endpoint upload riêng (policy Update)
- Mirror
types/khkk.ts×2 app — SHA256 trùng khít dotnet buildsạch (0 warning / 0 error) +tsc -b×2 app exit 0- 🔴 B-3 acceptance RUNTIME — treo, DB dev 0 phiếu (§4.1)
1. Đọc / khảo sát
- Spec đọc TRỌN. 3 QĐ owner nhận: (1) panel 1 = cây + 4 mitigation, (2) panel 3 chưa chọn = hộp dashed, (3) file đính kèm khi duyệt = LÀM THẬT.
- Nhóm 1 chỉ đụng BE ⇒ QĐ-1/QĐ-2 là việc của lane FE; lane này phục vụ bằng dữ-liệu (BE-1..BE-7). QĐ-3 = BE-7, làm thật.
- Đo được (khác spec, ghi để lane sau không chép sai):
POST {id}/attachmentsĐÃ TỒN-TẠI, policyKeHoachKyKet.Update, handlerUploadContractSigningPlanAttachmentCommandHandlerKHÔNG có guard owner/phase ⇒ người duyệt (có Update) upload được. BE-7 chỉ cần enum + 1 đường riêng épPurpose.ContractSigningPlansControllerTRƯỚC S171: R = 18 action,^\s*\[Authorize\(Policy= 19 = 1 + R (đúng số spec nêu). Không có[Authorize]trần.ApprovalDecision: Pending=0 · Approve=1 · Reject=2 · AutoApprove=3 (Domain/Contracts/ApprovalDecision.cs).ApprovalWorkflow.Version(:24) có thật ⇒WorkflowVersionlấy trực tiếp.- 🔴 KHKK ≠ PE ở con-trỏ Bước:
CurrentWorkflowStepIndexlà INDEX 0-based vào list ĐÃ SORT (ContractSigningPlanWorkflowService.cs:26,422-435), KHÔNG phảiStep.Order - 1như PE. Chép nguyên công thức PE = sai khi Order không tuần-tự.
2. Nhật-ký file sửa
src/Backend/SolutionErp.Application/ContractSigningPlans/ContractSigningPlanFeatures.cs— (a)ContractSigningPlanWorkflowStepDto+DepartmentName(append cuối, default null) [BE-4/B-4]; (b) đẻ mớiContractSigningPlanApprovalDto13 field (kèm StepOrder/StepName/LevelOrder/LevelName dẫn xuất từApprovalWorkflowLevelIdloose-Guid) [BE-2/BE-6/B-2]; (c)ContractSigningPlanDetailDto+5:WorkflowVersion·EndedByLevelFinalize·FinalizeStepName·FinalizeLevelName·Approvals[BE-1/B-1].…/ContractSigningPlanFeatures.cs(handlerGetContractSigningPlanQueryHandler) — (a)levelMeta+LevelName; (b) joinDepartments1 lượt choDepartmentNamecủa Bước (không N+1) [BE-4]; (c) BE-1b DẪN XUẤT finalize step/level + 3-ngảEndedByLevelFinalizein-memory (cây đã nạp) thay 3 subquery của PE — cùng ngữ nghĩa, bớt 3 lượt truy, và SỬA công thức con-trỏ theo INDEX 0-based của KHKK; (d) queryContractSigningPlanApprovals+ joinUserslấy tên [BE-2/BE-3]; (e) call-sitenew ContractSigningPlanDetailDto(:784)truyền 5 field mới.…/ContractSigningPlanFeatures.cs(cuối file, Region 8 mới) —ContractSigningPlanChangelogDto+ListContractSigningPlanChangelogsQuery+ handler cóEnsureCanViewAsync(rào hàng, KHÔNG chép lỗ #82 của PE) [BE-5].src/Backend/SolutionErp.Domain/ContractSigningPlans/ContractSigningPlanAttachment.cs— enum+ApprovalAttachment = 3, KHÔNG đánh số lạiOther = 99[BE-7].src/Backend/SolutionErp.Api/Controllers/ContractSigningPlansController.cs— (a)GET {id}/changelogspolicyKeHoachKyKet.Read[BE-5/B-5]; (b)POST {id}/approval-attachmentspolicyKeHoachKyKet.Update, épPurpose=ApprovalAttachmentở BE (FE không truyền số enum) [BE-7/QĐ-3].
🔴 BE-3 — khai lý do chọn đường THỨ BA (spec cho 2 lựa chọn)
Spec: "lấy tên từ changelog cùng transition hoặc denorm cột tên vào Approval. Chọn 1, khai lý do." Tôi chọn join Users, vì:
- Ghép theo changelog KHÔNG CÓ KHOÁ CHÍNH XÁC:
Approval.ApprovedAt=nowcủa domain (ContractSigningPlanWorkflowService.cs:532), cònChangelog.CreatedAtdoAuditingInterceptorđóng lúcSaveChanges⇒ hai giá trị lệch nhau vài ms. Ghép được thì phải dùng "bucket thời gian" — đúng cái hack 5s mà PE đang phải chịu ở FE (PeDetailTabs.tsx:3152-3192), tức nhập thêm nợ chứ không trả nợ. - Denorm cột tên = cần MIGRATION (Mig 72) — ngoài phạm vi đợt UI này, và đợt này không có mục migration nào khác để đi kèm.
- Chi phí thật của join: 1 query batch trên tập
DISTINCT ApprovedByUserIdcủa đúng 1 phiếu (thực tế ≤ ~6 người). Handler này vốn đã multi-query (pe · project · dept · drafter · suppliers · workflow), không phải projection 1 phát.
3. Tự kiểm B-1 · B-2 · B-4 · B-5 · B-6 (số ĐO được, không phải tuyên bố)
| Thước | Yêu cầu | Đo được | Kết |
|---|---|---|---|
| B-1 | 5 tên mới trong record ContractSigningPlanDetailDto -A 80 |
WorkflowVersion :216 · EndedByLevelFinalize :217 · FinalizeStepName :218 · FinalizeLevelName :219 · List<ContractSigningPlanApprovalDto> Approvals :220 |
✅ 5/5 |
| B-2 | grep -c ContractSigningPlanApprovalDto src/Backend ≥ 2 (hôm nay 0) |
3 (1 định-nghĩa + 1 field DetailDto + 1 call-site projection) | ✅ |
| B-3 | 🔴 RUNTIME: phiếu DaDuyet ⇒ approvals[] có ≥1 Decision=Reject và approvedByFullName ≠ null ∀ phần tử |
CHƯA CHẠY ĐƯỢC — xem §4 | ⛔ treo |
| B-4 | DepartmentName trong …WorkflowStepDto ≥ 1 |
1 | ✅ |
| B-5 | grep -n changelogs <controller> = 1 hit + [Authorize(Policy = "KeHoachKyKet.Read")] liền kề |
1 hit (:91), policy ở :92 ngay dưới [HttpGet] — đúng convention file (mọi action đều [Http*] rồi [Authorize(Policy…)]) |
✅ |
| B-6 | R = ^\s*\[Http(Get|Post|Put|Delete|Patch) · ^\s*\[Authorize\(Policy = 1+R · ^\s*\[Authorize\]\s*$ = 0 |
R = 20 (18 cũ + changelogs + approval-attachments) · Policy = 21 = 1+20 ✅ · trần = 0 ✅ | ✅ |
| build | dotnet build SolutionErp.slnx |
Build succeeded · 0 Warning · 0 Error (lượt 1 FAIL CS1739 dossierItemId → sửa thành DossierItemId, lượt 2+3 sạch) |
✅ |
| tsc | fe-admin + fe-user tsc -b |
exit 0 cả hai (types-only, mọi consumer đọc từ response — không nơi nào dựng object literal) | ✅ |
| E-1 | mirror types/khkk.ts SHA256 |
aa4103de6362e67e68d026e125fe65a33efb92af4bd6ab811d6e0d9db3610812 ×2 app trùng khít |
✅ |
| E-2 | 0 hit 4 path cấm + 2 path nhạy (hooks/usePipelineStages.ts, components/pipeline/) |
diff lane này = đúng 5 file (3 BE + 2 types/khkk.ts), 0 path cấm, KHÔNG chạm hook/pipeline |
✅ |
🧪 Fault-injection — chứng thước B-6 CÓ RĂNG (không chỉ happy-path PASS)
Cắm 1 action [Authorize] trần vào cây tạm (…/scratchpad/injected.cs, KHÔNG đụng repo):
- gauge 3
^\s*\[Authorize\]\s*$: 0 → 1 ⇒ FLAG đúng. - gauge 2 đồng thời vỡ: R 21 nhưng
Authorize(Policy)vẫn 21 ≠ 1+R=22 ⇒ FLAG lần hai. ⇒ Cả 2 vế của thước đều bắt được ca xấu; số xanh ở bảng trên KHÔNG phải do thước mù.
🔸 1 chỗ tôi tự sửa để thước B-5 khỏi bị bơm tử-số
Comment cảnh-báo #82 của tôi ban đầu chứa literal changelogs ⇒ grep -c ra 2 trên file 100% đúng — đúng lớp citation-trap mà chính spec vừa phải sửa cho B-6. Đã đổi chữ trong comment (endpoint lịch-sử), giữ nguyên nội dung cảnh-báo. Đo lại = 1.
4. Chưa làm / lý do (khai thẳng — lỗ có khai > báo xanh mà sai)
- B-3 (RUNTIME) — CHƯA CHẠY. Lý do đo được, không phải phỏng đoán: DB dev
(localdb)\MSSQLLocalDB / SolutionErp_DevcóContractSigningPlans= 0 hàng,DaDuyet= 0,ContractSigningPlanApprovals= 0 hàng ⇒ không tồn tại phiếu nào để gọi thật. Cần chạy ở UAT (lane E-5/E-6,fe-user:8080). Cận-dưới đã có từ code, KHÔNG thay được B-3:LogSigningPlanTransitionAsyncghiApprovalsthẳng trong thân hàm, khôngifnào bọc (…WorkflowService.cs:534), 6 call-site phủ trọn (:204:283:296:311:325:393—:393phủ CẢ Trả lại lẫn Từ chối cùngApprovalDecision.Reject); bộ lọc của tôi chỉ bỏPending⇒ hàngRejectkhông thể bị nuốt.approvedByFullNamenull CHỈ khiApprovedByUserIdnull (transition do system/SLA) hoặc user đã bị xoá — không có đường nào khác. - Không đụng
api/khkk.tscủa FE. Lane FE (nhóm 2-3) sở hữu file đó và đang sửa song song; tôi ghi hợp-đồng endpoint ở §5 thay vì chen vào (tránh đụng độ). Không phải "đã wire" — FE còn phải gọi. - Không viết test. Theo
docs/rules.md §7: đây là feature mới ⇒ test-after (UAT ổn rồi viết), không phải bug-fix/critical-algorithm.dotnet testchưa chạy trong lane này (E-4 là thước của lane reviewer, và build agent nền khác đang chạy — bài #68). - BE-3 chọn đường THỨ BA so với 2 lựa chọn spec đưa — lý do đầy đủ ở §2, tóm: ghép-theo-changelog không có khoá chính xác (2 timestamp khác nguồn), denorm cột cần migration ngoài phạm vi.
5. Hợp-đồng cho lane FE (literal — khỏi đoán)
GET /api/contract-signing-plans/{id}→KhkkDetailDto+5:workflowVersion: number|null·endedByLevelFinalize: boolean·finalizeStepName: string|null·finalizeLevelName: string|null·approvals: KhkkApprovalDto[]; và mỗiworkflowSteps[]+departmentName: string|null.GET /api/contract-signing-plans/{id}/changelogs?take=200→KhkkChangelogDto[](mới nhất trước). PolicyKeHoachKyKet.Read.POST /api/contract-signing-plans/{id}/approval-attachments— multipart, fieldfile(+notetuỳ chọn). BE tự éppurpose = ApprovalAttachment (3); FE KHÔNG gửi purpose. PolicyKeHoachKyKet.Update(cùng policy với/transitions⇒ người duyệt đã có quyền).- Khối "📎 File đính kèm khi duyệt" lọc
attachments.filter(a => a.purpose === KhkkAttachmentPurpose.ApprovalAttachment), rỗng ⇒ tự ẩn (D-9 vế 2). approvals[]BE đã lọcdecision = Pending⇒ FE KHÔNG dedupe lại;(N)=approvals.lengthlà cùng nghĩa với(N)của PE (D-11).
END implementer-backend-s171 — 7/7 mục BE xong (B-3 acceptance RUNTIME còn treo, đã khai) · build PASS