-- =========================================================================== -- 90-seed.sql : 실사용 이력 시드 데이터 (운영 3개월치) -- 가맹점 40개 + 2026-04-20 ~ 2026-07-19 매입/승인/원장/정산/집계 이력. -- 실제 운영계처럼: 주중>주말 거래량, 금액 구간 분포, MDR 요율별 수수료, -- 최근 거래는 A/M/H 상태 잔존, 과거 거래는 S(정산완료). -- =========================================================================== SELECT setseed(0.42); -- --------------------------------------------------------------------------- -- 1. 가맹점 마스터 (실제풍 상호 40개, 업종·요율·한도 다양) -- --------------------------------------------------------------------------- INSERT INTO merchant (merchant_id, name, mdr_bps, daily_limit, status, created_at) VALUES ('M0001','클라로 커피 강남점', 25, 100000000,'ACTIVE', now()-interval '2 years'), ('M0002','포지 마트 역삼점', 22, 200000000,'ACTIVE', now()-interval '2 years'), ('M0003','클라로 면세점', 30, 500000000,'ACTIVE', now()-interval '2 years'), ('M0004','한강 곱창 본점', 28, 50000000,'ACTIVE', now()-interval '22 months'), ('M0005','서울정형외과의원', 18, 300000000,'ACTIVE', now()-interval '21 months'), ('M0006','블루문 헤어살롱', 27, 30000000,'ACTIVE', now()-interval '20 months'), ('M0007','대박 치킨 서초점', 28, 40000000,'ACTIVE', now()-interval '19 months'), ('M0008','그린약국', 18, 100000000,'ACTIVE', now()-interval '19 months'), ('M0009','썬샤인 주유소', 15, 400000000,'ACTIVE', now()-interval '18 months'), ('M0010','한빛서점', 24, 20000000,'ACTIVE', now()-interval '18 months'), ('M0011','미소분식 교대점', 29, 15000000,'ACTIVE', now()-interval '17 months'), ('M0012','로얄가구백화점', 26, 300000000,'ACTIVE', now()-interval '16 months'), ('M0013','스마일 안경원', 25, 50000000,'ACTIVE', now()-interval '16 months'), ('M0014','제주흑돼지 전문점', 28, 80000000,'ACTIVE', now()-interval '15 months'), ('M0015','네오 PC방', 27, 20000000,'ACTIVE', now()-interval '14 months'), ('M0016','골든짐 휘트니스', 26, 100000000,'ACTIVE', now()-interval '14 months'), ('M0017','파리의아침 베이커리', 26, 60000000,'ACTIVE', now()-interval '13 months'), ('M0018','한성전자 대리점', 23, 500000000,'ACTIVE', now()-interval '12 months'), ('M0019','포시즌 꽃집', 27, 10000000,'ACTIVE', now()-interval '12 months'), ('M0020','바다마트 수산', 24, 90000000,'ACTIVE', now()-interval '11 months'), ('M0021','씨티 세탁소', 28, 8000000,'ACTIVE', now()-interval '11 months'), ('M0022','건강한약국 2호점', 18, 120000000,'ACTIVE', now()-interval '10 months'), ('M0023','달빛 노래연습장', 29, 15000000,'ACTIVE', now()-interval '10 months'), ('M0024','우리동물병원', 20, 150000000,'ACTIVE', now()-interval '9 months'), ('M0025','에버그린 골프연습장', 25, 200000000,'ACTIVE', now()-interval '9 months'), ('M0026','정성한식 뷔페', 27, 100000000,'ACTIVE', now()-interval '8 months'), ('M0027','키즈랜드 문구완구', 26, 30000000,'ACTIVE', now()-interval '8 months'), ('M0028','모던 인테리어', 24, 250000000,'ACTIVE', now()-interval '7 months'), ('M0029','신선청과 도매', 21, 180000000,'ACTIVE', now()-interval '7 months'), ('M0030','휘게 캠핑용품', 26, 70000000,'ACTIVE', now()-interval '6 months'), ('M0031','더퀵 오토바이센터', 27, 60000000,'ACTIVE', now()-interval '6 months'), ('M0032','라온 웨딩홀', 23, 800000000,'ACTIVE', now()-interval '5 months'), ('M0033','스카이 여행사', 22, 400000000,'ACTIVE', now()-interval '5 months'), ('M0034','명가 한우촌', 28, 150000000,'ACTIVE', now()-interval '4 months'), ('M0035','코지 게스트하우스', 25, 50000000,'ACTIVE', now()-interval '4 months'), ('M0036','알파 어학원', 20, 200000000,'ACTIVE', now()-interval '3 months'), ('M0037','퓨어 요가스튜디오', 26, 40000000,'ACTIVE', now()-interval '3 months'), ('M0038','테크노 휴대폰 매장', 24, 300000000,'ACTIVE', now()-interval '2 months'), ('M0039','오후의 홍차', 27, 20000000,'HOLD', now()-interval '2 months'), ('M0040','신규상사 (심사중)', 25, 10000000,'PENDING',now()-interval '10 days') ON CONFLICT (merchant_id) DO UPDATE SET name=EXCLUDED.name, mdr_bps=EXCLUDED.mdr_bps; -- --------------------------------------------------------------------------- -- 2. 매입 이력 (2026-04-20 ~ 2026-07-19, 주중 ~110건/일·주말 ~40건/일 ≈ 8,600건) -- --------------------------------------------------------------------------- CREATE TEMP TABLE seed_tx AS WITH days AS ( SELECT d::date AS biz_date, CASE WHEN extract(isodow FROM d) >= 6 THEN 40 ELSE 110 END AS ntx FROM generate_series('2026-04-20'::date, '2026-07-19'::date, '1 day') d ), raw AS ( SELECT row_number() OVER (ORDER BY days.biz_date, gs) + 1000 AS purchase_id, days.biz_date, 'M' || lpad((1 + floor(random()*39))::int::text, 4, '0') AS merchant_id, random() AS r_amt, random() AS r_st, random() AS r_ch, random() AS r_is FROM days CROSS JOIN LATERAL generate_series(1, days.ntx) gs ) SELECT r.purchase_id, r.biz_date, r.merchant_id, -- 금액 분포: 소액 50% / 일반 35% / 고액 12% / 초고액 3%, 100원 단위 (CASE WHEN r.r_amt < 0.50 THEN 5000 + floor(random()*45000) WHEN r.r_amt < 0.85 THEN 50000 + floor(random()*250000) WHEN r.r_amt < 0.97 THEN 300000+ floor(random()*1200000) ELSE 1500000 + floor(random()*3500000) END)::bigint / 100 * 100 AS amount, m.mdr_bps, -- 상태: 7/15 이전 = 96% S, 3% C, 1% V | 최근 5일 = A/M/H 혼재 CASE WHEN r.biz_date < '2026-07-15' THEN CASE WHEN r.r_st < 0.96 THEN 'S' WHEN r.r_st < 0.99 THEN 'C' ELSE 'V' END ELSE CASE WHEN r.r_st < 0.30 THEN 'A' WHEN r.r_st < 0.70 THEN 'M' WHEN r.r_st < 0.92 THEN 'S' WHEN r.r_st < 0.97 THEN 'H' ELSE 'C' END END AS status, CASE WHEN r.r_ch < 0.62 THEN 'POS' WHEN r.r_ch < 0.85 THEN 'EDC' WHEN r.r_ch < 0.95 THEN 'EDI' ELSE 'FOREIGN' END AS channel, CASE WHEN r.r_is < 0.28 THEN 'BC' WHEN r.r_is < 0.46 THEN 'KB' WHEN r.r_is < 0.62 THEN 'SHINHAN' WHEN r.r_is < 0.74 THEN 'HYUNDAI' WHEN r.r_is < 0.84 THEN 'SAMSUNG' WHEN r.r_is < 0.92 THEN 'LOTTE' WHEN r.r_is < 0.97 THEN 'NH' ELSE 'HANA' END AS issuer FROM raw r JOIN merchant m ON m.merchant_id = r.merchant_id; INSERT INTO purchase (purchase_id, merchant_id, amount, fee, net, status, biz_date, channel, issuer, category, created_at, updated_at) SELECT purchase_id, merchant_id, amount, amount * mdr_bps / 10000, amount - amount * mdr_bps / 10000, status, biz_date, channel, issuer, CASE channel WHEN 'FOREIGN' THEN 'FX' ELSE 'GEN' END, biz_date::timestamptz + (interval '9 hours') + (random() * interval '12 hours'), biz_date::timestamptz + (interval '9 hours') + (random() * interval '13 hours') FROM seed_tx; -- 승인 (매입 1:1, 99% 승인) INSERT INTO approval (approval_id, purchase_id, approved, approved_at) SELECT purchase_id, purchase_id, random() < 0.99, biz_date::timestamptz + interval '9 hours' + (random() * interval '11 hours') FROM seed_tx; -- 원장: 정산완료(S) 건 = RECON + SETTLE 이중 기표 INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date, created_at) SELECT row_number() OVER (ORDER BY purchase_id) + 5000, purchase_id, 'RECON', amount - amount*mdr_bps/10000, biz_date, biz_date::timestamptz + interval '20 hours' FROM seed_tx WHERE status = 'S'; INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date, created_at) SELECT (SELECT count(*) FROM seed_tx WHERE status='S') + row_number() OVER (ORDER BY purchase_id) + 5000, purchase_id, 'SETTLE', amount - amount*mdr_bps/10000, biz_date, biz_date::timestamptz + interval '21 hours' FROM seed_tx WHERE status = 'S'; -- 정산 (S건, T+1 지급) INSERT INTO settlement (settlement_id, purchase_id, merchant_id, net, biz_date, status, settled_at) SELECT row_number() OVER (ORDER BY purchase_id) + 3000, purchase_id, merchant_id, amount - amount*mdr_bps/10000, biz_date, 'SETTLED', biz_date::timestamptz + interval '1 day 10 hours' FROM seed_tx WHERE status = 'S'; -- 가맹점 일집계 INSERT INTO merchant_summary (merchant_id, biz_date, txn_count, gross_amount, fee_amount, net_amount) SELECT merchant_id, biz_date, count(*), sum(amount), sum(amount*mdr_bps/10000), sum(amount - amount*mdr_bps/10000) FROM seed_tx GROUP BY merchant_id, biz_date ON CONFLICT (merchant_id, biz_date) DO NOTHING; -- 시퀀스 정렬 (라이브 거래가 시드 뒤 번호를 받도록) SELECT setval('purchase_seq', (SELECT max(purchase_id) FROM purchase)); SELECT setval('approval_seq', (SELECT max(approval_id) FROM approval)); SELECT setval('ledger_seq', (SELECT max(ledger_id) FROM ledger)); SELECT setval('settlement_seq', (SELECT max(settlement_id) FROM settlement)); DROP TABLE seed_tx;