feat: 假单按俱乐部开关,并按商品类型会员资格展示

This commit is contained in:
XingQue
2026-07-30 19:55:19 +08:00
parent 6f4e458871
commit bb74140150
6 changed files with 128 additions and 15 deletions

View File

@@ -371,7 +371,8 @@ CELERY_TASK_QUEUES = {
# 订单超时自动结算Celery process_expired_order / check_order_expire_task总开关False=彻底关闭 # 订单超时自动结算Celery process_expired_order / check_order_expire_task总开关False=彻底关闭
ORDER_AUTO_SETTLEMENT_ENABLED = False ORDER_AUTO_SETTLEMENT_ENABLED = False
# 打手抢单大厅假单展示False=仅返回真实订单(后台假单管理仍可用;改 True 可恢复假单池) # 打手抢单大厅假单展示False=仅返回真实订单(后台假单管理仍可用;改 True 可恢复假单池)
FAKE_GRAB_ORDER_POOL_ENABLED = False # 假单功能总闸(紧急关闭用)。实际是否对打手生效由各俱乐部 config_json.fake_grab_order_enabled 控制。
FAKE_GRAB_ORDER_POOL_ENABLED = True
ORDER_EXPIRE_SECONDS = 48 * 60 * 60 # 48小时 = 172800秒开关关闭时不生效 ORDER_EXPIRE_SECONDS = 48 * 60 * 60 # 48小时 = 172800秒开关关闭时不生效
DAILY_TASK_HOUR = 0 DAILY_TASK_HOUR = 0
DAILY_TASK_MINUTE = 30 DAILY_TASK_MINUTE = 30

View File

@@ -113,6 +113,7 @@ def build_fake_order_list_payload(request, leixing_id=None, page=1, page_size=20
'scope': resolve_club_scope(request), 'scope': resolve_club_scope(request),
'scope_error': CLUB_READ_SCOPE_MSG, 'scope_error': CLUB_READ_SCOPE_MSG,
'leixing_options': build_leixing_options(), 'leixing_options': build_leixing_options(),
'fake_grab_enabled': False,
**list_response_meta(request), **list_response_meta(request),
} }
@@ -129,6 +130,8 @@ def build_fake_order_list_payload(request, leixing_id=None, page=1, page_size=20
rows = list(qs[offset:offset + page_size]) rows = list(qs[offset:offset + page_size])
names = _leixing_name_map() names = _leixing_name_map()
from jituan.services.fake_grab_switch import is_club_fake_grab_enabled
return { return {
'list': [_row_to_dict(r, names) for r in rows], 'list': [_row_to_dict(r, names) for r in rows],
'total': total, 'total': total,
@@ -137,10 +140,35 @@ def build_fake_order_list_payload(request, leixing_id=None, page=1, page_size=20
'club_id': club_id, 'club_id': club_id,
'scope': resolve_club_scope(request), 'scope': resolve_club_scope(request),
'leixing_options': build_leixing_options(), 'leixing_options': build_leixing_options(),
'fake_grab_enabled': is_club_fake_grab_enabled(club_id),
**list_response_meta(request), **list_response_meta(request),
} }
def set_fake_grab_enabled(request, enabled):
"""开启/关闭本俱乐部打手侧假单功能。"""
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
from jituan.models import Club
from jituan.services.fake_grab_switch import (
is_club_fake_grab_enabled,
set_club_fake_grab_enabled,
)
club_id = club_id_for_write(request)
club = Club.query.filter(club_id=club_id).first()
if not club:
return None, '俱乐部不存在'
set_club_fake_grab_enabled(club, bool(enabled))
club.save(update_fields=['config_json'])
return {
'club_id': club_id,
'fake_grab_enabled': is_club_fake_grab_enabled(club_id),
}, None
def _parse_decimal(value, field_name='金额'): def _parse_decimal(value, field_name='金额'):
try: try:
val = Decimal(str(value)) val = Decimal(str(value))

View File

@@ -0,0 +1,36 @@
"""俱乐部级假单功能开关config_json.fake_grab_order_enabled"""
import logging
logger = logging.getLogger(__name__)
CONFIG_KEY = 'fake_grab_order_enabled'
def is_club_fake_grab_enabled(club_id: str) -> bool:
"""某俱乐部是否对打手侧开启假单池。默认关闭。"""
cid = (club_id or '').strip()
if not cid:
return False
try:
from django.conf import settings
if not getattr(settings, 'FAKE_GRAB_ORDER_POOL_ENABLED', True):
return False
except Exception:
pass
try:
from jituan.models import Club
club = Club.query.filter(club_id=cid).first()
if not club:
return False
cfg = club.config_json or {}
return bool(cfg.get(CONFIG_KEY))
except Exception:
logger.exception('read fake_grab_order_enabled failed club=%s', cid)
return False
def set_club_fake_grab_enabled(club, enabled: bool) -> None:
"""写入 club.config_json调用方负责 save。"""
cfg = dict(getattr(club, 'config_json', None) or {})
cfg[CONFIG_KEY] = bool(enabled)
club.config_json = cfg

View File

@@ -59,6 +59,7 @@ class FakeGrabOrderManageView(APIView):
from jituan.services.fake_grab_order_admin import ( from jituan.services.fake_grab_order_admin import (
create_fake_order, create_fake_order,
delete_fake_order, delete_fake_order,
set_fake_grab_enabled,
update_fake_order, update_fake_order,
) )
@@ -82,6 +83,26 @@ class FakeGrabOrderManageView(APIView):
return Response({'code': 400, 'msg': err_msg}) return Response({'code': 400, 'msg': err_msg})
return Response({'code': 0, 'msg': '已删除', 'data': data}) return Response({'code': 0, 'msg': '已删除', 'data': data})
if action in ('set_enabled', 'enable', 'disable'):
if action == 'enable':
enabled = True
elif action == 'disable':
enabled = False
else:
raw = request.data.get('enabled', request.data.get('fake_grab_enabled'))
if isinstance(raw, str):
enabled = raw.strip().lower() in ('1', 'true', 'yes', 'on')
else:
enabled = bool(raw)
data, err_msg = set_fake_grab_enabled(request, enabled)
if err_msg:
return Response({'code': 400, 'msg': err_msg})
return Response({
'code': 0,
'msg': '假单功能已开启' if data.get('fake_grab_enabled') else '假单功能已关闭',
'data': data,
})
return Response({'code': 400, 'msg': '无效的 action'}) return Response({'code': 400, 'msg': '无效的 action'})

View File

@@ -1,7 +1,6 @@
"""抢单大厅假单:无资格/封禁打手仅展示假数据。""" """抢单大厅假单:按俱乐部开关 + 按商品类型会员资格展示假数据。"""
import random import random
from datetime import datetime, timedelta from datetime import datetime, timedelta
from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction from django.db import transaction
@@ -11,19 +10,41 @@ from rest_framework.response import Response
from backend.utils import fmt_datetime, pick_leixing_id from backend.utils import fmt_datetime, pick_leixing_id
from jituan.services.club_context import resolve_effective_club_id from jituan.services.club_context import resolve_effective_club_id
from jituan.services.dashou_exam import user_has_active_member from jituan.services.fake_grab_switch import is_club_fake_grab_enabled
from jituan.services.huiyuan_bundle import normalize_huiyuan_id, user_has_huiyuan_access
from orders.models import FakeGrabOrder, FakeGrabOrderShuffleState from orders.models import FakeGrabOrder, FakeGrabOrderShuffleState
DEFAULT_DISPATCHER_AVATAR = 'beijing/morentouxiang.jpg' DEFAULT_DISPATCHER_AVATAR = 'beijing/morentouxiang.jpg'
SHUFFLE_STATE_PK = 1 SHUFFLE_STATE_PK = 1
def dashou_should_use_fake_pool(user): def _leixing_required_huiyuan_id(leixing_id):
"""商品类型绑定的会员 ID无绑定则视为不要求会员。"""
if leixing_id is None:
return ''
try:
from products.models import ShangpinLeixing
row = ShangpinLeixing.query.filter(id=int(leixing_id)).values('huiyuan_id').first()
if not row:
return ''
return normalize_huiyuan_id(row.get('huiyuan_id'))
except Exception:
return ''
def dashou_should_use_fake_pool(user, club_id=None, leixing_id=None):
""" """
打手是否只能看假单: 当前 Tab商品类型是否只展示假单:
- 账号封禁zhanghaozhuangtai != 1 - 俱乐部未开启假单功能 → 否(看真实单
- 或未开通有效会员 且 押金不大于 5 - 账号封禁 → 是
- 该类型要求的会员:已充(含总会员覆盖子类型)→ 否
- 未充对应会员 → 是
不同类型互不影响;假单在独立表,抢不了。
""" """
cid = (club_id or '').strip()
if not cid or not is_club_fake_grab_enabled(cid):
return False
try: try:
dashou = user.DashouProfile dashou = user.DashouProfile
except ObjectDoesNotExist: except ObjectDoesNotExist:
@@ -32,11 +53,16 @@ def dashou_should_use_fake_pool(user):
if dashou.zhanghaozhuangtai != 1: if dashou.zhanghaozhuangtai != 1:
return True return True
yajin = Decimal(str(dashou.yajin or 0)) if leixing_id is None:
if yajin > Decimal('5'): # 未指定类型时不整池切假单,走真实单列表
return False return False
if user_has_active_member(user.UserUID): required = _leixing_required_huiyuan_id(leixing_id)
if not required:
# 类型未绑会员 → 不推假单
return False
if user_has_huiyuan_access(user.UserUID, required, club_id=cid):
return False return False
return True return True

View File

@@ -97,14 +97,10 @@ class DashouDingdanHuoquView(APIView):
def post(self, request): def post(self, request):
try: try:
from django.conf import settings
from orders.services.fake_order_pool import ( from orders.services.fake_order_pool import (
dashou_should_use_fake_pool, dashou_should_use_fake_pool,
build_fake_order_pool_response, build_fake_order_pool_response,
) )
if getattr(settings, 'FAKE_GRAB_ORDER_POOL_ENABLED', False):
if dashou_should_use_fake_pool(request.user):
return build_fake_order_pool_response(request)
# 1. 获取请求参数 # 1. 获取请求参数
data = request.data data = request.data
@@ -119,6 +115,11 @@ class DashouDingdanHuoquView(APIView):
resolve_grab_pool_member_display, resolve_grab_pool_member_display,
) )
club_id = resolve_effective_club_id(request, getattr(request, 'user', None)) club_id = resolve_effective_club_id(request, getattr(request, 'user', None))
# 假单:按俱乐部开关 + 当前商品类型是否具备对应会员(含总会员覆盖)
if dashou_should_use_fake_pool(request.user, club_id=club_id, leixing_id=leixing_id):
return build_fake_order_pool_response(request)
yonghuid = request.user.UserUID yonghuid = request.user.UserUID
_has_member, user_clumber = refresh_grab_pool_membership_state( _has_member, user_clumber = refresh_grab_pool_membership_state(
yonghuid, club_id=club_id, yonghuid, club_id=club_id,