feat: 展示顺运宝观测规格 (#140)

This commit is contained in:
chengma
2026-08-11 10:46:11 +08:00
parent 9181329917
commit c05c4fafdb
8 changed files with 199 additions and 3 deletions
+52 -1
View File
@@ -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 更符合"这个字段还没有值"的语义。