fix: 抢单池有体验/已付会员一律按已开通返回
/ddhq 有任意有效会员时 yaoqiuleixing 强制为 0;czjilu 已付单可合成 clumber;抢单校验支持充值单兜底。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
"""总会员(包含式会员):购买一个会员,可接多个子会员类型订单。"""
|
||||
from datetime import timedelta
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from products.models import Huiyuan, Huiyuangoumai
|
||||
@@ -106,6 +108,45 @@ def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
|
||||
included_set.add(sx.lstrip('0') or '0')
|
||||
if req_set & included_set:
|
||||
return True
|
||||
|
||||
if _czjilu_grants_huiyuan_access(yonghu_id, req, club_id):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _czjilu_grants_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
|
||||
"""已支付会员充值单(含体验)在有效期内,视同已开通。"""
|
||||
from jituan.constants import CLUB_ID_DEFAULT
|
||||
from jituan.services.member_recharge import (
|
||||
MEMBER_LEIXING,
|
||||
MEMBER_PAID_STATUS,
|
||||
_order_is_trial_like,
|
||||
club_huiyuan_sellable,
|
||||
)
|
||||
from products.models import Czjilu
|
||||
|
||||
req = normalize_huiyuan_id(required_huiyuan_id)
|
||||
if not req:
|
||||
return False
|
||||
now = timezone.now()
|
||||
for order in Czjilu.query.filter(
|
||||
yonghuid=yonghu_id,
|
||||
leixing=MEMBER_LEIXING,
|
||||
zhuangtai=MEMBER_PAID_STATUS,
|
||||
).order_by('-CreateTime')[:30]:
|
||||
if not huiyuan_ids_match(order.huiyuan_id, req):
|
||||
continue
|
||||
cid = getattr(order, 'club_id', None) or club_id or CLUB_ID_DEFAULT
|
||||
is_trial = _order_is_trial_like(order, cid)
|
||||
days = int(getattr(order, 'purchase_days', None) or 0)
|
||||
if days <= 0:
|
||||
_, _, days = club_huiyuan_sellable(cid, order.huiyuan_id, is_trial=is_trial)
|
||||
days = max(int(days or 0), 1)
|
||||
start = order.CreateTime or now
|
||||
if timezone.is_naive(start):
|
||||
start = timezone.make_aware(start, timezone.get_current_timezone())
|
||||
if start + timedelta(days=days) > now:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -145,60 +186,20 @@ def resolve_order_membership_id(order_dict):
|
||||
return ids[0] if ids else ''
|
||||
|
||||
|
||||
def _clumber_covers_required_ids(user_clumber, required_ids):
|
||||
"""老端用 clumber[].huiyuanid 匹配,补齐 ID 变体。"""
|
||||
owned = set()
|
||||
for item in user_clumber or []:
|
||||
for key in ('huiyuanid', 'huiyuan_id'):
|
||||
v = normalize_huiyuan_id(item.get(key))
|
||||
if not v:
|
||||
continue
|
||||
owned.add(v)
|
||||
owned.add(v.lstrip('0') or '0')
|
||||
for req in required_ids or []:
|
||||
req_n = normalize_huiyuan_id(req)
|
||||
if not req_n:
|
||||
continue
|
||||
req_variants = set(expand_huiyuan_id_variants(req_n))
|
||||
if owned & req_variants:
|
||||
return True
|
||||
for o in owned:
|
||||
if huiyuan_ids_match(o, req_n):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def user_can_grab_pool_show_member_order(order_dict, yonghu_id, club_id=None, user_clumber=None):
|
||||
"""抢单池展示/老端本地校验:体验会员与正式会员同等视为已开通。"""
|
||||
grab_req = int(order_dict.get('GrabRequirement') or 0)
|
||||
if grab_req != 1:
|
||||
return True
|
||||
required_ids = resolve_order_membership_ids(order_dict)
|
||||
if not required_ids:
|
||||
return True
|
||||
if user_has_huiyuan_access_any(yonghu_id, required_ids, club_id=club_id):
|
||||
return True
|
||||
if user_clumber is None:
|
||||
user_clumber = build_user_clumber(yonghu_id)
|
||||
return _clumber_covers_required_ids(user_clumber, required_ids)
|
||||
|
||||
|
||||
def resolve_grab_pool_member_display(order_dict, yonghu_id, club_id=None, user_clumber=None):
|
||||
"""
|
||||
老小程序抢单池:仅当 yaoqiuleixing==1 且 huiyuan_id 有值时才显示「未开通会员」。
|
||||
用户已有体验/正式/总会员权限时,强制返回 yaoqiuleixing=0(老端无需改代码即可正常显示与点抢单)。
|
||||
真实权限仍在 /dingdan/qiangdan 服务端校验。
|
||||
老小程序抢单池:yaoqiuleixing==1 且 huiyuan_id 有值才会显示「未开通会员」。
|
||||
用户只要有任意有效会员(体验=正式),会员单一律返回 yaoqiuleixing=0。
|
||||
"""
|
||||
grab_req = int(order_dict.get('GrabRequirement') or 0)
|
||||
membership_id = resolve_order_membership_id(order_dict)
|
||||
if grab_req != 1:
|
||||
return grab_req, membership_id or '', True
|
||||
if user_can_grab_pool_show_member_order(
|
||||
order_dict, yonghu_id, club_id=club_id, user_clumber=user_clumber,
|
||||
):
|
||||
if user_clumber is None:
|
||||
_, user_clumber = ensure_grab_pool_clumber(yonghu_id)
|
||||
if user_clumber:
|
||||
return 0, '', True
|
||||
required_ids = resolve_order_membership_ids(order_dict)
|
||||
return 1, (required_ids[0] if required_ids else membership_id), False
|
||||
return 1, membership_id or '', False
|
||||
|
||||
|
||||
def enrich_clumber_item(record):
|
||||
@@ -252,15 +253,7 @@ def _append_bundle_sub_clumber(items, seen, record, base_item):
|
||||
_append_clumber_id_variants(items, seen, sub_base, sid)
|
||||
|
||||
|
||||
def build_user_clumber(yonghu_id):
|
||||
"""
|
||||
老小程序抢单页 clumber:
|
||||
- 字段 huiyuanid(必须)+ daoqi 字符串
|
||||
- 体验会员、总会员子类型均展开
|
||||
- 同一会员补齐 1/001 等 ID 变体
|
||||
"""
|
||||
from jituan.services.member_recharge import sync_member_entitlements_for_user
|
||||
sync_member_entitlements_for_user(yonghu_id)
|
||||
def _build_user_clumber_core(yonghu_id):
|
||||
items = []
|
||||
seen = set()
|
||||
for record in Huiyuangoumai.query.filter(yonghu_id=yonghu_id):
|
||||
@@ -274,6 +267,77 @@ def build_user_clumber(yonghu_id):
|
||||
return items
|
||||
|
||||
|
||||
def _build_clumber_from_paid_czjilu(yonghu_id):
|
||||
"""huiyuangoumai 缺失时,用已支付会员充值单合成 clumber(体验=正式)。"""
|
||||
from jituan.constants import CLUB_ID_DEFAULT
|
||||
from jituan.services.member_recharge import (
|
||||
MEMBER_LEIXING,
|
||||
MEMBER_PAID_STATUS,
|
||||
_order_is_trial_like,
|
||||
club_huiyuan_sellable,
|
||||
)
|
||||
from products.models import Czjilu, Huiyuan
|
||||
|
||||
items = []
|
||||
seen = set()
|
||||
now = timezone.now()
|
||||
for order in Czjilu.query.filter(
|
||||
yonghuid=yonghu_id,
|
||||
leixing=MEMBER_LEIXING,
|
||||
zhuangtai=MEMBER_PAID_STATUS,
|
||||
).order_by('-CreateTime')[:30]:
|
||||
hid = normalize_huiyuan_id(order.huiyuan_id)
|
||||
if not hid:
|
||||
continue
|
||||
cid = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
|
||||
is_trial = _order_is_trial_like(order, cid)
|
||||
days = int(getattr(order, 'purchase_days', None) or 0)
|
||||
if days <= 0:
|
||||
_, _, days = club_huiyuan_sellable(cid, hid, is_trial=is_trial)
|
||||
days = max(int(days or 0), 1)
|
||||
start = order.CreateTime or now
|
||||
if timezone.is_naive(start):
|
||||
start = timezone.make_aware(start, timezone.get_current_timezone())
|
||||
daoqi = start + timedelta(days=days)
|
||||
if daoqi <= now:
|
||||
continue
|
||||
try:
|
||||
hy = Huiyuan.query.get(huiyuan_id=hid)
|
||||
ming = hy.jieshao or f'会员{hid}'
|
||||
except Huiyuan.DoesNotExist:
|
||||
ming = f'会员{hid}'
|
||||
base = {
|
||||
'huiyuanming': ming,
|
||||
'daoqi': format_legacy_daoqi(daoqi),
|
||||
'huiyuan_zhuangtai': 1,
|
||||
'shifou_daoqi': False,
|
||||
}
|
||||
_append_clumber_id_variants(items, seen, base, hid)
|
||||
return items
|
||||
|
||||
|
||||
def ensure_grab_pool_clumber(yonghu_id):
|
||||
"""抢单池:同步权益 + huiyuangoumai + 已付充值单兜底,体验会员按正式返回。"""
|
||||
from jituan.services.member_recharge import sync_member_entitlements_for_user
|
||||
|
||||
sync_member_entitlements_for_user(yonghu_id)
|
||||
items = _build_user_clumber_core(yonghu_id)
|
||||
if not items:
|
||||
items = _build_clumber_from_paid_czjilu(yonghu_id)
|
||||
return bool(items), items
|
||||
|
||||
|
||||
def build_user_clumber(yonghu_id):
|
||||
"""
|
||||
老小程序抢单页 clumber:
|
||||
- 字段 huiyuanid(必须)+ daoqi 字符串
|
||||
- 体验会员、总会员子类型均展开
|
||||
- 同一会员补齐 1/001 等 ID 变体
|
||||
"""
|
||||
_, items = ensure_grab_pool_clumber(yonghu_id)
|
||||
return items
|
||||
|
||||
|
||||
def build_auth_clumber(yonghu_id):
|
||||
"""登录接口 clumber:老端认 huiyuanid,保留原状态字段。"""
|
||||
from jituan.services.member_recharge import sync_member_entitlements_for_user
|
||||
|
||||
@@ -647,8 +647,11 @@ def repair_member_entitlement_if_paid(dingdan_id):
|
||||
return False
|
||||
|
||||
club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
|
||||
days = order.purchase_days or 30
|
||||
is_trial = _order_is_trial_like(order, club_id)
|
||||
days = int(getattr(order, 'purchase_days', None) or 0)
|
||||
if days <= 0:
|
||||
_, _, days = club_huiyuan_sellable(club_id, order.huiyuan_id, is_trial=is_trial)
|
||||
days = max(int(days or 0), 1)
|
||||
with transaction.atomic():
|
||||
_apply_huiyuan_entitlement(
|
||||
order.yonghuid, order.huiyuan_id, huiyuan, club_id, is_trial, days,
|
||||
|
||||
@@ -297,13 +297,11 @@ class DashouDingdanHuoquView(APIView):
|
||||
identity_tag_map = batch_identity_tags(club_id, identity_pairs) if identity_pairs else {}
|
||||
|
||||
from jituan.services.huiyuan_bundle import (
|
||||
build_user_clumber,
|
||||
ensure_grab_pool_clumber,
|
||||
resolve_grab_pool_member_display,
|
||||
)
|
||||
from jituan.services.member_recharge import sync_member_entitlements_for_user
|
||||
yonghuid = request.user.UserUID
|
||||
sync_member_entitlements_for_user(yonghuid)
|
||||
user_clumber = build_user_clumber(yonghuid)
|
||||
_has_member, user_clumber = ensure_grab_pool_clumber(yonghuid)
|
||||
|
||||
# 12. 格式化返回数据
|
||||
formatted_list = []
|
||||
|
||||
Reference in New Issue
Block a user