109 lines
4.2 KiB
Python
109 lines
4.2 KiB
Python
# 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})
|