pharmacy-pos-qr-system/docs/ARCHITECTURE.md

434 lines
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🏗️ 약국 통합 솔루션 아키텍처
## 📋 개요
본 시스템은 **동물약 도매상(애니팜)**, **개별 약국 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 |
```sql
-- 예시: 인기 동물약 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` | 주민번호 |
```sql
-- 예시: 오늘 판매 내역 + 제품명 조회
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;
```
```sql
-- 예시: 동물약 목록 조회 (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 |
```sql
-- 예시: 회원별 적립 내역 조회
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 생성 & 저장 /생성 적립 기록
```
**쿼리 흐름:**
```sql
-- 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개 제품 목록 전달
가격 포함 조회
```
**쿼리 흐름:**
```sql
-- 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 │
│마일리지 │ │회원정보 │ │조제이력 │
└─────────┘ └─────────┘ └─────────┘
```
**쿼리 흐름:**
```sql
-- 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)
```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 | 청춘약국 통합 솔루션*