233 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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/<int:book_id>', methods=['GET', 'POST'])
 | 
						||
@login_required
 | 
						||
@permission_required('manage_inventory')
 | 
						||
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/<int:book_id>/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)
 |