#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 通知系統 API 路由 Author: PANJIT IT Team Created: 2024-01-28 Modified: 2024-01-28 """ from flask import Blueprint, jsonify, request, g from flask_jwt_extended import jwt_required, get_jwt_identity from sqlalchemy import desc, and_, or_ from datetime import datetime, timedelta from app import db from app.models import Notification, NotificationType, User from app.utils.response import create_taiwan_response # 移除不需要的導入 # 建立藍圖 notification_bp = Blueprint('notification', __name__, url_prefix='/notifications') @notification_bp.route('', methods=['GET']) @jwt_required() def get_notifications(): """獲取當前用戶的通知列表""" try: # 獲取當前用戶 current_user_id = get_jwt_identity() # 獲取查詢參數 page = request.args.get('page', 1, type=int) per_page = min(request.args.get('per_page', 20, type=int), 100) status_filter = request.args.get('status', 'all') type_filter = request.args.get('type', None) # 建構查詢 query = Notification.query.filter_by(user_id=current_user_id) # 只顯示未過期的通知 query = query.filter(or_( Notification.expires_at.is_(None), Notification.expires_at > datetime.now() )) # 過濾狀態 if status_filter == 'unread': query = query.filter_by(is_read=False) elif status_filter == 'read': query = query.filter_by(is_read=True) # 過濾類型 if type_filter: query = query.filter_by(type=type_filter) # 排序 - 未讀在前,然後按時間排序 query = query.order_by(Notification.is_read.asc(), desc(Notification.created_at)) # 分頁 paginated = query.paginate( page=page, per_page=per_page, error_out=False ) # 獲取未讀數量 unread_count = Notification.query.filter_by( user_id=current_user_id, is_read=False ).filter(or_( Notification.expires_at.is_(None), Notification.expires_at > datetime.now() )).count() return jsonify(create_taiwan_response( success=True, data={ 'notifications': [n.to_dict() for n in paginated.items], 'pagination': { 'total': paginated.total, 'page': page, 'per_page': per_page, 'pages': paginated.pages }, 'unread_count': unread_count }, message='獲取通知列表成功' )) except Exception as e: return jsonify(create_taiwan_response( success=False, error=f'獲取通知失敗:{str(e)}' )), 500 @notification_bp.route('/', methods=['GET']) @jwt_required() def get_notification(notification_id): """獲取單個通知詳情""" try: current_user_id = get_jwt_identity() # 查找通知 notification = Notification.query.filter_by( notification_uuid=notification_id, user_id=current_user_id ).first() if not notification: return jsonify(create_taiwan_response( success=False, error='通知不存在' )), 404 # 自動標記為已讀 if not notification.is_read: notification.mark_as_read() db.session.commit() return jsonify(create_taiwan_response( success=True, data=notification.to_dict(), message='獲取通知成功' )) except Exception as e: return jsonify(create_taiwan_response( success=False, error=f'獲取通知失敗:{str(e)}' )), 500 @notification_bp.route('//read', methods=['POST']) @jwt_required() def mark_notification_read(notification_id): """標記通知為已讀""" try: current_user_id = get_jwt_identity() # 查找通知 notification = Notification.query.filter_by( notification_uuid=notification_id, user_id=current_user_id ).first() if not notification: return jsonify(create_taiwan_response( success=False, error='通知不存在' )), 404 # 標記為已讀 notification.mark_as_read() db.session.commit() return jsonify(create_taiwan_response( success=True, message='標記已讀成功' )) except Exception as e: return jsonify(create_taiwan_response( success=False, error=f'標記已讀失敗:{str(e)}' )), 500 @notification_bp.route('/read-all', methods=['POST']) @jwt_required() def mark_all_read(): """標記所有通知為已讀""" try: current_user_id = get_jwt_identity() # 取得所有未讀通知 unread_notifications = Notification.query.filter_by( user_id=current_user_id, is_read=False ).filter(or_( Notification.expires_at.is_(None), Notification.expires_at > datetime.now() )).all() # 標記為已讀 for notification in unread_notifications: notification.mark_as_read() db.session.commit() return jsonify(create_taiwan_response( success=True, data={'marked_count': len(unread_notifications)}, message=f'已標記 {len(unread_notifications)} 個通知為已讀' )) except Exception as e: return jsonify(create_taiwan_response( success=False, error=f'標記全部已讀失敗:{str(e)}' )), 500 @notification_bp.route('/', methods=['DELETE']) @jwt_required() def delete_notification(notification_id): """刪除通知""" try: current_user_id = get_jwt_identity() # 查找通知 notification = Notification.query.filter_by( notification_uuid=notification_id, user_id=current_user_id ).first() if not notification: return jsonify(create_taiwan_response( success=False, error='通知不存在' )), 404 # 刪除通知 db.session.delete(notification) db.session.commit() return jsonify(create_taiwan_response( success=True, message='刪除通知成功' )) except Exception as e: db.session.rollback() return jsonify(create_taiwan_response( success=False, error=f'刪除通知失敗:{str(e)}' )), 500 @notification_bp.route('/clear', methods=['POST']) @jwt_required() def clear_read_notifications(): """清空所有已讀通知""" try: current_user_id = get_jwt_identity() # 刪除所有已讀通知 deleted_count = Notification.query.filter_by( user_id=current_user_id, is_read=True ).delete() db.session.commit() return jsonify(create_taiwan_response( success=True, data={'deleted_count': deleted_count}, message=f'已清除 {deleted_count} 個已讀通知' )) except Exception as e: db.session.rollback() return jsonify(create_taiwan_response( success=False, error=f'清除通知失敗:{str(e)}' )), 500 @notification_bp.route('/test', methods=['POST']) @jwt_required() def create_test_notification(): """創建測試通知(開發用)""" try: current_user_id = get_jwt_identity() # 創建測試通知 test_notification = create_notification( user_id=current_user_id, title="測試通知", message=f"這是一個測試通知,創建於 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", notification_type=NotificationType.INFO ) return jsonify(create_taiwan_response( success=True, data=test_notification.to_dict(), message='測試通知已創建' )) except Exception as e: return jsonify(create_taiwan_response( success=False, error=f'創建測試通知失敗:{str(e)}' )), 500 # 工具函數:創建通知 def create_notification(user_id, title, message, notification_type=NotificationType.INFO, job_uuid=None, extra_data=None): """ 創建通知的工具函數 Args: user_id: 用戶ID title: 通知標題 message: 通知內容 notification_type: 通知類型 job_uuid: 關聯的任務UUID(可選) extra_data: 額外數據(可選) Returns: Notification: 創建的通知對象 """ try: notification = Notification( user_id=user_id, type=notification_type.value, title=title, message=message, job_uuid=job_uuid, extra_data=extra_data, link=f"/job/{job_uuid}" if job_uuid else None ) db.session.add(notification) db.session.commit() return notification except Exception as e: db.session.rollback() raise e