#!/usr/bin/env python3 """ PharmQ SaaS 구독 서비스 데이터베이스 테이블 생성 스크립트 """ import sqlite3 import os from datetime import datetime def create_subscription_tables(): """구독 서비스 관련 테이블 생성""" # FARMQ 데이터베이스 연결 db_path = '/srv/headscale-setup/farmq-admin/farmq.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() print("🚀 PharmQ SaaS 구독 서비스 테이블 생성 중...") try: # 1. service_products - 서비스 상품 마스터 print("📦 service_products 테이블 생성 중...") cursor.execute(''' CREATE TABLE IF NOT EXISTS service_products ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_code VARCHAR(20) UNIQUE NOT NULL, product_name VARCHAR(100) NOT NULL, description TEXT, monthly_price DECIMAL(10,2) NOT NULL, setup_fee DECIMAL(10,2) DEFAULT 0, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # 2. pharmacy_subscriptions - 약국별 구독 현황 print("🏥 pharmacy_subscriptions 테이블 생성 중...") cursor.execute(''' CREATE TABLE IF NOT EXISTS pharmacy_subscriptions ( id INTEGER PRIMARY KEY AUTOINCREMENT, pharmacy_id INTEGER NOT NULL, product_id INTEGER NOT NULL, subscription_status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', start_date DATE NOT NULL, end_date DATE, next_billing_date DATE, monthly_fee DECIMAL(10,2) NOT NULL, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (pharmacy_id) REFERENCES pharmacies(id), FOREIGN KEY (product_id) REFERENCES service_products(id), UNIQUE(pharmacy_id, product_id) ) ''') # 3. subscription_usage_logs - 서비스 이용 로그 print("📊 subscription_usage_logs 테이블 생성 중...") cursor.execute(''' CREATE TABLE IF NOT EXISTS subscription_usage_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, subscription_id INTEGER NOT NULL, usage_type VARCHAR(50) NOT NULL, usage_amount INTEGER DEFAULT 1, usage_date DATE NOT NULL, metadata TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (subscription_id) REFERENCES pharmacy_subscriptions(id) ) ''') # 4. billing_history - 결제 이력 print("💳 billing_history 테이블 생성 중...") cursor.execute(''' CREATE TABLE IF NOT EXISTS billing_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, subscription_id INTEGER NOT NULL, billing_period_start DATE NOT NULL, billing_period_end DATE NOT NULL, amount DECIMAL(10,2) NOT NULL, billing_status VARCHAR(20) NOT NULL DEFAULT 'PENDING', billing_date DATE, payment_method VARCHAR(50), invoice_number VARCHAR(100), notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (subscription_id) REFERENCES pharmacy_subscriptions(id) ) ''') # 인덱스 생성 print("🔍 인덱스 생성 중...") indexes = [ "CREATE INDEX IF NOT EXISTS idx_pharmacy_subscriptions_pharmacy_id ON pharmacy_subscriptions(pharmacy_id)", "CREATE INDEX IF NOT EXISTS idx_pharmacy_subscriptions_product_id ON pharmacy_subscriptions(product_id)", "CREATE INDEX IF NOT EXISTS idx_pharmacy_subscriptions_status ON pharmacy_subscriptions(subscription_status)", "CREATE INDEX IF NOT EXISTS idx_usage_logs_subscription_id ON subscription_usage_logs(subscription_id)", "CREATE INDEX IF NOT EXISTS idx_usage_logs_date ON subscription_usage_logs(usage_date)", "CREATE INDEX IF NOT EXISTS idx_billing_subscription_id ON billing_history(subscription_id)", "CREATE INDEX IF NOT EXISTS idx_billing_status ON billing_history(billing_status)" ] for index_sql in indexes: cursor.execute(index_sql) conn.commit() print("✅ 모든 테이블이 성공적으로 생성되었습니다!") except Exception as e: print(f"❌ 오류 발생: {e}") conn.rollback() raise finally: conn.close() def insert_sample_service_products(): """기본 서비스 상품 데이터 삽입""" db_path = '/srv/headscale-setup/farmq-admin/farmq.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() print("📦 기본 서비스 상품 데이터 삽입 중...") # 기본 서비스 상품 정의 products = [ { 'product_code': 'CLOUD_PC', 'product_name': '클라우드 PC', 'description': 'Proxmox 기반 가상 데스크톱 서비스. 언제 어디서나 안전한 클라우드 환경에서 업무 처리 가능.', 'monthly_price': 60000.00, 'setup_fee': 0.00 }, { 'product_code': 'AI_CCTV', 'product_name': 'AI CCTV', 'description': '인공지능 기반 보안 모니터링 시스템. 실시간 이상 상황 탐지 및 알림 서비스.', 'monthly_price': 80000.00, 'setup_fee': 50000.00 }, { 'product_code': 'CRM', 'product_name': 'CRM 시스템', 'description': '고객 관계 관리 및 매출 분석 도구. 고객 데이터 통합 관리 및 마케팅 자동화.', 'monthly_price': 40000.00, 'setup_fee': 0.00 } ] try: for product in products: # 중복 확인 cursor.execute("SELECT id FROM service_products WHERE product_code = ?", (product['product_code'],)) if cursor.fetchone(): print(f"⚠️ {product['product_name']} 상품이 이미 존재합니다.") continue cursor.execute(''' INSERT INTO service_products (product_code, product_name, description, monthly_price, setup_fee) VALUES (?, ?, ?, ?, ?) ''', ( product['product_code'], product['product_name'], product['description'], product['monthly_price'], product['setup_fee'] )) print(f"✅ {product['product_name']} 상품 추가됨 (월 {product['monthly_price']:,.0f}원)") conn.commit() print("✅ 기본 서비스 상품 데이터 삽입 완료!") except Exception as e: print(f"❌ 서비스 상품 데이터 삽입 오류: {e}") conn.rollback() raise finally: conn.close() def create_sample_subscriptions(): """샘플 구독 데이터 생성 (기존 약국 데이터 기반)""" db_path = '/srv/headscale-setup/farmq-admin/farmq.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() print("🏥 샘플 구독 데이터 생성 중...") try: # 기존 약국 ID 조회 cursor.execute("SELECT id, pharmacy_name FROM pharmacies LIMIT 10") pharmacies = cursor.fetchall() # 서비스 상품 ID 조회 cursor.execute("SELECT id, product_code, monthly_price FROM service_products") products = cursor.fetchall() if not pharmacies: print("⚠️ 약국 데이터가 없습니다. 구독 데이터를 생성할 수 없습니다.") return if not products: print("⚠️ 서비스 상품 데이터가 없습니다.") return import random from datetime import date, timedelta subscription_count = 0 for pharmacy_id, pharmacy_name in pharmacies: # 각 약국마다 랜덤하게 1-3개의 서비스 구독 num_subscriptions = random.randint(1, 3) selected_products = random.sample(products, num_subscriptions) for product_id, product_code, monthly_price in selected_products: # 중복 구독 확인 cursor.execute(''' SELECT id FROM pharmacy_subscriptions WHERE pharmacy_id = ? AND product_id = ? ''', (pharmacy_id, product_id)) if cursor.fetchone(): continue # 이미 구독 중 # 구독 시작일 (최근 1년 내 랜덤) start_date = date.today() - timedelta(days=random.randint(0, 365)) # 다음 결제일 (시작일로부터 월 단위) next_billing = start_date + timedelta(days=30) if next_billing < date.today(): # 과거 날짜면 현재 날짜 기준으로 조정 days_since_start = (date.today() - start_date).days months_passed = days_since_start // 30 next_billing = start_date + timedelta(days=(months_passed + 1) * 30) cursor.execute(''' INSERT INTO pharmacy_subscriptions (pharmacy_id, product_id, subscription_status, start_date, next_billing_date, monthly_fee, notes) VALUES (?, ?, ?, ?, ?, ?, ?) ''', ( pharmacy_id, product_id, 'ACTIVE', start_date.isoformat(), next_billing.isoformat(), monthly_price, f'{pharmacy_name}의 {product_code} 서비스 구독' )) subscription_count += 1 print(f"✅ {pharmacy_name} → {product_code} 구독 추가 (월 {monthly_price:,.0f}원)") conn.commit() print(f"✅ 총 {subscription_count}개의 샘플 구독 데이터가 생성되었습니다!") except Exception as e: print(f"❌ 샘플 구독 데이터 생성 오류: {e}") conn.rollback() raise finally: conn.close() def show_subscription_summary(): """구독 현황 요약 출력""" db_path = '/srv/headscale-setup/farmq-admin/farmq.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() print("\n" + "="*60) print("📊 PharmQ SaaS 구독 현황 요약") print("="*60) try: # 전체 구독 통계 cursor.execute(''' SELECT sp.product_name, COUNT(*) as subscription_count, SUM(ps.monthly_fee) as total_monthly_revenue FROM pharmacy_subscriptions ps JOIN service_products sp ON ps.product_id = sp.id WHERE ps.subscription_status = 'ACTIVE' GROUP BY sp.product_name ORDER BY total_monthly_revenue DESC ''') results = cursor.fetchall() total_revenue = 0 for product_name, count, revenue in results: print(f"🔹 {product_name}: {count}개 약국 구독 (월 {revenue:,.0f}원)") total_revenue += revenue print(f"\n💰 총 월 매출: {total_revenue:,.0f}원") # 약국별 구독 수 cursor.execute(''' SELECT COUNT(DISTINCT pharmacy_id) as subscribed_pharmacies FROM pharmacy_subscriptions WHERE subscription_status = 'ACTIVE' ''') subscribed_pharmacies = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM pharmacies") total_pharmacies = cursor.fetchone()[0] print(f"🏥 구독 약국: {subscribed_pharmacies}/{total_pharmacies}개 ({subscribed_pharmacies/total_pharmacies*100:.1f}%)") except Exception as e: print(f"❌ 요약 정보 조회 오류: {e}") finally: conn.close() print("="*60) if __name__ == "__main__": print("🚀 PharmQ SaaS 구독 서비스 데이터베이스 초기화") print("-" * 60) try: # 1. 테이블 생성 create_subscription_tables() print() # 2. 기본 서비스 상품 삽입 insert_sample_service_products() print() # 3. 샘플 구독 데이터 생성 create_sample_subscriptions() print() # 4. 구독 현황 요약 show_subscription_summary() print("\n🎉 PharmQ SaaS 구독 서비스 데이터베이스 초기화 완료!") except Exception as e: print(f"\n💥 초기화 실패: {e}") exit(1)