import random from django.core.management.base import BaseCommand from contest.models import Contest, ContestQuestion, Question, QuestionVersion def _generate_beginner(): """入门:100以内加减乘除,约 400 题。""" questions = [] ops = [ ("+", lambda a, b: a + b), ("-", lambda a, b: a - b), ("×", lambda a, b: a * b), ("÷", lambda a, b: a // b if b != 0 and a % b == 0 else None), ] idx = 0 for a in range(2, 100): for b in range(2, min(a + 1, 100)): for sym, fn in ops: if idx >= 400: return questions if sym == "÷" and (b == 0 or a % b != 0): continue if sym == "-" and a < b: continue result = fn(a, b) if result is None or result < 0 or result > 10000: continue slug = f"b-auto-{idx:04d}" prompt = f"{a} {sym} {b}" answer = str(result) if (slug, prompt, answer) not in questions: questions.append((slug, prompt, answer)) idx += 1 return questions[:400] def _generate_standard(): """标准:两位数乘除、大数减法、平方,约 400 题。""" questions = [] idx = 0 # 两位数乘法 for a in range(11, 100): for b in range(11, min(a + 1, 100)): if idx >= 150: break questions.append((f"s-auto-mul-{idx:04d}", f"{a} × {b}", str(a * b))) idx += 1 # 三位数除法 for a in range(100, 1000, 7): for b in range(11, 50): if idx >= 300: break if a % b == 0: questions.append((f"s-auto-div-{idx:04d}", f"{a} ÷ {b}", str(a // b))) idx += 1 # 大数减法 for a in range(1000, 10000, 137): b = random.randint(100, a - 1) if idx >= 350: break questions.append((f"s-auto-sub-{idx:04d}", f"{a} - {b}", str(a - b))) idx += 1 # 平方 for a in range(11, 100): if idx >= 400: break questions.append((f"s-auto-sq-{idx:04d}", f"{a}²", str(a * a))) idx += 1 return questions[:400] def _generate_advanced(): """进阶:模运算、组合数、高次幂、开方、等差数列求和,约 200 题。""" questions = [] idx = 0 # 模运算 for base in range(2, 20): for exp in range(2, 13): for mod in range(3, 20): if idx >= 50: break result = pow(base, exp, mod) questions.append((f"a-auto-mod-{idx:04d}", f"{base}^{exp} ÷ {mod} 的余数", str(result))) idx += 1 # 组合数 C(n,k) for n in range(5, 31): for k in range(2, min(n // 2 + 1, 8)): if idx >= 100: break from math import comb questions.append((f"a-auto-comb-{idx:04d}", f"C({n},{k})", str(comb(n, k)))) idx += 1 # 高次幂 for base in range(2, 10): for exp in range(3, 10): if idx >= 130: break result = base ** exp if result > 10**8: continue questions.append((f"a-auto-pow-{idx:04d}", f"{base}^{exp}", str(result))) idx += 1 # 开方(完全平方数) for n in range(10, 200): if idx >= 160: break r = int(n**0.5) if r * r == n: questions.append((f"a-auto-sqrt-{idx:04d}", f"√{n}", str(r))) idx += 1 # 等差数列求和 1..n for n in range(10, 201, 5): if idx >= 200: break result = n * (n + 1) // 2 questions.append((f"a-auto-sum-{idx:04d}", f"1 到 {n} 的整数和", str(result))) idx += 1 return questions[:200] QUESTIONS = { Question.Track.BEGINNER: _generate_beginner(), Question.Track.STANDARD: _generate_standard(), Question.Track.ADVANCED: _generate_advanced(), } class Command(BaseCommand): help = "创建分层题库(入门/标准/进阶)和实时 1v1、每日赛、单人练习" def handle(self, *args, **options): versions = {} for track, questions in QUESTIONS.items(): versions[track] = [] for slug, prompt, answer in questions: question, _ = Question.objects.update_or_create( slug=slug, defaults={"track": track, "tags": ["口算"], "is_active": True}, ) version, _ = QuestionVersion.objects.update_or_create( question=question, version=1, defaults={ "prompt": prompt, "answer": answer, "explanation": f"答案为 {answer}", }, ) versions[track].append(version) contest_specs = [] for track, label in ( (Question.Track.BEGINNER, "入门"), (Question.Track.STANDARD, "标准"), (Question.Track.ADVANCED, "进阶"), ): contest_specs.extend( [ ( f"realtime-{track}", f"{label}实时 1v1", Contest.Kind.REALTIME, track, 60, ), ( f"daily-{track}", f"{label}今日挑战", Contest.Kind.DAILY, track, 180, ), ( f"practice-{track}", f"{label}单人闯关", Contest.Kind.PRACTICE, track, 300, ), ] ) for slug, title, kind, track, duration in contest_specs: contest, _ = Contest.objects.update_or_create( slug=slug, defaults={ "title": title, "kind": kind, "track": track, "duration_seconds": duration, "status": Contest.Status.PUBLISHED, }, ) ContestQuestion.objects.filter(contest=contest).delete() pool = list(versions[track]) random.shuffle(pool) selected = pool[:8] ContestQuestion.objects.bulk_create( [ ContestQuestion( contest=contest, question_version=version, order=index, points=100, ) for index, version in enumerate(selected, start=1) ] ) total = sum(map(len, QUESTIONS.values())) self.stdout.write( self.style.SUCCESS( f"已创建 {total} 道题(入门{len(QUESTIONS[Question.Track.BEGINNER])}、" f"标准{len(QUESTIONS[Question.Track.STANDARD])}、" f"进阶{len(QUESTIONS[Question.Track.ADVANCED])})和 {len(contest_specs)} 场比赛" ) )