/*==========================================================================*/ /* CL_DAILY.pc - 일마감 배치 (As-Is 원본, Oracle Pro*C) */ /*--------------------------------------------------------------------------*/ /* 대상 : BC 매입시스템 / 일일 마감 배치 CLDAILY (야간 JOB) */ /* DBMS : Oracle 19c 전처리 : Pro*C/C++ (proc) */ /* 실행 : cldaily (인자 생략시 전영업일 = SYSDATE-1) */ /* ※ 이 파일은 **As-Is 역사 원본**이다. 현행 오픈스택 이식본은 */ /* app/src/cl/svc/CL_DAILY.pgc (ECPG/PostgreSQL, Tuxedo 서비스화). */ /* 원본은 순수 배치(main+커서루프) 였다. 컴파일 대상 아님(빌드 제외). */ /*--------------------------------------------------------------------------*/ /* 변경이력 */ /* 1999.02.15 KIM K.H 최초작성 (가맹점별 당일 매입 집계/마감이력) */ /* 2003.10.07 PARK J.S 기간잠금(cl_period_lock) 선점검 추가 */ /* 2009.05.22 LEE S.W 보류(H)/미매칭 잔존시 경고후 마감강행 정책 반영 */ /* 2015.08.11 JUNG M.K 마감 규모별 심각도(sev) 분류, cm_log 요약적재 */ /* 2019.09.01 CHOI H.W Oracle 11g -> 19c, CHAR->VARCHAR2 정비 */ /*==========================================================================*/ #include #include #include EXEC SQL INCLUDE sqlca; EXEC SQL INCLUDE oraca; EXEC ORACLE OPTION (ORACA=YES); EXEC SQL BEGIN DECLARE SECTION; VARCHAR h_user[32]; VARCHAR h_pass[32]; VARCHAR h_dbstr[32]; VARCHAR h_bizdate[9]; /* YYYYMMDD */ VARCHAR h_merch[16]; VARCHAR h_mname[64]; VARCHAR h_status[2]; VARCHAR h_zmsg[64]; long h_locked; long h_pend; long h_unmatch; long h_prevcnt; long h_cid; long h_txncnt; long h_gross; long h_fee; long h_net; long h_totcnt; long h_totamt; long h_nmerch; long h_zsev; EXEC SQL END DECLARE SECTION; /* 가맹점별 당일 확정매입 커서 (취소 C 제외) — 마감 대상 스윕 */ EXEC SQL DECLARE cur_merch CURSOR FOR SELECT m.merchant_id, NVL(m.name,'(무명)'), NVL(m.status,'N'), COUNT(p.purchase_id), NVL(SUM(p.amount), 0), NVL(SUM(p.fee), 0), NVL(SUM(p.net), 0) FROM merchant m, purchase p WHERE p.merchant_id (+) = m.merchant_id AND p.biz_date = TO_DATE(:h_bizdate,'YYYYMMDD') AND NVL(p.status,'A') <> 'C' GROUP BY m.merchant_id, m.name, m.status HAVING COUNT(p.purchase_id) > 0 ORDER BY m.merchant_id; static void sql_error(const char *stage); int main(int argc, char **argv) { int done = 0; /* ---- 0) 접속 (배치는 자기 세션으로 CONNECT) ---------------------- */ strcpy((char *)h_user.arr, "clbatch"); h_user.len = strlen((char *)h_user.arr); strcpy((char *)h_pass.arr, "clbatch"); h_pass.len = strlen((char *)h_pass.arr); strcpy((char *)h_dbstr.arr, "ACQPRD"); h_dbstr.len = strlen((char *)h_dbstr.arr); EXEC SQL WHENEVER SQLERROR GOTO connect_err; EXEC SQL CONNECT :h_user IDENTIFIED BY :h_pass USING :h_dbstr; /* 이후 오류는 표준 라벨분기 */ EXEC SQL WHENEVER SQLERROR GOTO sqlfail; /* ---- 1) 영업일 결정 (인자 없으면 전영업일 SYSDATE-1) ------------- */ if (argc >= 2) { strncpy((char *)h_bizdate.arr, argv[1], 8); h_bizdate.arr[8] = '\0'; h_bizdate.len = strlen((char *)h_bizdate.arr); } else { EXEC SQL SELECT TO_CHAR(SYSDATE-1,'YYYYMMDD') INTO :h_bizdate FROM DUAL; } printf("[CL_DAILY] 일마감 개시 bizdate=%.8s\n", h_bizdate.arr); /* ---- 2) 기간 잠금 선점검 (잠기면 즉시 중단) --------------------- */ EXEC SQL SELECT COUNT(*) INTO :h_locked FROM cl_period_lock WHERE period_key = :h_bizdate AND locked = 'Y'; if (h_locked > 0) { fprintf(stderr, "[CL_DAILY] 기간잠금 상태 - 일마감 불가 bizdate=%.8s\n", h_bizdate.arr); EXEC SQL ROLLBACK WORK RELEASE; return 4; } /* ---- 3) 사전상태 점검 (보류/미매칭/기마감) — 경고후 강행 -------- */ EXEC SQL SELECT COUNT(*) INTO :h_pend FROM purchase WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD') AND status = 'H'; EXEC SQL SELECT NVL(COUNT(*),0) INTO :h_unmatch FROM ac_unmatch WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD'); EXEC SQL SELECT COUNT(*) INTO :h_prevcnt FROM cl_close_log WHERE close_type = 'DAILY' AND period_key = :h_bizdate AND status <> 'X'; if (h_pend > 0) printf("[CL_DAILY] 경고: 보류(H) %ld건 잔존 - 마감 진행\n", h_pend); if (h_unmatch > 0) printf("[CL_DAILY] 경고: 미매칭 %ld건 잔존 - 마감 진행\n", h_unmatch); if (h_prevcnt > 0) printf("[CL_DAILY] 기마감 %ld건 존재 - 신규 마감이력 추가생성\n", h_prevcnt); /* ---- 4) 마감헤더 채번 (시퀀스 .NEXTVAL) ------------------------ */ EXEC SQL SELECT cl_close_seq.NEXTVAL INTO :h_cid FROM DUAL; EXEC SQL INSERT INTO cl_close_log (close_id, close_type, period_key, status, txn_count, total_amount, opened_at) VALUES (:h_cid, 'DAILY', :h_bizdate, 'O', 0, 0, SYSDATE); /* ---- 5) 본처리: 가맹점별 집계 커서 루프 ------------------------ */ h_totcnt = 0; h_totamt = 0; h_nmerch = 0; EXEC SQL OPEN cur_merch; for (;;) { EXEC SQL WHENEVER NOTFOUND DO done = 1; EXEC SQL FETCH cur_merch INTO :h_merch, :h_mname, :h_status, :h_txncnt, :h_gross, :h_fee, :h_net; if (done) break; /* 비활성 가맹점은 집계만 하고 정산연계는 보류(로그) */ if (strncmp((char *)h_status.arr, "A", 1) != 0) printf("[CL_DAILY] 주의: 비활성(%.1s) 가맹점 %.*s 마감집계 포함\n", h_status.arr, h_merch.len, h_merch.arr); EXEC SQL INSERT INTO cl_close_detail (close_id, merchant_id, txn_count, gross_amount, fee_amount, net_amount, created_at) VALUES (:h_cid, :h_merch, :h_txncnt, :h_gross, :h_fee, :h_net, SYSDATE); /* 마감된 매입행을 'S'(마감완료)로 상태전이 */ EXEC SQL UPDATE purchase SET status = DECODE(status, 'A', 'S', status), closed_at = SYSDATE, close_id = :h_cid WHERE merchant_id = :h_merch AND biz_date = TO_DATE(:h_bizdate,'YYYYMMDD') AND status = 'A'; h_totcnt += h_txncnt; h_totamt += h_gross; h_nmerch += 1; if (h_nmerch % 500 == 0) { printf("[CL_DAILY] 진행 %ld개 가맹점 누적 %ld건 %ld원 - 중간커밋\n", h_nmerch, h_totcnt, h_totamt); EXEC SQL COMMIT WORK; } } EXEC SQL CLOSE cur_merch; /* ---- 6) 마감헤더 확정 (집계 반영, 상태 O->C) ------------------- */ EXEC SQL UPDATE cl_close_log SET txn_count = :h_totcnt, total_amount = :h_totamt, status = 'C', closed_at = SYSDATE WHERE close_id = :h_cid; /* ---- 7) 마감 규모별 심각도 분류 (DECODE 대체 if-ladder) -------- */ if (h_totcnt == 0) { h_zsev = 0; strcpy((char *)h_zmsg.arr, "마감대상 없음"); } else if (h_totamt >= 500000000L){ h_zsev = 3; strcpy((char *)h_zmsg.arr, "고액 마감구간"); } else if (h_totamt >= 50000000L) { h_zsev = 2; strcpy((char *)h_zmsg.arr, "중액 마감구간"); } else { h_zsev = 1; strcpy((char *)h_zmsg.arr, "소액 마감구간"); } h_zmsg.len = strlen((char *)h_zmsg.arr); EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) VALUES (cm_log_seq.NEXTVAL, 'CL_DAILY', DECODE(SIGN(:h_zsev - 2), 1, 'W', 'I'), 'CL_DAILY 일마감 sev='||:h_zsev||' '||:h_zmsg ||' cnt='||:h_totcnt||' amt='||:h_totamt, TO_DATE(:h_bizdate,'YYYYMMDD')); /* ---- 8) 최종 커밋 --------------------------------------------- */ EXEC SQL COMMIT WORK; printf("[CL_DAILY] 일마감 완료 cid=%ld 가맹점=%ld 건수=%ld 금액=%ld sev=%ld(%s)\n", h_cid, h_nmerch, h_totcnt, h_totamt, h_zsev, h_zmsg.arr); EXEC SQL COMMIT WORK RELEASE; return 0; connect_err: fprintf(stderr, "[CL_DAILY] DB 접속 실패: %.70s\n", sqlca.sqlerrm.sqlerrmc); return 8; sqlfail: sql_error("일마감 본처리"); EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL ROLLBACK WORK RELEASE; return 12; } /*--------------------------------------------------------------------------*/ /* sql_error - 오류 로깅 (sqlca/oraca) */ /*--------------------------------------------------------------------------*/ static void sql_error(const char *stage) { fprintf(stderr, "[CL_DAILY] SQL 오류 (%s): %.70s\n", stage, sqlca.sqlerrm.sqlerrmc); fprintf(stderr, " sqlcode=%ld rows=%ld stmt=%.60s\n", sqlca.sqlcode, sqlca.sqlerrd[2], oraca.orastxt.orastxtc); }