# πŸ—οΈ μ•½κ΅­ 톡합 μ†”λ£¨μ…˜ μ•„ν‚€ν…μ²˜ ## πŸ“‹ κ°œμš” λ³Έ μ‹œμŠ€ν…œμ€ **동물약 도맀상(μ• λ‹ˆνŒœ)**, **κ°œλ³„ μ•½κ΅­ 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 | μ²­μΆ˜μ•½κ΅­ 톡합 μ†”λ£¨μ…˜*