|
|
|
|
@ -1,3 +1,8 @@
|
|
|
|
|
# Django博客系统信号处理模块
|
|
|
|
|
# 该模块用于注册和处理Django内置信号及自定义信号,实现事件驱动的业务逻辑
|
|
|
|
|
# 核心功能包括:邮件发送、OAuth用户登录处理、模型保存后缓存清理/搜索引擎通知、用户登录登出缓存处理等
|
|
|
|
|
# 通过信号机制解耦业务逻辑,当特定事件触发时自动执行对应处理函数
|
|
|
|
|
|
|
|
|
|
import _thread
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
@ -9,6 +14,7 @@ from django.core.mail import EmailMultiAlternatives
|
|
|
|
|
from django.db.models.signals import post_save
|
|
|
|
|
from django.dispatch import receiver
|
|
|
|
|
|
|
|
|
|
# 导入项目内部模块:评论相关、插件通知、缓存工具、站点工具、OAuth模型
|
|
|
|
|
from comments.models import Comment
|
|
|
|
|
from comments.utils import send_comment_email
|
|
|
|
|
from djangoblog.spider_notify import SpiderNotify
|
|
|
|
|
@ -16,51 +22,72 @@ from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, del
|
|
|
|
|
from djangoblog.utils import get_current_site
|
|
|
|
|
from oauth.models import OAuthUser
|
|
|
|
|
|
|
|
|
|
# 初始化日志记录器,记录信号处理过程中的信息和错误
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
# 自定义信号:OAuth用户登录信号,携带参数'id'(OAuthUser的主键)
|
|
|
|
|
oauth_user_login_signal = django.dispatch.Signal(['id'])
|
|
|
|
|
# 自定义信号:邮件发送信号,携带参数'emailto'(收件人列表)、'title'(邮件标题)、'content'(邮件内容)
|
|
|
|
|
send_email_signal = django.dispatch.Signal(
|
|
|
|
|
['emailto', 'title', 'content'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@receiver(send_email_signal)
|
|
|
|
|
def send_email_signal_handler(sender, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
邮件发送信号的处理函数
|
|
|
|
|
当send_email_signal信号触发时,自动发送HTML格式邮件并记录发送日志
|
|
|
|
|
"""
|
|
|
|
|
# 从信号参数中提取邮件相关信息
|
|
|
|
|
emailto = kwargs['emailto']
|
|
|
|
|
title = kwargs['title']
|
|
|
|
|
content = kwargs['content']
|
|
|
|
|
|
|
|
|
|
# 构建HTML格式邮件(content_subtype设为html支持富文本)
|
|
|
|
|
msg = EmailMultiAlternatives(
|
|
|
|
|
title,
|
|
|
|
|
content,
|
|
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
to=emailto)
|
|
|
|
|
msg.content_subtype = "html"
|
|
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL, # 发件人从Django配置中读取
|
|
|
|
|
to=emailto) # 收件人列表
|
|
|
|
|
msg.content_subtype = "html" # 指定邮件内容为HTML格式
|
|
|
|
|
|
|
|
|
|
# 初始化邮件发送日志模型,记录发送详情
|
|
|
|
|
from servermanager.models import EmailSendLog
|
|
|
|
|
log = EmailSendLog()
|
|
|
|
|
log.title = title
|
|
|
|
|
log.content = content
|
|
|
|
|
log.emailto = ','.join(emailto)
|
|
|
|
|
log.emailto = ','.join(emailto) # 多个收件人用逗号拼接存储
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 发送邮件,result为成功发送的邮件数量
|
|
|
|
|
result = msg.send()
|
|
|
|
|
log.send_result = result > 0
|
|
|
|
|
log.send_result = result > 0 # 发送数量>0表示发送成功
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# 捕获发送异常,记录错误日志
|
|
|
|
|
logger.error(f"失败邮箱号: {emailto}, {e}")
|
|
|
|
|
log.send_result = False
|
|
|
|
|
log.save()
|
|
|
|
|
log.send_result = False # 标记发送失败
|
|
|
|
|
log.save() # 保存日志到数据库
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@receiver(oauth_user_login_signal)
|
|
|
|
|
def oauth_user_login_signal_handler(sender, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
OAuth用户登录信号的处理函数
|
|
|
|
|
当OAuth用户登录成功后,处理用户头像(如跨域头像本地化存储)并清理侧边栏缓存
|
|
|
|
|
"""
|
|
|
|
|
# 从信号参数中提取OAuthUser的id,查询对应的用户实例
|
|
|
|
|
id = kwargs['id']
|
|
|
|
|
oauthuser = OAuthUser.objects.get(id=id)
|
|
|
|
|
# 获取当前站点域名(用于判断头像是否为本地地址)
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
|
|
|
|
|
# 若用户有头像且头像地址不包含当前站点域名(跨域头像),则本地化存储
|
|
|
|
|
if oauthuser.picture and not oauthuser.picture.find(site) >= 0:
|
|
|
|
|
from djangoblog.utils import save_user_avatar
|
|
|
|
|
oauthuser.picture = save_user_avatar(oauthuser.picture)
|
|
|
|
|
oauthuser.save()
|
|
|
|
|
oauthuser.picture = save_user_avatar(oauthuser.picture) # 保存头像到本地并更新地址
|
|
|
|
|
oauthuser.save() # 保存更新后的用户信息
|
|
|
|
|
|
|
|
|
|
# 清理侧边栏缓存(确保登录后侧边栏展示最新数据)
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -73,42 +100,64 @@ def model_post_save_callback(
|
|
|
|
|
using,
|
|
|
|
|
update_fields,
|
|
|
|
|
**kwargs):
|
|
|
|
|
clearcache = False
|
|
|
|
|
"""
|
|
|
|
|
Django模型保存后信号的处理函数(post_save)
|
|
|
|
|
触发时机:任何模型执行save()方法后(新增/更新)
|
|
|
|
|
主要处理:搜索引擎通知、缓存清理、评论审核通过后的联动操作
|
|
|
|
|
"""
|
|
|
|
|
clearcache = False # 标记是否需要清理全局缓存
|
|
|
|
|
|
|
|
|
|
# 跳过Admin操作日志模型(LogEntry)的处理,无需触发后续逻辑
|
|
|
|
|
if isinstance(instance, LogEntry):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 若模型实例有get_full_url方法(通常是博客文章等需要对外展示的模型)
|
|
|
|
|
if 'get_full_url' in dir(instance):
|
|
|
|
|
# 判断是否仅更新浏览量字段(views)
|
|
|
|
|
is_update_views = update_fields == {'views'}
|
|
|
|
|
# 非测试环境且不是仅更新浏览量时,通知搜索引擎(如百度)收录新链接
|
|
|
|
|
if not settings.TESTING and not is_update_views:
|
|
|
|
|
try:
|
|
|
|
|
notify_url = instance.get_full_url()
|
|
|
|
|
SpiderNotify.baidu_notify([notify_url])
|
|
|
|
|
notify_url = instance.get_full_url() # 获取模型实例的完整访问链接
|
|
|
|
|
SpiderNotify.baidu_notify([notify_url]) # 通知百度搜索引擎
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
logger.error("notify sipder", ex)
|
|
|
|
|
logger.error("notify sipder", ex) # 记录搜索引擎通知失败的错误
|
|
|
|
|
# 非浏览量更新时,标记需要清理缓存
|
|
|
|
|
if not is_update_views:
|
|
|
|
|
clearcache = True
|
|
|
|
|
|
|
|
|
|
# 若保存的是评论模型实例
|
|
|
|
|
if isinstance(instance, Comment):
|
|
|
|
|
# 仅处理审核通过的评论(is_enable为True)
|
|
|
|
|
if instance.is_enable:
|
|
|
|
|
# 获取评论对应的文章绝对路径
|
|
|
|
|
path = instance.article.get_absolute_url()
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
# 处理带端口的域名(如localhost:8000),仅保留主域名部分
|
|
|
|
|
if site.find(':') > 0:
|
|
|
|
|
site = site[0:site.find(':')]
|
|
|
|
|
|
|
|
|
|
# 清理文章详情页的视图缓存(确保评论实时展示)
|
|
|
|
|
expire_view_cache(
|
|
|
|
|
path,
|
|
|
|
|
servername=site,
|
|
|
|
|
serverport=80,
|
|
|
|
|
key_prefix='blogdetail')
|
|
|
|
|
# 清理SEO相关缓存
|
|
|
|
|
if cache.get('seo_processor'):
|
|
|
|
|
cache.delete('seo_processor')
|
|
|
|
|
# 清理该文章的评论列表缓存
|
|
|
|
|
comment_cache_key = 'article_comments_{id}'.format(
|
|
|
|
|
id=instance.article.id)
|
|
|
|
|
cache.delete(comment_cache_key)
|
|
|
|
|
# 清理侧边栏缓存和评论相关视图缓存
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
delete_view_cache('article_comments', [str(instance.article.pk)])
|
|
|
|
|
|
|
|
|
|
# 启动新线程发送评论通知邮件(避免阻塞主线程)
|
|
|
|
|
_thread.start_new_thread(send_comment_email, (instance,))
|
|
|
|
|
|
|
|
|
|
# 若标记需要清理缓存,则执行全局缓存清理
|
|
|
|
|
if clearcache:
|
|
|
|
|
cache.clear()
|
|
|
|
|
|
|
|
|
|
@ -116,7 +165,13 @@ def model_post_save_callback(
|
|
|
|
|
@receiver(user_logged_in)
|
|
|
|
|
@receiver(user_logged_out)
|
|
|
|
|
def user_auth_callback(sender, request, user, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
用户登录/登出信号的处理函数
|
|
|
|
|
触发时机:用户登录(user_logged_in)或登出(user_logged_out)后
|
|
|
|
|
主要处理:记录日志并清理侧边栏缓存(确保登录状态变化后展示最新数据)
|
|
|
|
|
"""
|
|
|
|
|
# 若用户存在且用户名有效
|
|
|
|
|
if user and user.username:
|
|
|
|
|
logger.info(user)
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
# cache.clear()
|
|
|
|
|
logger.info(user) # 记录用户登录/登出日志
|
|
|
|
|
delete_sidebar_cache() # 清理侧边栏缓存
|
|
|
|
|
# cache.clear() # 全局缓存清理(按需启用)
|