package repository import ( "database/sql" "fmt" "strings" ) // PDDPriceBackfillTarget 是一次性 Excel 价格回填需要的 PDD 原值。 // 原值同时用于 dry-run、乐观锁和本机回退备份。 type PDDPriceBackfillTarget struct { GoodsID string SKUsJSON string CollectStatus string CollectMsg *string ArtifactRef *string CollectedAt *string UpdatedAt string } // ListShopPDDPriceBackfillTargets 读取指定蝦皮店铺当前关联的活动 PDD 商品。 // 同一个 PDD 被多个蝦皮商品关联时只返回一份原值。 func ListShopPDDPriceBackfillTargets(q Execer, shopName string) ([]PDDPriceBackfillTarget, error) { rows, err := q.Query(` SELECT DISTINCT pp.goods_id, COALESCE(pp.skus_json, ''), pp.collect_status, pp.collect_msg, pp.artifact_ref, pp.collected_at, pp.updated_at FROM shopee_products sp JOIN pdd_products pp ON pp.goods_id = sp.pdd_goods_id WHERE sp.deleted_at IS NULL AND pp.deleted_at IS NULL AND sp.shopee_shop_name = ? ORDER BY pp.goods_id`, strings.TrimSpace(shopName)) if err != nil { return nil, fmt.Errorf("读取店铺 PDD 价格回填目标失败: %w", err) } defer rows.Close() var targets []PDDPriceBackfillTarget for rows.Next() { var target PDDPriceBackfillTarget var collectMsg, artifactRef, collectedAt sql.NullString if err := rows.Scan(&target.GoodsID, &target.SKUsJSON, &target.CollectStatus, &collectMsg, &artifactRef, &collectedAt, &target.UpdatedAt); err != nil { return nil, fmt.Errorf("解析 PDD 价格回填目标失败: %w", err) } target.CollectMsg = nullableStringPointer(collectMsg) target.ArtifactRef = nullableStringPointer(artifactRef) target.CollectedAt = nullableStringPointer(collectedAt) targets = append(targets, target) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("遍历 PDD 价格回填目标失败: %w", err) } return targets, nil } func nullableStringPointer(value sql.NullString) *string { if !value.Valid { return nil } result := value.String return &result } // ReplacePDDPricesAndMarkCollected 使用完整原值作乐观锁,写入价格并标记已采集。 // 返回 false 表示 dry-run 后目标发生变化,调用方必须回滚整批,不能部分提交。 func ReplacePDDPricesAndMarkCollected(q Execer, target PDDPriceBackfillTarget, newSKUsJSON, collectedAt, updatedAt string) (bool, error) { result, err := q.Exec(` UPDATE pdd_products SET skus_json = ?, collect_status = 'collected', collect_msg = NULL, artifact_ref = NULL, collected_at = ?, updated_at = ? WHERE goods_id = ? AND deleted_at IS NULL AND COALESCE(skus_json, '') = ? AND collect_status = ? AND updated_at = ?`, newSKUsJSON, collectedAt, updatedAt, target.GoodsID, target.SKUsJSON, target.CollectStatus, target.UpdatedAt) if err != nil { return false, fmt.Errorf("更新 PDD 商品 %s 的正式价格失败: %w", target.GoodsID, err) } rows, err := result.RowsAffected() if err != nil { return false, fmt.Errorf("读取 PDD 商品 %s 的价格回填结果失败: %w", target.GoodsID, err) } return rows == 1, nil }