stage8-cash-point-gap-rootcause-20260506 ๋ฉ๋ชจ๋ฆฌ์ ๊ถ์ฅ ์ฌํญ: "future remediation: introduce 1127_cash_point_audit_log table that records every cash_point UPDATE with before/after value, source code path, and admin actor. Apply at all UPDATE sites." + 5/6 PRIMARY fix(payoutAllSummary status not 2) ๋ฏธ๋ฐฐํฌ ์์กด.
์ ๊ท ํ
์ด๋ธ 1127_cash_point_audit_log:
prisma db pull ์ผ๋ก ์๋ model ์ถ๊ฐ + prisma generate ํต๊ณผ.
/home/stage8-user/src/lib/audit/cashPointAudit.ts (76 LOC)
recordCashPointAudit(client, input) โ tx ๋๋ prisma ์ธ์ ๋ชจ๋ ํธํPhase 1 (5 ํ์ผ 5 endpoint, 7๊ณณ):
| ํ์ผ | ์์น | source |
|---|---|---|
admin/payout/route.ts:245 |
reject ํ๋ถ ์งํ | payout_refund |
admin/wallet/adjust/route.ts:102 |
manual credit/debit ์งํ | admin_credit / admin_debit |
ewallet/transfer/route.ts:112 |
sender ์ฐจ๊ฐ ์งํ | transfer_out |
ewallet/transfer/route.ts:124 |
recipient ๊ฐ์ฐ ์งํ | transfer_in |
admin/users/bulk-register/route.ts:49 |
initialCash ๊ฐ์ฐ ์งํ | bulk_register |
Phase 2 (2 ํ์ผ 4 endpoint, 4๊ณณ) โ ์ฌ์ฉ์ ๋ช ๋ น ํ์ wiring:
| ํ์ผ | ์์น | source |
|---|---|---|
investmentService.ts:260 |
์ ๊ท ํฌ์ ์ฐจ๊ฐ ์งํ | investment_create |
investmentService.ts:441 |
์ผ์ผ ์์ต ๊ฐ์ฐ ์งํ | investment_yield |
investmentService.ts:503 |
๋ง๊ธฐ ๋ฐํ ๊ฐ์ฐ ์งํ | investment_principal |
BscWatcher.ts:174 |
USDT ์๋ ์ ๊ธ ์งํ | deposit_bsc |
SKIP (์ ์ฑ ๋ฐ์):
TronWatcher.ts:174 โ TRON ๋ฏธ์ง์ ์ ์ฑ
(์ฌ์ฉ์ ๋ช
์ + ๋ฉ๋ชจ๋ฆฌ 47 sprint ์ผ์น)authService.ts:151 โ cash_point=0 ์ ๊ท INSERT, 0 amount audit ์๋ฏธ ์์/home/stage8-user/src/app/api/user/ewallet/route.ts:213
- prisma.payout_release_requests.aggregate({
- where: { user_id: userIdBigInt },
+ prisma.payout_release_requests.aggregate({
+ where: { user_id: userIdBigInt, status: { not: 2 } },
_sum: { amount: true, payout_fee: true }
})
โ rejected payout double-count 5๊ฑด ($114) ํด์.
| # | ํญ๋ชฉ | ๊ฒฐ๊ณผ |
|---|---|---|
| 1 | 1127_cash_point_audit_log ํ
์ด๋ธ + 4 ์ธ๋ฑ์ค ์์ฑ |
โ |
| 2 | Prisma model cash_point_audit_log ์์ฑ + generate |
โ |
| 3 | helper recordCashPointAudit 76 LOC | โ |
| 4 | 5 ํ์ผ 7๊ณณ wiring (audit insert ํธ์ถ) | โ |
| 5 | 5/6 PRIMARY fix ์ ์ฉ (status: { not: 2 }) |
โ |
| 6 | DB ๋น๊ต ๊ฒ์ฆ โ kstone0675 $54 / star365 $5 / LHU86968923 $25 / anyhome1 $30 = $114 ํด์ | โ |
| 7 | PM2 ์์ชฝ reload zero-downtime, unstable=0 | โ |
stage8-cash-point-gap-rootcause-20260506 ์ PRIMARY ROOT CAUSE ๊ฒ์ฆ:
| ํญ๋ชฉ | ์ํ |
|---|---|
investmentService.ts 3๊ณณ (create / yield / principal return) |
โ Phase 2 ์๋ฃ |
BscWatcher.ts:174 (deposit_bsc) |
โ Phase 2 ์๋ฃ (PM2 stage8-bsc-watcher reload) |
TronWatcher.ts |
โญ๏ธ TRON ๋ฏธ์ง์ ์ ์ฑ SKIP |
authService.ts:151 |
โญ๏ธ cash_point=0 INSERT (audit ์๋ฏธ ์์) SKIP |
์ต์ข : 9 wiring + 1 helper + 1 migration. ํต์ฌ cash_point ๋ณ๋ ํ๋ฆ 100% ์ปค๋ฒ (admin actor 5 + ์๋ ํธ๋ฆฌ๊ฑฐ 4 โ BSC deposit / investment yield / investment principal / investment create).
์ด๋ฒ sprint ํ ๋์: ์ ๊ท cash_point ๋ณ๊ฒฝ ์ audit ์๋ ๊ธฐ๋ก๋จ. ๊ธฐ์กด gap์ ํ ์๋ OFF-LEDGER ๋ณ๊ฒฝ(race condition + retroactive INSERT)์ด๋ผ backfill ์ด๋ ค์. ํฅํ ๋ชจ๋ ๋ณ๋์ด audit_log์ ๋จ์ผ๋ฏ๋ก ์ฌ๋ฐ ์ ์ฆ์ root cause ์๋ณ ๊ฐ๋ฅ.
stage8-user, stage8-admin, stage8-bsc-watcher (Phase 2 BscWatcher ๋ณ๊ฒฝ ํฌํจ) ๋ชจ๋ reload, unstable=0-- ์ฌ์ฉ์๋ณ audit ์ด๋ ฅ
SELECT * FROM 1127_cash_point_audit_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 50;
-- 24h source๋ณ ํต๊ณ
SELECT source, COUNT(*) cnt, SUM(amount) total_amount
FROM 1127_cash_point_audit_log
WHERE created_at >= NOW() - INTERVAL 24 HOUR
GROUP BY source ORDER BY cnt DESC;
-- balance_before/after ์ ํฉ ๊ฒ์ฆ (race ์ถ์ )
SELECT user_id, source, amount, balance_before, balance_after,
(balance_after - balance_before) AS calc_delta
FROM 1127_cash_point_audit_log
WHERE balance_before IS NOT NULL AND balance_after IS NOT NULL
AND ABS((balance_after - balance_before) - amount) > 0.001
ORDER BY created_at DESC LIMIT 20;