--- name: daoyou-data-layer description: Daoyou Drizzle/PostgreSQL、事务、V6 角色与宗门归属、统一背包、Redis 战局和回放归档指南。Use when modifying schema, migrations, repositories, persistence mappers, resource commits or durable game models. Covers Better Auth's separate migration stream and legacy storage boundaries. --- # Daoyou Data Layer ## Locate the Current Write Path Read `drizzle.config.ts`, `drizzle.auth.config.ts`, `apps/api/src/lib/drizzle/db.ts` and the relevant definitions in `apps/api/src/lib/drizzle/schema.ts`. Follow the route → service → repository/SQL path before treating a table or DTO as authoritative. Historical migration/design files describe intent; they do not prove a deployed database has applied it. ## Database and Transactions - Business tables use the main `wanjiedaoyou_*` migration stream in `drizzle/`. Better Auth uses `apps/api/src/lib/auth/schema.ts`, `drizzle.auth.config.ts` and `drizzle-auth/`, with fixed `better_auth` schema and independent history. - Nest `apps/api/src/database/database.module.ts` exports the same client via `DRIZZLE_DATABASE` and `DatabaseService`. Runtime drains requests/messages before closing it. Connection settings come from validated `getRuntimeEnvironment()`; do not create feature-local pools. - Runtime uses a module-level `pg.Pool` and `drizzle-orm/node-postgres`, not Bun SQL. `DATABASE_URL` supplies the connection; `DB_MAX_CONNECTIONS` controls pool size. Session settings live in `db.ts`. - Reuse `db`, `getExecutor(tx?)`, `DbExecutor` and `DbTransaction`; do not create parallel DB layers or feature-local pools. - Pass the executor through every nested write. `runDbTasks(executor, tasks)` serializes work on a transaction's single connection and permits parallel pool reads; do not replace it with unconditional `Promise.all`. - Reuse the owning mutation flow (`CommandExecutors.ts`, `InventoryService.ts`, or the relevant V6 service). Preserve locks, ownership predicates, expected revisions, idempotency and resource event commits; raw SQL success alone does not complete a player mutation. - `ResourceEventCommitter.ts` and `playerStateRepository.ts` maintain resource scopes/versions/events and request records. Publish updates only through the existing post-commit path. ## Authoritative Models Table names below omit the `wanjiedaoyou_` prefix. The exact names and constraints are defined in `schema.ts`. | Domain | Storage | Runtime entrypoints | | --- | --- | --- | | Character identity, permanent six attributes, condition | `cultivators` | `cultivatorRepository.ts`, `cultivator/application/readers`, V6 condition services | | Personal manuals and active slots | `cultivator_manual_states`, `cultivator_manual_slots` | `characterLoadoutRepository.ts`, `CombatV6ManualService.ts` | | Bag/storage item instances | `inventory_items` | `InventoryService.ts`, `packages/game-rules/src/inventory`, `packages/game-domain/src/items` | | Equipped V6 equipment | `cultivator_equipment_slots` | `characterLoadoutRepository.ts`, `InventoryService.ts` | | Beasts and lineups | `cultivator_beasts`, `cultivator_beast_lineups` | `combatV6BeastRepository.ts`, `CombatV6BeastService.ts` | | Sect progression | `sect_combat_states`, `sect_method_progress`, `sect_meridian_loadouts`, `sect_meridian_nodes` | `sectCombatRepository.ts` | | V6 history/replays | `combat_replay_archives`, `combat_replay_participants` | `combatV6ReplayRepository.ts` | Repository names resolve under `apps/api/src/lib/repositories`; V6 service names resolve under `apps/api/src/combat/application`. ### Ownership and JSON - Personal assets belong directly to `cultivators.id`. Sect state, methods and loadouts belong to `sect_memberships.id`; nodes belong to a loadout. Do not recreate a shared build-profile parent or transfer personal assets with sect progression. - Equipment slots reference inventory owner and item ID together. Preserve that composite FK and clear equipment references through the existing mutation flow when moving/removing items. - Inventory stores definition, quantity, location, slot, revision and instance facts. Parse via `InventoryItemSchema`, definition-specific schemas and `inventoryItemOf`; use existing stack-key helpers. A domain `spec` field does not imply storage in the old `consumables` table. - Current bag consumables use `inventory_items` with definition `consumable.v1`; `BagConsumables.ts` parses `instanceData` into consumable facts including `spec`. - Beast row ID/owner are authoritative; use `beastIndividualData` / `beastFromRow` rather than duplicating identity in JSON. Starter claim time is independent of beast lifetime. - Runtime combat assembly uses independent personal assets plus optional sect progress for display. Do not persist projected units/panels as the character build authority. See `docs/combat-domain-ownership.md`. - `cultivators.condition` remains the persistent condition field. Do not restore `persistent_state` / `persistent_statuses` or old consumable `effects` / `use_spec` / `details` contracts. ### Redis, Messages and Replays - Redis is authoritative for active V6 battle state, commands and RNG. Inspect `CombatV6RuntimeStore.ts` and the mode-specific stores for CAS revisions, occupancy, expiry and outboxes; do not substitute process-local sessions. - Access Redis through `apps/api/src/lib/redis`; NATS through `apps/api/src/lib/nats`. `apps/api/src/runtime/messaging/combatV6Messaging.ts` coordinates terminal/replay publication and archival. - PostgreSQL V6 archives enforce source/idempotency uniqueness. Replay participants intentionally do not cascade from character deletion; the battle archive owns their lifecycle. - Keep settlement, resource events and replay delivery idempotent across retries. Read `docs/nats-domain-events.md` together with the relevant consumer before changing message boundaries. ## Legacy Boundary - `creation_products`, `materials` and `consumables` still have residual code paths. Inspect their actual callers before modifying or deleting them; they are not fallback sources for V6 equipment/manuals or the unified bag. - Legacy product views use `packages/game-domain/src/legacy/products.ts`. Do not restore creation-v2 rehydration / `battleProjection` or treat `creation_products.is_equipped` as current V6 equipment state. - `/api/battle-records/*` has been removed. V6 history uses the combat replay repository, not `battle_records_v2`. - `battle_records_v3`, `battle_replay_archives` and `bet_battles` are deprecated historical schema, with physical deletion deferred to a separate future migration. Read `docs/combat-v6-legacy-table-retirement.md` for that policy, but verify exact table names against schema. Do not generate DROP migrations simply while cleaning up skills or legacy references. - Do not infer that every older table is still present, or already physically deleted, from a DTO, directory or migration file alone. ## Verify - Business schema changes: inspect generated SQL, snapshot and `drizzle/meta/_journal.json`; preserve published migrations. Apply only to the intended environment. - Auth schema changes: use `auth:generate` / `auth:migrate` with explicit environment selection, and inspect `drizzle-auth/` independently. - Persistence changes: inspect transaction propagation, JSON parsers, ownership and resource commits, then lint/build and focused local runtime checks per `docs/testing.md`. - Do not add repository, service, Redis or database unit tests. Pure reusable shared parsers/rules may have focused shared tests.