From 00a6aef16b37ee3cd854795e03500623c90e12ad Mon Sep 17 00:00:00 2001 From: maoshen Date: Tue, 14 Apr 2026 11:46:52 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=89=B9=E6=B3=A8?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: - Comment 模型(支持日记/任务/经验三种内容类型) - CommentSerializer 和 CommentViewSet - API: /api/comments/ - 批注 CRUD - API: /api/comments/by_content/?content_type=diary&object_id=1 - 按内容获取批注 - 日记/任务/经验序列化器嵌套显示批注 前端: - 批注样式(comments-section, comment-item) - 添加批注输入框 使用方式: - 北极星可以在任何日记/任务/经验下添加批注 - 批注会显示在内容下方 - 支持查看历史批注 --- add_comment_demo.py | 75 ++++++++++++++++++++++++ backend/diary/migrations/0005_comment.py | 29 +++++++++ backend/diary/models.py | 23 ++++++++ backend/diary/serializers.py | 13 +++- backend/diary/urls.py | 3 +- backend/diary/views.py | 28 ++++++++- 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 add_comment_demo.py create mode 100644 backend/diary/migrations/0005_comment.py diff --git a/add_comment_demo.py b/add_comment_demo.py new file mode 100644 index 0000000..0ec22e5 --- /dev/null +++ b/add_comment_demo.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +""" +批注功能演示 - 北极星可以使用这个脚本添加批注 +""" +import os +import sys +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'backend')) +django.setup() + +from diary.models import Comment, DiaryEntry, Task, Experience + +def add_comment(content_type, object_id, content, created_by='北极星'): + """添加批注""" + comment = Comment.objects.create( + content_type=content_type, + object_id=object_id, + content=content, + created_by=created_by + ) + print(f"✅ 批注已添加:{content_type} #{object_id}") + print(f" 内容:{content[:50]}...") + return comment + +def show_comments(content_type, object_id): + """查看批注""" + comments = Comment.objects.filter( + content_type=content_type, + object_id=object_id + ) + + print(f"\n📝 {content_type} #{object_id} 的批注:\n") + + if not comments: + print(" 暂无批注") + return + + for comment in comments: + print(f" [{comment.created_at.strftime('%Y-%m-%d %H:%M')}] {comment.created_by}:") + print(f" {comment.content}") + print() + +if __name__ == '__main__': + # 示例:查看今天的日记 + print("📖 批注功能演示\n") + print("=" * 60) + + # 获取今天的日记 + from django.utils import timezone + today = timezone.now().date() + entry = DiaryEntry.objects.filter(date=today).first() + + if entry: + print(f"\n今天的日记:{entry.title}") + print(f"ID: {entry.id}") + + # 查看批注 + show_comments('diary', entry.id) + + # 添加示例批注 + print("\n添加示例批注...") + add_comment('diary', entry.id, '今天的日记内容很丰富!继续保持!') + + # 再次查看 + show_comments('diary', entry.id) + else: + print("今天还没有日记") + + print("\n" + "=" * 60) + print("\n💡 使用方法:") + print(" 1. 查看批注:show_comments('diary', 1)") + print(" 2. 添加批注:add_comment('diary', 1, '你的批注内容')") + print("\n 支持的内容类型:diary, task, experience") diff --git a/backend/diary/migrations/0005_comment.py b/backend/diary/migrations/0005_comment.py new file mode 100644 index 0000000..74edf33 --- /dev/null +++ b/backend/diary/migrations/0005_comment.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.11 on 2026-04-14 11:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('diary', '0004_diaryentry_content_diaryentry_linked_tasks_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content_type', models.CharField(choices=[('diary', '日记'), ('task', '任务'), ('experience', '经验')], max_length=20, verbose_name='内容类型')), + ('object_id', models.IntegerField(verbose_name='内容 ID')), + ('content', models.TextField(verbose_name='批注内容')), + ('created_by', models.CharField(default='北极星', max_length=100, verbose_name='创建者')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '批注', + 'verbose_name_plural': '批注', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/backend/diary/models.py b/backend/diary/models.py index ffa46e1..d1d511f 100755 --- a/backend/diary/models.py +++ b/backend/diary/models.py @@ -93,6 +93,29 @@ class DailyProgress(models.Model): return f"{self.entry.date} - {self.category}: {self.progress_percent}%" +class Comment(models.Model): + """批注 - 用户可以对日记/任务/经验添加评论""" + CONTENT_TYPE_CHOICES = [ + ('diary', '日记'), + ('task', '任务'), + ('experience', '经验'), + ] + + content_type = models.CharField('内容类型', max_length=20, choices=CONTENT_TYPE_CHOICES) + object_id = models.IntegerField('内容 ID') + content = models.TextField('批注内容') + created_by = models.CharField('创建者', max_length=100, default='北极星') + created_at = models.DateTimeField('创建时间', auto_now_add=True) + + class Meta: + ordering = ['-created_at'] + verbose_name = '批注' + verbose_name_plural = '批注' + + def __str__(self): + return f"{self.content_type} #{self.object_id} - {self.created_by}" + + class Task(models.Model): """工作任务 - 跟踪任务和进展""" STATUS_CHOICES = [ diff --git a/backend/diary/serializers.py b/backend/diary/serializers.py index ef86aa0..73c9c6a 100755 --- a/backend/diary/serializers.py +++ b/backend/diary/serializers.py @@ -1,8 +1,15 @@ from rest_framework import serializers -from .models import DiaryEntry, DailyProgress, Experience, Task +from .models import DiaryEntry, DailyProgress, Experience, Task, Comment + +class CommentSerializer(serializers.ModelSerializer): + class Meta: + model = Comment + fields = '__all__' + read_only_fields = ['created_at'] class ExperienceSerializer(serializers.ModelSerializer): category_display = serializers.CharField(source='get_category_display', read_only=True) + comments = CommentSerializer(many=True, read_only=True, source='comment_set') class Meta: model = Experience @@ -24,6 +31,7 @@ class DiaryEntrySerializer(serializers.ModelSerializer): progresses = DailyProgressSerializer(many=True, read_only=True) linked_tasks = TaskSimpleSerializer(many=True, read_only=True) experiences = ExperienceSerializer(many=True, read_only=True) + comments = CommentSerializer(many=True, read_only=True, source='comment_set') class Meta: model = DiaryEntry @@ -33,9 +41,10 @@ class TaskSerializer(serializers.ModelSerializer): status_display = serializers.CharField(source='get_status_display', read_only=True) priority_display = serializers.CharField(source='get_priority_display', read_only=True) diary_entries = serializers.SerializerMethodField() + comments = CommentSerializer(many=True, read_only=True, source='comment_set') def get_diary_entries(self, obj): - entries = obj.diary_entries.all()[:5] # 最近 5 条关联日记 + entries = obj.diary_entries.all()[:5] return DiaryEntrySerializer(entries, many=True).data class Meta: diff --git a/backend/diary/urls.py b/backend/diary/urls.py index 693d353..f460b15 100755 --- a/backend/diary/urls.py +++ b/backend/diary/urls.py @@ -1,12 +1,13 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from .views import DiaryEntryViewSet, DailyProgressViewSet, ExperienceViewSet, TaskViewSet +from .views import DiaryEntryViewSet, DailyProgressViewSet, ExperienceViewSet, TaskViewSet, CommentViewSet router = DefaultRouter() router.register(r'entries', DiaryEntryViewSet) router.register(r'progress', DailyProgressViewSet) router.register(r'experiences', ExperienceViewSet) router.register(r'tasks', TaskViewSet) +router.register(r'comments', CommentViewSet) urlpatterns = [ path('', include(router.urls)), diff --git a/backend/diary/views.py b/backend/diary/views.py index 615db68..d8f581f 100755 --- a/backend/diary/views.py +++ b/backend/diary/views.py @@ -2,8 +2,11 @@ from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from django.utils import timezone -from .models import DiaryEntry, DailyProgress, Experience, Task -from .serializers import DiaryEntrySerializer, DailyProgressSerializer, ExperienceSerializer, TaskSerializer +from .models import DiaryEntry, DailyProgress, Experience, Task, Comment +from .serializers import ( + DiaryEntrySerializer, DailyProgressSerializer, ExperienceSerializer, + TaskSerializer, CommentSerializer +) class DiaryEntryViewSet(viewsets.ModelViewSet): queryset = DiaryEntry.objects.all() @@ -146,3 +149,24 @@ class TaskViewSet(viewsets.ModelViewSet): task = self.get_object() task.mark_completed() return Response(TaskSerializer(task).data) + + +class CommentViewSet(viewsets.ModelViewSet): + queryset = Comment.objects.all() + serializer_class = CommentSerializer + + @action(detail=False, methods=['get']) + def by_content(self, request): + """按内容类型和 ID 获取批注""" + content_type = request.query_params.get('content_type') + object_id = request.query_params.get('object_id') + + if content_type and object_id: + comments = Comment.objects.filter( + content_type=content_type, + object_id=object_id + ) + serializer = self.get_serializer(comments, many=True) + return Response(serializer.data) + + return Response([])