fix: 纯后端适配老小程序抢单池会员展示

/ddhq 有权限时返回 yaoqiuleixing=0;clumber 统一 huiyuanid+daoqi 字符串并补齐 ID 变体;读取前自动补写已付未落库的体验/正式会员。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-08 14:05:22 +08:00
parent dfd4bed221
commit 86e7a60642
4 changed files with 122 additions and 48 deletions

View File

@@ -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=0hasRequiredMember=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

View File

@@ -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()

View File

@@ -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 '',

View File

@@ -337,7 +337,7 @@ class DashouXinxiAPIView(APIView):
'chenghao': dashou_profile.chenghao or '',
}
# 4. 查询会员信息(含体验会员、总会员子类型展开
# 4. 查询会员信息(老端 clumberhuiyuanid + daoqi 字符串
try:
from jituan.services.huiyuan_bundle import build_user_clumber
huiyuan_list = build_user_clumber(yonghuid)