feat: 增加蝦皮店铺图片筛选 (#150)

This commit is contained in:
chengma
2026-08-11 12:04:38 +08:00
parent 2a3224b86d
commit c39f5a7912
9 changed files with 212 additions and 11 deletions
+28 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/gin-gonic/gin"
"cmautobuy/admin/model"
"cmautobuy/admin/service"
)
func listPostContext(t *testing.T, path string, values url.Values) (*gin.Context, *httptest.ResponseRecorder) {
@@ -42,11 +43,11 @@ func assertRedirectQuery(t *testing.T, recorder *httptest.ResponseRecorder, want
func Test列表写操作跳转保留筛选和页码(t *testing.T) {
t.Run("蝦皮", func(t *testing.T) {
context, recorder := listPostContext(t, "/shopee/save", url.Values{
"list_goods_id": {"1001"}, "status": {"no_link"}, "page": {"2"},
"list_goods_id": {"1001"}, "status": {"no_link"}, "shop": {"has"}, "image": {"missing"}, "page": {"2"},
})
(&Handler{}).shopeeRedirect(context, "完成")
assertRedirectQuery(t, recorder, "/shopee", map[string]string{
"goods_id": "1001", "status": "no_link", "page": "2", "msg": "完成",
"goods_id": "1001", "status": "no_link", "shop": "has", "image": "missing", "page": "2", "msg": "完成",
})
})
t.Run("顺运宝", func(t *testing.T) {
@@ -96,3 +97,28 @@ func Test列表写操作跳转保留筛选和页码(t *testing.T) {
})
})
}
func Test蝦皮分页详情和状态条保留资料筛选(t *testing.T) {
values := url.Values{
"goods_id": {"1001"},
"status": {"has_link"},
"shop": {"has"},
"image": {"missing"},
}
detail, err := url.Parse("/shopee/detail?" + detailValuesForShopee(values, 3))
if err != nil {
t.Fatal(err)
}
for key, want := range map[string]string{
"goods_id": "1001", "status": "has_link", "shop": "has", "image": "missing", "page": "3",
} {
if got := detail.Query().Get(key); got != want {
t.Errorf("详情参数 %s=%q,期望 %q", key, got, want)
}
}
result := &service.ShopeeListResult{Total: 7, Page: 1, TotalPages: 1, IsFiltered: true}
if got := shopeeStatusLine("", result); got != "筛选结果:7 个商品 · 第 1/1 页" {
t.Errorf("资料筛选状态条=%q", got)
}
}
+27 -4
View File
@@ -18,7 +18,7 @@ import (
// **商品级一行**(不是 SKU 级),数据来自 shopee_products 和 shopee_skus
// 聚合联查。列定义见 docs/admin/05-ui-specification.md §4.2、工单 #41。
func (h *Handler) ShopeeList(c *gin.Context) {
h.renderShopeeList(c, c.Query("goods_id"), c.Query("status"), c.Query("page"), c.Query("msg"))
h.renderShopeeList(c, c.Query("goods_id"), c.Query("status"), c.Query("shop"), c.Query("image"), c.Query("page"), c.Query("msg"))
}
// ShopeeDetail 渲染双击行弹出的那个弹窗的**内容**(不是整页)。
@@ -46,18 +46,22 @@ func (h *Handler) ShopeeDetail(c *gin.Context) {
c.HTML(http.StatusOK, "shopee/detail_modal", gin.H{
"D": detail, "CSRFToken": csrfToken(c),
"Keyword": c.Query("goods_id"), "StatusFilter": c.Query("status"),
"ShopFilter": service.ParseShopeePresence(c.Query("shop")),
"ImageFilter": service.ParseShopeePresence(c.Query("image")),
"CurrentPage": service.ParsePage(c.Query("page")),
})
}
// renderShopeeList 统一渲染蝦皮列表;数据写入改由商品目录接口负责。
//
// `[必须]` 分页控件用 <a href> 纯 GET 导航,翻页要保留 keyword/statusRaw,
// 所以这里把它们原样透传回模板去拼下一页的链接,不在这里丢掉(工单 #43)。
func (h *Handler) renderShopeeList(c *gin.Context, keyword, statusRaw, pageRaw, msg string) {
// `[必须]` 分页控件用 <a href> 纯 GET 导航,翻页要保留关键词和三个下拉筛选,
// 所以这里把归一后的有效值透传回模板去拼下一页的链接(工单 #43、#150)。
func (h *Handler) renderShopeeList(c *gin.Context, keyword, statusRaw, shopRaw, imageRaw, pageRaw, msg string) {
filter := repository.ShopeeFilter{
Keyword: keyword,
Status: service.ParseShopeeStatus(statusRaw),
Shop: service.ParseShopeePresence(shopRaw),
Image: service.ParseShopeePresence(imageRaw),
}
// 不叫 page:本文件末尾要调用同名的 page(c, ...) 渲染辅助函数,
// 局部变量会把它遮住导致编译失败。
@@ -82,16 +86,27 @@ func (h *Handler) renderShopeeList(c *gin.Context, keyword, statusRaw, pageRaw,
if filter.Status != "" {
values.Set("status", filter.Status)
}
if filter.Shop != "" {
values.Set("shop", filter.Shop)
}
if filter.Image != "" {
values.Set("image", filter.Image)
}
c.HTML(http.StatusOK, "shopee/list", page(c, "shopee", "蝦皮数据", gin.H{
"Keyword": keyword,
"StatusFilter": filter.Status,
"StatusOptions": service.ShopeeStatusOptions(),
"ShopFilter": filter.Shop,
"ShopOptions": service.ShopeePresenceOptions("有店铺", "无店铺"),
"ImageFilter": filter.Image,
"ImageOptions": service.ShopeePresenceOptions("有图片", "无图片"),
"Rows": result.Rows,
"Status": status,
"HasAnyProducts": result.HasAnyProducts,
"IsFiltered": result.IsFiltered,
"Pagination": service.NewPaginationView(result.Page, result.TotalPages, values.Encode()),
"CurrentPage": result.Page,
"DetailURL": "/shopee/detail?" + detailValuesForShopee(values, result.Page),
}))
}
@@ -114,6 +129,8 @@ func shopeeStatusLine(statusFilter string, result *service.ShopeeListResult) str
prefix := fmt.Sprintf("共 %d 个商品", result.Total)
if text := service.ShopeeStatusLabel(statusFilter); text != "" {
prefix = fmt.Sprintf("%s:%d 个商品", text, result.Total)
} else if result.IsFiltered {
prefix = fmt.Sprintf("筛选结果:%d 个商品", result.Total)
}
return fmt.Sprintf("%s · 第 %d/%d 页", prefix, result.Page, result.TotalPages)
}
@@ -165,6 +182,12 @@ func (h *Handler) shopeeRedirect(c *gin.Context, msg string) {
if value := strings.TrimSpace(c.PostForm("status")); value != "" {
params.Set("status", service.ParseShopeeStatus(value))
}
if value := service.ParseShopeePresence(c.PostForm("shop")); value != "" {
params.Set("shop", value)
}
if value := service.ParseShopeePresence(c.PostForm("image")); value != "" {
params.Set("image", value)
}
if value := strings.TrimSpace(c.PostForm("page")); value != "" {
params.Set("page", value)
}