pharmacy-pos-qr-system/docs/AI_자동발주시스템_통합기획서_v1.html
thug0bin a672c7a2a0 feat(order): 지오영/수인 선택적 주문 + 장바구니 보존 기능
- internal_code DB 저장 → 프론트에서 선택한 제품 그대로 주문
- 기존 장바구니 백업/복구로 사용자 장바구니 보존
- 수인약품 submit_order() 수정 (체크박스 제외 방식)
- 테스트 파일 정리 및 문서 추가
2026-03-06 23:26:44 +09:00

1148 lines
38 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🤖 AI 자동발주시스템 통합 기획서</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;600;700;900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif !important;
line-height: 1.9;
color: #1a1a2e;
max-width: 850px;
margin: 0 auto;
padding: 50px 40px 80px;
background: linear-gradient(180deg, #fafbff 0%, #ffffff 100%);
font-size: 16px;
-webkit-font-smoothing: antialiased;
}
/* ===== 헤더 ===== */
h1 {
font-size: 2.2em;
font-weight: 900;
color: #2d3436;
margin: 60px 0 30px;
padding-bottom: 15px;
border-bottom: 4px solid #6c5ce7;
letter-spacing: -1px;
position: relative;
}
h1::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 80px;
height: 4px;
background: linear-gradient(90deg, #a29bfe, #6c5ce7);
}
h1:first-of-type {
margin-top: 0;
font-size: 2.5em;
text-align: center;
border-bottom: none;
background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 50%, #fd79a8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
padding: 30px 0 40px;
letter-spacing: -2px;
}
h1:first-of-type::after {
display: none;
}
h2 {
font-size: 1.6em;
font-weight: 700;
color: #2d3436;
margin: 55px 0 22px;
padding: 12px 0 12px 18px;
border-left: 5px solid #6c5ce7;
background: linear-gradient(90deg, #f8f9ff 0%, transparent 100%);
}
h3 {
font-size: 1.3em;
font-weight: 600;
color: #2d3436;
margin: 40px 0 18px;
padding-left: 12px;
border-left: 3px solid #a29bfe;
}
h4 {
font-size: 1.1em;
font-weight: 600;
color: #636e72;
margin: 30px 0 14px;
}
/* ===== 텍스트 ===== */
p {
margin: 18px 0;
text-align: justify;
word-break: keep-all;
color: #2d3436;
}
strong {
color: #2d3436;
font-weight: 700;
background: linear-gradient(180deg, transparent 60%, #fff3cd 60%);
padding: 0 2px;
}
em {
color: #636e72;
font-style: italic;
}
/* ===== 인용문 ===== */
blockquote {
background: linear-gradient(135deg, #f8f9ff 0%, #fff5f8 100%);
border-left: 5px solid #6c5ce7;
padding: 22px 28px;
margin: 28px 0;
border-radius: 0 16px 16px 0;
color: #2d3436;
font-size: 0.98em;
box-shadow: 0 4px 15px rgba(108, 92, 231, 0.08);
}
blockquote p {
margin: 10px 0;
}
blockquote p:first-child {
margin-top: 0;
}
blockquote p:last-child {
margin-bottom: 0;
}
/* ===== 코드 ===== */
code {
background: #f1f3f8;
padding: 3px 8px;
border-radius: 6px;
font-family: 'JetBrains Mono', 'D2Coding', Consolas, monospace !important;
font-size: 0.88em;
color: #e74c3c;
border: 1px solid #e9ecef;
}
pre {
background: linear-gradient(145deg, #2d3436, #1e272e);
color: #dfe6e9;
padding: 28px;
border-radius: 16px;
overflow-x: auto;
margin: 28px 0;
font-size: 0.9em;
line-height: 1.8;
box-shadow: 0 15px 50px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.1);
border: 1px solid #3d3d3d;
}
pre code {
background: none !important;
color: inherit;
padding: 0;
border: none;
font-size: 1em;
}
/* ===== 테이블 ===== */
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 35px 0;
font-size: 0.95em;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 8px 30px rgba(108, 92, 231, 0.12);
border: 1px solid #e9ecef;
}
th {
background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%);
color: #fff;
padding: 16px 20px;
text-align: left;
font-weight: 600;
font-size: 0.95em;
letter-spacing: 0.3px;
text-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
td {
padding: 15px 20px;
border-bottom: 1px solid #f1f3f8;
vertical-align: top;
background: #fff;
}
tr:last-child td {
border-bottom: none;
}
tr:nth-child(even) td {
background: #fafbff;
}
tr:hover td {
background: #f0f3ff;
}
/* ===== 리스트 ===== */
ul, ol {
margin: 20px 0;
padding-left: 28px;
}
li {
margin: 12px 0;
line-height: 1.8;
color: #2d3436;
}
li::marker {
color: #6c5ce7;
font-weight: 700;
}
ul li {
list-style-type: none;
position: relative;
padding-left: 8px;
}
ul li::before {
content: '•';
color: #6c5ce7;
font-weight: 900;
position: absolute;
left: -20px;
font-size: 1.2em;
}
/* ===== 구분선 ===== */
hr {
border: none;
height: 3px;
background: linear-gradient(90deg, #6c5ce7, #a29bfe, #fd79a8, #fdcb6e);
margin: 60px 0;
border-radius: 3px;
}
/* ===== 링크 ===== */
a {
color: #6c5ce7;
text-decoration: none;
border-bottom: 2px solid transparent;
transition: all 0.2s ease;
font-weight: 500;
}
a:hover {
border-bottom-color: #6c5ce7;
color: #5f4fcf;
}
/* ===== 이미지 ===== */
img {
max-width: 100%;
height: auto;
border-radius: 16px;
margin: 25px 0;
box-shadow: 0 10px 40px rgba(0,0,0,0.12);
}
/* ===== 이모지 강조 ===== */
p:has(> strong:first-child) {
background: linear-gradient(90deg, #f8f9ff 0%, transparent 100%);
padding: 12px 16px;
border-radius: 8px;
margin: 20px 0;
}
/* ===== 프린트 ===== */
@media print {
body {
padding: 15mm;
max-width: none;
font-size: 11pt;
background: #fff;
}
h1:first-of-type {
-webkit-text-fill-color: #6c5ce7;
color: #6c5ce7;
}
pre {
white-space: pre-wrap;
word-wrap: break-word;
box-shadow: none;
border: 1px solid #ddd;
}
table {
box-shadow: none;
}
h1, h2, h3 {
page-break-after: avoid;
}
table, pre, blockquote, img {
page-break-inside: avoid;
}
}
/* ===== 모바일 ===== */
@media (max-width: 768px) {
body {
padding: 25px 18px 50px;
font-size: 15px;
}
h1 { font-size: 1.8em; }
h1:first-of-type { font-size: 2em; }
h2 { font-size: 1.4em; padding-left: 14px; }
h3 { font-size: 1.2em; }
table { font-size: 0.85em; }
th, td { padding: 12px 14px; }
blockquote { padding: 18px 20px; }
pre { padding: 20px; }
}
</style>
</head>
<body>
<h1 id="ai">🤖 AI 자동발주시스템 통합 기획서</h1>
<blockquote>
<p><strong>버전</strong>: 1.0<br />
<strong>작성일</strong>: 2026-03-06<br />
<strong>작성자</strong>: 용림 (with 약사님)<br />
<strong>상태</strong>: 기획 완료, 개발 대기</p>
</blockquote>
<hr />
<h2 id="_1">📋 목차</h2>
<ol>
<li><a href="#1-비전-및-목표">비전 및 목표</a></li>
<li><a href="#2-현재-구현-현황">현재 구현 현황</a></li>
<li><a href="#3-시스템-아키텍처">시스템 아키텍처</a></li>
<li><a href="#4-ai-학습-요소">AI 학습 요소</a></li>
<li><a href="#5-핵심-기능-설계">핵심 기능 설계</a></li>
<li><a href="#6-데이터-모델">데이터 모델</a></li>
<li><a href="#7-api-설계">API 설계</a></li>
<li><a href="#8-자동화-레벨">자동화 레벨</a></li>
<li><a href="#9-알림-시스템">알림 시스템</a></li>
<li><a href="#10-개발-로드맵">개발 로드맵</a></li>
<li><a href="#11-성공-지표">성공 지표</a></li>
</ol>
<hr />
<h2 id="1">1. 비전 및 목표</h2>
<h3 id="_2">🎯 비전</h3>
<blockquote>
<p><strong>"약사님이 주문에 신경 쓰지 않아도 되는 약국"</strong></p>
</blockquote>
<p>AI가 사용량, 재고, 도매상 상황, 과거 주문 패턴을 학습하여:<br />
- <strong>언제</strong> 주문할지<br />
- <strong>어느 도매상</strong>에 주문할지<br />
- <strong>어떤 규격</strong>으로 주문할지<br />
- <strong>얼마나</strong> 주문할지</p>
<p>모든 것을 자동으로 결정하고 실행합니다.</p>
<h3 id="_3">핵심 가치</h3>
<table>
<thead>
<tr>
<th>AS-IS</th>
<th>TO-BE</th>
</tr>
</thead>
<tbody>
<tr>
<td>매일 재고 확인</td>
<td>AI가 자동 모니터링</td>
</tr>
<tr>
<td>수동으로 도매상 선택</td>
<td>AI가 최적 도매상 선택</td>
</tr>
<tr>
<td>경험에 의존한 주문량</td>
<td>데이터 기반 최적 주문량</td>
</tr>
<tr>
<td>주문 누락/지연 발생</td>
<td>선제적 자동 주문</td>
</tr>
<tr>
<td>배송 마감 놓침</td>
<td>마감시간 자동 알림</td>
</tr>
</tbody>
</table>
<h3 id="_4">핵심 원칙</h3>
<blockquote>
<p><strong>"AI는 대체하는 것이 아니라, 약사님의 방식을 자동화합니다."</strong></p>
</blockquote>
<ul>
<li>약사님이 항상 지오영에 먼저 주문하면 → AI도 지오영 우선</li>
<li>약사님이 300T보다 30T를 선호하면 → AI도 소량 주문</li>
<li>약사님이 여유 있게 주문하면 → AI도 안전 재고 확보</li>
<li>약사님이 가격에 민감하면 → AI도 최저가 추적 (OTC/비급여)</li>
</ul>
<hr />
<h2 id="2">2. 현재 구현 현황</h2>
<h3 id="21-api">2.1 도매상 API (✅ 완료)</h3>
<table>
<thead>
<tr>
<th>도매상</th>
<th style="text-align: center;">재고조회</th>
<th style="text-align: center;">장바구니</th>
<th style="text-align: center;">주문</th>
<th style="text-align: center;">취소/복원</th>
<th style="text-align: center;">잔고</th>
<th style="text-align: center;">월매출</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>지오영</strong></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;">✅ 확정포함</td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
</tr>
<tr>
<td><strong>수인약품</strong></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
</tr>
<tr>
<td><strong>백제약품</strong></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
<td style="text-align: center;"></td>
</tr>
</tbody>
</table>
<h3 id="22-db">2.2 주문 DB (✅ 완료)</h3>
<pre><code>orders.db
├── wholesalers # 도매상 마스터
├── orders # 주문 헤더
├── order_items # 주문 품목
├── order_logs # 주문 이력
├── order_context # AI 학습용 컨텍스트 ⭐
├── daily_usage # 일별 사용량 시계열
└── order_patterns # AI 분석 결과
</code></pre>
<h3 id="23">2.3 배송 스케줄 (✅ 확인 완료)</h3>
<pre><code>┌──────────┬──────────┬──────────────┬──────────────┬──────────┐
│ 도매상 │ 배송 │ 주문 마감 │ 도착 예정 │ 비고 │
├──────────┼──────────┼──────────────┼──────────────┼──────────┤
│ 지오영 │ 오전 │ 10:00 │ 11:30 │ 당일 │
│ │ 오후 │ 13:00 │ 15:00 │ 당일 │
├──────────┼──────────┼──────────────┼──────────────┼──────────┤
│ 수인 │ 오후 │ 13:00 │ 14:30 │ 당일 │
├──────────┼──────────┼──────────────┼──────────────┼──────────┤
│ 백제 │ 익일 │ 16:00 │ 다음날 15:00 │ ⚠️ 익일 │
└──────────┴──────────┴──────────────┴──────────────┴──────────┘
</code></pre>
<h3 id="24-ui">2.4 UI (✅ 완료)</h3>
<ul>
<li>Rx 사용량 페이지 (처방 기반)</li>
<li>장바구니 모달</li>
<li>도매상 잔고/월매출 모달</li>
</ul>
<hr />
<h2 id="3">3. 시스템 아키텍처</h2>
<h3 id="_5">전체 흐름</h3>
<pre><code>┌─────────────────────────────────────────────────────────────────┐
│ AI 자동발주시스템 │
└─────────────────────────────────────────────────────────────────┘
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 데이터 수집 │ │ AI 분석 │ │ 자동 실행 │
│ │ │ │ │ │
│ • POS 판매 │─────▶│ • 사용량 예측 │─────▶│ • 도매상 API │
│ • 처방전 조제 │ │ • 재고 분석 │ │ • 주문 실행 │
│ • 현재 재고 │ │ • 주문 추천 │ │ • 결과 피드백 │
│ • 도매상 재고 │ │ • 패턴 학습 │ │ │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
┌───────────────────┐
│ 학습 루프 │
│ │
│ 주문 결과 평가 │
│ → 모델 업데이트 │
│ → 전략 조정 │
└───────────────────┘
</code></pre>
<h3 id="_6">컴포넌트 구조</h3>
<pre><code>┌──────────────────────────────────────────────────────────────────┐
│ 데이터 레이어 │
├──────────────────────────────────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ PIT3000 │ │ SQLite │ │ 지오영 │ │ 수인 │ │
│ │ (MSSQL) │ │ Orders DB │ │ API │ │ API │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │
│ └───────────────┴───────────────┴───────────────┘ │
└────────────────────────────────┬─────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ 서비스 레이어 │
├──────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ InventorySync │ │ UsageAnalyzer │ │ OrderExecutor │ │
│ │ 재고 동기화 │ │ 사용량 분석 │ │ 주문 실행 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ AIPredictor │ │ AIOptimizer │ │ AILearner │ │
│ │ 수요 예측 │ │ 규격/도매상 │ │ 패턴 학습 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ 인터페이스 레이어 │
├──────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 웹 대시보드 │ │ 알림 시스템 │ │ 관리자 앱 │ │
│ │ 재고/주문/AI │ │ 카톡/텔레그램 │ │ 수동 개입 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
</code></pre>
<hr />
<h2 id="4-ai">4. AI 학습 요소</h2>
<h3 id="41-spec-selection">4.1 규격 선택 학습 (Spec Selection)</h3>
<pre><code>⚠️ 중요: 전문의약품(ETC)은 보험약가 고정!
- 30T든 300T든 1T당 가격 동일
- 단가 효율은 OTC/비급여에서만 의미 있음
학습 데이터:
- 약품별 과거 주문 규격 (30T, 100T, 300T, 500T)
- 각 규격 선택 시점의 재고/사용량
- 선택 결과 (남은 재고, 다음 주문까지 기간)
- 도매상별 규격 재고 현황
학습 목표:
- 사용량 대비 최적 규격 예측
- 재고 있는 규격 우선 선택
- 낭비 최소화 (유통기한 고려)
- 소분 vs 대용량 선호도 파악
</code></pre>
<p><strong>예시 시나리오:</strong><br />
| 필요량 | 가능 규격 | AI 선택 | 이유 |<br />
|--------|-----------|---------|------|<br />
| 280T | 30T(재고50), 100T(품절), 300T(재고10) | 30T x 10 | 100T 품절, 소분 선호 |<br />
| 800T | 30T(재고100), 300T(재고5) | 300T x 3 | 대량, 재고 충분 |<br />
| 50T | 30T(재고20), 100T(재고10) | 30T x 2 | 소량, 빠른 회전 |</p>
<h3 id="42-inventory-strategy">4.2 재고 전략 학습 (Inventory Strategy)</h3>
<pre><code>학습 데이터:
- 주문 시점의 재고 수준
- 재고 소진까지 남은 일수
- 주문 후 입고까지 리드타임
- 품절 발생 이력
학습 목표:
- 약사님의 재고 선호도 파악
- 타이트형: 최소 재고 유지 (현금 흐름 중시)
- 여유형: 안전 재고 확보 (품절 방지 중시)
</code></pre>
<p><strong>재고 전략 프로파일:</strong></p>
<pre><code class="language-python">class InventoryStrategy:
TIGHT = {
'safety_days': 2, # 안전 재고 2일치
'reorder_point': 0.8, # 80% 소진 시 주문
'order_coverage': 7 # 7일치 주문
}
MODERATE = {
'safety_days': 5,
'reorder_point': 0.6,
'order_coverage': 14
}
CONSERVATIVE = {
'safety_days': 10,
'reorder_point': 0.5,
'order_coverage': 30
}
</code></pre>
<h3 id="43-wholesaler-selection">4.3 도매상 선택 학습 (Wholesaler Selection)</h3>
<pre><code>학습 데이터:
- 도매상별 주문 빈도
- 도매상별 재고 상황
- 도매상별 배송 스케줄
- 월별 한도 사용량
- 분할 주문 패턴
학습 목표:
- 기본 도매상 선호도
- 상황별 대체 도매상
- 한도 고려한 분배
- 배송 시간 고려 (긴급 시)
</code></pre>
<p><strong>도매상 선택 로직:</strong></p>
<pre><code class="language-python">def select_wholesaler(drug_code, quantity, need_by_time=None):
&quot;&quot;&quot;
AI가 학습한 도매상 선택 로직
우선순위:
1. 재고 (있는 곳 우선)
2. 배송 (need_by_time 충족 가능한 곳)
3. 한도 (여유 있는 곳)
4. 선호도 (과거 패턴)
&quot;&quot;&quot;
candidates = []
for ws in ['geoyoung', 'sooin', 'baekje']:
score = 0
# 1. 재고 체크
if has_stock(ws, drug_code, quantity):
score += 100
else:
continue # 재고 없으면 제외
# 2. 배송 시간 체크
if need_by_time:
delivery = get_next_delivery(ws, need_by_time)
if delivery['can_deliver']:
score += 50
else:
score -= 30 # 감점
# 3. 한도 체크
limit_usage = get_limit_usage(ws)
if limit_usage &lt; 0.9:
score += 30
elif limit_usage &gt;= 1.0:
score -= 50 # 한도 초과
# 4. 학습된 선호도
score += ai_model.preference_score(ws, drug_code) * 20
candidates.append((ws, score))
return max(candidates, key=lambda x: x[1])[0]
</code></pre>
<h3 id="44">4.4 주문 타이밍 학습</h3>
<pre><code>학습 데이터:
- 하루 중 주문 시점 (오전/오후)
- 요일별 주문 패턴
- 배송 마감 시간 전 주문 여부
학습 목표:
- 최적 주문 시점 파악
- 배송 마감 놓치지 않기
- 분할 주문 (오전/오후) 패턴
</code></pre>
<hr />
<h2 id="5">5. 핵심 기능 설계</h2>
<h3 id="51">5.1 선주문 반영 시스템</h3>
<p><strong>목적</strong>: 같은 날 이미 주문한 품목 자동 차감</p>
<pre><code class="language-python">def calculate_order_qty(drug_code, usage_qty, current_stock):
# 오늘 &quot;실제로&quot; 주문 완료된 수량 조회
today_ordered = get_today_orders(drug_code)
# 필요량 = 사용량 - 현재고 - 선주문량
needed = usage_qty - current_stock - today_ordered
if needed &gt; 0:
return calculate_spec_qty(needed)
return 0
</code></pre>
<p><strong>⚠️ 핵심: 실제 주문만 카운트</strong></p>
<pre><code class="language-sql">SELECT SUM(oi.total_dose) as today_ordered
FROM order_items oi
JOIN orders o ON oi.order_id = o.id
WHERE oi.drug_code = ?
AND o.order_date = DATE('now')
AND o.is_dry_run = 0 -- dry_run 제외!
AND oi.status IN ('success', 'submitted')
</code></pre>
<h3 id="52">5.2 도매상 한도 관리</h3>
<p><strong>목적</strong>: 월별 거래 한도 설정 및 자동 분배</p>
<pre><code>[한도 도달 시 동작]
1. 90% 도달 → 경고 알림
2. 100% 도달 → 다른 도매상으로 자동 전환
3. 장바구니 단계에서 미리 분류
</code></pre>
<h3 id="53">5.3 배송 스케줄 기반 주문</h3>
<p><strong>목적</strong>: 주문 마감시간 + 배송 도착시간 분리 관리</p>
<pre><code>AI 판단 예시:
현재 오전 11시, &quot;오후 3시에 필요&quot;
→ 지오영 오전: 10시 마감 지남 ❌
→ 지오영 오후: 13시 마감 → 15:00 도착 (⚠️ 딱 맞음)
→ 수인: 13시 마감 → 14:30 도착 (✅ 여유)
→ 백제: 내일 도착 ❌
결론: 수인 추천 (14:30 도착, 30분 여유)
</code></pre>
<h3 id="54">5.4 주문 실패 시 재시도</h3>
<pre><code>시나리오 1: 재고 없음
- A도매상 재고 0 → B도매상 검색 → 재고 있으면 B로 주문
시나리오 2: 주문 오류
- A도매상 API 오류 → 3회 재시도 → 실패 시 B도매상
시나리오 3: 부분 성공
- 10개 품목 중 7개 성공, 3개 실패
- 실패한 3개 → B도매상으로 자동 재시도
[리포트]
- 최종 주문 결과 리포트
- 알림: &quot;A도매상 품절로 B도매상으로 변경됨&quot;
</code></pre>
<hr />
<h2 id="6">6. 데이터 모델</h2>
<h3 id="61">6.1 핵심 테이블 (기존)</h3>
<pre><code class="language-sql">-- 주문 컨텍스트 (AI 학습용)
CREATE TABLE order_context (
id INTEGER PRIMARY KEY,
order_item_id INTEGER,
-- 약품 정보
drug_code TEXT,
product_name TEXT,
-- 주문 시점 상황
stock_at_order INTEGER,
usage_1d INTEGER,
usage_7d INTEGER,
usage_30d INTEGER,
avg_daily_usage REAL,
-- 주문 결정
ordered_spec TEXT,
ordered_qty INTEGER,
wholesaler_id TEXT,
-- 선택지 정보 (AI 학습용)
available_specs JSON,
spec_stocks JSON,
selection_reason TEXT,
-- 예측 vs 실제
predicted_days_coverage REAL,
actual_days_to_reorder INTEGER,
-- 결과 평가
was_optimal BOOLEAN,
stockout_occurred BOOLEAN,
created_at TIMESTAMP
);
</code></pre>
<h3 id="62">6.2 신규 테이블</h3>
<pre><code class="language-sql">-- 도매상 한도 관리
CREATE TABLE wholesaler_limits (
id INTEGER PRIMARY KEY,
wholesaler_id TEXT NOT NULL,
monthly_limit INTEGER DEFAULT 0,
warning_threshold REAL DEFAULT 0.9,
priority INTEGER DEFAULT 1,
is_active INTEGER DEFAULT 1,
created_at TIMESTAMP,
FOREIGN KEY (wholesaler_id) REFERENCES wholesalers(id)
);
-- 배송 스케줄
CREATE TABLE delivery_schedules (
id INTEGER PRIMARY KEY,
wholesaler_id TEXT NOT NULL,
delivery_seq INTEGER NOT NULL,
delivery_name TEXT,
order_cutoff_time TEXT NOT NULL, -- 주문 마감 (HH:MM)
delivery_days_offset INTEGER DEFAULT 0, -- 0=당일, 1=익일
delivery_arrival_time TEXT NOT NULL, -- 도착 예정 (HH:MM)
weekdays TEXT, -- JSON [1,2,3,4,5]
is_active INTEGER DEFAULT 1,
UNIQUE(wholesaler_id, delivery_seq)
);
-- 실제 배송 스케줄 데이터
INSERT INTO delivery_schedules VALUES
('geoyoung', 1, '오전배송', '10:00', 0, '11:30'),
('geoyoung', 2, '오후배송', '13:00', 0, '15:00'),
('sooin', 1, '오후배송', '13:00', 0, '14:30'),
('baekje', 1, '익일배송', '16:00', 1, '15:00');
-- 월별 사용량 추적
CREATE TABLE wholesaler_monthly_usage (
id INTEGER PRIMARY KEY,
wholesaler_id TEXT NOT NULL,
year_month TEXT NOT NULL,
total_orders INTEGER DEFAULT 0,
total_amount INTEGER DEFAULT 0,
UNIQUE(wholesaler_id, year_month)
);
-- 주문 재시도 로그
CREATE TABLE order_fallback_log (
id INTEGER PRIMARY KEY,
order_item_id INTEGER NOT NULL,
original_wholesaler TEXT NOT NULL,
original_error TEXT,
fallback_wholesaler TEXT NOT NULL,
fallback_result TEXT,
created_at TIMESTAMP
);
</code></pre>
<h3 id="63">6.3 기존 테이블 확장</h3>
<pre><code class="language-sql">-- orders 테이블 확장
ALTER TABLE orders ADD COLUMN is_dry_run INTEGER DEFAULT 0;
-- order_items 테이블 확장
ALTER TABLE order_items ADD COLUMN fallback_from_wholesaler TEXT;
ALTER TABLE order_items ADD COLUMN prior_order_qty INTEGER DEFAULT 0;
</code></pre>
<hr />
<h2 id="7-api">7. API 설계</h2>
<h3 id="71-api">7.1 도매상 관리 API</h3>
<table>
<thead>
<tr>
<th>엔드포인트</th>
<th>메서드</th>
<th>기능</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/api/wholesaler/limits</code></td>
<td>GET</td>
<td>한도 조회</td>
</tr>
<tr>
<td><code>/api/wholesaler/limits/{id}</code></td>
<td>PUT</td>
<td>한도 설정</td>
</tr>
<tr>
<td><code>/api/wholesaler/schedules</code></td>
<td>GET</td>
<td>배송 스케줄</td>
</tr>
<tr>
<td><code>/api/wholesaler/can-deliver-by</code></td>
<td>POST</td>
<td>배송 가능 여부</td>
</tr>
</tbody>
</table>
<h3 id="72-api">7.2 주문 API</h3>
<table>
<thead>
<tr>
<th>엔드포인트</th>
<th>메서드</th>
<th>기능</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/api/order/today/{drug_code}</code></td>
<td>GET</td>
<td>오늘 주문량</td>
</tr>
<tr>
<td><code>/api/order/recommend-spec</code></td>
<td>POST</td>
<td>규격 추천</td>
</tr>
<tr>
<td><code>/api/order/create</code></td>
<td>POST</td>
<td>주문 생성</td>
</tr>
<tr>
<td><code>/api/order/submit</code></td>
<td>POST</td>
<td>주문 제출 (dry_run 지원)</td>
</tr>
<tr>
<td><code>/api/order/retry</code></td>
<td>POST</td>
<td>실패 재시도</td>
</tr>
</tbody>
</table>
<h3 id="73-ai-api">7.3 AI API</h3>
<table>
<thead>
<tr>
<th>엔드포인트</th>
<th>메서드</th>
<th>기능</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/api/ai/daily-analysis</code></td>
<td>GET</td>
<td>일일 분석</td>
</tr>
<tr>
<td><code>/api/ai/recommendations</code></td>
<td>GET</td>
<td>주문 추천</td>
</tr>
<tr>
<td><code>/api/ai/training-data</code></td>
<td>GET</td>
<td>학습 데이터</td>
</tr>
<tr>
<td><code>/api/ai/patterns/{drug_code}</code></td>
<td>GET</td>
<td>패턴 분석</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="8">8. 자동화 레벨</h2>
<h3 id="level-0">Level 0: 수동</h3>
<ul>
<li>AI 추천만 제공</li>
<li>모든 주문은 수동 실행</li>
</ul>
<h3 id="level-1">Level 1: 반자동</h3>
<ul>
<li>AI가 주문 계획 생성</li>
<li>약사님 승인 후 자동 실행</li>
<li>알림: 승인 요청</li>
</ul>
<h3 id="level-2">Level 2: 조건부 자동</h3>
<ul>
<li>신뢰도 높은 주문은 자동 실행</li>
<li>신뢰도 낮은 주문만 승인 요청</li>
<li>조건:</li>
<li>자주 주문하는 품목</li>
<li>금액 임계값 이하</li>
<li>긴급하지 않은 주문</li>
</ul>
<h3 id="level-3">Level 3: 완전 자동</h3>
<ul>
<li>모든 주문 자동 실행</li>
<li>이상 상황만 알림</li>
<li>약사님은 대시보드로 모니터링</li>
</ul>
<pre><code class="language-python">def should_auto_execute(order_plan):
level = settings.automation_level
if level == 0:
return False
if level == 1:
return False # 항상 승인 필요
if level == 2:
conditions = [
order_plan['confidence'] &gt; 0.9,
order_plan['estimated_cost'] &lt; 100000,
order_plan['drug_code'] in trusted_drugs,
order_plan['urgency'] != 'critical'
]
return all(conditions)
if level == 3:
return not is_anomaly(order_plan)
</code></pre>
<hr />
<h2 id="9">9. 알림 시스템</h2>
<h3 id="_7">알림 유형</h3>
<table>
<thead>
<tr>
<th>유형</th>
<th>조건</th>
<th>우선순위</th>
</tr>
</thead>
<tbody>
<tr>
<td>승인 요청</td>
<td>자동 실행 안 되는 주문</td>
<td>높음</td>
</tr>
<tr>
<td>주문 완료</td>
<td>자동 주문 실행됨</td>
<td>보통</td>
</tr>
<tr>
<td>한도 경고</td>
<td>90% 도달</td>
<td>높음</td>
</tr>
<tr>
<td>품절 긴급</td>
<td>재고 0, 당일 필요</td>
<td>긴급</td>
</tr>
<tr>
<td>배송 마감</td>
<td>마감 30분 전</td>
<td>높음</td>
</tr>
<tr>
<td>도매상 변경</td>
<td>품절로 다른 도매상</td>
<td>보통</td>
</tr>
</tbody>
</table>
<h3 id="_8">알림 예시</h3>
<pre><code>📦 주문 승인 요청
약품: 콩코르정 2.5mg
현재고: 45개 (3일치)
추천 주문: 300T x 2박스
도매상: 지오영 (점심배송 11:00 마감)
예상 금액: 72,000원
[승인] [수정] [거절]
</code></pre>
<pre><code>⚠️ 배송 마감 알림
지오영 오후배송 마감 30분 전!
현재 장바구니: 5품목
13:00까지 주문하지 않으면 다음 배송은 내일입니다.
[지금 주문] [나중에]
</code></pre>
<hr />
<h2 id="10">10. 개발 로드맵</h2>
<h3 id="phase-1-1">Phase 1: 핵심 기반 (1주차)</h3>
<ul>
<li>[x] 도매상 API 연동 (3개)</li>
<li>[x] 주문 DB 스키마</li>
<li>[x] dry_run 테스트 모드</li>
<li>[ ] 선주문 조회 API</li>
<li>[ ] 도매상 한도 테이블</li>
<li>[ ] 배송 스케줄 테이블</li>
</ul>
<h3 id="phase-2-2">Phase 2: 주문 자동화 (2주차)</h3>
<ul>
<li>[ ] 규격 추천 API</li>
<li>[ ] 한도 체크 로직</li>
<li>[ ] 주문 재시도 로직</li>
<li>[ ] 장바구니 동기화</li>
</ul>
<h3 id="phase-3-ui-2">Phase 3: UI 개선 (2주차)</h3>
<ul>
<li>[ ] 한도 대시보드</li>
<li>[ ] 주문 화면 (선주문 반영)</li>
<li>[ ] 배송 스케줄 표시</li>
</ul>
<h3 id="phase-4-ai-3">Phase 4: AI 학습 (3주차)</h3>
<ul>
<li>[ ] 피드백 루프 구현</li>
<li>[ ] 주문 평가 시스템</li>
<li>[ ] 패턴 학습 (규격, 도매상)</li>
<li>[ ] 수요 예측 (단순 이동평균)</li>
</ul>
<h3 id="phase-5-4">Phase 5: 완전 자동화 (4주차~)</h3>
<ul>
<li>[ ] Level 1 자동화</li>
<li>[ ] 알림 시스템 연동</li>
<li>[ ] Level 2 조건부 자동화</li>
<li>[ ] 모니터링 대시보드</li>
</ul>
<hr />
<h2 id="11-kpi">11. 성공 지표 (KPI)</h2>
<table>
<thead>
<tr>
<th>지표</th>
<th>현재</th>
<th>목표</th>
</tr>
</thead>
<tbody>
<tr>
<td>주문 소요 시간</td>
<td>30분/일</td>
<td>0분 (자동)</td>
</tr>
<tr>
<td>품절 발생률</td>
<td>5%</td>
<td>&lt;1%</td>
</tr>
<tr>
<td>재고 회전율</td>
<td>-</td>
<td>+20%</td>
</tr>
<tr>
<td>배송 마감 놓침</td>
<td>가끔</td>
<td>0회</td>
</tr>
<tr>
<td>주문 비용 절감</td>
<td>-</td>
<td>5-10% (OTC)</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="_9">📚 참고 문서</h2>
<ul>
<li>어제 작성 (AI 비전/모델): <code>docs/AI_ERP_AUTO_ORDER_SYSTEM.md</code></li>
<li>오늘 작성 (API/DB 상세): <code>docs/자동발주시스템_고도화_기획서_v2.md</code></li>
<li>도매상 API 분석: <code>docs/GEOYOUNG_API_REVERSE_ENGINEERING.md</code></li>
<li>Rx 사용량 가이드: <code>docs/RX_USAGE_GEOYOUNG_GUIDE.md</code></li>
</ul>
<hr />
<blockquote>
<p>🐉 <strong>용림</strong>: 이 문서는 AI_ERP_AUTO_ORDER_SYSTEM.md(비전/AI모델)와 <br />
자동발주시스템_고도화_기획서_v2.md(API/DB상세)를 통합한 마스터 기획서입니다.</p>
</blockquote>
</body>
</html>