fix: 纯后端适配老小程序抢单池会员展示
/ddhq 有权限时返回 yaoqiuleixing=0;clumber 统一 huiyuanid+daoqi 字符串并补齐 ID 变体;读取前自动补写已付未落库的体验/正式会员。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 '',
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user