Files
jiu/backend/internal/handler/public_test.go
T
wangjia 73955a139c feat(backend): 库存三态落库(stock/on_sale/sold)——挂售可控 + 卖光留痕
- inventories 加 status 枚举列(默认 stock,AutoMigrate 存量即全部「库存」)+ 索引
- 出库审核扣光置 sold 不再软删(留痕台账);退单恢复行置回 stock;盘亏仍软删
- 新接口 PUT /inventory/:id/status(仅收 stock/on_sale,已售行拒改,租户校验)
- List 默认排除已售(显式 status=sold 才显示)+ status 多值筛选
- Summary:SKU/货值/数量排除已售,新增 sold_count
- 公开店铺页/API 只列 on_sale——酒单从全量裸奔改为人工精选
- 测试:卖光置售/部分不改/退单回库/接口边界/跨租户/汇总口径 8 用例
- 设计方案 docs/design/inventory-sale-status.html + db-schema/CLAUDE.md 同步

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-07 13:35:03 +08:00

127 lines
3.8 KiB
Go
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.
package handler
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"github.com/wangjia/jiu/backend/internal/model"
"github.com/wangjia/jiu/backend/testutil"
)
func setupPublicRouter(db *gorm.DB) *gin.Engine {
h := NewPublicHandler(db)
r := gin.New()
r.Use(gin.Recovery())
r.GET("/api/v1/public/shops/:shop_code/products", h.ListShopProducts)
return r
}
// 给商品补 public_idCreateTestProduct 默认不设)
func setPublicID(db *gorm.DB, productID uint64, pub string) {
require := func(err error) {
if err != nil {
panic(err)
}
}
require(db.Model(&model.Product{}).Where("id = ?", productID).Update("public_id", pub).Error)
}
func addInventory(db *gorm.DB, shopID, warehouseID, productID uint64, qty float64) {
wid := warehouseID
pid := productID
if err := db.Create(&model.Inventory{
ShopID: shopID,
WarehouseID: &wid,
ProductID: &pid,
Quantity: qty,
Status: "on_sale", // 公开页 fixtures:三态改造后酒单只挂在售
}).Error; err != nil {
panic(err)
}
}
func TestPublicHandler_ListShopProducts_InStockOnly(t *testing.T) {
db := testutil.SetupTestDB()
shop := testutil.CreateTestShop(db, "PUB001")
wh := testutil.CreateTestWarehouse(db, shop.ID, "仓")
r := setupPublicRouter(db)
// A:有库存 5 → 应出现,quantity=5
pa := testutil.CreateTestProduct(db, shop.ID, "茅台A")
setPublicID(db, pa.ID, "pub-a")
addInventory(db, shop.ID, wh.ID, pa.ID, 5)
// B:有 public_id 但无库存 → 不出现
pb := testutil.CreateTestProduct(db, shop.ID, "五粮液B")
setPublicID(db, pb.ID, "pub-b")
// C:两条库存 3+2 → 出现,quantity=5
pc := testutil.CreateTestProduct(db, shop.ID, "汾酒C")
setPublicID(db, pc.ID, "pub-c")
addInventory(db, shop.ID, wh.ID, pc.ID, 3)
addInventory(db, shop.ID, wh.ID, pc.ID, 2)
// D:有库存但无 public_id → 不出现
pd := testutil.CreateTestProduct(db, shop.ID, "无公开D")
addInventory(db, shop.ID, wh.ID, pd.ID, 9)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/public/shops/PUB001/products", nil)
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
// 只 A、C 两个有库存 + 有 public_id
assert.Equal(t, float64(2), resp["total"])
data := resp["data"].([]interface{})
require.Len(t, data, 2)
byName := map[string]map[string]interface{}{}
for _, it := range data {
m := it.(map[string]interface{})
byName[m["name"].(string)] = m
}
require.Contains(t, byName, "茅台A")
require.Contains(t, byName, "汾酒C")
assert.NotContains(t, byName, "五粮液B") // 无库存
assert.NotContains(t, byName, "无公开D") // 无 public_id
// 数量正确(C 聚合 3+2=5
assert.Equal(t, float64(5), byName["茅台A"]["quantity"])
assert.Equal(t, float64(5), byName["汾酒C"]["quantity"])
// 带上了序列号 code
assert.Equal(t, "P-茅台A", byName["茅台A"]["code"])
}
func TestPublicHandler_ListShopProducts_Isolation(t *testing.T) {
db := testutil.SetupTestDB()
shopA := testutil.CreateTestShop(db, "PUBA")
shopB := testutil.CreateTestShop(db, "PUBB")
whB := testutil.CreateTestWarehouse(db, shopB.ID, "仓B")
r := setupPublicRouter(db)
// B 店有个有库存商品
pb := testutil.CreateTestProduct(db, shopB.ID, "他店商品")
setPublicID(db, pb.ID, "pub-x")
addInventory(db, shopB.ID, whB.ID, pb.ID, 7)
// 查 A 店:看不到 B 店商品
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/public/shops/"+shopA.Code+"/products", nil)
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, float64(0), resp["total"])
}