feat: 添加 points_audit_ledger 及 JSON 字段 Pydantic Schema 约束
This commit is contained in:
@@ -4,15 +4,17 @@ This protocol defines the canonical data contract for user profile, points accou
|
||||
|
||||
Protocol verification status:
|
||||
|
||||
- Last audited migration: `backend/alembic/versions/20260403_0004_remove_points_reason_code.py`
|
||||
- Last audited models: `backend/src/models/profile.py`, `backend/src/models/user_points.py`, `backend/src/models/points_ledger.py`, `backend/src/models/agent_chat_session.py`, `backend/src/models/agent_chat_message.py`
|
||||
- Current status: aligned
|
||||
- Last audited migration: `backend/alembic/versions/20260410_0005_add_points_audit_and_register_bonus_claims.py`
|
||||
- Last audited models: `backend/src/models/profile.py`, `backend/src/models/user_points.py`, `backend/src/models/points_ledger.py`, `backend/src/models/points_audit_ledger.py`, `backend/src/models/register_bonus_claims.py`, `backend/src/models/agent_chat_session.py`, `backend/src/models/agent_chat_message.py`
|
||||
- Current status: partially aligned (register bonus still runs in DB trigger, audit ledger tables are additive and ready)
|
||||
|
||||
## Scope
|
||||
|
||||
- `profiles`
|
||||
- `user_points`
|
||||
- `points_ledger`
|
||||
- `points_audit_ledger`
|
||||
- `register_bonus_claims`
|
||||
- `sessions`
|
||||
- `messages`
|
||||
|
||||
@@ -29,7 +31,8 @@ Protocol verification status:
|
||||
- Failure behavior: failed/canceled runs do not deduct points.
|
||||
- Precheck: before accepting a run, backend must verify `available = balance - frozen_balance >= 20`.
|
||||
- Session follow-up cap: one session allows at most 2 user runs total (initial divination + 1 follow-up).
|
||||
- Billing idempotency key for per-run consume: `chat.run.success:{session_id}:{run_id}`.
|
||||
- Billing idempotency key for per-run consume: `chat.run.success:{sha1(session_id:run_id)}`.
|
||||
- Failed/canceled runs do not deduct user points. If real provider cost is observed, audit record is written with `billed_to='platform'`.
|
||||
|
||||
## Table contract
|
||||
|
||||
@@ -74,6 +77,32 @@ Protocol verification status:
|
||||
- `adjust => direction in (1, -1)`
|
||||
- idempotency: `unique (user_id, event_id)`
|
||||
|
||||
### points_audit_ledger
|
||||
|
||||
- PK: `id`
|
||||
- No FK to `auth.users` for `user_id_snapshot` to avoid cascade delete and preserve audit retention
|
||||
- Core fields: `event_id`, `user_id_snapshot`, `user_email_snapshot`, `change_type`, `biz_type`, `biz_id`, `direction`, `amount`, `balance_after`, `billed_to`, `run_id`, `request_id`, `input_tokens`, `output_tokens`, `cost`, `metadata`, `created_at`, `updated_at`
|
||||
- Constraints:
|
||||
- `amount >= 0`
|
||||
- `direction in (1, 0, -1)`
|
||||
- `balance_after >= 0`
|
||||
- `change_type in ('register', 'consume', 'grant', 'adjust')`
|
||||
- `biz_type is null or biz_type='chat'`
|
||||
- `billed_to in ('user', 'platform')`
|
||||
- metadata must be object
|
||||
- idempotency: `unique (event_id)`
|
||||
|
||||
### register_bonus_claims
|
||||
|
||||
- PK: `id`
|
||||
- Core fields: `email_hash`, `user_email_snapshot`, `first_user_id`, `grant_event_id`, `created_at`, `updated_at`
|
||||
- Constraints:
|
||||
- `email_hash` unique
|
||||
- `grant_event_id` unique
|
||||
- Notes:
|
||||
- `email_hash` must be HMAC-SHA256 over normalized email (`trim + lower`)
|
||||
- key source: backend config `points_policy.register_bonus_hmac_key`
|
||||
|
||||
#### points_ledger.metadata (schema_version=1)
|
||||
|
||||
Canonical shape:
|
||||
@@ -110,18 +139,16 @@ JSON constraints:
|
||||
- `grant`: no extra metadata shape requirement
|
||||
- `adjust`: requires `ext.ticket_id` non-empty
|
||||
|
||||
## Signup initialization contract
|
||||
## Signup initialization contract (current + target)
|
||||
|
||||
- Trigger: `auth.users` after insert
|
||||
- Function: `public.initialize_profile_and_points_on_signup()`
|
||||
- Side effects:
|
||||
- create `profiles` row with default settings
|
||||
- username format: `user_xxxxxx` (`x` = 6 chars from `[a-z0-9]`)
|
||||
- create `user_points` row with initial `balance=100`, `lifetime_earned=100`
|
||||
- create `points_ledger` register row:
|
||||
- `change_type='register'`
|
||||
- `biz_type=null`, `biz_id=null`
|
||||
- `amount=100`, `direction=1`, `balance_after=100`
|
||||
- Current trigger:
|
||||
- Trigger: `auth.users` after insert
|
||||
- Function: `public.initialize_profile_and_invite_code_on_signup()`
|
||||
- Side effects include profile init + invite code init + register points (currently fixed to 60)
|
||||
- Target migration:
|
||||
- remove register points grant from DB trigger
|
||||
- grant register bonus in application service with eligibility ledger `register_bonus_claims`
|
||||
- keep trigger focused on profile/invite initialization only
|
||||
|
||||
### sessions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user