Files
cmautobuy/admin/cmd/backfill-xlsx-pdd-prices/main.go
T

127 lines
4.2 KiB
Go
Raw Normal View History

// backfill-xlsx-pdd-prices 用三个固定蝦皮 Excel 的价格最大值回填关联 PDD 正式价格。
// 默认只预览;只有显式传入 --apply 才写数据库。
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"time"
"cmautobuy/admin/config"
"cmautobuy/admin/repository"
"cmautobuy/admin/service"
)
type backupFile struct {
Shop string `json:"shop"`
GeneratedAt string `json:"generated_at"`
Formula string `json:"formula"`
Files []string `json:"files"`
Items []backupItem `json:"items"`
}
type backupItem struct {
PDDGoodsID string `json:"pdd_goods_id"`
SourceMaxTWD string `json:"source_max_twd"`
PriceCent int64 `json:"price_cent"`
OriginalSKUsJSON string `json:"original_skus_json"`
OriginalCollectStatus string `json:"original_collect_status"`
OriginalCollectMsg *string `json:"original_collect_msg"`
OriginalArtifactRef *string `json:"original_artifact_ref"`
OriginalCollectedAt *string `json:"original_collected_at"`
OriginalUpdatedAt string `json:"original_updated_at"`
}
func main() {
dir := flag.String("dir", "../raw_data", "三个固定 Excel 所在目录")
apply := flag.Bool("apply", false, "实际写入;不传时只做 dry-run")
flag.Parse()
if flag.NArg() != 0 {
log.Fatal("不接受额外文件参数;只会读取 --dir 下三个固定文件名")
}
sources, err := service.LoadXLSXPriceSources(*dir)
if err != nil {
log.Fatalf("读取三个固定 Excel 失败: %v", err)
}
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()
targets, err := repository.ListShopPDDPriceBackfillTargets(db, service.XLSXPriceBackfillShop)
if err != nil {
log.Fatal(err)
}
plan := service.BuildXLSXPriceBackfillPlan(sources, targets, time.Now())
if !*apply {
printReport(plan, 0, "dry-run")
return
}
if len(plan.Candidates) == 0 {
printReport(plan, 0, "applied-noop")
return
}
backupPath, err := writeBackup(plan)
if err != nil {
log.Fatalf("创建本机回退备份失败,未写数据库: %v", err)
}
updated, err := service.ApplyXLSXPriceBackfill(db, plan, time.Now())
if err != nil {
log.Fatalf("PDD 正式价格回填失败: %v;本机备份位于 %s", err, backupPath)
}
printReport(plan, updated, "applied")
fmt.Printf("本机回退备份:%s\n", backupPath)
}
func printReport(plan service.XLSXPriceBackfillPlan, updated int, mode string) {
summary, _ := json.Marshal(plan.Summary)
skuCount := 0
for _, candidate := range plan.Candidates {
skuCount += candidate.SKUCount
}
fmt.Printf("模式:%s\n店铺:%s\nExcel 行:%d\n蝦皮商品:%d\nPDD 商品:%d\n共用 PDD 且最大价不同:%d\n候选 PDD:%d\n候选 SKU:%d\n本次更新 PDD:%d\n分类:%s\n",
mode, plan.Shop, plan.Sources.RowCount, plan.Sources.ShopeeProductCount,
plan.Sources.PDDProductCount, plan.Sources.SharedPDDWithDifferentMaxCount,
len(plan.Candidates), skuCount, updated, summary)
}
func writeBackup(plan service.XLSXPriceBackfillPlan) (string, error) {
file, err := os.CreateTemp("", "cmautobuy_xlsx_price_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),
Formula: "max_twd / 10 * 100 = cny_cent", Files: append([]string(nil), service.XLSXPriceBackfillFiles...),
}
for _, candidate := range plan.Candidates {
original := candidate.Original
backup.Items = append(backup.Items, backupItem{
PDDGoodsID: candidate.PDDGoodsID, SourceMaxTWD: candidate.SourceMaxTWD,
PriceCent: candidate.PriceCent, OriginalSKUsJSON: original.SKUsJSON,
OriginalCollectStatus: original.CollectStatus, OriginalCollectMsg: original.CollectMsg,
OriginalArtifactRef: original.ArtifactRef, OriginalCollectedAt: original.CollectedAt,
OriginalUpdatedAt: original.UpdatedAt,
})
}
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(backup); err != nil {
return "", err
}
return path, nil
}