chore: acquire-core migration monorepo (legacy C + Spring Boot target skeleton)
Some checks failed
ci / build (push) Failing after 2s
Some checks failed
ci / build (push) Failing after 2s
TxCore/ECPG legacy 매입·정산 C 슬라이스(빌드검증) + Spring Boot 멀티모듈 골격(mvn test green) + MIGRATION.md 전환룰 + CI(boot 빌드).
This commit is contained in:
commit
ff0b0b60a2
31 changed files with 2208 additions and 0 deletions
147
legacy/app/batch/rc_match_batch.pgc
Normal file
147
legacy/app/batch/rc_match_batch.pgc
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* rc_match_batch.pgc - 승인-매입 대사(對査) 배치 (RC_MATCH)
|
||||
*
|
||||
* 지정 영업일의 접수(status='R') 매입 건을 커서로 순회하며 승인원장과
|
||||
* 대사한다. 금액이 일치하면 대사완료('M'), 아니면 불일치('U') 로 갱신하고,
|
||||
* 처리 요약을 출력한다. 배치 main 엔트리를 포함한다.
|
||||
*
|
||||
* 실행: rc_match_batch <YYYYMMDD> [db@host]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "txcore.h"
|
||||
#include "txcore_dbio.h"
|
||||
#include "acq_util.h"
|
||||
#include "purchase_dbio.h"
|
||||
|
||||
EXEC SQL INCLUDE sqlca;
|
||||
|
||||
static int run_match(const char *biz_date)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_biz_date[9];
|
||||
char h_appr_no[10];
|
||||
char h_merch_id[16];
|
||||
long h_amount;
|
||||
char h_new_status[2];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
long total = 0, matched = 0, unmatched = 0, errcnt = 0;
|
||||
int rc;
|
||||
|
||||
strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1);
|
||||
h_biz_date[sizeof(h_biz_date) - 1] = '\0';
|
||||
|
||||
/* 접수상태 매입 건에 대한 커서 선언 */
|
||||
EXEC SQL DECLARE cur_purchase CURSOR FOR
|
||||
SELECT appr_no, merch_id, amount
|
||||
FROM purchase
|
||||
WHERE txn_date = :h_biz_date
|
||||
AND status = 'R'
|
||||
ORDER BY appr_no;
|
||||
|
||||
EXEC SQL OPEN cur_purchase;
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("OPEN cur_purchase");
|
||||
return TX_EDB;
|
||||
}
|
||||
|
||||
if (tx_begin() != TX_OK)
|
||||
tx_log(TX_LOG_WARN, "[RC_MATCH] tx_begin 경고");
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_purchase
|
||||
INTO :h_appr_no, :h_merch_id, :h_amount;
|
||||
|
||||
if (sqlca.sqlcode == TX_SQL_NOTFOUND) /* 100 = 커서 소진 */
|
||||
break;
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("FETCH cur_purchase");
|
||||
errcnt++;
|
||||
break;
|
||||
}
|
||||
|
||||
total++;
|
||||
|
||||
/* 업무규칙: 승인원장에서 승인번호+금액 일치 확인 */
|
||||
rc = approval_match(h_appr_no, h_amount);
|
||||
if (rc == TX_OK) {
|
||||
strncpy(h_new_status, PUR_ST_MATCHED, sizeof(h_new_status));
|
||||
matched++;
|
||||
} else if (rc == TX_ENOENT) {
|
||||
strncpy(h_new_status, PUR_ST_UNMATCH, sizeof(h_new_status));
|
||||
unmatched++;
|
||||
} else {
|
||||
tx_log(TX_LOG_ERROR, "[RC_MATCH] 승인대사 오류 appr=%s rc=%d",
|
||||
h_appr_no, rc);
|
||||
errcnt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = purchase_update_status(h_appr_no, h_new_status);
|
||||
if (rc != TX_OK) {
|
||||
tx_log(TX_LOG_ERROR, "[RC_MATCH] 상태갱신 실패 appr=%s rc=%d",
|
||||
h_appr_no, rc);
|
||||
errcnt++;
|
||||
}
|
||||
}
|
||||
|
||||
EXEC SQL CLOSE cur_purchase;
|
||||
|
||||
if (errcnt == 0)
|
||||
tx_commit();
|
||||
else
|
||||
tx_abort();
|
||||
|
||||
tx_log(TX_LOG_INFO,
|
||||
"[RC_MATCH] 요약 date=%s 대상=%ld 대사완료=%ld 불일치=%ld 오류=%ld",
|
||||
biz_date, total, matched, unmatched, errcnt);
|
||||
|
||||
printf("========== 승인-매입 대사 배치 결과 ==========\n");
|
||||
printf(" 영업일 : %s\n", biz_date);
|
||||
printf(" 대상 건수 : %ld\n", total);
|
||||
printf(" 대사완료(M) : %ld\n", matched);
|
||||
printf(" 대사불일치(U) : %ld\n", unmatched);
|
||||
printf(" 오류 건수 : %ld\n", errcnt);
|
||||
printf("=============================================\n");
|
||||
|
||||
return (errcnt == 0) ? TX_OK : TX_FAIL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *biz_date;
|
||||
const char *target;
|
||||
int rc;
|
||||
|
||||
tx_set_loglevel(TX_LOG_INFO);
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "사용법: %s <YYYYMMDD> [db@host]\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
biz_date = argv[1];
|
||||
target = (argc >= 3) ? argv[2] : NULL;
|
||||
|
||||
if (!date_is_valid(biz_date)) {
|
||||
fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tx_log(TX_LOG_INFO, "[RC_MATCH] 배치 시작 date=%s", biz_date);
|
||||
|
||||
rc = dbio_connect(target);
|
||||
if (rc != TX_OK) {
|
||||
tx_log(TX_LOG_ERROR, "[RC_MATCH] DB 접속 실패 rc=%d", rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = run_match(biz_date);
|
||||
|
||||
dbio_disconnect();
|
||||
|
||||
tx_log(TX_LOG_INFO, "[RC_MATCH] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc));
|
||||
return (rc == TX_OK) ? 0 : 1;
|
||||
}
|
||||
82
legacy/app/common/util_amount.c
Normal file
82
legacy/app/common/util_amount.c
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* util_amount.c - 금액/통화 유틸리티 (plain C)
|
||||
*/
|
||||
#include "acq_util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* 거래금액 상한 (원). 건당 1억원 */
|
||||
#define AMOUNT_MAX 100000000L
|
||||
|
||||
long amount_parse(const char *field, int len)
|
||||
{
|
||||
long v = 0;
|
||||
int i;
|
||||
|
||||
if (!field || len <= 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
char c = field[i];
|
||||
if (c == ' ') /* 선행 공백 허용 */
|
||||
continue;
|
||||
if (!isdigit((unsigned char)c))
|
||||
return -1;
|
||||
v = v * 10 + (c - '0');
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int amount_format(long amount, char *out, int len)
|
||||
{
|
||||
char tmp[32];
|
||||
int n;
|
||||
|
||||
if (!out || len <= 0 || amount < 0)
|
||||
return -1;
|
||||
|
||||
n = snprintf(tmp, sizeof(tmp), "%0*ld", len, amount);
|
||||
if (n < 0 || n > len) /* 폭 초과 = 오버플로우 */
|
||||
return -1;
|
||||
|
||||
memcpy(out, tmp, len); /* 널 종료 없이 고정폭 복사 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amount_format_won(long amount, char *out, int outlen)
|
||||
{
|
||||
char raw[32];
|
||||
int n, i, j, digits, first;
|
||||
|
||||
if (!out || outlen <= 0)
|
||||
return;
|
||||
|
||||
n = snprintf(raw, sizeof(raw), "%ld", amount);
|
||||
if (n < 0) {
|
||||
out[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
first = (raw[0] == '-') ? 1 : 0;
|
||||
digits = n - first;
|
||||
|
||||
j = 0;
|
||||
if (first && j < outlen - 1)
|
||||
out[j++] = '-';
|
||||
|
||||
for (i = first; i < n && j < outlen - 1; i++) {
|
||||
int pos = i - first; /* 0-based 자릿수 위치 */
|
||||
if (pos > 0 && (digits - pos) % 3 == 0)
|
||||
if (j < outlen - 1)
|
||||
out[j++] = ',';
|
||||
out[j++] = raw[i];
|
||||
}
|
||||
out[j] = '\0';
|
||||
}
|
||||
|
||||
int amount_is_valid(long amount)
|
||||
{
|
||||
return (amount >= 1 && amount <= AMOUNT_MAX) ? 1 : 0;
|
||||
}
|
||||
135
legacy/app/common/util_date.c
Normal file
135
legacy/app/common/util_date.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* util_date.c - 일자/영업일 유틸리티 (plain C)
|
||||
*/
|
||||
#include "acq_util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
static int is_all_digit(const char *s, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!isdigit((unsigned char)s[i]))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int to_int(const char *s, int len)
|
||||
{
|
||||
int i, v = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
v = v * 10 + (s[i] - '0');
|
||||
return v;
|
||||
}
|
||||
|
||||
static int days_in_month(int y, int m)
|
||||
{
|
||||
static const int d[] = { 31, 28, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31 };
|
||||
if (m < 1 || m > 12)
|
||||
return 0;
|
||||
if (m == 2) {
|
||||
int leap = (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
|
||||
return leap ? 29 : 28;
|
||||
}
|
||||
return d[m - 1];
|
||||
}
|
||||
|
||||
int date_is_valid(const char *yyyymmdd)
|
||||
{
|
||||
int y, m, d;
|
||||
|
||||
if (!yyyymmdd || strlen(yyyymmdd) < 8)
|
||||
return 0;
|
||||
if (!is_all_digit(yyyymmdd, 8))
|
||||
return 0;
|
||||
|
||||
y = to_int(yyyymmdd, 4);
|
||||
m = to_int(yyyymmdd + 4, 2);
|
||||
d = to_int(yyyymmdd + 6, 2);
|
||||
|
||||
if (y < 1900 || y > 2999)
|
||||
return 0;
|
||||
if (m < 1 || m > 12)
|
||||
return 0;
|
||||
if (d < 1 || d > days_in_month(y, m))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Sakamoto 알고리즘: 0=일요일 ... 6=토요일 */
|
||||
int date_weekday(const char *yyyymmdd)
|
||||
{
|
||||
static const int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
|
||||
int y, m, d;
|
||||
|
||||
if (!date_is_valid(yyyymmdd))
|
||||
return -1;
|
||||
|
||||
y = to_int(yyyymmdd, 4);
|
||||
m = to_int(yyyymmdd + 4, 2);
|
||||
d = to_int(yyyymmdd + 6, 2);
|
||||
|
||||
if (m < 3)
|
||||
y -= 1;
|
||||
return (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7;
|
||||
}
|
||||
|
||||
int date_is_weekend(const char *yyyymmdd)
|
||||
{
|
||||
int w = date_weekday(yyyymmdd);
|
||||
return (w == 0 || w == 6) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* YYYYMMDD 를 하루 증가시킨다 (in==out 허용) */
|
||||
static void add_one_day(const char *in, char *out)
|
||||
{
|
||||
int y = to_int(in, 4);
|
||||
int m = to_int(in + 4, 2);
|
||||
int d = to_int(in + 6, 2);
|
||||
|
||||
d++;
|
||||
if (d > days_in_month(y, m)) {
|
||||
d = 1;
|
||||
m++;
|
||||
if (m > 12) {
|
||||
m = 1;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
snprintf(out, 9, "%04d%02d%02d", y, m, d);
|
||||
}
|
||||
|
||||
int date_next_business(const char *yyyymmdd, char *out)
|
||||
{
|
||||
char cur[9];
|
||||
int guard = 0;
|
||||
|
||||
if (!date_is_valid(yyyymmdd) || !out)
|
||||
return -1;
|
||||
|
||||
memcpy(cur, yyyymmdd, 8);
|
||||
cur[8] = '\0';
|
||||
|
||||
do {
|
||||
add_one_day(cur, cur);
|
||||
if (++guard > 14) /* 무한루프 방지 */
|
||||
return -1;
|
||||
} while (date_is_weekend(cur));
|
||||
|
||||
memcpy(out, cur, 8);
|
||||
out[8] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
void date_today(char *out)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm tmv;
|
||||
localtime_r(&now, &tmv);
|
||||
strftime(out, 9, "%Y%m%d", &tmv);
|
||||
}
|
||||
109
legacy/app/common/util_msg.c
Normal file
109
legacy/app/common/util_msg.c
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* util_msg.c - 고정길이 전문 pack/unpack 유틸리티 (plain C)
|
||||
*/
|
||||
#include "acq_util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void msg_field_copy(char *out, const char *field, int len)
|
||||
{
|
||||
int end;
|
||||
|
||||
if (!out || !field || len < 0)
|
||||
return;
|
||||
|
||||
/* 우측 공백 제거하여 널 종료 문자열 생성 */
|
||||
end = len;
|
||||
while (end > 0 && (field[end - 1] == ' ' || field[end - 1] == '\0'))
|
||||
end--;
|
||||
|
||||
memcpy(out, field, end);
|
||||
out[end] = '\0';
|
||||
}
|
||||
|
||||
void msg_field_set(char *field, const char *src, int len)
|
||||
{
|
||||
int slen, i;
|
||||
|
||||
if (!field || len < 0)
|
||||
return;
|
||||
|
||||
slen = src ? (int)strlen(src) : 0;
|
||||
if (slen > len)
|
||||
slen = len;
|
||||
|
||||
memcpy(field, src, slen);
|
||||
for (i = slen; i < len; i++) /* 우측 공백 패딩 */
|
||||
field[i] = ' ';
|
||||
}
|
||||
|
||||
void msg_field_set_num(char *field, const char *src, int len)
|
||||
{
|
||||
int slen, pad, i;
|
||||
|
||||
if (!field || len < 0)
|
||||
return;
|
||||
|
||||
slen = src ? (int)strlen(src) : 0;
|
||||
if (slen > len)
|
||||
slen = len;
|
||||
|
||||
pad = len - slen;
|
||||
for (i = 0; i < pad; i++) /* 좌측 zero 패딩 */
|
||||
field[i] = '0';
|
||||
if (src)
|
||||
memcpy(field + pad, src, slen);
|
||||
}
|
||||
|
||||
int msg_unpack(acq_msg_t *msg, const char *raw, int rawlen)
|
||||
{
|
||||
if (!msg || !raw)
|
||||
return -1;
|
||||
if (rawlen < MSG_ACQ_LEN)
|
||||
return -1;
|
||||
|
||||
/* 위치기반 레이아웃이 곧 구조체 메모리 배열과 동일 */
|
||||
memcpy(msg, raw, MSG_ACQ_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msg_pack(const acq_msg_t *msg, char *raw, int rawlen)
|
||||
{
|
||||
if (!msg || !raw)
|
||||
return -1;
|
||||
if (rawlen < MSG_ACQ_LEN)
|
||||
return -1;
|
||||
|
||||
memcpy(raw, msg, MSG_ACQ_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int field_blank(const char *field, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (field[i] != ' ' && field[i] != '\0')
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int msg_validate(const acq_msg_t *msg)
|
||||
{
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
if (field_blank(msg->msg_type, FLD_MSG_TYPE_LEN))
|
||||
return -1;
|
||||
if (field_blank(msg->merch_id, FLD_MERCH_ID_LEN))
|
||||
return -1;
|
||||
if (field_blank(msg->card_no, FLD_CARD_NO_LEN))
|
||||
return -1;
|
||||
if (field_blank(msg->amount, FLD_AMOUNT_LEN))
|
||||
return -1;
|
||||
if (field_blank(msg->txn_date, FLD_TXN_DATE_LEN))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
206
legacy/app/dbio/purchase_dbio.pgc
Normal file
206
legacy/app/dbio/purchase_dbio.pgc
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* purchase_dbio.pgc - 매입/정산 DBIO (ECPG 임베디드 SQL)
|
||||
*
|
||||
* Pro*C 상당의 PostgreSQL ECPG 로 매입/승인/정산 테이블에 대한
|
||||
* INSERT/SELECT/UPDATE 를 캡슐화한다. 서비스/배치는 이 함수만 호출.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "txcore.h"
|
||||
#include "txcore_dbio.h"
|
||||
#include "purchase_dbio.h"
|
||||
|
||||
EXEC SQL INCLUDE sqlca;
|
||||
|
||||
/* 컴파일 시 접속하지 않지만, 실환경 접속 규약을 그대로 둔다. */
|
||||
int dbio_connect(const char *target)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
const char *h_target;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_target = (target && target[0]) ? target : "acquire@localhost";
|
||||
|
||||
EXEC SQL CONNECT TO :h_target;
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("CONNECT");
|
||||
return TX_EDB;
|
||||
}
|
||||
tx_log(TX_LOG_INFO, "DB 접속 완료 target=%s", h_target);
|
||||
return TX_OK;
|
||||
}
|
||||
|
||||
int dbio_disconnect(void)
|
||||
{
|
||||
EXEC SQL DISCONNECT CURRENT;
|
||||
return TX_OK;
|
||||
}
|
||||
|
||||
int purchase_insert(const purchase_rec_t *rec)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_appr_no[10];
|
||||
char h_merch_id[16];
|
||||
char h_card_no[17];
|
||||
long h_amount;
|
||||
char h_txn_date[9];
|
||||
char h_status[2];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
if (!rec)
|
||||
return TX_EINVAL;
|
||||
|
||||
strncpy(h_appr_no, rec->appr_no, sizeof(h_appr_no) - 1);
|
||||
h_appr_no[sizeof(h_appr_no) - 1] = '\0';
|
||||
strncpy(h_merch_id, rec->merch_id, sizeof(h_merch_id) - 1);
|
||||
h_merch_id[sizeof(h_merch_id) - 1] = '\0';
|
||||
strncpy(h_card_no, rec->card_no, sizeof(h_card_no) - 1);
|
||||
h_card_no[sizeof(h_card_no) - 1] = '\0';
|
||||
strncpy(h_txn_date, rec->txn_date, sizeof(h_txn_date) - 1);
|
||||
h_txn_date[sizeof(h_txn_date) - 1] = '\0';
|
||||
strncpy(h_status, rec->status, sizeof(h_status) - 1);
|
||||
h_status[sizeof(h_status) - 1] = '\0';
|
||||
h_amount = rec->amount;
|
||||
|
||||
EXEC SQL INSERT INTO purchase
|
||||
(appr_no, merch_id, card_no, amount, txn_date, status)
|
||||
VALUES
|
||||
(:h_appr_no, :h_merch_id, :h_card_no, :h_amount, :h_txn_date, :h_status);
|
||||
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("purchase_insert");
|
||||
return TX_EDB;
|
||||
}
|
||||
return TX_OK;
|
||||
}
|
||||
|
||||
int purchase_select_by_appr(const char *appr_no, purchase_rec_t *out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_appr_no[10];
|
||||
char h_merch_id[16];
|
||||
char h_card_no[17];
|
||||
long h_amount;
|
||||
char h_txn_date[9];
|
||||
char h_status[2];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
if (!appr_no || !out)
|
||||
return TX_EINVAL;
|
||||
|
||||
strncpy(h_appr_no, appr_no, sizeof(h_appr_no) - 1);
|
||||
h_appr_no[sizeof(h_appr_no) - 1] = '\0';
|
||||
|
||||
EXEC SQL SELECT merch_id, card_no, amount, txn_date, status
|
||||
INTO :h_merch_id, :h_card_no, :h_amount, :h_txn_date, :h_status
|
||||
FROM purchase
|
||||
WHERE appr_no = :h_appr_no;
|
||||
|
||||
if (sqlca.sqlcode == TX_SQL_NOTFOUND)
|
||||
return TX_ENOENT;
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("purchase_select_by_appr");
|
||||
return TX_EDB;
|
||||
}
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
strncpy(out->appr_no, h_appr_no, sizeof(out->appr_no) - 1);
|
||||
strncpy(out->merch_id, h_merch_id, sizeof(out->merch_id) - 1);
|
||||
strncpy(out->card_no, h_card_no, sizeof(out->card_no) - 1);
|
||||
strncpy(out->txn_date, h_txn_date, sizeof(out->txn_date) - 1);
|
||||
strncpy(out->status, h_status, sizeof(out->status) - 1);
|
||||
out->amount = h_amount;
|
||||
return TX_OK;
|
||||
}
|
||||
|
||||
int purchase_update_status(const char *appr_no, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_appr_no[10];
|
||||
char h_status[2];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
if (!appr_no || !status)
|
||||
return TX_EINVAL;
|
||||
|
||||
strncpy(h_appr_no, appr_no, sizeof(h_appr_no) - 1);
|
||||
h_appr_no[sizeof(h_appr_no) - 1] = '\0';
|
||||
strncpy(h_status, status, sizeof(h_status) - 1);
|
||||
h_status[sizeof(h_status) - 1] = '\0';
|
||||
|
||||
EXEC SQL UPDATE purchase
|
||||
SET status = :h_status, upd_ts = now()
|
||||
WHERE appr_no = :h_appr_no;
|
||||
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("purchase_update_status");
|
||||
return TX_EDB;
|
||||
}
|
||||
if (sqlca.sqlerrd[2] == 0) /* 갱신된 행 수 0 = 대상 없음 */
|
||||
return TX_ENOENT;
|
||||
return TX_OK;
|
||||
}
|
||||
|
||||
int approval_match(const char *appr_no, long amount)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_appr_no[10];
|
||||
long h_amount;
|
||||
int h_cnt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
if (!appr_no)
|
||||
return TX_EINVAL;
|
||||
|
||||
strncpy(h_appr_no, appr_no, sizeof(h_appr_no) - 1);
|
||||
h_appr_no[sizeof(h_appr_no) - 1] = '\0';
|
||||
h_amount = amount;
|
||||
|
||||
EXEC SQL SELECT count(*)
|
||||
INTO :h_cnt
|
||||
FROM approval
|
||||
WHERE appr_no = :h_appr_no
|
||||
AND amount = :h_amount
|
||||
AND status = 'A';
|
||||
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("approval_match");
|
||||
return TX_EDB;
|
||||
}
|
||||
return (h_cnt > 0) ? TX_OK : TX_ENOENT;
|
||||
}
|
||||
|
||||
int settlement_accumulate(const char *merch_id, const char *biz_date,
|
||||
long amount)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch_id[16];
|
||||
char h_biz_date[9];
|
||||
long h_amount;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
if (!merch_id || !biz_date)
|
||||
return TX_EINVAL;
|
||||
|
||||
strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1);
|
||||
h_merch_id[sizeof(h_merch_id) - 1] = '\0';
|
||||
strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1);
|
||||
h_biz_date[sizeof(h_biz_date) - 1] = '\0';
|
||||
h_amount = amount;
|
||||
|
||||
/* 가맹점/영업일 단위 정산집계 upsert */
|
||||
EXEC SQL INSERT INTO settlement
|
||||
(merch_id, biz_date, total_amount, txn_cnt)
|
||||
VALUES
|
||||
(:h_merch_id, :h_biz_date, :h_amount, 1)
|
||||
ON CONFLICT (merch_id, biz_date) DO UPDATE
|
||||
SET total_amount = settlement.total_amount + EXCLUDED.total_amount,
|
||||
txn_cnt = settlement.txn_cnt + 1;
|
||||
|
||||
if (sqlca.sqlcode != TX_SQL_OK) {
|
||||
TX_DBIO_LOG_ERR("settlement_accumulate");
|
||||
return TX_EDB;
|
||||
}
|
||||
return TX_OK;
|
||||
}
|
||||
45
legacy/app/include/acq_util.h
Normal file
45
legacy/app/include/acq_util.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* acq_util.h - 공통 유틸리티 (일자/금액/전문) 선언
|
||||
*/
|
||||
#ifndef ACQ_UTIL_H
|
||||
#define ACQ_UTIL_H
|
||||
|
||||
#include "msg_layout.h"
|
||||
|
||||
/* ---- util_date.c : 일자/영업일 ---- */
|
||||
/* YYYYMMDD 형식 유효성 검사. 유효=1, 아니면 0 */
|
||||
int date_is_valid(const char *yyyymmdd);
|
||||
/* 요일 계산 (0=일 ... 6=토). 실패 시 -1 */
|
||||
int date_weekday(const char *yyyymmdd);
|
||||
/* 주말(토/일) 여부. 1=주말 */
|
||||
int date_is_weekend(const char *yyyymmdd);
|
||||
/* 다음 영업일(주말 건너뜀)을 out(YYYYMMDD, 최소 9바이트)에 기록. 0=성공 */
|
||||
int date_next_business(const char *yyyymmdd, char *out);
|
||||
/* 오늘 일자를 YYYYMMDD 로 out 에 기록 */
|
||||
void date_today(char *out);
|
||||
|
||||
/* ---- util_amount.c : 금액/통화 ---- */
|
||||
/* zero-pad 금액 문자열(len 폭)을 long 으로 파싱. 실패 시 <0 */
|
||||
long amount_parse(const char *field, int len);
|
||||
/* long 금액을 폭 len 의 zero-pad 문자열로 out 에 기록. 0=성공 */
|
||||
int amount_format(long amount, char *out, int len);
|
||||
/* 원화 콤마 포맷 (예: 12500 -> "12,500"). out 은 32바이트 권장 */
|
||||
void amount_format_won(long amount, char *out, int outlen);
|
||||
/* 금액 범위 검증 (1 이상, 한도 이하). 1=유효 */
|
||||
int amount_is_valid(long amount);
|
||||
|
||||
/* ---- util_msg.c : 전문 pack/unpack ---- */
|
||||
/* 고정폭 필드를 널종료 문자열 out 으로 복사(우측 공백 trim). */
|
||||
void msg_field_copy(char *out, const char *field, int len);
|
||||
/* 널종료 문자열 src 를 폭 len 필드에 좌측정렬/우측공백으로 기록 */
|
||||
void msg_field_set(char *field, const char *src, int len);
|
||||
/* 숫자 문자열 src 를 폭 len 필드에 zero-pad 로 기록 */
|
||||
void msg_field_set_num(char *field, const char *src, int len);
|
||||
/* 100바이트 raw 버퍼를 acq_msg_t 로 매핑(길이검증). 0=성공 */
|
||||
int msg_unpack(acq_msg_t *msg, const char *raw, int rawlen);
|
||||
/* acq_msg_t 를 raw(최소 MSG_ACQ_LEN) 로 직렬화. 0=성공 */
|
||||
int msg_pack(const acq_msg_t *msg, char *raw, int rawlen);
|
||||
/* 필수항목(전문구분/가맹점/카드/금액/일자) 존재 검증. 0=성공 */
|
||||
int msg_validate(const acq_msg_t *msg);
|
||||
|
||||
#endif /* ACQ_UTIL_H */
|
||||
54
legacy/app/include/msg_layout.h
Normal file
54
legacy/app/include/msg_layout.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* msg_layout.h - 카드 매입 전문(電文) 고정길이 레이아웃
|
||||
*
|
||||
* 대외/대내 전문은 고정길이 위치기반(positional) 구조다. 각 필드는
|
||||
* 널 종료가 없는 고정폭 char 배열이며, 숫자 필드는 좌측 zero-padding
|
||||
* (예: 거래금액 000000012500). util_msg.c 가 pack/unpack 을 담당한다.
|
||||
*/
|
||||
#ifndef MSG_LAYOUT_H
|
||||
#define MSG_LAYOUT_H
|
||||
|
||||
/* 전문구분 코드 */
|
||||
#define MSG_TYPE_RECV "0200" /* 매입요청(수신) */
|
||||
#define MSG_TYPE_RESP "0210" /* 매입응답 */
|
||||
#define MSG_TYPE_RECON "0500" /* 정산/대사 */
|
||||
|
||||
/* 응답코드 */
|
||||
#define RESP_OK "0000" /* 정상 */
|
||||
#define RESP_INVALID "9001" /* 항목오류 */
|
||||
#define RESP_NOMERCH "9002" /* 가맹점 없음 */
|
||||
#define RESP_SYSERR "9999" /* 시스템 오류 */
|
||||
|
||||
/*
|
||||
* 매입 요청 전문 헤더 + 바디 (총 MSG_ACQ_LEN 바이트)
|
||||
* 필드 순서/폭이 곧 와이어 포맷이므로 재배치 금지.
|
||||
*/
|
||||
typedef struct {
|
||||
char msg_type[4]; /* 전문구분 4 */
|
||||
char trans_code[6]; /* 거래코드 6 */
|
||||
char merch_id[15]; /* 가맹점번호 15 */
|
||||
char card_no[16]; /* 카드번호(마스킹) 16 */
|
||||
char approval_no[9]; /* 승인번호 9 */
|
||||
char amount[12]; /* 거래금액 12 (zero-pad, 원) */
|
||||
char txn_date[8]; /* 거래일자 8 (YYYYMMDD) */
|
||||
char txn_time[6]; /* 거래시각 6 (HHMMSS) */
|
||||
char inst_month[2]; /* 할부개월 2 (00=일시불) */
|
||||
char resp_code[4]; /* 응답코드 4 */
|
||||
char filler[18]; /* 예비 18 */
|
||||
} acq_msg_t; /* 합계 100 바이트 */
|
||||
|
||||
#define MSG_ACQ_LEN ((int)sizeof(acq_msg_t)) /* = 100 */
|
||||
|
||||
/* 필드 폭 상수 (unpack 검증용) */
|
||||
#define FLD_MSG_TYPE_LEN 4
|
||||
#define FLD_TRANS_CODE_LEN 6
|
||||
#define FLD_MERCH_ID_LEN 15
|
||||
#define FLD_CARD_NO_LEN 16
|
||||
#define FLD_APPROVAL_LEN 9
|
||||
#define FLD_AMOUNT_LEN 12
|
||||
#define FLD_TXN_DATE_LEN 8
|
||||
#define FLD_TXN_TIME_LEN 6
|
||||
#define FLD_INST_LEN 2
|
||||
#define FLD_RESP_LEN 4
|
||||
|
||||
#endif /* MSG_LAYOUT_H */
|
||||
47
legacy/app/include/purchase_dbio.h
Normal file
47
legacy/app/include/purchase_dbio.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* purchase_dbio.h - 매입/정산 DBIO 인터페이스
|
||||
*
|
||||
* 서비스/배치 코드는 SQL 을 직접 다루지 않고 이 함수들을 호출한다.
|
||||
* 구현은 app/dbio/purchase_dbio.pgc (ECPG).
|
||||
*/
|
||||
#ifndef PURCHASE_DBIO_H
|
||||
#define PURCHASE_DBIO_H
|
||||
|
||||
/* 매입 상태 코드 */
|
||||
#define PUR_ST_RECV "R" /* 접수(수신) */
|
||||
#define PUR_ST_MATCHED "M" /* 대사완료 */
|
||||
#define PUR_ST_UNMATCH "U" /* 대사불일치 */
|
||||
#define PUR_ST_SETTLED "S" /* 정산완료 */
|
||||
|
||||
/* 매입 레코드 (purchase 테이블 1행) */
|
||||
typedef struct {
|
||||
char appr_no[10]; /* 승인번호 PK */
|
||||
char merch_id[16]; /* 가맹점번호 */
|
||||
char card_no[17]; /* 카드번호 */
|
||||
long amount; /* 거래금액(원) */
|
||||
char txn_date[9]; /* 거래일자 YYYYMMDD */
|
||||
char status[2]; /* 상태코드 */
|
||||
} purchase_rec_t;
|
||||
|
||||
/* DB 연결/해제 (배치/데몬 기동 시). target 은 "db@host" 또는 NULL */
|
||||
int dbio_connect(const char *target);
|
||||
int dbio_disconnect(void);
|
||||
|
||||
/* 매입 접수 INSERT. 0=성공 */
|
||||
int purchase_insert(const purchase_rec_t *rec);
|
||||
/* 승인번호로 단건 조회. 0=성공, TX_ENOENT=없음 */
|
||||
int purchase_select_by_appr(const char *appr_no, purchase_rec_t *out);
|
||||
/* 상태 갱신. 0=성공 */
|
||||
int purchase_update_status(const char *appr_no, const char *status);
|
||||
|
||||
/*
|
||||
* 승인원장(approval) 에서 승인번호+금액 일치 여부 확인.
|
||||
* 0=일치, TX_ENOENT=불일치/없음.
|
||||
*/
|
||||
int approval_match(const char *appr_no, long amount);
|
||||
|
||||
/* 정산집계(settlement) upsert. 가맹점/일자별 금액 누적. 0=성공 */
|
||||
int settlement_accumulate(const char *merch_id, const char *biz_date,
|
||||
long amount);
|
||||
|
||||
#endif /* PURCHASE_DBIO_H */
|
||||
136
legacy/app/online/ac_intake_svc.pgc
Normal file
136
legacy/app/online/ac_intake_svc.pgc
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* ac_intake_svc.pgc - 매입접수 서비스 (AC_INTAKE)
|
||||
*
|
||||
* MG_RECV 가 적재한 TXBUF 필드를 검증하고, 트랜잭션 하에서
|
||||
* purchase 테이블에 접수 INSERT 한 뒤 정산집계 서비스를 tx_call 한다.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "txcore.h"
|
||||
#include "acq_util.h"
|
||||
#include "purchase_dbio.h"
|
||||
|
||||
EXEC SQL INCLUDE sqlca;
|
||||
|
||||
TX_SERVICE(AC_INTAKE, ctx)
|
||||
{
|
||||
purchase_rec_t rec;
|
||||
char merch[16];
|
||||
char card[20];
|
||||
char appr[16];
|
||||
char txndate[16];
|
||||
long amount = 0;
|
||||
int rc;
|
||||
TXBUF *sub_in;
|
||||
TXBUF *sub_out;
|
||||
|
||||
tx_log(TX_LOG_INFO, "[AC_INTAKE] 매입접수 서비스 진입");
|
||||
|
||||
/* 입력 필드 추출 */
|
||||
if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK ||
|
||||
tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK ||
|
||||
tx_buf_get(ctx->in, "APPRNO", appr, sizeof(appr)) != TX_OK ||
|
||||
tx_buf_get(ctx->in, "TXNDATE", txndate, sizeof(txndate)) != TX_OK) {
|
||||
tx_log(TX_LOG_ERROR, "[AC_INTAKE] 입력 필드 누락");
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
tx_buf_getlong(ctx->in, "AMOUNT", &amount);
|
||||
|
||||
/* 업무 검증: 가맹점/금액/일자 */
|
||||
if (merch[0] == '\0') {
|
||||
tx_log(TX_LOG_WARN, "[AC_INTAKE] 가맹점번호 없음");
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
if (!amount_is_valid(amount)) {
|
||||
tx_log(TX_LOG_WARN, "[AC_INTAKE] 금액 범위 오류 amount=%ld", amount);
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
if (!date_is_valid(txndate)) {
|
||||
tx_log(TX_LOG_WARN, "[AC_INTAKE] 거래일자 오류 date=%s", txndate);
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 레코드 구성 */
|
||||
memset(&rec, 0, sizeof(rec));
|
||||
strncpy(rec.appr_no, appr, sizeof(rec.appr_no) - 1);
|
||||
strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1);
|
||||
strncpy(rec.card_no, card, sizeof(rec.card_no) - 1);
|
||||
strncpy(rec.txn_date, txndate, sizeof(rec.txn_date) - 1);
|
||||
strncpy(rec.status, PUR_ST_RECV, sizeof(rec.status) - 1);
|
||||
rec.amount = amount;
|
||||
|
||||
/* 트랜잭션 시작 → 접수 INSERT */
|
||||
if (tx_begin() != TX_OK) {
|
||||
tx_return(ctx, TX_FAIL, ctx->out);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = purchase_insert(&rec);
|
||||
if (rc != TX_OK) {
|
||||
tx_log(TX_LOG_ERROR, "[AC_INTAKE] 접수 INSERT 실패 rc=%d(%s)",
|
||||
rc, tx_strerror(rc));
|
||||
tx_abort();
|
||||
tx_return(ctx, rc, ctx->out);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 정산집계 서비스 호출 (동일 트랜잭션 컨텍스트) */
|
||||
sub_in = tx_buf_alloc();
|
||||
sub_out = tx_buf_alloc();
|
||||
if (sub_in && sub_out) {
|
||||
tx_buf_sets(sub_in, "MERCHID", merch);
|
||||
tx_buf_sets(sub_in, "TXNDATE", txndate);
|
||||
tx_buf_setlong(sub_in, "AMOUNT", amount);
|
||||
rc = tx_call("AC_SETTLE", sub_in, sub_out);
|
||||
if (rc != TX_OK)
|
||||
tx_log(TX_LOG_WARN, "[AC_INTAKE] 정산집계 경고 rc=%d", rc);
|
||||
}
|
||||
tx_buf_free(sub_in);
|
||||
tx_buf_free(sub_out);
|
||||
|
||||
tx_commit();
|
||||
|
||||
tx_buf_reset(ctx->out);
|
||||
tx_buf_sets(ctx->out, "RESPCODE", RESP_OK);
|
||||
tx_buf_sets(ctx->out, "APPRNO", appr);
|
||||
tx_log(TX_LOG_INFO, "[AC_INTAKE] 접수완료 appr=%s amount=%ld", appr, amount);
|
||||
|
||||
tx_return(ctx, TX_OK, ctx->out);
|
||||
}
|
||||
|
||||
/*
|
||||
* AC_SETTLE - 정산집계 서비스 (동일 모듈에 배치)
|
||||
* purchase 접수 건을 가맹점/영업일 단위 정산집계에 누적한다.
|
||||
*/
|
||||
TX_SERVICE(AC_SETTLE, ctx)
|
||||
{
|
||||
char merch[16];
|
||||
char txndate[16];
|
||||
char bizdate[9];
|
||||
long amount = 0;
|
||||
int rc;
|
||||
|
||||
if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK ||
|
||||
tx_buf_get(ctx->in, "TXNDATE", txndate, sizeof(txndate)) != TX_OK) {
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
tx_buf_getlong(ctx->in, "AMOUNT", &amount);
|
||||
|
||||
/* 정산 영업일 = 거래일의 다음 영업일 */
|
||||
if (date_next_business(txndate, bizdate) != 0) {
|
||||
memcpy(bizdate, txndate, 8);
|
||||
bizdate[8] = '\0';
|
||||
}
|
||||
|
||||
rc = settlement_accumulate(merch, bizdate, amount);
|
||||
tx_log(TX_LOG_INFO, "[AC_SETTLE] 집계 merch=%s biz=%s amount=%ld rc=%d",
|
||||
merch, bizdate, amount, rc);
|
||||
|
||||
tx_return(ctx, rc, ctx->out);
|
||||
}
|
||||
71
legacy/app/online/mg_recv_svc.pgc
Normal file
71
legacy/app/online/mg_recv_svc.pgc
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* mg_recv_svc.pgc - 전문수신 서비스 (MG_RECV)
|
||||
*
|
||||
* 대외 매입요청 전문(고정길이 100바이트)을 수신하여 unpack 하고,
|
||||
* 필드를 TXBUF 로 적재한 뒤 후속 서비스가 소비하도록 응답한다.
|
||||
* (ATMI 의 void SVC(TPSVCINFO*) 대응)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "txcore.h"
|
||||
#include "acq_util.h"
|
||||
#include "purchase_dbio.h"
|
||||
|
||||
EXEC SQL INCLUDE sqlca;
|
||||
|
||||
TX_SERVICE(MG_RECV, ctx)
|
||||
{
|
||||
char raw[MSG_ACQ_LEN + 1];
|
||||
acq_msg_t msg;
|
||||
char fld[64];
|
||||
long amount;
|
||||
int rc;
|
||||
TXBUF *out;
|
||||
|
||||
tx_log(TX_LOG_INFO, "[MG_RECV] 전문수신 서비스 진입");
|
||||
|
||||
/* 요청 버퍼에서 원시 전문 획득 */
|
||||
memset(raw, 0, sizeof(raw));
|
||||
rc = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw));
|
||||
if (rc != TX_OK) {
|
||||
tx_log(TX_LOG_ERROR, "[MG_RECV] RAWMSG 누락 rc=%d", rc);
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_unpack(&msg, raw, (int)strlen(raw)) != 0) {
|
||||
tx_log(TX_LOG_ERROR, "[MG_RECV] 전문 언팩 실패 len=%zu", strlen(raw));
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_validate(&msg) != 0) {
|
||||
tx_log(TX_LOG_WARN, "[MG_RECV] 필수항목 검증 실패");
|
||||
tx_return(ctx, TX_EINVAL, ctx->out);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 필드를 응답 TXBUF 로 적재 (후속 매입접수 서비스가 소비) */
|
||||
out = ctx->out;
|
||||
tx_buf_reset(out);
|
||||
|
||||
msg_field_copy(fld, msg.msg_type, FLD_MSG_TYPE_LEN);
|
||||
tx_buf_sets(out, "MSGTYPE", fld);
|
||||
msg_field_copy(fld, msg.merch_id, FLD_MERCH_ID_LEN);
|
||||
tx_buf_sets(out, "MERCHID", fld);
|
||||
msg_field_copy(fld, msg.card_no, FLD_CARD_NO_LEN);
|
||||
tx_buf_sets(out, "CARDNO", fld);
|
||||
msg_field_copy(fld, msg.approval_no, FLD_APPROVAL_LEN);
|
||||
tx_buf_sets(out, "APPRNO", fld);
|
||||
msg_field_copy(fld, msg.txn_date, FLD_TXN_DATE_LEN);
|
||||
tx_buf_sets(out, "TXNDATE", fld);
|
||||
|
||||
amount = amount_parse(msg.amount, FLD_AMOUNT_LEN);
|
||||
tx_buf_setlong(out, "AMOUNT", amount);
|
||||
|
||||
tx_log(TX_LOG_INFO, "[MG_RECV] 언팩완료 merch=%.15s appr=%.9s amount=%ld",
|
||||
msg.merch_id, msg.approval_no, amount);
|
||||
|
||||
tx_return(ctx, TX_OK, out);
|
||||
}
|
||||
36
legacy/app/online/server_main.c
Normal file
36
legacy/app/online/server_main.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* server_main.c - acquire-core 온라인 서버 데모 부트스트랩
|
||||
*
|
||||
* Tuxedo 의 tmboot/서버 메인처럼, 온라인 서비스들을 TxCore 레지스트리에
|
||||
* 등록한다. 실제 TP 모니터에서는 tpsvrinit() 에 해당한다. 여기서는
|
||||
* 등록/디스패치 링크 경로를 검증하기 위한 최소 데모 엔트리다.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "txcore.h"
|
||||
|
||||
/* 각 .pgc 서비스의 외부 선언 (TX_SERVICE 매크로가 생성한 함수) */
|
||||
void MG_RECV(TXSVCINFO *ctx);
|
||||
void AC_INTAKE(TXSVCINFO *ctx);
|
||||
void AC_SETTLE(TXSVCINFO *ctx);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tx_set_loglevel(TX_LOG_INFO);
|
||||
tx_log(TX_LOG_INFO, "acquire-core 온라인 서버 기동 (TxCore)");
|
||||
|
||||
/* 서비스 등록 (tpsvrinit 상당) */
|
||||
tx_register("MG_RECV", MG_RECV);
|
||||
tx_register("AC_INTAKE", AC_INTAKE);
|
||||
tx_register("AC_SETTLE", AC_SETTLE);
|
||||
|
||||
tx_log(TX_LOG_INFO, "등록 서비스 수 = %d", tx_service_count());
|
||||
printf("acquire-core-server 준비완료: 서비스 %d개 등록\n",
|
||||
tx_service_count());
|
||||
|
||||
/*
|
||||
* 실환경에서는 여기서 TP 모니터 이벤트 루프(advertise/serve)로 진입한다.
|
||||
* 데모에서는 DB 접속 없이 링크 경로만 검증하고 종료한다.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
Reference in a new issue