Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
This commit is contained in:
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
@@ -24,7 +25,12 @@ func NewProductOptionHandler(db *gorm.DB) *ProductOptionHandler {
|
||||
func (h *ProductOptionHandler) ListNames(c *gin.Context) {
|
||||
shopID := middleware.GetShopID(c)
|
||||
items := make([]model.ProductNameOption, 0)
|
||||
h.db.Where("shop_id = ?", shopID).Order("id ASC").Find(&items)
|
||||
q := h.db.Where("shop_id = ?", shopID)
|
||||
if kw := strings.TrimSpace(c.Query("keyword")); kw != "" {
|
||||
like := "%" + kw + "%"
|
||||
q = q.Where("name LIKE ? OR code LIKE ?", like, like)
|
||||
}
|
||||
q.Order("id ASC").Find(&items)
|
||||
util.RespondSuccess(c, items)
|
||||
}
|
||||
|
||||
@@ -85,7 +91,12 @@ func (h *ProductOptionHandler) DeleteName(c *gin.Context) {
|
||||
func (h *ProductOptionHandler) ListSeries(c *gin.Context) {
|
||||
shopID := middleware.GetShopID(c)
|
||||
items := make([]model.ProductSeriesOption, 0)
|
||||
h.db.Where("shop_id = ?", shopID).Order("id ASC").Find(&items)
|
||||
q := h.db.Where("shop_id = ?", shopID)
|
||||
if kw := strings.TrimSpace(c.Query("keyword")); kw != "" {
|
||||
like := "%" + kw + "%"
|
||||
q = q.Where("name LIKE ? OR code LIKE ?", like, like)
|
||||
}
|
||||
q.Order("id ASC").Find(&items)
|
||||
util.RespondSuccess(c, items)
|
||||
}
|
||||
|
||||
@@ -146,7 +157,12 @@ func (h *ProductOptionHandler) DeleteSeries(c *gin.Context) {
|
||||
func (h *ProductOptionHandler) ListSpecs(c *gin.Context) {
|
||||
shopID := middleware.GetShopID(c)
|
||||
items := make([]model.ProductSpecOption, 0)
|
||||
h.db.Where("shop_id = ?", shopID).Order("id ASC").Find(&items)
|
||||
q := h.db.Where("shop_id = ?", shopID)
|
||||
if kw := strings.TrimSpace(c.Query("keyword")); kw != "" {
|
||||
like := "%" + kw + "%"
|
||||
q = q.Where("name LIKE ? OR code LIKE ?", like, like)
|
||||
}
|
||||
q.Order("id ASC").Find(&items)
|
||||
util.RespondSuccess(c, items)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/wangjia/jiu/backend/internal/middleware"
|
||||
"github.com/wangjia/jiu/backend/internal/model"
|
||||
"github.com/wangjia/jiu/backend/testutil"
|
||||
)
|
||||
|
||||
// setupOptionRouter 注册 product-options 名称路由(含 JWT)。
|
||||
func setupOptionRouter(db *gorm.DB) *gin.Engine {
|
||||
h := NewProductOptionHandler(db)
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
api := r.Group("/api/v1")
|
||||
api.Use(middleware.JWT(db))
|
||||
names := api.Group("/product-options/names")
|
||||
names.GET("", h.ListNames)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestProductOptionHandler_ListNamesKeyword(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "OPT001")
|
||||
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
||||
token := getAuthToken(user.ID, shop.ID, "admin")
|
||||
r := setupOptionRouter(db)
|
||||
|
||||
db.Create(&model.ProductNameOption{
|
||||
TenantBase: model.TenantBase{ShopID: shop.ID}, Code: "P001", Name: "茅台",
|
||||
})
|
||||
db.Create(&model.ProductNameOption{
|
||||
TenantBase: model.TenantBase{ShopID: shop.ID}, Code: "P002", Name: "五粮液",
|
||||
})
|
||||
|
||||
// 1. 无 keyword → 返回全部
|
||||
w := makeRequest(r, "GET", "/api/v1/product-options/names", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data := parseResponse(w)["data"].([]interface{})
|
||||
assert.Len(t, data, 2)
|
||||
|
||||
// 2. keyword 命中名称
|
||||
w = makeRequest(r, "GET", "/api/v1/product-options/names?keyword=五粮", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data = parseResponse(w)["data"].([]interface{})
|
||||
assert.Len(t, data, 1)
|
||||
assert.Equal(t, "五粮液", data[0].(map[string]interface{})["name"])
|
||||
|
||||
// 3. keyword 命中编码
|
||||
w = makeRequest(r, "GET", "/api/v1/product-options/names?keyword=P001", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data = parseResponse(w)["data"].([]interface{})
|
||||
assert.Len(t, data, 1)
|
||||
assert.Equal(t, "茅台", data[0].(map[string]interface{})["name"])
|
||||
|
||||
// 4. keyword 无命中 → 空
|
||||
w = makeRequest(r, "GET", "/api/v1/product-options/names?keyword=不存在", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data = parseResponse(w)["data"].([]interface{})
|
||||
assert.Len(t, data, 0)
|
||||
}
|
||||
|
||||
func TestProductOptionHandler_ListNamesIsolation(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shopA := testutil.CreateTestShop(db, "OPT_A")
|
||||
userA := testutil.CreateTestUser(db, shopA.ID, "adminA", "pass", "admin")
|
||||
tokenA := getAuthToken(userA.ID, shopA.ID, "admin")
|
||||
shopB := testutil.CreateTestShop(db, "OPT_B")
|
||||
r := setupOptionRouter(db)
|
||||
|
||||
db.Create(&model.ProductNameOption{
|
||||
TenantBase: model.TenantBase{ShopID: shopB.ID}, Name: "他店商品",
|
||||
})
|
||||
|
||||
// A 店即使 keyword 命中 B 店数据也查不到(shop_id 隔离)
|
||||
w := makeRequest(r, "GET", "/api/v1/product-options/names?keyword=他店", tokenA, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data := parseResponse(w)["data"].([]interface{})
|
||||
assert.Len(t, data, 0)
|
||||
}
|
||||
@@ -241,6 +241,37 @@ func SetupTestDB() *gorm.DB {
|
||||
content TEXT,
|
||||
remark TEXT
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS product_name_options (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
shop_id INTEGER NOT NULL,
|
||||
code TEXT,
|
||||
name TEXT NOT NULL,
|
||||
remark TEXT
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS product_series_options (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
shop_id INTEGER NOT NULL,
|
||||
code TEXT,
|
||||
name TEXT NOT NULL,
|
||||
remark TEXT
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS product_spec_options (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME,
|
||||
shop_id INTEGER NOT NULL,
|
||||
code TEXT,
|
||||
name TEXT NOT NULL,
|
||||
quantity INTEGER DEFAULT 0,
|
||||
remark TEXT
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS warehouses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at DATETIME,
|
||||
|
||||
Reference in New Issue
Block a user