chore: acquire-core migration monorepo (legacy C + Spring Boot target skeleton)
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:
forge-bot 2026-07-18 09:55:46 +00:00
commit ff0b0b60a2
31 changed files with 2208 additions and 0 deletions

View file

@ -0,0 +1,31 @@
/*
* txcore_dbio.h - TxCore DBIO
*
* ProFrame/Pro*C DBIO . DBIO(.pgc)
* ECPG SQL , /
* . sqlca .pgc EXEC SQL INCLUDE sqlca; .
*/
#ifndef TXCORE_DBIO_H
#define TXCORE_DBIO_H
#include "txcore.h"
/* SQLCODE 관례값 (ECPG/Pro*C 공통) */
#define TX_SQL_OK 0 /* 정상 처리 */
#define TX_SQL_NOTFOUND 100 /* 조회 결과 없음 */
/*
* DBIO . sqlca .
* (0)=TX_OK, NOTFOUND(100)=TX_ENOENT, =TX_EDB .
*/
#define TX_DBIO_RESULT(sc) \
((sc) == TX_SQL_OK ? TX_OK : \
(sc) == TX_SQL_NOTFOUND ? TX_ENOENT : TX_EDB)
/* SQL 오류를 표준 로그로 남기는 헬퍼 (sqlca 필드 참조) */
#define TX_DBIO_LOG_ERR(tag) \
tx_log(TX_LOG_ERROR, "DBIO 오류 [%s] sqlcode=%ld msg=%.*s", \
(tag), (long)sqlca.sqlcode, \
(int)sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc)
#endif /* TXCORE_DBIO_H */

View file

@ -0,0 +1,128 @@
/*
* txcore.h - TxCore API
*
* TxCore , Tuxedo/ProFrame TP
* ATMI(tpcall/tpreturn/tpbegin) FML/UBF(Fchg/Fget)
* . TP (Enduro/X )
* / in-process .
*
* API , TP
* .
*/
#ifndef TXCORE_H
#define TXCORE_H
#include <stddef.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------ */
/* 에러 코드 (ATMI tperrno 대응) */
/* ------------------------------------------------------------------ */
#define TX_OK 0 /* 정상 */
#define TX_FAIL (-1) /* 일반 실패 */
#define TX_ENOENT (-2) /* 서비스 미등록 */
#define TX_EINVAL (-3) /* 파라미터 오류 */
#define TX_EDB (-4) /* DB 오류 */
#define TX_ENOMEM (-5) /* 버퍼/자원 부족 */
#define TX_ENOKEY (-6) /* 버퍼 키 없음 */
/* 로그 레벨 */
#define TX_LOG_DEBUG 0
#define TX_LOG_INFO 1
#define TX_LOG_WARN 2
#define TX_LOG_ERROR 3
/* ------------------------------------------------------------------ */
/* TXBUF - 키/값 고정 슬롯 버퍼 (FML/UBF Fielded buffer 대응) */
/* ------------------------------------------------------------------ */
#define TX_MAX_SLOTS 64
#define TX_KEY_LEN 32
#define TX_VAL_LEN 256
typedef struct {
char key[TX_KEY_LEN];
char val[TX_VAL_LEN];
int len; /* val 유효 바이트 수 */
int used; /* 슬롯 사용 여부 */
} TXFIELD;
typedef struct {
int count;
TXFIELD slots[TX_MAX_SLOTS];
} TXBUF;
/* ------------------------------------------------------------------ */
/* 서비스 컨텍스트 (Tuxedo TPSVCINFO 대응) */
/* ------------------------------------------------------------------ */
typedef struct {
char name[TX_KEY_LEN]; /* 호출된 서비스명 */
TXBUF *in; /* 요청 버퍼 */
TXBUF *out; /* 응답 버퍼 */
int rcode; /* tx_return 코드 */
long xid; /* 현재 트랜잭션 ID (0=없음) */
} TXSVCINFO;
/*
* TX_SERVICE(name, ctx) -
* (Tuxedo void SVC(TPSVCINFO *) )
*
* TX_SERVICE(AC_INTAKE, ctx) { ... tx_return(ctx, TX_OK, out); }
*/
typedef void (*tx_service_fn)(TXSVCINFO *ctx);
#define TX_SERVICE(name, ctx) void name(TXSVCINFO *ctx)
/* ------------------------------------------------------------------ */
/* 버퍼 API (Fchg/Fget 대응) */
/* ------------------------------------------------------------------ */
TXBUF *tx_buf_alloc(void);
void tx_buf_free(TXBUF *buf);
void tx_buf_reset(TXBUF *buf);
/* 키에 문자열/바이너리 값을 설정 (없으면 추가, 있으면 갱신) */
int tx_buf_set(TXBUF *buf, const char *key, const char *val, int len);
/* 문자열 편의 setter (strlen 사용) */
int tx_buf_sets(TXBUF *buf, const char *key, const char *val);
/* long 값을 문자열로 저장 */
int tx_buf_setlong(TXBUF *buf, const char *key, long val);
/* 값을 out 으로 복사(널 종료). outlen 은 out 버퍼 크기 */
int tx_buf_get(TXBUF *buf, const char *key, char *out, int outlen);
/* long 으로 파싱하여 반환 */
int tx_buf_getlong(TXBUF *buf, const char *key, long *out);
/* ------------------------------------------------------------------ */
/* 서비스 레지스트리 & 디스패처 (tpcall/tpreturn 대응) */
/* ------------------------------------------------------------------ */
int tx_register(const char *name, tx_service_fn fn);
/* in -> 서비스 -> out. 로컬 레지스트리에서 해석되므로 단독 링크된다. */
int tx_call(const char *svc, TXBUF *in, TXBUF *out);
/* 서비스 내부에서 응답 반환 (out 을 ctx->out 으로 복사) */
int tx_return(TXSVCINFO *ctx, int rc, TXBUF *out);
/* 등록된 서비스 수 */
int tx_service_count(void);
/* ------------------------------------------------------------------ */
/* 트랜잭션 API (XA tpbegin/tpcommit/tpabort 대응, 여기선 로깅 스텁) */
/* ------------------------------------------------------------------ */
int tx_begin(void);
int tx_commit(void);
int tx_abort(void);
long tx_current_xid(void);
/* ------------------------------------------------------------------ */
/* 로깅 (userlog 대응) */
/* ------------------------------------------------------------------ */
void tx_log(int level, const char *fmt, ...);
void tx_set_loglevel(int level);
const char *tx_strerror(int rc);
#ifdef __cplusplus
}
#endif
#endif /* TXCORE_H */

View file

@ -0,0 +1,331 @@
/*
* txcore.c - TxCore
*
* in-process + / TP
* . gcc .
*/
#include "txcore.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------ */
/* 전역 상태 */
/* ------------------------------------------------------------------ */
#define TX_MAX_SERVICES 128
typedef struct {
char name[TX_KEY_LEN];
tx_service_fn fn;
} tx_entry_t;
static tx_entry_t g_registry[TX_MAX_SERVICES];
static int g_registry_count = 0;
static int g_loglevel = TX_LOG_INFO;
static long g_xid_seq = 0; /* 발급된 트랜잭션 ID 시퀀스 */
static long g_cur_xid = 0; /* 현재 활성 트랜잭션 */
/* ------------------------------------------------------------------ */
/* 로깅 */
/* ------------------------------------------------------------------ */
static const char *level_name(int level)
{
switch (level) {
case TX_LOG_DEBUG: return "DEBUG";
case TX_LOG_INFO: return "INFO ";
case TX_LOG_WARN: return "WARN ";
case TX_LOG_ERROR: return "ERROR";
default: return "?????";
}
}
void tx_set_loglevel(int level)
{
g_loglevel = level;
}
void tx_log(int level, const char *fmt, ...)
{
va_list ap;
time_t now;
struct tm tmv;
char ts[20];
if (level < g_loglevel)
return;
now = time(NULL);
localtime_r(&now, &tmv);
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", &tmv);
fprintf(stderr, "[%s] %s ", ts, level_name(level));
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
const char *tx_strerror(int rc)
{
switch (rc) {
case TX_OK: return "정상";
case TX_FAIL: return "일반 실패";
case TX_ENOENT: return "서비스 미등록";
case TX_EINVAL: return "파라미터 오류";
case TX_EDB: return "DB 오류";
case TX_ENOMEM: return "자원 부족";
case TX_ENOKEY: return "버퍼 키 없음";
default: return "알 수 없는 오류";
}
}
/* ------------------------------------------------------------------ */
/* 버퍼 */
/* ------------------------------------------------------------------ */
TXBUF *tx_buf_alloc(void)
{
TXBUF *b = (TXBUF *)calloc(1, sizeof(TXBUF));
return b;
}
void tx_buf_free(TXBUF *buf)
{
if (buf)
free(buf);
}
void tx_buf_reset(TXBUF *buf)
{
if (buf)
memset(buf, 0, sizeof(*buf));
}
static TXFIELD *find_slot(TXBUF *buf, const char *key)
{
int i;
for (i = 0; i < TX_MAX_SLOTS; i++) {
if (buf->slots[i].used &&
strncmp(buf->slots[i].key, key, TX_KEY_LEN) == 0)
return &buf->slots[i];
}
return NULL;
}
static TXFIELD *alloc_slot(TXBUF *buf, const char *key)
{
int i;
TXFIELD *f = find_slot(buf, key);
if (f)
return f;
for (i = 0; i < TX_MAX_SLOTS; i++) {
if (!buf->slots[i].used) {
f = &buf->slots[i];
memset(f, 0, sizeof(*f));
strncpy(f->key, key, TX_KEY_LEN - 1);
f->used = 1;
buf->count++;
return f;
}
}
return NULL; /* 슬롯 부족 */
}
int tx_buf_set(TXBUF *buf, const char *key, const char *val, int len)
{
TXFIELD *f;
if (!buf || !key || !val)
return TX_EINVAL;
if (len < 0 || len >= TX_VAL_LEN)
return TX_ENOMEM;
f = alloc_slot(buf, key);
if (!f)
return TX_ENOMEM;
memcpy(f->val, val, len);
f->val[len] = '\0';
f->len = len;
return TX_OK;
}
int tx_buf_sets(TXBUF *buf, const char *key, const char *val)
{
if (!val)
return TX_EINVAL;
return tx_buf_set(buf, key, val, (int)strlen(val));
}
int tx_buf_setlong(TXBUF *buf, const char *key, long val)
{
char tmp[32];
snprintf(tmp, sizeof(tmp), "%ld", val);
return tx_buf_set(buf, key, tmp, (int)strlen(tmp));
}
int tx_buf_get(TXBUF *buf, const char *key, char *out, int outlen)
{
TXFIELD *f;
if (!buf || !key || !out || outlen <= 0)
return TX_EINVAL;
f = find_slot(buf, key);
if (!f)
return TX_ENOKEY;
if (f->len >= outlen)
return TX_ENOMEM;
memcpy(out, f->val, f->len);
out[f->len] = '\0';
return TX_OK;
}
int tx_buf_getlong(TXBUF *buf, const char *key, long *out)
{
TXFIELD *f;
char *end;
long v;
if (!buf || !key || !out)
return TX_EINVAL;
f = find_slot(buf, key);
if (!f)
return TX_ENOKEY;
v = strtol(f->val, &end, 10);
if (end == f->val)
return TX_EINVAL;
*out = v;
return TX_OK;
}
/* ------------------------------------------------------------------ */
/* 레지스트리 & 디스패처 */
/* ------------------------------------------------------------------ */
int tx_register(const char *name, tx_service_fn fn)
{
int i;
if (!name || !fn)
return TX_EINVAL;
if (g_registry_count >= TX_MAX_SERVICES)
return TX_ENOMEM;
/* 중복 등록 시 갱신 */
for (i = 0; i < g_registry_count; i++) {
if (strncmp(g_registry[i].name, name, TX_KEY_LEN) == 0) {
g_registry[i].fn = fn;
return TX_OK;
}
}
strncpy(g_registry[g_registry_count].name, name, TX_KEY_LEN - 1);
g_registry[g_registry_count].fn = fn;
g_registry_count++;
tx_log(TX_LOG_DEBUG, "서비스 등록: %s", name);
return TX_OK;
}
int tx_service_count(void)
{
return g_registry_count;
}
static tx_service_fn lookup(const char *name)
{
int i;
for (i = 0; i < g_registry_count; i++) {
if (strncmp(g_registry[i].name, name, TX_KEY_LEN) == 0)
return g_registry[i].fn;
}
return NULL;
}
int tx_call(const char *svc, TXBUF *in, TXBUF *out)
{
tx_service_fn fn;
TXSVCINFO ctx;
if (!svc || !in || !out)
return TX_EINVAL;
fn = lookup(svc);
if (!fn) {
tx_log(TX_LOG_ERROR, "tx_call: 서비스 미등록 svc=%s", svc);
return TX_ENOENT;
}
memset(&ctx, 0, sizeof(ctx));
strncpy(ctx.name, svc, TX_KEY_LEN - 1);
ctx.in = in;
ctx.out = out;
ctx.rcode = TX_OK;
ctx.xid = g_cur_xid;
tx_log(TX_LOG_DEBUG, "tx_call ▶ %s", svc);
fn(&ctx);
tx_log(TX_LOG_DEBUG, "tx_call ◀ %s rc=%d", svc, ctx.rcode);
return ctx.rcode;
}
int tx_return(TXSVCINFO *ctx, int rc, TXBUF *out)
{
if (!ctx)
return TX_EINVAL;
ctx->rcode = rc;
/* 서비스가 별도 out 버퍼를 채웠다면 컨텍스트 응답 버퍼로 복사 */
if (out && ctx->out && out != ctx->out)
memcpy(ctx->out, out, sizeof(TXBUF));
return rc;
}
/* ------------------------------------------------------------------ */
/* 트랜잭션 (XA 스텁: 실제 자원관리자 대신 로깅) */
/* ------------------------------------------------------------------ */
int tx_begin(void)
{
if (g_cur_xid != 0) {
tx_log(TX_LOG_WARN, "tx_begin: 이미 진행중인 트랜잭션 xid=%ld", g_cur_xid);
return TX_FAIL;
}
g_cur_xid = ++g_xid_seq;
tx_log(TX_LOG_INFO, "tx_begin xid=%ld", g_cur_xid);
return TX_OK;
}
int tx_commit(void)
{
if (g_cur_xid == 0) {
tx_log(TX_LOG_WARN, "tx_commit: 진행중인 트랜잭션 없음");
return TX_FAIL;
}
tx_log(TX_LOG_INFO, "tx_commit xid=%ld", g_cur_xid);
g_cur_xid = 0;
return TX_OK;
}
int tx_abort(void)
{
if (g_cur_xid == 0) {
tx_log(TX_LOG_WARN, "tx_abort: 진행중인 트랜잭션 없음");
return TX_FAIL;
}
tx_log(TX_LOG_WARN, "tx_abort xid=%ld", g_cur_xid);
g_cur_xid = 0;
return TX_OK;
}
long tx_current_xid(void)
{
return g_cur_xid;
}