325 lines
10 KiB
Python
325 lines
10 KiB
Python
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
|
||
from app.models.announcement import Announcement
|
||
from app.models.log import Log
|
||
from app.utils.auth import admin_required
|
||
from flask_login import login_required, current_user
|
||
from datetime import datetime
|
||
from app.models.notification import Notification
|
||
|
||
# 创建蓝图
|
||
announcement_bp = Blueprint('announcement', __name__)
|
||
|
||
|
||
@announcement_bp.route('/list', methods=['GET'])
|
||
def announcement_list():
|
||
"""公告列表页面 - 所有用户可见"""
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = 10
|
||
|
||
# 查询活跃的公告
|
||
query = Announcement.query.filter_by(status=1).order_by(
|
||
Announcement.is_top.desc(),
|
||
Announcement.created_at.desc()
|
||
)
|
||
|
||
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
||
|
||
return render_template('announcement/list.html', pagination=pagination)
|
||
|
||
|
||
@announcement_bp.route('/detail/<int:announcement_id>', methods=['GET'])
|
||
def announcement_detail(announcement_id):
|
||
"""公告详情页面"""
|
||
announcement = Announcement.get_announcement_by_id(announcement_id)
|
||
|
||
if not announcement or announcement.status == 0:
|
||
flash('公告不存在或已被删除', 'error')
|
||
return redirect(url_for('announcement.announcement_list'))
|
||
|
||
return render_template('announcement/detail.html', announcement=announcement)
|
||
|
||
|
||
@announcement_bp.route('/manage', methods=['GET'])
|
||
@login_required
|
||
@admin_required
|
||
def manage_announcements():
|
||
"""管理员公告管理页面"""
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = 10
|
||
search = request.args.get('search', '')
|
||
status = request.args.get('status', type=int)
|
||
|
||
# 构建查询
|
||
query = Announcement.query
|
||
|
||
# 搜索过滤
|
||
if search:
|
||
query = query.filter(Announcement.title.like(f'%{search}%'))
|
||
|
||
# 状态过滤
|
||
if status is not None:
|
||
query = query.filter(Announcement.status == status)
|
||
|
||
# 排序
|
||
query = query.order_by(
|
||
Announcement.is_top.desc(),
|
||
Announcement.created_at.desc()
|
||
)
|
||
|
||
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
|
||
|
||
# 记录访问日志
|
||
Log.add_log(
|
||
action="访问公告管理",
|
||
user_id=current_user.id,
|
||
ip_address=request.remote_addr,
|
||
description=f"管理员 {current_user.username} 访问公告管理页面"
|
||
)
|
||
|
||
return render_template(
|
||
'announcement/manage.html',
|
||
pagination=pagination,
|
||
search=search,
|
||
status=status
|
||
)
|
||
|
||
|
||
@announcement_bp.route('/add', methods=['GET', 'POST'])
|
||
@login_required
|
||
@admin_required
|
||
def add_announcement():
|
||
"""添加公告"""
|
||
if request.method == 'POST':
|
||
title = request.form.get('title')
|
||
content = request.form.get('content')
|
||
is_top = request.form.get('is_top') == 'on'
|
||
|
||
if not title or not content:
|
||
flash('标题和内容不能为空', 'error')
|
||
return render_template('announcement/add.html')
|
||
|
||
success, result = Announcement.create_announcement(
|
||
title=title,
|
||
content=content,
|
||
publisher_id=current_user.id,
|
||
is_top=is_top
|
||
)
|
||
|
||
if success:
|
||
# 记录操作日志
|
||
Log.add_log(
|
||
action="添加公告",
|
||
user_id=current_user.id,
|
||
target_type="公告",
|
||
target_id=result.id,
|
||
ip_address=request.remote_addr,
|
||
description=f"管理员 {current_user.username} 添加了新公告: {title}"
|
||
)
|
||
|
||
flash('公告发布成功', 'success')
|
||
return redirect(url_for('announcement.manage_announcements'))
|
||
else:
|
||
flash(f'公告发布失败: {result}', 'error')
|
||
return render_template('announcement/add.html')
|
||
|
||
return render_template('announcement/add.html')
|
||
|
||
|
||
@announcement_bp.route('/edit/<int:announcement_id>', methods=['GET', 'POST'])
|
||
@login_required
|
||
@admin_required
|
||
def edit_announcement(announcement_id):
|
||
"""编辑公告"""
|
||
announcement = Announcement.get_announcement_by_id(announcement_id)
|
||
|
||
if not announcement:
|
||
flash('公告不存在', 'error')
|
||
return redirect(url_for('announcement.manage_announcements'))
|
||
|
||
if request.method == 'POST':
|
||
title = request.form.get('title')
|
||
content = request.form.get('content')
|
||
is_top = request.form.get('is_top') == 'on'
|
||
|
||
if not title or not content:
|
||
flash('标题和内容不能为空', 'error')
|
||
return render_template('announcement/edit.html', announcement=announcement)
|
||
|
||
success, result = Announcement.update_announcement(
|
||
announcement_id=announcement_id,
|
||
title=title,
|
||
content=content,
|
||
is_top=is_top
|
||
)
|
||
|
||
if success:
|
||
# 记录操作日志
|
||
Log.add_log(
|
||
action="编辑公告",
|
||
user_id=current_user.id,
|
||
target_type="公告",
|
||
target_id=announcement_id,
|
||
ip_address=request.remote_addr,
|
||
description=f"管理员 {current_user.username} 编辑了公告: {title}"
|
||
)
|
||
|
||
flash('公告更新成功', 'success')
|
||
return redirect(url_for('announcement.manage_announcements'))
|
||
else:
|
||
flash(f'公告更新失败: {result}', 'error')
|
||
return render_template('announcement/edit.html', announcement=announcement)
|
||
|
||
return render_template('announcement/edit.html', announcement=announcement)
|
||
|
||
|
||
@announcement_bp.route('/status/<int:announcement_id>', methods=['POST'])
|
||
@login_required
|
||
@admin_required
|
||
def change_status(announcement_id):
|
||
"""更改公告状态"""
|
||
data = request.get_json()
|
||
status = data.get('status')
|
||
|
||
if status is None or status not in [0, 1]:
|
||
return jsonify({'success': False, 'message': '无效的状态值'})
|
||
|
||
# 查询公告获取标题(用于日志)
|
||
announcement = Announcement.get_announcement_by_id(announcement_id)
|
||
if not announcement:
|
||
return jsonify({'success': False, 'message': '公告不存在'})
|
||
|
||
success, message = Announcement.change_status(announcement_id, status)
|
||
|
||
if success:
|
||
# 记录状态变更日志
|
||
status_text = "发布" if status == 1 else "撤销"
|
||
Log.add_log(
|
||
action=f"公告{status_text}",
|
||
user_id=current_user.id,
|
||
target_type="公告",
|
||
target_id=announcement_id,
|
||
ip_address=request.remote_addr,
|
||
description=f"管理员 {current_user.username} {status_text}公告: {announcement.title}"
|
||
)
|
||
|
||
return jsonify({'success': True, 'message': f'公告已{status_text}'})
|
||
else:
|
||
return jsonify({'success': False, 'message': message})
|
||
|
||
|
||
@announcement_bp.route('/top/<int:announcement_id>', methods=['POST'])
|
||
@login_required
|
||
@admin_required
|
||
def change_top_status(announcement_id):
|
||
"""更改公告置顶状态"""
|
||
data = request.get_json()
|
||
is_top = data.get('is_top')
|
||
|
||
if is_top is None:
|
||
return jsonify({'success': False, 'message': '无效的置顶状态'})
|
||
|
||
# 查询公告获取标题(用于日志)
|
||
announcement = Announcement.get_announcement_by_id(announcement_id)
|
||
if not announcement:
|
||
return jsonify({'success': False, 'message': '公告不存在'})
|
||
|
||
success, message = Announcement.change_top_status(announcement_id, is_top)
|
||
|
||
if success:
|
||
# 记录置顶状态变更日志
|
||
action_text = "置顶" if is_top else "取消置顶"
|
||
Log.add_log(
|
||
action=f"公告{action_text}",
|
||
user_id=current_user.id,
|
||
target_type="公告",
|
||
target_id=announcement_id,
|
||
ip_address=request.remote_addr,
|
||
description=f"管理员 {current_user.username} {action_text}公告: {announcement.title}"
|
||
)
|
||
|
||
return jsonify({'success': True, 'message': f'公告已{action_text}'})
|
||
else:
|
||
return jsonify({'success': False, 'message': message})
|
||
|
||
|
||
@announcement_bp.route('/latest', methods=['GET'])
|
||
def get_latest_announcements():
|
||
"""获取最新公告列表,用于首页和API"""
|
||
limit = request.args.get('limit', 5, type=int)
|
||
announcements = Announcement.get_active_announcements(limit=limit)
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'announcements': [announcement.to_dict() for announcement in announcements]
|
||
})
|
||
|
||
|
||
@announcement_bp.route('/notifications')
|
||
@login_required
|
||
def user_notifications():
|
||
"""用户个人通知列表页面"""
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = 10
|
||
unread_only = request.args.get('unread_only') == '1'
|
||
|
||
pagination = Notification.get_user_notifications(
|
||
user_id=current_user.id,
|
||
page=page,
|
||
per_page=per_page,
|
||
unread_only=unread_only
|
||
)
|
||
|
||
return render_template(
|
||
'announcement/notifications.html',
|
||
pagination=pagination,
|
||
unread_only=unread_only
|
||
)
|
||
|
||
|
||
@announcement_bp.route('/notification/<int:notification_id>')
|
||
@login_required
|
||
def view_notification(notification_id):
|
||
"""查看单条通知"""
|
||
notification = Notification.query.get_or_404(notification_id)
|
||
|
||
# 检查权限 - 只能查看自己的通知
|
||
if notification.user_id != current_user.id:
|
||
flash('您无权查看此通知', 'error')
|
||
return redirect(url_for('announcement.user_notifications'))
|
||
|
||
# 标记为已读
|
||
if notification.status == 0:
|
||
Notification.mark_as_read(notification_id, current_user.id)
|
||
|
||
# 如果是借阅类型的通知,可能需要跳转到相关页面
|
||
if notification.type == 'borrow' and 'borrow_id' in notification.content:
|
||
# 这里可以解析content获取borrow_id然后重定向
|
||
pass
|
||
|
||
return render_template('announcement/notification_detail.html', notification=notification)
|
||
|
||
|
||
@announcement_bp.route('/notifications/mark-all-read')
|
||
@login_required
|
||
def mark_all_as_read():
|
||
"""标记所有通知为已读"""
|
||
try:
|
||
# 获取所有未读通知
|
||
unread_notifications = Notification.query.filter_by(
|
||
user_id=current_user.id,
|
||
status=0
|
||
).all()
|
||
|
||
# 标记为已读
|
||
for notification in unread_notifications:
|
||
notification.status = 1
|
||
notification.read_at = datetime.now()
|
||
|
||
db.session.commit()
|
||
flash('所有通知已标记为已读', 'success')
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
flash(f'操作失败: {str(e)}', 'error')
|
||
|
||
return redirect(url_for('announcement.user_notifications'))
|