Skip to content

SAPP-20603 eContract (Retry · Check luồng · Thống kê lỗi) - FS Overview ​

File tổng quan + index của FS. Mỗi User Story là 1 file riêng trong cùng thư mục (US-XX-<slug>.md). Các phần dùng chung (tổng quan, flow tổng, related features, NFR, Open Questions, References) đặt tập trung tại đây — file US chỉ tham chiếu ID, không lặp lại.

⚠️ As-built, dựng từ code tại commit e77c47a — chưa chạy màn, chưa gọi API. Hành vi cron của hệ thống econtract (repo econtract-payment của SAPP, ngoài repo này) chỉ biết qua tài liệu shared/api-docs/sapp.md — mọi câu về cron đều ghi rõ "theo tài liệu".

FieldValue
Doc IDFS-SAPP-20603
ModeB – As-built
As-built tại commite77c47a
Last Updated2026-09-30
AuthorAI (lane ba/)
ReviewersBA
StatusDRAFT
Figma ProjectN/A — as-built, không có Figma
Related BRD../../brd/brd-as-built-v0.1-20260930.md
Related Impact MatrixN/A — Mode B, không có Impact Matrix

1. Tổng quan dự án &ZeroWidthSpace;

Nhóm 3 sub-tool "SAPP 20603 - EContract" trong workspace /works/sapp/tools làm việc trực tiếp trên DB econtract PRODUCTION của SAPP: kiểm tra và chạy lại (retry) luồng hợp đồng điện tử của một HubSpot deal bị lỗi (sapp.005), dựng lại timeline xử lý hợp đồng của một deal (sapp.012), và thống kê lỗi job econtract theo khoảng thời gian kèm danh sách deal lỗi (sapp.013). Chỉ duy nhất thao tác Retry là ghi (đặt lại bộ đếm retry trên prod để cron của econtract chạy lại luồng và gọi nhà cung cấp EFY/FPT thật); mọi phần còn lại chỉ đọc.

1.1 Bối cảnh &ZeroWidthSpace;

  • Luồng hợp đồng điện tử của deal do hệ thống econtract của SAPP chạy bằng cron: reTryFlow mỗi phút chạy lại dòng FAIL chưa retry, reProcessFlow mỗi 5 phút cấp thêm 1 vòng (theo tài liệu shared/api-docs/sapp.md:2919-2931, đọc ở econtract-payment commit efbdb2d). Dòng đã hết vòng tự động thì chỉ retry tay mới chạy lại được.
  • Trước tool, việc điều tra / chạy lại cần truy cập DB econtract prod. Tool dùng 1 user DB cấu hình trong env_systems (SAPP_PROD_ECONTRACT_DB_*).
  • SAPP đặt điều kiện cho phần hiển thị: che token EFY/FPT, không export, link_file_pdf không bấm được (2026-09-25-sapp-20603-tra-loi.md §4.4).

1.2 Mục tiêu &ZeroWidthSpace;

  • Assumption (chưa có câu trả lời G-05 / OQ-E-12): giảm thời gian điều tra và xử lý deal có luồng hợp đồng điện tử lỗi mà không cần dev truy cập DB prod; theo dõi mức lỗi của luồng econtract theo thời gian.
  • Gợi ý từ nguồn SAPP (không phải KPI): tool là tool nội bộ, người dùng là đội vận hành; dữ liệu chi tiết để "đối chiếu khi điều tra" (2026-09-25-sapp-20603-tra-loi.md:230-232).
  • Không có KPI / ràng buộc thời gian nào được xác nhận.

1.3 In Scope &ZeroWidthSpace;

  • 3 màn: Retry econtract (/works/sapp/tools/econtract, sapp.005), Check luồng econtract (…/econtract-timeline, sapp.012), Thống kê lỗi econtract (…/econtract-error-stats, sapp.013).
  • 4 API /api/v1/sapp/prod/econtract/*: đọc flow process, retry (ghi prod), timeline, thống kê lỗi.
  • Liên kết chéo giữa 3 màn qua ?dealId=.
  • Các bảng ngoài được đọc: econtract_flow_processes, econtract_job_traces, contracts, econtract_webhook_logs (DB econtract prod); cache Redis mốc job trace cũ nhất.

1.4 Out Of Scope &ZeroWidthSpace;

  • Khoá / ẩn / mã bí mật của tool, hub sidebar → FS nền tảng US-P01, US-P02.
  • Cơ chế @Public() / guard → FS nền tảng NFR-P-01 (xem NFR-E-01).
  • Cấu hình kết nối env_systems → FS nền tảng US-P04.
  • Pool kết nối DB ngoài (chỉ ghi ảnh hưởng múi giờ, OQ-E-05).
  • Cron reTryFlow / reProcessFlow và lời gọi EFY/FPT thật — nằm ở repo econtract-payment của SAPP.
  • Nút Retry ngay trong danh sách deal lỗi (việc còn mở T2-05 của audit — chưa có trong code, OQ-E-11).

1.5 Roles And Permissions &ZeroWidthSpace;

Role IDRole NameDescriptionPermission Summary
R1Người dùng toolAssumption (OQ về G-01) — nguồn SAPP gợi ý "đội vận hành" và "tester" (2026-09-25-sapp-20603-tra-loi.md:225-230), chưa được BA xác nhậnDùng cả 3 màn khi tool đang bật / đã mở khoá ở FE (US-P02). API không đòi đăng nhập — xem NFR-E-01

2. Luồng tổng quan của dự án &ZeroWidthSpace;

text
[Hub /works/sapp/tools — US-P01, US-P02]
  ├─→ [Retry econtract (sapp.005)]
  │     US-E01 Kiểm tra flow process
  │       ├─ Dừng ─────────────→ US-E03 Copy thông tin gửi dev
  │       ├─ Hệ thống tự chạy ─→ (chờ cron, đọc lại — như US-E02 M2)
  │       └─ Đủ điều kiện ─────→ US-E02 Retry (⚠️ GHI PROD) → Chờ cron → Kết quả
  │                                 ├─ SUCCESS ──→ US-E03 link → US-E04
  │                                 └─ khác ─────→ Kiểm tra lại (US-E01, Lượt N+1)
  ├─→ [Check luồng econtract (sapp.012)] US-E04 Timeline (chỉ đọc)
  └─→ [Thống kê lỗi econtract (sapp.013)] US-E05 Thống kê (chỉ đọc)
                                            └─ "Danh sách" → US-E06 Deal lỗi → mở US-E04 (tab mới)

Phía econtract-payment (theo tài liệu, không có trong repo này — shared/api-docs/sapp.md:2919-2931):

text
[FAIL · retry=0] --(reTryFlow, mỗi phút)--> chạy lại → [SUCCESS] | [FAIL · retry=1]
[FAIL · retry=1 · reprocess=false] --(reProcessFlow, mỗi 5 phút)--> [FAIL · retry=0 · reprocess=true]
[FAIL · retry=1 · reprocess=true] --(chỉ Retry tay của tool này)--> [FAIL · retry=0]
[NEW] / [SUCCESS] : không cron nào lấy

Link flow: N/A — không có flow vẽ riêng.

3. Các tính năng liên quan &ZeroWidthSpace;

MãTính năng liên quanLink tài liệuLoại liên quanMục bị ảnh hưởng ở feature kiaMục ảnh hưởng ở feature hiện tạiMô tả ảnh hưởng
REL-01Hub & khoá tool (nền tảng)US-P01 · US-P02Affects-Truy cập cả 3 màn (tool code sapp.005 / 012 / 013)Tool tắt / ẩn / khoá thì FE không cho vào màn; BE không kiểm tra (NFR-E-01)
REL-02Cấu hình env_systems (nền tảng)US-P04Affects-Mọi API của nhómThiếu host / tên DB / username của SAPP_PROD_ECONTRACT_DB_* → mã 13001, không màn nào chạy được
REL-03Cron econtract-payment + EFY/FPT (hệ thống SAPP, ngoài repo)shared/api-docs/sapp.md:2919-2931Be AffectedLượt cron reTryFlow kế tiếpUS-E02 action RetryRetry đặt retry = 0 trên prod → cron chạy lại luồng và gọi EFY/FPT thật (tạo / huỷ hợp đồng)
REL-04HubSpot deal-Affects-Ô "HubSpot deal id" của US-E01, US-E04Deal id chỉ là khoá tra cứu; nhóm này không gọi HubSpot và Retry không đổi deal stage

4. User Story Index &ZeroWidthSpace;

US IDTên User StoryFileRolePriorityStatus
US-E01Kiểm tra flow process eContract của một dealUS-E01-kiem-tra-flow-process.mdR1N/A — as-builtDraft
US-E02Retry các flow FAIL và chờ cron xử lý (⚠️ Ghi PROD)US-E02-retry-flow-fail.mdR1N/A — as-builtDraft
US-E03Chia sẻ kết quả retry (copy gửi dev, mở Check luồng)US-E03-chia-se-ket-qua.mdR1N/A — as-builtDraft
US-E04Xem timeline luồng eContract của một dealUS-E04-timeline-econtract.mdR1N/A — as-builtDraft
US-E05Thống kê lỗi eContract theo khoảng thời gianUS-E05-thong-ke-loi.mdR1N/A — as-builtDraft
US-E06Xem danh sách deal lỗi và trạng thái tự hồi phụcUS-E06-danh-sach-deal-loi.mdR1N/A — as-builtDraft

Status Draft = as-built chờ Gate R2. Truy vết hành động inventory: A-01, A-02 → US-E01 · A-03, A-04, A-05 → US-E02 · A-06, A-07 → US-E03 · A-08, A-09 → US-E04 · A-10, A-11 → US-E05 · A-12 → US-E06.

5. Non-functional Requirements &ZeroWidthSpace;

IDRequirementApplies To (US/màn/API)Acceptance Criteria
NFR-E-01Quyền truy cập API: theo NFR-P-01 của FS nền tảng — không lặp lại ở đây. Riêng nhóm này: 4 route /api/v1/sapp/prod/econtract/* đều thuộc diện đó, kể cả route Retry ghi DB prod (be/apps/api/src/sapp/sapp.controller.ts:174 · :193 · :213 · :233)US-E01…US-E06, 4 APIXem NFR-P-01. Rủi ro riêng của route ghi → OQ-E-01
NFR-E-02Bảo mật dữ liệu hiển thị (điều kiện SAPP): che request/response của step lấy access token EFY/FPT ngay ở backend; không đọc request_data (đường dẫn file trên server) và contract_info; dữ liệu chi tiết chỉ hiện dạng text JSON, link_file_pdf không bấm được; không có chức năng xuất file (chỉ copy deal id / thông tin tóm tắt vào clipboard)US-E01, US-E03, US-E04, US-E06Step getEfyAccessToken/getFptAccessToken hiện [hidden] (be/libs/constants/src/lib/sapp/sapp.constants.ts:169-177 · be/libs/modules/src/lib/sapp/common.service.ts:2284-2297); không có nút export trên 3 màn. Nguồn: 2026-09-25-sapp-20603-tra-loi.md:223-231
NFR-E-03Tải trên DB prod: không có index trên deal_id / created_at → mọi truy vấn quét toàn bảng; thống kê chạy 6 truy vấn song song mỗi lượt; pool tối đa 5 kết nối. Giới hạn: timeline 500 dòng / bảng; thống kê ≤ 1500 bucket, top 20 lỗi, bước lỗi trên 2000 lần chạy lỗi, 500 deal lỗi; mốc job trace cũ nhất cache Redis 1 ngàyUS-E04, US-E05, US-E06Giới hạn ở sapp.constants.ts:157-161 · :197-215; pool be/libs/services/src/lib/external-db/external-db.service.ts:31-49; cache common.service.ts:2252-2282. Không có rate limit — OQ-E-07
NFR-E-04Chịu lỗi từng bảng: bảng chưa tồn tại (MISSING_TABLE) hoặc user DB không có quyền đọc (ACCESS_DENIED) → bảng đó rỗng + cảnh báo, không làm hỏng cả lượtUS-E04, US-E05common.service.ts:2209-2232 · :1761-1766 · :1982-1991
NFR-E-05Múi giờ: timeline và thống kê đọc giờ theo đồng hồ DB econtract (gắn offset), hiển thị theo giờ máy người xem, có ghi tên múi giờ DB. Màn Retry không áp dụng cách này (OQ-E-05)US-E04, US-E05, US-E06 (US-E01/E02 ngoại lệ)common.service.ts:2200-2207 · :2234-2250; dòng "Giờ hiển thị theo máy của bạn · đồng hồ DB econtract" fe/src/ui/components/works/sapp/econtract-timeline-tool.tsx:794
NFR-E-06Nhật ký thao tác: hiện trạng không có ghi nhận ai / lúc nào / deal nào đã Retry trên prod — chỉ log lỗi phía BEUS-E02common.service.ts:2428-2487 · sapp.controller.ts:201-210. Có cần hay không → OQ-E-03
NFR-E-07Không thông báo: không gửi mail / push / MQTT / Discord trong cả nhómUS-E01…US-E06common.service.ts:1707-1906 · :1917-2176 · :2428-2487

6. Open Questions &ZeroWidthSpace;

IDQuestionOwnerDue DateStatusBlocks (US)
OQ-E-01Route Retry ghi DB econtract PROD nhưng không đòi đăng nhập (gọi thẳng API là bỏ qua được khoá tool ở FE; tài liệu ghi "Public (temporary)"). Có chủ đích tạm thời hay cần bảo vệ? Ai được phép Retry? (inventory Q-01 · G-02)BA / BETBDOpenUS-E02
OQ-E-02⚠️ Observed — một lần Retry đặt lại mọi dòng FAIL có retry ≥ 1 của deal, kể cả khi thuộc nhiều flow khác tên (vd CREATE_CONTRACT và EFY_CANCEL_CONTRACT cùng FAIL). Cố ý hay bug? Nên chỉ 1 flow / chuyển sang nhánh "liên hệ dev"? (inventory Q-02, R-06 · G-03; audit từng khuyến nghị nhánh liên hệ dev, quyết định D2 chỉ chốt trường hợp trùng tên)BATBDOpenUS-E02
OQ-E-03⚠️ Observed — không có nhật ký Retry (ai / lúc nào / deal nào). Có cần lưu vết thao tác ghi prod không? (inventory Q-03)BATBDOpenUS-E02
OQ-E-04Người dùng mục tiêu của 3 màn là ai (đội vận hành, tester, dev)? Mọi US Statement đang ghi "Người dùng tool" là Assumption. (inventory Q-04 · G-01)BATBDOpenUS-E01…US-E06
OQ-E-05⚠️ Observed — giờ "cập nhật" ở màn Retry đọc thẳng cột DATETIME qua driver, pool không đặt múi giờ → có thể lệch theo múi giờ của tiến trình BE (khác cách màn Check luồng đọc giờ). Cố ý hay bug? (inventory Q-05, R-09)BA / BETBDOpenUS-E01, US-E02, US-E03
OQ-E-06Màn Retry đọc lại ở giây 15 của phút kế tiếp, giả định cron reTryFlow chạy giây 0 mỗi phút; lịch cron lấy từ bản local econtract-payment efbdb2d, không phải bản deploy prod. SAPP xác nhận lịch cron prod? (inventory Q-06, R-11)BA → SAPPTBDOpenUS-E02
OQ-E-07API thống kê không đòi đăng nhập, mỗi lượt 6 truy vấn quét toàn bảng prod, không giới hạn tần suất. Cần rate limit / chỉ giờ hành chính? (SAPP đã nói "giờ hành chính ổn" ở ~0,11 s) (inventory Q-07)BA / BETBDOpenUS-E05
OQ-E-08⚠️ Observed — nếu ngữ cảnh giao diện không cấp hàm hộp xác nhận, nút Retry ghi prod ngay không hỏi (nhánh dự phòng). Thực tế provider có cấp. Chấp nhận hay phải chặn cứng? Kèm: hộp xác nhận không bật kiểu "thao tác nguy hiểm". (inventory Q-08, R-07)BA / FETBDOpenUS-E02
OQ-E-09⚠️ Observed — deal id không được kiểm tra định dạng (số) ở BE; FE không mã hoá deal id trong URL của đọc / retry flow process (timeline thì có). Cố ý hay bug? (inventory Q-09)BA / BE / FETBDOpenUS-E01, US-E02
OQ-E-10DR-E-01: cảnh báo cắt dữ liệu bật ở "≥ trần" chứ không phải "> trần" — sửa tài liệu hay sửa code? (chỉ BE quyết) (inventory Q-10)BETBDOpenUS-E05, US-E06
OQ-E-11Nút Retry ngay trong danh sách deal lỗi (audit T2-05 phần còn lại) — chưa có trong code; có thuộc phạm vi tài liệu / backlog không? (inventory Q-11)BATBDOpenUS-E06
OQ-E-12Mục đích nghiệp vụ của SAPP-20603: vì sao cần retry tay thay vì chờ cron; chỉ số "tỷ lệ lỗi" dùng để làm gì (báo cáo, cảnh báo)? Mọi "So that" và §1.2 đang là Assumption. (inventory Q-12 · G-05)BATBDOpenUS-E01…US-E06

6b. Drift: tài liệu ↔ code (Mode B/C — Mode A ghi N/A) &ZeroWidthSpace;

IDTài liệu nóiCode đang làm (bằng chứng file:dòng)Ảnh hưởngOwner quyết
DR-E-01shared/api-docs/sapp.md:3142-3144 (§GET error-stats, warnings[].code): TRUNCATED khi nhiều hơn 2000 lần chạy lỗi; DEALS_TRUNCATED khi nhiều hơn 500 deal (inventory DR-01)Cảnh báo bật khi ≥ 2000 / ≥ 500, tức cả khi đúng bằng trần — be/libs/modules/src/lib/sapp/common.service.ts:2109 · :2116Không phân biệt được "vừa đủ" với "bị cắt" (US-E05 BR-10, US-E06 BR-02)BE (OQ-E-10)
DR-E-02shared/api-docs/sapp.md:2901-2917 (mẫu GET flow-processes) ghi giờ ISO …Z; phần "Timestamps" (:3056-3059, đọc qua DATE_FORMAT + offset DB) chỉ nêu cho timeline, im lặng với flow-processes (inventory DR-02)GET flow-processes và response Retry đọc DATETIME trực tiếp, pool không đặt timezone → giờ phụ thuộc múi giờ tiến trình BE — common.service.ts:2405-2415 · be/libs/services/src/lib/external-db/external-db.service.ts:35-45. Mẫu tài liệu ghi rõ "written from source, not fetched live"Giờ "cập nhật" ở US-E01 / US-E02 / US-E03 có thể lệch (OQ-E-05)BE
DR-E-03be/.agents/maps/modules/tool.md:86: Retry chỉ chạy "khi có row FAIL còn retry" (inventory DR-03)Điều kiện là FAIL và retry > 0 = đã chạy lại ≥ 1 lần (retry đếm lần đã chạy); FAIL + retry = 0 bị từ chối 13006 — common.service.ts:2462-2468 · be/libs/constants/src/lib/error-codes/error-codes.constants.ts:543-551Người đọc bản đồ module hiểu ngược điều kiện Retry (US-E02 BR-03)BE

7. References &ZeroWidthSpace;

Template Conformance Check &ZeroWidthSpace;

Quét 2026-09-30, so mục lục từng file với ba/_workflow/templates/fs/User-Story.md: Metadata (US ID · Role · Phase · Priority · Status · NFR · OQ) · 1. US Statement · 2. Dependencies & DoR · 3. Business Rules (cột "Đã cover bởi (AC)" + "Nguồn code", mọi mã AC/EC được trỏ tới đều có trong file) · 4. Design (a. flow, b. mỗi màn: UI States · Fields · Filter/Search/Sort · Actions · AC G/W/T · Negative AC) · 5. Cross-feature AC. Section không áp dụng giữ heading + N/A — <lý do>.

US fileSection thiếuTrạng thái
US-E01-kiem-tra-flow-process.md(đủ) — 1 màn; Filter = N/A có lý do; DoR = N/A + dependency runtime✅
US-E02-retry-flow-fail.md(đủ) — 2 màn (M1 hộp xác nhận, M2 chờ cron & kết quả), mỗi màn đủ 6 mục; Filter = N/A có lý do✅
US-E03-chia-se-ket-qua.md(đủ) — 1 màn; Filter, Loading / Error / Disabled = N/A có lý do✅
US-E04-timeline-econtract.md(đủ) — 1 màn, có Filter/Search/Sort (lọc theo nguồn)✅
US-E05-thong-ke-loi.md(đủ) — 1 màn, có Filter/Search/Sort✅
US-E06-danh-sach-deal-loi.md(đủ) — 1 màn (modal), có Filter/Search/Sort✅

Tổng: 6 file US · 67 BR · 48 AC màn + 12 Cross-feature AC · 41 EC · 7 NFR · 12 OQ · 3 DR.