feat(报告系统): 添加汇总记录功能

新增汇总记录模型、表单、视图和模板,支持在报告中显示昨日汇总记录
This commit is contained in:
2026-01-24 19:48:40 +08:00
parent 6ef03ee63a
commit ab24c120e1
7 changed files with 248 additions and 1 deletions

View File

@@ -6,6 +6,8 @@ from .models import (
Status, Status,
PlanType, PlanType,
FamilyMember, FamilyMember,
SummaryCategory,
Summary,
ReadingRecord, ReadingRecord,
InsightRecord, InsightRecord,
FamilyTask, FamilyTask,
@@ -14,6 +16,19 @@ from .models import (
) )
@admin.register(SummaryCategory)
class SummaryCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'created_at', 'updated_at')
search_fields = ('name',)
@admin.register(Summary)
class SummaryAdmin(admin.ModelAdmin):
list_display = ('date', 'category', 'speaker', 'source', 'created_at')
list_filter = ('date', 'category', 'speaker')
search_fields = ('content', 'source')
@admin.register(FamilyMember) @admin.register(FamilyMember)
class FamilyMemberAdmin(admin.ModelAdmin): class FamilyMemberAdmin(admin.ModelAdmin):
list_display = ('name', 'created_at', 'updated_at') list_display = ('name', 'created_at', 'updated_at')

View File

@@ -4,6 +4,7 @@ from django.utils import timezone
from .models import ( from .models import (
ReadingRecord, ReadingRecord,
InsightRecord, InsightRecord,
Summary,
FamilyTask, FamilyTask,
TodayPlan, TodayPlan,
SystemConfig, SystemConfig,
@@ -35,6 +36,20 @@ class InsightRecordForm(forms.ModelForm):
'file': forms.FileInput(attrs={'class': 'form-control'}), 'file': forms.FileInput(attrs={'class': 'form-control'}),
} }
class SummaryForm(forms.ModelForm):
"""汇总记录表单"""
class Meta:
model = Summary
fields = ['date', 'category', 'speaker', 'content', 'source', 'file']
widgets = {
'date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'category': forms.Select(attrs={'class': 'form-select'}),
'speaker': forms.Select(attrs={'class': 'form-select'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'placeholder': '请输入汇总内容'}),
'source': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入来源'}),
'file': forms.FileInput(attrs={'class': 'form-control'}),
}
class FamilyTaskForm(forms.ModelForm): class FamilyTaskForm(forms.ModelForm):
"""家庭事项表单""" """家庭事项表单"""
class Meta: class Meta:

View File

@@ -0,0 +1,47 @@
# Generated by Django 5.1.4 on 2026-01-23 15:06
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_add_speaker_fields'),
]
operations = [
migrations.CreateModel(
name='SummaryCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, unique=True, verbose_name='名称')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '汇总分类',
'verbose_name_plural': '汇总分类',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='Summary',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(default=django.utils.timezone.now, verbose_name='日期')),
('content', models.TextField(verbose_name='内容')),
('source', models.CharField(max_length=200, blank=True, null=True, verbose_name='来源')),
('file', models.FileField(blank=True, null=True, upload_to='summary_files/', verbose_name='上传文件')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.summarycategory', verbose_name='分类')),
('speaker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.familymember', verbose_name='发言人')),
],
options={
'verbose_name': '汇总记录',
'verbose_name_plural': '汇总记录',
'ordering': ['-date', '-created_at'],
},
),
]

View File

@@ -159,6 +159,39 @@ class TodayPlan(models.Model):
def __str__(self): def __str__(self):
return f"{self.date} - {self.content[:20]}..." return f"{self.date} - {self.content[:20]}..."
class SummaryCategory(models.Model):
"""汇总分类"""
name = models.CharField(max_length=20, unique=True, verbose_name="名称")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "汇总分类"
verbose_name_plural = "汇总分类"
ordering = ['name']
def __str__(self):
return self.name
class Summary(models.Model):
"""汇总记录表"""
date = models.DateField(default=timezone.now, verbose_name="日期")
category = models.ForeignKey(SummaryCategory, on_delete=models.CASCADE, verbose_name="分类")
speaker = models.ForeignKey(FamilyMember, on_delete=models.CASCADE, verbose_name="发言人")
content = models.TextField(verbose_name="内容")
source = models.CharField(max_length=200, blank=True, null=True, verbose_name="来源")
file = models.FileField(upload_to='summary_files/', blank=True, null=True, verbose_name="上传文件")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "汇总记录"
verbose_name_plural = "汇总记录"
ordering = ['-date', '-created_at']
def __str__(self):
return f"{self.category.name} - {self.date}"
class SystemConfig(models.Model): class SystemConfig(models.Model):
"""系统配置表""" """系统配置表"""
smtp_server = models.CharField(max_length=100, blank=True, null=True, verbose_name="SMTP服务器") smtp_server = models.CharField(max_length=100, blank=True, null=True, verbose_name="SMTP服务器")

View File

@@ -201,4 +201,36 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 昨日汇总记录 -->
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="card-title mb-0">昨日汇总记录</h5>
</div>
<div class="card-body">
{% if yesterday_summary %}
<div class="row">
{% for summary in yesterday_summary %}
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<span><span class="badge bg-primary">{{ summary.category.name }}</span></span>
<small class="text-muted">{{ summary.speaker.name }}</small>
</div>
<div class="card-body">
<p class="card-text">{{ summary.content }}</p>
{% if summary.source %}
<small class="text-muted">来源:{{ summary.source }}</small>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-muted">昨日没有汇总记录</p>
{% endif %}
</div>
</div>
{% endblock %} {% endblock %}

View File

@@ -141,6 +141,35 @@
{% endif %} {% endif %}
</div> </div>
<!-- 昨日汇总记录 -->
<div class="section">
<h2>昨日汇总记录</h2>
{% if yesterday_summary %}
<table>
<thead>
<tr>
<th>分类</th>
<th>发言人</th>
<th>内容</th>
<th>来源</th>
</tr>
</thead>
<tbody>
{% for summary in yesterday_summary %}
<tr>
<td>{{ summary.category.name }}</td>
<td>{{ summary.speaker.name }}</td>
<td>{{ summary.content }}</td>
<td>{{ summary.source|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="no-data">昨日没有汇总记录</div>
{% endif %}
</div>
<!-- 今日计划 --> <!-- 今日计划 -->
<div class="section"> <div class="section">
<h2>今日计划</h2> <h2>今日计划</h2>

View File

@@ -37,14 +37,17 @@ def is_weasyprint_available():
from .models import ( from .models import (
ReadingRecord, ReadingRecord,
InsightRecord, InsightRecord,
Summary,
FamilyTask, FamilyTask,
TodayPlan, TodayPlan,
SystemConfig, SystemConfig,
FamilyMember FamilyMember,
SummaryCategory
) )
from .forms import ( from .forms import (
ReadingRecordForm, ReadingRecordForm,
InsightRecordForm, InsightRecordForm,
SummaryForm,
FamilyTaskForm, FamilyTaskForm,
TodayPlanForm, TodayPlanForm,
SystemConfigForm SystemConfigForm
@@ -304,6 +307,69 @@ def delete_today_insight(request, pk):
context = {'insight': insight} context = {'insight': insight}
return render(request, 'core/delete_insight.html', context) return render(request, 'core/delete_insight.html', context)
# 汇总记录视图
def summaries(request):
"""汇总记录"""
logger.info("用户访问汇总记录页面")
today = timezone.now().date()
yesterday = today - timedelta(days=1)
summary_records = Summary.objects.filter(date=yesterday)
context = {
'yesterday': yesterday,
'summary_records': summary_records,
}
return render(request, 'core/summaries.html', context)
# 添加汇总记录
def add_summary(request):
"""添加汇总记录"""
family_members = FamilyMember.objects.all()
categories = SummaryCategory.objects.all()
if request.method == 'POST':
form = SummaryForm(request.POST, request.FILES)
if form.is_valid():
form.save()
logger.info(f"添加汇总记录: {form.cleaned_data['content'][:20]}...")
return redirect('summaries')
else:
form = SummaryForm()
context = {'form': form, 'family_members': family_members, 'categories': categories}
return render(request, 'core/add_summary.html', context)
# 编辑汇总记录
def edit_summary(request, pk):
"""编辑汇总记录"""
summary = get_object_or_404(Summary, pk=pk)
family_members = FamilyMember.objects.all()
categories = SummaryCategory.objects.all()
if request.method == 'POST':
form = SummaryForm(request.POST, request.FILES, instance=summary)
if form.is_valid():
form.save()
logger.info(f"编辑汇总记录: {form.cleaned_data['content'][:20]}...")
return redirect('summaries')
else:
form = SummaryForm(instance=summary)
context = {'form': form, 'summary': summary, 'family_members': family_members, 'categories': categories}
return render(request, 'core/edit_summary.html', context)
# 删除汇总记录
def delete_summary(request, pk):
"""删除汇总记录"""
summary = get_object_or_404(Summary, pk=pk)
if request.method == 'POST':
summary.delete()
logger.info(f"删除汇总记录: {summary.content[:20]}...")
return redirect('summaries')
context = {'summary': summary}
return render(request, 'core/delete_summary.html', context)
# 家庭事项视图 # 家庭事项视图
def family_tasks(request): def family_tasks(request):
"""家庭事项""" """家庭事项"""
@@ -445,6 +511,7 @@ def generate_report(request):
# 获取昨日记录 # 获取昨日记录
yesterday_reading = ReadingRecord.objects.filter(date=yesterday) yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
yesterday_insight = InsightRecord.objects.filter(date=yesterday) yesterday_insight = InsightRecord.objects.filter(date=yesterday)
yesterday_summary = Summary.objects.filter(date=yesterday)
# 获取今日计划 # 获取今日计划
today_plan = TodayPlan.objects.filter(date=today) today_plan = TodayPlan.objects.filter(date=today)
@@ -478,6 +545,7 @@ def generate_report(request):
'yesterday': yesterday, 'yesterday': yesterday,
'yesterday_reading': yesterday_reading, 'yesterday_reading': yesterday_reading,
'yesterday_insight': yesterday_insight, 'yesterday_insight': yesterday_insight,
'yesterday_summary': yesterday_summary,
'today_plan': today_plan, 'today_plan': today_plan,
'family_task_stats': family_task_stats, 'family_task_stats': family_task_stats,
'historical_dates': historical_dates, 'historical_dates': historical_dates,
@@ -496,6 +564,7 @@ def view_report(request, date):
# 获取指定日期的记录 # 获取指定日期的记录
yesterday_reading = ReadingRecord.objects.filter(date=yesterday) yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
yesterday_insight = InsightRecord.objects.filter(date=yesterday) yesterday_insight = InsightRecord.objects.filter(date=yesterday)
yesterday_summary = Summary.objects.filter(date=yesterday)
today_plan = TodayPlan.objects.filter(date=report_date) today_plan = TodayPlan.objects.filter(date=report_date)
# 获取家庭事项统计 # 获取家庭事项统计
@@ -506,6 +575,7 @@ def view_report(request, date):
'yesterday': yesterday, 'yesterday': yesterday,
'yesterday_reading': yesterday_reading, 'yesterday_reading': yesterday_reading,
'yesterday_insight': yesterday_insight, 'yesterday_insight': yesterday_insight,
'yesterday_summary': yesterday_summary,
'today_plan': today_plan, 'today_plan': today_plan,
'family_task_stats': family_task_stats, 'family_task_stats': family_task_stats,
} }
@@ -528,6 +598,7 @@ def generate_pdf_report(request, date):
# 获取指定日期的记录 # 获取指定日期的记录
yesterday_reading = ReadingRecord.objects.filter(date=yesterday) yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
yesterday_insight = InsightRecord.objects.filter(date=yesterday) yesterday_insight = InsightRecord.objects.filter(date=yesterday)
yesterday_summary = Summary.objects.filter(date=yesterday)
today_plan = TodayPlan.objects.filter(date=report_date) today_plan = TodayPlan.objects.filter(date=report_date)
# 获取家庭事项统计 # 获取家庭事项统计
@@ -538,6 +609,7 @@ def generate_pdf_report(request, date):
'yesterday': yesterday, 'yesterday': yesterday,
'yesterday_reading': yesterday_reading, 'yesterday_reading': yesterday_reading,
'yesterday_insight': yesterday_insight, 'yesterday_insight': yesterday_insight,
'yesterday_summary': yesterday_summary,
'today_plan': today_plan, 'today_plan': today_plan,
'family_task_stats': family_task_stats, 'family_task_stats': family_task_stats,
} }
@@ -579,6 +651,7 @@ def preview_pdf_report(request, date):
# 获取指定日期的记录 # 获取指定日期的记录
yesterday_reading = ReadingRecord.objects.filter(date=yesterday) yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
yesterday_insight = InsightRecord.objects.filter(date=yesterday) yesterday_insight = InsightRecord.objects.filter(date=yesterday)
yesterday_summary = Summary.objects.filter(date=yesterday)
today_plan = TodayPlan.objects.filter(date=report_date) today_plan = TodayPlan.objects.filter(date=report_date)
# 获取家庭事项统计 # 获取家庭事项统计
@@ -589,6 +662,7 @@ def preview_pdf_report(request, date):
'yesterday': yesterday, 'yesterday': yesterday,
'yesterday_reading': yesterday_reading, 'yesterday_reading': yesterday_reading,
'yesterday_insight': yesterday_insight, 'yesterday_insight': yesterday_insight,
'yesterday_summary': yesterday_summary,
'today_plan': today_plan, 'today_plan': today_plan,
'family_task_stats': family_task_stats, 'family_task_stats': family_task_stats,
} }
@@ -641,6 +715,7 @@ def send_email_view(request):
yesterday_reading = ReadingRecord.objects.filter(date=yesterday) yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
yesterday_insight = InsightRecord.objects.filter(date=yesterday) yesterday_insight = InsightRecord.objects.filter(date=yesterday)
yesterday_summary = Summary.objects.filter(date=yesterday)
today_plan = TodayPlan.objects.filter(date=report_date) today_plan = TodayPlan.objects.filter(date=report_date)
family_task_stats = FamilyTask.objects.values('type').annotate(count=Count('id')) family_task_stats = FamilyTask.objects.values('type').annotate(count=Count('id'))
@@ -649,6 +724,7 @@ def send_email_view(request):
'yesterday': yesterday, 'yesterday': yesterday,
'yesterday_reading': yesterday_reading, 'yesterday_reading': yesterday_reading,
'yesterday_insight': yesterday_insight, 'yesterday_insight': yesterday_insight,
'yesterday_summary': yesterday_summary,
'today_plan': today_plan, 'today_plan': today_plan,
'family_task_stats': family_task_stats, 'family_task_stats': family_task_stats,
} }