o2o-negosium-original/lps
민헌 5906acc48a feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프
같은 상품 반복 검색 시 최저가를 스냅샷으로 적재 → 네이버/쿠팡/최종 3개 선 그래프.
배치 아님(조회된 상품만, 실제 검색 시각에 기록) — 트래픽/리소스 절약.

- price_history 테이블: product_code·triggered_at(X축)·naver/coupang/final 최저가+상세·outcome
- crud/price_history: record() + list_by_product(시각 오름차순)
- handler: AI 매칭 후 소스별 min + 전체 min 스냅샷 기록(_price_snapshot).
  found/not_found 기록, 네거티브 캐시 히트·기술실패는 미기록
- API: GET /v1/lps/products/{product_code}/history → 그래프 데이터(시각 오름차순)
- tests: 스냅샷 계산/기록·조회/핸들러 기록규칙/API → 전체 54/54

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:13:28 +09:00
..
common feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00
config refactor(lps): 환경설정 TOML 단일화 — .env 제거, 시크릿도 config.local.toml 로 통합 2026-07-09 10:06:00 +09:00
crud feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00
router feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00
services feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00
tests feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00
worker feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00
.dockerignore feat(lps): 인터넷 최저가 검색 솔루션 프레임워크 골격 추가 2026-07-08 15:18:27 +09:00
conftest.py feat(lps): 인터넷 최저가 검색 솔루션 프레임워크 골격 추가 2026-07-08 15:18:27 +09:00
Dockerfile feat(lps): 작업 큐 엔진 — PostgreSQL 원자적 claim + lease + dead-letter 2026-07-08 16:37:32 +09:00
pytest.ini feat(lps): 인터넷 최저가 검색 솔루션 프레임워크 골격 추가 2026-07-08 15:18:27 +09:00
README.md refactor(lps): 환경설정 TOML 단일화 — .env 제거, 시크릿도 config.local.toml 로 통합 2026-07-09 10:06:00 +09:00
requirements.txt refactor(lps): 환경설정 TOML 단일화 — .env 제거, 시크릿도 config.local.toml 로 통합 2026-07-09 10:06:00 +09:00
run_local_server.sh feat(lps): 작업 큐 엔진 — PostgreSQL 원자적 claim + lease + dead-letter 2026-07-08 16:37:32 +09:00
web_main.py feat(lps): 작업 큐 엔진 — PostgreSQL 원자적 claim + lease + dead-letter 2026-07-08 16:37:32 +09:00
worker_main.py feat(lps): 최저가 이력(price_history) — 트리거 기반 시계열 그래프 2026-07-09 11:13:28 +09:00

LPS (Lowest Price Search)

인터넷 최저가를 찾는 솔루션. 프레임워크는 backend 와 동일하게 구성한 골격이며, 구체적인 도메인 로직(크롤링/오픈API 연동/최저가 산정 등)은 아직 정해지지 않았다.

스택 / 아키텍처 (backend 미러링)

  • FastAPI 앱 (router/router.py) + web_main.py 부트스트랩
  • 설정: config/ — TOML 로더(config.local.toml) + pydantic 모델, APP_ENV(기본 local)
  • DB: common/database/db_session_manager.py — 논리 DB × Read/Write 엔진, service→람다 위임 패턴
    • 엔진은 lazy 생성이라 DB 없이도 부팅/healthz 동작한다. 테이블·crud 가 생기면 그때 실제 접속.
  • 공통 응답 규약: common/models/gmodel.py — Res_WebPacketProtocol.result(성공/코드/설명)
  • 결과 코드: common/enums.py — ErrorType, DBType, DBWRType
  • 계층 컨벤션: router(컨트롤러) → services(비즈니스) → crud(DB 접근)

폴더 구조

lps/
├── web_main.py                     # 진입점
├── requirements.txt / Dockerfile / .dockerignore
├── run_local_server.sh             # 로컬 실행(대화형), 포트 9600
├── pytest.ini / conftest.py
├── config/
│   ├── config_loader.py / config_models.py / server_configs.py
│   └── config.local.toml.example   # cp 해서 config.local.toml 로 사용(시크릿, 미커밋)
├── common/
│   ├── enums.py / logger.py / singleton.py
│   ├── utils/gtime.py
│   ├── models/gmodel.py            # 프로토콜 base
│   └── database/{db_session_manager.py, model/models.py(MAIN_BASE)}
├── router/
│   ├── router.py                   # app + /healthz (도메인 라우터 미등록)
│   └── v1/validator/dependencies.py# RemoveNoneResponse
├── services/                       # (비어있음) 도메인 서비스 추가 위치
├── crud/                           # (비어있음) DB 접근 계층 추가 위치
└── tests/test_health.py            # 스모크 테스트

로컬 실행

cp config/config.local.toml.example config/config.local.toml   # 설정+시크릿 전부(포트/DB/API 키)
./run_local_server.sh                                          # → http://localhost:9600/docs

config.local.toml 한 파일에 설정과 시크릿(API 키)을 통합 관리(git 미추적). 배포는 이 파일을 마운트하거나, 환경별로 바뀌는 값만 env override(DB_HOST 등). 워커는 별도 프로세스: python worker_main.py

테스트

python -m pytest            # tests/ (기본 healthz 스모크)

포트

  • backend 9300 / negodata 9400 / agent 9500 과 겹치지 않도록 LPS 는 9600 사용.

새 도메인 추가 순서 (backend 컨벤션)

  1. common/database/model/models.py 에 테이블 정의(+ DBType)
  2. crud/<domain>_crud.py (ABC 인터페이스 + 구현)
  3. services/<domain>_service.py (비즈니스 로직)
  4. router/v1/<domain>/{protocol.py, <domain>.py} 작성 후 router/router.py 에서 include_router