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
+18 -1
View File
@@ -158,6 +158,23 @@ func (h *InventoryHandler) List(c *gin.Context) {
return
}
// 排序(2026-07-07 列头排序):白名单列 → SQL 表达式,防注入;带 id 兜底保证分页稳定。
// cost/price/prod_date 用与 SELECT 相同的 COALESCE 口径,排序结果与显示一致。
orderExpr := "inv.id DESC"
if sortCol, ok := map[string]string{
"qty": "inv.quantity",
"cost": "COALESCE(sii.cost_price, inv.unit_price, p.purchase_price)",
"price": "inv.quantity * COALESCE(sii.cost_price, inv.unit_price, p.purchase_price)",
"prod_date": "COALESCE(DATE(sii.production_date), DATE(inv.production_date))",
"in_time": "inv.created_at",
}[c.Query("sort_by")]; ok {
dir := "ASC"
if c.Query("sort_dir") == "desc" {
dir = "DESC"
}
orderExpr = sortCol + " " + dir + ", inv.id DESC"
}
// Data query
dataSQL := `
SELECT
@@ -183,7 +200,7 @@ func (h *InventoryHandler) List(c *gin.Context) {
LEFT JOIN products p ON p.id = inv.product_id AND p.deleted_at IS NULL
LEFT JOIN warehouses w ON w.id = inv.warehouse_id
WHERE ` + baseWhere + `
ORDER BY inv.id DESC
ORDER BY ` + orderExpr + `
LIMIT ? OFFSET ?`
dataArgs := append(args, pageSize, (page-1)*pageSize)