From 58a519a62f42e0a05e625b983331f78a3820113f Mon Sep 17 00:00:00 2001 From: DXC Date: Thu, 12 Mar 2026 09:39:48 +0800 Subject: [PATCH] fix: refactor advanced filters to use WHERE for subquery columns and rewrite warning sort in pure order_by expressions --- .../app/services/inbound/base_service.py | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py index c021777..796b968 100644 --- a/inventory-backend/app/services/inbound/base_service.py +++ b/inventory-backend/app/services/inbound/base_service.py @@ -230,12 +230,12 @@ class MaterialBaseService: .outerjoin(MaterialWarningSetting, MaterialBase.id == MaterialWarningSetting.base_id) # ============================================================ - # 【修复3】高级筛选:聚合字段使用 HAVING,非聚合字段使用 WHERE + # 【修复3】高级筛选:统一使用 WHERE (filter) + # 聚合字段同样是子查询列,直接用 filter 过滤即可 # ============================================================ advanced_filters = filters.get('advancedFilters', []) if filters else [] - having_conditions = [] filter_conditions = [] - + if advanced_filters: allowed_fields = { 'companyName': 'company_name', @@ -248,9 +248,9 @@ class MaterialBaseService: 'inventoryCount': 'total_inv', 'availableCount': 'total_avail' } - # 聚合字段列表(这些需要用 HAVING) + # 聚合字段列表 aggregate_fields = {'inventoryCount', 'availableCount'} - + field_permission_map = { 'companyName': 'material_list:companyName', 'name': 'material_list:name', @@ -262,18 +262,18 @@ class MaterialBaseService: 'inventoryCount': 'material_list:inventoryCount', 'availableCount': 'material_list:availableCount' } - + for condition in advanced_filters: field = condition.get('field') operator = condition.get('operator') value = condition.get('value') if not field or not operator or value is None: continue - + db_field = allowed_fields.get(field) if not db_field: continue - + # 权限校验 if user_permissions is not None: perm_code = field_permission_map.get(field) @@ -281,32 +281,32 @@ class MaterialBaseService: pass elif perm_code and perm_code not in user_permissions: continue - + # 判断是否为聚合字段 is_aggregate = field in aggregate_fields - + if is_aggregate: - # 聚合字段:使用 HAVING - 通过子查询列进行比较 + # 聚合字段:直接使用子查询列,通过 WHERE (filter) 过滤 col = inner_sub.c.total_inv if field == 'inventoryCount' else inner_sub.c.total_avail try: num_val = float(value) except ValueError: continue - + if operator == 'eq': - having_conditions.append(col == num_val) + filter_conditions.append(col == num_val) elif operator == 'ne': - having_conditions.append(col != num_val) + filter_conditions.append(col != num_val) elif operator == 'ge': - having_conditions.append(col >= num_val) + filter_conditions.append(col >= num_val) elif operator == 'le': - having_conditions.append(col <= num_val) + filter_conditions.append(col <= num_val) else: # 非聚合字段:使用 WHERE column = getattr(MaterialBase, db_field, None) if column is None: continue - + if operator == 'eq': filter_conditions.append(column == value) elif operator == 'ne': @@ -325,14 +325,10 @@ class MaterialBaseService: filter_conditions.append(column <= num_val) except ValueError: continue - - # 应用 WHERE 条件 + + # 应用 WHERE 条件(统一使用 filter) if filter_conditions: query = query.filter(and_(*filter_conditions)) - - # 应用 HAVING 条件(聚合字段筛选) - if having_conditions: - query = query.having(and_(*having_conditions)) # 排序处理(支持全字段) order_by_column = filters.get('orderByColumn', '') @@ -342,9 +338,8 @@ class MaterialBaseService: enable_warning_sort = filters.get('enableWarningSort', False) if enable_warning_sort: - print("====== [DEBUG] 成功进入预警强排逻辑 (SQLA 2.0) ======") - # 【核心修复】使用子查询列进行预警排序计算 - # total_inv 现在是 inner_sub.c.total_inv,可以安全参与 case 计算 + print("====== [DEBUG] 成功进入预警强排逻辑 ======") + # 直接在 order_by 中进行计算排序,不污染 select 列 inv_val = inner_sub.c.total_inv red_val = cast(MaterialWarningSetting.red_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 <= yellow_val), 1), else_=0 - ).label('sort_level') - - # 红色预警时的缺口(库存与红色阈值的差距) + ) + # 红色预警时的缺口 red_shortage = case( (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= red_val), red_val - inv_val), else_=0 - ).label('sort_red') - + ) # 黄色预警时的缺口 yellow_distance = case( (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val > red_val, inv_val <= yellow_val), inv_val - red_val), else_=999999 - ).label('sort_yellow') - - query = query.add_columns(warning_level, red_shortage, yellow_distance) - query = query.order_by(None).order_by( - text("sort_level DESC"), - text("sort_red DESC"), - text("sort_yellow ASC"), - MaterialBase.id.desc() + ) + + # 直接在 order_by 中使用 case() 表达式 + query = query.order_by( + desc(warning_level), + desc(red_shortage), + asc(yellow_distance), + desc(MaterialBase.id) ) elif order_by_column: # 字段映射 - 使用子查询列进行排序