feat: 保存并展示 PDD 店铺与价格采样信息 (#31)

This commit is contained in:
chengma
2026-08-07 17:13:25 +08:00
parent a2fc1501c4
commit 93beef8b36
17 changed files with 492 additions and 73 deletions
+38 -4
View File
@@ -227,6 +227,7 @@ type PddProductView struct {
GoodsID string
URL string
Title string // 未采集时是占位符
ShopName string // 未采到时是占位符
StatusText string
SkuCountText string // 未采集时是占位符
CollectedAt string
@@ -269,6 +270,7 @@ func ListPddProducts(db *sql.DB, keyword string, status model.CollectStatus) (*P
GoodsID: r.GoodsID,
URL: r.URL,
Title: r.Title,
ShopName: r.ShopName,
StatusText: statusTextFor(r.PddProduct),
CollectedAt: formatLocalTime(r.CollectedAt),
UpdatedAt: formatLocalTime(r.UpdatedAt),
@@ -278,6 +280,9 @@ func ListPddProducts(db *sql.DB, keyword string, status model.CollectStatus) (*P
if v.Title == "" {
v.Title = "(未采集,采集后自动回填)"
}
if v.ShopName == "" {
v.ShopName = placeholder
}
// -1 是"还没采过"。注意 0 要如实显示 0:
// 采到 0 个规格说明采集出了问题,显示成 — 就看不出来了。
if r.SkuCount < 0 {
@@ -318,7 +323,9 @@ func (r *PddListResult) StatusLine() string {
type PddSkuView struct {
Options []string // 按 DimensionNames 的顺序排好,模板直接 range
PriceText string
Available string
// PriceSource 在按颜色采样时显示“✓ 实测”或“推断”,不能只靠颜色区分。
PriceSource string
Available string
}
// PddProductDetail 是双击弹窗要显示的全部内容。
@@ -327,15 +334,17 @@ type PddProductDetail struct {
GoodsID string
URL string
Title string
ShopName string
StatusText string
CollectMsg string
CollectedAt string
ArtifactRef string
// Collected 为 false 时模板显示"尚未采集",而不是一张空表。
Collected bool
DimensionNames []string
SKUs []PddSkuView
Collected bool
ShowPriceSource bool
DimensionNames []string
SKUs []PddSkuView
// SkusError 非空表示 skus_json 存的东西解析不了。
// 这种情况要如实说出来,不能装作"没有规格"——
@@ -356,6 +365,7 @@ func GetPddProductDetail(db *sql.DB, id int64) (*PddProductDetail, error) {
GoodsID: p.GoodsID,
URL: p.URL,
Title: p.Title,
ShopName: p.ShopName,
StatusText: statusTextFor(*p),
CollectMsg: p.CollectMsg,
CollectedAt: formatLocalTime(p.CollectedAt),
@@ -364,6 +374,9 @@ func GetPddProductDetail(db *sql.DB, id int64) (*PddProductDetail, error) {
if d.Title == "" {
d.Title = placeholder
}
if d.ShopName == "" {
d.ShopName = placeholder
}
if d.CollectMsg == "" {
d.CollectMsg = placeholder
}
@@ -379,6 +392,7 @@ func GetPddProductDetail(db *sql.DB, id int64) (*PddProductDetail, error) {
}
d.Collected = true
d.ShowPriceSource = collected.PriceGranularity == "color"
keys, names := dimensionOrder(collected)
d.DimensionNames = names
@@ -391,6 +405,12 @@ func GetPddProductDetail(db *sql.DB, id int64) (*PddProductDetail, error) {
if sku.Available {
row.Available = "是"
}
if d.ShowPriceSource {
row.PriceSource = "推断"
if sameOptions(sku.Options, sku.PriceObservedAt) {
row.PriceSource = "✓ 实测"
}
}
for _, k := range keys {
value := sku.Options[k]
if value == "" {
@@ -403,6 +423,20 @@ func GetPddProductDetail(db *sql.DB, id int64) (*PddProductDetail, error) {
return d, nil
}
// sameOptions 判断价格读取时实际选中的组合是否就是当前这一行。
// 两个 map 必须键和值都完全一致,避免只比较颜色后把多个尺码都标成实测。
func sameOptions(options, observed map[string]string) bool {
if len(options) == 0 || len(options) != len(observed) {
return false
}
for key, value := range options {
if observed[key] != value {
return false
}
}
return true
}
// dimensionOrder 定下规格各维度的显示顺序,返回 (取值用的 key, 表头文字)。
//
// 优先用采集结果里的 dimensions;它缺失时退回"把所有 SKU 出现过的 key 排序"。