fix: 总会员按子会员迁出;我的会员去掉展开重复展示
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -477,11 +477,43 @@ def _resolve_migrate_target_huiyuan_ids(
|
||||
*, src_huiyuan_id: str, from_club: str = '', to_club: str = '',
|
||||
) -> List[str]:
|
||||
"""
|
||||
会员 ID 集团公共(含总会员):原样迁同一 ID。
|
||||
不要求目标店 ClubHuiyuanPrice 上架,不拆成子会员。
|
||||
普通会员:原样迁同 ID。
|
||||
总会员:按子会员格式迁出(写入各子会员开通记录),不在目标店再挂一条总会员。
|
||||
未配置子项时才退回迁总会员本身,避免丢权益。
|
||||
"""
|
||||
src_id = str(src_huiyuan_id or '').strip()
|
||||
return [src_id] if src_id else []
|
||||
from jituan.services.huiyuan_bundle import (
|
||||
is_bundle_huiyuan,
|
||||
normalize_huiyuan_id,
|
||||
resolve_bundle_included_ids,
|
||||
)
|
||||
|
||||
src_id = normalize_huiyuan_id(src_huiyuan_id)
|
||||
if not src_id:
|
||||
return []
|
||||
if not is_bundle_huiyuan(src_id):
|
||||
return [src_id]
|
||||
|
||||
kids = []
|
||||
seen = set()
|
||||
for cid in (from_club, to_club):
|
||||
for hid in resolve_bundle_included_ids(
|
||||
src_id,
|
||||
club_id=(cid or '').strip() or None,
|
||||
record_club_id=(from_club or '').strip() or None,
|
||||
) or []:
|
||||
kid = normalize_huiyuan_id(hid)
|
||||
if not kid:
|
||||
continue
|
||||
core = kid.lstrip('0') or '0'
|
||||
if core in seen:
|
||||
continue
|
||||
seen.add(core)
|
||||
kids.append(kid)
|
||||
if kids:
|
||||
break
|
||||
if kids:
|
||||
return kids
|
||||
return [src_id]
|
||||
|
||||
|
||||
def _revoke_source_member_czjilu(from_uid: str, huiyuan_id: str):
|
||||
@@ -627,14 +659,18 @@ def _add_huiyuan_duration(*, to_uid: str, to_club: str, huiyuan_id: str, remain,
|
||||
|
||||
def _transfer_huiyuan(*, from_uid, to_uid, from_club, to_club, role) -> List[dict]:
|
||||
"""
|
||||
未到期会员:目标店加同等剩余时长(同会员 ID,含总会员),源店置已到期并撤销充值权益。
|
||||
数据来源与打手端 clumber 一致(开通表 + 充值单)。
|
||||
未到期会员:目标店加同等剩余时长;源店置已到期并撤销充值权益。
|
||||
总会员按子会员拆分写入(不用总会员 ID 在目标店再挂一条)。
|
||||
"""
|
||||
from products.models import Huiyuan
|
||||
from jituan.services.huiyuan_bundle import is_bundle_huiyuan, normalize_huiyuan_id
|
||||
|
||||
now = _now_aware()
|
||||
entries = _collect_migratable_huiyuan(from_uid, from_club, for_update=True)
|
||||
out = []
|
||||
# 同一迁移内按子会员去重,避免源店「总会员+子会员」重复加时长
|
||||
transferred_cores = set()
|
||||
|
||||
for e in entries:
|
||||
src_exp = _aware(e.get('daoqi_time'))
|
||||
name = (e.get('jieshao') or '').strip()
|
||||
@@ -654,8 +690,12 @@ def _transfer_huiyuan(*, from_uid, to_uid, from_club, to_club, role) -> List[dic
|
||||
if remain is None:
|
||||
continue
|
||||
|
||||
tid = (_resolve_migrate_target_huiyuan_ids(src_huiyuan_id=e['huiyuan_id']) or [None])[0]
|
||||
if not tid:
|
||||
target_ids = _resolve_migrate_target_huiyuan_ids(
|
||||
src_huiyuan_id=e['huiyuan_id'],
|
||||
from_club=from_club,
|
||||
to_club=to_club,
|
||||
)
|
||||
if not target_ids:
|
||||
raise ValueError(f'会员 ID 无效,无法迁移: {e["huiyuan_id"]}')
|
||||
|
||||
formal_floor = 0
|
||||
@@ -666,23 +706,40 @@ def _transfer_huiyuan(*, from_uid, to_uid, from_club, to_club, role) -> List[dic
|
||||
)
|
||||
formal_floor = max(int(src_paid or 0), 1)
|
||||
|
||||
wrote_any = False
|
||||
src_is_bundle = is_bundle_huiyuan(e['huiyuan_id'])
|
||||
for tid in target_ids:
|
||||
tid = normalize_huiyuan_id(tid) or tid
|
||||
core = (tid.lstrip('0') or '0') if tid else ''
|
||||
if core and core in transferred_cores:
|
||||
continue
|
||||
|
||||
child_name = name
|
||||
if src_is_bundle or tid != normalize_huiyuan_id(e['huiyuan_id']):
|
||||
chy = Huiyuan.query.filter(huiyuan_id=tid).first()
|
||||
if not chy:
|
||||
from jituan.services.huiyuan_bundle import expand_huiyuan_id_variants
|
||||
for vid in expand_huiyuan_id_variants(tid):
|
||||
chy = Huiyuan.query.filter(huiyuan_id=vid).first()
|
||||
if chy:
|
||||
break
|
||||
child_name = ((chy.jieshao if chy else '') or '').strip() or tid
|
||||
|
||||
exp, before_dst = _add_huiyuan_duration(
|
||||
to_uid=to_uid,
|
||||
to_club=to_club,
|
||||
huiyuan_id=tid,
|
||||
remain=remain,
|
||||
jieshao=name,
|
||||
jieshao=child_name,
|
||||
is_trial=bool(e.get('is_trial')),
|
||||
has_used_trial=bool(e.get('has_used_trial')),
|
||||
)
|
||||
if exp is None:
|
||||
raise ValueError(f'会员过户失败(加时长): {name}({tid})')
|
||||
raise ValueError(f'会员过户失败(加时长): {child_name}({tid})')
|
||||
|
||||
_expire_source_membership(
|
||||
from_uid=from_uid,
|
||||
huiyuan_id=e['huiyuan_id'],
|
||||
goumai_row=e.get('goumai_row'),
|
||||
)
|
||||
if core:
|
||||
transferred_cores.add(core)
|
||||
wrote_any = True
|
||||
out.append({
|
||||
'item_type': ClubMigrateLedgerItem.ITEM_HUIYUAN,
|
||||
'role': role,
|
||||
@@ -697,13 +754,22 @@ def _transfer_huiyuan(*, from_uid, to_uid, from_club, to_club, role) -> List[dic
|
||||
'before_src': before,
|
||||
'before_dst': before_dst,
|
||||
'src_huiyuan_id': e['huiyuan_id'],
|
||||
'name': name,
|
||||
'mapped_from_bundle': False,
|
||||
'name': child_name,
|
||||
'mapped_from_bundle': bool(src_is_bundle and tid != normalize_huiyuan_id(e['huiyuan_id'])),
|
||||
'formal_purchase_floor': formal_floor,
|
||||
'is_trial': bool(e.get('is_trial')),
|
||||
'source': e.get('source') or '',
|
||||
},
|
||||
})
|
||||
|
||||
if not wrote_any:
|
||||
raise ValueError(f'会员过户失败: {name}({e["huiyuan_id"]})')
|
||||
|
||||
_expire_source_membership(
|
||||
from_uid=from_uid,
|
||||
huiyuan_id=e['huiyuan_id'],
|
||||
goumai_row=e.get('goumai_row'),
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -365,6 +365,7 @@ def enrich_clumber_item(record, club_id=None):
|
||||
'daoqi': daoqi_str,
|
||||
'huiyuan_zhuangtai': 1,
|
||||
'shifou_daoqi': False,
|
||||
'display_owned': True,
|
||||
}
|
||||
if is_bundle_huiyuan(record.huiyuan_id):
|
||||
item['is_bundle'] = True
|
||||
@@ -379,16 +380,30 @@ def enrich_clumber_item(record, club_id=None):
|
||||
|
||||
|
||||
def _append_clumber_id_variants(items, seen, base_item, huiyuan_id):
|
||||
"""为老端 == 比较补齐多种会员 ID 写法。"""
|
||||
"""为老端 == 比较补齐多种会员 ID 写法。仅首条供「我的会员」展示。"""
|
||||
hide_owned = (
|
||||
base_item.get('display_owned') is False
|
||||
or bool(base_item.get('from_bundle'))
|
||||
or bool(base_item.get('id_variant'))
|
||||
)
|
||||
first = True
|
||||
for vid in expand_huiyuan_id_variants(huiyuan_id):
|
||||
if vid in seen:
|
||||
continue
|
||||
seen.add(vid)
|
||||
items.append({
|
||||
row = {
|
||||
**base_item,
|
||||
'huiyuanid': vid,
|
||||
'huiyuan_id': vid,
|
||||
})
|
||||
}
|
||||
if hide_owned or not first:
|
||||
row['display_owned'] = False
|
||||
if not first:
|
||||
row['id_variant'] = True
|
||||
else:
|
||||
row['display_owned'] = True
|
||||
first = False
|
||||
items.append(row)
|
||||
|
||||
|
||||
def _append_bundle_sub_clumber(items, seen, base_item):
|
||||
@@ -405,10 +420,23 @@ def _append_bundle_sub_clumber(items, seen, base_item):
|
||||
sid = str(sub_id).strip()
|
||||
if not sid:
|
||||
continue
|
||||
# 子项用子会员自己的名称;不进「我的会员」主列表(仅抢单匹配)
|
||||
child_name = ''
|
||||
try:
|
||||
hy = Huiyuan.query.filter(huiyuan_id=sid).first()
|
||||
if not hy:
|
||||
for vid in expand_huiyuan_id_variants(sid):
|
||||
hy = Huiyuan.query.filter(huiyuan_id=vid).first()
|
||||
if hy:
|
||||
break
|
||||
child_name = ((hy.jieshao if hy else '') or '').strip()
|
||||
except Exception:
|
||||
child_name = ''
|
||||
sub_base = {
|
||||
'huiyuanming': base_item.get('huiyuanming', ''),
|
||||
'huiyuanming': child_name or sid,
|
||||
'daoqi': base_item.get('daoqi', ''),
|
||||
'from_bundle': bundle_hid,
|
||||
'display_owned': False,
|
||||
'huiyuan_zhuangtai': 1,
|
||||
'shifou_daoqi': False,
|
||||
}
|
||||
@@ -486,6 +514,7 @@ def _build_clumber_from_paid_czjilu(yonghu_id, club_id=None):
|
||||
'daoqi': format_legacy_daoqi(daoqi),
|
||||
'huiyuan_zhuangtai': 1,
|
||||
'shifou_daoqi': False,
|
||||
'display_owned': True,
|
||||
}
|
||||
if is_bundle_huiyuan(hid):
|
||||
base['is_bundle'] = True
|
||||
|
||||
Reference in New Issue
Block a user