diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py index 600d51c..84a3c8f 100644 --- a/inventory-backend/app/services/inbound/base_service.py +++ b/inventory-backend/app/services/inbound/base_service.py @@ -6,7 +6,7 @@ from app.models.inbound.buy import StockBuy from app.models.inbound.semi import StockSemi from app.models.inbound.product import StockProduct # from app.models.inbound.service import StockService -from sqlalchemy import or_, and_, func, case, desc, asc +from sqlalchemy import or_, and_, func, case, desc, asc, cast, Numeric, text import traceback import json import io @@ -309,36 +309,27 @@ class MaterialBaseService: order_by_column = filters.get('orderByColumn', '') is_asc = filters.get('isAsc', None) - # 检查是否启用了预警智能排序 - enable_warning_sort = has_warning_permission and filters.get('enableWarningSort', False) + # 信任前端传递的预警排序开关(前端已基于权限注入) + enable_warning_sort = filters.get('enableWarningSort', False) if enable_warning_sort: - from sqlalchemy import text - - print("====== [DEBUG] 执行虚拟列强排模式 ======") - - # 1. 建立虚拟列标签 (显式暴露出计算结果) + print("====== [DEBUG] 成功进入预警强排逻辑 ======") + # 强制统一数据类型 + red_val = cast(MaterialWarningSetting.red_threshold, Numeric) + yellow_val = cast(MaterialWarningSetting.yellow_threshold, Numeric) + inv_val = cast(total_inv, Numeric) + warning_level = case([ - (and_(MaterialWarningSetting.is_enabled.is_(True), total_inv <= MaterialWarningSetting.red_threshold), 2), - (and_(MaterialWarningSetting.is_enabled.is_(True), total_inv <= MaterialWarningSetting.yellow_threshold), 1) + (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') - - # 红色组内部:缺口越大越靠前 DESC red_shortage = case([ - (and_(MaterialWarningSetting.is_enabled.is_(True), total_inv <= MaterialWarningSetting.red_threshold), - MaterialWarningSetting.red_threshold - total_inv) + (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val <= red_val), red_val - inv_val) ], else_=0).label('sort_red') - - # 黄色组内部:距离红线越近越靠前 ASC yellow_distance = case([ - (and_(MaterialWarningSetting.is_enabled.is_(True), total_inv > MaterialWarningSetting.red_threshold, total_inv <= MaterialWarningSetting.yellow_threshold), - total_inv - MaterialWarningSetting.red_threshold) + (and_(MaterialWarningSetting.is_enabled.is_(True), inv_val > red_val, inv_val <= yellow_val), inv_val - red_val) ], else_=999999).label('sort_yellow') - - # 2. 将虚拟列加入查询 (使其成为真实可引用的字段) query = query.add_columns(warning_level, red_shortage, yellow_distance) - - # 3. 清除任何杂乱排序,使用原生 text 强行指引数据库按虚拟列名排序 query = query.order_by(None).order_by( text("sort_level DESC"), text("sort_red DESC"), diff --git a/inventory-web/src/views/material/list.vue b/inventory-web/src/views/material/list.vue index fb89180..0de5fd1 100644 --- a/inventory-web/src/views/material/list.vue +++ b/inventory-web/src/views/material/list.vue @@ -580,6 +580,7 @@ interface QueryParams { isAsc: string | undefined; advancedFilters?: any[]; has_stock?: string; + enableWarningSort?: boolean; } interface CascaderOption { @@ -880,6 +881,8 @@ const querySearchType = (queryString: string, cb: any) => { const getList = () => { loading.value = true; + // 强制注入预警排序开关(基于权限) + queryParams.enableWarningSort = userStore.hasPermission('material_list:view_warning'); // Stringify advancedFilters to JSON string as backend expects const params = { ...queryParams,