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
31
legacy/framework/txcore/dbio/txcore_dbio.h
Normal file
31
legacy/framework/txcore/dbio/txcore_dbio.h
Normal 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 */
|
||||
128
legacy/framework/txcore/include/txcore.h
Normal file
128
legacy/framework/txcore/include/txcore.h
Normal 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 */
|
||||
331
legacy/framework/txcore/src/txcore.c
Normal file
331
legacy/framework/txcore/src/txcore.c
Normal 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;
|
||||
}
|
||||
Reference in a new issue