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

7.5 KiB

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/:

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:

<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.