605 lines
25 KiB
Markdown
605 lines
25 KiB
Markdown
# 🏗️ 약국 통합 솔루션 아키텍처
|
||
|
||
## 📋 개요
|
||
|
||
본 시스템은 **동물약 도매상(애니팜)**, **개별 약국 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` | 주민번호 |
|
||
|
||
**PM_PRES.dbo.PS_sub_pharm** - 조제 약품 상세 ⭐
|
||
| 컬럼 | 설명 |
|
||
|------|------|
|
||
| `PreSerial` | 처방번호 (FK) |
|
||
| `SUB_SERIAL` | 약품 순번 |
|
||
| `DrugCode` | 제품 코드 |
|
||
| `Days` | 복용일수 |
|
||
| `QUAN` | 1회 복용량 |
|
||
| `QUAN_TIME` | 1일 복용횟수 |
|
||
| `INV_QUAN` | 총 투약량 |
|
||
| `PS_Type` | **조제 유형** (아래 참고) |
|
||
|
||
#### PS_Type 값 (대체조제 구분) ⭐
|
||
|
||
| PS_Type | 의미 | 표시 |
|
||
|---------|------|------|
|
||
| **0** | 일반 처방 | ✅ 표시 |
|
||
| **1** | 일반 대체조제 | ✅ 표시 + `대)` 배지 (주황색) |
|
||
| **4** | 저가대체 인센티브 - **실제 조제약** | ✅ 표시 + `저)` 배지 (초록색) |
|
||
| **9** | 저가대체 인센티브 - **원본 처방약** | ❌ 숨김 (약가 계산용)
|
||
|
||
**대체조제 데이터 패턴:**
|
||
```
|
||
SUB_SERIAL 순서로 4(실제) → 9(원본) 쌍으로 저장됨
|
||
|
||
예시 (김현지 처방):
|
||
PS_Type=4 | 사이톱신정 ← 실제 조제 (표시)
|
||
PS_Type=9 | 씨프러스정 ← 원본 처방 (숨김, 사이톱신의 원처방)
|
||
PS_Type=4 | 티로파정 ← 실제 조제 (표시)
|
||
PS_Type=9 | 티램정 ← 원본 처방 (숨김, 티로파의 원처방)
|
||
```
|
||
|
||
**쿼리 예시:**
|
||
```sql
|
||
-- 실제 조제약만 조회 (대체조제 원본 제외)
|
||
SELECT * FROM PS_sub_pharm WHERE PreSerial = '처방번호' AND PS_Type != '9'
|
||
|
||
-- 대체조제 쌍 확인
|
||
SELECT
|
||
s1.DrugCode AS 실제조제,
|
||
s2.DrugCode AS 원본처방
|
||
FROM PS_sub_pharm s1
|
||
JOIN PS_sub_pharm s2 ON s1.PreSerial = s2.PreSerial
|
||
AND s1.SUB_SERIAL + 1 = s2.SUB_SERIAL
|
||
WHERE s1.PS_Type = '4' AND s2.PS_Type = '9'
|
||
```
|
||
|
||
```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 | 회원 상세 (적립+구매+조제) | 전체 |
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 🤖 PAAI 시스템 (처방 AI 분석)
|
||
|
||
### 개요
|
||
|
||
**PAAI (Prescription AI Analysis)**는 처방 접수 시 자동으로 AI 분석을 수행하고,
|
||
분석 결과를 영수증 프린터로 출력하는 시스템입니다.
|
||
|
||
### 아키텍처
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ PAAI 시스템 흐름 │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ POS 접수 │────►│ PM_PRES_LOG │────►│ Trigger Module │
|
||
│ (처방입력) │ │ (MSSQL) │ │ (폴링 감지) │
|
||
└─────────────┘ └─────────────────┘ └─────────────────┘
|
||
│
|
||
┌───────────────────────────────┤
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────┐ ┌─────────────────┐
|
||
│ WebSocket 알림 │ │ PAAI 분석 요청 │
|
||
│ (ws://8765) │ │ Flask API │
|
||
└─────────────────┘ └─────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────┐ ┌─────────────────┐
|
||
│ 프론트엔드 │ │ Claude API │
|
||
│ pmr.html │ │ (분석 수행) │
|
||
└─────────────────┘ └─────────────────┘
|
||
│ │
|
||
│◄──────────────────────────────┤
|
||
│ analysis_completed 이벤트
|
||
▼
|
||
┌─────────────────┐
|
||
│ 자동 인쇄 │
|
||
│ ESC/POS 프린터 │
|
||
└─────────────────┘
|
||
```
|
||
|
||
### 구성 요소
|
||
|
||
| 모듈 | 위치 | 역할 |
|
||
|------|------|------|
|
||
| **Trigger Module** | `prescription-trigger/prescription_trigger.py` | PM_PRES_LOG 폴링, 처방 감지, 분석 요청 |
|
||
| **WebSocket Server** | Trigger 내장 (port 8765) | 프론트엔드에 실시간 이벤트 전송 |
|
||
| **PAAI API** | `backend/pmr_api.py` | 분석 요청 처리, Claude API 호출, 결과 저장 |
|
||
| **프론트엔드** | `backend/templates/pmr.html` | 조제관리 UI, 자동인쇄 토글 |
|
||
| **프린터 모듈** | `backend/paai_printer.py` | ESC/POS 영수증 프린터 출력 |
|
||
|
||
### WebSocket 이벤트
|
||
|
||
| 이벤트 | 방향 | 설명 |
|
||
|--------|------|------|
|
||
| `prescription_detected` | Server → Client | 새 처방 감지됨 |
|
||
| `analysis_started` | Server → Client | AI 분석 시작 |
|
||
| `analysis_completed` | Server → Client | 분석 완료 (결과 포함) |
|
||
| `analysis_failed` | Server → Client | 분석 실패 (에러 포함) |
|
||
|
||
### 자동 인쇄 흐름
|
||
|
||
```javascript
|
||
// 1. WebSocket으로 analysis_completed 수신
|
||
ws.onmessage = (event) => {
|
||
const data = JSON.parse(event.data);
|
||
if (data.event === 'analysis_completed') {
|
||
// 2. 자동인쇄 ON 상태면 인쇄
|
||
if (window.autoPrintEnabled) {
|
||
printPaaiResult(data.pre_serial, data.patient_name, data);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 3. 인쇄 API 호출
|
||
POST /pmr/api/paai/print
|
||
{
|
||
"pre_serial": "20260305000099",
|
||
"patient_name": "홍길동",
|
||
"result": { "analysis": {...}, "kims_summary": {...} }
|
||
}
|
||
|
||
// 4. ESC/POS 프린터로 출력
|
||
```
|
||
|
||
### 중복 방지
|
||
|
||
```javascript
|
||
// window.printedSerials (Set) 으로 중복 인쇄 방지
|
||
if (window.printedSerials.has(preSerial)) {
|
||
console.log('[AutoPrint] 이미 인쇄됨, 스킵:', preSerial);
|
||
return;
|
||
}
|
||
window.printedSerials.add(preSerial); // 요청 전에 추가 (race condition 방지)
|
||
```
|
||
|
||
### 자동 재시도
|
||
|
||
| 시도 | 대기 시간 | 상태 |
|
||
|------|----------|------|
|
||
| 1회차 | - | 최초 시도 |
|
||
| 2회차 | 2초 | 첫 번째 재시도 |
|
||
| 3회차 | 4초 | 두 번째 재시도 |
|
||
| 실패 | - | `analysis_failed` 이벤트 발송 |
|
||
|
||
### 로그 파일
|
||
|
||
| 파일 | 위치 | 내용 |
|
||
|------|------|------|
|
||
| `print_history.log` | `backend/logs/` | 인쇄 성공/실패 기록 |
|
||
| `analysis_failures.log` | `prescription-trigger/logs/` | 분석 실패 상세 기록 |
|
||
| `paai_logs.db` | `backend/db/` | 분석 결과 SQLite 저장 |
|
||
|
||
### 관련 문서
|
||
|
||
- `docs/PAAI_AUTO_PRINT_TROUBLESHOOTING.md` - 자동인쇄 트러블슈팅 가이드
|
||
|
||
---
|
||
|
||
## 📝 버전 이력
|
||
|
||
| 날짜 | 버전 | 변경 내용 |
|
||
|------|------|----------|
|
||
| 2026-02-28 | 1.0 | 초기 아키텍처 문서 작성 |
|
||
| | | 동물약 AI 챗봇 추가 |
|
||
| | | 플로팅 챗봇 UI 구현 |
|
||
| 2026-03-05 | 1.1 | PAAI 시스템 아키텍처 추가 |
|
||
| | | 자동인쇄, WebSocket, 재시도 로직 |
|
||
|
||
---
|
||
|
||
*작성: Clawdbot AI | 청춘약국 통합 솔루션*
|