felhasználói felületre pü
This commit is contained in:
149
docs/p0_wallet_balance_500_bugfix_2026-07-26.md
Normal file
149
docs/p0_wallet_balance_500_bugfix_2026-07-26.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# P0 Bug #420: `GET /api/v1/billing/wallet/balance` - 500 Internal Server Error
|
||||
|
||||
**Dátum:** 2026-07-26
|
||||
**Súlyosság:** P0 - Blokkoló (minden wallet/balance hívást érint)
|
||||
**Státusz:** Megoldva (pycache purge + restart)
|
||||
|
||||
---
|
||||
|
||||
## 1. Hiba Leírása
|
||||
|
||||
A `GET /api/v1/billing/wallet/balance` végpont `500 Internal Server Error` hibával tért vissza bizonyos meglévő felhasználók (pl. user_id=86, 28) esetén.
|
||||
|
||||
### Hibaüzenet a logból:
|
||||
```
|
||||
2026-07-26 12:37:49,388 [ERROR] app.api.v1.endpoints.billing: Pénztárca egyenleg lekérdezési hiba: 'FinancialLedger' object has no attribute 'description'
|
||||
```
|
||||
|
||||
### Érintett fájlok:
|
||||
- [`backend/app/models/system/audit.py:78`](backend/app/models/system/audit.py:78) — `FinancialLedger` SQLAlchemy modell
|
||||
- [`backend/app/services/billing_engine.py:1078`](backend/app/services/billing_engine.py:1078) — `get_user_balance()`
|
||||
- [`backend/app/services/billing_engine.py:598`](backend/app/services/billing_engine.py:598) — `get_wallet_summary()`
|
||||
- [`backend/app/api/v1/endpoints/billing.py:298`](backend/app/api/v1/endpoints/billing.py:298) — `get_wallet_balance()` endpoint
|
||||
|
||||
---
|
||||
|
||||
## 2. Root Cause Analysis
|
||||
|
||||
### 2.1 A FinancialLedger modell
|
||||
|
||||
A [`FinancialLedger`](backend/app/models/system/audit.py:78) modell **NEM rendelkezik `description` oszloppal**. Az egyetlen "leíró" mező a `details` (JSONB). A modell oszlopai:
|
||||
|
||||
```
|
||||
id, user_id, person_id, amount, currency, transaction_type,
|
||||
related_agent_id, details, created_at, entry_type, balance_after,
|
||||
wallet_type, issuer_id, invoice_status, tax_amount, gross_amount,
|
||||
net_amount, transaction_id, status
|
||||
```
|
||||
|
||||
### 2.2 A jelenlegi forráskód helyes
|
||||
|
||||
A [`billing_engine.py`](backend/app/services/billing_engine.py:542) `get_transaction_history()` metódusa helyesen használja a `details` JSONB mezőt a leírás kinyerésére:
|
||||
|
||||
```python
|
||||
# Line 577-579
|
||||
desc = None
|
||||
if entry.details and isinstance(entry.details, dict):
|
||||
desc = entry.details.get("description") or entry.details.get("desc") or None
|
||||
```
|
||||
|
||||
A [`billing.py`](backend/app/api/v1/endpoints/billing.py:373) router szintén helyesen:
|
||||
```python
|
||||
# Line 376-378
|
||||
description = ""
|
||||
if entry.details and isinstance(entry.details, dict):
|
||||
description = entry.details.get("description", "")
|
||||
```
|
||||
|
||||
### 2.3 A tényleges ok: Stale Python Bytecode Cache
|
||||
|
||||
Az MD5 ellenőrzés igazolta, hogy a konténerben futó forráskód és a host-on lévő forráskód **azonos**:
|
||||
|
||||
| Fájl | MD5 |
|
||||
|------|-----|
|
||||
| `billing.py` (host & container) | `6b00968c129376e3db630e266835261b` |
|
||||
| `billing_engine.py` (host & container) | `54d86868aa077bebfeddaf32c69642fc` |
|
||||
|
||||
A konténerben azonban régi `.pyc` bytecode fájlok voltak cache-elve:
|
||||
```
|
||||
/app/app/api/v1/endpoints/__pycache__/billing.cpython-312.pyc
|
||||
/app/app/services/__pycache__/billing_engine.cpython-312.pyc
|
||||
```
|
||||
|
||||
Ezek a `.pyc` fájlok egy **korábbi forráskód-verzióhoz** tartoztak, ahol még létezett `entry.description` hivatkozás a `FinancialLedger` objektumokon (vagy egy másik séma érvényben volt).
|
||||
|
||||
### 2.4 Miért nem frissültek a .pyc fájlok?
|
||||
|
||||
A `docker-compose.yml`-ben a `sf_api` konténer forráskódja volume mount-tal van csatolva (nem COPY). Amikor a forráskód változik a host-on, a Python értelmező a konténerben azonnal látja az új `.py` fájlokat, de a már legenerált `.pyc` cache fájlok megmaradnak. Ha a `.py` fájl timestamp-je régebbi, mint a `.pyc`-é (vagy a Python nem érzékeli a változást), a régi bytecode fut.
|
||||
|
||||
---
|
||||
|
||||
## 3. Megoldás
|
||||
|
||||
### Végrehajtott lépések:
|
||||
|
||||
1. **Pycache törlése a konténerben:**
|
||||
```bash
|
||||
docker compose exec sf_api find /app -type d -name "__pycache__" -exec rm -rf {} +
|
||||
```
|
||||
|
||||
2. **Konténer újraindítása:**
|
||||
```bash
|
||||
docker compose restart sf_api
|
||||
```
|
||||
|
||||
3. **Verifikáció:** A restart után az endpoint már NEM dob 500-as hibát. A logokban nem jelenik meg több `description` hiba.
|
||||
|
||||
---
|
||||
|
||||
## 4. Code Mode Blueprint (Preventív Intézkedés)
|
||||
|
||||
Bár a jelenlegi forráskód helyes, az alábbi preventív intézkedést javaslom a `Code Mode`-ban:
|
||||
|
||||
### 4.1 Docker Compose: pycache purge a startup előtt
|
||||
|
||||
A `docker-compose.yml` `sf_api` szolgáltatás `command` sorában futtassunk egy automatikus pycache purge-öt a Uvicorn indítása előtt:
|
||||
|
||||
```yaml
|
||||
command: >
|
||||
/bin/sh -c "
|
||||
find /app -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null;
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
"
|
||||
```
|
||||
|
||||
### 4.2 Opcionális: `.pyc` fájlok teljes letiltása fejlesztői környezetben
|
||||
|
||||
A `docker-compose.yml` environment változói közé:
|
||||
```yaml
|
||||
environment:
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
```
|
||||
|
||||
Ez megakadályozza, hogy a Python egyáltalán `.pyc` fájlokat generáljon, így mindig a forráskódból fog futni.
|
||||
|
||||
---
|
||||
|
||||
## 5. Függelék: Mermaid Folyamatábra
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant FastAPI as billing.py router
|
||||
participant BillingEngine as billing_engine.py
|
||||
participant Model as FinancialLedger
|
||||
participant DB as PostgreSQL
|
||||
|
||||
Client->>FastAPI: GET /api/v1/billing/wallet/balance
|
||||
FastAPI->>BillingEngine: get_user_balance(db, user_id)
|
||||
BillingEngine->>BillingEngine: get_wallet_summary(db, user_id)
|
||||
BillingEngine->>DB: SELECT * FROM identity.wallets WHERE user_id=?
|
||||
BillingEngine->>DB: SELECT * FROM audit.financial_ledger LIMIT 10
|
||||
BillingEngine->>Model: entry.details.get("description")
|
||||
Note over Model: Helyes: details JSONB mezőből olvas
|
||||
Model-->>BillingEngine: description string
|
||||
BillingEngine-->>FastAPI: {"earned": X, "purchased": Y, ...}
|
||||
FastAPI-->>Client: 200 OK + JSON balances
|
||||
```
|
||||
|
||||
**A hiba idején** a `.pyc` bytecode `entry.description`-t próbált elérni, ami nem létező attribútum a `FinancialLedger` modellen. Ez `AttributeError`-t okozott, amit a `billing.py` router `except Exception` ága elkapott és 500-ra fordított.
|
||||
Reference in New Issue
Block a user