Phase 2b: 11개 모듈 전량 실동작 (333 서비스, 전서버 부팅)
- 10개 모듈 추가/확장(au py rc st lg cl mm vl mg cm), 각 30 서비스 = 330 + tmsrv - build.sh 자동발견으로 11 모듈서버 + dbio 라이브러리 23 + 배치 22 빌드 - schema.d 11파일 → 75 테이블, 모듈별 disjoint - 부팅 리소스 규명: fs.mqueue.queues_max=8192(330큐>256), NDRX_MSGSIZEMAX 16000· MSGMAX 50(큐당 5.6MB→0.8MB), msgqueue ulimit 2GB → 전 12서버 runok - 검증: xadmin psc 333 AVAIL, 매입체인 XA 커밋, prepared_xacts=0 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8219b85f14
commit
bec83c721e
73 changed files with 10705 additions and 35 deletions
56
app/src/mg/batch/mg_resend_batch.pgc
Normal file
56
app/src/mg/batch/mg_resend_batch.pgc
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* mg_resend_batch.pgc - mg 실패 전문 재전송 배치 (커서 + XA).
|
||||
*
|
||||
* ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global
|
||||
* transaction, an EXEC SQL cursor sweeps mg_queue rows in F(실패)/Q(대기) state
|
||||
* for a biz_date, re-queues each (status->Q, retry_cnt++) via the linked dbio
|
||||
* lib, then tpcommit drives XA 2PC. Usage: mg_resend_batch [YYYY-MM-DD]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include "mg_dbio.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
|
||||
long rows = 0;
|
||||
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_qid, h_retry;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
|
||||
EXEC SQL DECLARE rsc CURSOR FOR
|
||||
SELECT queue_id, retry_cnt FROM mg_queue
|
||||
WHERE biz_date = :h_bizdate AND status = 'F'
|
||||
ORDER BY queue_id;
|
||||
EXEC SQL OPEN rsc;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH rsc INTO :h_qid, :h_retry;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE rsc; tpabort(0); return 1; }
|
||||
/* 실패 전문을 재전송 대기(Q)로 되돌리고 retry_cnt 증가 */
|
||||
EXEC SQL UPDATE mg_queue
|
||||
SET status = 'Q', retry_cnt = retry_cnt + 1, updated_at = now()
|
||||
WHERE queue_id = :h_qid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) { EXEC SQL CLOSE rsc; tpabort(0); return 1; }
|
||||
rows++;
|
||||
}
|
||||
EXEC SQL CLOSE rsc;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> mg_resend_batch COMMIT: bizdate=%s resent=%ld\n", bizdate, rows);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
62
app/src/mg/batch/mg_stat_batch.pgc
Normal file
62
app/src/mg/batch/mg_stat_batch.pgc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* mg_stat_batch.pgc - mg 채널 일통계 배치 (커서 + XA).
|
||||
*
|
||||
* Cursors over mg_msg_log aggregated per channel for a biz_date and upserts
|
||||
* mg_stat (수신/송신/오류 건수 + 금액합), all under ONE global XA transaction.
|
||||
* Usage: mg_stat_batch [YYYY-MM-DD]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include "mg_dbio.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
|
||||
long rows = 0;
|
||||
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
char h_chan[16];
|
||||
long h_in, h_out, h_err, h_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
|
||||
EXEC SQL DECLARE msc CURSOR FOR
|
||||
SELECT channel,
|
||||
coalesce(sum(CASE WHEN direction='IN' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN direction='OUT' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN rc<>'00' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(amount),0)
|
||||
FROM mg_msg_log WHERE biz_date = :h_bizdate
|
||||
GROUP BY channel ORDER BY channel;
|
||||
EXEC SQL OPEN msc;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH msc INTO :h_chan, :h_in, :h_out, :h_err, :h_sum;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE msc; tpabort(0); return 1; }
|
||||
EXEC SQL INSERT INTO mg_stat (channel, biz_date, in_cnt, out_cnt, err_cnt, amount_sum)
|
||||
VALUES (:h_chan, :h_bizdate, :h_in, :h_out, :h_err, :h_sum)
|
||||
ON CONFLICT (channel, biz_date) DO UPDATE
|
||||
SET in_cnt = EXCLUDED.in_cnt, out_cnt = EXCLUDED.out_cnt,
|
||||
err_cnt = EXCLUDED.err_cnt, amount_sum = EXCLUDED.amount_sum,
|
||||
updated_at = now();
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPSERT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE msc; tpabort(0); return 1; }
|
||||
rows++;
|
||||
}
|
||||
EXEC SQL CLOSE msc;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> mg_stat_batch COMMIT: bizdate=%s channels=%ld\n", bizdate, rows);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
41
app/src/mg/dbio/mg_dbio.h
Normal file
41
app/src/mg/dbio/mg_dbio.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* mg_dbio.h - mg 모듈 (전문게이트웨이) DB 접근 계층 (libmgdbio.a).
|
||||
* ECPG (EXEC SQL) functions compiled into a static library and linked into
|
||||
* mg_svr and the mg batches. They run on the caller's XA branch/connection
|
||||
* (no EXEC SQL CONNECT); sqlca is the shared per-thread ECPG SQLCA.
|
||||
* Return convention: 0 = ok, -1 = SQL error (caller may also inspect sqlca).
|
||||
*/
|
||||
#ifndef MG_DBIO_H
|
||||
#define MG_DBIO_H
|
||||
|
||||
/* 채번 (STAN/전문 시퀀스) */
|
||||
long mgdb_next_stan(void);
|
||||
long mgdb_next_msg_id(void);
|
||||
long mgdb_next_queue_id(void);
|
||||
long mgdb_seq_bump(const char *name); /* mg_seq 이름있는 카운터 +1 -> 새 값 */
|
||||
|
||||
/* 전문 로그 (mg_msg_log) */
|
||||
int mgdb_insert_msg(long msg_id, long stan, const char *mti, const char *channel,
|
||||
const char *direction, const char *proc_code, const char *bitmap,
|
||||
long amount, const char *pan, const char *rc, const char *raw,
|
||||
const char *status, const char *bizdate);
|
||||
int mgdb_update_msg_rc(long msg_id, const char *rc, const char *status);
|
||||
int mgdb_get_msg_status(long msg_id, char *out /* >= 2 bytes */);
|
||||
long mgdb_find_msg_by_stan(long stan); /* msg_id 반환, 없으면 -1 */
|
||||
|
||||
/* 라우팅 (mg_route) */
|
||||
int mgdb_insert_route(long route_id, const char *channel, const char *mti,
|
||||
const char *dest, long priority, const char *bizdate);
|
||||
int mgdb_lookup_route(const char *channel, const char *mti, char *dest_out /* >= 64 */);
|
||||
|
||||
/* 채널 (mg_channel) */
|
||||
int mgdb_get_channel_status(const char *channel, char *status_out /* >= 16 */);
|
||||
int mgdb_set_channel_status(const char *channel, const char *status);
|
||||
int mgdb_set_channel_session(const char *channel, const char *session_key, long stan);
|
||||
|
||||
/* 큐 (mg_queue) */
|
||||
int mgdb_enqueue(long queue_id, long stan, const char *channel, const char *mti,
|
||||
const char *payload, const char *bizdate);
|
||||
int mgdb_set_queue_status(long queue_id, const char *status);
|
||||
|
||||
#endif /* MG_DBIO_H */
|
||||
200
app/src/mg/dbio/mg_msg_dbio.pgc
Normal file
200
app/src/mg/dbio/mg_msg_dbio.pgc
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* mg_msg_dbio.pgc - 전문 로그/채번/큐 DB 접근 (part of libmgdbio.a).
|
||||
* ECPG functions; run on the caller's XA branch. No EXEC SQL CONNECT.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <userlog.h>
|
||||
#include "mg_dbio.h"
|
||||
|
||||
long mgdb_next_stan(void)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT nextval('mg_stan_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_next_stan FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
|
||||
long mgdb_next_msg_id(void)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT nextval('mg_msg_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_next_msg_id FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
|
||||
long mgdb_next_queue_id(void)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT nextval('mg_queue_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_next_queue_id FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
|
||||
/* mg_seq 이름있는 카운터를 원자적으로 +1 하고 새 값을 돌려준다 (세션 시퀀스 채번). */
|
||||
long mgdb_seq_bump(const char *name)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_val;
|
||||
char h_name[32];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_name, name, sizeof(h_name) - 1); h_name[sizeof(h_name)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mg_seq SET seq_val = seq_val + 1, updated_at = now()
|
||||
WHERE seq_name = :h_name;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_seq_bump UPDATE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
if (sqlca.sqlcode == 100) { /* 최초 사용: 행 생성 */
|
||||
EXEC SQL INSERT INTO mg_seq (seq_name, seq_val) VALUES (:h_name, 1);
|
||||
if (sqlca.sqlcode < 0) return -1;
|
||||
return 1;
|
||||
}
|
||||
EXEC SQL SELECT seq_val INTO :h_val FROM mg_seq WHERE seq_name = :h_name;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) return -1;
|
||||
return h_val;
|
||||
}
|
||||
|
||||
int mgdb_insert_msg(long msg_id, long stan, const char *mti, const char *channel,
|
||||
const char *direction, const char *proc_code, const char *bitmap,
|
||||
long amount, const char *pan, const char *rc, const char *raw,
|
||||
const char *status, const char *bizdate)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = msg_id, h_stan = stan, h_amount = amount;
|
||||
char h_mti[8], h_chan[16], h_dir[8], h_proc[8], h_bitmap[40];
|
||||
char h_pan[24], h_rc[4], h_raw[512], h_status[4], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_mti, mti, sizeof(h_mti)-1); h_mti[sizeof(h_mti)-1] = 0;
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_dir, direction, sizeof(h_dir)-1); h_dir[sizeof(h_dir)-1] = 0;
|
||||
strncpy(h_proc, proc_code, sizeof(h_proc)-1); h_proc[sizeof(h_proc)-1] = 0;
|
||||
strncpy(h_bitmap, bitmap, sizeof(h_bitmap)-1); h_bitmap[sizeof(h_bitmap)-1] = 0;
|
||||
strncpy(h_pan, pan, sizeof(h_pan)-1); h_pan[sizeof(h_pan)-1] = 0;
|
||||
strncpy(h_rc, rc, sizeof(h_rc)-1); h_rc[sizeof(h_rc)-1] = 0;
|
||||
strncpy(h_raw, raw, sizeof(h_raw)-1); h_raw[sizeof(h_raw)-1] = 0;
|
||||
strncpy(h_status, status, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO mg_msg_log
|
||||
(msg_id, stan, mti, channel, direction, proc_code, bitmap, amount,
|
||||
pan, rc, raw_msg, status, biz_date)
|
||||
VALUES (:h_id, :h_stan, :h_mti, :h_chan, :h_dir, :h_proc, :h_bitmap,
|
||||
:h_amount, :h_pan, :h_rc, :h_raw, :h_status, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_insert_msg FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgdb_update_msg_rc(long msg_id, const char *rc, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = msg_id;
|
||||
char h_rc[4], h_status[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_rc, rc, sizeof(h_rc)-1); h_rc[sizeof(h_rc)-1] = 0;
|
||||
strncpy(h_status, status, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mg_msg_log SET rc = :h_rc, status = :h_status WHERE msg_id = :h_id;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mgdb_update_msg_rc FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgdb_get_msg_status(long msg_id, char *out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = msg_id;
|
||||
char h_status[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT status INTO :h_status FROM mg_msg_log WHERE msg_id = :h_id;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mgdb_get_msg_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
out[0] = h_status[0];
|
||||
out[1] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long mgdb_find_msg_by_stan(long stan)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_stan = stan, h_id;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT msg_id INTO :h_id FROM mg_msg_log
|
||||
WHERE stan = :h_stan ORDER BY msg_id DESC LIMIT 1;
|
||||
if (sqlca.sqlcode == 100) return -1;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_find_msg_by_stan FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
|
||||
int mgdb_enqueue(long queue_id, long stan, const char *channel, const char *mti,
|
||||
const char *payload, const char *bizdate)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = queue_id, h_stan = stan;
|
||||
char h_chan[16], h_mti[8], h_payload[512], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_mti, mti, sizeof(h_mti)-1); h_mti[sizeof(h_mti)-1] = 0;
|
||||
strncpy(h_payload, payload, sizeof(h_payload)-1); h_payload[sizeof(h_payload)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO mg_queue (queue_id, stan, channel, mti, payload, status, biz_date)
|
||||
VALUES (:h_id, :h_stan, :h_chan, :h_mti, :h_payload, 'Q', :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_enqueue FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgdb_set_queue_status(long queue_id, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = queue_id;
|
||||
char h_status[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_status, status, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mg_queue SET status = :h_status, updated_at = now()
|
||||
WHERE queue_id = :h_id;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mgdb_set_queue_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
111
app/src/mg/dbio/mg_route_dbio.pgc
Normal file
111
app/src/mg/dbio/mg_route_dbio.pgc
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* mg_route_dbio.pgc - 라우팅 룰 + 채널 세션 DB 접근 (part of libmgdbio.a).
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <userlog.h>
|
||||
#include "mg_dbio.h"
|
||||
|
||||
int mgdb_insert_route(long route_id, const char *channel, const char *mti,
|
||||
const char *dest, long priority, const char *bizdate)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = route_id, h_pri = priority;
|
||||
char h_chan[16], h_mti[8], h_dest[64], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_mti, mti, sizeof(h_mti)-1); h_mti[sizeof(h_mti)-1] = 0;
|
||||
strncpy(h_dest, dest, sizeof(h_dest)-1); h_dest[sizeof(h_dest)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO mg_route (route_id, channel, mti, dest, priority, active, biz_date)
|
||||
VALUES (:h_id, :h_chan, :h_mti, :h_dest, :h_pri, true, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_insert_route FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 채널+전문유형으로 활성 라우팅 목적지를 우선순위 순으로 1건 조회. */
|
||||
int mgdb_lookup_route(const char *channel, const char *mti, char *dest_out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_chan[16], h_mti[8], h_dest[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_mti, mti, sizeof(h_mti)-1); h_mti[sizeof(h_mti)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT dest INTO :h_dest FROM mg_route
|
||||
WHERE channel = :h_chan AND mti = :h_mti AND active = true
|
||||
ORDER BY priority ASC LIMIT 1;
|
||||
if (sqlca.sqlcode == 100) { dest_out[0] = 0; return -1; }
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_lookup_route FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
strncpy(dest_out, h_dest, 63); dest_out[63] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgdb_get_channel_status(const char *channel, char *status_out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_chan[16], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT status INTO :h_status FROM mg_channel WHERE channel = :h_chan;
|
||||
if (sqlca.sqlcode == 100) { strcpy(status_out, "UNKNOWN"); return -1; }
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_get_channel_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
strncpy(status_out, h_status, 15); status_out[15] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgdb_set_channel_status(const char *channel, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_chan[16], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_status, status, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mg_channel SET status = :h_status, updated_at = now()
|
||||
WHERE channel = :h_chan;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mgdb_set_channel_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
if (sqlca.sqlcode == 100) { /* 신규 채널 등록 */
|
||||
EXEC SQL INSERT INTO mg_channel (channel, status) VALUES (:h_chan, :h_status);
|
||||
if (sqlca.sqlcode < 0) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgdb_set_channel_session(const char *channel, const char *session_key, long stan)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_stan = stan;
|
||||
char h_chan[16], h_key[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_chan, channel, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_key, session_key, sizeof(h_key)-1); h_key[sizeof(h_key)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mg_channel
|
||||
SET session_key = :h_key, last_stan = :h_stan, status = 'SIGNON',
|
||||
signon_at = now(), updated_at = now()
|
||||
WHERE channel = :h_chan;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mgdb_set_channel_session FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
649
app/src/mg/mg_svr.pgc
Normal file
649
app/src/mg/mg_svr.pgc
Normal file
|
|
@ -0,0 +1,649 @@
|
|||
/*
|
||||
* mg_svr.pgc - mg (전문게이트웨이 / message gateway) MODULE SERVER.
|
||||
*
|
||||
* ONE binary that tpadvertise()s ALL ~30 online mg services (the real Tuxedo
|
||||
* "one module server, many services" pattern). Every service is a distinct 전문
|
||||
* (ISO8583) gateway operation with genuine EXEC SQL (directly, or via the linked
|
||||
* libmgdbio.a dbio layer) plus 고정길이/비트맵 pack/unpack idioms and STAN 채번
|
||||
* via a Postgres sequence.
|
||||
*
|
||||
* XA: the connection is opened once by tpopen() (ECPG XA switch libndrxxaecpg.so);
|
||||
* there is NO EXEC SQL CONNECT. Each tpcall runs on this process's XA branch.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include <ubf.h>
|
||||
#include <userlog.h>
|
||||
#include <ndebug.h>
|
||||
#include "acq.fd.h"
|
||||
#include "mg_dbio.h"
|
||||
#include "acq_common.h"
|
||||
|
||||
/* ----- small UBF helpers ------------------------------------------------- */
|
||||
static long getl(UBFH *b, BFLDID f) { long v = 0; Bget(b, f, 0, (char *)&v, 0L); return v; }
|
||||
static void gets_(UBFH *b, BFLDID f, char *out, int cap)
|
||||
{ BFLDLEN l = cap; out[0] = 0; Bget(b, f, 0, out, &l); }
|
||||
static void setl(UBFH *b, BFLDID f, long v) { Bchg(b, f, 0, (char *)&v, 0L); }
|
||||
static void sets_(UBFH *b, BFLDID f, const char *s) { Bchg(b, f, 0, (char *)s, 0L); }
|
||||
|
||||
#define FAIL(b) do { tpreturn(TPFAIL, 0, (char *)(b), 0L, 0L); return; } while (0)
|
||||
#define OK(b) do { tpreturn(TPSUCCESS, 0, (char *)(b), 0L, 0L); return; } while (0)
|
||||
|
||||
/* ----- service prototypes ------------------------------------------------ */
|
||||
void MG_RECV(TPSVCINFO *p); void MG_SEND(TPSVCINFO *p);
|
||||
void MG_ISO8583(TPSVCINFO *p); void MG_BITMAP(TPSVCINFO *p);
|
||||
void MG_STAN(TPSVCINFO *p); void MG_ROUTE(TPSVCINFO *p);
|
||||
void MG_RESEND(TPSVCINFO *p); void MG_CONVERT(TPSVCINFO *p);
|
||||
void MG_ERRMSG(TPSVCINFO *p); void MG_LOG(TPSVCINFO *p);
|
||||
void MG_CHANAUTH(TPSVCINFO *p); void MG_SESSION(TPSVCINFO *p);
|
||||
void MG_ACK(TPSVCINFO *p); void MG_NACK(TPSVCINFO *p);
|
||||
void MG_ECHO(TPSVCINFO *p); void MG_HEALTH(TPSVCINFO *p);
|
||||
void MG_SIGNON(TPSVCINFO *p); void MG_SIGNOFF(TPSVCINFO *p);
|
||||
void MG_KEYEXCH(TPSVCINFO *p); void MG_MAC(TPSVCINFO *p);
|
||||
void MG_PARSE(TPSVCINFO *p); void MG_PACK(TPSVCINFO *p);
|
||||
void MG_UNPACK(TPSVCINFO *p); void MG_QUEUE(TPSVCINFO *p);
|
||||
void MG_DEQUEUE(TPSVCINFO *p); void MG_RETRY(TPSVCINFO *p);
|
||||
void MG_STATS(TPSVCINFO *p); void MG_CHANSTAT(TPSVCINFO *p);
|
||||
void MG_STATUS(TPSVCINFO *p); void MG_CONFIRM(TPSVCINFO *p);
|
||||
|
||||
/* ----- advertise table (this is what "scales to 30" cleanly) ------------- */
|
||||
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
|
||||
{"MG_RECV", MG_RECV}, {"MG_SEND", MG_SEND},
|
||||
{"MG_ISO8583", MG_ISO8583}, {"MG_BITMAP", MG_BITMAP},
|
||||
{"MG_STAN", MG_STAN}, {"MG_ROUTE", MG_ROUTE},
|
||||
{"MG_RESEND", MG_RESEND}, {"MG_CONVERT", MG_CONVERT},
|
||||
{"MG_ERRMSG", MG_ERRMSG}, {"MG_LOG", MG_LOG},
|
||||
{"MG_CHANAUTH", MG_CHANAUTH}, {"MG_SESSION", MG_SESSION},
|
||||
{"MG_ACK", MG_ACK}, {"MG_NACK", MG_NACK},
|
||||
{"MG_ECHO", MG_ECHO}, {"MG_HEALTH", MG_HEALTH},
|
||||
{"MG_SIGNON", MG_SIGNON}, {"MG_SIGNOFF", MG_SIGNOFF},
|
||||
{"MG_KEYEXCH", MG_KEYEXCH}, {"MG_MAC", MG_MAC},
|
||||
{"MG_PARSE", MG_PARSE}, {"MG_PACK", MG_PACK},
|
||||
{"MG_UNPACK", MG_UNPACK}, {"MG_QUEUE", MG_QUEUE},
|
||||
{"MG_DEQUEUE", MG_DEQUEUE}, {"MG_RETRY", MG_RETRY},
|
||||
{"MG_STATS", MG_STATS}, {"MG_CHANSTAT", MG_CHANSTAT},
|
||||
{"MG_STATUS", MG_STATUS}, {"MG_CONFIRM", MG_CONFIRM},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int tpsvrinit(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
if (tpopen() < 0) {
|
||||
userlog("mg_svr: tpopen FAIL: %s", tpstrerror(tperrno));
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; SVCS[i].name != NULL; i++) {
|
||||
if (tpadvertise((char *)SVCS[i].name, SVCS[i].fn) < 0) {
|
||||
userlog("mg_svr: tpadvertise(%s) FAIL: %s", SVCS[i].name, tpstrerror(tperrno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
userlog("mg_svr: %d mg 서비스 광고 완료, RM 오픈", i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tpsvrdone(void)
|
||||
{
|
||||
tpclose();
|
||||
userlog("mg_svr: tpsvrdone");
|
||||
}
|
||||
|
||||
/* ----- module-local pure helpers (고정길이/비트맵/체크섬) ----------------- */
|
||||
/* 16-hex primary bitmap from a 64-bit field-present mask. */
|
||||
static void mg_bitmap_hex(unsigned long mask, char *out /* >= 17 */)
|
||||
{
|
||||
sprintf(out, "%016lX", mask);
|
||||
}
|
||||
/* simple additive 4-hex MAC/checksum over a buffer (전문 무결성 데모). */
|
||||
static void mg_checksum(const char *s, char *out /* >= 5 */)
|
||||
{
|
||||
unsigned int sum = 0; int i;
|
||||
for (i = 0; s[i]; i++) sum = (sum + (unsigned char)s[i] * 31u) & 0xFFFF;
|
||||
sprintf(out, "%04X", sum);
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* 1. MG_RECV - 전문 수신 접수 (direction=IN). STAN 채번 후 로그 적재. */
|
||||
/* ========================================================================= */
|
||||
void MG_RECV(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], raw[512], bizdate[16], pan[24];
|
||||
long stan, msg_id, amount;
|
||||
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_STR5, pan, sizeof(pan));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
amount = getl(b, T_AMOUNT);
|
||||
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b);
|
||||
if (mgdb_insert_msg(msg_id, stan, mti, chan, "IN", "000000", "", amount,
|
||||
pan, "00", raw, "R", bizdate) < 0) FAIL(b);
|
||||
|
||||
setl(b, T_ARG1, stan);
|
||||
setl(b, T_ID1, msg_id);
|
||||
userlog("MG_RECV 수신 stan=%ld msg_id=%ld chan=%s mti=%s amt=%ld", stan, msg_id, chan, mti, amount);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 2. MG_SEND - 전문 송신 (direction=OUT). */
|
||||
void MG_SEND(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], raw[512], bizdate[16];
|
||||
long stan, msg_id, amount;
|
||||
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0210");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
amount = getl(b, T_AMOUNT);
|
||||
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b);
|
||||
if (mgdb_insert_msg(msg_id, stan, mti, chan, "OUT", "000000", "", amount,
|
||||
"", "00", raw, "S", bizdate) < 0) FAIL(b);
|
||||
setl(b, T_ARG1, stan);
|
||||
setl(b, T_ID1, msg_id);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 3. MG_ISO8583 - ISO8583 전문 파싱 후 로그 (mti=전문 앞 4자리). */
|
||||
void MG_ISO8583(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], raw[512], bizdate[16], mti[8], proc[8];
|
||||
long stan, msg_id, amount, n;
|
||||
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
n = (long)strlen(raw);
|
||||
/* 고정길이 전문: [0..3]=MTI, [4..9]=처리코드, [10..21]=금액(12자리) */
|
||||
memset(mti, 0, sizeof(mti)); memset(proc, 0, sizeof(proc));
|
||||
if (n >= 4) strncpy(mti, raw, 4);
|
||||
else strcpy(mti, "0200");
|
||||
if (n >= 10) strncpy(proc, raw + 4, 6);
|
||||
else strcpy(proc, "000000");
|
||||
amount = (n >= 22) ? atol(raw + 10) : getl(b, T_AMOUNT);
|
||||
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b);
|
||||
if (mgdb_insert_msg(msg_id, stan, mti, chan, "IN", proc, "", amount,
|
||||
"", "00", raw, "R", bizdate) < 0) FAIL(b);
|
||||
sets_(b, T_STR1, mti);
|
||||
sets_(b, T_STR4, proc);
|
||||
setl(b, T_AMOUNT, amount);
|
||||
setl(b, T_ID1, msg_id);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 4. MG_BITMAP - 비트맵 계산 후 해당 전문(msg_id)에 반영. */
|
||||
void MG_BITMAP(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char hex[24];
|
||||
unsigned long mask = (unsigned long)getl(b, T_ARG1); /* 필드 존재 마스크 */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
char h_bitmap[40];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
mg_bitmap_hex(mask, hex);
|
||||
sets_(b, T_STR6, hex);
|
||||
|
||||
h_id = getl(b, T_ID1);
|
||||
if (h_id > 0) {
|
||||
strncpy(h_bitmap, hex, sizeof(h_bitmap)-1); h_bitmap[sizeof(h_bitmap)-1] = 0;
|
||||
EXEC SQL UPDATE mg_msg_log SET bitmap = :h_bitmap WHERE msg_id = :h_id;
|
||||
if (sqlca.sqlcode < 0) { userlog("MG_BITMAP UPDATE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
}
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 5. MG_STAN - STAN 채번 (Postgres sequence). */
|
||||
void MG_STAN(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long stan = mgdb_next_stan();
|
||||
if (stan < 0) FAIL(b);
|
||||
setl(b, T_ARG1, stan);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 6. MG_ROUTE - 채널+전문유형 -> 목적지 라우팅 조회. */
|
||||
void MG_ROUTE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], dest[64];
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200");
|
||||
if (mgdb_lookup_route(chan, mti, dest) < 0) {
|
||||
userlog("MG_ROUTE 라우팅 없음 chan=%s mti=%s", chan, mti);
|
||||
setl(b, T_RC, 1);
|
||||
FAIL(b);
|
||||
}
|
||||
sets_(b, T_STR3, dest);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 7. MG_RESEND - 전문 재전송: STAN 으로 원전문 찾아 큐에 적재. */
|
||||
void MG_RESEND(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], raw[512], bizdate[16];
|
||||
long stan, msg_id, qid;
|
||||
|
||||
stan = getl(b, T_ARG1);
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
|
||||
msg_id = mgdb_find_msg_by_stan(stan);
|
||||
if (msg_id < 0) { userlog("MG_RESEND 원전문 없음 stan=%ld", stan); FAIL(b); }
|
||||
|
||||
qid = mgdb_next_queue_id(); if (qid < 0) FAIL(b);
|
||||
if (mgdb_enqueue(qid, stan, chan, mti, raw, bizdate) < 0) FAIL(b);
|
||||
setl(b, T_ID2, qid);
|
||||
userlog("MG_RESEND 재전송 큐적재 stan=%ld qid=%ld", stan, qid);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 8. MG_CONVERT - 요청전문 -> 응답전문 변환 (MTI +0x10) 후 송신 로그. */
|
||||
void MG_CONVERT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], rmti[8], raw[512], bizdate[16];
|
||||
long stan, msg_id, amount, mnum;
|
||||
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
amount = getl(b, T_AMOUNT);
|
||||
|
||||
mnum = atol(mti) + 10; /* 0200 -> 0210 응답 */
|
||||
sprintf(rmti, "%04ld", mnum);
|
||||
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b);
|
||||
if (mgdb_insert_msg(msg_id, stan, rmti, chan, "OUT", "000000", "", amount,
|
||||
"", "00", raw, "S", bizdate) < 0) FAIL(b);
|
||||
sets_(b, T_STR1, rmti);
|
||||
setl(b, T_ID1, msg_id);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 9. MG_ERRMSG - 오류 전문 기록 (status=N, rc=입력). */
|
||||
void MG_ERRMSG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], raw[512], bizdate[16], rc[4];
|
||||
long stan, msg_id, rcnum;
|
||||
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0210");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
rcnum = getl(b, T_RC); if (rcnum <= 0) rcnum = 96;
|
||||
sprintf(rc, "%02ld", rcnum % 100);
|
||||
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b);
|
||||
if (mgdb_insert_msg(msg_id, stan, mti, chan, "OUT", "000000", "", 0,
|
||||
"", rc, raw, "N", bizdate) < 0) FAIL(b);
|
||||
setl(b, T_ID1, msg_id);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 10. MG_LOG - 채널 당일 전문 로그 건수 조회. */
|
||||
void MG_LOG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt;
|
||||
char h_chan[16], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT count(*) INTO :h_cnt FROM mg_msg_log
|
||||
WHERE channel = :h_chan AND biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 11. MG_CHANAUTH - 채널 인증: UP/SIGNON 상태만 통과. */
|
||||
void MG_CHANAUTH(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], st[16];
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
if (mgdb_get_channel_status(chan, st) < 0) { setl(b, T_RC, 2); FAIL(b); }
|
||||
if (strcmp(st, "UP") == 0 || strcmp(st, "SIGNON") == 0) {
|
||||
setl(b, T_RC, 0);
|
||||
sets_(b, T_STATUS, st);
|
||||
OK(b);
|
||||
}
|
||||
userlog("MG_CHANAUTH 거부 chan=%s status=%s", chan, st);
|
||||
setl(b, T_RC, 1);
|
||||
sets_(b, T_STATUS, st);
|
||||
FAIL(b);
|
||||
}
|
||||
|
||||
/* 12. MG_SESSION - 세션 시퀀스 채번 (mg_seq SESSION). */
|
||||
void MG_SESSION(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long v = mgdb_seq_bump("SESSION");
|
||||
if (v < 0) FAIL(b);
|
||||
setl(b, T_ARG2, v);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 13. MG_ACK - 응답 확인 (status=A, rc=00). */
|
||||
void MG_ACK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long msg_id = getl(b, T_ID1);
|
||||
if (mgdb_update_msg_rc(msg_id, "00", "A") < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, "A");
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 14. MG_NACK - 부정 응답 (status=N, rc=입력). */
|
||||
void MG_NACK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char rc[4];
|
||||
long msg_id = getl(b, T_ID1), rcnum = getl(b, T_RC);
|
||||
if (rcnum <= 0) rcnum = 91;
|
||||
sprintf(rc, "%02ld", rcnum % 100);
|
||||
if (mgdb_update_msg_rc(msg_id, rc, "N") < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, "N");
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 15. MG_ECHO - 에코 전문(0800) 처리 + 채널 UP 표시. */
|
||||
void MG_ECHO(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], bizdate[16];
|
||||
long stan, msg_id;
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b);
|
||||
if (mgdb_insert_msg(msg_id, stan, "0800", chan, "IN", "990000", "", 0,
|
||||
"", "00", "ECHO-TEST", "A", bizdate) < 0) FAIL(b);
|
||||
if (mgdb_set_channel_status(chan, "UP") < 0) FAIL(b);
|
||||
setl(b, T_ID1, msg_id);
|
||||
sets_(b, T_STATUS, "UP");
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 16. MG_HEALTH - 게이트웨이 헬스: 활성 채널 수 조회. */
|
||||
void MG_HEALTH(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_up;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
EXEC SQL SELECT count(*) INTO :h_up FROM mg_channel WHERE status IN ('UP','SIGNON');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_up);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 17. MG_SIGNON - 채널 사인온 + 세션키 설정. */
|
||||
void MG_SIGNON(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], key[64];
|
||||
long stan;
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_KEY1, key, sizeof(key)); if (key[0] == 0) strcpy(key, "SESSION-KEY-DFLT");
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
if (mgdb_set_channel_session(chan, key, stan) < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, "SIGNON");
|
||||
userlog("MG_SIGNON chan=%s stan=%ld", chan, stan);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 18. MG_SIGNOFF - 채널 사인오프 (status=DOWN). */
|
||||
void MG_SIGNOFF(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16];
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
if (mgdb_set_channel_status(chan, "DOWN") < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, "DOWN");
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 19. MG_KEYEXCH - 키 교환: 키버전 채번 + 세션키 갱신. */
|
||||
void MG_KEYEXCH(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], key[64];
|
||||
long ver, stan;
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_KEY1, key, sizeof(key)); if (key[0] == 0) strcpy(key, "NEW-KEY");
|
||||
ver = mgdb_seq_bump("KEYVER"); if (ver < 0) FAIL(b);
|
||||
stan = mgdb_next_stan(); if (stan < 0) FAIL(b);
|
||||
if (mgdb_set_channel_session(chan, key, stan) < 0) FAIL(b);
|
||||
setl(b, T_ARG2, ver);
|
||||
userlog("MG_KEYEXCH chan=%s keyver=%ld", chan, ver);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 20. MG_MAC - 전문 MAC 계산 후 해당 전문에 반영 (무결성). */
|
||||
void MG_MAC(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char raw[512], mac[8];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
char h_mac[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
mg_checksum(raw, mac);
|
||||
sets_(b, T_KEY1, mac);
|
||||
|
||||
h_id = getl(b, T_ID1);
|
||||
if (h_id > 0) {
|
||||
strncpy(h_mac, mac, sizeof(h_mac)-1); h_mac[sizeof(h_mac)-1] = 0;
|
||||
EXEC SQL UPDATE mg_msg_log SET mac = :h_mac WHERE msg_id = :h_id;
|
||||
if (sqlca.sqlcode < 0) { userlog("MG_MAC UPDATE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
}
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 21. MG_PARSE - 고정길이 전문에서 처리코드 필드 추출 (순수 계산). */
|
||||
void MG_PARSE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char raw[512], mti[8], proc[8];
|
||||
long n;
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
n = (long)strlen(raw);
|
||||
memset(mti, 0, sizeof(mti)); memset(proc, 0, sizeof(proc));
|
||||
if (n >= 4) strncpy(mti, raw, 4); else strcpy(mti, "0000");
|
||||
if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "000000");
|
||||
sets_(b, T_STR1, mti);
|
||||
sets_(b, T_STR4, proc);
|
||||
setl(b, T_COUNT, n);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 22. MG_PACK - 고정길이 전문 조립 (순수 계산): MTI(4)+처리코드(6)+금액(12). */
|
||||
void MG_PACK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char mti[8], proc[8], raw[64];
|
||||
long amount;
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200");
|
||||
gets_(b, T_STR4, proc, sizeof(proc)); if (proc[0] == 0) strcpy(proc, "000000");
|
||||
amount = getl(b, T_AMOUNT);
|
||||
sprintf(raw, "%.4s%.6s%012ld", mti, proc, amount);
|
||||
sets_(b, T_STR3, raw);
|
||||
setl(b, T_COUNT, (long)strlen(raw));
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 23. MG_UNPACK - 고정길이 전문 해체 (순수 계산). */
|
||||
void MG_UNPACK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char raw[512], mti[8], proc[8];
|
||||
long amount, n;
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
n = (long)strlen(raw);
|
||||
memset(mti, 0, sizeof(mti)); memset(proc, 0, sizeof(proc));
|
||||
if (n >= 4) strncpy(mti, raw, 4); else strcpy(mti, "0000");
|
||||
if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "000000");
|
||||
amount = (n >= 22) ? atol(raw + 10) : 0;
|
||||
sets_(b, T_STR1, mti);
|
||||
sets_(b, T_STR4, proc);
|
||||
setl(b, T_AMOUNT, amount);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 24. MG_QUEUE - 송신 큐 적재. */
|
||||
void MG_QUEUE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], mti[8], raw[512], bizdate[16];
|
||||
long stan, qid;
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200");
|
||||
gets_(b, T_STR3, raw, sizeof(raw));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
stan = getl(b, T_ARG1);
|
||||
if (stan <= 0) { stan = mgdb_next_stan(); if (stan < 0) FAIL(b); }
|
||||
qid = mgdb_next_queue_id(); if (qid < 0) FAIL(b);
|
||||
if (mgdb_enqueue(qid, stan, chan, mti, raw, bizdate) < 0) FAIL(b);
|
||||
setl(b, T_ID2, qid);
|
||||
setl(b, T_ARG1, stan);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 25. MG_DEQUEUE - 대기(Q) 전문 1건 인출 후 송신중(S) 전이. */
|
||||
void MG_DEQUEUE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_qid, h_stan;
|
||||
char h_payload[512], h_chan[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT queue_id, stan, channel, payload INTO :h_qid, :h_stan, :h_chan, :h_payload
|
||||
FROM mg_queue WHERE status = 'Q' ORDER BY queue_id ASC LIMIT 1;
|
||||
if (sqlca.sqlcode == 100) { setl(b, T_COUNT, 0); OK(b); }
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
EXEC SQL UPDATE mg_queue SET status = 'S', updated_at = now() WHERE queue_id = :h_qid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
|
||||
setl(b, T_ID2, h_qid);
|
||||
setl(b, T_ARG1, h_stan);
|
||||
sets_(b, T_CHANNEL, h_chan);
|
||||
sets_(b, T_STR3, h_payload);
|
||||
setl(b, T_COUNT, 1);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 26. MG_RETRY - 큐 재시도: retry_cnt +1, status=Q. */
|
||||
void MG_RETRY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_qid, h_retry;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_qid = getl(b, T_ID2);
|
||||
EXEC SQL UPDATE mg_queue SET retry_cnt = retry_cnt + 1, status = 'Q', updated_at = now()
|
||||
WHERE queue_id = :h_qid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
EXEC SQL SELECT retry_cnt INTO :h_retry FROM mg_queue WHERE queue_id = :h_qid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
setl(b, T_COUNT, h_retry);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 27. MG_STATS - 채널 당일 통계: 수신/송신/오류 건수. */
|
||||
void MG_STATS(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_in, h_out, h_err, h_sum;
|
||||
char h_chan[16], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT
|
||||
coalesce(sum(CASE WHEN direction='IN' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN direction='OUT' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN rc<>'00' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(amount),0)
|
||||
INTO :h_in, :h_out, :h_err, :h_sum
|
||||
FROM mg_msg_log WHERE channel = :h_chan AND biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_ARG1, h_in);
|
||||
setl(b, T_ARG2, h_out);
|
||||
setl(b, T_ARG3, h_err);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 28. MG_CHANSTAT - 채널 상태 조회. */
|
||||
void MG_CHANSTAT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char chan[16], st[16];
|
||||
gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01");
|
||||
if (mgdb_get_channel_status(chan, st) < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, st);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 29. MG_STATUS - 전문 처리 상태 조회 (msg_id). */
|
||||
void MG_STATUS(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long msg_id = getl(b, T_ID1);
|
||||
char st[4];
|
||||
if (mgdb_get_msg_status(msg_id, st) < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, st);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 30. MG_CONFIRM - 전문 확정 (status=C). */
|
||||
void MG_CONFIRM(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long msg_id = getl(b, T_ID1);
|
||||
if (mgdb_update_msg_rc(msg_id, "00", "C") < 0) FAIL(b);
|
||||
sets_(b, T_STATUS, "C");
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 et smartindent: */
|
||||
Loading…
Add table
Add a link
Reference in a new issue