67 lines
5.6 KiB
Markdown
67 lines
5.6 KiB
Markdown
# Data Flow
|
|
|
|
## Web Access Boundary
|
|
|
|
1. An anonymous browser requesting `/` is redirected to `/login` with an allowlisted return target. `/h5` and its H5
|
|
assets are public; anonymous access is limited to `/api/public/h5/months`, `/api/public/h5/analytics` and the
|
|
no-detail `/healthz` signal. Other anonymous API/download requests receive JSON `401 AUTH_REQUIRED`.
|
|
2. `POST /api/login` accepts strict same-origin JSON, verifies runtime-owned credentials under a bounded per-client attempt ledger and issues a random server-side session plus CSRF token in an `HttpOnly`, `SameSite=Strict` cookie.
|
|
3. Every desktop portal page, detailed health response, generic read API, upload/mutation, task trace and artifact
|
|
download validates the authenticated session. Public H5 endpoints return only sanitized aggregate dashboard data;
|
|
mutations additionally validate the session CSRF token.
|
|
4. `POST /api/logout` validates CSRF, revokes the server-side session and expires the cookie. `/healthz` remains a no-detail anonymous readiness signal for container orchestration.
|
|
5. `GET /api/history-months` is an authenticated read-only discovery path. It combines daily processing/monthly publication counts from PostgreSQL with durable company-job counts; each desktop history then reads its selected month through the existing paged list endpoint.
|
|
|
|
## Primary XML Flow
|
|
|
|
| Step | Source | Destination | Gate |
|
|
|---|---|---|---|
|
|
| 1 | Browser XML upload | Uploaded-basename task provenance + private canonical source object + queued DB job | Filename/content/size and immutable object identity |
|
|
| 2 | Committed source object | Isolated processor input | Materialization rechecks stored bytes |
|
|
| 3 | Fixed `process_daily.py` | Daily/result/structured or failure artifacts | Timeout, confined paths, exit/JSON agreement |
|
|
| 4 | Local artifacts | Private committed OSS objects | Role, MIME, size and SHA-256 |
|
|
| 5 | Canonical `DeliveryEnvelope` | `DeliveryValidator` | Strict Schema, reconciliation, source/artifact hashes and independent validation |
|
|
| 6 | Verified delivery | PostgreSQL Finance facts | Serializable atomic commit/version activation |
|
|
| 7 | Accepted/failed run | Task trace and outbox | Persisted state is authoritative |
|
|
| 8 | `arr.daily_version_committed` | Dedicated monthly worker | Lease, `SKIP LOCKED`, bounded retry/dead-letter |
|
|
| 9 | Retained Finance `ARRIVAL` facts | Monthly snapshot request | Affected month from event version; “更新至” is max included `ARRIVAL` |
|
|
| 10 | Monthly snapshot | XLSX/result + `reporting` metadata | Formula reopen-validation, current-pin recheck, atomic activation |
|
|
| 11 | Registered active report | Web list/download + outbox acknowledgement | Real report ID; visible monthly tab polls automatically; path/size/SHA-256 recheck before download |
|
|
|
|
## State Ownership
|
|
|
|
- PostgreSQL owns job/attempt/delivery state, Finance facts, current version and outbox.
|
|
- PostgreSQL `reporting` owns monthly publication versions, daily-version lineage, channel manifest and artifact identities; it does not duplicate monthly business rows.
|
|
- OSS owns immutable source/output bytes; ARR stores only controlled object identities in the database.
|
|
- `processing_runs.uploaded_filename` owns the user-facing browser basename; the source artifact remains canonically
|
|
named `source.xml` for processing and validation.
|
|
- Temporary processor/validator paths are deleted after each request.
|
|
- Monthly XLSX/result bytes live in immutable local archive paths on the current shared output volume; generated report artifacts are derived outputs, not an alternative business fact source.
|
|
|
|
## Booking Source And Company-Report Flow
|
|
|
|
| Step | Source | Destination | Gate |
|
|
|---|---|---|---|
|
|
| 1 | Authenticated Booking XLSX upload | Bounded parser 2.0 | Requires Tour Code + exact `โรงแรม`, latest-row/cancellation semantics and room-item extraction/review |
|
|
| 2 | Parsed raw items | Private artifact + extraction draft | Live migration 015 preserves source coordinates, confirmed/pending/deleted item state and extracted values |
|
|
| 3 | Human review | Draft item updates | Confirm/edit or atomically soft-delete 1-50 unique selected items while canonical current Booking source remains unchanged |
|
|
| 4 | Fully reviewed draft | Accepted canonical batch + `booking.current_source_batch` | Live 014/015 plus repository zero-pending/atomic activation; real PostgreSQL transaction-only vertical slice passes |
|
|
| 5 | Current Finance facts | Company-report stay segments | Finance supplies company/channel/dates/pricing; normalized Group Code is the Booking lookup key; open draft blocks new job |
|
|
| 6 | Current Booking room summary | `Booking Room` output | Sum by Group Code + room type; missing or unmatched Group Code leaves Booking Room blank |
|
|
|
|
Migrations 014/015 are formally applied: batch 1 is the current source and the draft tables are empty. The source
|
|
contains a PostgreSQL draft repository, backend routes, browser editor and GET/POST/PATCH/DELETE transport coverage.
|
|
A real-PostgreSQL transaction-only probe created a draft, confirmed its pending item, activated immutable facts and then
|
|
rolled everything back, preserving the current source and leaving no synthetic residue. First activation of an actual
|
|
operator workbook remains a controlled business action.
|
|
|
|
## Monthly Derivation Boundary
|
|
|
|
The worker never infers a date from an XML filename or the current clock. It treats the event's `daily_version_id` only
|
|
as a lookup key, derives scope from retained Finance `ARRIVAL`, and verifies that the generated report's maximum arrival
|
|
equals its persisted `as_of_date`. Every data-row `TOTAL PRICE` cell is `=R[row]*C[row]*G[row]`.
|
|
|
|
## Last Updated
|
|
|
|
2026-07-31
|