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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user