feat: 展示顺运宝观测规格 (#140)
This commit is contained in:
+52
-1
@@ -259,7 +259,15 @@ func UpsertSybOrder(q Execer, o model.SybOrder) (created bool, err error) {
|
||||
if _, err := q.Exec(`
|
||||
INSERT INTO shopee_products (goods_id, title, source, created_at, updated_at)
|
||||
VALUES (?, ?, 'syb', ?, ?)
|
||||
ON DUPLICATE KEY UPDATE goods_id=VALUES(goods_id)`,
|
||||
ON DUPLICATE KEY UPDATE
|
||||
title = CASE
|
||||
WHEN source = 'syb' AND TRIM(VALUES(title)) <> '' THEN VALUES(title)
|
||||
ELSE title
|
||||
END,
|
||||
updated_at = CASE
|
||||
WHEN source = 'syb' AND TRIM(VALUES(title)) <> '' THEN VALUES(updated_at)
|
||||
ELSE updated_at
|
||||
END`,
|
||||
o.ShopeeGoodsID, o.Title, now, now); err != nil {
|
||||
return false, fmt.Errorf("为顺运宝明细 %s 补建蝦皮商品骨架失败: %w", o.SybID, err)
|
||||
}
|
||||
@@ -289,6 +297,49 @@ func UpsertSybOrder(q Execer, o model.SybOrder) (created bool, err error) {
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// SybSpecObservation 是顺运宝对某个蝦皮商品规格的历史观测汇总。
|
||||
// 它不是正式蝦皮 SKU,不得写回 shopee_skus。
|
||||
type SybSpecObservation struct {
|
||||
SpecKey string
|
||||
SpecRaw string
|
||||
LatestPriceCent int64
|
||||
LatestImageURL string
|
||||
OrderCount int
|
||||
TotalQuantity int
|
||||
LastObservedAt string
|
||||
}
|
||||
|
||||
// ListSybSpecObservations 按规格身份汇总货运单明细;同一 syb_id 重复同步仍只有一行。
|
||||
func ListSybSpecObservations(q Execer, goodsID string) ([]SybSpecObservation, error) {
|
||||
rows, err := q.Query(`WITH ranked AS (
|
||||
SELECT spec_key,product_spec,price_twd_cent,image_url,quantity,created_at,syb_id,
|
||||
ROW_NUMBER() OVER(PARTITION BY spec_key ORDER BY created_at DESC,syb_id DESC) AS row_num,
|
||||
COUNT(*) OVER(PARTITION BY spec_key) AS order_count,
|
||||
SUM(quantity) OVER(PARTITION BY spec_key) AS total_quantity,
|
||||
MAX(created_at) OVER(PARTITION BY spec_key) AS last_observed_at
|
||||
FROM syb_orders
|
||||
WHERE shopee_goods_id=? AND spec_key IS NOT NULL AND spec_key<>''
|
||||
)
|
||||
SELECT spec_key,product_spec,price_twd_cent,image_url,order_count,total_quantity,last_observed_at
|
||||
FROM ranked WHERE row_num=1 ORDER BY last_observed_at DESC,spec_key`, goodsID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询蝦皮商品 %s 的顺运宝观测规格失败: %w", goodsID, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var list []SybSpecObservation
|
||||
for rows.Next() {
|
||||
var item SybSpecObservation
|
||||
var raw, image sql.NullString
|
||||
if err := rows.Scan(&item.SpecKey, &raw, &item.LatestPriceCent, &image, &item.OrderCount, &item.TotalQuantity, &item.LastObservedAt); err != nil {
|
||||
return nil, fmt.Errorf("读取顺运宝观测规格失败: %w", err)
|
||||
}
|
||||
item.SpecRaw = raw.String
|
||||
item.LatestImageURL = image.String
|
||||
list = append(list, item)
|
||||
}
|
||||
return list, rows.Err()
|
||||
}
|
||||
|
||||
// nullableText 把空字符串转成 SQL NULL,非空字符串原样写入。
|
||||
// syb_orders 的这几列在建表语句里都允许 NULL,空字符串和 NULL
|
||||
// 在页面上显示效果一样,统一存 NULL 更符合"这个字段还没有值"的语义。
|
||||
|
||||
Reference in New Issue
Block a user