실운영 데이터 + 운영 대시보드/조회 화면 (실사용 시스템화)
- db/schema.d/90-seed.sql: 가맹점 40(실제풍 상호·업종별 MDR 18~30bp·한도), 2026-04-20~07-19 3개월 매입 8,190건(주중/주말 거래량·금액구간 분포·채널/발급사 비중), 승인 1:1, 정산 7,560, 원장 15,120(RECON+SETTLE 이중기표), 가맹점 일집계, 시퀀스 정렬 - 게이트웨이 /api/query: libpq 읽기전용 조회(SELECT만, 단일문, 500행 캡) — MIS/리포팅 경로 - UI 조회/현황 그룹 7화면: 운영 대시보드(카드 7종+최근거래+가맹점TOP7+7영업일 실적), 매입/정산 목록, 가맹점 현황, 일자별 실적, 원장, 채널·발급사 분석 — 기본화면=대시보드 - 검증: 시드 로딩 psql 확인, /api/query 동작, 헤드리스 스크린샷으로 대시보드 실데이터 렌더 확인 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c5692410f6
commit
1fd60a1e49
4 changed files with 305 additions and 2 deletions
|
|
@ -25,6 +25,8 @@
|
|||
#include <ubf.h>
|
||||
#include <userlog.h>
|
||||
#include <acq.fd.h>
|
||||
#include <strings.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#define GW_PORT 8090
|
||||
#define REQ_MAX 16384
|
||||
|
|
@ -186,6 +188,63 @@ static void handle_api_call(int fd, char *query)
|
|||
tpfree((char *)b);
|
||||
}
|
||||
|
||||
|
||||
/* ---- /api/query (읽기전용 조회 — 화면 그리드용, MIS/리포팅 경로) ------------- */
|
||||
static PGconn *g_pg = NULL;
|
||||
|
||||
static void handle_api_query(int fd, char *query)
|
||||
{
|
||||
static char out[524288];
|
||||
static char sql[8192];
|
||||
char esc[4096];
|
||||
char *tok, *save = NULL;
|
||||
sql[0] = '\0';
|
||||
|
||||
for (tok = strtok_r(query, "&", &save); tok; tok = strtok_r(NULL, "&", &save)) {
|
||||
char *eq = strchr(tok, '=');
|
||||
if (!eq) continue;
|
||||
*eq = '\0';
|
||||
if (0 == strcmp(tok, "q")) {
|
||||
strncpy(sql, eq + 1, sizeof(sql) - 1); sql[sizeof(sql)-1] = '\0';
|
||||
url_decode(sql);
|
||||
}
|
||||
}
|
||||
char *p = sql;
|
||||
while (*p == ' ' || *p == '\n' || *p == '\t') p++;
|
||||
if (g_pg && PQstatus(g_pg) != CONNECTION_OK) PQreset(g_pg);
|
||||
if (!g_pg || PQstatus(g_pg) != CONNECTION_OK ||
|
||||
strncasecmp(p, "select", 6) != 0 || strchr(p, ';')) {
|
||||
const char *e = "{\"ok\":false,\"error\":\"select only / no db\"}";
|
||||
http_send(fd, 400, "application/json", e, (long)strlen(e)); return;
|
||||
}
|
||||
PGresult *res = PQexec(g_pg, p);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||
json_escape(PQerrorMessage(g_pg), esc, sizeof(esc));
|
||||
long n = snprintf(out, sizeof(out), "{\"ok\":false,\"error\":\"%s\"}", esc);
|
||||
http_send(fd, 200, "application/json", out, n); PQclear(res); return;
|
||||
}
|
||||
int nc = PQnfields(res), nr = PQntuples(res);
|
||||
if (nr > 500) nr = 500;
|
||||
long o = snprintf(out, sizeof(out), "{\"ok\":true,\"cols\":[");
|
||||
int c, r;
|
||||
for (c = 0; c < nc; c++) {
|
||||
json_escape(PQfname(res, c), esc, sizeof(esc));
|
||||
o += snprintf(out + o, sizeof(out) - o, "%s\"%s\"", c ? "," : "", esc);
|
||||
}
|
||||
o += snprintf(out + o, sizeof(out) - o, "],\"rows\":[");
|
||||
for (r = 0; r < nr && (size_t)o < sizeof(out) - 8192; r++) {
|
||||
o += snprintf(out + o, sizeof(out) - o, "%s[", r ? "," : "");
|
||||
for (c = 0; c < nc; c++) {
|
||||
json_escape(PQgetvalue(res, r, c), esc, sizeof(esc));
|
||||
o += snprintf(out + o, sizeof(out) - o, "%s\"%s\"", c ? "," : "", esc);
|
||||
}
|
||||
o += snprintf(out + o, sizeof(out) - o, "]");
|
||||
}
|
||||
o += snprintf(out + o, sizeof(out) - o, "]}");
|
||||
http_send(fd, 200, "application/json", out, o);
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
/* ---- 정적 파일 ------------------------------------------------------------- */
|
||||
static void handle_static(int fd, const char *path)
|
||||
{
|
||||
|
|
@ -217,6 +276,10 @@ int main(void)
|
|||
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; }
|
||||
|
||||
g_pg = PQconnectdb("host=db dbname=acq user=acq password=acq");
|
||||
if (PQstatus(g_pg) != CONNECTION_OK)
|
||||
fprintf(stderr, "경고: 조회DB 연결 실패(%s) — /api/query 비활성\n", PQerrorMessage(g_pg));
|
||||
|
||||
int srv = socket(AF_INET, SOCK_STREAM, 0);
|
||||
int on = 1;
|
||||
setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
|
@ -246,6 +309,8 @@ int main(void)
|
|||
|
||||
if (0 == strcmp(path, "/api/call") && query)
|
||||
handle_api_call(fd, query);
|
||||
else if (0 == strcmp(path, "/api/query") && query)
|
||||
handle_api_query(fd, query);
|
||||
else
|
||||
handle_static(fd, path);
|
||||
close(fd);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue