docs: 데이터베이스 구조 문서화 (APC, MSSQL, PostgreSQL)
This commit is contained in:
parent
68ad59285a
commit
dd28958a59
299
docs/APC_MAPPING_PLAN.md
Normal file
299
docs/APC_MAPPING_PLAN.md
Normal file
@ -0,0 +1,299 @@
|
||||
# 🎯 APC 기반 동물약 매핑 기획서
|
||||
|
||||
> ⚠️ **주의**: 기획 단계에서는 MSSQL **READ ONLY**. 절대 데이터 입력/수정 금지.
|
||||
|
||||
---
|
||||
|
||||
## 📋 현상황 분석
|
||||
|
||||
### 1. 동물약 바코드 문제
|
||||
|
||||
#### 문제 1: 바코드 없음
|
||||
- 동물약은 **수의사 소분 판매 방지** 목적으로 공산품이지만 바코드가 없는 경우 많음
|
||||
- "판매 최소포장단위"별 바코드가 부여되지 않음
|
||||
- 예: 다이로하트, 넥스가드 등 → 바코드 없음
|
||||
|
||||
#### 문제 2: 바코드 중복
|
||||
- 바코드가 있어도 **여러 사이즈 제품이 동일 바코드** 사용
|
||||
- 예: 다이로하트정 SS/S/M/L → 모두 동일 바코드
|
||||
- 바코드만으로 사이즈/체중 구분 불가
|
||||
|
||||
#### 문제 3: 약국별 자체 바코드
|
||||
- 약국은 POS 재고관리를 위해 **자체 바코드 생성**하여 사용
|
||||
- 원래 바코드 무시하고 새로 지정
|
||||
- 이유: POS에서 스캔 시 제품별 즉시 구분 + 재고 차감 필요
|
||||
|
||||
#### 결과: 중앙 매핑 불가
|
||||
```
|
||||
약국 A: "안텔민사사" → 바코드 "A001"
|
||||
약국 B: "안텔민사사" → 바코드 "B999"
|
||||
약국 C: "안텔민사사" → 바코드 없음 (수기 입력)
|
||||
|
||||
↓ 중앙 시스템 입장
|
||||
|
||||
바코드 "A001" = ??? (알 수 없음)
|
||||
바코드 "B999" = ??? (알 수 없음)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 현재 데이터 구조 (2025-06-30 최종 확인)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ MSSQL (팜IT3000 - 약국 POS) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ CD_GOODS (제품 마스터) - 178,182개 │
|
||||
│ ├── DrugCode: LB000003157 (PK) │
|
||||
│ ├── GoodsName: "안텔민킹(5kg이상)" │
|
||||
│ └── 팜IT3000 전체 제품 DB │
|
||||
│ │ │
|
||||
│ ├────────────────┬─────────────────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ CD_SALEGOODS CD_ITEM_UNIT_MEMBER CD_BARCODE│
|
||||
│ (대표 바코드 1개) (바코드 N개!) ★ (인체용) │
|
||||
│ 3,053개 ├ CD_CD_BARCODE 306,565개│
|
||||
│ BARCODE: │ 0230237810109 (APC!) 동물약X │
|
||||
│ 9990000001134 │ 9990000001134 (자체) │
|
||||
│ └ DRUGCODE → CD_GOODS.DrugCode │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
★ 핵심: 한 제품에 여러 바코드 가능! → CD_ITEM_UNIT_MEMBER
|
||||
★ APC 저장 위치: CD_ITEM_UNIT_MEMBER.CD_CD_BARCODE
|
||||
★ APC로 이미지 조회: https://ani.0bin.in/img/{APC}_F.jpg
|
||||
```
|
||||
│
|
||||
│ 매핑 필요
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL (애니팜 - 동물약 마스터) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ apc 테이블 │
|
||||
│ ├── apc: "0230237010107" (고유!) │
|
||||
│ ├── product_name: "대성 안텔민 사사 정 100mg/25mg/10정" │
|
||||
│ ├── company_name: "(주)대성미생물연구소" │
|
||||
│ ├── for_pets: true │
|
||||
│ ├── image_url1: "https://ani.0bin.in/img/..." │
|
||||
│ └── godoimage_url_f: "https://cdn.../..." │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 해결책: APC 기반 매핑
|
||||
|
||||
### 핵심 아이디어
|
||||
|
||||
**APC(Animal Product Code)를 고유 매핑 키로 사용**
|
||||
|
||||
```
|
||||
CD_GOODS.DrugCode ←→ CD_BARCODE.DRUGCODE ←→ APC(새로추가) ←→ PostgreSQL.apc
|
||||
```
|
||||
|
||||
### 왜 APC인가?
|
||||
|
||||
| 키 | 고유성 | 중앙관리 | 이미지 | 현황 |
|
||||
|----|--------|----------|--------|------|
|
||||
| 바코드 | ❌ 중복/없음 | ❌ 약국별 다름 | ❌ | 사용 불가 |
|
||||
| DrugCode | ⚠️ 약국내 고유 | ❌ 약국별 다름 | ❌ | 내부용 |
|
||||
| **APC** | ✅ 전국 고유 | ✅ 애니팜 관리 | ✅ | **사용 가능** |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 구현 계획
|
||||
|
||||
### Phase 1: 동물약 태깅 (완료 ✅)
|
||||
```
|
||||
CD_GOODS에서 POS_BOON='010103' 추출 → 38개 동물약 식별
|
||||
```
|
||||
|
||||
### Phase 2: AI 기반 APC 매핑
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ MSSQL 동물약 │ │ AI 분석 │ │ PostgreSQL │
|
||||
│ 38개 제품 │────►│ 제품명 매칭 │────►│ apc 후보 추천 │
|
||||
│ │ │ 성분/체중 분석 │ │ │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 관리자 확인 │
|
||||
│ 매핑 승인/수정 │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
**AI 매칭 로직:**
|
||||
```python
|
||||
# MSSQL 제품
|
||||
mssql_product = "안텔민사사(5kg이하)"
|
||||
|
||||
# PostgreSQL 검색
|
||||
pgsql_candidates = search_apc("안텔민 사사")
|
||||
# → [
|
||||
# "대성 안텔민 사사 정 100mg/25mg/10정" (APC: 0230237010107),
|
||||
# "대성 안텔민 사사 정 100mg/25mg/50정" (APC: 0230237010205),
|
||||
# ...
|
||||
# ]
|
||||
|
||||
# AI 추천: 체중 범위, 포장단위 분석
|
||||
recommended_apc = "0230237010107" # 10정 (최소 판매단위)
|
||||
```
|
||||
|
||||
### Phase 3: APC 바코드 등록 방법
|
||||
|
||||
**옵션 A: CD_SALEGOODS.BARCODE 업데이트 (현재 구조 활용)**
|
||||
```sql
|
||||
-- CD_SALEGOODS에서 바코드를 APC로 변경
|
||||
UPDATE CD_SALEGOODS
|
||||
SET BARCODE = '0230237010107' -- APC 코드
|
||||
WHERE DrugCode = 'LB000003158'; -- 안텔민뽀삐
|
||||
```
|
||||
|
||||
또는 POS에서 직접:
|
||||
1. 제품 선택 → 바코드 수정 → APC 입력 → 저장
|
||||
|
||||
**옵션 B: 별도 매핑 테이블 (SQLite) - MSSQL 수정 최소화**
|
||||
```sql
|
||||
-- SQLite에 매핑 테이블 생성
|
||||
CREATE TABLE animal_drug_apc_mapping (
|
||||
id INTEGER PRIMARY KEY,
|
||||
mssql_drug_code TEXT NOT NULL, -- CD_GOODS.DrugCode
|
||||
mssql_barcode TEXT, -- CD_SALEGOODS.BARCODE (현재값)
|
||||
apc_code TEXT NOT NULL, -- PostgreSQL apc
|
||||
product_name TEXT, -- 확인용
|
||||
verified BOOLEAN DEFAULT 0, -- 관리자 검증 여부
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
**현재 동물약 바코드 현황:**
|
||||
| 제품 | DrugCode | CD_SALEGOODS.BARCODE |
|
||||
|------|----------|---------------------|
|
||||
| 안텔민뽀삐 | LB000003158 | 9990000001133 |
|
||||
| 안텔민킹 | LB000003157 | 9990000001134 |
|
||||
| 다이로하트S | LB000003150 | 9990000001131 |
|
||||
| 다이로하트M | LB000003151 | 9990000001132 |
|
||||
|
||||
### Phase 4: QR 라벨 출력 연동
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 프론트엔드: 제품 검색 → QR 라벨 출력 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. 제품 선택 (MSSQL) │
|
||||
│ └── DrugCode: LB000003158 │
|
||||
│ │
|
||||
│ 2. APC 매핑 확인 │
|
||||
│ └── APC: 0230237010107 (매핑됨 ✅) │
|
||||
│ │
|
||||
│ 3. QR 라벨 생성 │
|
||||
│ ├── QR 내용: APC 코드 │
|
||||
│ ├── 라벨 텍스트: 제품명 + 가격 │
|
||||
│ └── [인쇄] 버튼 활성화 │
|
||||
│ │
|
||||
│ ※ APC 미매핑 제품 → [인쇄] 버튼 비활성화 또는 경고 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Phase 5: 이미지 표시 연동
|
||||
|
||||
```
|
||||
챗봇 응답: "안텔민을 추천드려요"
|
||||
│
|
||||
├── MSSQL: "안텔민사사(5kg이하)" 재고 확인
|
||||
│
|
||||
├── APC 매핑: LB000003158 → 0230237010107
|
||||
│
|
||||
├── PostgreSQL: 이미지 URL 조회
|
||||
│ └── https://ani.0bin.in/img/0230237010107_F.jpg
|
||||
│
|
||||
└── 프론트: 제품 칩 + 이미지 썸네일 표시
|
||||
┌──────────────────────────┐
|
||||
│ 📦 안텔민사사 (5,000원) │
|
||||
│ [썸네일 이미지] │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 예상 데이터 흐름
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 전체 데이터 흐름 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [약국 POS] │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ CD_GOODS ──────► CD_BARCODE (APC 추가) │
|
||||
│ │ │ │
|
||||
│ │ │ APC = "0230237010107" │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ 제품 판매 ◄──── QR 스캔 (APC 인식) │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ [챗봇/이미지] [애니팜 연동] │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ PostgreSQL ◄─────────────┘ │
|
||||
│ (이미지, 상세정보, 용법용량) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ TODO 체크리스트
|
||||
|
||||
### 1단계: 분석 (READ ONLY)
|
||||
- [x] MSSQL 동물약 38개 추출
|
||||
- [x] CD_BARCODE 구조 분석
|
||||
- [x] PostgreSQL apc 테이블 구조 분석
|
||||
- [x] 매핑 가능 제품 샘플 확인 (안텔민, 하트가드 등)
|
||||
- [ ] 전체 38개 제품 APC 후보 목록 생성
|
||||
|
||||
### 2단계: 기획
|
||||
- [x] 매핑 전략 수립 (APC 기반)
|
||||
- [ ] CD_BARCODE 활용 vs SQLite 매핑 테이블 결정
|
||||
- [ ] 관리자 매핑 UI 설계
|
||||
- [ ] QR 라벨 출력 연동 설계
|
||||
|
||||
### 3단계: 구현 (약사님 승인 후)
|
||||
- [ ] 매핑 테이블 생성
|
||||
- [ ] AI 매핑 추천 기능
|
||||
- [ ] 관리자 매핑 확인/수정 UI
|
||||
- [ ] QR 라벨 출력 (APC 기반)
|
||||
- [ ] 챗봇 이미지 연동
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
1. **MSSQL 수정 금지** (기획 단계)
|
||||
- READ ONLY 유지
|
||||
- 테스트도 SELECT만
|
||||
|
||||
2. **APC 신뢰성**
|
||||
- PostgreSQL apc 테이블이 마스터
|
||||
- 애니팜에서 관리하는 공식 코드
|
||||
|
||||
3. **약국별 차이**
|
||||
- 자체 바코드 사용 중인 약국 고려
|
||||
- 기존 워크플로우 방해하지 않도록
|
||||
|
||||
4. **단계적 적용**
|
||||
- 매핑 확인된 제품만 QR 출력 허용
|
||||
- 미매핑 제품은 기존 방식 유지
|
||||
|
||||
---
|
||||
|
||||
*작성일: 2025-06-30*
|
||||
*작성자: 용림 (Clawdbot)*
|
||||
*상태: 기획 중*
|
||||
433
docs/ARCHITECTURE.md
Normal file
433
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,433 @@
|
||||
# 🏗️ 약국 통합 솔루션 아키텍처
|
||||
|
||||
## 📋 개요
|
||||
|
||||
본 시스템은 **동물약 도매상(애니팜)**, **개별 약국 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 | 청춘약국 통합 솔루션*
|
||||
165
docs/DATABASE_STRUCTURE.md
Normal file
165
docs/DATABASE_STRUCTURE.md
Normal file
@ -0,0 +1,165 @@
|
||||
# 데이터베이스 구조 (2025-06-30 정리)
|
||||
|
||||
## 개요
|
||||
|
||||
양구청춘약국 시스템은 3개의 데이터베이스를 사용합니다:
|
||||
|
||||
| DB | 용도 | 위치 |
|
||||
|----|------|------|
|
||||
| **MSSQL (PM_DRUG)** | POS 제품/재고/판매 | localhost (팜IT3000) |
|
||||
| **MSSQL (PM_PRES)** | 처방전/조제 | localhost (팜IT3000) |
|
||||
| **PostgreSQL** | 동물약 상세 정보 (RAG) | 192.168.0.87:5432 |
|
||||
| **SQLite** | 마일리지 시스템 | backend/db/mileage.db |
|
||||
|
||||
---
|
||||
|
||||
## MSSQL 테이블 구조 (PM_DRUG)
|
||||
|
||||
### 핵심 테이블 관계
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CD_GOODS (제품 마스터) - 178,182개 │
|
||||
│ └── DrugCode (PK): LB000003157 │
|
||||
│ │ │
|
||||
│ ┌─────────┴─────────────┬──────────────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ CD_SALEGOODS CD_ITEM_UNIT_MEMBER CD_BARCODE│
|
||||
│ (대표 바코드) (바코드 N개) ★ (인체용) │
|
||||
│ 3,053개 N:1 관계 306,565개│
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### CD_GOODS (제품 마스터)
|
||||
|
||||
팜IT3000 전체 제품 DB. 약국이 개별 등록한 제품은 `LB`, `S`로 시작.
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| DrugCode | nvarchar | PK. `LB000003157` (약국등록), `050000010` (표준) |
|
||||
| GoodsName | nvarchar | 제품명 |
|
||||
| Saleprice | decimal | 판매가 |
|
||||
| BARCODE | nvarchar | (보통 비어있음 - CD_SALEGOODS 사용) |
|
||||
| POS_BOON | nvarchar | 분류코드. `010103` = 동물약 |
|
||||
| GoodsSelCode | nvarchar | `B` = 판매용 |
|
||||
|
||||
### CD_SALEGOODS (판매용 제품)
|
||||
|
||||
약국에서 실제 판매하는 제품. **대표 바코드 1개** 저장.
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| DrugCode | nvarchar | FK → CD_GOODS |
|
||||
| GoodsName | nvarchar | 제품명 |
|
||||
| BARCODE | nvarchar | **대표 바코드** (자체생성: `999000000xxxx`) |
|
||||
| SplCode | nvarchar | 공급처 코드 |
|
||||
| SplName | nvarchar | 공급처명 |
|
||||
|
||||
### CD_ITEM_UNIT_MEMBER (바코드 N개) ★
|
||||
|
||||
**한 제품에 여러 바코드** 저장. APC 코드는 여기에 저장됨!
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| DRUGCODE | nvarchar | FK → CD_GOODS.DrugCode |
|
||||
| CD_CD_BARCODE | nvarchar | **바코드** (APC: `0230237810109`) |
|
||||
| CD_CD_UNIT | nvarchar | 단위코드 (13, 015 등) |
|
||||
| CD_MY_UNIT | decimal | 판매가 |
|
||||
| CD_IN_UNIT | decimal | 입고가 |
|
||||
| CHANGE_DATE | nvarchar | 변경일 (YYYYMMDD) |
|
||||
| SN | bigint | 일련번호 |
|
||||
|
||||
### CD_BARCODE (인체용 표준)
|
||||
|
||||
식약처 인체용 의약품 표준 바코드. **동물약은 없음!**
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| DRUGCODE | nvarchar | 제품코드 |
|
||||
| BARCODE | nvarchar | 표준 바코드 |
|
||||
| BASECODE | nvarchar | 표준코드 |
|
||||
| ETCNAME | nvarchar | 제품명 |
|
||||
| CL_GUBUN | nvarchar | 구분 (전문의약품 등) |
|
||||
|
||||
---
|
||||
|
||||
## PostgreSQL 구조 (apdb_master)
|
||||
|
||||
동물약품 상세 정보. 농림축산검역본부 데이터 + LLM 가공.
|
||||
|
||||
### apc 테이블 (핵심)
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| apc | varchar | **PK**. `0230237810109` |
|
||||
| product_name | varchar | 제품명 |
|
||||
| company_name | varchar | 제조사 |
|
||||
| main_ingredient | varchar | 주성분 |
|
||||
| efficacy_effect | text | 효능/효과 (HTML) |
|
||||
| dosage_instructions | text | 용법/용량 (HTML) |
|
||||
| precautions | text | 주의사항 (HTML) |
|
||||
| **llm_pharm** | jsonb | **LLM 가공 정보** ★ |
|
||||
| image_url1 | varchar | 앞면 이미지 |
|
||||
| image_url2 | varchar | 뒷면 이미지 |
|
||||
| weight_min_kg | float | 최소 체중 |
|
||||
| weight_max_kg | float | 최대 체중 |
|
||||
|
||||
### llm_pharm JSON 구조 (핵심!)
|
||||
|
||||
```json
|
||||
{
|
||||
"사용가능 동물": "개, 고양이",
|
||||
"분류": "내부구충제",
|
||||
"성분1": "메벤다졸",
|
||||
"성분2": "프라지콴텔",
|
||||
"체중/부위": "체중 5~9kg: 1정, 10~19kg: 2정...",
|
||||
"기간/용법": "1일 1회, 1~2일간 경구투여",
|
||||
"월령금기": "생후 1주 미만 사용 금지",
|
||||
"반려인주의": "사람이 복용 시 즉시 의사의 조치 필요",
|
||||
"앞이미지": "https://...",
|
||||
"뒤이미지": "https://..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 바코드 체계
|
||||
|
||||
| 패턴 | 설명 | 예시 |
|
||||
|------|------|------|
|
||||
| `023xxxxxxxx` | **APC (동물약 표준)** | `0230237810109` |
|
||||
| `999000000xxxx` | 약국 자체 생성 | `9990000001134` |
|
||||
| `880xxxxxxxxx` | 일반 GS1 바코드 | `8809989000009` |
|
||||
|
||||
---
|
||||
|
||||
## 연결 예시
|
||||
|
||||
**안텔민킹(5kg이상) 조회:**
|
||||
|
||||
```sql
|
||||
-- MSSQL: 바코드 조회
|
||||
SELECT CD_CD_BARCODE
|
||||
FROM CD_ITEM_UNIT_MEMBER
|
||||
WHERE DRUGCODE = 'LB000003157'
|
||||
AND CD_CD_BARCODE LIKE '023%';
|
||||
-- → 0230237810109
|
||||
|
||||
-- PostgreSQL: 상세 정보 조회
|
||||
SELECT llm_pharm->>'사용가능 동물', efficacy_effect
|
||||
FROM apc
|
||||
WHERE apc = '0230237810109';
|
||||
-- → 개, 고양이
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 이미지 URL 규칙
|
||||
|
||||
```
|
||||
https://ani.0bin.in/img/{APC}_F.jpg # 앞면
|
||||
https://ani.0bin.in/img/{APC}_B.jpg # 뒷면
|
||||
https://ani.0bin.in/img/{APC}_D.jpg # 상세
|
||||
```
|
||||
|
||||
예: `https://ani.0bin.in/img/0230237810109_F.jpg`
|
||||
167
docs/ENCODING_GUIDE.md
Normal file
167
docs/ENCODING_GUIDE.md
Normal file
@ -0,0 +1,167 @@
|
||||
# 🔤 인코딩 가이드 (필독!)
|
||||
|
||||
> ⚠️ **중요**: 한글 데이터 처리 시 반드시 이 가이드를 따를 것
|
||||
|
||||
---
|
||||
|
||||
## ✅ 현재 설정 (2025-06-30 적용됨)
|
||||
|
||||
```
|
||||
환경변수: PYTHONIOENCODING=utf-8 (User 레벨)
|
||||
```
|
||||
|
||||
이 설정으로 모든 Python 스크립트에서 UTF-8 출력이 기본 적용됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 📋 문제 상황
|
||||
|
||||
### 증상
|
||||
```
|
||||
DB 실제 값: "안텔민뽀삐"
|
||||
콘솔 출력: "안텔민사사" ← 깨져서 다른 글자로 보임!
|
||||
```
|
||||
|
||||
### 원인
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ DB (UTF-8) │ ──► │ Python │ ──► │ Windows 콘솔 │
|
||||
│ "뽀삐" │ │ stdout │ │ (CP949) │
|
||||
│ U+BF40 │ │ │ │ "사사" │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
↑
|
||||
인코딩 변환 실패!
|
||||
```
|
||||
|
||||
- Windows 콘솔 기본 인코딩: **CP949** (한국어 완성형)
|
||||
- CP949에서 지원하지 않거나 다르게 매핑되는 유니코드 문자 존재
|
||||
- "뽀삐" 같은 글자가 "사사"로 잘못 표시됨
|
||||
|
||||
---
|
||||
|
||||
## ✅ 해결책
|
||||
|
||||
### 1. 스크립트 상단에 인코딩 설정 추가 (필수!)
|
||||
|
||||
```python
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import io
|
||||
|
||||
# stdout을 UTF-8로 강제 설정
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
```
|
||||
|
||||
### 2. 환경변수 설정 (권장)
|
||||
|
||||
```powershell
|
||||
# PowerShell에서 실행 전 설정
|
||||
$env:PYTHONIOENCODING = "utf-8"
|
||||
|
||||
# 또는 시스템 환경변수로 영구 설정
|
||||
[Environment]::SetEnvironmentVariable("PYTHONIOENCODING", "utf-8", "User")
|
||||
```
|
||||
|
||||
### 3. Windows Terminal UTF-8 모드
|
||||
|
||||
```powershell
|
||||
# 콘솔 코드페이지를 UTF-8로 변경
|
||||
chcp 65001
|
||||
```
|
||||
|
||||
### 4. JSON 출력 사용 (가장 안전)
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
# 콘솔 출력 대신 JSON으로 반환
|
||||
result = {
|
||||
"product_name": "안텔민뽀삐",
|
||||
"apc": "0230237010107"
|
||||
}
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 스크립트 템플릿
|
||||
|
||||
모든 DB 조회 스크립트는 이 템플릿을 사용할 것:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
스크립트 설명
|
||||
"""
|
||||
import sys
|
||||
import io
|
||||
import json
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 인코딩 설정 (Windows CP949 문제 방지)
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 메인 로직
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def main():
|
||||
# ... 로직 ...
|
||||
|
||||
# 결과는 JSON으로 출력 (가장 안전)
|
||||
result = {"data": [...]}
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 기존 스크립트 수정 목록
|
||||
|
||||
| 스크립트 | 상태 | 수정 필요 |
|
||||
|----------|------|-----------|
|
||||
| `scripts/query_mileage.py` | ⚠️ | 인코딩 설정 추가 |
|
||||
| `scripts/query_sales.py` | ⚠️ | 인코딩 설정 추가 |
|
||||
| `scripts/query_aniparm.py` | ⚠️ | 인코딩 설정 추가 |
|
||||
| `scripts/search_mssql.py` | ⚠️ | 인코딩 설정 추가 |
|
||||
| `scripts/check_*.py` | ⚠️ | 인코딩 설정 추가 |
|
||||
|
||||
---
|
||||
|
||||
## 🧪 테스트 방법
|
||||
|
||||
```python
|
||||
# 인코딩 테스트 스크립트
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import io
|
||||
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
test_words = ["뽀삐", "킹", "안텔민뽀삐(5kg이하)", "다이로하트정M(12~22kg)"]
|
||||
|
||||
for word in test_words:
|
||||
print(f"원본: {word}")
|
||||
print(f"유니코드: {[f'U+{ord(c):04X}' for c in word]}")
|
||||
print()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
1. **절대 CP949 출력을 믿지 말 것** - 깨진 글자가 다른 글자로 보일 수 있음
|
||||
2. **DB 데이터 확인 시** - 직접 DB 툴로 확인하거나 JSON 출력 사용
|
||||
3. **AI 분석 시** - 유니코드 코드포인트로 확인 (U+XXXX)
|
||||
4. **매핑 작업 시** - 반드시 양쪽 DB 직접 확인 후 진행
|
||||
|
||||
---
|
||||
|
||||
*작성일: 2025-06-30*
|
||||
*사유: "뽀삐"가 "사사"로 잘못 표시되는 인코딩 문제 발생*
|
||||
238
docs/IMAGE_MAPPING_PLAN.md
Normal file
238
docs/IMAGE_MAPPING_PLAN.md
Normal file
@ -0,0 +1,238 @@
|
||||
# 🖼️ 동물약 이미지 매핑 계획
|
||||
|
||||
## 📋 목표
|
||||
챗봇에서 동물약 추천 시 **제품 이미지**를 함께 표시
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 데이터 현황
|
||||
|
||||
### MSSQL (약국 POS - PM_DRUG.CD_GOODS)
|
||||
|
||||
| 컬럼 | 설명 | 현황 |
|
||||
|------|------|------|
|
||||
| `DrugCode` | 제품코드 | ✅ 전체 있음 (예: LB000003151) |
|
||||
| `GoodsName` | 제품명 | ✅ 전체 있음 |
|
||||
| `BARCODE` | 바코드 | ⚠️ **14/38개만 있음 (37%)** |
|
||||
| `BaseCode` | 표준코드 | ❌ **0개** (사용 불가) |
|
||||
|
||||
### PostgreSQL (애니팜 - apc 테이블)
|
||||
|
||||
| 컬럼 | 설명 |
|
||||
|------|------|
|
||||
| `idx` | 고유 ID |
|
||||
| `apc` | APC 코드 (고유) |
|
||||
| `product_name` | 제품명 |
|
||||
| `image_url1` ~ `image_url3` | 이미지 URL |
|
||||
| `godoimage_url_f` | 고도몰 CDN - 앞 이미지 |
|
||||
| `godoimage_url_b` | 고도몰 CDN - 뒤 이미지 |
|
||||
| `godoimage_url_d` | 고도몰 CDN - 상세 이미지 |
|
||||
|
||||
**확인 필요**: `apc` 테이블에 바코드 컬럼이 있는지?
|
||||
|
||||
---
|
||||
|
||||
## 🔗 매핑 전략
|
||||
|
||||
### 옵션 1: 바코드 매핑 (37% 커버)
|
||||
```
|
||||
MSSQL.BARCODE ↔ PostgreSQL.barcode(?)
|
||||
```
|
||||
- 장점: 정확한 매칭
|
||||
- 단점: 14/38개만 매핑 가능
|
||||
|
||||
### 옵션 2: 제품명 유사도 매핑 (Fuzzy Matching)
|
||||
```python
|
||||
from fuzzywuzzy import fuzz
|
||||
|
||||
# MSSQL: "다이로하트정M(12~22kg)"
|
||||
# PostgreSQL: "다이로하트 정M 12~22kg" 등 유사 이름 매칭
|
||||
score = fuzz.partial_ratio(mssql_name, pgsql_name)
|
||||
if score > 80:
|
||||
matched = True
|
||||
```
|
||||
- 장점: 100% 커버 가능
|
||||
- 단점: 오매칭 위험
|
||||
|
||||
### 옵션 3: 매핑 테이블 생성 (권장) ✅
|
||||
```sql
|
||||
-- SQLite에 매핑 테이블 생성
|
||||
CREATE TABLE drug_image_mapping (
|
||||
id INTEGER PRIMARY KEY,
|
||||
mssql_drug_code TEXT UNIQUE, -- MSSQL DrugCode
|
||||
pgsql_apc TEXT, -- PostgreSQL apc 코드
|
||||
image_url TEXT, -- 확정된 이미지 URL
|
||||
verified BOOLEAN DEFAULT 0, -- 수동 검증 여부
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
**작업 흐름:**
|
||||
1. 바코드 매칭 (자동) → 14개 즉시 매핑
|
||||
2. 제품명 유사도로 후보 추천 → 관리자 확인
|
||||
3. 수동 매핑 → 나머지 제품
|
||||
|
||||
---
|
||||
|
||||
## 📊 MSSQL 동물약 바코드 현황 (38개)
|
||||
|
||||
### ✅ 바코드 있음 (14개)
|
||||
| 제품명 | DrugCode | 바코드 |
|
||||
|--------|----------|--------|
|
||||
| 가드L(20~40kg) | LB000003570 | 8801244508268 |
|
||||
| 가드M(10~20kg) | LB000003569 | 8801244508237 |
|
||||
| 가드S(2~10kg) | LB000003568 | 8801244508220 |
|
||||
| 하트가드정L(10~20kg) | LB000003564 | 8801244508343 |
|
||||
| 하트가드정M(5~10kg) | LB000003453 | 8801244508329 |
|
||||
| 하트가드정S(2.5~5kg) | LB000003452 | 8801244508312 |
|
||||
| 하트가드정SS(2.5kg이하) | LB000003451 | 8801244508305 |
|
||||
| 심파리카L(10~25kg) | LB000003634 | 8801244508534 |
|
||||
| 심파리카M(4~10kg) | LB000003635 | 8801244508435 |
|
||||
| 안텔민 | S0000001 | 8809989000009 |
|
||||
| 세레타정(10정) | LB000003146 | 8809720800455 |
|
||||
| 파라칸L(5kg이상) | LB000003159 | 8809625390914 |
|
||||
| 파라칸S(5kg이하) | LB000003160 | 8809625390655 |
|
||||
| 하트칸츄어블(11kg이하) | LB000003696 | 8809625390563 |
|
||||
|
||||
### ❌ 바코드 없음 (24개)
|
||||
| 제품명 | DrugCode |
|
||||
|--------|----------|
|
||||
| (동)클리어민50(100정) | LB000003504 |
|
||||
| 넥스가드L(15~30kg) | LB000003531 |
|
||||
| 넥스가드xs(2~3.5kg) | LB000003530 |
|
||||
| 다이로하트정M(12~22kg) | LB000003151 |
|
||||
| 다이로하트정S(5.6~11kg) | LB000003150 |
|
||||
| 다이로하트정SS(5.6kg이하) | LB000003149 |
|
||||
| 레보M(10~20kg) | LB000003161 |
|
||||
| 레보S(2~10kg) | LB000003162 |
|
||||
| 밀베마이신A정16mg(대동미어) | LB000003353 |
|
||||
| 밀베마이신A정24mg(대동미어) | LB000003354 |
|
||||
| 하트가드정XL(20~40kg) | LB000003545 |
|
||||
| 안텔민사사(5kg이하) | LB000003158 |
|
||||
| 안텔민킹(5kg이상) | LB000003157 |
|
||||
| 캐치펫캅(2.5~7.5kg)/고양이 | LB000003167 |
|
||||
| 캐치펫L(10~20kg)/개 | LB000003166 |
|
||||
| 캐치펫M(5~10kg)/개 | LB000003165 |
|
||||
| 캐치펫S(2.5~5kg)/개 | LB000003164 |
|
||||
| 캐치펫SS(2.5kg이하/개,고양이가능) | LB000003163 |
|
||||
| 하트플레이버블정L(23~45kg) | LB000003544 |
|
||||
| 하트플레이버블정M(12~22kg) | LB000003152 |
|
||||
| 하트플레이버블정mini(5.6kg이하) | LB000003154 |
|
||||
| 하트플레이버블정S(5.6~11kg) | LB000003153 |
|
||||
| 하트플라블러스정M(12~22kg) | LB000003155 |
|
||||
| 하트플라블러스정S(11kg이하) | LB000003156 |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 구현 단계
|
||||
|
||||
### Phase 1: PostgreSQL 조사
|
||||
- [ ] apc 테이블에 바코드 컬럼 확인
|
||||
- [ ] 이미지 URL 실제 데이터 샘플 확인
|
||||
- [ ] 동물약 제품 필터링 방법 확인 (`for_pets = true`?)
|
||||
|
||||
### Phase 2: 매핑 테이블 생성
|
||||
- [ ] SQLite에 `drug_image_mapping` 테이블 생성
|
||||
- [ ] 바코드 있는 14개 자동 매핑 시도
|
||||
- [ ] 관리자 페이지에 매핑 UI 추가
|
||||
|
||||
### Phase 3: 챗봇 연동
|
||||
- [ ] AI 응답에서 제품 매칭 시 이미지 URL 포함
|
||||
- [ ] 프론트엔드에 이미지 표시 (썸네일)
|
||||
- [ ] 클릭 시 큰 이미지 또는 상세 페이지
|
||||
|
||||
---
|
||||
|
||||
## 📝 다음 작업
|
||||
|
||||
1. **PostgreSQL apc 테이블 샘플 조회**
|
||||
```sql
|
||||
SELECT product_name, image_url1, godoimage_url_f
|
||||
FROM apc
|
||||
WHERE for_pets = true
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
2. **바코드 컬럼 존재 여부 확인**
|
||||
```sql
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'apc' AND column_name LIKE '%barcode%';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗃️ MSSQL 테이블 구조 상세
|
||||
|
||||
### CD_GOODS vs CD_BARCODE 관계
|
||||
|
||||
```
|
||||
CD_GOODS (제품 마스터) CD_BARCODE (바코드 마스터)
|
||||
├── DrugCode (PK) ──────────► DRUGCODE (FK)
|
||||
├── GoodsName ├── BARCODE (개별 바코드)
|
||||
├── BARCODE (대표 바코드) ├── TITLECODE (대표 바코드)
|
||||
├── BaseCode (❌ 비어있음) ├── BASECODE ✅ (100% 있음!)
|
||||
├── SUNG_CODE ├── SUNG_CODE (성분코드)
|
||||
└── Saleprice ├── DIK_CODE (의약품통합코드)
|
||||
├── ETCNAME (제품명)
|
||||
└── SPLNAME (제조사)
|
||||
```
|
||||
|
||||
**핵심 포인트:**
|
||||
- `CD_GOODS.BaseCode`는 비어있음 (사용 안 함)
|
||||
- `CD_BARCODE.BASECODE`에 표준코드 100% 있음!
|
||||
- 1개 제품(DrugCode)에 여러 바코드 가능 (낱개, 박스 등)
|
||||
|
||||
### CD_BARCODE 매핑 키 통계
|
||||
|
||||
| 컬럼 | 보유율 | 설명 | 외부 매핑 |
|
||||
|------|--------|------|-----------|
|
||||
| `BARCODE` | 100% | 개별 바코드 | ⭐ PostgreSQL 매핑 가능 |
|
||||
| `BASECODE` | 100% | 표준코드 (식약처) | 인체용만 |
|
||||
| `TITLECODE` | 100% | 대표 바코드 | |
|
||||
| `DIK_CODE` | 68.2% | 의약품통합코드 | |
|
||||
| `SUNG_CODE` | 44.5% | 성분코드 | |
|
||||
|
||||
### 동물약 특이사항
|
||||
|
||||
```
|
||||
동물약 38개
|
||||
├── CD_GOODS에 있음 ✅ (POS_BOON = '010103')
|
||||
├── CD_BARCODE에 없음 ❌ (인체용 아님)
|
||||
├── DrugCode가 "LB"로 시작 (로컬/자체 등록)
|
||||
└── BASECODE 매핑 불가 → PostgreSQL(애니팜) 필요
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔗 PostgreSQL(애니팜) 매핑 전략
|
||||
|
||||
### 확인 필요 사항
|
||||
|
||||
PostgreSQL `apc` 테이블에서 확인할 컬럼:
|
||||
|
||||
```sql
|
||||
-- 바코드 관련 컬럼 확인
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'apc'
|
||||
AND column_name ILIKE '%barcode%';
|
||||
|
||||
-- 코드 관련 컬럼 확인
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'apc'
|
||||
AND (column_name ILIKE '%code%' OR column_name ILIKE '%apc%');
|
||||
```
|
||||
|
||||
### 예상 매핑 키
|
||||
|
||||
| MSSQL (CD_GOODS/BARCODE) | PostgreSQL (apc) | 매핑 방식 |
|
||||
|--------------------------|------------------|-----------|
|
||||
| `BARCODE` | `barcode`? | 직접 매핑 |
|
||||
| `GoodsName` | `product_name` | 유사도 매칭 |
|
||||
| `제조사` | `company_name` | 보조 키 |
|
||||
|
||||
---
|
||||
|
||||
*작성일: 2025-06-30*
|
||||
*업데이트: 2025-06-30 - CD_BARCODE 구조 분석 추가*
|
||||
*프로젝트: pharmacy-pos-qr-system*
|
||||
Loading…
Reference in New Issue
Block a user