init: dbx-corm 프로젝트 Docker 기반 마이그레이션
주요 변경사항: - DB 접속정보를 환경변수(.env) 방식으로 분리 - orderlist_app → orderlist_db 변경 (main.py, routers/returns.py) - 네이버/카페24 API 키도 환경변수로 분리 - Dockerfile / docker-compose.yml 작성 (외부 PostgreSQL 네트워크 연결) - .gitignore: 민감파일(.env, cafe24_tokens.json, manual-ordering.json) 제외 - 불필요 파일 정리 (venv, __pycache__, 일회성 스크립트, xlsx 등) - README.md / .env.example / push_to_gitea.bat 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
import os
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
import datetime
|
||||
|
||||
router = APIRouter(prefix="/api/return", tags=["Returns"])
|
||||
|
||||
# 환경변수 기반 DB 설정
|
||||
_PG_HOST = os.getenv('POSTGRES_HOST', 'postgres-db')
|
||||
_PG_PORT = int(os.getenv('POSTGRES_PORT', '5432'))
|
||||
_PG_USER = os.getenv('POSTGRES_USER', 'king')
|
||||
_PG_PASSWORD = os.getenv('POSTGRES_PASSWORD', '')
|
||||
|
||||
RETURN_DB_CONFIG = {
|
||||
'host': _PG_HOST,
|
||||
'port': _PG_PORT,
|
||||
'dbname': os.getenv('RETURN_DB', 'return_db'),
|
||||
'user': _PG_USER,
|
||||
'password': _PG_PASSWORD,
|
||||
}
|
||||
|
||||
def get_return_db_conn():
|
||||
return psycopg2.connect(**RETURN_DB_CONFIG, cursor_factory=psycopg2.extras.DictCursor)
|
||||
|
||||
|
||||
ORDER_DB_CONFIG = {
|
||||
'host': _PG_HOST,
|
||||
'port': _PG_PORT,
|
||||
'dbname': os.getenv('ORDERLIST_DB', 'orderlist_db'),
|
||||
'user': _PG_USER,
|
||||
'password': _PG_PASSWORD,
|
||||
}
|
||||
|
||||
def get_order_db_conn():
|
||||
return psycopg2.connect(**ORDER_DB_CONFIG, cursor_factory=psycopg2.extras.DictCursor)
|
||||
|
||||
class ReturnRequest(BaseModel):
|
||||
id: Optional[int] = None
|
||||
request_date: str
|
||||
request_type: str
|
||||
receiver_name: str
|
||||
address: str
|
||||
phone: str
|
||||
tracking_no: str
|
||||
mall: str
|
||||
remarks: str
|
||||
|
||||
class ReturnReceive(BaseModel):
|
||||
id: Optional[int] = None
|
||||
receive_date: str
|
||||
tracking_no: str
|
||||
mall: str
|
||||
receiver_name: str
|
||||
phone: str
|
||||
address: Optional[str] = ''
|
||||
remarks: str
|
||||
receive_status: str
|
||||
|
||||
class BulkReturnReceive(BaseModel):
|
||||
tracking_numbers: List[str]
|
||||
|
||||
@router.get("/lookup")
|
||||
async def return_lookup(tracking_no: str):
|
||||
try:
|
||||
conn = get_order_db_conn()
|
||||
cur = conn.cursor()
|
||||
# 주문일시(order_date 또는 upload_date) 기준 가장 최신 1건 조회
|
||||
cur.execute("""
|
||||
SELECT vendor, recipient_name, recipient_phone, recipient_mobile, address
|
||||
FROM orders
|
||||
WHERE tracking_number = %s
|
||||
ORDER BY order_date DESC LIMIT 1
|
||||
""", (tracking_no,))
|
||||
row = cur.fetchone()
|
||||
conn.close()
|
||||
|
||||
if row:
|
||||
return {"success": True, "data": dict(row)}
|
||||
return {"success": False, "message": "송장번호를 찾을 수 없습니다."}
|
||||
except Exception as e:
|
||||
print(f"Lookup error: {e}")
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
@router.post("/request")
|
||||
async def create_return_request(req: ReturnRequest):
|
||||
try:
|
||||
conn = get_return_db_conn()
|
||||
cur = conn.cursor()
|
||||
if req.id:
|
||||
cur.execute("""
|
||||
UPDATE return_requests
|
||||
SET request_date=%s, request_type=%s, receiver_name=%s, address=%s, phone=%s, tracking_no=%s, mall=%s, remarks=%s
|
||||
WHERE id=%s
|
||||
""", (req.request_date, req.request_type, req.receiver_name, req.address, req.phone, req.tracking_no, req.mall, req.remarks, req.id))
|
||||
else:
|
||||
cur.execute("""
|
||||
INSERT INTO return_requests
|
||||
(request_date, request_type, receiver_name, address, phone, tracking_no, mall, remarks)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||
""", (req.request_date, req.request_type, req.receiver_name, req.address, req.phone, req.tracking_no, req.mall, req.remarks))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
print(f"Request error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/request")
|
||||
async def get_return_requests(tracking_no: Optional[str] = None):
|
||||
try:
|
||||
conn = get_return_db_conn()
|
||||
cur = conn.cursor()
|
||||
if tracking_no:
|
||||
cur.execute("SELECT * FROM return_requests WHERE tracking_no = %s ORDER BY created_at DESC LIMIT 1", (tracking_no,))
|
||||
else:
|
||||
cur.execute("SELECT * FROM return_requests ORDER BY id DESC LIMIT 50")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return {"success": True, "data": [dict(r) for r in rows]}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
@router.post("/receive/bulk")
|
||||
async def create_return_receive_bulk(req: BulkReturnReceive):
|
||||
try:
|
||||
from datetime import datetime
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
|
||||
conn_return = get_return_db_conn()
|
||||
cur_return = conn_return.cursor()
|
||||
|
||||
conn_order = get_order_db_conn()
|
||||
cur_order = conn_order.cursor()
|
||||
|
||||
success_count = 0
|
||||
|
||||
for tracking_no in req.tracking_numbers:
|
||||
tracking_no = tracking_no.strip()
|
||||
if not tracking_no:
|
||||
continue
|
||||
|
||||
mall = ""
|
||||
receiver_name = ""
|
||||
phone = ""
|
||||
address = ""
|
||||
|
||||
# 1. Check return_requests
|
||||
cur_return.execute("SELECT mall, receiver_name, phone, address, request_type, remarks FROM return_requests WHERE tracking_no = %s", (tracking_no,))
|
||||
req_row = cur_return.fetchone()
|
||||
if req_row:
|
||||
mall = req_row['mall'] or ""
|
||||
receiver_name = req_row['receiver_name'] or ""
|
||||
phone = req_row['phone'] or ""
|
||||
address = req_row['address'] or ""
|
||||
req_type = req_row['request_type'] or ""
|
||||
req_remarks = req_row['remarks'] or ""
|
||||
if req_remarks:
|
||||
remarks = f"{req_type}/{req_remarks}"
|
||||
else:
|
||||
remarks = req_type
|
||||
else:
|
||||
remarks = "고객이 반품"
|
||||
# 2. Check orders
|
||||
cur_order.execute("""
|
||||
SELECT vendor, recipient_name, recipient_phone, recipient_mobile, address
|
||||
FROM orders
|
||||
WHERE tracking_number = %s
|
||||
ORDER BY order_date DESC LIMIT 1
|
||||
""", (tracking_no,))
|
||||
ord_row = cur_order.fetchone()
|
||||
if ord_row:
|
||||
mall = ord_row['vendor'] or ""
|
||||
receiver_name = ord_row['recipient_name'] or ""
|
||||
phone = ord_row['recipient_phone'] or ord_row['recipient_mobile'] or ""
|
||||
address = ord_row['address'] or ""
|
||||
|
||||
# Insert into return_receivings
|
||||
cur_return.execute("""
|
||||
INSERT INTO return_receivings
|
||||
(receive_date, tracking_no, mall, receiver_name, phone, address, remarks, receive_status)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||
""", (today, tracking_no, mall, receiver_name, phone, address, remarks, "환불 대기"))
|
||||
success_count += 1
|
||||
|
||||
conn_return.commit()
|
||||
conn_return.close()
|
||||
conn_order.close()
|
||||
|
||||
return {"success": True, "count": success_count}
|
||||
except Exception as e:
|
||||
print(f"Bulk insert error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/receive")
|
||||
async def create_return_receive(req: ReturnReceive):
|
||||
try:
|
||||
conn = get_return_db_conn()
|
||||
cur = conn.cursor()
|
||||
|
||||
if not req.id:
|
||||
cur.execute("SELECT request_type, remarks FROM return_requests WHERE tracking_no = %s", (req.tracking_no,))
|
||||
req_row = cur.fetchone()
|
||||
if not req_row:
|
||||
if req.remarks:
|
||||
if "고객이 반품" not in req.remarks:
|
||||
req.remarks = "고객이 반품 / " + req.remarks
|
||||
else:
|
||||
req.remarks = "고객이 반품"
|
||||
else:
|
||||
req_type = req_row['request_type'] or ""
|
||||
req_remarks = req_row['remarks'] or ""
|
||||
auto_remark = f"{req_type}/{req_remarks}" if req_remarks else req_type
|
||||
if not req.remarks:
|
||||
req.remarks = auto_remark
|
||||
elif auto_remark not in req.remarks:
|
||||
req.remarks = f"{auto_remark} / {req.remarks}"
|
||||
|
||||
if req.id:
|
||||
# Update existing record
|
||||
cur.execute("""
|
||||
UPDATE return_receivings
|
||||
SET receive_date=%s, tracking_no=%s, mall=%s, receiver_name=%s, phone=%s, address=%s, remarks=%s, receive_status=%s
|
||||
WHERE id=%s
|
||||
""", (req.receive_date, req.tracking_no, req.mall, req.receiver_name, req.phone, req.address, req.remarks, req.receive_status, req.id))
|
||||
else:
|
||||
# Insert new record
|
||||
cur.execute("""
|
||||
INSERT INTO return_receivings
|
||||
(receive_date, tracking_no, mall, receiver_name, phone, address, remarks, receive_status)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||
""", (req.receive_date, req.tracking_no, req.mall, req.receiver_name, req.phone, req.address, req.remarks, req.receive_status))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/receive")
|
||||
async def get_return_receives():
|
||||
try:
|
||||
conn = get_return_db_conn()
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT * FROM return_receivings ORDER BY receive_date DESC, mall ASC, id DESC LIMIT 50")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return {"success": True, "data": [dict(r) for r in rows]}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
@router.delete("/request/{item_id}")
|
||||
async def delete_return_request(item_id: int):
|
||||
try:
|
||||
conn = get_return_db_conn()
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM return_requests WHERE id = %s", (item_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.delete("/receive/{item_id}")
|
||||
async def delete_return_receive(item_id: int):
|
||||
try:
|
||||
conn = get_return_db_conn()
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM return_receivings WHERE id = %s", (item_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
Reference in New Issue
Block a user