From 93ad681689baa4dbf01a2c4a408ba16f2604d673 Mon Sep 17 00:00:00 2001 From: jeremygan2021 Date: Fri, 27 Feb 2026 14:44:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A1=E6=A0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/community/admin.py | 19 +++++++++++++++---- .../community/migrations/0015_topic_status.py | 18 ++++++++++++++++++ backend/community/models.py | 7 +++++++ backend/community/serializers.py | 4 ++-- backend/community/views.py | 12 +++++++++++- frontend/src/components/CreateTopicModal.jsx | 9 +++++++-- .../src/subpackages/forum/create/index.tsx | 10 ++++++++-- 7 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 backend/community/migrations/0015_topic_status.py diff --git a/backend/community/admin.py b/backend/community/admin.py index 9811c82..530fd6c 100644 --- a/backend/community/admin.py +++ b/backend/community/admin.py @@ -205,12 +205,23 @@ class ActivitySignupAdmin(ModelAdmin): @admin.register(Topic) class TopicAdmin(OrderableAdminMixin, ModelAdmin): - list_display = ('title', 'category', 'author', 'get_related_item', 'reply_count', 'view_count', 'is_pinned', 'created_at', 'order_actions') - list_filter = ('category', 'is_pinned', 'created_at', 'related_product', 'related_service', 'related_course') + list_display = ('title', 'status', 'category', 'author', 'get_related_item', 'reply_count', 'view_count', 'is_pinned', 'created_at', 'order_actions') + list_filter = ('status', 'category', 'is_pinned', 'created_at', 'related_product', 'related_service', 'related_course') search_fields = ('title', 'content', 'author__nickname') autocomplete_fields = ['author', 'related_product', 'related_service', 'related_course'] inlines = [TopicMediaInline, ReplyInline] - actions = ['reset_ordering'] + actions = ['reset_ordering', 'approve_topics', 'reject_topics'] + list_editable = ('status', 'is_pinned') + + @admin.action(description="批量通过审核") + def approve_topics(self, request, queryset): + rows_updated = queryset.update(status='published') + self.message_user(request, f"{rows_updated} 个帖子已通过审核") + + @admin.action(description="批量拒绝") + def reject_topics(self, request, queryset): + rows_updated = queryset.update(status='rejected') + self.message_user(request, f"{rows_updated} 个帖子已拒绝") def save_model(self, request, obj, form, change): # 当帖子被置顶时(新建或修改状态),默认将排序值设为0 @@ -232,7 +243,7 @@ class TopicAdmin(OrderableAdminMixin, ModelAdmin): fieldsets = ( ('帖子内容', { - 'fields': ('title', 'category', 'content', 'is_pinned') + 'fields': ('title', 'status', 'category', 'content', 'is_pinned') }), ('关联信息', { 'fields': ('author', 'related_product', 'related_service', 'related_course'), diff --git a/backend/community/migrations/0015_topic_status.py b/backend/community/migrations/0015_topic_status.py new file mode 100644 index 0000000..569f734 --- /dev/null +++ b/backend/community/migrations/0015_topic_status.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.1 on 2026-02-27 06:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('community', '0014_alter_topic_options_topic_order'), + ] + + operations = [ + migrations.AddField( + model_name='topic', + name='status', + field=models.CharField(choices=[('pending', '待审核'), ('published', '已发布'), ('rejected', '已拒绝')], default='published', max_length=20, verbose_name='状态'), + ), + ] diff --git a/backend/community/models.py b/backend/community/models.py index 4400985..9225fe3 100644 --- a/backend/community/models.py +++ b/backend/community/models.py @@ -127,6 +127,13 @@ class Topic(models.Model): ) category = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='discussion', verbose_name="分类") + STATUS_CHOICES = ( + ('pending', '待审核'), + ('published', '已发布'), + ('rejected', '已拒绝'), + ) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='published', verbose_name="状态") + content = models.TextField(verbose_name="内容", help_text="支持Markdown格式,支持插入图片") author = models.ForeignKey(WeChatUser, on_delete=models.CASCADE, related_name='topics', verbose_name="作者") diff --git a/backend/community/serializers.py b/backend/community/serializers.py index 00fe015..4c95832 100644 --- a/backend/community/serializers.py +++ b/backend/community/serializers.py @@ -120,14 +120,14 @@ class TopicSerializer(serializers.ModelSerializer): class Meta: model = Topic fields = [ - 'id', 'title', 'category', 'content', 'author', 'author_info', + 'id', 'title', 'category', 'status', 'content', 'author', 'author_info', 'related_product', 'product_info', 'related_service', 'service_info', 'related_course', 'course_info', 'view_count', 'is_pinned', 'created_at', 'updated_at', 'is_verified_owner', 'replies', 'media', 'media_ids' ] - read_only_fields = ['author', 'view_count', 'created_at', 'updated_at', 'is_verified_owner'] + read_only_fields = ['author', 'view_count', 'created_at', 'updated_at', 'is_verified_owner', 'status'] def create(self, validated_data): media_ids = validated_data.pop('media_ids', []) diff --git a/backend/community/views.py b/backend/community/views.py index b510573..fa09654 100644 --- a/backend/community/views.py +++ b/backend/community/views.py @@ -262,11 +262,21 @@ class TopicViewSet(viewsets.ModelViewSet): ordering_fields = ['created_at', 'view_count', 'order'] ordering = ['-is_pinned', 'order', '-created_at'] + def get_queryset(self): + qs = super().get_queryset() + # 列表接口仅显示已发布的帖子 + if self.action == 'list': + qs = qs.filter(status='published') + return qs + def perform_create(self, serializer): user = get_current_wechat_user(self.request) # Auth check is done in create or permission, but here we need user for save if user: - serializer.save(author=user) + # 如果关联了系统用户(user字段不为空),则是管理员/内部人员,直接发布 + # 否则进入审核流程 + status = 'published' if user.user else 'pending' + serializer.save(author=user, status=status) def create(self, request, *args, **kwargs): user = get_current_wechat_user(request) diff --git a/frontend/src/components/CreateTopicModal.jsx b/frontend/src/components/CreateTopicModal.jsx index 6ffd63f..15d08f0 100644 --- a/frontend/src/components/CreateTopicModal.jsx +++ b/frontend/src/components/CreateTopicModal.jsx @@ -152,8 +152,13 @@ const CreateTopicModal = ({ visible, onClose, onSuccess, initialValues, isEditMo await updateTopic(topicId, payload); message.success('修改成功'); } else { - await createTopic(payload); - message.success('发布成功'); + const res = await createTopic(payload); + const topic = res.data || res; + if (topic.status === 'pending') { + message.info('提交成功,请等待管理员审核'); + } else { + message.success('发布成功'); + } } form.resetFields(); diff --git a/miniprogram/src/subpackages/forum/create/index.tsx b/miniprogram/src/subpackages/forum/create/index.tsx index b27af21..e156e5e 100644 --- a/miniprogram/src/subpackages/forum/create/index.tsx +++ b/miniprogram/src/subpackages/forum/create/index.tsx @@ -105,12 +105,18 @@ const CreateTopic = () => { }) Taro.showToast({ title: '更新成功', icon: 'success' }) } else { - await createTopic({ + const res = await createTopic({ title, content, category: categories[categoryIndex].key }) - Taro.showToast({ title: '发布成功', icon: 'success' }) + + const topic = res.data || res + if (topic.status === 'pending') { + Taro.showToast({ title: '已提交,等待审核', icon: 'none', duration: 2000 }) + } else { + Taro.showToast({ title: '发布成功', icon: 'success' }) + } } setTimeout(() => {