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
68
app/src/mm/batch/mm_grade_recalc_batch.pgc
Normal file
68
app/src/mm/batch/mm_grade_recalc_batch.pgc
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* mm_grade_recalc_batch.pgc - 가맹점 등급 재산정 배치 (누적매출 커서 + XA).
|
||||
*
|
||||
* ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global
|
||||
* transaction, an EXEC SQL cursor sums each merchant's 누적 매출(gross) from
|
||||
* merchant_summary, derives a grade (S/A/B/C) and UPDATEs mm_merchant_ext (via
|
||||
* the linked dbio lib), then tpcommit drives XA 2PC.
|
||||
* Usage: mm_grade_recalc_batch
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include "mm_dbio.h"
|
||||
|
||||
/* 누적매출 -> 등급 임계 (원): S>=10억, A>=3억, B>=5천만, 그 외 C */
|
||||
static const char *grade_of(long gross)
|
||||
{
|
||||
if (gross >= 1000000000L) return "S";
|
||||
if (gross >= 300000000L) return "A";
|
||||
if (gross >= 50000000L) return "B";
|
||||
return "C";
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
long rows = 0;
|
||||
(void)argc; (void)argv;
|
||||
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64];
|
||||
long h_gross;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
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; }
|
||||
|
||||
/* 가맹점별 누적 매출 = merchant_summary.gross_amount 합계 */
|
||||
EXEC SQL DECLARE grc CURSOR FOR
|
||||
SELECT ms.merchant_id, coalesce(sum(ms.gross_amount),0)
|
||||
FROM merchant_summary ms
|
||||
JOIN mm_merchant_ext me ON me.merchant_id = ms.merchant_id
|
||||
WHERE me.status = 'ACTIVE'
|
||||
GROUP BY ms.merchant_id ORDER BY ms.merchant_id;
|
||||
EXEC SQL OPEN grc;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH grc INTO :h_merch, :h_gross;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE grc; tpabort(0); return 1; }
|
||||
if (mmdb_update_merchant_grade(h_merch, grade_of(h_gross)) < 0) {
|
||||
EXEC SQL CLOSE grc; tpabort(0); return 1;
|
||||
}
|
||||
if (mmdb_insert_merch_hist(h_merch, "GRADE", "", grade_of(h_gross), "배치 재산정") < 0) {
|
||||
EXEC SQL CLOSE grc; tpabort(0); return 1;
|
||||
}
|
||||
rows++;
|
||||
}
|
||||
EXEC SQL CLOSE grc;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> mm_grade_recalc_batch COMMIT: 재산정 가맹점=%ld\n", rows);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
53
app/src/mm/batch/mm_stat_batch.pgc
Normal file
53
app/src/mm/batch/mm_stat_batch.pgc
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* mm_stat_batch.pgc - 등급별 가맹점 수 통계 배치 (커서 + XA).
|
||||
*
|
||||
* Cursors over mm_merchant_ext grouped by grade, upserting a per-grade count
|
||||
* into mm_grade_stat for the run date, all under ONE global XA transaction.
|
||||
* Usage: mm_stat_batch [YYYY-MM-DD]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *statdate = (argc > 1) ? argv[1] : "2026-07-19";
|
||||
long rows = 0;
|
||||
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_statdate[16], h_grade[8];
|
||||
long h_cnt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_statdate, statdate, sizeof(h_statdate)-1); h_statdate[sizeof(h_statdate)-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 gsc CURSOR FOR
|
||||
SELECT grade, count(*) FROM mm_merchant_ext
|
||||
WHERE status = 'ACTIVE' GROUP BY grade ORDER BY grade;
|
||||
EXEC SQL OPEN gsc;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH gsc INTO :h_grade, :h_cnt;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE gsc; tpabort(0); return 1; }
|
||||
EXEC SQL INSERT INTO mm_grade_stat (stat_date, grade, merch_cnt)
|
||||
VALUES (:h_statdate, :h_grade, :h_cnt)
|
||||
ON CONFLICT (stat_date, grade) DO UPDATE
|
||||
SET merch_cnt = EXCLUDED.merch_cnt, updated_at = now();
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPSERT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE gsc; tpabort(0); return 1; }
|
||||
rows++;
|
||||
}
|
||||
EXEC SQL CLOSE gsc;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> mm_stat_batch COMMIT: statdate=%s 등급구간=%ld\n", statdate, rows);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
28
app/src/mm/dbio/mm_dbio.h
Normal file
28
app/src/mm/dbio/mm_dbio.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* mm_dbio.h - mm 모듈 (마스터/master) DB 접근 계층 (libmmdbio.a).
|
||||
* ECPG (EXEC SQL) functions compiled into a static library and linked into
|
||||
* mm_svr and the mm 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 MM_DBIO_H
|
||||
#define MM_DBIO_H
|
||||
|
||||
/* 가맹점 마스터 (mm_merch_dbio) */
|
||||
long mmdb_next_hist_id(void);
|
||||
int mmdb_insert_merchant(const char *merch, const char *name, const char *grade,
|
||||
const char *acct, const char *status);
|
||||
int mmdb_get_merchant(const char *merch, char *name_out /*>=64*/,
|
||||
char *grade_out /*>=8*/, char *status_out /*>=16*/);
|
||||
int mmdb_update_merchant_status(const char *merch, const char *status);
|
||||
int mmdb_update_merchant_grade(const char *merch, const char *grade);
|
||||
int mmdb_insert_merch_hist(const char *merch, const char *action,
|
||||
const char *before_val, const char *after_val,
|
||||
const char *memo);
|
||||
|
||||
/* 수수료율 마스터 (mm_feerate_dbio) */
|
||||
int mmdb_set_fee_rate(const char *merch, const char *card_type, long rate_bps);
|
||||
int mmdb_get_fee_rate(const char *merch, const char *card_type, long *bps_out);
|
||||
long mmdb_feerate_hist_count(const char *merch);
|
||||
|
||||
#endif /* MM_DBIO_H */
|
||||
77
app/src/mm/dbio/mm_feerate_dbio.pgc
Normal file
77
app/src/mm/dbio/mm_feerate_dbio.pgc
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* mm_feerate_dbio.pgc - 수수료율 마스터 upsert + 이력 적재 (libmmdbio.a).
|
||||
* ECPG functions; run on the caller's XA branch. No EXEC SQL CONNECT.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <userlog.h>
|
||||
#include "mm_dbio.h"
|
||||
|
||||
int mmdb_set_fee_rate(const char *merch, const char *card_type, long rate_bps)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id, h_bps = rate_bps;
|
||||
char h_merch[64], h_ctype[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_ctype, card_type, sizeof(h_ctype) - 1); h_ctype[sizeof(h_ctype)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO mm_fee_rate (merchant_id, card_type, rate_bps, eff_date)
|
||||
VALUES (:h_merch, :h_ctype, :h_bps, current_date)
|
||||
ON CONFLICT (merchant_id, card_type) DO UPDATE
|
||||
SET rate_bps = EXCLUDED.rate_bps, eff_date = current_date, updated_at = now();
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mmdb_set_fee_rate FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 이력 적재 */
|
||||
EXEC SQL SELECT nextval('mm_feerate_hist_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) return -1;
|
||||
EXEC SQL INSERT INTO mm_fee_rate_hist
|
||||
(hist_id, merchant_id, card_type, rate_bps, eff_date)
|
||||
VALUES (:h_id, :h_merch, :h_ctype, :h_bps, current_date);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mmdb_set_fee_rate hist FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmdb_get_fee_rate(const char *merch, const char *card_type, long *bps_out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_bps;
|
||||
char h_merch[64], h_ctype[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_ctype, card_type, sizeof(h_ctype) - 1); h_ctype[sizeof(h_ctype)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT rate_bps INTO :h_bps FROM mm_fee_rate
|
||||
WHERE merchant_id = :h_merch AND card_type = :h_ctype;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mmdb_get_fee_rate FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
*bps_out = h_bps;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long mmdb_feerate_hist_count(const char *merch)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt;
|
||||
char h_merch[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_fee_rate_hist
|
||||
WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mmdb_feerate_hist_count FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_cnt;
|
||||
}
|
||||
133
app/src/mm/dbio/mm_merch_dbio.pgc
Normal file
133
app/src/mm/dbio/mm_merch_dbio.pgc
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* mm_merch_dbio.pgc - 가맹점 확장 마스터 CRUD + 변경 이력 (libmmdbio.a).
|
||||
* ECPG functions; run on the caller's XA branch. No EXEC SQL CONNECT.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <userlog.h>
|
||||
#include "mm_dbio.h"
|
||||
|
||||
long mmdb_next_hist_id(void)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT nextval('mm_merch_hist_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mmdb_next_hist_id FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
|
||||
int mmdb_insert_merchant(const char *merch, const char *name, const char *grade,
|
||||
const char *acct, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_name[128], h_grade[8], h_acct[64], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_name, name, sizeof(h_name) - 1); h_name[sizeof(h_name)-1] = 0;
|
||||
strncpy(h_grade, grade, sizeof(h_grade) - 1); h_grade[sizeof(h_grade)-1] = 0;
|
||||
strncpy(h_acct, acct, sizeof(h_acct) - 1); h_acct[sizeof(h_acct)-1] = 0;
|
||||
strncpy(h_status, status, sizeof(h_status) - 1); h_status[sizeof(h_status)-1]= 0;
|
||||
|
||||
EXEC SQL INSERT INTO mm_merchant_ext
|
||||
(merchant_id, name, grade, settle_acct, status)
|
||||
VALUES (:h_merch, :h_name, :h_grade, :h_acct, :h_status)
|
||||
ON CONFLICT (merchant_id) DO UPDATE
|
||||
SET name = EXCLUDED.name, grade = EXCLUDED.grade,
|
||||
settle_acct = EXCLUDED.settle_acct, status = EXCLUDED.status,
|
||||
updated_at = now();
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mmdb_insert_merchant FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmdb_get_merchant(const char *merch, char *name_out, char *grade_out,
|
||||
char *status_out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_name[128], h_grade[8], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT name, grade, status INTO :h_name, :h_grade, :h_status
|
||||
FROM mm_merchant_ext WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mmdb_get_merchant FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
strncpy(name_out, h_name, 63); name_out[63] = 0;
|
||||
strncpy(grade_out, h_grade, 7); grade_out[7] = 0;
|
||||
strncpy(status_out, h_status, 15); status_out[15] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmdb_update_merchant_status(const char *merch, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_status, status, sizeof(h_status) - 1); h_status[sizeof(h_status)-1]= 0;
|
||||
|
||||
EXEC SQL UPDATE mm_merchant_ext SET status = :h_status, updated_at = now()
|
||||
WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mmdb_update_merchant_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmdb_update_merchant_grade(const char *merch, const char *grade)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_grade[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_grade, grade, sizeof(h_grade) - 1); h_grade[sizeof(h_grade)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mm_merchant_ext SET grade = :h_grade, updated_at = now()
|
||||
WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("mmdb_update_merchant_grade FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmdb_insert_merch_hist(const char *merch, const char *action,
|
||||
const char *before_val, const char *after_val,
|
||||
const char *memo)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
char h_merch[64], h_action[16], h_before[64], h_after[64], h_memo[128];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_action, action, sizeof(h_action) - 1); h_action[sizeof(h_action)-1]= 0;
|
||||
strncpy(h_before, before_val, sizeof(h_before) - 1); h_before[sizeof(h_before)-1]= 0;
|
||||
strncpy(h_after, after_val, sizeof(h_after) - 1); h_after[sizeof(h_after)-1] = 0;
|
||||
strncpy(h_memo, memo, sizeof(h_memo) - 1); h_memo[sizeof(h_memo)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT nextval('mm_merch_hist_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) return -1;
|
||||
|
||||
EXEC SQL INSERT INTO mm_merch_hist
|
||||
(hist_id, merchant_id, action, before_val, after_val, memo)
|
||||
VALUES (:h_id, :h_merch, :h_action, :h_before, :h_after, :h_memo);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("mmdb_insert_merch_hist FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
655
app/src/mm/mm_svr.pgc
Normal file
655
app/src/mm/mm_svr.pgc
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
/*
|
||||
* mm_svr.pgc - mm (마스터/master) MODULE SERVER.
|
||||
*
|
||||
* ONE binary that tpadvertise()s ALL ~30 online mm services (the real Tuxedo
|
||||
* "one module server, many services" pattern). Every service is a distinct
|
||||
* 마스터(기준정보) operation with genuine EXEC SQL (directly, or via the linked
|
||||
* libmmdbio.a dbio layer) and common math from libacqcommon.a.
|
||||
*
|
||||
* 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, so
|
||||
* a service sees its OWN uncommitted writes but NOT a sibling branch's.
|
||||
*/
|
||||
#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 "mm_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); }
|
||||
|
||||
#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 MM_MERCH_REG(TPSVCINFO *p); void MM_MERCH_UPD(TPSVCINFO *p);
|
||||
void MM_MERCH_INQ(TPSVCINFO *p); void MM_MERCH_DEL(TPSVCINFO *p);
|
||||
void MM_FEERATE_SET(TPSVCINFO *p); void MM_FEERATE_INQ(TPSVCINFO *p);
|
||||
void MM_FEERATE_HIST(TPSVCINFO *p); void MM_BIN_REG(TPSVCINFO *p);
|
||||
void MM_BIN_INQ(TPSVCINFO *p); void MM_LIMIT_SET(TPSVCINFO *p);
|
||||
void MM_LIMIT_INQ(TPSVCINFO *p); void MM_CODE_REG(TPSVCINFO *p);
|
||||
void MM_CODE_INQ(TPSVCINFO *p); void MM_ISSUER_REG(TPSVCINFO *p);
|
||||
void MM_ISSUER_INQ(TPSVCINFO *p); void MM_MERCH_STATUS(TPSVCINFO *p);
|
||||
void MM_MERCH_ACCT(TPSVCINFO *p); void MM_MERCH_GRADE(TPSVCINFO *p);
|
||||
void MM_TERMINAL_REG(TPSVCINFO *p); void MM_TERMINAL_INQ(TPSVCINFO *p);
|
||||
void MM_MCC_REG(TPSVCINFO *p); void MM_HOLIDAY_REG(TPSVCINFO *p);
|
||||
void MM_FX_REG(TPSVCINFO *p); void MM_FX_INQ(TPSVCINFO *p);
|
||||
void MM_MERCH_BULK(TPSVCINFO *p); void MM_VALIDATE(TPSVCINFO *p);
|
||||
void MM_MERCH_SUM(TPSVCINFO *p); void MM_CODE_CACHE(TPSVCINFO *p);
|
||||
void MM_STATUS(TPSVCINFO *p); void MM_CONFIRM(TPSVCINFO *p);
|
||||
|
||||
/* ----- advertise table (this is what "scales to 30" cleanly) ------------- */
|
||||
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
|
||||
{"MM_MERCH_REG", MM_MERCH_REG}, {"MM_MERCH_UPD", MM_MERCH_UPD},
|
||||
{"MM_MERCH_INQ", MM_MERCH_INQ}, {"MM_MERCH_DEL", MM_MERCH_DEL},
|
||||
{"MM_FEERATE_SET", MM_FEERATE_SET}, {"MM_FEERATE_INQ", MM_FEERATE_INQ},
|
||||
{"MM_FEERATE_HIST", MM_FEERATE_HIST}, {"MM_BIN_REG", MM_BIN_REG},
|
||||
{"MM_BIN_INQ", MM_BIN_INQ}, {"MM_LIMIT_SET", MM_LIMIT_SET},
|
||||
{"MM_LIMIT_INQ", MM_LIMIT_INQ}, {"MM_CODE_REG", MM_CODE_REG},
|
||||
{"MM_CODE_INQ", MM_CODE_INQ}, {"MM_ISSUER_REG", MM_ISSUER_REG},
|
||||
{"MM_ISSUER_INQ", MM_ISSUER_INQ}, {"MM_MERCH_STATUS", MM_MERCH_STATUS},
|
||||
{"MM_MERCH_ACCT", MM_MERCH_ACCT}, {"MM_MERCH_GRADE", MM_MERCH_GRADE},
|
||||
{"MM_TERMINAL_REG", MM_TERMINAL_REG}, {"MM_TERMINAL_INQ", MM_TERMINAL_INQ},
|
||||
{"MM_MCC_REG", MM_MCC_REG}, {"MM_HOLIDAY_REG", MM_HOLIDAY_REG},
|
||||
{"MM_FX_REG", MM_FX_REG}, {"MM_FX_INQ", MM_FX_INQ},
|
||||
{"MM_MERCH_BULK", MM_MERCH_BULK}, {"MM_VALIDATE", MM_VALIDATE},
|
||||
{"MM_MERCH_SUM", MM_MERCH_SUM}, {"MM_CODE_CACHE", MM_CODE_CACHE},
|
||||
{"MM_STATUS", MM_STATUS}, {"MM_CONFIRM", MM_CONFIRM},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int tpsvrinit(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
if (tpopen() < 0) {
|
||||
userlog("mm_svr: tpopen 실패: %s", tpstrerror(tperrno));
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; SVCS[i].name != NULL; i++) {
|
||||
if (tpadvertise((char *)SVCS[i].name, SVCS[i].fn) < 0) {
|
||||
userlog("mm_svr: tpadvertise(%s) 실패: %s", SVCS[i].name, tpstrerror(tperrno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
userlog("mm_svr: mm 마스터 서비스 %d개 광고 완료, RM open", i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tpsvrdone(void)
|
||||
{
|
||||
tpclose();
|
||||
userlog("mm_svr: tpsvrdone");
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* 1. MM_MERCH_REG - 가맹점 확장 마스터 등록 (dbio upsert + 이력). */
|
||||
/* ========================================================================= */
|
||||
void MM_MERCH_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], name[128], grade[8], acct[64];
|
||||
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
if (merch[0] == 0) { userlog("MM_MERCH_REG: T_MERCHANT 누락"); FAIL(b); }
|
||||
gets_(b, T_STR1, name, sizeof(name)); if (name[0] == 0) strcpy(name, "무명가맹점");
|
||||
gets_(b, T_STR2, grade, sizeof(grade)); if (grade[0] == 0) strcpy(grade, "C");
|
||||
gets_(b, T_STR3, acct, sizeof(acct));
|
||||
|
||||
if (mmdb_insert_merchant(merch, name, grade, acct, "ACTIVE") < 0) FAIL(b);
|
||||
if (mmdb_insert_merch_hist(merch, "REG", "", grade, "신규 등록") < 0) FAIL(b);
|
||||
|
||||
Bchg(b, T_STATUS, 0, "ACTIVE", 0L);
|
||||
userlog("MM_MERCH_REG merch=%s grade=%s 등록", merch, grade);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 2. MM_MERCH_UPD - 가맹점명/계좌 수정 (직접 UPDATE + 이력). */
|
||||
void MM_MERCH_UPD(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], name[128], acct[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_name[128], h_acct[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STR1, name, sizeof(name)); if (name[0] == 0) strcpy(name, "무명가맹점");
|
||||
gets_(b, T_STR3, acct, sizeof(acct));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
|
||||
strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE mm_merchant_ext
|
||||
SET name = :h_name, settle_acct = :h_acct, updated_at = now()
|
||||
WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("MM_MERCH_UPD FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b);
|
||||
}
|
||||
if (mmdb_insert_merch_hist(merch, "UPD", "", name, "정보 수정") < 0) FAIL(b);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 3. MM_MERCH_INQ - 가맹점 마스터 조회 (dbio). */
|
||||
void MM_MERCH_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], name[64], grade[8], status[16];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
if (mmdb_get_merchant(merch, name, grade, status) < 0) FAIL(b);
|
||||
Bchg(b, T_STR1, 0, name, 0L);
|
||||
Bchg(b, T_STR2, 0, grade, 0L);
|
||||
Bchg(b, T_STATUS, 0, status, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 4. MM_MERCH_DEL - 가맹점 논리 삭제 (상태 DELETED + 이력). */
|
||||
void MM_MERCH_DEL(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
if (mmdb_update_merchant_status(merch, "DELETED") < 0) FAIL(b);
|
||||
if (mmdb_insert_merch_hist(merch, "DEL", "ACTIVE", "DELETED", "논리 삭제") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "DELETED", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 5. MM_FEERATE_SET - 수수료율 설정 (dbio upsert + 이력). */
|
||||
void MM_FEERATE_SET(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], ctype[16];
|
||||
long bps;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STR4, ctype, sizeof(ctype)); if (ctype[0] == 0) strcpy(ctype, "CREDIT");
|
||||
bps = getl(b, T_ARG1); if (bps <= 0) bps = 25;
|
||||
if (mmdb_set_fee_rate(merch, ctype, bps) < 0) FAIL(b);
|
||||
userlog("MM_FEERATE_SET merch=%s type=%s bps=%ld", merch, ctype, bps);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 6. MM_FEERATE_INQ - 수수료율 조회 (dbio). */
|
||||
void MM_FEERATE_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], ctype[16];
|
||||
long bps = 25;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STR4, ctype, sizeof(ctype)); if (ctype[0] == 0) strcpy(ctype, "CREDIT");
|
||||
if (mmdb_get_fee_rate(merch, ctype, &bps) < 0) FAIL(b);
|
||||
setl(b, T_ARG1, bps);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 7. MM_FEERATE_HIST - 수수료율 변경 이력 건수 (dbio). */
|
||||
void MM_FEERATE_HIST(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64];
|
||||
long cnt;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
cnt = mmdb_feerate_hist_count(merch);
|
||||
if (cnt < 0) FAIL(b);
|
||||
setl(b, T_COUNT, cnt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 8. MM_BIN_REG - BIN 마스터 등록. */
|
||||
void MM_BIN_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bin[16], issuer[32], brand[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bin[16], h_issuer[32], h_brand[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_KEY1, bin, sizeof(bin));
|
||||
if (bin[0] == 0) { userlog("MM_BIN_REG: T_KEY1(bin) 누락"); FAIL(b); }
|
||||
gets_(b, T_ISSUER, issuer, sizeof(issuer)); if (issuer[0] == 0) strcpy(issuer, "UNKNOWN");
|
||||
gets_(b, T_STR5, brand, sizeof(brand)); if (brand[0] == 0) strcpy(brand, "LOCAL");
|
||||
strncpy(h_bin, bin, sizeof(h_bin)-1); h_bin[sizeof(h_bin)-1] = 0;
|
||||
strncpy(h_issuer, issuer, sizeof(h_issuer)-1); h_issuer[sizeof(h_issuer)-1] = 0;
|
||||
strncpy(h_brand, brand, sizeof(h_brand)-1); h_brand[sizeof(h_brand)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO mm_bin (bin, issuer_cd, card_brand)
|
||||
VALUES (:h_bin, :h_issuer, :h_brand)
|
||||
ON CONFLICT (bin) DO UPDATE
|
||||
SET issuer_cd = EXCLUDED.issuer_cd, card_brand = EXCLUDED.card_brand;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_BIN_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 9. MM_BIN_INQ - BIN 조회. */
|
||||
void MM_BIN_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bin[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bin[16], h_issuer[32], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_KEY1, bin, sizeof(bin));
|
||||
strncpy(h_bin, bin, sizeof(h_bin)-1); h_bin[sizeof(h_bin)-1] = 0;
|
||||
EXEC SQL SELECT issuer_cd, status INTO :h_issuer, :h_status
|
||||
FROM mm_bin WHERE bin = :h_bin;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
Bchg(b, T_ISSUER, 0, h_issuer, 0L);
|
||||
Bchg(b, T_STATUS, 0, h_status, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 10. MM_LIMIT_SET - 가맹점 한도 설정 (upsert). */
|
||||
void MM_LIMIT_SET(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_daily, h_monthly, h_pertxn;
|
||||
char h_merch[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
char merch[64];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
h_daily = getl(b, T_AMT1); if (h_daily <= 0) h_daily = 100000000;
|
||||
h_monthly = getl(b, T_AMT2); if (h_monthly <= 0) h_monthly = h_daily * 30;
|
||||
h_pertxn = getl(b, T_AMT3); if (h_pertxn <= 0) h_pertxn = 10000000;
|
||||
|
||||
EXEC SQL INSERT INTO mm_limit
|
||||
(merchant_id, daily_limit, monthly_limit, per_txn_limit)
|
||||
VALUES (:h_merch, :h_daily, :h_monthly, :h_pertxn)
|
||||
ON CONFLICT (merchant_id) DO UPDATE
|
||||
SET daily_limit = EXCLUDED.daily_limit, monthly_limit = EXCLUDED.monthly_limit,
|
||||
per_txn_limit = EXCLUDED.per_txn_limit, updated_at = now();
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_LIMIT_SET FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 11. MM_LIMIT_INQ - 가맹점 한도 조회. */
|
||||
void MM_LIMIT_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_daily, h_monthly, h_pertxn;
|
||||
char h_merch[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
char merch[64];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
EXEC SQL SELECT daily_limit, monthly_limit, per_txn_limit
|
||||
INTO :h_daily, :h_monthly, :h_pertxn FROM mm_limit WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
setl(b, T_AMT1, h_daily); setl(b, T_AMT2, h_monthly); setl(b, T_AMT3, h_pertxn);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 12. MM_CODE_REG - 공통코드 등록. */
|
||||
void MM_CODE_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char grp[32], code[32], name[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_grp[32], h_code[32], h_name[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STR1, grp, sizeof(grp)); if (grp[0] == 0) strcpy(grp, "GEN");
|
||||
gets_(b, T_KEY1, code, sizeof(code));
|
||||
if (code[0] == 0) { userlog("MM_CODE_REG: T_KEY1(code) 누락"); FAIL(b); }
|
||||
gets_(b, T_STR2, name, sizeof(name));
|
||||
strncpy(h_grp, grp, sizeof(h_grp)-1); h_grp[sizeof(h_grp)-1] = 0;
|
||||
strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0;
|
||||
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
|
||||
EXEC SQL INSERT INTO mm_code (code_group, code, code_name)
|
||||
VALUES (:h_grp, :h_code, :h_name)
|
||||
ON CONFLICT (code_group, code) DO UPDATE SET code_name = EXCLUDED.code_name;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_CODE_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 13. MM_CODE_INQ - 공통코드 조회. */
|
||||
void MM_CODE_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char grp[32], code[32];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_grp[32], h_code[32], h_name[64], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STR1, grp, sizeof(grp)); if (grp[0] == 0) strcpy(grp, "GEN");
|
||||
gets_(b, T_KEY1, code, sizeof(code));
|
||||
strncpy(h_grp, grp, sizeof(h_grp)-1); h_grp[sizeof(h_grp)-1] = 0;
|
||||
strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0;
|
||||
EXEC SQL SELECT code_name, status INTO :h_name, :h_status
|
||||
FROM mm_code WHERE code_group = :h_grp AND code = :h_code;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
Bchg(b, T_STR2, 0, h_name, 0L);
|
||||
Bchg(b, T_STATUS, 0, h_status, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 14. MM_ISSUER_REG - 발급사 마스터 등록. */
|
||||
void MM_ISSUER_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char cd[32], name[64], country[8];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_cd[32], h_name[64], h_country[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_ISSUER, cd, sizeof(cd));
|
||||
if (cd[0] == 0) { userlog("MM_ISSUER_REG: T_ISSUER 누락"); FAIL(b); }
|
||||
gets_(b, T_STR1, name, sizeof(name));
|
||||
gets_(b, T_STR2, country, sizeof(country)); if (country[0] == 0) strcpy(country, "KR");
|
||||
strncpy(h_cd, cd, sizeof(h_cd)-1); h_cd[sizeof(h_cd)-1] = 0;
|
||||
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
|
||||
strncpy(h_country, country, sizeof(h_country)-1); h_country[sizeof(h_country)-1] = 0;
|
||||
EXEC SQL INSERT INTO mm_issuer (issuer_cd, issuer_name, country)
|
||||
VALUES (:h_cd, :h_name, :h_country)
|
||||
ON CONFLICT (issuer_cd) DO UPDATE
|
||||
SET issuer_name = EXCLUDED.issuer_name, country = EXCLUDED.country;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_ISSUER_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 15. MM_ISSUER_INQ - 발급사 조회. */
|
||||
void MM_ISSUER_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char cd[32];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_cd[32], h_name[64], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_ISSUER, cd, sizeof(cd));
|
||||
strncpy(h_cd, cd, sizeof(h_cd)-1); h_cd[sizeof(h_cd)-1] = 0;
|
||||
EXEC SQL SELECT issuer_name, status INTO :h_name, :h_status
|
||||
FROM mm_issuer WHERE issuer_cd = :h_cd;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
Bchg(b, T_STR1, 0, h_name, 0L);
|
||||
Bchg(b, T_STATUS, 0, h_status, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 16. MM_MERCH_STATUS - 가맹점 상태 변경 (dbio + 이력). */
|
||||
void MM_MERCH_STATUS(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], status[16];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STATUS, status, sizeof(status)); if (status[0] == 0) strcpy(status, "HOLD");
|
||||
if (mmdb_update_merchant_status(merch, status) < 0) FAIL(b);
|
||||
if (mmdb_insert_merch_hist(merch, "STATUS", "", status, "상태 변경") < 0) FAIL(b);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 17. MM_MERCH_ACCT - 정산계좌 변경 (직접 UPDATE + 이력). */
|
||||
void MM_MERCH_ACCT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], acct[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_acct[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STR3, acct, sizeof(acct));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0;
|
||||
EXEC SQL UPDATE mm_merchant_ext SET settle_acct = :h_acct, updated_at = now()
|
||||
WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (mmdb_insert_merch_hist(merch, "ACCT", "", acct, "계좌 변경") < 0) FAIL(b);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 18. MM_MERCH_GRADE - 가맹점 등급 변경 (dbio + 이력). */
|
||||
void MM_MERCH_GRADE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], grade[8];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STR2, grade, sizeof(grade)); if (grade[0] == 0) strcpy(grade, "C");
|
||||
if (mmdb_update_merchant_grade(merch, grade) < 0) FAIL(b);
|
||||
if (mmdb_insert_merch_hist(merch, "GRADE", "", grade, "등급 변경") < 0) FAIL(b);
|
||||
Bchg(b, T_STR2, 0, grade, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 19. MM_TERMINAL_REG - 단말 마스터 등록. */
|
||||
void MM_TERMINAL_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char term[32], merch[64], model[32];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_term[32], h_merch[64], h_model[32];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STR5, term, sizeof(term));
|
||||
if (term[0] == 0) { userlog("MM_TERMINAL_REG: T_STR5(terminal) 누락"); FAIL(b); }
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_STR6, model, sizeof(model)); if (model[0] == 0) strcpy(model, "STD");
|
||||
strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0;
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_model, model, sizeof(h_model)-1); h_model[sizeof(h_model)-1] = 0;
|
||||
EXEC SQL INSERT INTO mm_terminal (terminal_id, merchant_id, model)
|
||||
VALUES (:h_term, :h_merch, :h_model)
|
||||
ON CONFLICT (terminal_id) DO UPDATE
|
||||
SET merchant_id = EXCLUDED.merchant_id, model = EXCLUDED.model;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_TERMINAL_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 20. MM_TERMINAL_INQ - 단말 조회. */
|
||||
void MM_TERMINAL_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char term[32];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_term[32], h_merch[64], h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STR5, term, sizeof(term));
|
||||
strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0;
|
||||
EXEC SQL SELECT merchant_id, status INTO :h_merch, :h_status
|
||||
FROM mm_terminal WHERE terminal_id = :h_term;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
Bchg(b, T_MERCHANT, 0, h_merch, 0L);
|
||||
Bchg(b, T_STATUS, 0, h_status, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 21. MM_MCC_REG - MCC 업종 마스터 등록. */
|
||||
void MM_MCC_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char mcc[16], name[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_risk;
|
||||
char h_mcc[16], h_name[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STR6, mcc, sizeof(mcc));
|
||||
if (mcc[0] == 0) { userlog("MM_MCC_REG: T_STR6(mcc) 누락"); FAIL(b); }
|
||||
gets_(b, T_STR1, name, sizeof(name));
|
||||
h_risk = getl(b, T_ARG1); if (h_risk <= 0) h_risk = 1;
|
||||
strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0;
|
||||
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
|
||||
EXEC SQL INSERT INTO mm_mcc (mcc, mcc_name, risk_level)
|
||||
VALUES (:h_mcc, :h_name, :h_risk)
|
||||
ON CONFLICT (mcc) DO UPDATE
|
||||
SET mcc_name = EXCLUDED.mcc_name, risk_level = EXCLUDED.risk_level;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_MCC_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 22. MM_HOLIDAY_REG - 공휴일 마스터 등록. */
|
||||
void MM_HOLIDAY_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char hdate[16], name[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_date[16], h_name[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_BIZDATE, hdate, sizeof(hdate));
|
||||
if (hdate[0] == 0) { userlog("MM_HOLIDAY_REG: T_BIZDATE 누락"); FAIL(b); }
|
||||
gets_(b, T_STR1, name, sizeof(name)); if (name[0] == 0) strcpy(name, "공휴일");
|
||||
strncpy(h_date, hdate, sizeof(h_date)-1); h_date[sizeof(h_date)-1] = 0;
|
||||
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
|
||||
EXEC SQL INSERT INTO mm_holiday (holiday_date, holiday_name)
|
||||
VALUES (:h_date, :h_name)
|
||||
ON CONFLICT (holiday_date) DO UPDATE SET holiday_name = EXCLUDED.holiday_name;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_HOLIDAY_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 23. MM_FX_REG - 환율 마스터 upsert. */
|
||||
void MM_FX_REG(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char ccy[8];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_rate;
|
||||
char h_ccy[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_CCY, ccy, sizeof(ccy)); if (ccy[0] == 0) strcpy(ccy, "USD");
|
||||
h_rate = getl(b, T_ARG2); if (h_rate <= 0) h_rate = 1350;
|
||||
strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0;
|
||||
EXEC SQL INSERT INTO mm_fx_rate (ccy, rate_bps, base_date)
|
||||
VALUES (:h_ccy, :h_rate, current_date)
|
||||
ON CONFLICT (ccy) DO UPDATE
|
||||
SET rate_bps = EXCLUDED.rate_bps, base_date = current_date, updated_at = now();
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_FX_REG FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 24. MM_FX_INQ - 환율 조회 + 금액 환산 (rate_bps는 100 기준 bps). */
|
||||
void MM_FX_INQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char ccy[8];
|
||||
long fx_amt, krw;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_rate;
|
||||
char h_ccy[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_CCY, ccy, sizeof(ccy)); if (ccy[0] == 0) strcpy(ccy, "USD");
|
||||
fx_amt = getl(b, T_FX_AMT);
|
||||
strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0;
|
||||
EXEC SQL SELECT rate_bps INTO :h_rate FROM mm_fx_rate WHERE ccy = :h_ccy;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
krw = fx_amt * h_rate / 100;
|
||||
setl(b, T_ARG2, h_rate);
|
||||
setl(b, T_AMOUNT, krw);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 25. MM_MERCH_BULK - 가맹점 대량 등록 (T_COUNT개 파생 생성). */
|
||||
void MM_MERCH_BULK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char prefix[48], grade[8];
|
||||
long n, i, made = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_name[128], h_grade[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, prefix, sizeof(prefix)); if (prefix[0] == 0) strcpy(prefix, "MBULK");
|
||||
gets_(b, T_STR2, grade, sizeof(grade)); if (grade[0] == 0) strcpy(grade, "C");
|
||||
n = getl(b, T_COUNT); if (n < 1) n = 1; if (n > 1000) n = 1000;
|
||||
strncpy(h_grade, grade, sizeof(h_grade)-1); h_grade[sizeof(h_grade)-1] = 0;
|
||||
for (i = 1; i <= n; i++) {
|
||||
snprintf(h_merch, sizeof(h_merch), "%s-%04ld", prefix, i);
|
||||
snprintf(h_name, sizeof(h_name), "%s 지점%ld", prefix, i);
|
||||
EXEC SQL INSERT INTO mm_merchant_ext (merchant_id, name, grade)
|
||||
VALUES (:h_merch, :h_name, :h_grade)
|
||||
ON CONFLICT (merchant_id) DO NOTHING;
|
||||
if (sqlca.sqlcode < 0) { userlog("MM_MERCH_BULK FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
|
||||
made++;
|
||||
}
|
||||
setl(b, T_COUNT, made);
|
||||
userlog("MM_MERCH_BULK prefix=%s 생성=%ld", prefix, made);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 26. MM_VALIDATE - 가맹점 마스터 정합성 검사 (존재+수수료율 존재). */
|
||||
void MM_VALIDATE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64];
|
||||
long rc = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_mcnt, h_fcnt;
|
||||
char h_merch[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
EXEC SQL SELECT count(*) INTO :h_mcnt FROM mm_merchant_ext
|
||||
WHERE merchant_id = :h_merch AND status = 'ACTIVE';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL SELECT count(*) INTO :h_fcnt FROM mm_fee_rate WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (h_mcnt == 0) rc = 1; /* 마스터 없음/비활성 */
|
||||
else if (h_fcnt == 0) rc = 2; /* 수수료율 미설정 */
|
||||
setl(b, T_RC, rc);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 27. MM_MERCH_SUM - 상태별 가맹점 수 집계 (ACTIVE 기준). */
|
||||
void MM_MERCH_SUM(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char status[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt;
|
||||
char h_status[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STATUS, status, sizeof(status)); if (status[0] == 0) strcpy(status, "ACTIVE");
|
||||
strncpy(h_status, status, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
|
||||
EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merchant_ext WHERE status = :h_status;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 28. MM_CODE_CACHE - 코드그룹의 활성 코드 수 (캐시 워밍 시뮬). */
|
||||
void MM_CODE_CACHE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char grp[32];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt;
|
||||
char h_grp[32];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_STR1, grp, sizeof(grp)); if (grp[0] == 0) strcpy(grp, "CARD_TYPE");
|
||||
strncpy(h_grp, grp, sizeof(h_grp)-1); h_grp[sizeof(h_grp)-1] = 0;
|
||||
EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_code
|
||||
WHERE code_group = :h_grp AND status = 'ACTIVE';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 29. MM_STATUS - 서비스 상태 (전체 가맹점 수, RC=0). */
|
||||
void MM_STATUS(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merchant_ext;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_MSG, 0, "mm 마스터 서비스 정상", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 30. MM_CONFIRM - 가맹점 승인 확정 (상태 ACTIVE + 이력). */
|
||||
void MM_CONFIRM(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64];
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
if (mmdb_update_merchant_status(merch, "ACTIVE") < 0) FAIL(b);
|
||||
if (mmdb_insert_merch_hist(merch, "STATUS", "HOLD", "ACTIVE", "승인 확정") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "ACTIVE", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 et smartindent: */
|
||||
Loading…
Add table
Add a link
Reference in a new issue