Files
Django/jituan/management/commands/cleanup_lsx_empty_club.py

211 lines
7.8 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.
"""清理龙先生重复/无 pem 证书俱乐部。
规则:
- 候选club_id=lsx / 名称含「龙先生」 / wx_appid=龙先生小程序
- 「有 pem」pay_key_path / pay_cert_path / private_key_path 任一路径以 .pem 结尾
(你后台上传的证书路径都是 *.pem脚本自动建的/空配置通常没有)
- 「可删」:上述三个字段都没有以 .pem 结尾的路径
- 默认 dry-run--apply 才删除
- 若候选里没有任何「有 pem」的对照默认不删加 --force-delete-empty-only 才删
用法:
python manage.py cleanup_lsx_empty_club
python manage.py cleanup_lsx_empty_club --apply
python manage.py cleanup_lsx_empty_club --apply --remove-seed-users
"""
from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models import Q
from jituan.models import (
Club,
ClubDashouExamConfig,
ClubDashouExamPass,
ClubDashouExamQuestion,
ClubDashouExamQuestionImage,
ClubFadanFenhongLilv,
ClubHuiyuanBundleInclude,
ClubHuiyuanPrice,
ClubKaoheChenghaoConfig,
ClubLilubiao,
ClubPaymentChannel,
ClubShangpinLeixingConfig,
ClubTixianQuota,
ClubWithdrawConfig,
)
from users.business_models import User
from users.models import UserBoss, UserGuanshi, UserZuzhang
LSX_APPID = 'wx7ff90e9d024fcdb8'
SEED_UIDS = ('9910001', '9910002')
RELATED_MODELS = (
ClubPaymentChannel,
ClubHuiyuanPrice,
ClubHuiyuanBundleInclude,
ClubLilubiao,
ClubWithdrawConfig,
ClubTixianQuota,
ClubShangpinLeixingConfig,
ClubKaoheChenghaoConfig,
ClubFadanFenhongLilv,
ClubDashouExamConfig,
ClubDashouExamQuestion,
ClubDashouExamQuestionImage,
ClubDashouExamPass,
)
# 证书「文件」字段(应以 .pem 结尾platform_cert_dir 是目录,不参与判定
_PEM_FILE_FIELDS = ('pay_key_path', 'pay_cert_path', 'private_key_path')
def _path_ends_with_pem(path):
return str(path or '').strip().lower().endswith('.pem')
def _has_pem_paths(club):
"""后台上传过证书:路径以 .pem 结尾。"""
return any(_path_ends_with_pem(getattr(club, f, '')) for f in _PEM_FILE_FIELDS)
def _pem_field_summary(club):
lines = []
for f in _PEM_FILE_FIELDS:
val = (getattr(club, f, '') or '').strip()
if not val:
tag = ''
elif _path_ends_with_pem(val):
tag = 'pem✓'
else:
tag = '非pem✗'
lines.append(f'{f}={val or "(空)"} [{tag}]')
plat = (getattr(club, 'platform_cert_dir', '') or '').strip()
lines.append(f'platform_cert_dir={plat or "(空)"} [目录仅展示]')
return lines
def _candidates():
return Club.query.filter(
Q(club_id__iexact='lsx')
| Q(club_id__icontains='lsx')
| Q(name__icontains='龙先生')
| Q(wx_appid=LSX_APPID)
).order_by('club_id')
def _purge_club_related(club_id, stdout, style):
for model in RELATED_MODELS:
try:
n = model.query.filter(club_id=club_id).count()
if n:
model.query.filter(club_id=club_id).delete()
stdout.write(f' - 已删 {model.__name__} x{n}')
except Exception as exc:
stdout.write(style.WARNING(f' - 跳过 {model.__name__}: {exc}'))
class Command(BaseCommand):
help = '列出并删除龙先生相关「证书路径不以 .pem 结尾」的重复俱乐部(默认 dry-run'
def add_arguments(self, parser):
parser.add_argument('--apply', action='store_true', help='真正写入删除')
parser.add_argument(
'--force-delete-empty-only',
action='store_true',
help='即使没有「有 .pem」的对照俱乐部也允许删除非 pem 候选',
)
parser.add_argument(
'--remove-seed-users',
action='store_true',
help='同时删除 seed_lsx_invite_data 种子用户 9910001/9910002',
)
parser.add_argument(
'--club-id',
type=str,
default='',
help='只处理指定 club_id精确匹配',
)
def handle(self, *args, **options):
apply = bool(options.get('apply'))
force = bool(options.get('force_delete_empty_only'))
only_id = (options.get('club_id') or '').strip()
qs = _candidates()
if only_id:
qs = Club.query.filter(club_id=only_id)
clubs = list(qs)
if not clubs:
self.stdout.write(self.style.WARNING('未找到龙先生/lsx 相关俱乐部'))
return
with_pem = []
without_pem = []
self.stdout.write(self.style.SUCCESS('=' * 60))
self.stdout.write('判定标准pay_key_path / pay_cert_path / private_key_path 是否以 .pem 结尾')
self.stdout.write('龙先生相关俱乐部扫描结果:')
for c in clubs:
keyed = _has_pem_paths(c)
(with_pem if keyed else without_pem).append(c)
self.stdout.write('-' * 40)
self.stdout.write(f'club_id={c.club_id} name={c.name} status={c.status}')
self.stdout.write(f' wx_appid={c.wx_appid or "(空)"}')
for line in _pem_field_summary(c):
self.stdout.write(f' {line}')
self.stdout.write(
f' >>> {"有 .pem 路径(保留)" if keyed else "无 .pem 路径(可删)"}'
)
self.stdout.write(self.style.SUCCESS('=' * 60))
self.stdout.write(f'有 .pem: {len(with_pem)} 条;无 .pem: {len(without_pem)}')
if not without_pem:
self.stdout.write(self.style.SUCCESS('没有「无 .pem 路径」的俱乐部,无需删除。'))
elif not with_pem and not force:
self.stdout.write(self.style.ERROR(
'全部候选都没有 .pem 路径。为安全起见不自动删。'
'若确认要删,加 --force-delete-empty-only --apply'
))
else:
for c in without_pem:
self.stdout.write(self.style.WARNING(
f'将删除无 .pem 俱乐部: {c.club_id} ({c.name})'
))
if not apply:
self.stdout.write(' (dry-run未真正删除加 --apply 执行)')
continue
with transaction.atomic():
_purge_club_related(c.club_id, self.stdout, self.style)
Club.query.filter(club_id=c.club_id).delete()
self.stdout.write(self.style.SUCCESS(f' 已删除俱乐部 {c.club_id}'))
if options.get('remove_seed_users'):
self.stdout.write('-' * 40)
self.stdout.write('处理种子用户 9910001 / 9910002 ...')
for uid in SEED_UIDS:
user = User.query.filter(UserUID=uid).first()
if not user:
self.stdout.write(f' {uid}: 不存在')
continue
self.stdout.write(
f' {uid}: ClubID={user.ClubID} Phone={user.Phone} OpenID={user.OpenID}'
)
if not apply:
self.stdout.write(' (dry-run)')
continue
with transaction.atomic():
UserZuzhang.query.filter(user=user).delete()
UserGuanshi.query.filter(user=user).delete()
UserBoss.query.filter(user=user).delete()
user.delete()
self.stdout.write(self.style.SUCCESS(f' 已删除用户 {uid}'))
if not apply:
self.stdout.write(self.style.WARNING(
'\n当前为预览。确认后执行:\n'
' python manage.py cleanup_lsx_empty_club --apply\n'
'如需顺带删种子账号:\n'
' python manage.py cleanup_lsx_empty_club --apply --remove-seed-users'
))