o2o-negosium-original/negodata/backend/common/anchoring/reader.py

58 lines
2.9 KiB
Python

"""현재 앵커링 값 조회(읽기 경로) — negodata 이식판.
원본 reader(schedules/anchoring)는 Redis 캐시를 먼저 보지만, 이식판은 DB 직조회 한 문장만 쓴다
(2026-07-03 단순화 결정 — 조회가 견적 생성 시에만 일어나 캐시가 불필요, Redis 의존 제거).
칸별 최신 조정 rate 는 모듈 소유 뷰 `anchoring.current_values` 가 제공하고,
조정 이력이 없는 칸은 결과에 없으므로 호출측이 정적 테이블 시작값으로 폴백한다.
견적 생성이 앵커 조회 때문에 실패해서는 안 된다(인수인계.md §1 규칙 6) — anchoring 스키마
미적용 환경을 포함해 어떤 실패도 밖으로 던지지 않고 빈 결과(전량 정적 폴백) + WARN 으로 처리한다.
"""
from sqlalchemy import column, select, table
from sqlalchemy.ext.asyncio import AsyncSession
from common.anchoring.constants import ANCHORING_VALUE_MAX, ANCHORING_VALUE_MIN, SAMPLEABLE_SUPPLIER_TYPES
from common.logger import LOG
# 모듈 소유 DDL(postgres-init/05-anchoring-schema.sql)의 조회용 뷰 — negodata 는 ORM 모델 없이 읽기만 한다.
_current_values = table(
"current_values",
column("company_id"),
column("price_range_index"),
column("anchoring_value"),
column("supplier_type"),
schema="anchoring",
)
async def fetch_current_values(db: AsyncSession, company_ids: list, supplier_types: list[int]) -> dict:
"""칸별 현재 앵커링 값 일괄 조회. {(company_id, supplier_type, price_range_index): rate‰} 반환.
supplier_type 은 세션의 (item_id, supplier_id) 매핑에서 온 supply_type 이다.
조정 이력이 없는 칸은 결과에 없다(호출측 정적 폴백). 조회 실패 시 빈 dict."""
valid_types = sorted({int(t) for t in supplier_types if t in SAMPLEABLE_SUPPLIER_TYPES})
if not company_ids or not valid_types:
return {}
try:
stmt = select(
_current_values.c.company_id,
_current_values.c.supplier_type,
_current_values.c.price_range_index,
_current_values.c.anchoring_value,
).where(
_current_values.c.company_id.in_(company_ids),
_current_values.c.supplier_type.in_(valid_types),
)
rows = (await db.execute(stmt)).all()
except Exception as ex:
LOG.w(f"[앵커링] current_values 조회 실패 — 전량 정적 테이블 폴백: {ex}")
return {}
out = {}
for company_id, supplier_type, price_range_index, rate in rows:
if not ANCHORING_VALUE_MIN <= rate <= ANCHORING_VALUE_MAX: # 범위 밖 값은 오염 방어 — 버리고 정적 폴백
LOG.w(f"[앵커링] rate 범위 밖 — 무시(정적 폴백): company={company_id} type={supplier_type} bracket={price_range_index} rate={rate}")
continue
out[(company_id, supplier_type, price_range_index)] = rate
return out