From 86e7a6064229b9df74cc1ddc24041acdf7239c0a Mon Sep 17 00:00:00 2001 From: XingQue Date: Wed, 8 Jul 2026 14:05:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BA=AF=E5=90=8E=E7=AB=AF=E9=80=82?= =?UTF-8?q?=E9=85=8D=E8=80=81=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=8A=A2=E5=8D=95?= =?UTF-8?q?=E6=B1=A0=E4=BC=9A=E5=91=98=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /ddhq 有权限时返回 yaoqiuleixing=0;clumber 统一 huiyuanid+daoqi 字符串并补齐 ID 变体;读取前自动补写已付未落库的体验/正式会员。 Co-authored-by: Cursor --- jituan/services/huiyuan_bundle.py | 135 ++++++++++++++++++++--------- jituan/services/member_recharge.py | 29 +++++-- orders/views/dashou.py | 4 +- users/views/user_info.py | 2 +- 4 files changed, 122 insertions(+), 48 deletions(-) diff --git a/jituan/services/huiyuan_bundle.py b/jituan/services/huiyuan_bundle.py index ffd5f4a..7cc9724 100644 --- a/jituan/services/huiyuan_bundle.py +++ b/jituan/services/huiyuan_bundle.py @@ -1,4 +1,6 @@ """总会员(包含式会员):购买一个会员,可接多个子会员类型订单。""" +from django.utils import timezone + from products.models import Huiyuan, Huiyuangoumai @@ -21,6 +23,32 @@ def huiyuan_ids_match(a, b): return left_core == right_core +def expand_huiyuan_id_variants(huiyuan_id): + """老小程序用 == 比较会员 ID,补齐 1 / 01 / 001 等写法。""" + hid = normalize_huiyuan_id(huiyuan_id) + if not hid: + return [] + variants = [] + for candidate in (hid, hid.lstrip('0') or '0'): + if candidate and candidate not in variants: + variants.append(candidate) + core = hid.lstrip('0') or '0' + if core.isdigit(): + for width in (3, 4, 6): + padded = core.zfill(width) + if padded not in variants: + variants.append(padded) + return variants + + +def format_legacy_daoqi(dt): + if not dt: + return '' + if timezone.is_aware(dt): + dt = timezone.localtime(dt) + return dt.strftime('%Y-%m-%d %H:%M:%S') + + def get_bundle_included_ids(club_id, bundle_huiyuan_id): from jituan.models import ClubHuiyuanBundleInclude @@ -81,52 +109,72 @@ def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None): return False -def resolve_order_membership_id(order_dict): - """从订单字段解析所需会员 ID(订单未写 huiyuan_id 时回退商品类型)。""" - membership_id = normalize_huiyuan_id(order_dict.get('MembershipID')) - if membership_id: - return membership_id +def user_has_huiyuan_access_any(yonghu_id, required_ids, club_id=None): + for rid in required_ids or []: + if user_has_huiyuan_access(yonghu_id, rid, club_id=club_id): + return True + return False + + +def resolve_order_membership_ids(order_dict): + """订单可能关联的会员 ID(订单字段 + 商品类型配置,兼容老数据)。""" + ids = [] + mid = normalize_huiyuan_id(order_dict.get('MembershipID')) + if mid: + ids.append(mid) grab_req = int(order_dict.get('GrabRequirement') or 0) if grab_req != 1: - return '' + return ids leixing_id = order_dict.get('ProductTypeID') if not leixing_id: - return '' + return ids try: from products.models import ShangpinLeixing row = ShangpinLeixing.query.filter(id=leixing_id).values('huiyuan_id').first() if row: - return normalize_huiyuan_id(row.get('huiyuan_id')) + lx_hid = normalize_huiyuan_id(row.get('huiyuan_id')) + if lx_hid and not any(huiyuan_ids_match(lx_hid, x) for x in ids): + ids.append(lx_hid) except Exception: pass - return '' + return ids + + +def resolve_order_membership_id(order_dict): + ids = resolve_order_membership_ids(order_dict) + return ids[0] if ids else '' def resolve_grab_pool_member_display(order_dict, yonghu_id, club_id=None): """ - 抢单池订单的会员展示字段(兼容旧小程序): - - 已有权限:yaoqiuleixing=0,hasRequiredMember=True - - 无权限:保持 yaoqiuleixing=1 + 老小程序抢单池适配(仅改接口返回,无需发版): + - 老端展示:yaoqiuleixing==1 && huiyuan_id 时才判「未开通会员」 + - 老端抢单:yaoqiuleixing==1 时校验会员 + 已有权限时统一返回 yaoqiuleixing=0、huiyuan_id=''。 """ 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, True - if not membership_id: return grab_req, '', True - has_access = user_has_huiyuan_access(yonghu_id, membership_id, club_id=club_id) + + required_ids = resolve_order_membership_ids(order_dict) + if not required_ids: + return 0, '', True + + has_access = user_has_huiyuan_access_any(yonghu_id, required_ids, club_id=club_id) if has_access: return 0, '', True - return 1, membership_id, False + + return 1, required_ids[0], False def enrich_clumber_item(record): - """为 clumber 条目附加总会员覆盖的子会员 ID 列表(旧端忽略该字段)。""" + """老端 clumber 条目:必须有 huiyuanid + 字符串 daoqi。""" + daoqi_str = format_legacy_daoqi(record.daoqi_time) item = { 'huiyuanid': record.huiyuan_id, 'huiyuan_id': record.huiyuan_id, 'huiyuanming': record.jieshao or f'会员{record.huiyuan_id}', - 'daoqi': record.daoqi_time, + 'daoqi': daoqi_str, 'is_trial': bool(getattr(record, 'is_trial', False)), } if is_bundle_huiyuan(record.huiyuan_id): @@ -137,6 +185,19 @@ def enrich_clumber_item(record): return item +def _append_clumber_id_variants(items, seen, base_item, huiyuan_id): + """为老端 == 比较补齐多种会员 ID 写法。""" + for vid in expand_huiyuan_id_variants(huiyuan_id): + if vid in seen: + continue + seen.add(vid) + items.append({ + **base_item, + 'huiyuanid': vid, + 'huiyuan_id': vid, + }) + + def _append_bundle_sub_clumber(items, seen, record, base_item): """总会员有效时,为抢单页展开子会员虚拟条目。""" if not base_item.get('is_bundle'): @@ -144,26 +205,26 @@ def _append_bundle_sub_clumber(items, seen, record, base_item): bundle_hid = str(record.huiyuan_id) for sub_id in base_item.get('included_huiyuan_ids') or []: sid = str(sub_id).strip() - if not sid or sid in seen: + if not sid: continue - seen.add(sid) - items.append({ - 'huiyuanid': sid, - 'huiyuan_id': sid, + sub_base = { 'huiyuanming': base_item.get('huiyuanming', ''), - 'daoqi': base_item.get('daoqi'), + 'daoqi': base_item.get('daoqi', ''), 'from_bundle': bundle_hid, 'is_trial': bool(getattr(record, 'is_trial', False)), - }) + } + _append_clumber_id_variants(items, seen, sub_base, sid) def build_user_clumber(yonghu_id): """ - 构建抢单页可用的 clumber 列表。 - - 仅包含未过期且状态有效的会员(含体验会员) - - 总会员自动展开所覆盖的子会员 ID - - 同时返回 huiyuanid / huiyuan_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) items = [] seen = set() for record in Huiyuangoumai.query.filter(yonghu_id=yonghu_id): @@ -172,16 +233,15 @@ def build_user_clumber(yonghu_id): if record.huiyuan_zhuangtai != 1: continue base = enrich_clumber_item(record) - hid = str(record.huiyuan_id) - if hid not in seen: - seen.add(hid) - items.append(base) + _append_clumber_id_variants(items, seen, base, record.huiyuan_id) _append_bundle_sub_clumber(items, seen, record, base) return items def build_auth_clumber(yonghu_id): - """登录/注册返回的 clumber:保留状态字段,并补齐抢单页所需的 huiyuanid。""" + """登录接口 clumber:老端认 huiyuanid,保留原状态字段。""" + from jituan.services.member_recharge import sync_member_entitlements_for_user + sync_member_entitlements_for_user(yonghu_id) items = [] seen = set() for record in Huiyuangoumai.query.filter(yonghu_id=yonghu_id): @@ -193,10 +253,7 @@ def build_auth_clumber(yonghu_id): 'shifou_daoqi': shifou_daoqi, 'jieshao': record.jieshao, }) - hid = str(record.huiyuan_id) - if hid not in seen: - seen.add(hid) - items.append(base) + _append_clumber_id_variants(items, seen, base, record.huiyuan_id) if not shifou_daoqi and record.huiyuan_zhuangtai == 1: _append_bundle_sub_clumber(items, seen, record, base) return items diff --git a/jituan/services/member_recharge.py b/jituan/services/member_recharge.py index b8effac..dd266d3 100644 --- a/jituan/services/member_recharge.py +++ b/jituan/services/member_recharge.py @@ -634,11 +634,11 @@ def repair_member_entitlement_if_paid(dingdan_id): if not order or order.zhuangtai != MEMBER_PAID_STATUS or not order.huiyuan_id: return False - exists = Huiyuangoumai.query.filter( - yonghu_id=order.yonghuid, huiyuan_id=order.huiyuan_id, - ).exists() - if exists: - return True + from jituan.services.huiyuan_bundle import huiyuan_ids_match + for rec in Huiyuangoumai.query.filter(yonghu_id=order.yonghuid): + if huiyuan_ids_match(rec.huiyuan_id, order.huiyuan_id): + if not rec.jiance_shifou_daoqi() and rec.huiyuan_zhuangtai == 1: + return True try: huiyuan = Huiyuan.query.get(huiyuan_id=order.huiyuan_id) @@ -680,6 +680,25 @@ def repair_member_entitlement_if_paid(dingdan_id): return True +def sync_member_entitlements_for_user(yonghu_id, limit=15): + """读取打手资料/抢单池前,补写已支付但未落库的会员权益(幂等)。""" + if not yonghu_id: + return 0 + orders = Czjilu.query.filter( + yonghuid=yonghu_id, + leixing=MEMBER_LEIXING, + zhuangtai=MEMBER_PAID_STATUS, + ).order_by('-CreateTime')[: max(1, int(limit or 15))] + fixed = 0 + for order in orders: + try: + if repair_member_entitlement_if_paid(order.dingdan_id): + fixed += 1 + except Exception as exc: + logger.error('同步会员权益失败 dingdan=%s err=%s', order.dingdan_id, exc) + return fixed + + @transaction.atomic def fulfill_member_recharge(dingdan_id, paid_amount_yuan=None, source='callback'): locked = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update() diff --git a/orders/views/dashou.py b/orders/views/dashou.py index 4ff6f73..0c7234c 100644 --- a/orders/views/dashou.py +++ b/orders/views/dashou.py @@ -309,7 +309,7 @@ class DashouDingdanHuoquView(APIView): zid = order.get('AssignedID') info = zhiding_info.get(zid, {}) sj_uid = shangjia_id_map.get(oid) if order['Platform'] == 2 else None - display_grab_req, display_huiyuan_id, has_required_member = ( + display_grab_req, display_huiyuan_id, _has_required_member = ( resolve_grab_pool_member_display(order, yonghuid, club_id=club_id) ) formatted_list.append({ @@ -321,8 +321,6 @@ class DashouDingdanHuoquView(APIView): 'tupian': order['ImageURL'] or '', 'yaoqiuleixing': display_grab_req, 'huiyuan_id': display_huiyuan_id, - 'hasRequiredMember': has_required_member, - 'has_required_member': has_required_member, 'yajin': float(order['CommissionReq']) if order['CommissionReq'] else 0.0, 'jieshao': order['Description'] or '', 'beizhu': order['Remark'] or '', diff --git a/users/views/user_info.py b/users/views/user_info.py index aa1c499..5955128 100644 --- a/users/views/user_info.py +++ b/users/views/user_info.py @@ -337,7 +337,7 @@ class DashouXinxiAPIView(APIView): 'chenghao': dashou_profile.chenghao or '', } - # 4. 查询会员信息(含体验会员、总会员子类型展开) + # 4. 查询会员信息(老端 clumber:huiyuanid + daoqi 字符串) try: from jituan.services.huiyuan_bundle import build_user_clumber huiyuan_list = build_user_clumber(yonghuid)