+28
-46
@@ -111,16 +111,15 @@ func UpsertShopeeSKU(q Execer, skuID, goodsID, specRaw, color, size, advice stri
|
||||
// NULL 表示这个蝦皮商品还没填 PDD 链接(LEFT JOIN 没查到对应行)。
|
||||
type ShopeeProductRow struct {
|
||||
model.ShopeeProduct
|
||||
BusinessShopName string
|
||||
ColorCount int
|
||||
SizeCount int
|
||||
SKUCount int
|
||||
PendingCount int
|
||||
CollectStatus sql.NullString
|
||||
CollectMsg sql.NullString
|
||||
ColorCount int
|
||||
SizeCount int
|
||||
SKUCount int
|
||||
PendingCount int
|
||||
CollectStatus sql.NullString
|
||||
CollectMsg sql.NullString
|
||||
}
|
||||
|
||||
// ShopeeFilter 是蝦皮商品列表页支持的筛选条件,四项都可以为空。
|
||||
// ShopeeFilter 是蝦皮商品列表页支持的筛选和内部范围条件。
|
||||
//
|
||||
// Status 取值见 §「状态筛选的四个取值」(工单 #43):
|
||||
//
|
||||
@@ -132,16 +131,14 @@ type ShopeeProductRow struct {
|
||||
// 认不出的取值一律当作 ""(不筛选),由 service 层的 ParseShopeeStatus 兜底,
|
||||
// 这里不做校验——repository 只管拼 SQL。
|
||||
type ShopeeFilter struct {
|
||||
Keyword string
|
||||
ShopName string
|
||||
Status string
|
||||
Shop string // has / missing / 空(全部)
|
||||
Image string // has / missing / 空(全部)
|
||||
StoreID string // 业务店铺 ID / unlinked / 空(全部)
|
||||
Deleted bool // true 只看软删除数据;false 只看正常数据
|
||||
Keyword string
|
||||
SearchField string // goods_id / title / shop_name
|
||||
Status string
|
||||
Deleted bool // true 只看软删除数据;false 只看正常数据
|
||||
Unlinked bool // true 只看尚未登记到店铺管理的正常商品
|
||||
}
|
||||
|
||||
// shopeeFilterClause 把关键字、状态、店铺和图片筛选拼成 WHERE 子句,供 ListShopeeProducts
|
||||
// shopeeFilterClause 把分类关键词、状态和内部范围拼成 WHERE 子句,供 ListShopeeProducts
|
||||
// 和 CountShopeeProductsFiltered 共用——两处筛选逻辑必须完全一致,
|
||||
// 否则底部统计会跟表格对不上(#19 踩过一次,见工单 #43)。
|
||||
//
|
||||
@@ -156,13 +153,17 @@ func shopeeFilterClause(filter ShopeeFilter) (string, []any) {
|
||||
var args []any
|
||||
|
||||
if kw := strings.TrimSpace(filter.Keyword); kw != "" {
|
||||
like := "%" + escapeLike(kw) + "%"
|
||||
clauses = append(clauses, `(sp.goods_id LIKE ? ESCAPE '!' OR sp.title LIKE ? ESCAPE '!')`)
|
||||
args = append(args, like, like)
|
||||
}
|
||||
if shopName := strings.TrimSpace(filter.ShopName); shopName != "" {
|
||||
clauses = append(clauses, `sp.shopee_shop_name LIKE ? ESCAPE '!'`)
|
||||
args = append(args, "%"+escapeLike(shopName)+"%")
|
||||
switch filter.SearchField {
|
||||
case "title":
|
||||
clauses = append(clauses, `sp.title LIKE ? ESCAPE '!'`)
|
||||
args = append(args, "%"+escapeLike(kw)+"%")
|
||||
case "shop_name":
|
||||
clauses = append(clauses, `sp.shopee_shop_name LIKE ? ESCAPE '!'`)
|
||||
args = append(args, "%"+escapeLike(kw)+"%")
|
||||
default:
|
||||
clauses = append(clauses, `sp.goods_id = ?`)
|
||||
args = append(args, kw)
|
||||
}
|
||||
}
|
||||
|
||||
switch filter.Status {
|
||||
@@ -175,26 +176,8 @@ func shopeeFilterClause(filter ShopeeFilter) (string, []any) {
|
||||
clauses = append(clauses, `(sp.pdd_goods_url IS NOT NULL AND sp.pdd_goods_url <> '')`)
|
||||
}
|
||||
|
||||
switch filter.Shop {
|
||||
case "has":
|
||||
clauses = append(clauses, `(sp.shopee_shop_name IS NOT NULL AND TRIM(sp.shopee_shop_name) <> '')`)
|
||||
case "missing":
|
||||
clauses = append(clauses, `(sp.shopee_shop_name IS NULL OR TRIM(sp.shopee_shop_name) = '')`)
|
||||
}
|
||||
|
||||
switch filter.Image {
|
||||
case "has":
|
||||
clauses = append(clauses, `(sp.image_url IS NOT NULL AND TRIM(sp.image_url) <> '')`)
|
||||
case "missing":
|
||||
clauses = append(clauses, `(sp.image_url IS NULL OR TRIM(sp.image_url) = '')`)
|
||||
}
|
||||
switch strings.TrimSpace(filter.StoreID) {
|
||||
case "":
|
||||
case "unlinked":
|
||||
if filter.Unlinked {
|
||||
clauses = append(clauses, `sp.shop_id IS NULL`)
|
||||
default:
|
||||
clauses = append(clauses, `sp.shop_id=?`)
|
||||
args = append(args, strings.TrimSpace(filter.StoreID))
|
||||
}
|
||||
|
||||
return " WHERE " + strings.Join(clauses, " AND "), args
|
||||
@@ -202,7 +185,7 @@ func shopeeFilterClause(filter ShopeeFilter) (string, []any) {
|
||||
|
||||
// ListShopeeProducts 按筛选条件分页查蝦皮商品列表(商品级一行),联查规格聚合数和采集状态。
|
||||
//
|
||||
// keyword 匹配商品 ID 或商品名称,**不匹配颜色/尺码**——
|
||||
// keyword 只匹配 SearchField 指定的商品 ID、商品名称或店铺名,**不匹配颜色/尺码**——
|
||||
// 那是 SKU 级信息,商品级列表里搜出来没法定位到具体是哪个 SKU(见 #41)。
|
||||
//
|
||||
// `[必须]` 分页用 LIMIT/OFFSET 在数据库里做,不把全量查出来在 Go 里切片
|
||||
@@ -210,7 +193,7 @@ func shopeeFilterClause(filter ShopeeFilter) (string, []any) {
|
||||
func ListShopeeProducts(q Execer, filter ShopeeFilter, limit, offset int) ([]ShopeeProductRow, error) {
|
||||
where, args := shopeeFilterClause(filter)
|
||||
sqlText := `
|
||||
SELECT sp.goods_id,sp.shop_id,COALESCE(MAX(bs.display_name),''), sp.title, sp.shopee_status, sp.main_sku_code, sp.image_url,sp.shopee_shop_name,sp.image_source,sp.image_observed_at,sp.image_is_manual,sp.shop_name_source,sp.shop_name_observed_at,sp.shop_name_is_manual,sp.source,
|
||||
SELECT sp.goods_id,sp.shop_id,sp.title,sp.shopee_status,sp.main_sku_code,sp.image_url,sp.shopee_shop_name,sp.image_source,sp.image_observed_at,sp.image_is_manual,sp.shop_name_source,sp.shop_name_observed_at,sp.shop_name_is_manual,sp.source,
|
||||
sp.pdd_goods_url, sp.pdd_goods_id,sp.deleted_at,sp.deleted_by_user_id, sp.created_at, sp.updated_at,
|
||||
COUNT(DISTINCT CASE WHEN sk.parse_ok = 1 THEN sk.color END) AS color_count,
|
||||
COUNT(DISTINCT CASE WHEN sk.parse_ok = 1 THEN sk.size END) AS size_count,
|
||||
@@ -218,7 +201,6 @@ func ListShopeeProducts(q Execer, filter ShopeeFilter, limit, offset int) ([]Sho
|
||||
COUNT(CASE WHEN sk.parse_ok = 0 THEN 1 END) AS pending_count,
|
||||
pp.collect_status, pp.collect_msg
|
||||
FROM shopee_products sp
|
||||
LEFT JOIN shops bs ON bs.shop_id=sp.shop_id
|
||||
LEFT JOIN shopee_skus sk ON sk.goods_id = sp.goods_id
|
||||
LEFT JOIN pdd_products pp
|
||||
ON pp.goods_id = sp.pdd_goods_id AND pp.deleted_at IS NULL` +
|
||||
@@ -237,7 +219,7 @@ func ListShopeeProducts(q Execer, filter ShopeeFilter, limit, offset int) ([]Sho
|
||||
var shopID, title, shopeeStatus, mainSKUCode, imageURL, shopName, imageSource, imageObserved, shopSource, shopObserved, source, pddGoodsURL, pddGoodsID, deletedAt, deletedBy sql.NullString
|
||||
var imageManual, shopManual int
|
||||
if err := rows.Scan(
|
||||
&r.GoodsID, &shopID, &r.BusinessShopName, &title, &shopeeStatus, &mainSKUCode, &imageURL, &shopName, &imageSource, &imageObserved, &imageManual, &shopSource, &shopObserved, &shopManual, &source,
|
||||
&r.GoodsID, &shopID, &title, &shopeeStatus, &mainSKUCode, &imageURL, &shopName, &imageSource, &imageObserved, &imageManual, &shopSource, &shopObserved, &shopManual, &source,
|
||||
&pddGoodsURL, &pddGoodsID, &deletedAt, &deletedBy, &r.CreatedAt, &r.UpdatedAt,
|
||||
&r.ColorCount, &r.SizeCount, &r.SKUCount, &r.PendingCount,
|
||||
&r.CollectStatus, &r.CollectMsg,
|
||||
|
||||
Reference in New Issue
Block a user