From b80145f2b68286566cfaaf56fea8a31aceaa1079 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 6 Aug 2026 01:36:06 -0600 Subject: [PATCH] =?UTF-8?q?feat(reaction):=20=E6=96=B0=E5=A2=9E=20reaction?= =?UTF-8?q?=20app=20=E4=B8=8E=E6=95=B0=E6=8D=AE=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 --- oj/settings.py | 1 + reaction/__init__.py | 0 reaction/migrations/0001_initial.py | 33 +++++++++++++++++++++++++++++ reaction/migrations/__init__.py | 0 reaction/models.py | 28 ++++++++++++++++++++++++ reaction/serializers.py | 18 ++++++++++++++++ reaction/urls/__init__.py | 0 reaction/views/__init__.py | 0 utils/constants.py | 1 + 9 files changed, 81 insertions(+) create mode 100644 reaction/__init__.py create mode 100644 reaction/migrations/0001_initial.py create mode 100644 reaction/migrations/__init__.py create mode 100644 reaction/models.py create mode 100644 reaction/serializers.py create mode 100644 reaction/urls/__init__.py create mode 100644 reaction/views/__init__.py diff --git a/oj/settings.py b/oj/settings.py index a4ec429..cadaf85 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -55,6 +55,7 @@ LOCAL_APPS = [ "judge", "message", "comment", + "reaction", "tutorial", "ai", "flowchart", diff --git a/reaction/__init__.py b/reaction/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/reaction/migrations/0001_initial.py b/reaction/migrations/0001_initial.py new file mode 100644 index 0000000..95e6658 --- /dev/null +++ b/reaction/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 6.0.4 on 2026-08-06 07:35 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('problem', '0014_problem_tag_ci_unique'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Reaction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(choices=[('too_easy', '太简单'), ('too_hard', '太难了'), ('confusing', '没看懂'), ('buggy', '题目有错'), ('learned', '学到了'), ('interesting', '有意思'), ('want_explain', '想听讲解')], max_length=20, verbose_name='表情类型')), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problem.problem')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'reaction', + 'indexes': [models.Index(fields=['problem', 'type'], name='reaction_problem_type_idx')], + 'unique_together': {('problem', 'user', 'type')}, + }, + ), + ] diff --git a/reaction/migrations/__init__.py b/reaction/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/reaction/models.py b/reaction/models.py new file mode 100644 index 0000000..1c15a5a --- /dev/null +++ b/reaction/models.py @@ -0,0 +1,28 @@ +from django.db import models + +from account.models import User +from problem.models import Problem + + +class ReactionType(models.TextChoices): + TOO_EASY = "too_easy", "太简单" + TOO_HARD = "too_hard", "太难了" + CONFUSING = "confusing", "没看懂" + BUGGY = "buggy", "题目有错" + LEARNED = "learned", "学到了" + INTERESTING = "interesting", "有意思" + WANT_EXPLAIN = "want_explain", "想听讲解" + + +class Reaction(models.Model): + problem = models.ForeignKey(Problem, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + type = models.CharField(max_length=20, choices=ReactionType.choices, verbose_name="表情类型") + create_time = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = "reaction" + unique_together = ("problem", "user", "type") + indexes = [ + models.Index(fields=["problem", "type"], name="reaction_problem_type_idx"), + ] diff --git a/reaction/serializers.py b/reaction/serializers.py new file mode 100644 index 0000000..edc234c --- /dev/null +++ b/reaction/serializers.py @@ -0,0 +1,18 @@ +from reaction.models import ReactionType +from utils.api import serializers + +MAX_REACTIONS = 3 + + +class SetReactionSerializer(serializers.Serializer): + problem_id = serializers.IntegerField() + types = serializers.ListField( + child=serializers.ChoiceField(choices=ReactionType.choices), + allow_empty=True, + ) + + def validate_types(self, value): + unique = list(dict.fromkeys(value)) + if len(unique) > MAX_REACTIONS: + raise serializers.ValidationError(f"最多只能选 {MAX_REACTIONS} 个") + return unique diff --git a/reaction/urls/__init__.py b/reaction/urls/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/reaction/views/__init__.py b/reaction/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/constants.py b/utils/constants.py index 622826c..c5f8fe9 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -18,6 +18,7 @@ class CacheKey: problem_authors = "problem_authors" problem_tags = "problem_tags" comment_stats = "comment_stats" + reaction_stats = "reaction_stats" user_activity_rank = "user_activity_rank" problem_yearly_ac = "problem_yearly_ac"