근거: - SMS 입금자명: 한글 7자 제한 (은행 시스템) - 사용자 수동 입력: 15자로 충분한 여유 - 매칭 로직: 앞 7자만 사용 변경사항: - CHECK (length(depositor_name) <= 50) → 15 - 데이터 복원 시 truncate: 50자 → 15자 - SCHEMA_MIGRATION_GUIDE.md 업데이트 - MIGRATION_SUMMARY.md 업데이트 로컬 테스트 결과: - ✅ 15자 이하: 정상 입력 - 숫자 15자: "123456789012345" ✓ - 한글 15자: "홍길동아버지어머니할머님고모고" ✓ - ✅ 16자 이상: 거부됨 - 숫자 16자: "1234567890123456" ✗ (CHECK 제약조건) - 한글 16자: "홍길동아버지어머니할머님고모고모" ✗ (CHECK 제약조건) 실용성: - SMS 7자 보장 + 사용자 입력 여유 - 불필요한 긴 이름 방지 - 매칭 로직과 완벽 호환 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
151 lines
5.5 KiB
SQL
151 lines
5.5 KiB
SQL
-- Migration 001: Schema Enhancements
|
|
-- Purpose: Add data integrity constraints and audit logging
|
|
-- Date: 2026-01-19
|
|
-- Author: Claude Code
|
|
|
|
-- =============================================================================
|
|
-- STEP 1: user_deposits - Add CHECK constraint for balance >= 0
|
|
-- =============================================================================
|
|
-- SQLite requires table recreation to add CHECK constraints
|
|
|
|
-- 1.1 Create backup table
|
|
CREATE TABLE user_deposits_backup AS SELECT * FROM user_deposits;
|
|
|
|
-- 1.2 Drop existing table
|
|
DROP TABLE user_deposits;
|
|
|
|
-- 1.3 Create new table with CHECK constraint
|
|
CREATE TABLE user_deposits (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL UNIQUE,
|
|
balance INTEGER NOT NULL DEFAULT 0 CHECK (balance >= 0),
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 1.4 Restore data (negative balances will be rejected)
|
|
INSERT INTO user_deposits (id, user_id, balance, created_at, updated_at)
|
|
SELECT id, user_id, balance, created_at, updated_at
|
|
FROM user_deposits_backup
|
|
WHERE balance >= 0;
|
|
|
|
-- 1.5 Log rejected records (if any)
|
|
-- Note: In production, manually review these records before migration
|
|
SELECT 'REJECTED NEGATIVE BALANCE:' as warning, *
|
|
FROM user_deposits_backup
|
|
WHERE balance < 0;
|
|
|
|
-- 1.6 Drop backup table
|
|
DROP TABLE user_deposits_backup;
|
|
|
|
-- 1.7 Recreate index
|
|
CREATE INDEX IF NOT EXISTS idx_deposits_user ON user_deposits(user_id);
|
|
|
|
-- =============================================================================
|
|
-- STEP 2: deposit_transactions - Add length constraint for depositor_name
|
|
-- =============================================================================
|
|
|
|
-- 2.1 Create backup table
|
|
CREATE TABLE deposit_transactions_backup AS SELECT * FROM deposit_transactions;
|
|
|
|
-- 2.2 Drop existing table
|
|
DROP TABLE deposit_transactions;
|
|
|
|
-- 2.3 Create new table with length constraint
|
|
CREATE TABLE 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 CHECK (length(depositor_name) <= 15),
|
|
description TEXT,
|
|
confirmed_at DATETIME,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 2.4 Restore data (truncate long depositor names)
|
|
INSERT INTO deposit_transactions (
|
|
id, user_id, type, amount, status, depositor_name, description, confirmed_at, created_at
|
|
)
|
|
SELECT
|
|
id,
|
|
user_id,
|
|
type,
|
|
amount,
|
|
status,
|
|
CASE
|
|
WHEN depositor_name IS NULL THEN NULL
|
|
WHEN length(depositor_name) > 15 THEN substr(depositor_name, 1, 15)
|
|
ELSE depositor_name
|
|
END as depositor_name,
|
|
description,
|
|
confirmed_at,
|
|
created_at
|
|
FROM deposit_transactions_backup;
|
|
|
|
-- 2.5 Log truncated records (if any)
|
|
SELECT 'TRUNCATED DEPOSITOR NAME:' as warning, id, depositor_name, length(depositor_name) as original_length
|
|
FROM deposit_transactions_backup
|
|
WHERE depositor_name IS NOT NULL AND length(depositor_name) > 15;
|
|
|
|
-- 2.6 Drop backup table
|
|
DROP TABLE deposit_transactions_backup;
|
|
|
|
-- 2.7 Recreate indexes
|
|
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);
|
|
|
|
-- =============================================================================
|
|
-- STEP 3: Audit Log Table
|
|
-- =============================================================================
|
|
|
|
-- 3.1 Create audit_logs table
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER,
|
|
telegram_id TEXT,
|
|
action TEXT NOT NULL,
|
|
resource_type TEXT,
|
|
resource_id INTEGER,
|
|
details TEXT,
|
|
ip_address TEXT,
|
|
user_agent TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- 3.2 Create indexes for query performance
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_telegram_id ON audit_logs(telegram_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource_type, resource_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at DESC);
|
|
|
|
-- =============================================================================
|
|
-- VERIFICATION QUERIES
|
|
-- =============================================================================
|
|
|
|
-- Count records in each table
|
|
SELECT 'users' as table_name, COUNT(*) as count FROM users
|
|
UNION ALL
|
|
SELECT 'user_deposits', COUNT(*) FROM user_deposits
|
|
UNION ALL
|
|
SELECT 'deposit_transactions', COUNT(*) FROM deposit_transactions
|
|
UNION ALL
|
|
SELECT 'audit_logs', COUNT(*) FROM audit_logs;
|
|
|
|
-- Verify CHECK constraints
|
|
-- Attempt to insert invalid data (should fail)
|
|
-- Uncomment to test:
|
|
-- INSERT INTO user_deposits (user_id, balance) VALUES (999999, -1000);
|
|
-- INSERT INTO deposit_transactions (user_id, type, amount, depositor_name) VALUES (999999, 'deposit', 1000, 'ThisIsAVeryLongNameThatExceedsFiftyCharactersAndShouldBeTruncatedOrRejected');
|
|
|
|
-- =============================================================================
|
|
-- MIGRATION COMPLETE
|
|
-- =============================================================================
|
|
|
|
SELECT 'Migration 001 completed successfully' as status, datetime('now') as timestamp;
|