Files
jiu/docs/review/validation-bugs.md
T
wangjia 66e54af8c6 feat: 商品追踪页面 + 出库提交库存校验
后端:
- feat(backend): 重命名 Batches→Products 接口,新增库存状态(在售/已卖出)和买家信息
- feat(backend): 出库单创建/提交时校验仓库库存,不足则返回明确错误信息
- fix(backend): 出库创建 status 判断逻辑修复(空值默认 draft)

前端:
- feat(client): 批次追踪改为商品追踪,新增状态列(在售/已卖出)和买家/时间列
- fix(client): 无批次号时显示"无批次"而非空
- refactor(client): BatchRecord → ProductTrackingRecord,repository 接口更新

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 22:26:42 +08:00

117 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 参数校验 Bug 报告
## BUG-001
**严重程度**:高
**问题描述**
`POST /api/v1/partners` 接口缺少对 `name` 字段的必填校验。传入空 `name` 时,handler 成功通过 `ShouldBindJSON` 绑定并返回 201,而不是期望的 400。
**复现步骤**
1. 调用 `POST /api/v1/partners`body 为 `{"type":"supplier"}`(不含 name
2. 期望返回 400,实际返回 201
**失败的测试用例**
```go
func TestPartnerHandler_Create_MissingName(t *testing.T) {
// 这个测试会失败,揭示了 partner handler 缺少 name 必填校验
body := `{"type":"supplier"}`
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/partners", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
// 预期 400,实际得到 201
}
```
**根因分析**
`backend/internal/model/partner.go` 第 6 行:
```go
Name string `gorm:"size:200;not null" json:"name"`
```
结构体 `Name` 字段只有 GORM 的 `not null` 约束,没有 `binding:"required"` 标签。
`c.ShouldBindJSON` 只校验 `binding` 标签,不校验 GORM 标签,导致空 name 可以通过校验。
SQLite in-memory 不严格强制 NOT NULL 约束(与 MySQL 行为不同),所以数据库层面也未报错。
**修复建议**
`Partner` model 的 `Name` 字段添加 `binding:"required"` 标签:
```go
Name string `gorm:"size:200;not null" json:"name" binding:"required"`
```
或者在 handler 中手动校验:
```go
if req.Name == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
return
}
```
---
## BUG-002
**严重程度**:高
**问题描述**
`POST /api/v1/stock-in/orders` 接口缺少对 `warehouse_id` 字段的必填校验。传入 `warehouse_id: 0`(JSON 中省略该字段时默认为 0)时,handler 返回 201,而不是期望的 400。
**复现步骤**
1. 调用 `POST /api/v1/stock-in/orders`body 为 `{"items":[]}`(不含 warehouse_id
2. 期望返回 400,实际返回 201
**失败的测试用例**
```go
func TestStockInHandler_Create_MissingWarehouse(t *testing.T) {
body := `{"items":[]}`
// 预期 400,实际得到 201
}
```
**根因分析**
`backend/internal/model/stock.go` 第 10 行:
```go
WarehouseID uint64 `gorm:"not null" json:"warehouse_id"`
```
同 BUG-001,结构体只有 GORM 约束,缺少 `binding:"required"`
对于 `uint64` 类型,JSON 省略时默认值为 0`ShouldBindJSON` 不会报错。
**修复建议**
`StockInOrder.WarehouseID` 添加 `binding:"required,min=1"` 标签:
```go
WarehouseID uint64 `gorm:"not null" json:"warehouse_id" binding:"required,min=1"`
```
或在 handler `Create` 函数中(`backend/internal/handler/stock_in.go`,约第 82 行)手动校验:
```go
if req.WarehouseID == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "warehouse_id is required"})
return
}
```
---
## BUG-003
**严重程度**:高
**问题描述**
`POST /api/v1/stock-out/orders` 接口缺少对 `warehouse_id` 字段的必填校验,与 BUG-002 问题相同。
**复现步骤**
1. 调用 `POST /api/v1/stock-out/orders`body 为 `{"items":[]}`(不含 warehouse_id
2. 期望返回 400,实际返回 201
**失败的测试用例**
```go
func TestStockOutHandler_Create_MissingWarehouse(t *testing.T) {
body := `{"items":[]}`
// 预期 400,实际得到 201
}
```
**根因分析**
同 BUG-002`backend/internal/model/stock.go``StockOutOrder.WarehouseID` 缺少 `binding` 校验标签。
**修复建议**
`StockOutOrder.WarehouseID` 添加 `binding:"required,min=1"` 标签,或在 `backend/internal/handler/stock_out.go``Create` 函数中手动校验。