feat: improve fuzzy search for buy inbound material
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
@ -41,7 +41,7 @@ class BuyInboundService:
|
||||
raise ValueError(f"该物料已存在批号【{batch_number}】,请勿重复录入,可直接在该批次下追加库存。")
|
||||
|
||||
# ============================================================
|
||||
# 1. 基础物料搜索 (参照 Base 模块:全量模糊匹配)
|
||||
# 1. 基础物料搜索 (增强模糊匹配:多字段、多关键词、分词匹配)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def search_base_material(keyword):
|
||||
@ -49,26 +49,56 @@ class BuyInboundService:
|
||||
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
||||
|
||||
if keyword:
|
||||
# [参照 Base 逻辑] 简单直接的模糊匹配:LIKE %keyword%
|
||||
k_str = f'%{keyword.strip()}%'
|
||||
# 1. 清理关键词:去除首尾空格,并将多个连续空格替换为单个
|
||||
import re
|
||||
keyword_clean = re.sub(r'\s+', ' ', keyword.strip())
|
||||
|
||||
# 2. 支持两种搜索模式:
|
||||
# a) 精确短语匹配:用双引号包裹,如 "蓝色电阻"
|
||||
# b) 多关键词 AND 匹配:空格分隔,如 "蓝色 电阻"
|
||||
# c) 单关键词模糊匹配:如 "蓝色"
|
||||
|
||||
if keyword_clean.startswith('"') and keyword_clean.endswith('"'):
|
||||
# 精确短语匹配
|
||||
exact_phrase = keyword_clean[1:-1]
|
||||
if exact_phrase:
|
||||
k_str = f'%{exact_phrase}%'
|
||||
conditions = [
|
||||
MaterialBase.name.ilike(k_str),
|
||||
MaterialBase.spec_model.ilike(k_str),
|
||||
MaterialBase.pinyin.ilike(k_str),
|
||||
MaterialBase.category.ilike(k_str),
|
||||
MaterialBase.material_type.ilike(k_str)
|
||||
]
|
||||
if hasattr(MaterialBase, 'brand'):
|
||||
conditions.append(MaterialBase.brand.ilike(k_str))
|
||||
if hasattr(MaterialBase, 'manufacturer'):
|
||||
conditions.append(MaterialBase.manufacturer.ilike(k_str))
|
||||
query = query.filter(or_(*conditions))
|
||||
else:
|
||||
# 多关键词 AND 匹配
|
||||
keywords = keyword_clean.split()
|
||||
if keywords:
|
||||
and_conditions = []
|
||||
for word in keywords:
|
||||
k_str = f'%{word}%'
|
||||
word_conditions = [
|
||||
MaterialBase.name.ilike(k_str),
|
||||
MaterialBase.spec_model.ilike(k_str),
|
||||
MaterialBase.pinyin.ilike(k_str),
|
||||
MaterialBase.category.ilike(k_str),
|
||||
MaterialBase.material_type.ilike(k_str)
|
||||
]
|
||||
if hasattr(MaterialBase, 'brand'):
|
||||
word_conditions.append(MaterialBase.brand.ilike(k_str))
|
||||
if hasattr(MaterialBase, 'manufacturer'):
|
||||
word_conditions.append(MaterialBase.manufacturer.ilike(k_str))
|
||||
and_conditions.append(or_(*word_conditions))
|
||||
# 使用 AND 连接所有关键词条件
|
||||
if and_conditions:
|
||||
query = query.filter(and_(*and_conditions))
|
||||
|
||||
conditions = [
|
||||
MaterialBase.name.ilike(k_str), # 名称
|
||||
MaterialBase.spec_model.ilike(k_str), # 规格
|
||||
MaterialBase.pinyin.ilike(k_str), # 拼音
|
||||
MaterialBase.category.ilike(k_str), # 类别
|
||||
MaterialBase.material_type.ilike(k_str) # 类型
|
||||
]
|
||||
|
||||
# 安全地添加可能存在的扩展字段 (品牌/厂家)
|
||||
if hasattr(MaterialBase, 'brand'):
|
||||
conditions.append(MaterialBase.brand.ilike(k_str))
|
||||
if hasattr(MaterialBase, 'manufacturer'):
|
||||
conditions.append(MaterialBase.manufacturer.ilike(k_str))
|
||||
|
||||
query = query.filter(or_(*conditions))
|
||||
|
||||
# [参照 Base 逻辑] 移除 limit,返回所有结果
|
||||
# 按 ID 倒序排序
|
||||
query = query.order_by(MaterialBase.id.desc())
|
||||
|
||||
results = []
|
||||
@ -80,7 +110,6 @@ class BuyInboundService:
|
||||
'category': item.category,
|
||||
'unit': item.unit,
|
||||
'type': item.material_type,
|
||||
# 使用 getattr 防止字段不存在报错
|
||||
'brand': getattr(item, 'brand', ''),
|
||||
'manufacturer': getattr(item, 'manufacturer', ''),
|
||||
'pinyin': getattr(item, 'pinyin', ''),
|
||||
@ -261,7 +290,7 @@ class BuyInboundService:
|
||||
raise e
|
||||
|
||||
# ============================================================
|
||||
# 5. 获取列表 (参照 Base 逻辑:全量模糊匹配)
|
||||
# 5. 获取列表 (增强模糊匹配:多字段、多关键词、分词匹配)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_list(page, limit, keyword=None, statuses=None):
|
||||
@ -269,30 +298,58 @@ class BuyInboundService:
|
||||
query = db.session.query(StockBuy).outerjoin(MaterialBase, StockBuy.base_id == MaterialBase.id)
|
||||
|
||||
if keyword:
|
||||
# [简单匹配] 只要任一字段包含该字符串,即匹配
|
||||
k_str = f'%{keyword.strip()}%'
|
||||
|
||||
conditions = [
|
||||
# 库存业务字段
|
||||
StockBuy.sku.ilike(k_str),
|
||||
StockBuy.barcode.ilike(k_str),
|
||||
StockBuy.batch_number.ilike(k_str),
|
||||
StockBuy.serial_number.ilike(k_str),
|
||||
StockBuy.supplier_name.ilike(k_str),
|
||||
StockBuy.buyer_name.ilike(k_str),
|
||||
# 基础物料关联字段
|
||||
MaterialBase.name.ilike(k_str),
|
||||
MaterialBase.spec_model.ilike(k_str),
|
||||
MaterialBase.pinyin.ilike(k_str),
|
||||
MaterialBase.category.ilike(k_str)
|
||||
]
|
||||
|
||||
if hasattr(MaterialBase, 'brand'):
|
||||
conditions.append(MaterialBase.brand.ilike(k_str))
|
||||
if hasattr(MaterialBase, 'manufacturer'):
|
||||
conditions.append(MaterialBase.manufacturer.ilike(k_str))
|
||||
|
||||
query = query.filter(or_(*conditions))
|
||||
# 1. 清理关键词
|
||||
import re
|
||||
keyword_clean = re.sub(r'\s+', ' ', keyword.strip())
|
||||
|
||||
if keyword_clean.startswith('"') and keyword_clean.endswith('"'):
|
||||
# 精确短语匹配
|
||||
exact_phrase = keyword_clean[1:-1]
|
||||
if exact_phrase:
|
||||
k_str = f'%{exact_phrase}%'
|
||||
conditions = [
|
||||
StockBuy.sku.ilike(k_str),
|
||||
StockBuy.barcode.ilike(k_str),
|
||||
StockBuy.batch_number.ilike(k_str),
|
||||
StockBuy.serial_number.ilike(k_str),
|
||||
StockBuy.supplier_name.ilike(k_str),
|
||||
StockBuy.buyer_name.ilike(k_str),
|
||||
MaterialBase.name.ilike(k_str),
|
||||
MaterialBase.spec_model.ilike(k_str),
|
||||
MaterialBase.pinyin.ilike(k_str),
|
||||
MaterialBase.category.ilike(k_str)
|
||||
]
|
||||
if hasattr(MaterialBase, 'brand'):
|
||||
conditions.append(MaterialBase.brand.ilike(k_str))
|
||||
if hasattr(MaterialBase, 'manufacturer'):
|
||||
conditions.append(MaterialBase.manufacturer.ilike(k_str))
|
||||
query = query.filter(or_(*conditions))
|
||||
else:
|
||||
# 多关键词 AND 匹配
|
||||
keywords = keyword_clean.split()
|
||||
if keywords:
|
||||
and_conditions = []
|
||||
for word in keywords:
|
||||
k_str = f'%{word}%'
|
||||
word_conditions = [
|
||||
StockBuy.sku.ilike(k_str),
|
||||
StockBuy.barcode.ilike(k_str),
|
||||
StockBuy.batch_number.ilike(k_str),
|
||||
StockBuy.serial_number.ilike(k_str),
|
||||
StockBuy.supplier_name.ilike(k_str),
|
||||
StockBuy.buyer_name.ilike(k_str),
|
||||
MaterialBase.name.ilike(k_str),
|
||||
MaterialBase.spec_model.ilike(k_str),
|
||||
MaterialBase.pinyin.ilike(k_str),
|
||||
MaterialBase.category.ilike(k_str)
|
||||
]
|
||||
if hasattr(MaterialBase, 'brand'):
|
||||
word_conditions.append(MaterialBase.brand.ilike(k_str))
|
||||
if hasattr(MaterialBase, 'manufacturer'):
|
||||
word_conditions.append(MaterialBase.manufacturer.ilike(k_str))
|
||||
and_conditions.append(or_(*word_conditions))
|
||||
if and_conditions:
|
||||
query = query.filter(and_(*and_conditions))
|
||||
|
||||
if not statuses:
|
||||
statuses = ['在库', '借库']
|
||||
@ -447,4 +504,4 @@ class BuyInboundService:
|
||||
locs = [row[0] for row in query.all()]
|
||||
return locs
|
||||
except Exception:
|
||||
return []
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user