From c5872aed3c5af09a757cfb30e7b9282823978c04 Mon Sep 17 00:00:00 2001 From: dxc Date: Mon, 2 Mar 2026 15:22:04 +0800 Subject: [PATCH] feat: add advanced filtering and full-field sorting to material list Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) --- inventory-backend/app/api/v1/inbound/base.py | 11 ++- .../app/services/inbound/base_service.py | 85 ++++++++++++++++--- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/inventory-backend/app/api/v1/inbound/base.py b/inventory-backend/app/api/v1/inbound/base.py index ae00f80..d8b220e 100644 --- a/inventory-backend/app/api/v1/inbound/base.py +++ b/inventory-backend/app/api/v1/inbound/base.py @@ -5,6 +5,7 @@ from app.services.inbound.base_service import MaterialBaseService from app.utils.decorators import login_required, permission_required import traceback import datetime +import json inbound_base_bp = Blueprint('stock_base', __name__) @@ -105,6 +106,13 @@ def get_list(): page = request.args.get('pageNum', 1, type=int) limit = request.args.get('pageSize', 10, type=int) + # 解析高级筛选条件 + advanced_filters_raw = request.args.get('advancedFilters', '[]') + try: + advanced_filters_list = json.loads(advanced_filters_raw) + except: + advanced_filters_list = [] + # 构造筛选条件 filters = { 'keyword': request.args.get('keyword', ''), @@ -113,7 +121,8 @@ def get_list(): 'type': request.args.get('type', ''), 'isEnabled': request.args.get('isEnabled', None), 'orderByColumn': request.args.get('orderByColumn', ''), - 'isAsc': request.args.get('isAsc', None) + 'isAsc': request.args.get('isAsc', None), + 'advancedFilters': advanced_filters_list } result = MaterialBaseService.get_list(page, limit, filters) diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py index a96fb42..12aa0ec 100644 --- a/inventory-backend/app/services/inbound/base_service.py +++ b/inventory-backend/app/services/inbound/base_service.py @@ -112,7 +112,7 @@ class MaterialBaseService: @staticmethod def get_list(page, limit, filters=None): """ - 获取基础信息列表 (带分页和筛选) + 获取基础信息列表 (带分页、高级筛选和全字段排序) """ try: # 构建聚合子查询 @@ -178,19 +178,80 @@ class MaterialBaseService: is_active = bool(int(filters['isEnabled'])) query = query.filter_by(is_enabled=is_active) - # 排序处理 + # 3. 高级动态筛选 + advanced_filters = filters.get('advancedFilters', []) + if advanced_filters: + allowed_fields = { + 'companyName': 'company_name', + 'name': 'name', + 'commonName': 'common_name', + 'category': 'category', + 'type': 'material_type', + 'spec': 'spec_model', + 'unit': 'unit', + 'inventoryCount': total_inv, + 'availableCount': total_avail + } + filter_conditions = [] + 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 + # 对于聚合字段 (inventoryCount, availableCount),需要使用子查询别名 + if isinstance(db_field, type(total_inv)): + column = db_field + else: + column = getattr(MaterialBase, db_field, None) + if column is None: + continue + if operator == 'eq': + filter_conditions.append(column == value) + elif operator == 'ne': + filter_conditions.append(column != value) + elif operator == 'contains': + filter_conditions.append(column.ilike(f'%{value}%')) + elif operator == 'ge': + try: + num_val = float(value) + filter_conditions.append(column >= num_val) + except ValueError: + continue + elif operator == 'le': + try: + num_val = float(value) + filter_conditions.append(column <= num_val) + except ValueError: + continue + if filter_conditions: + query = query.filter(and_(*filter_conditions)) + + # 排序处理(支持全字段) order_by_column = filters.get('orderByColumn', '') is_asc = filters.get('isAsc', None) - if order_by_column == 'inventoryCount': - if is_asc == 'asc': - query = query.order_by(total_inv.asc()) - else: - query = query.order_by(total_inv.desc()) - elif order_by_column == 'availableCount': - if is_asc == 'asc': - query = query.order_by(total_avail.asc()) - else: - query = query.order_by(total_avail.desc()) + if order_by_column: + # 字段映射 + sort_field_map = { + 'companyName': MaterialBase.company_name, + 'name': MaterialBase.name, + 'commonName': MaterialBase.common_name, + 'category': MaterialBase.category, + 'type': MaterialBase.material_type, + 'spec': MaterialBase.spec_model, + 'unit': MaterialBase.unit, + 'inventoryCount': total_inv, + 'availableCount': total_avail + } + sort_column = sort_field_map.get(order_by_column) + if sort_column is not None: + if is_asc == 'asc': + query = query.order_by(sort_column.asc()) + elif is_asc == 'desc': + query = query.order_by(sort_column.desc()) else: # 默认排序:优先按总库存数降序,当库存相同时,再按规格型号升序 query = query.order_by(total_inv.desc(), MaterialBase.spec_model.asc())