Files
ga-commission-system/ga-common/src/main/resources/db/migration/V85__외부연동_호출이력.sql
T
GA Pro 5ee8321ef2 feat: P6 외부연동 호출 감사 이력 (V85)
펌뱅킹/SMS·카카오 어댑터 모든 호출을 external_call_log 테이블에 AOP 자동 영속화.
외부 SDK 통합 후에도 그대로 재사용. 운영 감사·장애 추적·SDK 검증 용도.

- V85: external_call_log 테이블 + 인덱스 4종 (called_at desc / type / target / failures)
       + SYSTEM_EXTERNAL_CALL 메뉴 + READ 권한 (SUPER_ADMIN/ADMIN)
- ga-core: ExternalCallLogVO/Resp/SearchParam + Mapper (insertOne / selectList / selectById)
- ga-api/aop: ExternalCallLoggingAspect — BankTransferAdapter+/MessageAdapter+
  모든 메서드 @Around 영속화. 호출자 트랜잭션과 분리(REQUIRES_NEW),
  request/response 는 PII 마스킹된 JSON 한 줄 요약(계좌·전화 뒤 4자리, 이름 첫글자만),
  영속화 자체가 실패해도 비즈니스 흐름 비차단(warn 로그만), 예외는 그대로 propagate.
- ga-api: ExternalCallLogService + Controller GET /api/external-call-logs[/{id}]
- ga-frontend: ExternalCallLogList.tsx + api/externalCallLog.ts + App.tsx 라우트
  /system/external-call-log (ProTable + Drawer 상세)

라이브 검증:
- Flyway V85 자동 적용 → 운영 DB schema v85
- 5모듈 컴파일 BUILD SUCCESSFUL + ga-frontend tsc --noEmit 통과
- 시나리오: withdraw#3 신규 → 결재 advance ×2 → 펌뱅킹 호출
  external_call_log #1: BANK/MockBankTransferAdapter/requestTransfer/
  success=true/durMs=3/target=WITHDRAW#3/accountNoMasked="****0123"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 22:56:27 +09:00

86 lines
4.4 KiB
SQL

-- V85: 외부 연동(펌뱅킹/SMS·카카오) 호출 감사 이력
--
-- 목적: BankTransferAdapter / MessageAdapter 모든 호출을 자동 영속화.
-- 운영 감사·장애 추적·외부 SDK 통합 검증에 사용. AOP(@Around)가 INSERT.
-- PII 는 요약 필드에 어댑터에서 이미 마스킹된 값만 들어간다 (계좌·전화 뒤 4자리만).
CREATE TABLE IF NOT EXISTS external_call_log (
log_id BIGSERIAL PRIMARY KEY,
call_type VARCHAR(20) NOT NULL, -- BANK / MESSAGE
adapter_class VARCHAR(200) NOT NULL, -- 실제 호출된 구현체 (Mock/Kftc/Toast)
method_name VARCHAR(50) NOT NULL, -- requestTransfer / queryStatus / send
target_ref_type VARCHAR(30), -- WITHDRAW / APPROVAL 등 (가능 시)
target_ref_id BIGINT, -- withdrawRequestId / refId 등
request_summary TEXT, -- 요청 요약 (마스킹된 JSON 한 줄)
response_summary TEXT, -- 응답 요약
result_code VARCHAR(20), -- 외부 응답 코드 (정상 시)
result_message TEXT,
success BOOLEAN NOT NULL DEFAULT TRUE,
duration_ms INTEGER, -- 호출 소요 시간 (ms)
error_class VARCHAR(200), -- 예외 발생 시 클래스명
error_message TEXT, -- 예외 메시지
called_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT -- 시도자 (가능 시)
);
COMMENT ON TABLE external_call_log IS '외부 연동 호출 감사 이력 (AOP 자동 영속화)';
COMMENT ON COLUMN external_call_log.call_type IS 'BANK / MESSAGE';
COMMENT ON COLUMN external_call_log.adapter_class IS '실제 호출된 어댑터 클래스 (Mock/Kftc/Toast)';
COMMENT ON COLUMN external_call_log.method_name IS 'requestTransfer / queryStatus / send';
COMMENT ON COLUMN external_call_log.success IS 'true=정상응답, false=예외/실패';
CREATE INDEX IF NOT EXISTS idx_ecl_called_at_desc
ON external_call_log(called_at DESC);
CREATE INDEX IF NOT EXISTS idx_ecl_type_called_at
ON external_call_log(call_type, called_at DESC);
CREATE INDEX IF NOT EXISTS idx_ecl_target
ON external_call_log(target_ref_type, target_ref_id)
WHERE target_ref_type IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_ecl_failures
ON external_call_log(called_at DESC)
WHERE success = FALSE;
-- ============================================================
-- 메뉴 등록 (시스템 그룹 하위)
-- ============================================================
INSERT INTO menu (menu_code, menu_name, menu_type, menu_path, component_path, menu_icon,
menu_level, sort_order, is_visible, is_active, parent_menu_id, description)
SELECT
'SYSTEM_EXTERNAL_CALL',
'외부연동 호출이력',
'PAGE',
'/system/external-call-log',
'system/ExternalCallLogList',
'api',
2,
20,
'Y',
'Y',
(SELECT menu_id FROM menu WHERE menu_code = 'GRP_SYSTEM'),
'펌뱅킹/SMS·카카오 어댑터 호출 감사 이력 (AOP 자동 영속화)'
WHERE EXISTS (SELECT 1 FROM menu WHERE menu_code = 'GRP_SYSTEM')
ON CONFLICT (menu_code) DO NOTHING;
-- ============================================================
-- 권한 정의 (READ 만 — 감사 로그는 수동 등록 없음)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT menu_id, 'READ', '조회', 1 FROM menu WHERE menu_code = 'SYSTEM_EXTERNAL_CALL'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 역할 부여 (SUPER_ADMIN / ADMIN)
-- ============================================================
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, m.menu_id, p.perm_code, 'Y'
FROM role r
CROSS JOIN menu m
JOIN menu_permission p ON p.menu_id = m.menu_id
WHERE r.role_code IN ('SUPER_ADMIN', 'ADMIN')
AND m.menu_code = 'SYSTEM_EXTERNAL_CALL'
AND p.perm_code = 'READ'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;