From 92d8215a6253d749794ca7551b5f0bc453ab08e9 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Tue, 11 Aug 2026 11:25:51 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20PurchaseEvaluation:=20YC-028=20s?= =?UTF-8?q?=E1=BB=B1=20c=E1=BB=91=20prod=20A/059=20=E2=80=94=20FE=20t?= =?UTF-8?q?=C3=A1ch=20"404=20th=E1=BA=ADt=20=E2=9F=82=20l=E1=BB=97i=20h?= =?UTF-8?q?=E1=BB=87=20th=E1=BB=91ng"=205=20site=20=C3=972=20app=20+=20gua?= =?UTF-8?q?rd=20disk/orphan=20v=C3=A0o=20runbook+cicd-monitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sự cố 2 tầng (đã giải trọn trong phiên, chi tiết runs/2026-08-10-S189-bookstart-open/run.md + WAL): - T1: ổ C VPS 0 byte (gitea repo-archive 16,49GB + npm-cache SYSTEM 12,76GB) → SQL Error -2 → "ko duyệt được" rồi FE vẽ mọi lỗi thành "Không tìm thấy phiếu" (#44). Dọn ~30GB cache + AUTO_CLOSE OFF ×3 DB + gỡ nén NTFS 4 file template (#88 tái phát). - T2: 3 sshd mồ côi (lệnh đo ssh bị cắt output) bão hòa 3/3 core → mọi GET detail 5-47s, A/059 (5 NCC) vượt trần hủy ~30s của FE. Kill orphan → A/059 47s→0,38s · phiếu bé 0,17s. Code (5 site cùng lớp, SHA-mirror 4 cặp MATCH, build ×2 xanh): - lib/apiError.ts +isNotFound(err) — chỉ 404 mới được nói "không tìm thấy" - PurchaseEvaluationsListPage (inline + overlay + fullpage) + KhkkListPage + KhkkCreatePage: detail.isError && !404 → "Hệ thống đang chậm hoặc lỗi khi tải phiếu — thử lại (refetch)" Ops-guard (YC-028 (4)): cicd-monitor persona +bước 1b server-health (fsutil free <5GB = FAIL, CPU-delta tìm orphan) · iis-deploy-runbook +§"Disk-free + orphan guard" + sửa path log Serilog sai (Logs\log-*.txt → logs\solution-erp-*.log, đo thật). (6) đóng: log 10/08 mất do đợt "giải phóng dung lượng" hôm qua (retention 30 file config đúng). (3B) cron archive_cleanup Gitea + decompress Vietreport_Master = chờ ngoài giờ (restart service dùng chung VIETREPORT). Sổ: +YC-028 (nguyên văn + 2 tầng đã xử). Squash wal: trailing theo §5.0. Co-Authored-By: Claude Opus 5 --- .claude/WAL.md | 56 +++++++++---------- .claude/agents/cicd-monitor.md | 8 +++ .claude/skills/iis-deploy-runbook/SKILL.md | 29 +++++++++- docs/governance/so-yeu-cau-chu-du-an.md | 1 + fe-admin/src/lib/apiError.ts | 8 +++ fe-admin/src/pages/khkk/KhkkCreatePage.tsx | 12 +++- fe-admin/src/pages/khkk/KhkkListPage.tsx | 11 +++- .../pages/pe/PurchaseEvaluationsListPage.tsx | 33 +++++++++-- fe-user/src/lib/apiError.ts | 8 +++ fe-user/src/pages/khkk/KhkkCreatePage.tsx | 12 +++- fe-user/src/pages/khkk/KhkkListPage.tsx | 11 +++- .../pages/pe/PurchaseEvaluationsListPage.tsx | 33 +++++++++-- 12 files changed, 175 insertions(+), 47 deletions(-) diff --git a/.claude/WAL.md b/.claude/WAL.md index ac6741a..97eb1bd 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -1,39 +1,33 @@ # WAL — auto-generated, không sửa tay updated: 2026-08-11 | session: S189 (phiên-LOGIC L15, window 1) | branch: main -# 🧊 Vá @S189 dòng nhãn phiên: bản trước gộp *conversation tiếp tục* (đúng, YC-026 sống nguyên) với -# *phiên-LOGIC chưa khép* (sai — `_end` L14, 6 bề mặt đồng thuận). Lineage → S189 `run.md`. - -goal: Bookstart L15 chạy TRỌN 1 lượt (YC-018/YC-027) ĐÃ XONG — 12 vai, 8 lượt #53, 0 byte mất. - Đang chạy loạt vá từ finding của đội. Phiên GIỮ MỞ chờ việc kế của anh (YC-026). +goal: SỰ CỐ PROD A/059 (YC-028) — ĐÃ GIẢI TRỌN 2 TẦNG: (T1) disk 0-byte do cache automation + (repo-archive 16,49GB + npm-cache 12,76GB) → SQL Error -2 → "ko duyệt được"/#44; + (T2) 3 sshd MỒ CÔI (lệnh đo của lead bị `head` cắt) bão hòa 3/3 core → mọi phiếu 5-47s, + A/059 vượt trần hủy 30s FE. SAU FIX: A/059 47s→0,38s · phiếu bé 0,17s · inbox 0,12s. chain: -[x] Bookstart @open TRỌN: V1 (H1 PASS_WITH_FLAGS-7 + H2 GATE-FAIL-5 → ring1 26Đ/4T/6KC/36) · - V2 (stale 11 FLAG + gap 7 FLAG → ring2 7Đ/3T/14KC/24, enum-gate 18/18) · V3 trio - (eval MIXED 38-trục/13-REGRESSION → refine 2-action/16-BÁC → audit 24Đ/7T/4KC re-chạy 98 số) · - V4 shard IM (NO-OP có khai) · V5 detector 58+26 · khối chấm điểm → KHÔNG ĐO ĐƯỢC (A8) · - Phase 3.5 `_mind-s-15` block-0 qua mind-check 10/0 + ctx-audit - → run-trace `.claude/workflows/runs/2026-08-10-S189-bookstart-open/` -[x] Vá 3 chỗ KHÔNG nằm bề-mặt-đo đóng băng: `CLAUDE.md:77` → `69, 71, 72` (ring1 phán - `ef-core-migration/SKILL.md` là DƯƠNG-GIẢ ⇒ vá 1 chỗ là đủ) · `contract-workflow/SKILL.md` - W5 "chưa làm" → ĐÃ LÀM `b275893c` (giữ vế liên-danh N-HĐ còn nợ THẬT) · WAL dòng 2 (trên) -[!] Loạt vá CÒN LẠI (chạy ngay sau khi ctx-audit đóng cửa mở — đều là bề-mặt-đo): - error-ledger entry TẦNG-2-SAI (L1+L2; 🔴 CẤM chạm `lead_self_audit.flag_classes` — đã niêm phong) · - HANDOFF lật 3 mục NEXT-em đã land `ef1d4fbb` (harvest-curator 21.308 · reviewer 85,7% · - measured 30 row 29/29) · `h24-signal-write.ps1` ghi tally 2 vai H24 · dọn stray dir RỖNG - trong run-folder (git MÙ với thư-mục rỗng ⇒ phải `find -type d -empty`) -[ ] chờ anh: (76) 2 governor sai đơn-vị [nay có chứng 0/29 vượt theo KÝ-TỰ] · (77) migration-todos - đóng băng S168 · (78) check-email:67 cãi P6 · (79) YC-002 · (89) 4 class vượt JUMP a/b/c · - (74) rotate · MỚI: value_protect phủ khối hợp-đồng-vai · MỚI: lật ô YC-008↔YC-010 · - YC-025 review 1 lần GĐ3 prod · "ổn rồi" mở W8-authz · tồn (22)(67)(72) -[ ] NEXT-em: gọi `ring5-audit` ở CẢ cửa MỞ (guard RCA ONESHOT-S188 hiện 0-hit `session-start.md`) · - khắc YC-027 vào `/tiep`+`/pause` (nay 2/4 cửa) + dựng phép kiểm ĐỦ-CỬA · sửa regex - `**🆕 Last updated:**` trong detector (máy bám mốc cũ 9 ngày) · [carry:sentinel-truoc-bu] · - [carry:reset-doi-het] · w7b-inbox-param +[x] Bookstart L15 TRỌN 12 vai + vá 7 bề mặt → push 9b3c0edb +[x] T1 disk: xóa repo-archive (cache Gitea tự tái tạo) + npm-cache SYSTEM + 567 u_ex cũ (0,73GB) + → free ~20GB+ · health 200 · AUTO_CLOSE OFF ×3 DB · gỡ nén 4 file template (#88 tái phát; + Vietreport_Master.mdf CÒN NÉN — đang mở, cần offline ~10s, làm ngoài giờ) +[x] T2 CPU: chẩn đoán ASYNC_NETWORK_IO + query [p7] CPU-66ms-elapsed-23,6s → đo CPU-delta bắt + 3 sshd orphan 6,3 core-s/5s trên máy 3-core → kill (chừa chain mình) → 0,17-0,41s ×2 lượt. + 🔴 BÀI HỌC MỚI: `ssh ... | head` = ĐẺ ORPHAN SPIN phía Windows-OpenSSH — cấm head trên + ssh output dài; recycle pool + đợi cold-start ~48s trước khi đo +[!] Hàng (1)-(6) YC-028 còn: (3B) bật cron archive_cleanup Gitea + decompress Vietreport_Master + [ngoài giờ — restart gitea vài giây] · (4) disk-watch vào cicd-monitor persona + runbook · + (5) FE tách "lỗi hệ thống"⟂"không tìm thấy" ×2 app + CÂN NHẮC AsSplitQuery cho + GetPurchaseEvaluationQuery:867 (6-include = mìn cartesian khi data lớn) · (6) truy log + 20260810 biến mất (đọc Serilog retention — lệnh trước chết vì quoting, chưa có data) +[ ] chờ anh (cũ): (76)(77)(78)(79) · (89) a/b/c · (74) rotate · value_protect · YC-008↔YC-010 · + YC-025 review GĐ3 · "ổn rồi" mở W8 · tồn (22)(67)(72) +[ ] NEXT-em (cũ, run.md S189): ring5 @open · YC-027 đủ-cửa · regex 🆕 detector · sentinel-truoc-bu · + reset-doi-het · w7b-inbox-param · gotcha #89 disk+orphan-sshd + RCA error-ledger -next: đóng cửa mở (ctx-audit) → chạy loạt vá còn lại → đợi anh. Có lệnh mới ⇒ ghi mạch mới rồi chạy. +next: chạy (4)(5)(6) + gotcha #89; (3B) chờ ngoài giờ. Anh em xác nhận phiếu OK thì báo. verify: - bookstart: ls .claude/workflows/runs/2026-08-10-S189-bookstart-open/*.md | wc -l # >= 12 - mind: python scripts/session_ctx.py mind-check --session 15 # exit 0 - 🔴 CẤM chạy `PROPOSE archive` cho ring2-audit — move-set = {Role baseline, Runs} ⇒ xoá THƯỚC vai KIỂM. + toc-do: curl (token admin) GET A/059 -> 200 <1s # da do x2 lan + cpu: ssh delta 5s -> chi gitea ~0,3 core # da do sau kill + free: ssh fsutil volume diskfree C: # ~20GB+ diff --git a/.claude/agents/cicd-monitor.md b/.claude/agents/cicd-monitor.md index e722df3..fc01a5b 100644 --- a/.claude/agents/cicd-monitor.md +++ b/.claude/agents/cicd-monitor.md @@ -121,6 +121,14 @@ $token = (curl -X POST https://api.solutions.com.vn/api/auth/login ` # Or UAT scope (non-admin): nv.test@solutions.com.vn / TestUser@123456 +# 1b. 🔴 SERVER-HEALTH GUARD (S189 2026-08-11 — sự cố YC-028 disk-full + orphan-sshd; chi tiết +# + lệnh đầy đủ = iis-deploy-runbook §"Disk-free + orphan guard"; chạy MỖI lần smoke): +ssh vietreport-vps "fsutil volume diskfree C:" # free < 5GB = FAIL verdict kèm cảnh báo +# free từng về 0 (repo-archive Gitea 16,5GB + npm-cache SYSTEM 12,8GB) → SQL Error -2 +# → FE hiện "Không tìm thấy phiếu" — TRIỆU CHỨNG DATA, GỐC HẠ TẦNG, đừng nghi oan phiếu. +# Kèm mắt thứ 2: smoke chậm bất thường (>5s cho GET detail) ⇒ đo CPU-delta tìm process +# mồ côi (sshd/powershell spin) THEO RUNBOOK trước khi kết luận code chậm. + # 2. Smoke 3-5 endpoint expected 2XX (include endpoint mới trong commit diff nếu có) curl -X GET https://api.solutions.com.vn/api/contracts -H "Authorization: Bearer $token" -w "%{http_code}\n" curl -X GET https://api.solutions.com.vn/api/purchase-evaluations -H "Authorization: Bearer $token" -w "%{http_code}\n" diff --git a/.claude/skills/iis-deploy-runbook/SKILL.md b/.claude/skills/iis-deploy-runbook/SKILL.md index ec73f12..ee91022 100644 --- a/.claude/skills/iis-deploy-runbook/SKILL.md +++ b/.claude/skills/iis-deploy-runbook/SKILL.md @@ -78,8 +78,9 @@ Get-Website -Name "SolutionErp-*" | Format-Table Name, State, Bindings ### Xem log API ```powershell -# Serilog file rolling daily -Get-Content "C:\inetpub\solution-erp\api\Logs\log-$(Get-Date -Format 'yyyyMMdd').txt" -Tail 50 +# Serilog file rolling daily (path ĐO THẬT @S189 2026-08-11 — bản cũ ghi `Logs\log-*.txt` SAI; +# config sống: appsettings.Production.json:42 `logs/solution-erp-.log`, retained 30, roll 100MB) +Get-Content "C:\inetpub\solution-erp\api\logs\solution-erp-$(Get-Date -Format 'yyyyMMdd').log" -Tail 50 # IIS log Get-Content "C:\inetpub\logs\LogFiles\W3SVC\u_ex$(Get-Date -Format 'yyMMdd').log" -Tail 30 @@ -99,6 +100,30 @@ curl http://127.0.0.1:5443/health/ready curl https://api.solutions.com.vn/health/ready ``` +### 🔴 Disk-free + orphan guard (S189 2026-08-11 — sự cố YC-028, PHẢI check khi prod chậm/500) + +```powershell +# 1. Disk C: — sự cố thật: 0 byte (repo-archive Gitea 16,5GB + npm-cache SYSTEM 12,8GB) +# → SQL Error -2 (không mở nổi connection) → FE hiện "Không tìm thấy phiếu" (#44 class) +fsutil volume diskfree C: # free < 5GB = ĐỎ; cache tự phình: C:\gitea\data\repo-archive + # (xóa an toàn — Gitea tự tái tạo) + npm-cache SYSTEM (CI) + +# 2. CPU orphan — sự cố thật: 3 sshd mồ côi (ssh bị cắt output giữa chừng) ăn 3/3 core +# → mọi API 5-47s, SQL wait ASYNC_NETWORK_IO (client đói CPU không nhận nổi data) +$a=@{}; Get-Process | ForEach-Object { $a[$_.Id]=$_.CPU }; Start-Sleep 5 +Get-Process | ForEach-Object { if($a.ContainsKey($_.Id)){ $d=$_.CPU-$a[$_.Id]; if($d -ge 1){ "$($_.Name) $($_.Id) $([math]::Round($d,1))" } } } +# sshd/powershell lạ ăn ≥1 core-s/5s → kill (CHỪA chuỗi tổ tiên session mình) + +# 3. NTFS compression trên .mdf/.ldf (gotcha #88 — "giải phóng dung lượng" nén khi ổ cạn) +Get-ChildItem 'C:\Program Files\Microsoft SQL Server\MSSQL16.SQLEXPRESS\MSSQL\DATA' -File | + Where-Object { $_.Attributes -match 'Compressed' } # có kết quả = compact /u (DB offline nếu đang mở) + +# 4. AUTO_CLOSE phải OFF (đã set @S189 — ON làm mỗi connection mở lại DB ~5s) +sqlcmd -S .\SQLEXPRESS -E -W -Q "SELECT name,is_auto_close_on FROM sys.databases WHERE database_id>4" +``` + +🔴 **Bài `ssh … | head` (S189):** cắt output sớm làm ssh client thoát nhưng **process phía Windows-OpenSSH chạy tiếp** → orphan spin ăn core. Lệnh ssh dài: bọc `Select-Object -First N` phía REMOTE, đừng `| head` phía local. + ## Let's Encrypt cert — win-acme ### Check trạng thái diff --git a/docs/governance/so-yeu-cau-chu-du-an.md b/docs/governance/so-yeu-cau-chu-du-an.md index ece4149..f972cc7 100644 --- a/docs/governance/so-yeu-cau-chu-du-an.md +++ b/docs/governance/so-yeu-cau-chu-du-an.md @@ -55,6 +55,7 @@ Dữ-liệu cũ (WAL · HANDOFF · auto-memory · migration-todos) **KHÔNG** đ | `YC-019` | "1 -> Tạm OK · 2 -> Role baseline + Runs · 3,4 -> giải thích · 5 -> OK kéo audit luôn giờ cứ end/start là cứ chạy · 6 -> Nợ cũ thì xử lý cho xong luôn" | 2026-08-10 · S185 | **đóng** *(6/6 vế thi hành @S185: (1) YC-013/016 đóng · (2) ring2 24.859B · (3)(4) đã giải thích → anh phán tiếp = YC-020 · (5) deep_every=1 · (6) nợ cũ đóng trọn)* | 1 | WAL chain (TASK → WAL) | 6 phán quyết một lượt: (1) acceptance 8-panel TẠM-OK ⇒ lật YC-013/016 nấc "tạm" · (2) C-6 = NÉN `Role baseline`+`Runs` của ring2 (khuôn moved-not-cut như lead-stale) · (3)(4) em giải thích W-1..W-4 + 5 SKIP-owner, chưa phán · (5) 🔴 ĐỔI NHỊP DEEP: từ nay **mỗi bookend end/start đều chạy deep** (deep_every → 1, hết chờ nhịp 15) · (6) uỷ quyền xử TRỌN nợ cũ theo bậc-2 LT3 (chọn nhánh an toàn + khai, việc-vật-lý-của-anh thì kê rõ) | | | `YC-024` | "(80) Đồng ý đúng, 7 loại ⏎ (81) Tức Duyệt NCC -> Chọn Duyệt NCC tạo KHKK -> KHKK (đã duyệt) -> Chọn để tạo HĐ ⏎ (82) Hiện phân quyền tất cả trước đi, sau khi ổn rồi thì phân quyền sau. ⏎ (83) Lấy tương tự như KHKK sau đó tao sẽ điều chỉnh sau, phần điều chỉnh trong Admin tao sẽ tự chủ động ⏎ (84) Đồng ý ⏎ (85) Đồng ý ⏎ (86) Đồng ý khóa cứng tương tự như KHKK và Duyệt NCC ⏎ (87) Chỗ này user có thể tự chọn KHKK cho loại Hợp đồng nào. ⏎ (88) Đồng ý ruột lấy từ các thông tin đã nhập (Có thể tham khảo 1 form Hợp đồng trước file word đã có, lấy làm mẫu trước, các thông tin đã nhập vào có thể lấy load lên, còn nếu chưa thì cho điền bổ sung, ví dụ như điều khoản)." | 2026-08-10 · S188 | **mở** | 1 | run `2026-08-10-S187-duyet-hd-tu-khkk/` spec + WAL *(thi-công → WAL)* | 9 phán quyết mở khoá plan 11-wave YC-023: (80) trục **7-LOẠI** · (81) **PA-A** + xác nhận chuỗi kế thừa PE→KHKK→HĐ · (82) *đội hiểu (bậc-2, khai)*: **MỞ TẤT CẢ trước** như posture hiện tại, phân quyền chi tiết = đợt SAU khi anh thấy ổn ⇒ **W8-authz HOÃN chờ lệnh anh**; guard đúng-trạm của máy duyệt V2 vẫn chạy (logic quy trình ≠ phân quyền) — anh bác cách hiểu thì em sửa · (83) W3 seed roster **SAO KHUÔN KHKK**, anh tự chỉnh trong Admin Designer sau ⇒ hết chặn cứng · (84) màn duyệt **2 nút** · (85) admin **giữ ẩn** ⇒ W9 HỦY · (86) **khoá cứng** quy trình theo loại, không đổi tay · (87) KHÔNG cột gợi-ý — user tự chọn loại HĐ khi tạo từ KHKK · (88) KHÔNG bảng hạng mục — **ruột HĐ pre-fill** từ dữ-liệu đã nhập + khuôn **form HĐ Word có sẵn** làm mẫu, field thiếu cho điền bổ sung (vd điều khoản) → vào W5/W6 | | | `YC-027` | "cái này sẽ là rules nhé, các cái command phải đc thực hiện trọn vẹn tao ko chấp nhận bất cứ 1 lý do nào" | 2026-08-10 · S188 | **mở** | 2 | 2 file lệnh + ACTIVE-MARKS (stamp chờ P4) | **NÂNG THÀNH RULE VĨNH VIỄN**: mọi command/nghi-thức (bookstart · bookend · /tiep · mọi lệnh anh gõ) phải chạy **TRỌN VẸN 100% các bước** — không vai nào được bỏ, không viện BẤT KỲ lý do nào (ngân sách · cạn context · "để lượt sau" · nghi-thức-đã-đóng). Đứt bất-khả-kháng (limit/crash) ⇒ lượt-nối đầu tiên chạy HẾT phần thiếu. Khắc vào `session-end.md` + `session-start.md` khối ONE-SHOT (trỏ YC-027/PN-001) ngay @S188; stamp User-Mark theo P4 report-before-stamp ở nhịp tới | | +| `YC-028` | "xóa bớt 1 tý để các bạn upload load phiếu rồi tính tiếp" | 2026-08-11 · S189 | **mở** *(vế "xóa bớt" ĐÃ XONG cùng lượt; vế "tính tiếp" treo chờ anh chọn số)* | 1 | WAL (mạch sống) + `runs/2026-08-10-S189-bookstart-open/run.md` | Bối cảnh: anh Kiệt báo phiếu `PE/2026/A/059` "ko duyệt được" rồi "ko tìm thấy phiếu" — chẩn đoán ra **ổ C VPS còn 0 byte** (free âm 20MB vào reserve) ⇒ SQL connection timeout ⇒ FE vẽ mọi lỗi thành "Không tìm thấy phiếu" (#44). Đội hiểu: giải phóng đĩa NGAY bằng mục AN TOÀN nhất — đã xóa cache `gitea\data epo-archive` **16,49GB** (cache chính chủ Gitea, tự tái tạo) → free **20,1GB**, API 200/401 chuẩn, 0 ERR mới; KHÔNG đụng DB/repo/uploads. "Tính tiếp" = bảng đánh số trình anh: npm-cache SYSTEM 12,76GB · inetpub\logs 2,15GB · chống tái phát repo-archive · disk-watch · FE tách lỗi-hệ-thống ⟂ không-tìm-thấy · truy log 10/08 biến mất | | | `YC-026` | "ko đóng phiên với bất cứ lý do nào" | 2026-08-10 · S188 (giữa lượt chạy-bù PN-001) | **mở** | 1 | WAL (mạch sống) | Phiên/conversation này KHÔNG được kết thúc — chạy hết mọi vai + việc tồn rồi **giữ phiên mở tiếp tục nhận việc**; CẤM viện bất kỳ lý do nào (ngân sách/context/nghi-thức-đã-đóng) để dừng. *(Đội hiểu thêm: `_end` phiên-LOGIC L14 đã ghi TRƯỚC lệnh này ~15 phút — dấu frozen không gỡ được; hiệu lực thực tế = conversation tiếp tục làm việc, mạch mới ghi WAL như thường. Anh bác cách hiểu thì em sửa.)* | | | `YC-025` | "OK làm tiếp hết đi, xong hết tao review 1 lần luôn" | 2026-08-10 · S188 | **mở** | 1 | WAL chain (thi-công → WAL) | Làm TRỌN các wave còn lại của YC-023/024 (W3 seed 7 quy trình + key Designer → W4 guard 4-cửa → W5 card nguồn-gốc + ruột pre-fill (88) → W6 màn duyệt 2-nút + Đ2-guard → W10 dry-run toàn trình) KHÔNG dừng chờ anh nhìn prod giữa wave; gate máy + reviewer-agent per-wave GIỮ NGUYÊN; push gom 1 lượt cuối (tránh #86 + tránh bản prod trung gian); anh review MỘT LẦN trên prod khi trọn. W8 vẫn HOÃN theo (82) "phân quyền sau", W9 vẫn HỦY theo (85) | | | `YC-018` | "2 phần bookend và bookstart -> trong đợt adapt này AI_INFRA cũng có hướng dẫn bọn mày chạy 1 mạch hết toàn bộ ko còn pause/tiep nữa, chú ý cấu hình cái đó kỹ nhé" | 2026-08-09 · S185 | **đóng** *(cấu hình land 4 site @S185 + chạy thật trọn 1 mạch bookend @close cùng đêm — nghiệm bằng vận hành; 🔴 nhắc lần 2 @S188 = PN-001: lead bỏ 3 vai @close với lý do "ngân sách" — owner bác, chạy nốt ngay trong lượt)* | **2** | WAL chain (TASK → WAL) | Nghi-thức 2 đầu phiên (`/session-start` bookstart + `/session-end` bookend) phải chạy **TRỌN TRONG MỘT LƯỢT** như hub kể ở thư 08-08 Phần 1b ("lượt mở phiên gần nhất chạy trọn trong một lượt duy nhất, không cần cửa tạm dừng nào chen giữa") — cấu hình luật ONE-SHOT vào 2 file lệnh, CẤM rải nghi-thức qua nhiều cửa pause/tiep, CẤM hẹn-cửa-sau. *(Đội hiểu: pause/tiep VẪN là recovery khi đứt bất-khả-kháng [limit/crash — H22 giữ nguyên]; thứ bị cấm là DÙNG chúng làm nhịp chia-chặng nghi-thức. Anh bác cách hiểu này thì em sửa.)* | | diff --git a/fe-admin/src/lib/apiError.ts b/fe-admin/src/lib/apiError.ts index d4eb450..e5bb075 100644 --- a/fe-admin/src/lib/apiError.ts +++ b/fe-admin/src/lib/apiError.ts @@ -11,3 +11,11 @@ export function getErrorMessage(err: unknown, fallback = 'Lỗi hệ thống'): } return fallback } + +// [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống/chậm — nền sự cố A/059: server nghẹt làm GET detail +// bị hủy sau ~30s, FE gom mọi nhánh lỗi vào "Không tìm thấy phiếu" ⇒ người dùng lẫn người chẩn +// đoán nghi oan DATA trong khi gốc là HẠ TẦNG (lớp gotcha #44 silent-error). Chỉ 404 mới được +// nói "không tìm thấy"; mọi lỗi khác phải nói thật là lỗi hệ thống + cho thử lại. +export function isNotFound(err: unknown): boolean { + return axios.isAxiosError(err) && err.response?.status === 404 +} diff --git a/fe-admin/src/pages/khkk/KhkkCreatePage.tsx b/fe-admin/src/pages/khkk/KhkkCreatePage.tsx index 39bab3b..f5f197f 100644 --- a/fe-admin/src/pages/khkk/KhkkCreatePage.tsx +++ b/fe-admin/src/pages/khkk/KhkkCreatePage.tsx @@ -38,7 +38,7 @@ import { Textarea } from '@/components/ui/Textarea' import { KhkkDetailContent } from '@/components/khkk/KhkkDetailContent' import { KhkkWorkListPanel } from '@/components/khkk/KhkkWorkListPanel' import { api } from '@/lib/api' -import { getErrorMessage } from '@/lib/apiError' +import { getErrorMessage, isNotFound } from '@/lib/apiError' import { useAuth } from '@/contexts/AuthContext' import { KHKK_APPROVAL_GROUP_DEFAULT, @@ -245,8 +245,16 @@ export function KhkkCreatePage() { tại TRƯỚC câu cảnh-báo đó. Thứ duy nhất mất đi là đường quay lại màn rỗng "Chọn phiếu Duyệt NCC…" (`:256`), mà đó chỉ là chỗ hướng-dẫn, không phải đích. */} {viewPlan.isLoading &&
Đang tải…
} + {/* [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống — mirror PurchaseEvaluationsListPage. */} {!viewPlan.isLoading && !viewPlan.data && ( -
Không tìm thấy phiếu.
+ viewPlan.isError && !isNotFound(viewPlan.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {viewPlan.data && ( viewPlan.refetch()} onDeleted={() => { setViewPlanId(null); setEditPlan(false) }} /> diff --git a/fe-admin/src/pages/khkk/KhkkListPage.tsx b/fe-admin/src/pages/khkk/KhkkListPage.tsx index 7a9464a..c14bda0 100644 --- a/fe-admin/src/pages/khkk/KhkkListPage.tsx +++ b/fe-admin/src/pages/khkk/KhkkListPage.tsx @@ -38,6 +38,7 @@ import { PeDetailTabs } from '@/components/pe/PeDetailTabs' import { type PeDetailBundle } from '@/types/purchaseEvaluation' import { useAuth } from '@/contexts/AuthContext' import { api } from '@/lib/api' +import { isNotFound } from '@/lib/apiError' import { KhkkPhase, type KhkkDetailDto, @@ -232,8 +233,16 @@ export function KhkkListPage() { )} {selectedId && detail.isLoading &&
Đang tải…
} + {/* [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống — mirror PurchaseEvaluationsListPage. */} {selectedId && !detail.isLoading && !plan && ( -
Không tìm thấy phiếu.
+ detail.isError && !isNotFound(detail.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {selectedId && plan && ( detail.refetch()} onDeleted={() => setId(null)} /> diff --git a/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx b/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx index a0aa23c..ce4a67d 100644 --- a/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx +++ b/fe-admin/src/pages/pe/PurchaseEvaluationsListPage.tsx @@ -12,7 +12,7 @@ import { Input } from '@/components/ui/Input' import { Select } from '@/components/ui/Select' import { EmptyState } from '@/components/EmptyState' import { api } from '@/lib/api' -import { getErrorMessage } from '@/lib/apiError' +import { getErrorMessage, isNotFound } from '@/lib/apiError' import { cn } from '@/lib/cn' import type { Paged } from '@/types/master' import { @@ -651,8 +651,17 @@ export function PurchaseEvaluationsListPage() { /> )} {selectedId && detail.isLoading &&
Đang tải…
} + {/* [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống — đừng để lỗi 500/timeout đội lốt + "không tìm thấy" (sự cố A/059: phiếu CÒN NGUYÊN, server nghẹt). */} {selectedId && !detail.isLoading && !detail.data && ( -
Không tìm thấy phiếu.
+ detail.isError && !isNotFound(detail.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {selectedId && !isExpand && detail.data && ( Đang tải phiếu… )} {!detail.isLoading && !detail.data && ( -
Không tìm thấy phiếu.
+ detail.isError && !isNotFound(detail.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {detail.data && (
@@ -794,7 +810,16 @@ export function PurchaseEvaluationDetailPage() { }, }) if (detail.isLoading) return
Đang tải…
- if (!detail.data) return
Không tìm thấy phiếu.
+ if (!detail.data) { + // [S189 · YC-028(5)] mirror 2 site trên — 404 thật mới được nói "không tìm thấy". + if (detail.isError && !isNotFound(detail.error)) return ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) + return
Không tìm thấy phiếu.
+ } return (
navigate('/purchase-evaluations')} onDelete={() => del.mutate()} /> diff --git a/fe-user/src/lib/apiError.ts b/fe-user/src/lib/apiError.ts index d4eb450..e5bb075 100644 --- a/fe-user/src/lib/apiError.ts +++ b/fe-user/src/lib/apiError.ts @@ -11,3 +11,11 @@ export function getErrorMessage(err: unknown, fallback = 'Lỗi hệ thống'): } return fallback } + +// [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống/chậm — nền sự cố A/059: server nghẹt làm GET detail +// bị hủy sau ~30s, FE gom mọi nhánh lỗi vào "Không tìm thấy phiếu" ⇒ người dùng lẫn người chẩn +// đoán nghi oan DATA trong khi gốc là HẠ TẦNG (lớp gotcha #44 silent-error). Chỉ 404 mới được +// nói "không tìm thấy"; mọi lỗi khác phải nói thật là lỗi hệ thống + cho thử lại. +export function isNotFound(err: unknown): boolean { + return axios.isAxiosError(err) && err.response?.status === 404 +} diff --git a/fe-user/src/pages/khkk/KhkkCreatePage.tsx b/fe-user/src/pages/khkk/KhkkCreatePage.tsx index 39bab3b..f5f197f 100644 --- a/fe-user/src/pages/khkk/KhkkCreatePage.tsx +++ b/fe-user/src/pages/khkk/KhkkCreatePage.tsx @@ -38,7 +38,7 @@ import { Textarea } from '@/components/ui/Textarea' import { KhkkDetailContent } from '@/components/khkk/KhkkDetailContent' import { KhkkWorkListPanel } from '@/components/khkk/KhkkWorkListPanel' import { api } from '@/lib/api' -import { getErrorMessage } from '@/lib/apiError' +import { getErrorMessage, isNotFound } from '@/lib/apiError' import { useAuth } from '@/contexts/AuthContext' import { KHKK_APPROVAL_GROUP_DEFAULT, @@ -245,8 +245,16 @@ export function KhkkCreatePage() { tại TRƯỚC câu cảnh-báo đó. Thứ duy nhất mất đi là đường quay lại màn rỗng "Chọn phiếu Duyệt NCC…" (`:256`), mà đó chỉ là chỗ hướng-dẫn, không phải đích. */} {viewPlan.isLoading &&
Đang tải…
} + {/* [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống — mirror PurchaseEvaluationsListPage. */} {!viewPlan.isLoading && !viewPlan.data && ( -
Không tìm thấy phiếu.
+ viewPlan.isError && !isNotFound(viewPlan.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {viewPlan.data && ( viewPlan.refetch()} onDeleted={() => { setViewPlanId(null); setEditPlan(false) }} /> diff --git a/fe-user/src/pages/khkk/KhkkListPage.tsx b/fe-user/src/pages/khkk/KhkkListPage.tsx index 7a9464a..c14bda0 100644 --- a/fe-user/src/pages/khkk/KhkkListPage.tsx +++ b/fe-user/src/pages/khkk/KhkkListPage.tsx @@ -38,6 +38,7 @@ import { PeDetailTabs } from '@/components/pe/PeDetailTabs' import { type PeDetailBundle } from '@/types/purchaseEvaluation' import { useAuth } from '@/contexts/AuthContext' import { api } from '@/lib/api' +import { isNotFound } from '@/lib/apiError' import { KhkkPhase, type KhkkDetailDto, @@ -232,8 +233,16 @@ export function KhkkListPage() { )} {selectedId && detail.isLoading &&
Đang tải…
} + {/* [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống — mirror PurchaseEvaluationsListPage. */} {selectedId && !detail.isLoading && !plan && ( -
Không tìm thấy phiếu.
+ detail.isError && !isNotFound(detail.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {selectedId && plan && ( detail.refetch()} onDeleted={() => setId(null)} /> diff --git a/fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx b/fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx index a0aa23c..ce4a67d 100644 --- a/fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx +++ b/fe-user/src/pages/pe/PurchaseEvaluationsListPage.tsx @@ -12,7 +12,7 @@ import { Input } from '@/components/ui/Input' import { Select } from '@/components/ui/Select' import { EmptyState } from '@/components/EmptyState' import { api } from '@/lib/api' -import { getErrorMessage } from '@/lib/apiError' +import { getErrorMessage, isNotFound } from '@/lib/apiError' import { cn } from '@/lib/cn' import type { Paged } from '@/types/master' import { @@ -651,8 +651,17 @@ export function PurchaseEvaluationsListPage() { /> )} {selectedId && detail.isLoading &&
Đang tải…
} + {/* [S189 · YC-028(5)] 404 thật ⟂ lỗi hệ thống — đừng để lỗi 500/timeout đội lốt + "không tìm thấy" (sự cố A/059: phiếu CÒN NGUYÊN, server nghẹt). */} {selectedId && !detail.isLoading && !detail.data && ( -
Không tìm thấy phiếu.
+ detail.isError && !isNotFound(detail.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {selectedId && !isExpand && detail.data && ( Đang tải phiếu…
)} {!detail.isLoading && !detail.data && ( -
Không tìm thấy phiếu.
+ detail.isError && !isNotFound(detail.error) ? ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) : ( +
Không tìm thấy phiếu.
+ ) )} {detail.data && (
@@ -794,7 +810,16 @@ export function PurchaseEvaluationDetailPage() { }, }) if (detail.isLoading) return
Đang tải…
- if (!detail.data) return
Không tìm thấy phiếu.
+ if (!detail.data) { + // [S189 · YC-028(5)] mirror 2 site trên — 404 thật mới được nói "không tìm thấy". + if (detail.isError && !isNotFound(detail.error)) return ( +
+ Hệ thống đang chậm hoặc lỗi khi tải phiếu —{' '} + . +
+ ) + return
Không tìm thấy phiếu.
+ } return (
navigate('/purchase-evaluations')} onDelete={() => del.mutate()} />