feat: 平台管理员可在前端分配商品给商户

后端:
- service: 新增 ListProductCatalog(自营商户商品目录)、ListMerchantProductsByAdmin、AssignProducts(按目录ID批量同步商户商品,未勾选的已有商品会被移除)
- handler + router: 新增 GET /platform/product-catalog、GET /platform/merchants/:id/products、POST /platform/merchants/:id/products/assign 三个管理员路由

前端:
- types: 新增 ProductCatalogItem 类型
- api: platformApi 新增 productCatalog / merchantProducts / assignProducts
- PlatformMerchants: 商户列表操作列新增「分配商品」按钮,弹窗内以表格+复选框展示平台商品目录,支持全选/清空/重置,打开时按已有商品预选,保存后同步分配结果

实测:创建商户自动获得27个默认商品,管理员可随时增减调整
This commit is contained in:
yml2213
2026-07-30 15:20:02 +08:00
parent 7cff2dffed
commit 502b719f48
6 changed files with 376 additions and 9 deletions
+43
View File
@@ -329,6 +329,49 @@ func (h *MerchantHandler) ListPlatformMerchants(c *gin.Context) {
response.Page(c, list, total, page, size)
}
// ListProductCatalog 返回自营商户的全部可售商品,作为平台默认商品目录供分配。
func (h *MerchantHandler) ListProductCatalog(c *gin.Context) {
list, err := h.merchantSvc.ListProductCatalog()
if err != nil {
response.ServerError(c, err.Error())
return
}
response.OK(c, list)
}
// ListMerchantProductsByAdmin 供平台管理员查看指定商户的可售商品。
func (h *MerchantHandler) ListMerchantProductsByAdmin(c *gin.Context) {
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
list, err := h.merchantSvc.ListMerchantProductsByAdmin(uint(id))
if err != nil {
response.ServerError(c, err.Error())
return
}
response.OK(c, list)
}
type assignProductsReq struct {
CatalogIDs []uint `json:"catalog_ids"`
}
// AssignProducts 按商品目录 ID 批量同步商户的可售商品。
func (h *MerchantHandler) AssignProducts(c *gin.Context) {
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
var req assignProductsReq
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
count, err := h.merchantSvc.AssignProducts(uint(id), service.AssignProductsInput{
CatalogIDs: req.CatalogIDs,
}, middleware.GetUserID(c))
if err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"assigned": count})
}
type createMerchantReq struct {
Code string `json:"code" binding:"required"`
Name string `json:"name" binding:"required"`