hyapp-h5/guild/ARCHITECTURE.md
2026-07-12 17:38:53 +08:00

96 lines
7.5 KiB
Markdown

# Guild H5 architecture
## Decision
The existing `gonghui/` tree is the Lalu legacy product and remains independently deployable. New guild products use a zero-build, static multi-package architecture under `guild/`:
```text
guild/
├── packages/
│ ├── core/ # Product context, date ranges, errors, state machines
│ ├── ui/ # Product-neutral center layouts and components
│ ├── host-center/ # Host feature package (added with the first host screen)
│ ├── agency-center/ # Agency feature package
│ ├── bd-center/ # BD feature package
│ └── wallet/ # Shared coin-seller and USDT withdrawal flow
├── products/
│ └── fami/ # Fami manifest, assets and visual overrides
└── fami/
├── host-center/ # Thin deployable page entry
├── agency-center/
├── bd-center/
└── wallet/
```
Each page entry composes packages with ordered classic `<script>` and `<link>` tags. There is deliberately no bundling step: the current repository is deployed as static files, so a build-system failure must not take down existing H5 pages.
## Package boundaries
| Package | Owns | Must not own |
| -------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------- |
| `common/` | Existing cross-H5 API transport, environment selection, i18n and base theme | Guild role semantics or Fami page state |
| `guild/packages/core` | Product context, shared time range model, request DTO mapping, async state primitives | DOM layout and product colors |
| `guild/packages/ui` | Responsive cards, filters, tables, empty/loading/error presentation | API paths or role permissions |
| `guild/packages/<feature>` | One role/flow's state, API adapter and render controller | Fami-only text, assets or hardcoded app code |
| `guild/products/fami` | `app_code=fami`, feature flags, Fami assets and permitted visual overrides | Shared business calculations |
| `guild/fami/<feature>` | HTML composition and page-only wiring | Reusable business logic |
## Runtime composition
Every Fami page must load scripts in this order:
```html
<script src="../../packages/core/product-context.js"></script>
<script src="../../products/fami/product.js"></script>
<script src="../../../common/api.js"></script>
<script src="../../../common/params.js"></script>
<script src="../../../common/i18n.js"></script>
```
`product.js` runs before `common/api.js`, making Fami the page-level default tenant even when an older client omits `app_code`. An explicit URL `app_code` still wins for diagnostics. Pages under the old `gonghui/` tree load none of these files, so their default remains Lalu.
## Shared domain rules from the initial specification
- Host, agency and BD statistics consume one date-range model: today, yesterday, last 7 days, last 30 days, this month, last month and custom range.
- Metrics are response-driven. The frontend renders values and definitions but does not recompute diamonds, gift senders, online duration, valid live duration/days, private-message users or new followers.
- Agency and BD list rows use stable IDs as keys. Removing a host or agency updates the view only after the server confirms the command.
- Wallet is a separate feature package because host, agency and BD reuse it. Coin-seller withdrawal and USDT withdrawal are separate state machines with separate validation, idempotency command IDs and histories.
- Permissions and feature switches come from backend capabilities. Functional switches default to enabled; only external addresses, secrets and third-party credentials may require environment configuration.
## Backend/admin contract direction
New endpoints should be app-scoped through `X-App-Code: fami` and return server-calculated metrics. Prefer additive `/api/v1/guild/...` contracts or additive fields on existing center endpoints; do not change Lalu response meaning in place.
The admin coin-seller withdrawal whitelist belongs to backend/admin storage. The H5 only consumes eligible coin sellers and must never treat a locally cached list as authorization.
### Implemented host statistics contract
`GET /api/v1/host-center/stats?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD` returns an inclusive UTC natural-day range and these server-owned metrics:
- `diamond_earnings`, `diamond_exchanged`, `gift_senders` from wallet-service POINT ledger entries;
- `online_duration_ms` from App session heartbeat spans, merged per day so overlapping sessions are not double-counted;
- `valid_mic_duration_ms`, `valid_mic_days` from user-service microphone daily facts;
- `private_message_senders` from authenticated Tencent IM `C2C.CallbackAfterSendMsg` events;
- `new_followers` from follow/unfollow delta events.
The Tencent C2C callback URL must include the matching product context, for example `app_code=fami`. The callback stores only event ID, sender, receiver and event time; message content is never written to the statistics table.
### Implemented Agency contract and revenue policy
`GET /api/v1/agency-center/stats?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD` returns the Agency summary and current host rows for the same inclusive UTC date range. Host earnings come from wallet POINT entries; engagement metrics come from user-service facts.
Agency share is configured in the shared Admin revenue-policy template JSON as `agency.point_ratio_percent`. The common legacy template defaults to `0`; the seeded `fami_guild_revenue_policy` template defaults to `20`. Publishing an App policy compiles the value into `wallet_policy_instances.agency_point_ratio_ppm`. Each successful gift atomically credits the owner POINT account and writes `agency_point_share_entries`, including the host, owner, ratio and policy instance snapshots. Historical earnings therefore do not move when a host later changes Agency.
### Implemented BD and wallet contracts
`GET /api/v1/bd-center/stats` aggregates the current BD's direct Agencies, active host counts and host gift POINT income without including Agency-share POINT. The page reuses the same date-range model and invitation acceptance flow as the existing organization domain.
The Fami wallet is a shared `guild/packages/wallet` feature mounted at `guild/fami/wallet/`. `GET /api/v1/point-wallet/overview` returns the POINT balance, published policy exchange/fee snapshot and the country-filtered coin-seller whitelist. POINT-to-seller transfers never accept an exchange ratio from H5: wallet-service locks `point_withdrawal_coin_seller_configs` and atomically debits `POINT` while crediting `COIN_SELLER_COIN`. Admin edits the same app-scoped table from `/host/point-withdrawal-config`; switching the selected App supports Fami, Huwaa, Lalu and future products without adding boolean columns.
## Adding another guild product
1. Add `guild/products/<product>/product.js` and optional assets/theme overrides.
2. Add thin entries under `guild/<product>/<feature>/`.
3. Reuse a feature package when its API/state semantics match; add a product adapter when DTOs differ.
4. Run `npm run test:guild-architecture` to prove the new default app code does not change legacy Lalu requests.