657 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from flask import Blueprint, request, redirect, url_for, flash, render_template, jsonify
 | 
						||
from flask_login import current_user, login_required
 | 
						||
from app.models.book import Book
 | 
						||
from app.models.borrow import BorrowRecord
 | 
						||
from app.models.inventory import InventoryLog
 | 
						||
from app.models.user import db, User
 | 
						||
from app.models.log import Log  # 导入日志模型
 | 
						||
import datetime
 | 
						||
from app.utils.auth import permission_required  # 修改导入,使用permission_required而不是admin_required
 | 
						||
 | 
						||
# 创建借阅蓝图
 | 
						||
borrow_bp = Blueprint('borrow', __name__, url_prefix='/borrow')
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/book', methods=['POST'])
 | 
						||
@login_required
 | 
						||
def borrow_book():
 | 
						||
    book_id = request.form.get('book_id', type=int)
 | 
						||
    borrow_days = request.form.get('borrow_days', type=int, default=14)
 | 
						||
 | 
						||
    if not book_id:
 | 
						||
        flash('请选择要借阅的图书', 'danger')
 | 
						||
        return redirect(url_for('book.book_list'))
 | 
						||
 | 
						||
    # 检查用户当前借阅数量是否达到上限(5本)
 | 
						||
    current_borrows_count = BorrowRecord.query.filter_by(
 | 
						||
        user_id=current_user.id,
 | 
						||
        status=1  # 1表示借阅中
 | 
						||
    ).count()
 | 
						||
 | 
						||
    if current_borrows_count >= 5:
 | 
						||
        flash('您当前已借阅5本图书,达到借阅上限。请先归还后再借阅新书。', 'warning')
 | 
						||
        return redirect(url_for('book.book_detail', book_id=book_id))
 | 
						||
 | 
						||
    book = Book.query.get_or_404(book_id)
 | 
						||
 | 
						||
    # 检查库存
 | 
						||
    if book.stock <= 0:
 | 
						||
        flash(f'《{book.title}》当前无库存,无法借阅', 'danger')
 | 
						||
        return redirect(url_for('book.book_detail', book_id=book_id))
 | 
						||
 | 
						||
    # 检查当前用户是否已借阅此书
 | 
						||
    existing_borrow = BorrowRecord.query.filter_by(
 | 
						||
        user_id=current_user.id,
 | 
						||
        book_id=book_id,
 | 
						||
        status=1  # 1表示借阅中
 | 
						||
    ).first()
 | 
						||
 | 
						||
    if existing_borrow:
 | 
						||
        flash(f'您已借阅《{book.title}》,请勿重复借阅', 'warning')
 | 
						||
        return redirect(url_for('book.book_detail', book_id=book_id))
 | 
						||
 | 
						||
    try:
 | 
						||
        # 创建借阅记录
 | 
						||
        now = datetime.datetime.now()
 | 
						||
        due_date = now + datetime.timedelta(days=borrow_days)
 | 
						||
 | 
						||
        borrow_record = BorrowRecord(
 | 
						||
            user_id=current_user.id,
 | 
						||
            book_id=book_id,
 | 
						||
            borrow_date=now,
 | 
						||
            due_date=due_date,
 | 
						||
            status=1,  # 1表示借阅中
 | 
						||
            created_at=now,
 | 
						||
            updated_at=now
 | 
						||
        )
 | 
						||
 | 
						||
        # 更新图书库存
 | 
						||
        book.stock -= 1
 | 
						||
        book.updated_at = now
 | 
						||
 | 
						||
        db.session.add(borrow_record)
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        # 添加库存变更日志
 | 
						||
        inventory_log = InventoryLog(
 | 
						||
            book_id=book_id,
 | 
						||
            change_type='借出',
 | 
						||
            change_amount=-1,
 | 
						||
            after_stock=book.stock,
 | 
						||
            operator_id=current_user.id,
 | 
						||
            remark='用户借书',
 | 
						||
            changed_at=now
 | 
						||
        )
 | 
						||
        db.session.add(inventory_log)
 | 
						||
 | 
						||
        # 添加系统操作日志
 | 
						||
        Log.add_log(
 | 
						||
            action='借阅图书',
 | 
						||
            user_id=current_user.id,
 | 
						||
            target_type='book',
 | 
						||
            target_id=book_id,
 | 
						||
            ip_address=request.remote_addr,
 | 
						||
            description=f'用户借阅图书《{book.title}》,归还日期: {due_date.strftime("%Y-%m-%d")}'
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        flash(f'成功借阅《{book.title}》,请在 {due_date.strftime("%Y-%m-%d")} 前归还', 'success')
 | 
						||
 | 
						||
    except Exception as e:
 | 
						||
        db.session.rollback()
 | 
						||
        flash(f'借阅失败: {str(e)}', 'danger')
 | 
						||
 | 
						||
    return redirect(url_for('book.book_detail', book_id=book_id))
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/add/<int:book_id>', methods=['POST'])
 | 
						||
@login_required
 | 
						||
def add_borrow(book_id):
 | 
						||
    # 验证图书存在
 | 
						||
    book = Book.query.get_or_404(book_id)
 | 
						||
 | 
						||
    # 检查用户当前借阅数量是否达到上限(5本)
 | 
						||
    current_borrows_count = BorrowRecord.query.filter_by(
 | 
						||
        user_id=current_user.id,
 | 
						||
        status=1  # 1表示借阅中
 | 
						||
    ).count()
 | 
						||
 | 
						||
    if current_borrows_count >= 5:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '您当前已借阅5本图书,达到借阅上限。请先归还后再借阅新书。'
 | 
						||
        })
 | 
						||
 | 
						||
    # 默认借阅天数
 | 
						||
    borrow_days = 14
 | 
						||
 | 
						||
    # 检查库存
 | 
						||
    if book.stock <= 0:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': f'《{book.title}》当前无库存,无法借阅'
 | 
						||
        })
 | 
						||
 | 
						||
    # 检查是否已借阅
 | 
						||
    existing_borrow = BorrowRecord.query.filter_by(
 | 
						||
        user_id=current_user.id,
 | 
						||
        book_id=book_id,
 | 
						||
        status=1  # 1表示借阅中
 | 
						||
    ).first()
 | 
						||
 | 
						||
    if existing_borrow:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': f'您已借阅《{book.title}》,请勿重复借阅'
 | 
						||
        })
 | 
						||
 | 
						||
    try:
 | 
						||
        # 创建借阅记录
 | 
						||
        now = datetime.datetime.now()
 | 
						||
        due_date = now + datetime.timedelta(days=borrow_days)
 | 
						||
 | 
						||
        borrow_record = BorrowRecord(
 | 
						||
            user_id=current_user.id,
 | 
						||
            book_id=book_id,
 | 
						||
            borrow_date=now,
 | 
						||
            due_date=due_date,
 | 
						||
            status=1,  # 1表示借阅中
 | 
						||
            created_at=now,
 | 
						||
            updated_at=now
 | 
						||
        )
 | 
						||
 | 
						||
        # 更新图书库存
 | 
						||
        book.stock -= 1
 | 
						||
        book.updated_at = now
 | 
						||
 | 
						||
        db.session.add(borrow_record)
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        # 添加库存变更日志
 | 
						||
        inventory_log = InventoryLog(
 | 
						||
            book_id=book_id,
 | 
						||
            change_type='借出',
 | 
						||
            change_amount=-1,
 | 
						||
            after_stock=book.stock,
 | 
						||
            operator_id=current_user.id,
 | 
						||
            remark='用户借书',
 | 
						||
            changed_at=now
 | 
						||
        )
 | 
						||
        db.session.add(inventory_log)
 | 
						||
 | 
						||
        # 添加系统操作日志
 | 
						||
        Log.add_log(
 | 
						||
            action='借阅图书',
 | 
						||
            user_id=current_user.id,
 | 
						||
            target_type='book',
 | 
						||
            target_id=book_id,
 | 
						||
            ip_address=request.remote_addr,
 | 
						||
            description=f'用户借阅图书《{book.title}》,归还日期: {due_date.strftime("%Y-%m-%d")}'
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        return jsonify({
 | 
						||
            'success': True,
 | 
						||
            'message': f'成功借阅《{book.title}》,请在 {due_date.strftime("%Y-%m-%d")} 前归还'
 | 
						||
        })
 | 
						||
 | 
						||
    except Exception as e:
 | 
						||
        db.session.rollback()
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': f'借阅失败: {str(e)}'
 | 
						||
        })
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/return/<int:borrow_id>', methods=['POST'])
 | 
						||
@login_required
 | 
						||
def return_book(borrow_id):
 | 
						||
    """还书操作"""
 | 
						||
    # 查找借阅记录
 | 
						||
    borrow_record = BorrowRecord.query.get_or_404(borrow_id)
 | 
						||
 | 
						||
    # 检查是否是自己的借阅记录或者是管理员
 | 
						||
    if borrow_record.user_id != current_user.id and not current_user.has_permission('manage_borrows'):
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '您无权执行此操作'
 | 
						||
        })
 | 
						||
 | 
						||
    # 检查是否已还
 | 
						||
    if borrow_record.status != 1:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '此书已归还,请勿重复操作'
 | 
						||
        })
 | 
						||
 | 
						||
    try:
 | 
						||
        book = Book.query.get(borrow_record.book_id)
 | 
						||
        now = datetime.datetime.now()
 | 
						||
 | 
						||
        # 更新借阅记录
 | 
						||
        borrow_record.status = 0  # 0表示已归还
 | 
						||
        borrow_record.return_date = now
 | 
						||
        borrow_record.updated_at = now
 | 
						||
 | 
						||
        # 更新图书库存
 | 
						||
        book.stock += 1
 | 
						||
        book.updated_at = now
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        # 添加库存变更日志
 | 
						||
        inventory_log = InventoryLog(
 | 
						||
            book_id=borrow_record.book_id,
 | 
						||
            change_type='归还',
 | 
						||
            change_amount=1,
 | 
						||
            after_stock=book.stock,
 | 
						||
            operator_id=current_user.id,
 | 
						||
            remark='用户还书',
 | 
						||
            changed_at=now
 | 
						||
        )
 | 
						||
        db.session.add(inventory_log)
 | 
						||
 | 
						||
        # 添加系统操作日志
 | 
						||
        # 判断是否逾期归还
 | 
						||
        is_overdue = now > borrow_record.due_date
 | 
						||
        overdue_msg = '(逾期归还)' if is_overdue else ''
 | 
						||
 | 
						||
        Log.add_log(
 | 
						||
            action='归还图书',
 | 
						||
            user_id=current_user.id,
 | 
						||
            target_type='book',
 | 
						||
            target_id=borrow_record.book_id,
 | 
						||
            ip_address=request.remote_addr,
 | 
						||
            description=f'用户归还图书《{book.title}》{overdue_msg}'
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        return jsonify({
 | 
						||
            'success': True,
 | 
						||
            'message': f'成功归还《{book.title}》'
 | 
						||
        })
 | 
						||
 | 
						||
    except Exception as e:
 | 
						||
        db.session.rollback()
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': f'归还失败: {str(e)}'
 | 
						||
        })
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/renew/<int:borrow_id>', methods=['POST'])
 | 
						||
@login_required
 | 
						||
def renew_book(borrow_id):
 | 
						||
    """续借操作"""
 | 
						||
    # 查找借阅记录
 | 
						||
    borrow_record = BorrowRecord.query.get_or_404(borrow_id)
 | 
						||
 | 
						||
    # 检查是否是自己的借阅记录或者是管理员
 | 
						||
    if borrow_record.user_id != current_user.id and not current_user.has_permission('manage_borrows'):
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '您无权执行此操作'
 | 
						||
        })
 | 
						||
 | 
						||
    # 检查是否已还
 | 
						||
    if borrow_record.status != 1:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '此书已归还,无法续借'
 | 
						||
        })
 | 
						||
 | 
						||
    # 检查续借次数限制(最多续借2次)
 | 
						||
    if borrow_record.renew_count >= 2:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '此书已达到最大续借次数,无法继续续借'
 | 
						||
        })
 | 
						||
 | 
						||
    try:
 | 
						||
        now = datetime.datetime.now()
 | 
						||
        book = Book.query.get(borrow_record.book_id)
 | 
						||
 | 
						||
        # 检查是否已逾期
 | 
						||
        if now > borrow_record.due_date:
 | 
						||
            return jsonify({
 | 
						||
                'success': False,
 | 
						||
                'message': '此书已逾期,请先归还并处理逾期情况'
 | 
						||
            })
 | 
						||
 | 
						||
        # 续借14天
 | 
						||
        new_due_date = borrow_record.due_date + datetime.timedelta(days=14)
 | 
						||
 | 
						||
        # 更新借阅记录
 | 
						||
        borrow_record.due_date = new_due_date
 | 
						||
        borrow_record.renew_count += 1
 | 
						||
        borrow_record.updated_at = now
 | 
						||
 | 
						||
        # 添加系统操作日志
 | 
						||
        Log.add_log(
 | 
						||
            action='续借图书',
 | 
						||
            user_id=current_user.id,
 | 
						||
            target_type='book',
 | 
						||
            target_id=borrow_record.book_id,
 | 
						||
            ip_address=request.remote_addr,
 | 
						||
            description=f'用户续借图书《{book.title}》,新归还日期: {new_due_date.strftime("%Y-%m-%d")}'
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        return jsonify({
 | 
						||
            'success': True,
 | 
						||
            'message': f'续借成功,新的归还日期为 {new_due_date.strftime("%Y-%m-%d")}'
 | 
						||
        })
 | 
						||
 | 
						||
    except Exception as e:
 | 
						||
        db.session.rollback()
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': f'续借失败: {str(e)}'
 | 
						||
        })
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/my_borrows')
 | 
						||
@login_required
 | 
						||
def my_borrows():
 | 
						||
    """用户查看自己的借阅记录"""
 | 
						||
    page = request.args.get('page', 1, type=int)
 | 
						||
    status = request.args.get('status', default=None, type=int)
 | 
						||
 | 
						||
    # 构建查询
 | 
						||
    query = BorrowRecord.query.filter_by(user_id=current_user.id)
 | 
						||
 | 
						||
    # 根据状态筛选
 | 
						||
    if status is not None:
 | 
						||
        query = query.filter_by(status=status)
 | 
						||
 | 
						||
    # 按借阅日期倒序排列
 | 
						||
    query = query.order_by(BorrowRecord.borrow_date.desc())
 | 
						||
 | 
						||
    # 分页
 | 
						||
    pagination = query.paginate(page=page, per_page=10, error_out=False)
 | 
						||
 | 
						||
    # 获取当前借阅数量和历史借阅数量(用于标签显示)
 | 
						||
    current_borrows_count = BorrowRecord.query.filter_by(user_id=current_user.id, status=1).count()
 | 
						||
    history_borrows_count = BorrowRecord.query.filter_by(user_id=current_user.id, status=0).count()
 | 
						||
 | 
						||
    # 记录日志 - 用户查看借阅记录
 | 
						||
    Log.add_log(
 | 
						||
        action='查看借阅记录',
 | 
						||
        user_id=current_user.id,
 | 
						||
        ip_address=request.remote_addr,
 | 
						||
        description='用户查看个人借阅记录'
 | 
						||
    )
 | 
						||
 | 
						||
    return render_template(
 | 
						||
        'borrow/my_borrows.html',
 | 
						||
        pagination=pagination,
 | 
						||
        current_borrows_count=current_borrows_count,
 | 
						||
        history_borrows_count=history_borrows_count,
 | 
						||
        status=status,
 | 
						||
        now=datetime.datetime.now()  # 添加当前时间变量
 | 
						||
    )
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/manage')
 | 
						||
@login_required
 | 
						||
@permission_required('manage_borrows')  # 替代 @admin_required
 | 
						||
def manage_borrows():
 | 
						||
    """管理员查看所有借阅记录"""
 | 
						||
    page = request.args.get('page', 1, type=int)
 | 
						||
    status = request.args.get('status', default=None, type=int)
 | 
						||
    user_id = request.args.get('user_id', default=None, type=int)
 | 
						||
    book_id = request.args.get('book_id', default=None, type=int)
 | 
						||
    search = request.args.get('search', default='')
 | 
						||
 | 
						||
    # 构建查询
 | 
						||
    query = BorrowRecord.query
 | 
						||
 | 
						||
    # 根据状态筛选
 | 
						||
    if status is not None:
 | 
						||
        query = query.filter_by(status=status)
 | 
						||
 | 
						||
    # 根据用户筛选
 | 
						||
    if user_id:
 | 
						||
        query = query.filter_by(user_id=user_id)
 | 
						||
 | 
						||
    # 根据图书筛选
 | 
						||
    if book_id:
 | 
						||
        query = query.filter_by(book_id=book_id)
 | 
						||
 | 
						||
    # 根据搜索条件筛选(用户名或图书名)
 | 
						||
    if search:
 | 
						||
        query = query.join(User, BorrowRecord.user_id == User.id) \
 | 
						||
            .join(Book, BorrowRecord.book_id == Book.id) \
 | 
						||
            .filter((User.username.like(f'%{search}%')) |
 | 
						||
                    (Book.title.like(f'%{search}%')))
 | 
						||
 | 
						||
    # 按借阅日期倒序排列
 | 
						||
    query = query.order_by(BorrowRecord.borrow_date.desc())
 | 
						||
 | 
						||
    # 分页
 | 
						||
    pagination = query.paginate(page=page, per_page=10, error_out=False)
 | 
						||
 | 
						||
    # 获取统计数据
 | 
						||
    current_borrows_count = BorrowRecord.query.filter_by(status=1).count()
 | 
						||
    history_borrows_count = BorrowRecord.query.filter_by(status=0).count()
 | 
						||
 | 
						||
    # 获取所有用户(用于筛选)
 | 
						||
    users = User.query.all()
 | 
						||
 | 
						||
    # 记录日志 - 管理员查看借阅记录
 | 
						||
    Log.add_log(
 | 
						||
        action='管理借阅记录',
 | 
						||
        user_id=current_user.id,
 | 
						||
        ip_address=request.remote_addr,
 | 
						||
        description='管理员查看借阅管理页面'
 | 
						||
    )
 | 
						||
 | 
						||
    return render_template(
 | 
						||
        'borrow/borrow_management.html',
 | 
						||
        pagination=pagination,
 | 
						||
        current_borrows_count=current_borrows_count,
 | 
						||
        history_borrows_count=history_borrows_count,
 | 
						||
        status=status,
 | 
						||
        user_id=user_id,
 | 
						||
        book_id=book_id,
 | 
						||
        search=search,
 | 
						||
        users=users,
 | 
						||
        now=datetime.datetime.now()  # 添加当前时间变量
 | 
						||
    )
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/admin/add', methods=['POST'])
 | 
						||
@login_required
 | 
						||
@permission_required('manage_borrows')  # 替代 @admin_required
 | 
						||
def admin_add_borrow():
 | 
						||
    """管理员为用户添加借阅记录"""
 | 
						||
    user_id = request.form.get('user_id', type=int)
 | 
						||
    book_id = request.form.get('book_id', type=int)
 | 
						||
    borrow_days = request.form.get('borrow_days', type=int, default=14)
 | 
						||
 | 
						||
    if not user_id or not book_id:
 | 
						||
        flash('用户ID和图书ID不能为空', 'danger')
 | 
						||
        return redirect(url_for('borrow.manage_borrows'))
 | 
						||
 | 
						||
    # 验证用户和图书是否存在
 | 
						||
    user = User.query.get_or_404(user_id)
 | 
						||
    book = Book.query.get_or_404(book_id)
 | 
						||
 | 
						||
    # 检查库存
 | 
						||
    if book.stock <= 0:
 | 
						||
        flash(f'《{book.title}》当前无库存,无法借阅', 'danger')
 | 
						||
        return redirect(url_for('borrow.manage_borrows'))
 | 
						||
 | 
						||
    # 检查用户是否已借阅此书
 | 
						||
    existing_borrow = BorrowRecord.query.filter_by(
 | 
						||
        user_id=user_id,
 | 
						||
        book_id=book_id,
 | 
						||
        status=1  # 1表示借阅中
 | 
						||
    ).first()
 | 
						||
 | 
						||
    if existing_borrow:
 | 
						||
        flash(f'用户 {user.username} 已借阅《{book.title}》,请勿重复借阅', 'warning')
 | 
						||
        return redirect(url_for('borrow.manage_borrows'))
 | 
						||
 | 
						||
    try:
 | 
						||
        # 创建借阅记录
 | 
						||
        now = datetime.datetime.now()
 | 
						||
        due_date = now + datetime.timedelta(days=borrow_days)
 | 
						||
 | 
						||
        borrow_record = BorrowRecord(
 | 
						||
            user_id=user_id,
 | 
						||
            book_id=book_id,
 | 
						||
            borrow_date=now,
 | 
						||
            due_date=due_date,
 | 
						||
            status=1,  # 1表示借阅中
 | 
						||
            created_at=now,
 | 
						||
            updated_at=now
 | 
						||
        )
 | 
						||
 | 
						||
        # 更新图书库存
 | 
						||
        book.stock -= 1
 | 
						||
        book.updated_at = now
 | 
						||
 | 
						||
        db.session.add(borrow_record)
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        # 添加库存变更日志
 | 
						||
        inventory_log = InventoryLog(
 | 
						||
            book_id=book_id,
 | 
						||
            change_type='借出',
 | 
						||
            change_amount=-1,
 | 
						||
            after_stock=book.stock,
 | 
						||
            operator_id=current_user.id,
 | 
						||
            remark=f'管理员 {current_user.username} 为用户 {user.username} 借书',
 | 
						||
            changed_at=now
 | 
						||
        )
 | 
						||
        db.session.add(inventory_log)
 | 
						||
 | 
						||
        # 添加系统操作日志
 | 
						||
        Log.add_log(
 | 
						||
            action='管理员借阅操作',
 | 
						||
            user_id=current_user.id,
 | 
						||
            target_type='book',
 | 
						||
            target_id=book_id,
 | 
						||
            ip_address=request.remote_addr,
 | 
						||
            description=f'管理员为用户 {user.username} 借阅图书《{book.title}》,归还日期: {due_date.strftime("%Y-%m-%d")}'
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        flash(f'成功为用户 {user.username} 借阅《{book.title}》,归还日期: {due_date.strftime("%Y-%m-%d")}', 'success')
 | 
						||
 | 
						||
    except Exception as e:
 | 
						||
        db.session.rollback()
 | 
						||
        flash(f'借阅失败: {str(e)}', 'danger')
 | 
						||
 | 
						||
    return redirect(url_for('borrow.manage_borrows'))
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/overdue')
 | 
						||
@login_required
 | 
						||
@permission_required('manage_overdue')  # 替代 @admin_required
 | 
						||
def overdue_borrows():
 | 
						||
    """查看逾期借阅"""
 | 
						||
    page = request.args.get('page', 1, type=int)
 | 
						||
    now = datetime.datetime.now()
 | 
						||
 | 
						||
    # 查询所有已逾期且未归还的借阅记录
 | 
						||
    query = BorrowRecord.query.filter(
 | 
						||
        BorrowRecord.status == 1,  # 借阅中
 | 
						||
        BorrowRecord.due_date < now  # 已过期
 | 
						||
    ).order_by(BorrowRecord.due_date)  # 按到期日期排序,最早到期的排在前面
 | 
						||
 | 
						||
    pagination = query.paginate(page=page, per_page=10, error_out=False)
 | 
						||
 | 
						||
    # 计算逾期总数
 | 
						||
    overdue_count = query.count()
 | 
						||
 | 
						||
    # 记录日志 - 管理员查看逾期记录
 | 
						||
    Log.add_log(
 | 
						||
        action='查看逾期记录',
 | 
						||
        user_id=current_user.id,
 | 
						||
        ip_address=request.remote_addr,
 | 
						||
        description='管理员查看逾期借阅记录'
 | 
						||
    )
 | 
						||
 | 
						||
    return render_template(
 | 
						||
        'borrow/overdue.html',
 | 
						||
        pagination=pagination,
 | 
						||
        overdue_count=overdue_count
 | 
						||
    )
 | 
						||
 | 
						||
 | 
						||
@borrow_bp.route('/overdue/notify/<int:borrow_id>', methods=['POST'])
 | 
						||
@login_required
 | 
						||
@permission_required('manage_overdue')  # 替代 @admin_required
 | 
						||
def notify_overdue(borrow_id):
 | 
						||
    """发送逾期通知"""
 | 
						||
    from app.models.notification import Notification
 | 
						||
 | 
						||
    borrow_record = BorrowRecord.query.get_or_404(borrow_id)
 | 
						||
 | 
						||
    # 检查是否已还
 | 
						||
    if borrow_record.status != 1:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '此书已归还,无需发送逾期通知'
 | 
						||
        })
 | 
						||
 | 
						||
    now = datetime.datetime.now()
 | 
						||
 | 
						||
    # 检查是否确实逾期
 | 
						||
    if borrow_record.due_date > now:
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': '此借阅记录尚未逾期'
 | 
						||
        })
 | 
						||
 | 
						||
    try:
 | 
						||
        book = Book.query.get(borrow_record.book_id)
 | 
						||
        user = User.query.get(borrow_record.user_id)
 | 
						||
 | 
						||
        # 创建通知
 | 
						||
        notification = Notification(
 | 
						||
            user_id=borrow_record.user_id,
 | 
						||
            title='图书逾期提醒',
 | 
						||
            content=f'您借阅的《{borrow_record.book.title}》已逾期,请尽快归还。应还日期: {borrow_record.due_date.strftime("%Y-%m-%d")}',
 | 
						||
            type='overdue',
 | 
						||
            sender_id=current_user.id,
 | 
						||
            created_at=now
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.add(notification)
 | 
						||
 | 
						||
        # 更新借阅记录备注
 | 
						||
        borrow_record.remark = f'{borrow_record.remark or ""}[{now.strftime("%Y-%m-%d")} 已发送逾期通知]'
 | 
						||
        borrow_record.updated_at = now
 | 
						||
 | 
						||
        # 添加系统操作日志
 | 
						||
        Log.add_log(
 | 
						||
            action='发送逾期通知',
 | 
						||
            user_id=current_user.id,
 | 
						||
            target_type='notification',
 | 
						||
            target_id=borrow_record.user_id,
 | 
						||
            ip_address=request.remote_addr,
 | 
						||
            description=f'管理员向用户 {user.username} 发送图书《{book.title}》逾期通知'
 | 
						||
        )
 | 
						||
 | 
						||
        db.session.commit()
 | 
						||
 | 
						||
        return jsonify({
 | 
						||
            'success': True,
 | 
						||
            'message': '已成功发送逾期通知'
 | 
						||
        })
 | 
						||
 | 
						||
    except Exception as e:
 | 
						||
        db.session.rollback()
 | 
						||
        return jsonify({
 | 
						||
            'success': False,
 | 
						||
            'message': f'发送通知失败: {str(e)}'
 | 
						||
        })
 |