From abfe497ddc9a53cf7f84781a389ae3b5a0f6841c Mon Sep 17 00:00:00 2001 From: XingQue Date: Wed, 8 Jul 2026 14:18:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8A=A2=E5=8D=95=E6=B1=A0=E6=9C=89?= =?UTF-8?q?=E4=BD=93=E9=AA=8C/=E5=B7=B2=E4=BB=98=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E4=B8=80=E5=BE=8B=E6=8C=89=E5=B7=B2=E5=BC=80=E9=80=9A=E8=BF=94?= =?UTF-8?q?=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /ddhq 有任意有效会员时 yaoqiuleixing 强制为 0;czjilu 已付单可合成 clumber;抢单校验支持充值单兜底。 Co-authored-by: Cursor --- jituan/services/huiyuan_bundle.py | 174 ++++++++++++++++++++--------- jituan/services/member_recharge.py | 5 +- orders/views/dashou.py | 6 +- 3 files changed, 125 insertions(+), 60 deletions(-) diff --git a/jituan/services/huiyuan_bundle.py b/jituan/services/huiyuan_bundle.py index e2e01f8..e57fa9c 100644 --- a/jituan/services/huiyuan_bundle.py +++ b/jituan/services/huiyuan_bundle.py @@ -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 diff --git a/jituan/services/member_recharge.py b/jituan/services/member_recharge.py index dd266d3..f493a4b 100644 --- a/jituan/services/member_recharge.py +++ b/jituan/services/member_recharge.py @@ -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, diff --git a/orders/views/dashou.py b/orders/views/dashou.py index 7a96a82..7c642ff 100644 --- a/orders/views/dashou.py +++ b/orders/views/dashou.py @@ -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 = []