fix: 迁移积分/分红次数/冻结入账,并修正无计划误导文案
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -41,6 +41,21 @@ OPEN_ORDER_STATUSES = (1, 2, 4, 7, 8, 9)
|
||||
# 未缴清罚单:待缴纳/申诉中/平台审核中
|
||||
UNPAID_PENALTY_STATUSES = (1, 3, 5)
|
||||
|
||||
# 目标店新建打手时绑定的默认管事邀请码(与小程序 PLATFORM 默认码一致)
|
||||
CLUB_MIGRATE_DEFAULT_GUANSHI_INVITE = {
|
||||
'lxs': '0000008VfJKPu4Spmcjt',
|
||||
'xq': '0000008RffVgKHMj7kQC',
|
||||
}
|
||||
|
||||
# 可提现类字段:迁入走目标店冻结网关(押金/积分/已冻结池除外)
|
||||
_FREEZE_CREDIT_FIELDS = frozenset({
|
||||
('dashou', 'yue'),
|
||||
('guanshi', 'yue'),
|
||||
('zuzhang', 'ketixian_jine'),
|
||||
('shenheguan', 'yue'),
|
||||
})
|
||||
MEMBER_JIFEN_CAP = 10
|
||||
|
||||
|
||||
def _money(v) -> Decimal:
|
||||
return Decimal(str(v or 0)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
|
||||
@@ -129,8 +144,25 @@ def _load_user(uid: str):
|
||||
return User.query.filter(UserUID=str(uid)).first()
|
||||
|
||||
|
||||
def _get_or_create_profile(user, role: str):
|
||||
"""目标店无角色时创建空扩展行(仅迁移模块调用)。"""
|
||||
def _resolve_default_guanshi_uid(club_id: str) -> Optional[str]:
|
||||
"""按俱乐部硬编码邀请码解析默认管事 UID;解析失败返回 None。"""
|
||||
code = CLUB_MIGRATE_DEFAULT_GUANSHI_INVITE.get((club_id or '').strip())
|
||||
if not code:
|
||||
return None
|
||||
from users.models import UserGuanshi
|
||||
gs = UserGuanshi.query.filter(yaoqingma=code).select_related('user').first()
|
||||
if not gs or not getattr(gs, 'user', None):
|
||||
logger.warning('migrate default guanshi invite not found club=%s code=%s', club_id, code)
|
||||
return None
|
||||
return str(gs.user.UserUID)
|
||||
|
||||
|
||||
def _get_or_create_profile(user, role: str, *, to_club_id: str = ''):
|
||||
"""目标店无角色时创建空扩展行(仅迁移模块调用)。
|
||||
|
||||
新建打手:绑定目标店默认管事(源店邀请人在新店无效)。
|
||||
已有打手资料:不改 yaoqingren(曾在新店注册过的情况保留原绑定)。
|
||||
"""
|
||||
from users.models import UserDashou, UserShangjia, UserGuanshi, UserZuzhang, UserShenheguan
|
||||
|
||||
mapping = {
|
||||
@@ -150,6 +182,9 @@ def _get_or_create_profile(user, role: str):
|
||||
defaults['nicheng'] = f'SJ{user.UserUID}'
|
||||
elif role == 'dashou':
|
||||
defaults['nicheng'] = f'DS{user.UserUID}'
|
||||
gs_uid = _resolve_default_guanshi_uid(to_club_id)
|
||||
if gs_uid:
|
||||
defaults['yaoqingren'] = gs_uid
|
||||
obj = model.query.create(**defaults)
|
||||
return obj
|
||||
|
||||
@@ -425,6 +460,15 @@ def _transfer_huiyuan(*, from_uid, to_uid, from_club, to_club, role) -> List[dic
|
||||
if tid != r.huiyuan_id:
|
||||
chy = Huiyuan.query.filter(huiyuan_id=tid).first()
|
||||
child_name = (chy.jieshao if chy else '') or tid
|
||||
# 正式会员迁入:继承源店正式购买次数,且至少 1(再充不可能算首次分红)
|
||||
formal_floor = 0
|
||||
if not bool(r.is_trial):
|
||||
from jituan.services.member_recharge import count_formal_paid_member_orders
|
||||
src_paid = count_formal_paid_member_orders(
|
||||
from_uid, r.huiyuan_id, from_club,
|
||||
)
|
||||
formal_floor = max(int(src_paid or 0), 1)
|
||||
|
||||
exp, before_dst = _add_huiyuan_duration(
|
||||
to_uid=to_uid,
|
||||
to_club=to_club,
|
||||
@@ -450,6 +494,8 @@ def _transfer_huiyuan(*, from_uid, to_uid, from_club, to_club, role) -> List[dic
|
||||
'src_huiyuan_id': r.huiyuan_id,
|
||||
'name': child_name,
|
||||
'mapped_from_bundle': tid != r.huiyuan_id,
|
||||
'formal_purchase_floor': formal_floor,
|
||||
'is_trial': bool(r.is_trial),
|
||||
},
|
||||
})
|
||||
return out
|
||||
@@ -510,6 +556,7 @@ def gate_for_user(*, club_id: str, user_uid: str, roles: Optional[List[str]] = N
|
||||
"""小程序启动/进打手端调用。无计划 → 全 false,现网无感。"""
|
||||
plan = find_active_plan_for_source(club_id)
|
||||
if not plan:
|
||||
target = find_active_plan_for_target(club_id)
|
||||
return {
|
||||
'active': False,
|
||||
'force': False,
|
||||
@@ -517,6 +564,8 @@ def gate_for_user(*, club_id: str, user_uid: str, roles: Optional[List[str]] = N
|
||||
'completed': False,
|
||||
'plan_id': '',
|
||||
'to_club_id': '',
|
||||
'is_target': bool(target),
|
||||
'target_from_club_id': (target.from_club_id if target else ''),
|
||||
}
|
||||
done = not can_migrate_again(plan, user_uid)
|
||||
force_roles = plan.force_roles or ['dashou']
|
||||
@@ -536,6 +585,7 @@ def gate_for_user(*, club_id: str, user_uid: str, roles: Optional[List[str]] = N
|
||||
'force_roles': force_roles,
|
||||
'success_count': success_count(plan.plan_id, user_uid),
|
||||
'max_allowed': max_allowed_success(plan, user_uid),
|
||||
'is_target': False,
|
||||
}
|
||||
|
||||
|
||||
@@ -662,10 +712,11 @@ def confirm_migrate(
|
||||
# 目标店:白名单涉及角色先建空扩展行
|
||||
for role in roles_from_whitelist(keys):
|
||||
if _profile_of(to_user, role) is None:
|
||||
_get_or_create_profile(to_user, role)
|
||||
_get_or_create_profile(to_user, role, to_club_id=plan.to_club_id)
|
||||
|
||||
items_payload = []
|
||||
migrate_seq = int(cons.success_count or 0) + 1
|
||||
freeze_biz_prefix = f'cm:{token_row.token}:{migrate_seq}'
|
||||
|
||||
for key in keys:
|
||||
meta = ASSET_CATALOG[key]
|
||||
@@ -689,6 +740,8 @@ def confirm_migrate(
|
||||
kind=meta['kind'],
|
||||
key=key,
|
||||
label=meta['label'],
|
||||
to_club_id=plan.to_club_id,
|
||||
freeze_biz_id=f'{freeze_biz_prefix}:{key}'[:64],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -735,7 +788,18 @@ def confirm_migrate(
|
||||
return serialize_ledger(ledger), ''
|
||||
|
||||
|
||||
def _transfer_balance_field(*, from_user, to_user, role, field, kind, key, label) -> dict:
|
||||
def _transfer_balance_field(
|
||||
*,
|
||||
from_user,
|
||||
to_user,
|
||||
role,
|
||||
field,
|
||||
kind,
|
||||
key,
|
||||
label,
|
||||
to_club_id: str = '',
|
||||
freeze_biz_id: str = '',
|
||||
) -> dict:
|
||||
src_prof = _lock_profile(from_user, role)
|
||||
amount = _read_field(src_prof, field, kind)
|
||||
before_src = amount
|
||||
@@ -750,17 +814,53 @@ def _transfer_balance_field(*, from_user, to_user, role, field, kind, key, label
|
||||
dst_prof = _lock_profile(to_user, role)
|
||||
created = False
|
||||
if dst_prof is None:
|
||||
dst_prof = _get_or_create_profile(to_user, role)
|
||||
dst_prof = _get_or_create_profile(to_user, role, to_club_id=to_club_id)
|
||||
created = True
|
||||
dst_prof = _lock_profile(to_user, role)
|
||||
|
||||
before_dst = _read_field(dst_prof, field, kind) if dst_prof else (0 if kind == 'int' else Decimal('0.00'))
|
||||
after_dst = before_dst
|
||||
meta_extra = {}
|
||||
|
||||
if dst_prof is not None and amount:
|
||||
if kind == 'int':
|
||||
# 积分:目标已满 10 保持 10;相加后也不得超过 10
|
||||
if field == 'jifen' and kind == 'int':
|
||||
if int(before_dst) >= MEMBER_JIFEN_CAP:
|
||||
after_dst = MEMBER_JIFEN_CAP
|
||||
else:
|
||||
after_dst = min(MEMBER_JIFEN_CAP, int(before_dst) + int(amount))
|
||||
type(dst_prof).objects.filter(pk=dst_prof.pk).update(jifen=int(after_dst))
|
||||
meta_extra['jifen_capped'] = True
|
||||
meta_extra['jifen_cap'] = MEMBER_JIFEN_CAP
|
||||
elif (role, field) in _FREEZE_CREDIT_FIELDS:
|
||||
# 可提现入账:走目标店冻结网关(开了冻结则按配置拆分)
|
||||
from jituan.services.fund_freeze import credit_role_balance, SOURCE_CLUB_MIGRATE
|
||||
to_uid = str(getattr(to_user, 'UserUID', '') or '')
|
||||
credit_res = credit_role_balance(
|
||||
club_id=to_club_id,
|
||||
user_id=to_uid,
|
||||
role=role,
|
||||
amount=_money(amount),
|
||||
source_type=SOURCE_CLUB_MIGRATE,
|
||||
biz_id=(freeze_biz_id or f'cm:{to_uid}:{key}')[:64],
|
||||
profile=dst_prof,
|
||||
freeze_reason='跨店资产迁移入账',
|
||||
ignore_source_scope=True,
|
||||
)
|
||||
dst_prof = _lock_profile(to_user, role)
|
||||
after_dst = _read_field(dst_prof, field, kind) if dst_prof else before_dst
|
||||
meta_extra.update({
|
||||
'freeze_available': str(credit_res.get('available') or 0),
|
||||
'freeze_frozen': str(credit_res.get('frozen') or 0),
|
||||
'freeze_ledger_id': credit_res.get('ledger_id'),
|
||||
'freeze_skipped': bool(credit_res.get('skipped_freeze')),
|
||||
'freeze_skip_reason': credit_res.get('skip_reason'),
|
||||
})
|
||||
elif kind == 'int':
|
||||
type(dst_prof).objects.filter(pk=dst_prof.pk).update(**{field: F(field) + int(amount)})
|
||||
after_dst = int(before_dst) + int(amount)
|
||||
else:
|
||||
# 押金 / 已冻结池等:原样累加,不再二次冻结
|
||||
type(dst_prof).objects.filter(pk=dst_prof.pk).update(**{field: F(field) + _money(amount)})
|
||||
after_dst = _money(before_dst) + _money(amount)
|
||||
|
||||
@@ -773,7 +873,7 @@ def _transfer_balance_field(*, from_user, to_user, role, field, kind, key, label
|
||||
'after_src': after_src if amount else before_src,
|
||||
'before_dst': before_dst,
|
||||
'after_dst': after_dst,
|
||||
'meta_json': {'key': key, 'label': label, 'dst_created': created},
|
||||
'meta_json': {'key': key, 'label': label, 'dst_created': created, **meta_extra},
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user