# inventory-backend/app/api/v1/common/search.py from flask import jsonify, request from . import common_bp from app.models import MaterialBase from app.models.inbound.buy import StockBuy from app.models.inbound.semi import StockSemi from app.models.inbound.product import StockProduct from app.models.bom import BomTable from app.extensions import db @common_bp.route('/global-search', methods=['GET']) def global_search(): """ 全局聚合搜索接口(多词 AND 模式,无数量限制) 入参: keyword (字符串,支持空格分词,多词必须同时匹配) 搜索范围: 基础物料、采购库、BOM配方 """ keyword = request.args.get('keyword', request.args.get('q', '')).strip() keywords = keyword.split() if not keywords: return jsonify({"code": 200, "data": []}) merged_list = [] # ── 1. 基础物料 (MaterialBase) ────────────────────────── # 真实字段: name, common_name, spec_model, category material_conditions = [] for kw in keywords: kw_term = f'%{kw}%' material_conditions.append( db.or_( MaterialBase.name.ilike(kw_term), MaterialBase.common_name.ilike(kw_term), MaterialBase.spec_model.ilike(kw_term), MaterialBase.category.ilike(kw_term) ) ) bases = MaterialBase.query.filter(db.and_(*material_conditions)).all() for b in bases: merged_list.append({ "id": b.id, "type": "material", "title": b.name, "subtitle": b.spec_model or b.common_name or '无规格型号', "badge": "基础物料", "extra": {"category": b.category or ''} }) # ── 2. 库存三表(采购/半成品/成品):按 SKU/条码/名称命中,显示所在库位 ── def _append_stock_items(model, type_key, badge): conds = [] for kw in keywords: t = f'%{kw}%' c = db.or_( MaterialBase.name.ilike(t), model.sku.ilike(t), model.barcode.ilike(t), ) if hasattr(model, 'serial_number'): c = db.or_(c, model.serial_number.ilike(t)) conds.append(c) rows = (model.query.join(MaterialBase, model.base_id == MaterialBase.id) .filter(db.and_(*conds)).order_by(model.id.desc()).limit(50).all()) for s in rows: merged_list.append({ "id": s.base_id, "stock_id": s.id, "type": type_key, "title": s.base.name if s.base else '未知物料', "subtitle": f"SKU: {s.sku or '-'} | 库位: {s.warehouse_location or '-'} | 库存: {s.stock_quantity or 0}", "badge": badge, "extra": { "sku": s.sku or '', "warehouse_location": s.warehouse_location or '', "stock_id": s.id, "source_table": model.__tablename__, } }) _append_stock_items(StockBuy, 'stock_buy', '采购库') _append_stock_items(StockSemi, 'stock_semi', '半成品') _append_stock_items(StockProduct, 'stock_product', '成品') # ── 3. BOM 配方 (BomTable) ────────────────────────────── # 真实字段: bom_no, version bom_conditions = [] for kw in keywords: kw_term = f'%{kw}%' bom_conditions.append( db.or_( BomTable.bom_no.ilike(kw_term), BomTable.version.ilike(kw_term) ) ) boms = BomTable.query.filter(db.and_(*bom_conditions)).all() for bom in boms: parent_name = bom.parent.name if bom.parent else '' merged_list.append({ "id": bom.id, "bom_no": bom.bom_no, "type": "bom", "title": f"{bom.bom_no} ({bom.version})", "subtitle": f"父件: {parent_name}" if parent_name else f"版本: {bom.version}", "badge": "配方BOM", "extra": {"version": bom.version, "parent_id": bom.parent_id} }) return jsonify({"code": 200, "data": merged_list})