|
|
|
|
@ -1,7 +1,11 @@
|
|
|
|
|
# 导入线程模块:用于异步执行耗时操作(如发送邮件,避免阻塞主流程)
|
|
|
|
|
import _thread
|
|
|
|
|
# 导入日志模块:记录操作日志和错误信息
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
# 导入 Django 信号核心类:用于定义和处理自定义信号
|
|
|
|
|
import django.dispatch
|
|
|
|
|
# 导入 Django 配置、模型、工具类:支撑信号处理中的业务逻辑
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
from django.contrib.admin.models import LogEntry
|
|
|
|
|
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
|
|
|
|
@ -9,6 +13,7 @@ from django.core.mail import EmailMultiAlternatives
|
|
|
|
|
from django.db.models.signals import post_save
|
|
|
|
|
from django.dispatch import receiver
|
|
|
|
|
|
|
|
|
|
# 导入项目自定义模型和工具函数:适配博客业务场景
|
|
|
|
|
from comments.models import Comment
|
|
|
|
|
from comments.utils import send_comment_email
|
|
|
|
|
from djangoblog.spider_notify import SpiderNotify
|
|
|
|
|
@ -16,54 +21,73 @@ 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参数
|
|
|
|
|
oauth_user_login_signal = django.dispatch.Signal(['id'])
|
|
|
|
|
# 定义自定义信号:发送邮件的信号,携带收件人、标题、内容参数
|
|
|
|
|
send_email_signal = django.dispatch.Signal(
|
|
|
|
|
['emailto', 'title', 'content'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 信号接收器:监听 send_email_signal 信号,触发邮件发送逻辑
|
|
|
|
|
@receiver(send_email_signal)
|
|
|
|
|
def send_email_signal_handler(sender, **kwargs):
|
|
|
|
|
emailto = kwargs['emailto']
|
|
|
|
|
title = kwargs['title']
|
|
|
|
|
content = kwargs['content']
|
|
|
|
|
# 从信号参数中提取邮件相关信息
|
|
|
|
|
emailto = kwargs['emailto'] # 收件人列表
|
|
|
|
|
title = kwargs['title'] # 邮件标题
|
|
|
|
|
content = kwargs['content'] # 邮件内容(HTML格式)
|
|
|
|
|
|
|
|
|
|
# 构建 HTML 格式邮件:支持富文本内容
|
|
|
|
|
msg = EmailMultiAlternatives(
|
|
|
|
|
title,
|
|
|
|
|
content,
|
|
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL, # 发件人(从项目配置中获取)
|
|
|
|
|
to=emailto)
|
|
|
|
|
msg.content_subtype = "html"
|
|
|
|
|
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 = 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 # 标记发送失败
|
|
|
|
|
finally:
|
|
|
|
|
# 保存日志记录到数据库
|
|
|
|
|
log.save()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 信号接收器:监听 oauth_user_login_signal 信号,处理 OAuth 登录后的逻辑
|
|
|
|
|
@receiver(oauth_user_login_signal)
|
|
|
|
|
def oauth_user_login_signal_handler(sender, **kwargs):
|
|
|
|
|
# 从信号参数中提取 OAuth 用户ID
|
|
|
|
|
id = kwargs['id']
|
|
|
|
|
# 获取对应的 OAuth 用户对象
|
|
|
|
|
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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 信号接收器:监听所有模型的 post_save 信号(模型保存后触发)
|
|
|
|
|
@receiver(post_save)
|
|
|
|
|
def model_post_save_callback(
|
|
|
|
|
sender,
|
|
|
|
|
@ -73,50 +97,74 @@ def model_post_save_callback(
|
|
|
|
|
using,
|
|
|
|
|
update_fields,
|
|
|
|
|
**kwargs):
|
|
|
|
|
# 标记是否需要清理缓存
|
|
|
|
|
clearcache = False
|
|
|
|
|
|
|
|
|
|
# 跳过 Admin 操作日志(LogEntry)的处理:避免日志保存时触发不必要的逻辑
|
|
|
|
|
if isinstance(instance, LogEntry):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 处理有 "get_full_url" 方法的模型(如 Article 文章模型)
|
|
|
|
|
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)
|
|
|
|
|
# 非阅读量更新:标记需要清理缓存(如文章内容、标题修改)
|
|
|
|
|
if not is_update_views:
|
|
|
|
|
clearcache = True
|
|
|
|
|
|
|
|
|
|
# 处理 Comment 评论模型的保存逻辑
|
|
|
|
|
if isinstance(instance, Comment):
|
|
|
|
|
# 仅处理已启用的评论(is_enable=True)
|
|
|
|
|
if instance.is_enable:
|
|
|
|
|
# 获取评论所属文章的访问路径
|
|
|
|
|
path = instance.article.get_absolute_url()
|
|
|
|
|
# 获取当前站点域名(处理端口号,仅保留域名部分)
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
if site.find(':') > 0:
|
|
|
|
|
site = site[0:site.find(':')]
|
|
|
|
|
|
|
|
|
|
# 清理文章详情页的视图缓存:避免显示旧评论
|
|
|
|
|
expire_view_cache(
|
|
|
|
|
path,
|
|
|
|
|
servername=site,
|
|
|
|
|
serverport=80,
|
|
|
|
|
key_prefix='blogdetail')
|
|
|
|
|
# 清理 SEO 处理器缓存:评论变化可能影响页面 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 信号接收器:同时监听用户登录(user_logged_in)和登出(user_logged_out)信号
|
|
|
|
|
@receiver(user_logged_in)
|
|
|
|
|
@receiver(user_logged_out)
|
|
|
|
|
def user_auth_callback(sender, request, user, **kwargs):
|
|
|
|
|
# 若用户存在且用户名有效(排除异常情况)
|
|
|
|
|
if user and user.username:
|
|
|
|
|
# 记录用户登录/登出日志
|
|
|
|
|
logger.info(user)
|
|
|
|
|
# 清理侧边栏缓存:登录状态变化可能影响侧边栏(如显示/隐藏用户菜单)
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
# cache.clear()
|
|
|
|
|
# cache.clear() # 注释:若需全局清缓存可启用,当前仅清理侧边栏缓存
|