18 KiB
18 KiB
🏗️ 약국 통합 솔루션 아키텍처
📋 개요
본 시스템은 동물약 도매상(애니팜), 개별 약국 POS, 마일리지 솔루션을 통합하는 멀티 데이터베이스 아키텍처입니다.
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🏢 애니팜 (동물약 도매상) │
│ PostgreSQL Database │
│ 제품 마스터, 재고, 주문, 거래처 │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ 제품 정보 / 발주
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 💊 개별 약국 (청춘약국 등) │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ MSSQL (팜IT3000) │ │ SQLite (솔루션) │ │
│ │ - 제품 마스터 │ │ - 마일리지 │ │
│ │ - 판매 내역 │◄──►│ - AI 추천 │ │
│ │ - 조제 이력 │ │ - 알림톡 로그 │ │
│ │ - 회원 정보 │ │ - 동물약 태그 │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ API / 웹 인터페이스
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🌐 Flask 웹 서버 (7001) │
│ QR 적립 | AI 챗봇 | 관리자 | 회원 조회 | 알림톡 │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ 외부 서비스
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🔌 외부 API 연동 │
│ - OpenAI GPT (동물약 챗봇, AI 업셀링) │
│ - 카카오 OAuth (로그인) │
│ - NHN Cloud 알림톡 │
│ - Clawdbot Gateway (AI 에이전트) │
└─────────────────────────────────────────────────────────────────────────────┘
🗄️ 데이터베이스 구조
1️⃣ PostgreSQL (애니팜 - 동물약 도매상)
역할: 동물약 도매 사업의 핵심 DB. 제품 마스터, 거래처(약국), 주문/발주 관리
| 테이블 | 설명 | 주요 컬럼 |
|---|---|---|
products |
제품 마스터 | id, name, barcode, price, category |
customers |
거래처 (약국) | id, pharmacy_name, owner, phone |
orders |
주문 내역 | id, customer_id, order_date, status |
order_items |
주문 상세 | order_id, product_id, qty, price |
inventory |
재고 현황 | product_id, stock_qty, location |
-- 예시: 인기 동물약 TOP 10 조회
SELECT p.name, SUM(oi.qty) as total_sold
FROM order_items oi
JOIN products p ON oi.product_id = p.id
WHERE oi.created_at >= NOW() - INTERVAL '30 days'
GROUP BY p.name
ORDER BY total_sold DESC
LIMIT 10;
2️⃣ MSSQL (팜IT3000 - 약국 POS)
역할: 약국 청구/POS 프로그램의 DB. 제품, 판매, 조제, 회원 정보
주요 데이터베이스
| DB명 | 설명 |
|---|---|
PM_DRUG |
제품 마스터 (의약품/건기식) |
PM_PRES |
판매/조제 내역 |
PM_BASE |
회원/거래처 기본 정보 |
핵심 테이블
PM_DRUG.dbo.CD_GOODS - 제품 마스터
| 컬럼 | 설명 |
|---|---|
DrugCode |
제품 코드 (PK) |
GoodsName |
제품명 |
BARCODE |
바코드 |
Saleprice |
판매가 |
Price |
원가 |
POS_BOON |
분류코드 (010103 = 동물약) |
GoodsSelCode |
판매상태 (B = 판매중) |
PM_PRES.dbo.SALE_MAIN - 판매 헤더
| 컬럼 | 설명 |
|---|---|
SL_NO_order |
거래번호 (PK) |
InsertTime |
거래 일시 |
SL_MY_total |
총 금액 |
SL_CD_custom |
고객 코드 |
PM_PRES.dbo.SALE_SUB - 판매 상세
| 컬럼 | 설명 |
|---|---|
SL_NO_order |
거래번호 (FK) |
DrugCode |
제품 코드 |
SL_NM_item |
수량 |
SL_TOTAL_PRICE |
금액 |
PM_BASE.dbo.CD_PERSON - 회원 정보
| 컬럼 | 설명 |
|---|---|
CUSCODE |
고객 코드 (PK) |
PANAME |
이름 |
PHONE |
전화번호 |
PANUM |
주민번호 |
-- 예시: 오늘 판매 내역 + 제품명 조회
SELECT
M.SL_NO_order AS 거래번호,
M.InsertTime AS 거래일시,
G.GoodsName AS 제품명,
S.SL_NM_item AS 수량,
S.SL_TOTAL_PRICE AS 금액
FROM PM_PRES.dbo.SALE_MAIN M
JOIN PM_PRES.dbo.SALE_SUB S ON M.SL_NO_order = S.SL_NO_order
JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
WHERE CONVERT(DATE, M.InsertTime) = CONVERT(DATE, GETDATE())
ORDER BY M.InsertTime DESC;
-- 예시: 동물약 목록 조회 (POS_BOON = '010103')
SELECT DrugCode, GoodsName, Saleprice, BARCODE
FROM PM_DRUG.dbo.CD_GOODS
WHERE POS_BOON = '010103' AND GoodsSelCode = 'B'
ORDER BY GoodsName;
3️⃣ SQLite (마일리지 솔루션)
역할: 약국별 마일리지 적립, AI 추천, 알림톡 로그 등 부가 기능
경로: backend/db/mileage.db
핵심 테이블
users - 마일리지 회원
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
INTEGER | PK |
nickname |
TEXT | 이름 |
phone |
TEXT | 전화번호 (UNIQUE) |
mileage_balance |
INTEGER | 포인트 잔액 |
birthday |
TEXT | 생년월일 |
created_at |
TIMESTAMP | 가입일 |
claim_tokens - QR 적립 토큰
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
INTEGER | PK |
transaction_id |
TEXT | POS 거래번호 (UNIQUE) |
token_hash |
TEXT | 토큰 해시 |
total_amount |
REAL | 구매 금액 |
claimable_points |
INTEGER | 적립 가능 포인트 |
claimed_at |
TIMESTAMP | 적립 완료 시각 |
claimed_by_user_id |
INTEGER | 적립한 회원 ID |
mileage_ledger - 포인트 원장
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
INTEGER | PK |
user_id |
INTEGER | 회원 ID |
transaction_id |
TEXT | 거래번호 |
points |
INTEGER | 적립/차감 포인트 |
balance_after |
INTEGER | 변동 후 잔액 |
reason |
TEXT | CLAIM / USE / ADMIN |
ai_recommendations - AI 업셀링 추천
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
INTEGER | PK |
user_id |
INTEGER | 회원 ID |
recommended_product |
TEXT | 추천 제품 |
recommendation_message |
TEXT | 추천 메시지 |
status |
TEXT | active / interested / dismissed |
drug_tags - 동물약 태그 (별도 DB: drug_tags.db)
| 컬럼 | 타입 | 설명 |
|---|---|---|
drug_code |
TEXT | 제품 코드 |
drug_name |
TEXT | 제품명 |
tag_type |
TEXT | animal_drug 등 |
tag_value |
TEXT | all / dog / cat |
-- 예시: 회원별 적립 내역 조회
SELECT
u.nickname, u.phone, u.mileage_balance,
ml.points, ml.reason, ml.created_at
FROM users u
JOIN mileage_ledger ml ON u.id = ml.user_id
WHERE u.phone = '01012345678'
ORDER BY ml.created_at DESC;
🔄 데이터 흐름 예시
📱 시나리오 1: QR 마일리지 적립
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ POS 결제 │────►│ QR 발행 │────►│ 고객 스캔 │────►│ 적립 완료 │
│ (MSSQL) │ │ (SQLite) │ │ (Flask) │ │ (SQLite) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
SALE_MAIN claim_tokens users 조회 mileage_ledger
SALE_SUB 생성 & 저장 /생성 적립 기록
쿼리 흐름:
-- 1. POS 판매 완료 시 (MSSQL)
INSERT INTO SALE_MAIN (SL_NO_order, SL_MY_total, ...) VALUES (...)
-- 2. QR 토큰 생성 (SQLite)
INSERT INTO claim_tokens (transaction_id, total_amount, claimable_points, ...)
VALUES ('20260228001234', 50000, 1500, ...)
-- 3. 고객 QR 스캔 → 회원 조회/생성 (SQLite)
SELECT * FROM users WHERE phone = '01012345678'
-- 없으면:
INSERT INTO users (nickname, phone, mileage_balance) VALUES ('홍길동', '01012345678', 0)
-- 4. 적립 처리 (SQLite)
UPDATE users SET mileage_balance = mileage_balance + 1500 WHERE id = 1
INSERT INTO mileage_ledger (user_id, transaction_id, points, balance_after, reason)
VALUES (1, '20260228001234', 1500, 1500, 'CLAIM')
-- 5. 토큰 사용 완료 표시 (SQLite)
UPDATE claim_tokens SET claimed_at = datetime('now'), claimed_by_user_id = 1
WHERE transaction_id = '20260228001234'
🐾 시나리오 2: 동물약 AI 챗봇
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 사용자 질문 │────►│ 동물약 조회 │────►│ OpenAI API │────►│ 응답 생성 │
│ "구충제 추천" │ │ (MSSQL) │ │ (RAG) │ │ + 제품 매칭 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
│ │
▼ ▼
CD_GOODS에서 지식 베이스 +
동물약 38개 제품 목록 전달
가격 포함 조회
쿼리 흐름:
-- 1. 동물약 목록 조회 (MSSQL → RAG 컨텍스트)
SELECT DrugCode, GoodsName, Saleprice, BARCODE
FROM PM_DRUG.dbo.CD_GOODS
WHERE POS_BOON = '010103' AND GoodsSelCode = 'B'
ORDER BY GoodsName;
-- 결과: 안텔민(5000원), 넥스가드L(84000원), ... 38개
-- 2. OpenAI API 호출 (Python)
# System Prompt에 포함:
# - 동물약 지식 (심장사상충, 구충제, 외부기생충 등)
# - 현재 보유 제품 목록 + 가격
# User: "구충제 추천해줘"
# AI 응답: "구충제로는 **안텔민**을 추천드려요! 프라지콴텔+피란텔 성분으로..."
-- 3. 응답에서 제품명 매칭 (Python)
# AI 응답에 "안텔민" 포함 → 가격 5000원 표시
👤 시나리오 3: 회원 상세 조회 (통합)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 전화번호 │────►│ DB 3곳 │────►│ 통합 응답 │
│ 입력 │ │ 동시 조회 │ │ 반환 │
└─────────────┘ └─────────────┘ └─────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ SQLite │ │ MSSQL │ │ MSSQL │
│ users │ │PM_BASE │ │PM_PRES │
│마일리지 │ │회원정보 │ │조제이력 │
└─────────┘ └─────────┘ └─────────┘
쿼리 흐름:
-- 1. 마일리지 회원 조회 (SQLite)
SELECT id, nickname, phone, mileage_balance, created_at
FROM users WHERE phone = '01012345678'
-- 2. 적립 이력 조회 (SQLite)
SELECT points, balance_after, reason, created_at, transaction_id
FROM mileage_ledger WHERE user_id = 1
ORDER BY created_at DESC LIMIT 50
-- 3. POS 고객 코드 조회 (MSSQL PM_BASE)
SELECT CUSCODE, PANAME FROM CD_PERSON
WHERE REPLACE(PHONE, '-', '') = '01012345678'
-- 4. 조제 이력 조회 (MSSQL PM_PRES)
SELECT P.PreSerial, P.Indate, P.Drname, P.OrderName
FROM PS_main P
WHERE P.CusCode = 'C00001234'
ORDER BY P.Indate DESC
-- 5. 구매 상세 조회 (MSSQL PM_PRES + PM_DRUG)
SELECT G.GoodsName, S.SL_NM_item, S.SL_TOTAL_PRICE
FROM SALE_SUB S
JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
WHERE S.SL_NO_order = '20260228001234'
🛠️ 기술 스택
| 계층 | 기술 | 용도 |
|---|---|---|
| Frontend | HTML/CSS/JS | 관리자 페이지, 키오스크, 마이페이지 |
| Backend | Flask (Python) | REST API, 템플릿 렌더링 |
| Database | PostgreSQL | 애니팜 (도매상) |
| MSSQL | 팜IT3000 (약국 POS) | |
| SQLite | 마일리지 솔루션 | |
| AI | OpenAI GPT-4o-mini | 동물약 챗봇, 업셀링 추천 |
| 인증 | 카카오 OAuth | 소셜 로그인 |
| 알림 | NHN Cloud | 알림톡/SMS |
| 프로세스 | PM2 | 서버 관리 |
| 도메인 | Cloudflare | SSL, 프록시 |
📁 프로젝트 구조
pharmacy-pos-qr-system/
├── backend/
│ ├── app.py # Flask 메인 앱
│ ├── db/
│ │ ├── dbsetup.py # DB 연결 관리
│ │ ├── mileage.db # SQLite (마일리지)
│ │ └── drug_tags.db # SQLite (동물약 태그)
│ ├── templates/ # HTML 템플릿
│ │ ├── admin.html
│ │ ├── admin_products.html # 제품 검색 + AI 챗봇
│ │ ├── admin_members.html
│ │ ├── kiosk.html
│ │ └── my_page.html
│ ├── services/
│ │ ├── kakao_client.py # 카카오 OAuth
│ │ ├── nhn_alimtalk.py # 알림톡
│ │ └── clawdbot_client.py # AI 에이전트
│ ├── utils/
│ │ └── qr_token_generator.py
│ └── .env # 환경 변수
├── docs/
│ └── ARCHITECTURE.md # 이 문서
├── logs/
└── ecosystem.config.js # PM2 설정
🔐 환경 변수 (.env)
# 카카오 OAuth
KAKAO_CLIENT_ID=xxx
KAKAO_CLIENT_SECRET=xxx
KAKAO_REDIRECT_URI=https://mile.0bin.in/claim/kakao/callback
# OpenAI API
OPENAI_API_KEY=sk-xxx
OPENAI_MODEL=gpt-4o-mini
# MSSQL 연결 (dbsetup.py에서 설정)
# SQLite 경로 (backend/db/)
📊 주요 API 엔드포인트
| 경로 | 메서드 | 설명 | DB |
|---|---|---|---|
/api/products |
GET | 제품 검색 | MSSQL |
/api/animal-chat |
POST | 동물약 AI 챗봇 | MSSQL + OpenAI |
/api/animal-drugs |
GET | 동물약 목록 | MSSQL |
/api/claim |
POST | 마일리지 적립 | SQLite |
/api/members/search |
GET | 회원 검색 | MSSQL |
/api/members/history/:phone |
GET | 회원 이력 통합 | 전체 |
/admin/user/:id |
GET | 회원 상세 (적립+구매+조제) | 전체 |
📝 버전 이력
| 날짜 | 버전 | 변경 내용 |
|---|---|---|
| 2026-02-28 | 1.0 | 초기 아키텍처 문서 작성 |
| 동물약 AI 챗봇 추가 | ||
| 플로팅 챗봇 UI 구현 |
작성: Clawdbot AI | 청춘약국 통합 솔루션