fix: refactor advanced filters to use WHERE for subquery columns and rewrite warning sort in pure order_by expressions

This commit is contained in:
DXC
2026-03-12 09:39:48 +08:00
parent d8e86959b8
commit 58a519a62f

View File

@ -230,12 +230,12 @@ class MaterialBaseService:
.outerjoin(MaterialWarningSetting, MaterialBase.id == MaterialWarningSetting.base_id) .outerjoin(MaterialWarningSetting, MaterialBase.id == MaterialWarningSetting.base_id)
# ============================================================ # ============================================================
# 【修复3】高级筛选聚合字段使用 HAVING非聚合字段使用 WHERE # 【修复3】高级筛选统一使用 WHERE (filter)
# 聚合字段同样是子查询列,直接用 filter 过滤即可
# ============================================================ # ============================================================
advanced_filters = filters.get('advancedFilters', []) if filters else [] advanced_filters = filters.get('advancedFilters', []) if filters else []
having_conditions = []
filter_conditions = [] filter_conditions = []
if advanced_filters: if advanced_filters:
allowed_fields = { allowed_fields = {
'companyName': 'company_name', 'companyName': 'company_name',
@ -248,9 +248,9 @@ class MaterialBaseService:
'inventoryCount': 'total_inv', 'inventoryCount': 'total_inv',
'availableCount': 'total_avail' 'availableCount': 'total_avail'
} }
# 聚合字段列表(这些需要用 HAVING # 聚合字段列表
aggregate_fields = {'inventoryCount', 'availableCount'} aggregate_fields = {'inventoryCount', 'availableCount'}
field_permission_map = { field_permission_map = {
'companyName': 'material_list:companyName', 'companyName': 'material_list:companyName',
'name': 'material_list:name', 'name': 'material_list:name',
@ -262,18 +262,18 @@ class MaterialBaseService:
'inventoryCount': 'material_list:inventoryCount', 'inventoryCount': 'material_list:inventoryCount',
'availableCount': 'material_list:availableCount' 'availableCount': 'material_list:availableCount'
} }
for condition in advanced_filters: for condition in advanced_filters:
field = condition.get('field') field = condition.get('field')
operator = condition.get('operator') operator = condition.get('operator')
value = condition.get('value') value = condition.get('value')
if not field or not operator or value is None: if not field or not operator or value is None:
continue continue
db_field = allowed_fields.get(field) db_field = allowed_fields.get(field)
if not db_field: if not db_field:
continue continue
# 权限校验 # 权限校验
if user_permissions is not None: if user_permissions is not None:
perm_code = field_permission_map.get(field) perm_code = field_permission_map.get(field)
@ -281,32 +281,32 @@ class MaterialBaseService:
pass pass
elif perm_code and perm_code not in user_permissions: elif perm_code and perm_code not in user_permissions:
continue continue
# 判断是否为聚合字段 # 判断是否为聚合字段
is_aggregate = field in aggregate_fields is_aggregate = field in aggregate_fields
if is_aggregate: if is_aggregate:
# 聚合字段:使用 HAVING - 通过子查询列进行比较 # 聚合字段:直接使用子查询列,通过 WHERE (filter) 过滤
col = inner_sub.c.total_inv if field == 'inventoryCount' else inner_sub.c.total_avail col = inner_sub.c.total_inv if field == 'inventoryCount' else inner_sub.c.total_avail
try: try:
num_val = float(value) num_val = float(value)
except ValueError: except ValueError:
continue continue
if operator == 'eq': if operator == 'eq':
having_conditions.append(col == num_val) filter_conditions.append(col == num_val)
elif operator == 'ne': elif operator == 'ne':
having_conditions.append(col != num_val) filter_conditions.append(col != num_val)
elif operator == 'ge': elif operator == 'ge':
having_conditions.append(col >= num_val) filter_conditions.append(col >= num_val)
elif operator == 'le': elif operator == 'le':
having_conditions.append(col <= num_val) filter_conditions.append(col <= num_val)
else: else:
# 非聚合字段:使用 WHERE # 非聚合字段:使用 WHERE
column = getattr(MaterialBase, db_field, None) column = getattr(MaterialBase, db_field, None)
if column is None: if column is None:
continue continue
if operator == 'eq': if operator == 'eq':
filter_conditions.append(column == value) filter_conditions.append(column == value)
elif operator == 'ne': elif operator == 'ne':
@ -325,14 +325,10 @@ class MaterialBaseService:
filter_conditions.append(column <= num_val) filter_conditions.append(column <= num_val)
except ValueError: except ValueError:
continue continue
# 应用 WHERE 条件 # 应用 WHERE 条件(统一使用 filter
if filter_conditions: if filter_conditions:
query = query.filter(and_(*filter_conditions)) query = query.filter(and_(*filter_conditions))
# 应用 HAVING 条件(聚合字段筛选)
if having_conditions:
query = query.having(and_(*having_conditions))
# 排序处理(支持全字段) # 排序处理(支持全字段)
order_by_column = filters.get('orderByColumn', '') order_by_column = filters.get('orderByColumn', '')
@ -342,9 +338,8 @@ class MaterialBaseService:
enable_warning_sort = filters.get('enableWarningSort', False) enable_warning_sort = filters.get('enableWarningSort', False)
if enable_warning_sort: if enable_warning_sort:
print("====== [DEBUG] 成功进入预警强排逻辑 (SQLA 2.0) ======") print("====== [DEBUG] 成功进入预警强排逻辑 ======")
# 【核心修复】使用子查询列进行预警排序计算 # 直接在 order_by 中进行计算排序,不污染 select 列
# total_inv 现在是 inner_sub.c.total_inv可以安全参与 case 计算
inv_val = inner_sub.c.total_inv inv_val = inner_sub.c.total_inv
red_val = cast(MaterialWarningSetting.red_threshold, Numeric) red_val = cast(MaterialWarningSetting.red_threshold, Numeric)
yellow_val = cast(MaterialWarningSetting.yellow_threshold, Numeric) yellow_val = cast(MaterialWarningSetting.yellow_threshold, Numeric)
@ -354,26 +349,24 @@ class MaterialBaseService:
(and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= red_val), 2), (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= red_val), 2),
(and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= yellow_val), 1), (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= yellow_val), 1),
else_=0 else_=0
).label('sort_level') )
# 红色预警时的缺口
# 红色预警时的缺口(库存与红色阈值的差距)
red_shortage = case( red_shortage = case(
(and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= red_val), red_val - inv_val), (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= red_val), red_val - inv_val),
else_=0 else_=0
).label('sort_red') )
# 黄色预警时的缺口 # 黄色预警时的缺口
yellow_distance = case( yellow_distance = case(
(and_(MaterialWarningSetting.is_enabled.is_(True), inv_val > red_val, inv_val <= yellow_val), inv_val - red_val), (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val > red_val, inv_val <= yellow_val), inv_val - red_val),
else_=999999 else_=999999
).label('sort_yellow') )
query = query.add_columns(warning_level, red_shortage, yellow_distance) # 直接在 order_by 中使用 case() 表达式
query = query.order_by(None).order_by( query = query.order_by(
text("sort_level DESC"), desc(warning_level),
text("sort_red DESC"), desc(red_shortage),
text("sort_yellow ASC"), asc(yellow_distance),
MaterialBase.id.desc() desc(MaterialBase.id)
) )
elif order_by_column: elif order_by_column:
# 字段映射 - 使用子查询列进行排序 # 字段映射 - 使用子查询列进行排序