# inventory-backend/app/api/v1/warehouse.py from flask import Blueprint, request, jsonify from flask_jwt_extended import jwt_required from app.extensions import db from app.models.system import SysWarehouseLocation from app.utils.decorators import audit_log warehouse_bp = Blueprint('warehouse', __name__, url_prefix='/api/v1/warehouse') def build_tree(nodes, parent_id=None): """ 将平铺的数据构建为树形结构 """ tree = [] for node in nodes: if node.parent_id == parent_id: children = build_tree(nodes, node.id) node_dict = node.to_dict() if children: node_dict['children'] = children else: node_dict['children'] = [] tree.append(node_dict) return tree @warehouse_bp.route('/tree', methods=['GET']) def get_tree(): """ 获取库位树形结构 """ try: # 查询所有库位 all_locations = SysWarehouseLocation.query.order_by(SysWarehouseLocation.level, SysWarehouseLocation.id).all() # 构建树形结构 tree_data = build_tree(all_locations, parent_id=None) return jsonify({ 'code': 200, 'msg': 'success', 'data': tree_data }) except Exception as e: return jsonify({ 'code': 500, 'msg': str(e), 'data': None }), 500 @warehouse_bp.route('', methods=['POST']) @jwt_required() @audit_log( module='库位管理', action='新增', get_target_name_fn=lambda: request.get_json().get('name') if request.get_json() else None ) def create_location(): """ 创建库位 """ try: data = request.get_json() name = data.get('name', '').strip() parent_id = data.get('parent_id') # None 表示顶级 is_enabled = data.get('is_enabled', True) if not name: return jsonify({'code': 400, 'msg': '库位名称不能为空', 'data': None}) # 计算 level 和 full_path if parent_id is None: level = 0 full_path = name parent_full_path = '' else: parent = SysWarehouseLocation.query.get(parent_id) if not parent: return jsonify({'code': 400, 'msg': '父级库位不存在', 'data': None}) level = parent.level + 1 parent_full_path = parent.full_path or '' full_path = f"{parent_full_path}/{name}" if parent_full_path else name location = SysWarehouseLocation( name=name, parent_id=parent_id, full_path=full_path, level=level, is_enabled=is_enabled ) db.session.add(location) db.session.commit() return jsonify({ 'code': 200, 'msg': '创建成功', 'data': location.to_dict() }) except Exception as e: db.session.rollback() return jsonify({ 'code': 500, 'msg': str(e), 'data': None }), 500 @warehouse_bp.route('/', methods=['PUT']) @jwt_required() @audit_log( module='库位管理', action='修改', get_target_id_fn=lambda: request.view_args.get('location_id'), get_target_name_fn=lambda: request.get_json().get('name') if request.get_json() else None ) def update_location(location_id): """ 更新库位 """ try: data = request.get_json() location = SysWarehouseLocation.query.get(location_id) if not location: return jsonify({'code': 404, 'msg': '库位不存在', 'data': None}) # 更新名称 if 'name' in data and data['name']: new_name = data['name'].strip() if new_name != location.name: # 需要更新 full_path parent = location.parent if parent: location.full_path = f"{parent.full_path}/{new_name}" if parent.full_path else new_name else: location.full_path = new_name location.name = new_name # 更新启用状态 if 'is_enabled' in data: location.is_enabled = data['is_enabled'] db.session.commit() return jsonify({ 'code': 200, 'msg': '更新成功', 'data': location.to_dict() }) except Exception as e: db.session.rollback() return jsonify({ 'code': 500, 'msg': str(e), 'data': None }), 500 @warehouse_bp.route('/', methods=['DELETE']) @jwt_required() @audit_log( module='库位管理', action='删除', get_target_id_fn=lambda: request.view_args.get('location_id') ) def delete_location(location_id): """ 删除库位(级联删除子库位) """ try: location = SysWarehouseLocation.query.get(location_id) if not location: return jsonify({'code': 404, 'msg': '库位不存在', 'data': None}) # 递归删除所有子库位 def delete_recursive(loc): # 先删除所有子节点 children = SysWarehouseLocation.query.filter_by(parent_id=loc.id).all() for child in children: delete_recursive(child) # 再删除自身 db.session.delete(loc) delete_recursive(location) db.session.commit() return jsonify({ 'code': 200, 'msg': '删除成功', 'data': None }) except Exception as e: db.session.rollback() return jsonify({ 'code': 500, 'msg': str(e), 'data': None }), 500