feat: 假单与真单混排,会员或押金达标后过滤假单

This commit is contained in:
XingQue
2026-07-30 20:41:10 +08:00
parent c8a3d0274d
commit 9744e800eb
5 changed files with 193 additions and 62 deletions

View File

@@ -1,7 +1,8 @@
"""抢单大厅假单:按俱乐部开关 + 按商品类型会员资格展示假数据"""
"""抢单大厅假单:按俱乐部开关混排/过滤"""
import logging
import random
from datetime import datetime, timedelta
from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
@@ -11,7 +12,10 @@ from rest_framework.response import Response
from backend.utils import fmt_datetime, pick_leixing_id
from jituan.services.club_context import resolve_effective_club_id
from jituan.services.fake_grab_switch import is_club_fake_grab_enabled
from jituan.services.fake_grab_switch import (
get_fake_yajin_hide_threshold,
is_club_fake_grab_enabled,
)
from jituan.services.huiyuan_bundle import normalize_huiyuan_id, user_has_huiyuan_access
from orders.models import FakeGrabOrder, FakeGrabOrderShuffleState
@@ -20,12 +24,14 @@ logger = logging.getLogger(__name__)
DEFAULT_DISPATCHER_AVATAR = 'beijing/morentouxiang.jpg'
SHUFFLE_STATE_PK = 1
MODE_OFF = 'off'
MODE_FAKE_ONLY = 'fake_only'
MODE_MIX = 'mix'
MODE_REAL_ONLY = 'real_only'
def _leixing_membership_meta(leixing_id):
"""
返回 (required_huiyuan_id, yaoqiuleixing)。
required 为空表示类型未绑定会员 ID。
"""
"""返回 (required_huiyuan_id, yaoqiuleixing)。"""
if leixing_id is None:
return '', None
try:
@@ -44,77 +50,77 @@ def _leixing_membership_meta(leixing_id):
return '', None
def dashou_should_use_fake_pool(user, club_id=None, leixing_id=None):
"""
当前商品类型 Tab 是否只展示假单(结果必须稳定,同用户同类型每次请求一致):
def _user_has_type_membership(user, club_id, leixing_id):
required, _yaoqiu = _leixing_membership_meta(leixing_id)
if not required:
return False
try:
return bool(user_has_huiyuan_access(user.UserUID, required, club_id=club_id))
except Exception:
logger.exception(
'user_has_huiyuan_access failed uid=%s required=%s club=%s',
getattr(user, 'UserUID', ''), required, club_id,
)
return False
- 俱乐部未开启 → 真单
- 已开启后:
- 账号异常/封禁 → 假单
- 类型绑定了会员 ID有对应会员含总会员覆盖子类型且未过期 → 真单;否则 → 假
- 类型要求会员但未绑定 ID → 假单(无法核验资格时不开真池)
- 类型不要求会员 → 真
不同类型互不影响。查库异常时 fail-closed偏向假单避免闪回真单。
def resolve_fake_pool_mode(user, club_id=None, leixing_id=None):
"""
- off俱乐部未开 → 只真
- fake_only封禁假单
- real_only有对应会员或押金≥门槛 → 过滤假
- mix未充对应会员且押金未达门槛 → 真单+假单
"""
cid = (club_id or '').strip()
if not cid:
return False
return MODE_OFF
try:
enabled = is_club_fake_grab_enabled(cid)
except Exception:
logger.exception('is_club_fake_grab_enabled failed club=%s', cid)
return False
return MODE_OFF
if not enabled:
return False
return MODE_OFF
# ---------- 俱乐部假单已开启:默认假单,只有核验通过才出真单 ----------
try:
dashou = user.DashouProfile
except ObjectDoesNotExist:
return True
return MODE_FAKE_ONLY
except Exception:
logger.exception('read DashouProfile failed uid=%s', getattr(user, 'UserUID', ''))
return True
return MODE_FAKE_ONLY
if int(getattr(dashou, 'zhanghaozhuangtai', 0) or 0) != 1:
return True
return MODE_FAKE_ONLY
if leixing_id is None:
# 未带类型时无法判断「对应会员」,开启假单后不出真池
return True
if leixing_id is not None and _user_has_type_membership(user, cid, leixing_id):
return MODE_REAL_ONLY
required, yaoqiu = _leixing_membership_meta(leixing_id)
if required:
try:
if user_has_huiyuan_access(user.UserUID, required, club_id=cid):
return False # 有对应会员(或总会员含该子类型)→ 真单
except Exception:
logger.exception(
'user_has_huiyuan_access failed uid=%s required=%s club=%s',
getattr(user, 'UserUID', ''), required, cid,
)
return True # 异常 → 假单,避免闪真单
return True # 无会员 / 已过期 → 假单
# 未绑定会员 ID
try:
yaoqiu_int = int(yaoqiu) if yaoqiu is not None else 0
except (TypeError, ValueError):
yaoqiu_int = 0
if yaoqiu_int == 1:
# 要求会员却没配 ID不能放行真单
return True
# 不要求会员(押金单等)→ 真单
return False
yajin = Decimal(str(getattr(dashou, 'yajin', 0) or 0))
except Exception:
yajin = Decimal('0')
if yajin >= get_fake_yajin_hide_threshold(cid):
return MODE_REAL_ONLY
return MODE_MIX
def dashou_should_use_fake_pool(user, club_id=None, leixing_id=None):
"""兼容旧调用:是否整页只假单。"""
return resolve_fake_pool_mode(user, club_id=club_id, leixing_id=leixing_id) == MODE_FAKE_ONLY
def list_formatted_fake_orders(club_id, leixing_id):
rows, anchor_dt = _load_fake_rows_in_saved_order(club_id, leixing_id)
return [_format_fake_row(r, anchor_dt) for r in rows]
def _scope_key(club_id, leixing_id):
lid = leixing_id if leixing_id is not None else 'all'
return f'{club_id}|{lid}'
def _ensure_aware(dt):
if dt is None:
return None
@@ -255,6 +261,10 @@ def _format_fake_row(row, anchor_dt=None):
'tupian': avatar,
'yaoqiuleixing': 0,
'huiyuan_id': '',
# 假单始终展示价格(前端 hasRequiredMember=true 才显示金额)
'hasRequiredMember': True,
'has_required_member': True,
'is_fake': True,
'yajin': 0.0,
'jieshao': row.Description or '',
'beizhu': row.Remark or '',