fix: 修复点单商品在多店铺间跳动
扫码绑店参数只消费一次,避免 Tab 返回反复换绑;一人多条脏绑定取最新并在换绑时清理,hqsp 稳定取当前店。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1
shop/services/__init__.py
Normal file
1
shop/services/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# shop.services
|
||||
95
shop/services/shop_binding.py
Normal file
95
shop/services/shop_binding.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""用户店铺绑定:一人只能绑定一家店(业务约定)。"""
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
|
||||
from shop.models import Dianpu, YonghuDianpuBangding
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_latest_binding_qs(yonghu):
|
||||
"""同一用户可能历史残留多条绑定,一律取最近更新的一条。"""
|
||||
return (
|
||||
YonghuDianpuBangding.objects
|
||||
.filter(yonghu=yonghu)
|
||||
.select_related('dianpu')
|
||||
.order_by('-UpdateTime', '-id')
|
||||
)
|
||||
|
||||
|
||||
def get_user_shop_binding(yonghu):
|
||||
"""读取当前生效绑定(最新一条)。"""
|
||||
return get_latest_binding_qs(yonghu).first()
|
||||
|
||||
|
||||
def _delete_extra_bindings(yonghu, keep_id=None):
|
||||
"""
|
||||
删除同一用户多余绑定行,并扣减对应店铺绑定数。
|
||||
keep_id: 保留的绑定主键;None 表示全部删除。
|
||||
"""
|
||||
qs = YonghuDianpuBangding.objects.select_for_update().filter(yonghu=yonghu)
|
||||
if keep_id is not None:
|
||||
qs = qs.exclude(pk=keep_id)
|
||||
extras = list(qs.select_related('dianpu'))
|
||||
for row in extras:
|
||||
try:
|
||||
dianpu = row.dianpu
|
||||
dianpu.bangding_yonghushu = F('bangding_yonghushu') - 1
|
||||
dianpu.save(update_fields=['bangding_yonghushu'])
|
||||
except Exception:
|
||||
logger.warning('扣减多余绑定店铺人数失败 binding=%s', row.pk, exc_info=True)
|
||||
row.delete()
|
||||
return len(extras)
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def set_user_shop_binding(yonghu, dianpu):
|
||||
"""
|
||||
将用户唯一绑定到 dianpu;清理多余行。
|
||||
返回 (changed: bool, msg: str)
|
||||
"""
|
||||
yonghu_locked = type(yonghu).objects.select_for_update().get(pk=yonghu.pk)
|
||||
dianpu_locked = Dianpu.objects.select_for_update().get(pk=dianpu.pk)
|
||||
|
||||
if dianpu_locked.zhuangtai != 1:
|
||||
return False, '店铺已被封禁,无法绑定'
|
||||
|
||||
rows = list(
|
||||
YonghuDianpuBangding.objects.select_for_update()
|
||||
.filter(yonghu=yonghu_locked)
|
||||
.select_related('dianpu')
|
||||
.order_by('-UpdateTime', '-id')
|
||||
)
|
||||
|
||||
if rows:
|
||||
primary = rows[0]
|
||||
if primary.dianpu_id == dianpu_locked.id:
|
||||
# 已是目标店:清掉多余行即可
|
||||
_delete_extra_bindings(yonghu_locked, keep_id=primary.pk)
|
||||
return False, '已绑定该店铺'
|
||||
|
||||
old = primary.dianpu
|
||||
old.bangding_yonghushu = F('bangding_yonghushu') - 1
|
||||
old.save(update_fields=['bangding_yonghushu'])
|
||||
|
||||
primary.dianpu = dianpu_locked
|
||||
primary.save(update_fields=['dianpu', 'UpdateTime'])
|
||||
_delete_extra_bindings(yonghu_locked, keep_id=primary.pk)
|
||||
else:
|
||||
primary = YonghuDianpuBangding.query.create(yonghu=yonghu_locked, dianpu=dianpu_locked)
|
||||
|
||||
dianpu_locked.bangding_yonghushu = F('bangding_yonghushu') + 1
|
||||
dianpu_locked.save(update_fields=['bangding_yonghushu'])
|
||||
return True, '绑定成功'
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def clear_user_shop_binding(yonghu):
|
||||
"""解除用户全部店铺绑定。返回被解除的主店铺 id 或 None。"""
|
||||
yonghu_locked = type(yonghu).objects.select_for_update().get(pk=yonghu.pk)
|
||||
primary = get_latest_binding_qs(yonghu_locked).first()
|
||||
old_id = primary.dianpu_id if primary else None
|
||||
_delete_extra_bindings(yonghu_locked, keep_id=None)
|
||||
return old_id
|
||||
Reference in New Issue
Block a user