/* Processed by ecpg (15.18 (Debian 15.18-0+deb12u1)) */ /* These include files are added by the preprocessor */ #include #include #include /* End of automatic include section */ #line 1 "app/online/cl_ol_0031.pgc" /* @tier easy @module cl @transform closing-online */ /* * cl_ol_0031.pgc - 마감 온라인 서비스 (CL_OL_0031) [tier=easy] * * 마감 스냅샷 비교 * ---------------------------------------------------------------------- * 두 스냅샷 처리키(SS1, SS2)를 표준 DBIO(cl_rec_select) 로 각각 조회 * 하여, 두 스냅샷의 합계금액(amount) 차이와 일치 여부(MATCH=Y/N) 를 * diff 응답으로 반환한다. 어느 한쪽 스냅샷이 없으면 비교 불가로 * ENOENT 를 응답한다. * * 금액 차이에 더해 건수(fee 필드에 적재) 차이와 변동 방향(DIRECTION: * UP/DOWN/FLAT), 기준(SS1) 대비 변동률(%)을 산출하여 제공한다. * 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" #include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" #line 1 "/usr/include/postgresql/sqlca.h" #ifndef POSTGRES_SQLCA_H #define POSTGRES_SQLCA_H #ifndef PGDLLIMPORT #if defined(WIN32) || defined(__CYGWIN__) #define PGDLLIMPORT __declspec (dllimport) #else #define PGDLLIMPORT #endif /* __CYGWIN__ */ #endif /* PGDLLIMPORT */ #define SQLERRMC_LEN 150 #ifdef __cplusplus extern "C" { #endif struct sqlca_t { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[SQLERRMC_LEN]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; /* Element 0: empty */ /* 1: OID of processed tuple if applicable */ /* 2: number of rows processed */ /* after an INSERT, UPDATE or */ /* DELETE statement */ /* 3: empty */ /* 4: empty */ /* 5: empty */ char sqlwarn[8]; /* Element 0: set to 'W' if at least one other is 'W' */ /* 1: if 'W' at least one character string */ /* value was truncated when it was */ /* stored into a host variable. */ /* * 2: if 'W' a (hopefully) non-fatal notice occurred */ /* 3: empty */ /* 4: empty */ /* 5: empty */ /* 6: empty */ /* 7: empty */ char sqlstate[5]; }; struct sqlca_t *ECPGget_sqlca(void); #ifndef POSTGRES_ECPG_INTERNAL #define sqlca (*ECPGget_sqlca()) #endif #ifdef __cplusplus } #endif #endif #line 24 "app/online/cl_ol_0031.pgc" TX_SERVICE(CL_OL_0031, ctx) { cl_rec_t rec1, rec2; char ss1[16], ss2[16]; char reqid[32]; char diff_won[32]; char summary[TX_VAL_LEN]; const char *direction; long diff, cnt_diff, abs_diff; long rate; int rc1, rc2; tx_log(TX_LOG_INFO, "[CL_OL_0031] 마감 스냅샷 비교 서비스 진입"); /* 1) 입력 수신 */ if (tx_buf_get(ctx->in, "SS1", ss1, sizeof(ss1)) != TX_OK || tx_buf_get(ctx->in, "SS2", ss2, sizeof(ss2)) != TX_OK) { tx_log(TX_LOG_ERROR, "[CL_OL_0031] SS1/SS2 누락"); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) strncpy(reqid, "-", sizeof(reqid) - 1); /* 2) 두 스냅샷 각각 조회 */ memset(&rec1, 0, sizeof(rec1)); memset(&rec2, 0, sizeof(rec2)); rc1 = cl_rec_select(ss1, &rec1); rc2 = cl_rec_select(ss2, &rec2); if (rc1 == TX_ENOENT || rc2 == TX_ENOENT) { tx_log(TX_LOG_WARN, "[CL_OL_0031] 스냅샷 없음 ss1=%s(rc=%d) ss2=%s(rc=%d)", ss1, rc1, ss2, rc2); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_buf_sets(ctx->out, "FOUND1", rc1 == TX_OK ? "Y" : "N"); tx_buf_sets(ctx->out, "FOUND2", rc2 == TX_OK ? "Y" : "N"); tx_buf_sets(ctx->out, "MESSAGE", "비교 대상 스냅샷 누락"); tx_return(ctx, TX_ENOENT, ctx->out); return; } if (rc1 != TX_OK || rc2 != TX_OK) { tx_log(TX_LOG_ERROR, "[CL_OL_0031] 스냅샷 조회 실패 rc1=%d rc2=%d", rc1, rc2); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_EDB, ctx->out); return; } /* 3) 금액/건수 차이, 방향, 변동률(기준 SS1) 산출 */ diff = rec2.amount - rec1.amount; /* SS1 -> SS2 변동 */ cnt_diff = rec2.fee - rec1.fee; if (diff > 0) direction = "UP"; else if (diff < 0) direction = "DOWN"; else direction = "FLAT"; rate = (rec1.amount != 0) ? (diff * 100 / (rec1.amount < 0 ? -rec1.amount : rec1.amount)) : 0; abs_diff = (diff < 0) ? -diff : diff; amount_format_won(diff, diff_won, sizeof(diff_won)); /* 4) 비교 결과 요약 문구 구성 */ snprintf(summary, sizeof(summary), "스냅샷 %s(%ld) -> %s(%ld) 변동 %ld원 (%s %ld%%)", ss1, rec1.amount, ss2, rec2.amount, diff, direction, rate); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "SS1", ss1); tx_buf_sets(ctx->out, "SS2", ss2); tx_buf_sets(ctx->out, "REQID", reqid); tx_buf_setlong(ctx->out, "AMT1", rec1.amount); tx_buf_setlong(ctx->out, "AMT2", rec2.amount); tx_buf_setlong(ctx->out, "CNT1", rec1.fee); tx_buf_setlong(ctx->out, "CNT2", rec2.fee); tx_buf_setlong(ctx->out, "DIFF", diff); tx_buf_sets(ctx->out, "DIFFWON", diff_won); tx_buf_setlong(ctx->out, "ABSDIFF", abs_diff); tx_buf_setlong(ctx->out, "CNTDIFF", cnt_diff); tx_buf_setlong(ctx->out, "RATE", rate); tx_buf_sets(ctx->out, "DIRECTION", direction); tx_buf_sets(ctx->out, "SUMMARY", summary); tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); /* 유의미 변동 여부(절대변동이 기준의 1% 초과) */ tx_buf_sets(ctx->out, "MATERIAL", (rate > 1 || rate < -1) ? "Y" : "N"); tx_log(TX_LOG_INFO, "[CL_OL_0031] 완료 ss1=%s(%ld) ss2=%s(%ld) 차이=%ld 방향=%s", ss1, rec1.amount, ss2, rec2.amount, diff, direction); tx_return(ctx, TX_OK, ctx->out); }