diff --git a/jituan/services/huiyuan_bundle.py b/jituan/services/huiyuan_bundle.py index f0c7fab..e8aa34c 100644 --- a/jituan/services/huiyuan_bundle.py +++ b/jituan/services/huiyuan_bundle.py @@ -79,6 +79,29 @@ def get_bundle_included_ids(club_id, bundle_huiyuan_id): ) +def _resolve_user_club_id(yonghu_id, club_id=None): + """解析用户所属俱乐部(体验/正式同一套;总会员子类型依赖此值)。""" + from jituan.constants import CLUB_ID_DEFAULT + from jituan.services.club_user import get_user_club_id + + cid = (club_id or '').strip() + if cid: + return cid + try: + from users.business_models import User + user = User.query.filter(UserUID=yonghu_id).first() + return get_user_club_id(user) or CLUB_ID_DEFAULT + except Exception: + return CLUB_ID_DEFAULT + + +def _record_club_id(record, club_id=None): + cid = (getattr(record, 'club_id', None) or '').strip() + if cid: + return cid + return _resolve_user_club_id(getattr(record, 'yonghu_id', ''), club_id=club_id) + + def is_bundle_huiyuan(huiyuan_id): try: return bool(Huiyuan.query.get(huiyuan_id=huiyuan_id).is_bundle) @@ -118,10 +141,11 @@ def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None): ) if bundle_ids: req_set = {req, req.lstrip('0') or '0'} + effective_club = _resolve_user_club_id(yonghu_id, club_id) for rec in Huiyuangoumai.query.filter(yonghu_id=yonghu_id, huiyuan_id__in=bundle_ids): if not _record_valid(rec): continue - cid = club_id or rec.club_id or '' + cid = _record_club_id(rec, club_id) or effective_club included = get_bundle_included_ids(cid, rec.huiyuan_id) included_set = set() for x in included: @@ -138,21 +162,21 @@ def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None): 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, _czjilu_entitlement_revoked, - club_huiyuan_sellable, + czjilu_entitlement_active, ) from products.models import Czjilu req = normalize_huiyuan_id(required_huiyuan_id) if not req: return False - now = _normalize_datetime_for_compare(timezone.now()) + req_set = {req, req.lstrip('0') or '0'} + effective_club = _resolve_user_club_id(yonghu_id, club_id) for order in Czjilu.query.filter( yonghuid=yonghu_id, leixing=MEMBER_LEIXING, @@ -160,17 +184,24 @@ def _czjilu_grants_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None): ).order_by('-CreateTime')[:30]: if _czjilu_entitlement_revoked(order): continue - if not huiyuan_ids_match(order.huiyuan_id, req): + cid = getattr(order, 'club_id', None) or club_id or effective_club or CLUB_ID_DEFAULT + if not czjilu_entitlement_active(order, cid): 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 = _normalize_datetime_for_compare(order.CreateTime or now) - if start + timedelta(days=days) > now: + hid = normalize_huiyuan_id(order.huiyuan_id) + if not hid: + continue + if huiyuan_ids_match(hid, req): return True + if is_bundle_huiyuan(hid): + included = get_bundle_included_ids(cid, hid) + included_set = set() + for x in included: + sx = normalize_huiyuan_id(x) + if sx: + included_set.add(sx) + included_set.add(sx.lstrip('0') or '0') + if req_set & included_set: + return True return False @@ -248,9 +279,10 @@ def resolve_grab_pool_member_display(order_dict, yonghu_id, club_id=None, user_c return 1, membership_id or '', has_valid -def enrich_clumber_item(record): - """老端 clumber:有效期内体验会员与正式会员返回格式完全一致。""" +def enrich_clumber_item(record, club_id=None): + """老端 clumber:体验会员与正式会员返回格式完全一致(不暴露 is_trial)。""" daoqi_str = format_legacy_daoqi(record.daoqi_time) + cid = _record_club_id(record, club_id) item = { 'huiyuanid': record.huiyuan_id, 'huiyuan_id': record.huiyuan_id, @@ -261,9 +293,8 @@ def enrich_clumber_item(record): } if is_bundle_huiyuan(record.huiyuan_id): item['is_bundle'] = True - item['included_huiyuan_ids'] = get_bundle_included_ids( - record.club_id or '', record.huiyuan_id, - ) + item['bundle_huiyuan_id'] = record.huiyuan_id + item['included_huiyuan_ids'] = get_bundle_included_ids(cid, record.huiyuan_id) return item @@ -280,11 +311,16 @@ def _append_clumber_id_variants(items, seen, base_item, huiyuan_id): }) -def _append_bundle_sub_clumber(items, seen, record, base_item): - """总会员有效时,为抢单页展开子会员虚拟条目。""" +def _append_bundle_sub_clumber(items, seen, base_item): + """总会员有效时,为抢单页展开子会员虚拟条目(体验=正式)。""" if not base_item.get('is_bundle'): return - bundle_hid = str(record.huiyuan_id) + bundle_hid = str( + base_item.get('bundle_huiyuan_id') + or base_item.get('huiyuanid') + or base_item.get('huiyuan_id') + or '', + ) for sub_id in base_item.get('included_huiyuan_ids') or []: sid = str(sub_id).strip() if not sid: @@ -299,15 +335,17 @@ def _append_bundle_sub_clumber(items, seen, record, base_item): _append_clumber_id_variants(items, seen, sub_base, sid) -def _build_user_clumber_core(yonghu_id): +def _build_user_clumber_core(yonghu_id, club_id=None): + """huiyuangoumai 未过期会员 → clumber(体验与正式完全同等,不读 is_trial)。""" items = [] seen = set() + effective_club = _resolve_user_club_id(yonghu_id, club_id) for record in Huiyuangoumai.query.filter(yonghu_id=yonghu_id): if not _membership_record_active(record): continue - base = enrich_clumber_item(record) + base = enrich_clumber_item(record, club_id=effective_club) _append_clumber_id_variants(items, seen, base, record.huiyuan_id) - _append_bundle_sub_clumber(items, seen, record, base) + _append_bundle_sub_clumber(items, seen, base) return items @@ -332,17 +370,20 @@ def _merge_clumber_extra_items(items, extra): return items -def _build_clumber_from_paid_czjilu(yonghu_id): - """只读:huiyuangoumai 缺失时,用已支付会员充值单合成 clumber(体验=正式,不写库)。""" +def _build_clumber_from_paid_czjilu(yonghu_id, club_id=None): + """只读:有效期内充值单合成 clumber(体验=正式,不写库);总会员展开子类型。""" + from jituan.constants import CLUB_ID_DEFAULT from jituan.services.member_recharge import ( MEMBER_LEIXING, MEMBER_PAID_STATUS, czjilu_entitlement_active, + _czjilu_entitlement_end, ) from products.models import Czjilu, Huiyuan items = [] seen = set() + effective_club = _resolve_user_club_id(yonghu_id, club_id) for order in Czjilu.query.filter( yonghuid=yonghu_id, leixing=MEMBER_LEIXING, @@ -351,7 +392,7 @@ def _build_clumber_from_paid_czjilu(yonghu_id): hid = normalize_huiyuan_id(order.huiyuan_id) if not hid: continue - cid = getattr(order, 'club_id', None) or '' + cid = getattr(order, 'club_id', None) or effective_club or CLUB_ID_DEFAULT if not czjilu_entitlement_active(order, cid): continue try: @@ -359,7 +400,6 @@ def _build_clumber_from_paid_czjilu(yonghu_id): ming = hy.jieshao or f'会员{hid}' except Huiyuan.DoesNotExist: ming = f'会员{hid}' - from jituan.services.member_recharge import _czjilu_entitlement_end daoqi = _czjilu_entitlement_end(order, cid) base = { 'huiyuanming': ming, @@ -367,10 +407,25 @@ def _build_clumber_from_paid_czjilu(yonghu_id): 'huiyuan_zhuangtai': 1, 'shifou_daoqi': False, } + if is_bundle_huiyuan(hid): + base['is_bundle'] = True + base['bundle_huiyuan_id'] = hid + base['included_huiyuan_ids'] = get_bundle_included_ids(cid, hid) _append_clumber_id_variants(items, seen, base, hid) + _append_bundle_sub_clumber(items, seen, base) return items +def _finalize_clumber_for_legacy_client(items): + """老端只认 huiyuanid;去掉 is_trial 等内部字段。""" + cleaned = [] + for it in items or []: + row = dict(it) + row.pop('is_trial', None) + cleaned.append(row) + return cleaned + + def ensure_grab_pool_clumber(yonghu_id, club_id=None): """ dddhq / dashouxinxi:只读 huiyuangoumai + 有效期内充值单 生成 clumber(体验=正式)。 @@ -378,11 +433,16 @@ def ensure_grab_pool_clumber(yonghu_id, club_id=None): """ from jituan.services.member_recharge import repair_recent_paid_entitlements_if_missing - items = _build_user_clumber_core(yonghu_id) + effective_club = _resolve_user_club_id(yonghu_id, club_id) + items = _build_user_clumber_core(yonghu_id, club_id=effective_club) if not items: repair_recent_paid_entitlements_if_missing(yonghu_id, limit=3) - items = _build_user_clumber_core(yonghu_id) - items = _merge_clumber_extra_items(items, _build_clumber_from_paid_czjilu(yonghu_id)) + items = _build_user_clumber_core(yonghu_id, club_id=effective_club) + items = _merge_clumber_extra_items( + items, + _build_clumber_from_paid_czjilu(yonghu_id, club_id=effective_club), + ) + items = _finalize_clumber_for_legacy_client(items) return bool(items), items