feat: 假单按俱乐部开关,并按商品类型会员资格展示
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
36
jituan/services/fake_grab_switch.py
Normal file
36
jituan/services/fake_grab_switch.py
Normal 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
|
||||||
@@ -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'})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user