This commit is contained in:
@@ -205,12 +205,23 @@ class ActivitySignupAdmin(ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(Topic)
|
@admin.register(Topic)
|
||||||
class TopicAdmin(OrderableAdminMixin, ModelAdmin):
|
class TopicAdmin(OrderableAdminMixin, ModelAdmin):
|
||||||
list_display = ('title', 'category', 'author', 'get_related_item', 'reply_count', 'view_count', 'is_pinned', 'created_at', 'order_actions')
|
list_display = ('title', 'status', '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_filter = ('status', 'category', 'is_pinned', 'created_at', 'related_product', 'related_service', 'related_course')
|
||||||
search_fields = ('title', 'content', 'author__nickname')
|
search_fields = ('title', 'content', 'author__nickname')
|
||||||
autocomplete_fields = ['author', 'related_product', 'related_service', 'related_course']
|
autocomplete_fields = ['author', 'related_product', 'related_service', 'related_course']
|
||||||
inlines = [TopicMediaInline, ReplyInline]
|
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):
|
def save_model(self, request, obj, form, change):
|
||||||
# 当帖子被置顶时(新建或修改状态),默认将排序值设为0
|
# 当帖子被置顶时(新建或修改状态),默认将排序值设为0
|
||||||
@@ -232,7 +243,7 @@ class TopicAdmin(OrderableAdminMixin, ModelAdmin):
|
|||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('帖子内容', {
|
('帖子内容', {
|
||||||
'fields': ('title', 'category', 'content', 'is_pinned')
|
'fields': ('title', 'status', 'category', 'content', 'is_pinned')
|
||||||
}),
|
}),
|
||||||
('关联信息', {
|
('关联信息', {
|
||||||
'fields': ('author', 'related_product', 'related_service', 'related_course'),
|
'fields': ('author', 'related_product', 'related_service', 'related_course'),
|
||||||
|
|||||||
18
backend/community/migrations/0015_topic_status.py
Normal file
18
backend/community/migrations/0015_topic_status.py
Normal file
@@ -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='状态'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -127,6 +127,13 @@ class Topic(models.Model):
|
|||||||
)
|
)
|
||||||
category = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='discussion', verbose_name="分类")
|
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格式,支持插入图片")
|
content = models.TextField(verbose_name="内容", help_text="支持Markdown格式,支持插入图片")
|
||||||
author = models.ForeignKey(WeChatUser, on_delete=models.CASCADE, related_name='topics', verbose_name="作者")
|
author = models.ForeignKey(WeChatUser, on_delete=models.CASCADE, related_name='topics', verbose_name="作者")
|
||||||
|
|
||||||
|
|||||||
@@ -120,14 +120,14 @@ class TopicSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Topic
|
model = Topic
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'title', 'category', 'content', 'author', 'author_info',
|
'id', 'title', 'category', 'status', 'content', 'author', 'author_info',
|
||||||
'related_product', 'product_info',
|
'related_product', 'product_info',
|
||||||
'related_service', 'service_info',
|
'related_service', 'service_info',
|
||||||
'related_course', 'course_info',
|
'related_course', 'course_info',
|
||||||
'view_count', 'is_pinned', 'created_at', 'updated_at',
|
'view_count', 'is_pinned', 'created_at', 'updated_at',
|
||||||
'is_verified_owner', 'replies', 'media', 'media_ids'
|
'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):
|
def create(self, validated_data):
|
||||||
media_ids = validated_data.pop('media_ids', [])
|
media_ids = validated_data.pop('media_ids', [])
|
||||||
|
|||||||
@@ -262,11 +262,21 @@ class TopicViewSet(viewsets.ModelViewSet):
|
|||||||
ordering_fields = ['created_at', 'view_count', 'order']
|
ordering_fields = ['created_at', 'view_count', 'order']
|
||||||
ordering = ['-is_pinned', 'order', '-created_at']
|
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):
|
def perform_create(self, serializer):
|
||||||
user = get_current_wechat_user(self.request)
|
user = get_current_wechat_user(self.request)
|
||||||
# Auth check is done in create or permission, but here we need user for save
|
# Auth check is done in create or permission, but here we need user for save
|
||||||
if user:
|
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):
|
def create(self, request, *args, **kwargs):
|
||||||
user = get_current_wechat_user(request)
|
user = get_current_wechat_user(request)
|
||||||
|
|||||||
@@ -152,8 +152,13 @@ const CreateTopicModal = ({ visible, onClose, onSuccess, initialValues, isEditMo
|
|||||||
await updateTopic(topicId, payload);
|
await updateTopic(topicId, payload);
|
||||||
message.success('修改成功');
|
message.success('修改成功');
|
||||||
} else {
|
} else {
|
||||||
await createTopic(payload);
|
const res = await createTopic(payload);
|
||||||
message.success('发布成功');
|
const topic = res.data || res;
|
||||||
|
if (topic.status === 'pending') {
|
||||||
|
message.info('提交成功,请等待管理员审核');
|
||||||
|
} else {
|
||||||
|
message.success('发布成功');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
|||||||
@@ -105,12 +105,18 @@ const CreateTopic = () => {
|
|||||||
})
|
})
|
||||||
Taro.showToast({ title: '更新成功', icon: 'success' })
|
Taro.showToast({ title: '更新成功', icon: 'success' })
|
||||||
} else {
|
} else {
|
||||||
await createTopic({
|
const res = await createTopic({
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
category: categories[categoryIndex].key
|
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(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user