# app/controllers/inventory.py from flask import Blueprint, render_template, request, jsonify, flash, redirect, url_for from flask_login import login_required, current_user from app.models.book import Book from app.models.inventory import InventoryLog from app.models.log import Log # 导入日志模型 from app.models.user import db from app.utils.auth import permission_required # 修改导入,使用permission_required替代admin_required from datetime import datetime inventory_bp = Blueprint('inventory', __name__, url_prefix='/inventory') @inventory_bp.route('/') @login_required @permission_required('manage_inventory') # 替代 @admin_required def inventory_list(): """库存管理页面 - 只有拥有库存管理权限的用户有权限进入""" page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 20, type=int) # 搜索功能 search = request.args.get('search', '') query = Book.query if search: query = query.filter( (Book.title.contains(search)) | (Book.author.contains(search)) | (Book.isbn.contains(search)) ) # 排序 sort = request.args.get('sort', 'id') order = request.args.get('order', 'asc') if order == 'desc': query = query.order_by(getattr(Book, sort).desc()) else: query = query.order_by(getattr(Book, sort)) pagination = query.paginate(page=page, per_page=per_page) books = pagination.items # 记录系统日志 - 访问库存管理页面 Log.add_log( action="访问库存管理", user_id=current_user.id, target_type="inventory", ip_address=request.remote_addr, description=f"用户访问库存管理页面,搜索条件:{search if search else '无'}" ) return render_template('inventory/list.html', books=books, pagination=pagination, search=search, sort=sort, order=order) @inventory_bp.route('/adjust/', methods=['GET', 'POST']) @login_required @permission_required('manage_inventory') # 替代 @admin_required def adjust_inventory(book_id): """调整图书库存""" book = Book.query.get_or_404(book_id) # GET请求记录日志 if request.method == 'GET': 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}》的库存调整页面" ) if request.method == 'POST': change_type = request.form.get('change_type') change_amount = int(request.form.get('change_amount', 0)) remark = request.form.get('remark', '') if change_amount <= 0: flash('调整数量必须大于0', 'danger') return redirect(url_for('inventory.adjust_inventory', book_id=book_id)) # 计算库存变化 original_stock = book.stock if change_type == 'in': book.stock += change_amount after_stock = book.stock operation_desc = "入库" elif change_type == 'out': if book.stock < change_amount: flash('出库数量不能大于当前库存', 'danger') return redirect(url_for('inventory.adjust_inventory', book_id=book_id)) book.stock -= change_amount after_stock = book.stock operation_desc = "出库" else: flash('无效的操作类型', 'danger') return redirect(url_for('inventory.adjust_inventory', book_id=book_id)) # 创建库存日志 log = InventoryLog( book_id=book.id, change_type=change_type, change_amount=change_amount, after_stock=after_stock, operator_id=current_user.id, remark=remark, changed_at=datetime.now() ) try: db.session.add(log) # 记录系统日志 - 库存调整 Log.add_log( action=f"库存{operation_desc}", user_id=current_user.id, target_type="book", target_id=book.id, ip_address=request.remote_addr, description=f"用户对图书《{book.title}》进行{operation_desc}操作,数量:{change_amount}," f"原库存:{original_stock},现库存:{after_stock},备注:{remark}" ) db.session.commit() flash(f'图书《{book.title}》库存调整成功!原库存:{original_stock},现库存:{after_stock}', 'success') return redirect(url_for('inventory.inventory_list')) except Exception as e: db.session.rollback() flash(f'操作失败:{str(e)}', 'danger') return redirect(url_for('inventory.adjust_inventory', book_id=book_id)) return render_template('inventory/adjust.html', book=book) @inventory_bp.route('/logs') @login_required @permission_required('manage_inventory') # 替代 @admin_required def inventory_logs(): """查看库存变动日志""" page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 20, type=int) # 搜索和筛选 book_id = request.args.get('book_id', type=int) change_type = request.args.get('change_type', '') date_from = request.args.get('date_from', '') date_to = request.args.get('date_to', '') query = InventoryLog.query if book_id: query = query.filter_by(book_id=book_id) if change_type: query = query.filter_by(change_type=change_type) if date_from: query = query.filter(InventoryLog.changed_at >= datetime.strptime(date_from, '%Y-%m-%d')) if date_to: query = query.filter(InventoryLog.changed_at <= datetime.strptime(date_to + ' 23:59:59', '%Y-%m-%d %H:%M:%S')) # 默认按时间倒序 query = query.order_by(InventoryLog.changed_at.desc()) pagination = query.paginate(page=page, per_page=per_page) logs = pagination.items # 获取所有图书用于筛选 books = Book.query.all() # 如果特定 book_id 被指定,也获取该书的详细信息 book = Book.query.get(book_id) if book_id else None # 记录系统日志 - 查看库存日志 filter_desc = [] if book_id: book_title = book.title if book else f"ID:{book_id}" filter_desc.append(f"图书:{book_title}") if change_type: change_type_text = "入库" if change_type == "in" else "出库" filter_desc.append(f"操作类型:{change_type_text}") if date_from or date_to: date_range = f"{date_from or '无限制'} 至 {date_to or '无限制'}" filter_desc.append(f"日期范围:{date_range}") Log.add_log( action="查看库存日志", user_id=current_user.id, target_type="inventory_log", ip_address=request.remote_addr, description=f"用户查看库存变动日志,筛选条件:{', '.join(filter_desc) if filter_desc else '无'}" ) return render_template('inventory/logs.html', logs=logs, pagination=pagination, books=books, book=book, book_id=book_id, change_type=change_type, date_from=date_from, date_to=date_to) @inventory_bp.route('/book//logs') @login_required @permission_required('manage_inventory') # 替代 @admin_required def book_inventory_logs(book_id): """查看特定图书的库存变动日志""" book = Book.query.get_or_404(book_id) page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 20, type=int) logs = InventoryLog.query.filter_by(book_id=book_id) \ .order_by(InventoryLog.changed_at.desc()) \ .paginate(page=page, per_page=per_page) # 记录系统日志 - 查看特定图书的库存日志 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}》的库存变动日志" ) return render_template('inventory/book_logs.html', book=book, logs=logs.items, pagination=logs)