[fix] negodata: 협상카드 성공률에 회사 스코프 누락 — 타사 견적 이력이 섞이던 문제
card_success_map 이 quotations 조인 없이 DB 전체 chats 를 카드별로 집계해, 다른 회사 견적에서 쓰인 이력이 내 카드 목록의 used_count/success_rate 로 잡혔다. 통계 화면 '협상카드 성공률 TOP 5' 와 견적 생성 시 카드 추천 순위가 모두 이 값을 쓴다. - quotations 조인 + 작성자 회사(users.company_id) 스코프 추가 - 소프트 삭제분 제외(sessions.deleted / quotations.deleted 필터 누락분 보강)
This commit is contained in:
parent
8112394aef
commit
69e4240039
@ -5,7 +5,7 @@ from sqlalchemy import select, func, and_, or_, update, case
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from common.database.db_session_manager import DB_SESSION_MNG
|
from common.database.db_session_manager import DB_SESSION_MNG
|
||||||
from common.database.model.models import users, chats, sessions
|
from common.database.model.models import users, chats, sessions, quotations
|
||||||
from common.enums import ErrorType, SessionStatus
|
from common.enums import ErrorType, SessionStatus
|
||||||
from common.logger import LOG
|
from common.logger import LOG
|
||||||
from common.utils.gtime import GTime
|
from common.utils.gtime import GTime
|
||||||
@ -38,7 +38,7 @@ class ICardCRUD(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def card_success_map(self, cdb: AsyncSession) -> Tuple[ErrorType, dict]:
|
async def card_success_map(self, cdb: AsyncSession, company_id) -> Tuple[ErrorType, dict]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@ -83,9 +83,10 @@ class CardCRUD(ICardCRUD):
|
|||||||
LOG.e_no_callstack(ex)
|
LOG.e_no_callstack(ex)
|
||||||
return ErrorType.DB_RUN_FAILED, [], 0
|
return ErrorType.DB_RUN_FAILED, [], 0
|
||||||
|
|
||||||
async def card_success_map(self, cdb: AsyncSession) -> Tuple[ErrorType, dict]:
|
async def card_success_map(self, cdb: AsyncSession, company_id) -> Tuple[ErrorType, dict]:
|
||||||
# 카드별 성공률: 카드 사용(card_used_yn) 채팅이 속한 세션의 타결(DONE) 비율.
|
# 카드별 성공률: 카드 사용(card_used_yn) 채팅이 속한 세션의 타결(DONE) 비율.
|
||||||
# {card_id(UUID): (used_sessions, won_sessions)}. 성공=세션 DONE(협상완료).
|
# {card_id(UUID): (used_sessions, won_sessions)}. 성공=세션 DONE(협상완료).
|
||||||
|
# 집계 범위는 내 회사 견적에서 쓰인 이력만 — quotations 까지 조인해 작성자 회사로 좁힌다.
|
||||||
try:
|
try:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(
|
select(
|
||||||
@ -94,7 +95,15 @@ class CardCRUD(ICardCRUD):
|
|||||||
func.count(func.distinct(case((sessions.status == SessionStatus.DONE.value, chats.session_id)))).label("won"),
|
func.count(func.distinct(case((sessions.status == SessionStatus.DONE.value, chats.session_id)))).label("won"),
|
||||||
)
|
)
|
||||||
.join(sessions, sessions.session_id == chats.session_id)
|
.join(sessions, sessions.session_id == chats.session_id)
|
||||||
.where(chats.card_used_yn == True, chats.card_id.isnot(None), chats.deleted == False) # noqa: E712
|
.join(quotations, quotations.qt_id == sessions.quotation_id)
|
||||||
|
.where(
|
||||||
|
chats.card_used_yn == True, # noqa: E712
|
||||||
|
chats.card_id.isnot(None),
|
||||||
|
chats.deleted == False, # noqa: E712
|
||||||
|
sessions.deleted == False, # noqa: E712
|
||||||
|
quotations.deleted == False, # noqa: E712
|
||||||
|
quotations.user_id.in_(select(users.user_id).where(users.company_id == company_id)),
|
||||||
|
)
|
||||||
.group_by(chats.card_id)
|
.group_by(chats.card_id)
|
||||||
)
|
)
|
||||||
err, rows = await DB_SESSION_MNG.execute(cdb, stmt)
|
err, rows = await DB_SESSION_MNG.execute(cdb, stmt)
|
||||||
|
|||||||
@ -142,7 +142,7 @@ class CardService:
|
|||||||
# 카드 성공률(#12) — 카드 사용→타결 집계를 page 카드에 매핑.
|
# 카드 성공률(#12) — 카드 사용→타결 집계를 page 카드에 매핑.
|
||||||
_e, success_map = await DB_SESSION_MNG.execute_lambda(
|
_e, success_map = await DB_SESSION_MNG.execute_lambda(
|
||||||
nego_cards.DBType(), DBWRType.DB_READ.value,
|
nego_cards.DBType(), DBWRType.DB_READ.value,
|
||||||
lambda s: self.card_crud.card_success_map(s),
|
lambda s: self.card_crud.card_success_map(s, company_uuid),
|
||||||
)
|
)
|
||||||
success_map = success_map or {}
|
success_map = success_map or {}
|
||||||
for c in page:
|
for c in page:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user