chore: 增加今日订单展示假单灌入脚本

This commit is contained in:
XingQue committed 2026-08-11 21:17:16 +08:00
1 parent 0d0b4810f9
commit 4c7f273cf7
1 file changed
+238
@@ -0,0 +1,238 @@
"""
灌入「今日订单」展示假单(TodayOrderFake),与抢单池 FakeGrabOrder 无关。
用法(服务器项目根目录):
python manage.py seed_today_order_fakes --club-id xq
python manage.py seed_today_order_fakes --club-id xq --count 500
python manage.py seed_today_order_fakes --club-id xq --clear # 先清该俱乐部旧假单再灌
python manage.py seed_today_order_fakes --club-id xq --dry-run
"""
from __future__ import annotations
import random
from decimal import Decimal
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils import timezone
from orders.models import TodayOrderFake
# 用户提供的介绍模板 + 价格(原样,不改写)
TEMPLATES = [
(
'绝密清图撤离1把,一直无法清图则保底2000万,所有的变卖和装备都归老板保持良好的服务态度',
'35.00',
),
(
'保底305W-800W【不卡保底老板自选图】机密',
'20.00',
),
(
'机密200W,打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'13.00',
),
(
'绝密清图保底310W-777W【不卡保底老板自选图】别试图蒙混,让老板挑刺',
'23.00',
),
(
'不卡保底444W~999W,打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'33.00',
),
(
'金牌护航,绝密清图,不卡保底2026W~2888W,所有的变卖和装备都归老板保持良好的服务态度',
'200.00',
),
(
'绝密清图单局800W,打手别让老板来挑刺,打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'80.00',
),
(
'护航600万(上不封顶),打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'50.00',
),
(
'绝密老板选图749W-1769W,炸弹一把加60w',
'65.00',
),
(
'绝密清图单局555W,别让老板来挑刺,打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'50.00',
),
(
'保底105W-555W【不卡保底】机密,打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'7.50',
),
(
'绝密连续清图撤离两把断一把重新打,所有的变卖和装备都归老板保持良好的服务态度',
'75.00',
),
(
'绝密赌老板自选图单局一个随机红,打手打完之后一定要提供相关明确的老板主页,战绩,收益,证据截图',
'25.00',
),
]
def _rel_media(path: str) -> str:
p = (path or '').strip()
if not p:
return ''
for prefix in ('https://', 'http://'):
if p.startswith(prefix):
# 取域名后路径
rest = p.split('://', 1)[-1]
slash = rest.find('/')
return rest[slash + 1 :] if slash >= 0 else ''
return p.lstrip('/')
def _resolve_sanjiaozhou(club_id: str, leixing_id: int | None):
"""解析三角洲类型 id + 图标相对 URL。"""
from products.models import ShangpinLeixing
from jituan.models import ClubShangpinLeixingConfig
lx = None
if leixing_id:
lx = ShangpinLeixing.query.filter(id=int(leixing_id)).first()
if not lx:
raise CommandError(f'找不到商品类型 id={leixing_id}')
else:
qs = ShangpinLeixing.query.filter(jieshao__icontains='三角洲').order_by('id')
rows = list(qs)
if not rows:
raise CommandError('库里没有名称含「三角洲」的商品类型,请用 --leixing-id 指定')
# 优先俱乐部已上架的
enabled_ids = set(
ClubShangpinLeixingConfig.query.filter(
club_id=club_id, is_enabled=True,
).values_list('leixing_id', flat=True)
)
preferred = [r for r in rows if r.id in enabled_ids] or rows
lx = preferred[0]
image = ''
cfg = ClubShangpinLeixingConfig.query.filter(
club_id=club_id, leixing_id=lx.id,
).first()
if cfg and (cfg.tupian_url or '').strip():
image = _rel_media(cfg.tupian_url)
if not image:
image = _rel_media(lx.tupian_url or '')
if not image:
raise CommandError(
f'三角洲类型 id={lx.id}({lx.jieshao})没有图标 URL,'
f'请先在后台配置类型图或俱乐部专属图标'
)
return int(lx.id), image, (lx.jieshao or '')
class Command(BaseCommand):
help = '灌入今日订单展示假单 TodayOrderFake(默认 500 条,模板循环随机)'
def add_arguments(self, parser):
parser.add_argument('--club-id', default='xq', help='俱乐部 ID,默认 xq')
parser.add_argument('--count', type=int, default=500, help='灌入条数,默认 500')
parser.add_argument(
'--leixing-id', type=int, default=None,
help='商品类型 ID;默认自动找名称含「三角洲」的类型',
)
parser.add_argument(
'--merchant-ratio', type=float, default=0.75,
help='商家单占比,默认 0.75(其余为平台单)',
)
parser.add_argument(
'--done-ratio', type=float, default=0.70,
help='已完成占比,默认 0.70(其余为进行中)',
)
parser.add_argument(
'--clear', action='store_true',
help='灌入前删除该俱乐部全部 TodayOrderFake',
)
parser.add_argument('--dry-run', action='store_true', help='只打印不写库')
parser.add_argument('--seed', type=int, default=None, help='随机种子(可复现)')
def handle(self, *args, **options):
club_id = (options['club_id'] or 'xq').strip()
count = max(1, int(options['count'] or 500))
merchant_ratio = float(options['merchant_ratio'])
done_ratio = float(options['done_ratio'])
dry = bool(options['dry_run'])
if options['seed'] is not None:
random.seed(int(options['seed']))
leixing_id, image_url, lx_name = _resolve_sanjiaozhou(club_id, options['leixing_id'])
self.stdout.write(
f'club={club_id} leixing={leixing_id}({lx_name}) '
f'image={image_url} count={count} '
f'merchant={merchant_ratio:.0%} done={done_ratio:.0%}'
)
self.stdout.write(f'模板数={len(TEMPLATES)} dry_run={dry} clear={options["clear"]}')
if dry:
# 抽样预览
for i in range(min(5, count)):
jieshao, price = random.choice(TEMPLATES)
plat = 2 if random.random() < merchant_ratio else 1
st = TodayOrderFake.STATUS_DONE if random.random() < done_ratio else TodayOrderFake.STATUS_DOING
self.stdout.write(
f' sample[{i}] platform={plat} status={st} price={price} | {jieshao[:40]}...'
)
self.stdout.write(self.style.WARNING('dry-run:未写入数据库'))
return
with transaction.atomic():
if options['clear']:
deleted, _ = TodayOrderFake.query.filter(club_id=club_id).delete()
self.stdout.write(f'已清理 club={club_id} 旧假单 {deleted} 条')
now = timezone.now()
if timezone.is_aware(now):
try:
now = timezone.make_naive(now)
except Exception:
pass
rows = []
for i in range(count):
jieshao, price = random.choice(TEMPLATES)
platform = (
TodayOrderFake.PLATFORM_SHANGJIA
if random.random() < merchant_ratio
else TodayOrderFake.PLATFORM_PINGTAI
)
status = (
TodayOrderFake.STATUS_DONE
if random.random() < done_ratio
else TodayOrderFake.STATUS_DOING
)
rows.append(
TodayOrderFake(
club_id=club_id,
platform=platform,
leixing_id=leixing_id,
jieshao=jieshao,
price=Decimal(price),
image_url=image_url,
status=status,
is_active=True,
sort_order=count - i,
CreateTime=now,
UpdateTime=now,
)
)
TodayOrderFake.objects.bulk_create(rows, batch_size=200)
total = TodayOrderFake.query.filter(club_id=club_id, is_active=True).count()
done_n = TodayOrderFake.query.filter(
club_id=club_id, is_active=True, status=TodayOrderFake.STATUS_DONE,
).count()
sj_n = TodayOrderFake.query.filter(
club_id=club_id, is_active=True, platform=TodayOrderFake.PLATFORM_SHANGJIA,
).count()
self.stdout.write(self.style.SUCCESS(
f'完成:新写入 {count} 条;当前启用假单共 {total} 条 '
f'(已完成 {done_n},商家单 {sj_n})'
))