From 94188b5f0a47537b94d403ca42e4fb964988317c Mon Sep 17 00:00:00 2001 From: huluxia <2056300012@qq.com> Date: Sun, 9 Aug 2026 16:45:22 +0800 Subject: [PATCH] new_bug_contest --- .../management/commands/seed_contests.py | 154 +++++++++++++++--- backend/static/js/app.js | 1 + backend/static/js/realtime.js | 3 + 3 files changed, 134 insertions(+), 24 deletions(-) diff --git a/backend/contest/management/commands/seed_contests.py b/backend/contest/management/commands/seed_contests.py index 4fe746e..c564dd0 100644 --- a/backend/contest/management/commands/seed_contests.py +++ b/backend/contest/management/commands/seed_contests.py @@ -2,33 +2,133 @@ from django.core.management.base import BaseCommand from contest.models import Contest, ContestQuestion, Question, QuestionVersion +import random + + +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: [ - ("b-12-plus-19", "12 + 19", "31"), - ("b-8-times-7", "8 × 7", "56"), - ("b-90-minus-37", "90 - 37", "53"), - ("b-144-div-12", "144 ÷ 12", "12"), - ("b-25-times-4", "25 × 4", "100"), - ], - Question.Track.STANDARD: [ - ("s-17-times-23", "17 × 23", "391"), - ("s-625-div-25", "625 ÷ 25", "25"), - ("s-48-times-15", "48 × 15", "720"), - ("s-1000-minus-387", "1000 - 387", "613"), - ("s-35-squared", "35²", "1225"), - ], - Question.Track.ADVANCED: [ - ("a-mod-2pow10", "2¹⁰ 除以 7 的余数", "2"), - ("a-sum-1-50", "1 到 50 的整数和", "1275"), - ("a-15-choose-2", "C(15,2)", "105"), - ("a-sqrt-2025", "√2025", "45"), - ("a-3pow6", "3⁶", "729"), - ], + Question.Track.BEGINNER: _generate_beginner(), + Question.Track.STANDARD: _generate_standard(), + Question.Track.ADVANCED: _generate_advanced(), } class Command(BaseCommand): - help = "创建首批分层题目、实时 1v1、每日赛和单人练习" + help = "创建分层题库(入门/标准/进阶)和实时 1v1、每日赛、单人练习" def handle(self, *args, **options): versions = {} @@ -93,6 +193,9 @@ class Command(BaseCommand): }, ) ContestQuestion.objects.filter(contest=contest).delete() + pool = list(versions[track]) + random.shuffle(pool) + selected = pool[:8] ContestQuestion.objects.bulk_create( [ ContestQuestion( @@ -101,11 +204,14 @@ class Command(BaseCommand): order=index, points=100, ) - for index, version in enumerate(versions[track], start=1) + for index, version in enumerate(selected, start=1) ] ) + total = sum(map(len, QUESTIONS.values())) self.stdout.write( self.style.SUCCESS( - f"已创建 {sum(map(len, QUESTIONS.values()))} 道题和 {len(contest_specs)} 场比赛" + f"已创建 {total} 道题(入门{len(QUESTIONS[Question.Track.BEGINNER])}、" + f"标准{len(QUESTIONS[Question.Track.STANDARD])}、" + f"进阶{len(QUESTIONS[Question.Track.ADVANCED])})和 {len(contest_specs)} 场比赛" ) ) diff --git a/backend/static/js/app.js b/backend/static/js/app.js index a25751e..cb1a43a 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -83,6 +83,7 @@ function collectApiErrorMessages(value, messages = []) { } function logApiError(error) { + if (error.status === 401 || error.status === 403) return; console.error("[Hulumath API]", { method: error.method, path: error.path, diff --git a/backend/static/js/realtime.js b/backend/static/js/realtime.js index 6ada6ab..c0a5d94 100644 --- a/backend/static/js/realtime.js +++ b/backend/static/js/realtime.js @@ -350,6 +350,9 @@ result.append(outcome, score, timeLine, rating); root.append(result); + // 刷新右上角用户 Rating 显示 + if (typeof loadUser === "function") loadUser(); + const review = document.createElement("div"); review.className = "realtime-review"; match.attempt.questions.forEach((question) => {