127 lines
4.2 KiB
Go
127 lines
4.2 KiB
Go
// 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
|
||
}
|