feat(backend): 库存列表列头排序——库存/成本价/总价/生产日期/入库时间全局排序

sort_by 白名单列映射 SQL 表达式(cost/price/prod_date 与 SELECT 同 COALESCE
口径,排序与显示一致)+ sort_dir,非法值忽略回默认;id 兜底保证分页稳定。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
wangjia
2026-07-07 16:45:38 +08:00
parent b682b7ea88
commit e012f6e02b
2 changed files with 60 additions and 1 deletions
@@ -3,6 +3,7 @@ package handler
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
@@ -525,3 +526,44 @@ func TestInventoryHandler_StatusTenantIsolation(t *testing.T) {
require.NoError(t, db.Where("id = ?", invB.ID).First(&got).Error)
assert.Equal(t, "stock", got.Status)
}
// 列头排序(2026-07-07):白名单列全局排序,非法列忽略回默认
func TestInventoryHandler_ListSort(t *testing.T) {
db := testutil.SetupTestDB()
shop := testutil.CreateTestShop(db, "INV_SORT")
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
wh := testutil.CreateTestWarehouse(db, shop.ID, "仓")
token := getAuthToken(user.ID, shop.ID, "admin")
r := setupProtectedRouter(db)
for i, qty := range []float64{5, 20, 10} {
p := testutil.CreateTestProduct(db, shop.ID, fmt.Sprintf("酒%d", i))
price := float64(100 * (i + 1))
require.NoError(t, db.Create(&model.Inventory{
ShopID: shop.ID, WarehouseID: &wh.ID, ProductID: &p.ID,
Quantity: qty, UnitPrice: &price,
}).Error)
}
qtys := func(w *httptest.ResponseRecorder) []float64 {
rows := parseResponse(w)["data"].([]interface{})
out := make([]float64, len(rows))
for i, r := range rows {
out[i] = r.(map[string]interface{})["quantity"].(float64)
}
return out
}
// qty 升序
w := makeRequest(r, "GET", "/api/v1/inventory?sort_by=qty&sort_dir=asc", token, nil)
assert.Equal(t, []float64{5, 10, 20}, qtys(w))
// qty 降序
w = makeRequest(r, "GET", "/api/v1/inventory?sort_by=qty&sort_dir=desc", token, nil)
assert.Equal(t, []float64{20, 10, 5}, qtys(w))
// 总价 = qty×进价:5×100=500, 20×200=4000, 10×300=3000 → 升序 5,10,20
w = makeRequest(r, "GET", "/api/v1/inventory?sort_by=price&sort_dir=asc", token, nil)
assert.Equal(t, []float64{5, 10, 20}, qtys(w))
// 非法列忽略 → 默认 id 倒序(最后插入的 qty=10 在前)
w = makeRequest(r, "GET", "/api/v1/inventory?sort_by=evil;drop", token, nil)
assert.Equal(t, []float64{10, 20, 5}, qtys(w))
}