diff --git a/jituan/management/commands/cleanup_lsx_empty_club.py b/jituan/management/commands/cleanup_lsx_empty_club.py index 342a166..91b243a 100644 --- a/jituan/management/commands/cleanup_lsx_empty_club.py +++ b/jituan/management/commands/cleanup_lsx_empty_club.py @@ -1,10 +1,12 @@ -"""清理龙先生重复/空证书俱乐部。 +"""清理龙先生重复/无 pem 证书俱乐部。 规则: - 候选:club_id=lsx / 名称含「龙先生」 / wx_appid=龙先生小程序 -- 「有 key」:pay_key_path / pay_cert_path / private_key_path / platform_cert_dir 任一非空 -- 默认 dry-run;--apply 才删除「完全没有 key 路径」的俱乐部行及相关配置表 -- 若只剩一条且无 key,不会删(避免误删唯一配置);除非 --force-delete-empty-only +- 「有 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 @@ -37,7 +39,6 @@ from users.models import UserBoss, UserGuanshi, UserZuzhang LSX_APPID = 'wx7ff90e9d024fcdb8' SEED_UIDS = ('9910001', '9910002') -# 删除俱乐部时一并清掉的配置表(按 club_id) RELATED_MODELS = ( ClubPaymentChannel, ClubHuiyuanPrice, @@ -54,15 +55,33 @@ RELATED_MODELS = ( ClubDashouExamPass, ) +# 证书「文件」字段(应以 .pem 结尾);platform_cert_dir 是目录,不参与判定 +_PEM_FILE_FIELDS = ('pay_key_path', 'pay_cert_path', 'private_key_path') -def _has_key_paths(club): - fields = ( - getattr(club, 'pay_key_path', '') or '', - getattr(club, 'pay_cert_path', '') or '', - getattr(club, 'private_key_path', '') or '', - getattr(club, 'platform_cert_dir', '') or '', - ) - return any(str(x).strip() for x in fields) + +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(): @@ -86,14 +105,14 @@ def _purge_club_related(club_id, stdout, style): class Command(BaseCommand): - help = '列出并删除龙先生相关「无证书 key 路径」的重复俱乐部(默认 dry-run)' + 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='即使没有「有 key」的对照俱乐部,也允许删除无 key 的候选', + help='即使没有「有 .pem」的对照俱乐部,也允许删除非 pem 候选', ) parser.add_argument( '--remove-seed-users', @@ -121,35 +140,38 @@ class Command(BaseCommand): self.stdout.write(self.style.WARNING('未找到龙先生/lsx 相关俱乐部')) return - with_keys = [] - without_keys = [] + 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_key_paths(c) - (with_keys if keyed else without_keys).append(c) + 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 "(空)"}') - self.stdout.write(f' pay_key_path={c.pay_key_path or "(空)"}') - self.stdout.write(f' pay_cert_path={c.pay_cert_path or "(空)"}') - self.stdout.write(f' private_key_path={c.private_key_path or "(空)"}') - self.stdout.write(f' platform_cert_dir={c.platform_cert_dir or "(空)"}') - self.stdout.write(f' >>> {"有 key 路径(保留)" if keyed else "无 key 路径(可删)"}') + 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'有 key: {len(with_keys)} 条;无 key: {len(without_keys)} 条') + self.stdout.write(f'有 .pem: {len(with_pem)} 条;无 .pem: {len(without_pem)} 条') - if not without_keys: - self.stdout.write(self.style.SUCCESS('没有「无 key 路径」的俱乐部,无需删除。')) - elif not with_keys and not force: + if not without_pem: + self.stdout.write(self.style.SUCCESS('没有「无 .pem 路径」的俱乐部,无需删除。')) + elif not with_pem and not force: self.stdout.write(self.style.ERROR( - '全部候选都没有 key 路径。为安全起见不自动删。' + '全部候选都没有 .pem 路径。为安全起见不自动删。' '若确认要删,加 --force-delete-empty-only --apply' )) else: - for c in without_keys: - self.stdout.write(self.style.WARNING(f'将删除无 key 俱乐部: {c.club_id} ({c.name})')) + 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