- Implement optimistic locking for deposit balance updates - Prevent race conditions in concurrent deposit requests - Add automatic retry with exponential backoff (max 3 attempts) - Add version column to user_deposits table - Improve type safety across codebase - Add explicit types for Namecheap API responses - Add typed function arguments (ManageDepositArgs, etc.) - Remove `any` types from deposit-agent and tool files - Add reconciliation job for balance integrity verification - Compare user_deposits.balance vs SUM(confirmed transactions) - Alert admin on discrepancy detection - Set up test environment with Vitest + Miniflare - Add 50+ test cases for deposit system - Add helper functions for test data creation - Update documentation - Add migration guide for version columns - Document optimistic locking patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
103 lines
4.1 KiB
SQL
103 lines
4.1 KiB
SQL
-- Telegram Bot Rolling Summary Schema
|
|
-- D1 Database for Cloudflare Workers
|
|
|
|
-- 사용자 테이블
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
telegram_id TEXT UNIQUE NOT NULL,
|
|
username TEXT,
|
|
first_name TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- 메시지 버퍼 (요약 전 임시 저장)
|
|
CREATE TABLE IF NOT EXISTS message_buffer (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
chat_id TEXT NOT NULL,
|
|
role TEXT NOT NULL CHECK(role IN ('user', 'bot')),
|
|
message TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 요약 저장 테이블 (슬라이딩 윈도우: 최대 3개만 유지)
|
|
CREATE TABLE IF NOT EXISTS summaries (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
chat_id TEXT NOT NULL,
|
|
generation INTEGER NOT NULL DEFAULT 1,
|
|
summary TEXT NOT NULL,
|
|
message_count INTEGER NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 도메인 소유권 테이블
|
|
CREATE TABLE IF NOT EXISTS user_domains (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
domain TEXT UNIQUE NOT NULL,
|
|
verified INTEGER DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 예치금 계정 테이블
|
|
CREATE TABLE IF NOT EXISTS user_deposits (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL UNIQUE,
|
|
balance INTEGER NOT NULL DEFAULT 0,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 은행 입금 알림 테이블 (SMS → 메일 → 파싱)
|
|
CREATE TABLE IF NOT EXISTS bank_notifications (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
bank_name TEXT,
|
|
depositor_name TEXT NOT NULL,
|
|
depositor_name_prefix TEXT,
|
|
amount INTEGER NOT NULL,
|
|
balance_after INTEGER,
|
|
transaction_time DATETIME,
|
|
raw_message TEXT,
|
|
matched_transaction_id INTEGER,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (matched_transaction_id) REFERENCES deposit_transactions(id)
|
|
);
|
|
|
|
-- 예치금 거래 내역 테이블
|
|
CREATE TABLE IF NOT EXISTS deposit_transactions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
type TEXT NOT NULL CHECK(type IN ('deposit', 'withdrawal', 'refund')),
|
|
amount INTEGER NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'confirmed', 'rejected', 'cancelled')),
|
|
depositor_name TEXT,
|
|
depositor_name_prefix TEXT,
|
|
description TEXT,
|
|
confirmed_at DATETIME,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 인덱스
|
|
CREATE INDEX IF NOT EXISTS idx_user_domains_user ON user_domains(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_domains_domain ON user_domains(domain);
|
|
CREATE INDEX IF NOT EXISTS idx_deposits_user ON user_deposits(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_deposits_user_version ON user_deposits(user_id, version);
|
|
CREATE INDEX IF NOT EXISTS idx_transactions_user ON deposit_transactions(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_transactions_status ON deposit_transactions(status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_transactions_prefix_pending ON deposit_transactions(status, type, depositor_name_prefix, amount, created_at) WHERE status = 'pending' AND type = 'deposit';
|
|
CREATE INDEX IF NOT EXISTS idx_bank_notifications_prefix_unmatched ON bank_notifications(depositor_name_prefix, amount, created_at DESC) WHERE matched_transaction_id IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_buffer_user ON message_buffer(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_buffer_chat ON message_buffer(user_id, chat_id);
|
|
CREATE INDEX IF NOT EXISTS idx_summary_user ON summaries(user_id, chat_id);
|
|
CREATE INDEX IF NOT EXISTS idx_summary_latest ON summaries(user_id, chat_id, generation DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_users_telegram ON users(telegram_id);
|