Files
Django/jituan/management/commands/rollback_erroneous_member_restore.py
XingQue 7c3f04e7f3 fix: 禁止过期充值单补开通 + 管理命令收回错误恢复会员
rollback_erroneous_member_restore:后台已减仍有效、充值单已过期仍显示有效。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 15:56:11 +08:00

118 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
收回因 dddhq 每次 sync 错误补开通的会员权益。
两类处理:
1. 后台「减会员」日志存在但权益仍有效 → 再次 revoke删记录+标记充值单撤销)
2. 当前仍显示有效,但已无任何未过期充值单支撑 → 置为过期
用法:
python manage.py rollback_erroneous_member_restore --dry-run
python manage.py rollback_erroneous_member_restore
"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.services.huiyuan_bundle import huiyuan_ids_match
from jituan.services.member_recharge import (
ENTITLEMENT_REVOKED_MARKER,
expire_huiyuangoumai_record,
revoke_member_entitlement_admin,
user_has_active_czjilu_entitlement,
)
from products.models import Huiyuangoumai
from users.models import Xiugaijilu
class Command(BaseCommand):
help = '收回错误恢复的会员(后台已减仍有效 / 充值单已过期仍显示有效)'
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='仅统计,不写库',
)
def handle(self, *args, **options):
dry_run = bool(options.get('dry_run'))
admin_revoke = 0
expire_orphan = 0
samples = []
with transaction.atomic():
# 1) 后台减会员日志 → 仍有效的再 revoke 一遍
logs = Xiugaijilu.query.filter(
huiyuan_id__isnull=False,
).exclude(huiyuan_id='').order_by('-CreateTime')
seen_pairs = set()
for log in logs:
note = (log.qitashuoming or '') + (log.huiyuan_id or '')
if '移除' not in (log.qitashuoming or '') and '减会员' not in (log.qitashuoming or ''):
continue
if not log.yonghuid or not log.huiyuan_id:
continue
pair = (log.yonghuid, str(log.huiyuan_id).strip())
if pair in seen_pairs:
continue
seen_pairs.add(pair)
active = False
for rec in Huiyuangoumai.query.filter(yonghu_id=log.yonghuid):
if not huiyuan_ids_match(rec.huiyuan_id, log.huiyuan_id):
continue
if not rec.jiance_shifou_daoqi() and rec.huiyuan_zhuangtai == 1:
active = True
break
if not active:
continue
admin_revoke += 1
samples.append(f'[后台已减仍有效] {log.yonghuid} 会员{log.huiyuan_id}')
if not dry_run:
revoke_member_entitlement_admin(log.yonghuid, log.huiyuan_id)
# 2) 仍显示有效,但没有任何未过期充值单支撑 → 过期收回
for rec in Huiyuangoumai.query.filter(huiyuan_zhuangtai=1):
if rec.jiance_shifou_daoqi():
continue
from products.models import Czjilu
from jituan.services.member_recharge import MEMBER_LEIXING, MEMBER_PAID_STATUS
paid_orders = [
o for o in Czjilu.query.filter(
yonghuid=rec.yonghu_id,
leixing=MEMBER_LEIXING,
zhuangtai=MEMBER_PAID_STATUS,
)
if huiyuan_ids_match(o.huiyuan_id, rec.huiyuan_id)
]
if not paid_orders:
continue
if user_has_active_czjilu_entitlement(
rec.yonghu_id, rec.huiyuan_id, rec.club_id,
):
continue
expire_orphan += 1
samples.append(
f'[充值单已过期仍有效] {rec.yonghu_id} 会员{rec.huiyuan_id} '
f'到期{rec.daoqi_time}',
)
if not dry_run:
expire_huiyuangoumai_record(
rec,
reason='rollback_erroneous_member_restore',
)
if dry_run:
transaction.set_rollback(True)
mode = 'DRY-RUN' if dry_run else 'APPLIED'
self.stdout.write(self.style.SUCCESS(
f'[{mode}] 后台减会员仍有效收回 {admin_revoke} 条;'
f'无有效充值单支撑过期 {expire_orphan}',
))
for line in samples[:50]:
self.stdout.write(f' {line}')
if len(samples) > 50:
self.stdout.write(f' ... 另有 {len(samples) - 50}')