feat: 假单列表全局2分钟打乱一次,全用户顺序一致
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
23
orders/migrations/0012_fakegrabordershufflestate.py
Normal file
23
orders/migrations/0012_fakegrabordershufflestate.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('orders', '0011_fakegraborder_merchantnickname'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FakeGrabOrderShuffleState',
|
||||||
|
fields=[
|
||||||
|
('id', models.PositiveSmallIntegerField(default=1, primary_key=True, serialize=False, verbose_name='固定主键=1')),
|
||||||
|
('LastShuffleTime', models.DateTimeField(verbose_name='上次打乱时刻')),
|
||||||
|
('IntervalSeconds', models.PositiveIntegerField(default=120, verbose_name='打乱间隔秒数(默认2分钟)')),
|
||||||
|
('UpdateTime', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'fake_grab_order_shuffle',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1094,6 +1094,24 @@ class FakeGrabOrder(QModel):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class FakeGrabOrderShuffleState(QModel):
|
||||||
|
"""假单列表全局打乱时刻(仅 id=1 一条记录,全用户共用)。"""
|
||||||
|
|
||||||
|
id = models.PositiveSmallIntegerField(
|
||||||
|
primary_key=True, default=1, verbose_name='固定主键=1',
|
||||||
|
)
|
||||||
|
LastShuffleTime = models.DateTimeField(
|
||||||
|
verbose_name='上次打乱时刻',
|
||||||
|
)
|
||||||
|
IntervalSeconds = models.PositiveIntegerField(
|
||||||
|
default=120, verbose_name='打乱间隔秒数(默认2分钟)',
|
||||||
|
)
|
||||||
|
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'fake_grab_order_shuffle'
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# 兼容别名(渐进迁移,全部迁移完成后可删除)
|
# 兼容别名(渐进迁移,全部迁移完成后可删除)
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ from datetime import timedelta
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework.response import Response
|
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.dashou_exam import user_has_active_member
|
||||||
from orders.models import FakeGrabOrder
|
from orders.models import FakeGrabOrder, FakeGrabOrderShuffleState
|
||||||
|
|
||||||
DEFAULT_DISPATCHER_AVATAR = 'beijing/morentouxiang.jpg'
|
DEFAULT_DISPATCHER_AVATAR = 'beijing/morentouxiang.jpg'
|
||||||
|
SHUFFLE_STATE_PK = 1
|
||||||
|
|
||||||
|
|
||||||
def dashou_should_use_fake_pool(user):
|
def dashou_should_use_fake_pool(user):
|
||||||
@@ -46,6 +48,49 @@ def _random_display_time_str():
|
|||||||
return fmt_datetime(dt)
|
return fmt_datetime(dt)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_shuffle_epoch():
|
||||||
|
"""
|
||||||
|
全用户共用一条打乱状态(id=1):
|
||||||
|
- 距上次打乱 >= IntervalSeconds:更新 LastShuffleTime,进入新打乱窗口
|
||||||
|
- 否则:沿用上次打乱时刻,本窗口内顺序不变
|
||||||
|
返回用于固定顺序的 epoch 时间戳;若应使用库表顺序则返回 None。
|
||||||
|
"""
|
||||||
|
now = timezone.now()
|
||||||
|
with transaction.atomic():
|
||||||
|
state, created = FakeGrabOrderShuffleState.objects.select_for_update().get_or_create(
|
||||||
|
pk=SHUFFLE_STATE_PK,
|
||||||
|
defaults={
|
||||||
|
'LastShuffleTime': now - timedelta(days=1),
|
||||||
|
'IntervalSeconds': 120,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
interval = max(1, int(state.IntervalSeconds or 120))
|
||||||
|
elapsed = (now - state.LastShuffleTime).total_seconds()
|
||||||
|
if created or elapsed >= interval:
|
||||||
|
state.LastShuffleTime = now
|
||||||
|
state.save(update_fields=['LastShuffleTime', 'UpdateTime'])
|
||||||
|
return int(state.LastShuffleTime.timestamp())
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _order_fake_rows(rows, shuffle_epoch):
|
||||||
|
"""shuffle_epoch 有值:按全局 epoch 固定打乱;None:按库表 SortOrder/CreateTime。"""
|
||||||
|
items = list(rows)
|
||||||
|
if shuffle_epoch is None:
|
||||||
|
items.sort(
|
||||||
|
key=lambda r: (
|
||||||
|
-(r.SortOrder or 0),
|
||||||
|
r.CreateTime or timezone.datetime.min.replace(tzinfo=timezone.utc),
|
||||||
|
r.id or 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return items
|
||||||
|
base = sorted(items, key=lambda r: (r.id or 0, r.OrderID or ''))
|
||||||
|
rng = random.Random(shuffle_epoch)
|
||||||
|
rng.shuffle(base)
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
def _format_fake_row(row, display_time=None):
|
def _format_fake_row(row, display_time=None):
|
||||||
"""字段与 DashouDingdanHuoquView 真实单列表保持一致。"""
|
"""字段与 DashouDingdanHuoquView 真实单列表保持一致。"""
|
||||||
# 平台单用 tupian、商家单用 tupian + sj_avatar,统一默认头像
|
# 平台单用 tupian、商家单用 tupian + sj_avatar,统一默认头像
|
||||||
@@ -97,8 +142,8 @@ def build_fake_order_pool_response(request):
|
|||||||
if leixing_id is not None:
|
if leixing_id is not None:
|
||||||
qs = qs.filter(ProductTypeID=leixing_id)
|
qs = qs.filter(ProductTypeID=leixing_id)
|
||||||
|
|
||||||
rows = list(qs)
|
shuffle_epoch = _resolve_shuffle_epoch()
|
||||||
random.shuffle(rows)
|
rows = _order_fake_rows(qs, shuffle_epoch)
|
||||||
|
|
||||||
total = len(rows)
|
total = len(rows)
|
||||||
offset = (page - 1) * page_size
|
offset = (page - 1) * page_size
|
||||||
|
|||||||
Reference in New Issue
Block a user