feat: 回填店铺关联PDD空规格 (#194)

This commit is contained in:
chengma
2026-08-12 18:31:14 +08:00
parent 13105faf1f
commit 8f7fb098ad
5 changed files with 595 additions and 24 deletions
+97
View File
@@ -0,0 +1,97 @@
// backfill-shop-pdd-specs 把指定蝦皮店铺的正式规格补成关联 PDD 的空规格骨架。
// 默认只预览;只有显式传入 --apply 才写数据库。
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"strings"
"time"
"cmautobuy/admin/config"
"cmautobuy/admin/repository"
"cmautobuy/admin/service"
)
type backupFile struct {
Shop string `json:"shop"`
GeneratedAt string `json:"generated_at"`
Items []backupItem `json:"items"`
}
type backupItem struct {
PDDGoodsID string `json:"pdd_goods_id"`
SKUsJSON string `json:"skus_json"`
}
func main() {
shop := flag.String("shop", "", "要处理的蝦皮店铺名(精确匹配,必填)")
apply := flag.Bool("apply", false, "实际写入;不传时只做 dry-run")
flag.Parse()
if strings.TrimSpace(*shop) == "" {
log.Fatal("必须通过 --shop 指定店铺名")
}
cfg, err := config.LoadDatabase()
if err != nil {
log.Fatalf("读取 MySQL 配置失败: %v", err)
}
db, err := repository.OpenMySQL(cfg)
if err != nil {
log.Fatalf("连接 MySQL 失败: %v", err)
}
defer db.Close()
sources, err := repository.ListShopSpecSources(db, *shop)
if err != nil {
log.Fatal(err)
}
now := time.Now()
plan := service.BuildSpecBackfillPlan(*shop, sources, now)
if !*apply {
printReport(plan, 0, "dry-run")
return
}
backupPath, err := writeBackup(plan)
if err != nil {
log.Fatalf("创建本机回退备份失败,未写数据库: %v", err)
}
updated, err := service.ApplySpecBackfill(db, plan, now)
if err != nil {
log.Fatalf("规格回填失败: %v;本机备份位于 %s", err, backupPath)
}
printReport(plan, updated, "applied")
fmt.Printf("本机回退备份:%s\n", backupPath)
}
func printReport(plan service.SpecBackfillPlan, updated int, mode string) {
summary, _ := json.Marshal(plan.Summary)
fmt.Printf("模式:%s\n店铺:%s\n蝦皮商品:%d\n候选 PDD:%d\n已回填 PDD:%d\n本次更新 PDD:%d\n分类:%s\n",
mode, plan.Shop, plan.SourceProductCount, plan.CandidatePDDCount,
plan.BackfilledPDDCount, updated, summary)
}
func writeBackup(plan service.SpecBackfillPlan) (string, error) {
file, err := os.CreateTemp("", "cmautobuy_spec_backfill_backup_*.json")
if err != nil {
return "", err
}
path := file.Name()
defer file.Close()
if err := file.Chmod(0o600); err != nil {
return "", err
}
backup := backupFile{Shop: plan.Shop, GeneratedAt: time.Now().UTC().Format(time.RFC3339)}
for _, candidate := range plan.Candidates {
backup.Items = append(backup.Items, backupItem{PDDGoodsID: candidate.PDDGoodsID, SKUsJSON: candidate.OriginalSKUsJSON})
}
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(backup); err != nil {
return "", err
}
return path, nil
}