feat(jituan): 集团多俱乐部改造 — club 隔离、财务/提现/分红/展示配置

This commit is contained in:
XingQue
2026-06-24 05:02:18 +08:00
parent ce9b09f096
commit dcc4936428
82 changed files with 5748 additions and 924 deletions

View File

View File

View File

@@ -0,0 +1,47 @@
"""回填 lunbo / gonggao / popup_page / tupianpeizhi 的 club_id 与 lunbo.page_key。"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from config.models import Gonggao, Lunbo, PopupPage, Tupianpeizhi
class Command(BaseCommand):
help = '回填展示配置表的 club_id并为 lunbo 补 page_key'
def handle(self, *args, **options):
updated = {'lunbo_club': 0, 'lunbo_page': 0, 'gonggao': 0, 'popup': 0, 'tupian': 0}
with transaction.atomic():
for row in Lunbo.query.all().only('id', 'club_id', 'page_key', 'ImageType'):
fields = []
if not row.club_id:
row.club_id = CLUB_ID_DEFAULT
fields.append('club_id')
updated['lunbo_club'] += 1
if not row.page_key:
if row.ImageType == 2:
row.page_key = 'dashou_center'
else:
row.page_key = 'order_pool'
fields.append('page_key')
updated['lunbo_page'] += 1
if fields:
row.save(update_fields=fields)
for row in Gonggao.query.filter(club_id='').only('id', 'club_id'):
Gonggao.query.filter(id=row.id).update(club_id=CLUB_ID_DEFAULT)
updated['gonggao'] += 1
for row in PopupPage.query.filter(club_id='').only('id', 'club_id'):
PopupPage.query.filter(id=row.id).update(club_id=CLUB_ID_DEFAULT)
updated['popup'] += 1
for row in Tupianpeizhi.query.filter(club_id='').only('id', 'club_id'):
Tupianpeizhi.query.filter(id=row.id).update(club_id=CLUB_ID_DEFAULT)
updated['tupian'] += 1
self.stdout.write(self.style.SUCCESS(
f'回填完成: lunbo_club={updated["lunbo_club"]}, lunbo_page={updated["lunbo_page"]}, '
f'gonggao={updated["gonggao"]}, popup={updated["popup"]}, tupian={updated["tupian"]}'
))

View File

@@ -0,0 +1,23 @@
"""按充值订单 / 订单 / 用户回填 gsfenhong.club_id。"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.club_penalty import resolve_gsfenhong_club_id
from products.models import Gsfenhong
class Command(BaseCommand):
help = '回填 gsfenhong.club_id'
def handle(self, *args, **options):
updated = 0
with transaction.atomic():
for row in Gsfenhong.query.all().only('dingdan_id', 'dashouid', 'club_id'):
cid = resolve_gsfenhong_club_id(dingdan_id=row.dingdan_id, dashouid=row.dashouid)
if not cid:
cid = CLUB_ID_DEFAULT
if row.club_id != cid:
Gsfenhong.query.filter(dingdan_id=row.dingdan_id).update(club_id=cid)
updated += 1
self.stdout.write(self.style.SUCCESS(f'gsfenhong club_id 更新 {updated}'))

View File

@@ -0,0 +1,42 @@
"""回填罚单 club_id从关联订单或被罚用户"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.club_penalty import resolve_penalty_club_id
from orders.models import Order, Penalty
from users.business_models import User
class Command(BaseCommand):
help = '按关联订单/被罚用户回填 fadan.club_id默认仅处理仍为 xq 的记录)'
def add_arguments(self, parser):
parser.add_argument('--all', action='store_true', help='重算全部罚单(慎用)')
parser.add_argument('--dry-run', action='store_true')
def handle(self, *args, **options):
dry = options['dry_run']
qs = Penalty.query.all()
if not options['all']:
qs = qs.filter(ClubID=CLUB_ID_DEFAULT)
updated = 0
with transaction.atomic():
for p in qs.iterator():
order = None
if p.RelatedOrderID:
order = Order.query.filter(OrderID=p.RelatedOrderID).first()
cid = resolve_penalty_club_id(
order=order,
penalized_user_id=p.PenalizedUserID,
)
if cid and cid != p.ClubID:
if dry:
self.stdout.write(f'would update penalty {p.id}: {p.ClubID} -> {cid}')
else:
p.ClubID = cid
p.save(update_fields=['ClubID'])
updated += 1
self.stdout.write(self.style.SUCCESS(f'罚单 club_id 更新 {updated}'))

View File

@@ -0,0 +1,35 @@
"""回填 chufajilu / duoci_fenhong club_id。"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.club_penalty import resolve_gsfenhong_club_id, resolve_penalty_record_club_id
from orders.models import PenaltyRecord
from products.models import DuociFenhong
class Command(BaseCommand):
help = '回填 PenaltyRecord / DuociFenhong 的 club_id'
def handle(self, *args, **options):
pr_updated = 0
df_updated = 0
with transaction.atomic():
for row in PenaltyRecord.query.all().only('id', 'OrderID', 'PlayerID', 'ClubID'):
cid = resolve_penalty_record_club_id(order_id=row.OrderID, player_id=row.PlayerID)
if row.ClubID != cid:
PenaltyRecord.query.filter(id=row.id).update(ClubID=cid)
pr_updated += 1
for row in DuociFenhong.query.all().only('huiyuan', 'yonghuid', 'cishu', 'club_id'):
cid = resolve_gsfenhong_club_id(dashouid=row.yonghuid) or CLUB_ID_DEFAULT
if row.club_id != cid:
DuociFenhong.query.filter(
club_id=row.club_id,
huiyuan=row.huiyuan,
yonghuid=row.yonghuid,
cishu=row.cishu,
).update(club_id=cid)
df_updated += 1
self.stdout.write(self.style.SUCCESS(
f'PenaltyRecord={pr_updated}, DuociFenhong={df_updated}'
))

View File

@@ -0,0 +1,36 @@
"""回填角色日统计 club_id按用户归属俱乐部"""
from django.core.management.base import BaseCommand
from django.db import transaction
from backend.models import (
LeaderDailyStats,
ManagerDailyStats,
ManagerRenewalDailyStats,
MerchantDailyStats,
PlayerDailyStats,
)
from backend.utils import _club_id_for_yonghuid
MODELS = [
(MerchantDailyStats, 'MerchantID'),
(PlayerDailyStats, 'PlayerID'),
(LeaderDailyStats, 'LeaderID'),
(ManagerDailyStats, 'ManagerID'),
(ManagerRenewalDailyStats, 'ManagerID'),
]
class Command(BaseCommand):
help = '回填 5 张角色日统计表的 club_id'
def handle(self, *args, **options):
updated = 0
with transaction.atomic():
for model, uid_field in MODELS:
for row in model.query.all().only('id', uid_field, 'club_id'):
cid = _club_id_for_yonghuid(getattr(row, uid_field))
if row.club_id != cid:
model.query.filter(id=row.id).update(club_id=cid)
updated += 1
self.stdout.write(self.style.SUCCESS(f'角色日统计 club_id 更新 {updated}'))

View File

@@ -0,0 +1,42 @@
"""按用户 ClubID 回填提现流水 club_id。"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.club_user import get_user_club_id
from users.business_models import User
from users.models import TixianAutoRecord, Tixianjilu, TixianShenheJilu
class Command(BaseCommand):
help = '回填 tixianjilu / tixian_shenhe_jilu / tixian_auto_record 的 club_id'
def handle(self, *args, **options):
uid_club = {}
for u in User.query.all().only('UserUID', 'ClubID'):
uid_club[u.UserUID] = get_user_club_id(u)
updated = {'jilu': 0, 'shenhe': 0, 'auto': 0}
with transaction.atomic():
for row in Tixianjilu.query.all().only('id', 'yonghuid', 'club_id'):
cid = uid_club.get(row.yonghuid) or CLUB_ID_DEFAULT
if row.club_id != cid:
Tixianjilu.query.filter(id=row.id).update(club_id=cid)
updated['jilu'] += 1
for row in TixianShenheJilu.query.all().only('id', 'yonghuid', 'club_id'):
cid = uid_club.get(row.yonghuid) or CLUB_ID_DEFAULT
if row.club_id != cid:
TixianShenheJilu.query.filter(id=row.id).update(club_id=cid)
updated['shenhe'] += 1
for row in TixianAutoRecord.query.all().only('id', 'yonghuid', 'club_id'):
cid = uid_club.get(row.yonghuid) or CLUB_ID_DEFAULT
if row.club_id != cid:
TixianAutoRecord.query.filter(id=row.id).update(club_id=cid)
updated['auto'] += 1
self.stdout.write(self.style.SUCCESS(
f'回填完成: jilu={updated["jilu"]}, shenhe={updated["shenhe"]}, auto={updated["auto"]}'
))

View File

@@ -0,0 +1,53 @@
"""回填用户 ClubID缺省或空时设为 xq 或 user_wx_openid 绑定)。"""
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import UserWxOpenid
from users.business_models import User
class Command(BaseCommand):
help = '回填 User.ClubIDOpenID 绑定 → user_wx_openid否则默认 xq'
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='仅统计不落库',
)
def handle(self, *args, **options):
dry_run = options.get('dry_run', False)
updated = 0
skipped = 0
users = User.query.all()
for user in users:
current = getattr(user, 'ClubID', None) or ''
if current and current != CLUB_ID_DEFAULT:
skipped += 1
continue
new_club = CLUB_ID_DEFAULT
if user.OpenID:
binding = UserWxOpenid.query.filter(openid=user.OpenID).order_by('id').first()
if binding:
new_club = binding.club_id
if current == new_club:
skipped += 1
continue
if dry_run:
self.stdout.write(f'[dry-run] {user.UserUID} -> ClubID={new_club}')
updated += 1
continue
with transaction.atomic():
user.ClubID = new_club
user.save(update_fields=['ClubID'])
updated += 1
suffix = 'dry-run' if dry_run else ''
self.stdout.write(self.style.SUCCESS(f'完成{suffix}:更新 {updated},跳过 {skipped}'))

View File

@@ -0,0 +1,104 @@
"""从 xq 模板复制配置,创建新子公司俱乐部。"""
from decimal import Decimal
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import Club, ClubHuiyuanPrice, ClubLilubiao, ClubTixianQuota, ClubWithdrawConfig
from jituan.services.szjilu_accounting import normalize_szjilu_club_id
from jituan.services.display_config import copy_display_config
from config.models import Szjilu
class Command(BaseCommand):
help = '从 xq 复制支付/会员价/利率配置,创建新俱乐部'
def add_arguments(self, parser):
parser.add_argument('club_id', type=str, help='新俱乐部 ID如 xy')
parser.add_argument('--name', type=str, required=True, help='展示名称')
parser.add_argument('--wx-appid', type=str, default='', help='小程序 appid')
parser.add_argument('--from-club', type=str, default=CLUB_ID_DEFAULT, help='模板俱乐部')
def handle(self, *args, **options):
new_id = (options['club_id'] or '').strip()
name = (options['name'] or '').strip()
template_id = normalize_szjilu_club_id(options.get('from_club'))
if not new_id or len(new_id) > 16:
raise CommandError('club_id 无效')
if new_id == template_id:
raise CommandError('新 club_id 不能与模板相同')
try:
template = Club.query.get(club_id=template_id)
except Club.DoesNotExist:
raise CommandError(f'模板俱乐部 {template_id} 不存在,请先 seed_club_xq')
with transaction.atomic():
if Club.query.filter(club_id=new_id).exists():
raise CommandError(f'俱乐部 {new_id} 已存在')
Club.query.create(
club_id=new_id,
name=name,
status=1,
wx_appid=options.get('wx_appid') or template.wx_appid,
wx_secret=template.wx_secret,
mch_id=template.mch_id,
pay_app_id=template.pay_app_id or options.get('wx_appid') or template.wx_appid,
api_v3_key=template.api_v3_key,
cert_serial_no=template.cert_serial_no,
private_key_path=template.private_key_path,
platform_cert_dir=template.platform_cert_dir,
official_appid=template.official_appid,
official_secret=template.official_secret,
official_token=template.official_token,
encoding_aes_key=template.encoding_aes_key,
template_id=template.template_id,
template_max_per_minute=template.template_max_per_minute,
h5_domain=template.h5_domain,
oss_prefix=template.oss_prefix or new_id,
goeasy_appkey=template.goeasy_appkey,
goeasy_secret=template.goeasy_secret,
config_json=dict(template.config_json or {}),
sort_order=template.sort_order + 1,
)
for row in ClubHuiyuanPrice.query.filter(club_id=template_id):
ClubHuiyuanPrice.query.create(
club_id=new_id,
huiyuan_id=row.huiyuan_id,
jiage=row.jiage or Decimal('0'),
guanshifc=row.guanshifc or Decimal('0'),
zuzhangfc=row.zuzhangfc or Decimal('0'),
is_enabled=row.is_enabled,
bankuai_id=row.bankuai_id,
)
for row in ClubLilubiao.query.filter(club_id=template_id):
ClubLilubiao.query.create(
club_id=new_id,
config_type=row.config_type,
lilv=row.lilv or Decimal('0'),
remark=row.remark or '',
)
try:
tpl_wd = ClubWithdrawConfig.query.get(club_id=template_id)
ClubWithdrawConfig.query.create(club_id=new_id, mode=tpl_wd.mode)
except ClubWithdrawConfig.DoesNotExist:
ClubWithdrawConfig.query.create(club_id=new_id, mode=1)
for row in ClubTixianQuota.query.filter(club_id=template_id):
ClubTixianQuota.query.create(
club_id=new_id,
leixing=row.leixing,
daily_limit=row.daily_limit or Decimal('0'),
)
Szjilu.objects.get_or_create(club_id=new_id)
copy_display_config(template_id, new_id)
self.stdout.write(self.style.SUCCESS(f'俱乐部 {new_id} ({name}) 已创建,配置自 {template_id} 复制'))

View File

@@ -0,0 +1,65 @@
"""为后台客服账号初始化 admin_assignment数据范围非功能权限"""
import logging
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_SINGLE, ADMIN_ROLE_LABELS
from jituan.models import AdminAssignment
from users.business_models import User
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '为 kefu 账号创建默认子公司任职club=xq, CLUB_ADMIN超管可不建 assignment'
def add_arguments(self, parser):
parser.add_argument(
'--club-id', default=CLUB_ID_DEFAULT, help='目标俱乐部 ID默认 xq',
)
parser.add_argument(
'--role-code', default='CLUB_ADMIN', help='任职角色码',
)
parser.add_argument(
'--dry-run', action='store_true', help='只打印不写入',
)
def handle(self, *args, **options):
club_id = options['club_id']
role_code = options['role_code']
dry_run = options['dry_run']
kefu_users = User.query.filter(UserType='kefu')
created = 0
skipped = 0
with transaction.atomic():
for u in kefu_users:
if u.IsSuperuser:
skipped += 1
continue
exists = AdminAssignment.query.filter(
yonghuid=u.UserUID, club_id=club_id, role_code=role_code,
).exists()
if exists:
skipped += 1
continue
if dry_run:
self.stdout.write(f'would assign {u.UserUID} -> {club_id} {role_code}')
created += 1
continue
AdminAssignment.query.create(
yonghuid=u.UserUID,
club_id=club_id,
role_code=role_code,
data_scope=DATA_SCOPE_SINGLE,
is_primary=True,
status=1,
)
created += 1
label = ADMIN_ROLE_LABELS.get(role_code, role_code)
self.stdout.write(self.style.SUCCESS(
f'完成:新建 {created} 条,跳过 {skipped}club={club_id}, role={label}'
))

View File

@@ -0,0 +1,183 @@
"""从现网配置初始化 xq 俱乐部及覆盖表。"""
import logging
from decimal import Decimal
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import (
Club,
ClubHuiyuanPrice,
ClubLilubiao,
ClubWithdrawConfig,
UserWxOpenid,
)
from users.business_models import User
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '初始化星阙(xq)俱乐部主数据,并从 huiyuan/lilubiao 复制覆盖配置'
def handle(self, *args, **options):
with transaction.atomic():
club, created = self._ensure_club_xq()
self.stdout.write(self.style.SUCCESS(
f'club xq: {"新建" if created else "已存在"}'
))
self._copy_huiyuan_prices()
self._copy_lilubiao()
self._ensure_withdraw_config()
migrated = self._migrate_openid_bindings()
self.stdout.write(self.style.SUCCESS(f'user_wx_openid 迁移绑定: {migrated}'))
def _ensure_club_xq(self):
wx_v3 = dict(getattr(settings, 'WECHAT_PAY_V3_CONFIG', {}) or {})
defaults = {
'name': '星阙电竞',
'status': 1,
'wx_appid': getattr(settings, 'WEIXIN_APPID', ''),
'wx_secret': getattr(settings, 'WEIXIN_SECRET', ''),
'mch_id': getattr(settings, 'WEIXIN_MCHID', '') or wx_v3.get('MCHID', ''),
'pay_app_id': wx_v3.get('APPID', '') or getattr(settings, 'WEIXIN_APPID', ''),
'api_v3_key': wx_v3.get('API_V3_KEY', ''),
'cert_serial_no': wx_v3.get('CERT_SERIAL_NO', ''),
'private_key_path': wx_v3.get('PRIVATE_KEY_PATH', ''),
'platform_cert_dir': wx_v3.get('PLATFORM_CERT_DIR', ''),
'config_json': {
'mch_key': getattr(settings, 'WEIXIN_SHANGHUMIYAO', ''),
'transfer_scene_id': wx_v3.get('TRANSFER_SCENE_ID', '1005'),
},
'official_appid': getattr(settings, 'WEIXIN_OFFICIAL_APPID', ''),
'official_secret': getattr(settings, 'WEIXIN_OFFICIAL_SECRET', ''),
'official_token': getattr(settings, 'WEIXIN_OFFICIAL_TOKEN', ''),
'encoding_aes_key': getattr(settings, 'WEIXIN_OFFICIAL_ENCODING_AES_KEY', ''),
'template_id': getattr(settings, 'WEIXIN_TEMPLATE_ID', ''),
'template_max_per_minute': getattr(settings, 'WEIXIN_TEMPLATE_MAX_PER_MINUTE', 950),
'h5_domain': getattr(settings, 'H5_DOMAIN', ''),
'goeasy_appkey': getattr(settings, 'GOEASY_APPKEY', ''),
'goeasy_secret': getattr(settings, 'GOEASY_SECRET', ''),
'sort_order': 0,
}
try:
club = Club.query.get(club_id=CLUB_ID_DEFAULT)
for k, v in defaults.items():
if k == 'config_json':
merged = dict(club.config_json or {})
for ck, cv in v.items():
if cv and not merged.get(ck):
merged[ck] = cv
club.config_json = merged
continue
if v and not getattr(club, k, None):
setattr(club, k, v)
# V3 字段允许用 settings 补全(证书路径等)
for field in ('api_v3_key', 'cert_serial_no', 'private_key_path', 'platform_cert_dir', 'pay_app_id'):
val = defaults.get(field)
if val and not getattr(club, field, None):
setattr(club, field, val)
club.save()
return club, False
except Club.DoesNotExist:
club = Club.query.create(club_id=CLUB_ID_DEFAULT, **defaults)
return club, True
def _copy_huiyuan_prices(self):
from products.models import Huiyuan
count = 0
for h in Huiyuan.query.all():
bankuai_id = getattr(h, 'bankuai_id', None)
existing = ClubHuiyuanPrice.query.filter(
club_id=CLUB_ID_DEFAULT, huiyuan_id=h.huiyuan_id
).first()
if existing:
continue
ClubHuiyuanPrice.query.create(
club_id=CLUB_ID_DEFAULT,
huiyuan_id=h.huiyuan_id,
jiage=h.jiage or Decimal('0'),
guanshifc=h.guanshifc or Decimal('0'),
zuzhangfc=h.zuzhangfc or Decimal('0'),
is_enabled=True,
bankuai_id=bankuai_id,
)
count += 1
self.stdout.write(f'club_huiyuan_price 新增 {count}')
def _copy_lilubiao(self):
from orders.models import CommissionRate
count = 0
for row in CommissionRate.query.all():
config_type = int(row.Platform or 0)
existing = ClubLilubiao.query.filter(
club_id=CLUB_ID_DEFAULT, config_type=config_type
).first()
if existing:
continue
ClubLilubiao.query.create(
club_id=CLUB_ID_DEFAULT,
config_type=config_type,
lilv=row.Rate or Decimal('0'),
remark='',
)
count += 1
self.stdout.write(f'club_lilubiao 新增 {count}')
def _ensure_withdraw_config(self):
from config.models import WithdrawConfig, TixianQuotaDefault
mode = 1
try:
wc = WithdrawConfig.query.first()
if wc:
mode = wc.mode
except Exception:
pass
try:
cfg = ClubWithdrawConfig.query.get(club_id=CLUB_ID_DEFAULT)
cfg.mode = mode
cfg.save(update_fields=['mode'])
except ClubWithdrawConfig.DoesNotExist:
ClubWithdrawConfig.query.create(club_id=CLUB_ID_DEFAULT, mode=mode)
self._copy_tixian_quota()
def _copy_tixian_quota(self):
from config.models import TixianQuotaDefault
from jituan.models import ClubTixianQuota
count = 0
for row in TixianQuotaDefault.query.all():
existing = ClubTixianQuota.query.filter(
club_id=CLUB_ID_DEFAULT, leixing=row.UserType,
).first()
if existing:
continue
ClubTixianQuota.query.create(
club_id=CLUB_ID_DEFAULT,
leixing=row.UserType,
daily_limit=row.default_quota or Decimal('0'),
)
count += 1
self.stdout.write(f'club_tixian_quota 新增 {count}')
def _migrate_openid_bindings(self):
migrated = 0
users = User.query.filter(OpenID__isnull=False).exclude(OpenID='')
for u in users:
exists = UserWxOpenid.query.filter(
club_id=CLUB_ID_DEFAULT, openid=u.OpenID
).exists()
if exists:
continue
UserWxOpenid.query.create(
club_id=CLUB_ID_DEFAULT,
openid=u.OpenID,
yonghuid=u.UserUID,
unionid=u.UnionID,
)
if hasattr(u, 'ClubID') and (not u.ClubID or u.ClubID != CLUB_ID_DEFAULT):
u.ClubID = CLUB_ID_DEFAULT
u.save(update_fields=['ClubID'])
migrated += 1
return migrated

View File

@@ -0,0 +1,40 @@
"""为每个启用俱乐部初始化 szjilu 收支流水行。"""
from django.core.management.base import BaseCommand
from django.db import transaction
from config.models import Szjilu
from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import Club
from jituan.services.szjilu_accounting import normalize_szjilu_club_id
class Command(BaseCommand):
help = '为每个俱乐部创建 szjilu 收支流水行(已有则跳过)'
def add_arguments(self, parser):
parser.add_argument(
'--club-id',
type=str,
default='',
help='仅处理指定 club_id默认处理全部启用俱乐部',
)
def handle(self, *args, **options):
club_id = (options.get('club_id') or '').strip()
if club_id:
club_ids = [normalize_szjilu_club_id(club_id)]
else:
club_ids = list(
Club.query.filter(status=1).values_list('club_id', flat=True)
)
if CLUB_ID_DEFAULT not in club_ids:
club_ids.append(CLUB_ID_DEFAULT)
created = 0
with transaction.atomic():
for cid in club_ids:
_, was_created = Szjilu.objects.get_or_create(club_id=cid)
if was_created:
created += 1
self.stdout.write(f'创建 szjilu: club_id={cid}')
self.stdout.write(self.style.SUCCESS(f'完成,新建 {created} 条 szjilu 记录'))