feat: add email notice for evaluation
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils import timezone
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -75,6 +77,19 @@ LABEL_MAP = {
|
||||
'qq': 'QQ',
|
||||
'email': '邮箱',
|
||||
'grader_major': '年级专业',
|
||||
|
||||
# 考评相关
|
||||
'personnel': '人员姓名',
|
||||
'grade': '年级',
|
||||
'item_description': '加/扣分事项说明',
|
||||
'bonus_score': '加分数值',
|
||||
'deduction_score': '扣分数值',
|
||||
'total_score': '总分',
|
||||
'evaluation_date': '考评日期',
|
||||
'remarks': '备注',
|
||||
'count': '记录数量',
|
||||
'records_count': '记录数量',
|
||||
'personnel_count': '人员数量',
|
||||
}
|
||||
|
||||
# 这些key为元信息,不参与详情表格展示
|
||||
@@ -126,6 +141,21 @@ def _build_data_items(instance_data: dict):
|
||||
class EmailNotificationService:
|
||||
"""邮件通知服务"""
|
||||
|
||||
@staticmethod
|
||||
def _send_notification_async(operation_type, model_name, instance_data, user_info=None):
|
||||
"""异步发送邮件通知的内部方法"""
|
||||
def send_email_task():
|
||||
try:
|
||||
EmailNotificationService.send_operation_notification(
|
||||
operation_type, model_name, instance_data, user_info
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"异步发送邮件通知失败: {e}")
|
||||
|
||||
# 使用线程异步发送
|
||||
thread = threading.Thread(target=send_email_task, daemon=True)
|
||||
thread.start()
|
||||
|
||||
@staticmethod
|
||||
def get_notification_settings():
|
||||
"""获取通知设置"""
|
||||
@@ -229,7 +259,7 @@ class EmailNotificationService:
|
||||
|
||||
@staticmethod
|
||||
def send_item_operation_notification(operation_type, item_instance, user_info=None):
|
||||
"""发送物品操作通知"""
|
||||
"""异步发送物品操作通知"""
|
||||
try:
|
||||
instance_data = {
|
||||
'id': getattr(item_instance, 'id', None),
|
||||
@@ -241,15 +271,15 @@ class EmailNotificationService:
|
||||
'timestamp': str(getattr(item_instance, 'updated_at', '')),
|
||||
}
|
||||
|
||||
EmailNotificationService.send_operation_notification(
|
||||
EmailNotificationService._send_notification_async(
|
||||
operation_type, '物品', instance_data, user_info
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"发送物品操作通知失败: {e}")
|
||||
logger.error(f"准备物品操作通知失败: {e}")
|
||||
|
||||
@staticmethod
|
||||
def send_finance_operation_notification(operation_type, finance_instance, user_info=None):
|
||||
"""发送财务记录操作通知"""
|
||||
"""异步发送财务记录操作通知"""
|
||||
try:
|
||||
instance_data = {
|
||||
'id': getattr(finance_instance, 'id', None),
|
||||
@@ -262,11 +292,48 @@ class EmailNotificationService:
|
||||
'timestamp': str(getattr(finance_instance, 'transaction_date', '')),
|
||||
}
|
||||
|
||||
EmailNotificationService.send_operation_notification(
|
||||
EmailNotificationService._send_notification_async(
|
||||
operation_type, '财务记录', instance_data, user_info
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"发送财务记录操作通知失败: {e}")
|
||||
logger.error(f"准备财务记录操作通知失败: {e}")
|
||||
|
||||
@staticmethod
|
||||
def send_evaluation_operation_notification(operation_type, evaluation_instance=None, user_info=None, operation_description=None):
|
||||
"""异步发送考评操作通知"""
|
||||
try:
|
||||
if evaluation_instance:
|
||||
# 单个考评记录的操作通知
|
||||
instance_data = {
|
||||
'id': getattr(evaluation_instance, 'id', None),
|
||||
'personnel': getattr(evaluation_instance, 'personnel', ''),
|
||||
'department': str(getattr(evaluation_instance, 'department', '')),
|
||||
'grade': getattr(evaluation_instance, 'grade', ''),
|
||||
'item_description': getattr(evaluation_instance, 'item_description', ''),
|
||||
'bonus_score': str(getattr(evaluation_instance, 'bonus_score', 0)),
|
||||
'deduction_score': str(getattr(evaluation_instance, 'deduction_score', 0)),
|
||||
'total_score': str(getattr(evaluation_instance, 'total_score', 0)),
|
||||
'evaluation_date': str(getattr(evaluation_instance, 'evaluation_date', '')),
|
||||
'remarks': getattr(evaluation_instance, 'remarks', ''),
|
||||
'timestamp': str(getattr(evaluation_instance, 'updated_at', '')),
|
||||
}
|
||||
model_name = '考评记录'
|
||||
else:
|
||||
# 批量操作通知(导入/导出/删除人员)
|
||||
instance_data = {
|
||||
'operation_type': operation_description or operation_type,
|
||||
'timestamp': str(timezone.now()),
|
||||
}
|
||||
model_name = '考评人员'
|
||||
|
||||
if operation_description:
|
||||
instance_data['operation_type'] = operation_description
|
||||
|
||||
EmailNotificationService._send_notification_async(
|
||||
operation_type, model_name, instance_data, user_info
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"准备考评操作通知失败: {e}")
|
||||
|
||||
@staticmethod
|
||||
def update_notification_emails(emails_data):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import csv
|
||||
import io
|
||||
import logging
|
||||
from datetime import datetime, date, timedelta
|
||||
from decimal import Decimal
|
||||
import os
|
||||
@@ -20,11 +21,14 @@ from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, Alignment, PatternFill
|
||||
|
||||
from finance.models import Department
|
||||
from email_notice.services import EmailNotificationService
|
||||
|
||||
from .filters import EvaluationRecordFilter
|
||||
from .models import EvaluationRecord
|
||||
from .serializers import EvaluationRecordSerializer, PersonnelSummarySerializer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EvaluationRecordViewSet(viewsets.ModelViewSet):
|
||||
"""考评记录视图集"""
|
||||
@@ -37,6 +41,43 @@ class EvaluationRecordViewSet(viewsets.ModelViewSet):
|
||||
ordering_fields = ['evaluation_date', 'created_at', 'total_score', 'bonus_score', 'deduction_score']
|
||||
ordering = ['-evaluation_date', '-created_at']
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""创建考评记录时发送邮箱通知"""
|
||||
instance = serializer.save()
|
||||
|
||||
# 异步发送邮箱通知
|
||||
user_info = getattr(self.request.user, 'username', '系统') if hasattr(self.request, 'user') else '系统'
|
||||
EmailNotificationService.send_evaluation_operation_notification(
|
||||
'CREATE',
|
||||
evaluation_instance=instance,
|
||||
user_info=user_info
|
||||
)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""更新考评记录时发送邮箱通知"""
|
||||
instance = serializer.save()
|
||||
|
||||
# 异步发送邮箱通知
|
||||
user_info = getattr(self.request.user, 'username', '系统') if hasattr(self.request, 'user') else '系统'
|
||||
EmailNotificationService.send_evaluation_operation_notification(
|
||||
'UPDATE',
|
||||
evaluation_instance=instance,
|
||||
user_info=user_info
|
||||
)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""删除考评记录时发送邮箱通知"""
|
||||
# 异步发送邮箱通知
|
||||
user_info = getattr(self.request.user, 'username', '系统') if hasattr(self.request, 'user') else '系统'
|
||||
EmailNotificationService.send_evaluation_operation_notification(
|
||||
'DELETE',
|
||||
evaluation_instance=instance,
|
||||
user_info=user_info
|
||||
)
|
||||
|
||||
# 执行删除
|
||||
instance.delete()
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='personnel-summary')
|
||||
def personnel_summary(self, request, *args, **kwargs):
|
||||
"""获取人员汇总列表"""
|
||||
@@ -98,13 +139,32 @@ class EvaluationRecordViewSet(viewsets.ModelViewSet):
|
||||
# 查找要删除的记录
|
||||
queryset = self.get_queryset().filter(**filter_params)
|
||||
deleted_count = queryset.count()
|
||||
|
||||
|
||||
if deleted_count == 0:
|
||||
return Response({'detail': '未找到要删除的记录'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
# 收集删除的记录信息用于通知
|
||||
deleted_info = {
|
||||
'personnel': personnel_name,
|
||||
'department': department_name if department_name else '未指定',
|
||||
'grade': grade if grade else '未指定',
|
||||
'count': deleted_count
|
||||
}
|
||||
|
||||
# 删除记录
|
||||
queryset.delete()
|
||||
|
||||
|
||||
# 异步发送邮箱通知
|
||||
user_info = getattr(request.user, 'username', '系统') if hasattr(request, 'user') else '系统'
|
||||
operation_description = f"删除人员考评记录 - {personnel_name}({deleted_info['department']}-{deleted_info['grade']}),共{deleted_count}条记录"
|
||||
|
||||
EmailNotificationService.send_evaluation_operation_notification(
|
||||
'DELETE',
|
||||
evaluation_instance=None,
|
||||
user_info=user_info,
|
||||
operation_description=operation_description
|
||||
)
|
||||
|
||||
return Response({
|
||||
'detail': f'成功删除 {deleted_count} 条记录',
|
||||
'deleted_count': deleted_count
|
||||
@@ -204,6 +264,18 @@ class EvaluationRecordViewSet(viewsets.ModelViewSet):
|
||||
output.seek(0)
|
||||
|
||||
filename = f'人员考评记录_{timezone.now().strftime("%Y%m%d_%H%M%S")}.xlsx'
|
||||
|
||||
# 异步发送邮箱通知
|
||||
user_info = getattr(request.user, 'username', '系统') if hasattr(request, 'user') else '系统'
|
||||
operation_description = f"导出考评记录 - {len(personnel_groups)}名人员,共{queryset.count()}条记录"
|
||||
|
||||
EmailNotificationService.send_evaluation_operation_notification(
|
||||
'CREATE',
|
||||
evaluation_instance=None,
|
||||
user_info=user_info,
|
||||
operation_description=operation_description
|
||||
)
|
||||
|
||||
response = HttpResponse(
|
||||
output.getvalue(),
|
||||
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
@@ -504,6 +576,21 @@ class EvaluationRecordViewSet(viewsets.ModelViewSet):
|
||||
EvaluationRecord.objects.all().delete()
|
||||
EvaluationRecord.objects.bulk_create(records_to_create)
|
||||
|
||||
# 异步发送邮箱通知
|
||||
user_info = getattr(request.user, 'username', '系统') if hasattr(request, 'user') else '系统'
|
||||
operation_description = f"导入考评人员 - {len(records_to_create)}条记录"
|
||||
if is_template_format:
|
||||
operation_description += "(样表格式)"
|
||||
else:
|
||||
operation_description += "(完整格式)"
|
||||
|
||||
EmailNotificationService.send_evaluation_operation_notification(
|
||||
'CREATE',
|
||||
evaluation_instance=None,
|
||||
user_info=user_info,
|
||||
operation_description=operation_description
|
||||
)
|
||||
|
||||
skip_msg = f',跳过 {skipped_count} 条已存在的记录' if skipped_count > 0 else ''
|
||||
return Response({
|
||||
'detail': f'成功导入 {len(records_to_create)} 条记录{skip_msg}'
|
||||
|
||||
Reference in New Issue
Block a user