diff --git a/core/migrations/0007_alter_insightrecord_speaker.py b/core/migrations/0007_alter_insightrecord_speaker.py
new file mode 100644
index 0000000..541d920
--- /dev/null
+++ b/core/migrations/0007_alter_insightrecord_speaker.py
@@ -0,0 +1,19 @@
+# Generated by Django 5.1.4 on 2026-01-28 10:34
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0006_add_summary'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='insightrecord',
+ name='speaker',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.familymember', verbose_name='发言人'),
+ ),
+ ]
diff --git a/core/templates/core/base.html b/core/templates/core/base.html
index d29981a..d09f994 100644
--- a/core/templates/core/base.html
+++ b/core/templates/core/base.html
@@ -66,6 +66,18 @@
+ {% if user.is_authenticated %}
+ -
+ 欢迎,{{ user.username }}
+
+ -
+ 注销
+
+ {% else %}
+ -
+ 登录
+
+ {% endif %}
-
后台管理
diff --git a/core/templates/core/login.html b/core/templates/core/login.html
new file mode 100644
index 0000000..05cd19f
--- /dev/null
+++ b/core/templates/core/login.html
@@ -0,0 +1,27 @@
+{% extends 'core/base.html' %}
+
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/core/urls.py b/core/urls.py
index 0a97875..9a48529 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -2,6 +2,10 @@ from django.urls import path
from . import views
urlpatterns = [
+ # 登录和注销
+ path('login/', views.user_login, name='login'),
+ path('logout/', views.user_logout, name='logout'),
+
# 首页
path('', views.index, name='index'),
diff --git a/core/views.py b/core/views.py
index 1a489ec..26c703e 100644
--- a/core/views.py
+++ b/core/views.py
@@ -5,6 +5,9 @@ from django.db.models import Count
from django.core.mail import send_mail, EmailMessage
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
+from django.contrib.auth import authenticate, login, logout
+from django.contrib.auth.decorators import login_required
+from django.contrib import messages
from datetime import timedelta, datetime
import os
from loguru import logger
@@ -55,6 +58,7 @@ from .forms import (
)
# 首页视图
+@login_required
def index(request):
"""首页"""
logger.info("用户访问首页")
@@ -864,3 +868,39 @@ def api_submit_summary(request):
except Exception as e:
logger.error(f"API: 提交汇总记录失败: {str(e)}")
return JsonResponse({'success': False, 'message': f"提交失败: {str(e)}"}, status=500)
+
+# 登录视图
+def user_login(request):
+ """用户登录"""
+ if request.user.is_authenticated:
+ logger.info(f"用户 {request.user.username} 已登录,重定向到首页")
+ return redirect('index')
+
+ if request.method == 'POST':
+ username = request.POST.get('username')
+ password = request.POST.get('password')
+
+ logger.info(f"用户登录尝试: {username}")
+
+ user = authenticate(request, username=username, password=password)
+
+ if user is not None:
+ login(request, user)
+ logger.info(f"用户 {username} 登录成功")
+ messages.success(request, '登录成功!')
+ return redirect('index')
+ else:
+ logger.warning(f"用户 {username} 登录失败: 用户名或密码错误")
+ messages.error(request, '用户名或密码错误,请重新尝试。')
+
+ return render(request, 'core/login.html')
+
+# 注销视图
+def user_logout(request):
+ """用户注销"""
+ if request.user.is_authenticated:
+ logger.info(f"用户 {request.user.username} 注销")
+ logout(request)
+ messages.success(request, '已成功注销!')
+
+ return redirect('login')
diff --git a/diary_family/settings.py b/diary_family/settings.py
index cf814ef..20bdd46 100644
--- a/diary_family/settings.py
+++ b/diary_family/settings.py
@@ -124,6 +124,9 @@ STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+# Login URL configuration
+LOGIN_URL = '/login/'
+
# Media files configuration
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'