feat: enhance tariff processing with error handling and category filtering
This commit is contained in:
@@ -3,9 +3,13 @@ package products
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"net/http"
|
||||||
"sipro-mps/internal/redis"
|
"sipro-mps/internal/redis"
|
||||||
|
"sipro-mps/pkg/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pb "sipro-mps/api/generated/v1/yandexmarket/products"
|
pb "sipro-mps/api/generated/v1/yandexmarket/products"
|
||||||
@@ -15,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"git.denco.store/fakz9/yandex-go-client"
|
"git.denco.store/fakz9/yandex-go-client"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -246,46 +251,90 @@ func (r *apiRepository) CalculateProductTariffs(ctx context.Context, marketplace
|
|||||||
func (r *apiRepository) setTariffsRateLimit() {
|
func (r *apiRepository) setTariffsRateLimit() {
|
||||||
ym.SetPathLimit("/tariffs/calculate", rateLimitWindow, tariffsRateLimit)
|
ym.SetPathLimit("/tariffs/calculate", rateLimitWindow, tariffsRateLimit)
|
||||||
}
|
}
|
||||||
|
func getCategoriesError(bodyData string) []int64 {
|
||||||
|
var result []int64
|
||||||
|
for _, v := range gjson.Get(bodyData, "errors.#.message").Array() {
|
||||||
|
errorMessage := v.String()
|
||||||
|
split := strings.Split(errorMessage, "categories: ")
|
||||||
|
if len(split) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
categories := strings.Split(strings.TrimSpace(split[1]), ",")
|
||||||
|
for _, category := range categories {
|
||||||
|
category = strings.TrimSpace(category)
|
||||||
|
if category == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isDigit, categoryId := utils.IsDigit(category)
|
||||||
|
if !isDigit || categoryId == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, *categoryId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// processTariffChunk processes a single chunk of offers for tariff calculation
|
func filterOffersByCategories(offers []*pb.CalculateProductTariffsRequest_Offer, categories []int64) []*pb.CalculateProductTariffsRequest_Offer {
|
||||||
func (r *apiRepository) processTariffChunk(ctx context.Context, client *ymclient.APIClient, ymParameters *ymclient.CalculateTariffsParametersDTO, offerChunk []*pb.CalculateProductTariffsRequest_Offer, chunkIndex int) (*pb.CalculateProductTariffsResponse, error) {
|
return lo.Filter(offers, func(offer *pb.CalculateProductTariffsRequest_Offer, _ int) bool {
|
||||||
|
return lo.Contains(categories, offer.CategoryId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *apiRepository) makeTariffRequest(context context.Context, client *ymclient.APIClient, ymParameters *ymclient.CalculateTariffsParametersDTO, offerChunk []*pb.CalculateProductTariffsRequest_Offer) (*ymclient.CalculateTariffsResponse, *http.Response, error) {
|
||||||
ymOffers := r.convertOffersToYM(offerChunk)
|
ymOffers := r.convertOffersToYM(offerChunk)
|
||||||
if len(ymOffers) == 0 {
|
if len(ymOffers) == 0 {
|
||||||
fmt.Printf("Skipping chunk %d: no valid offers\n", chunkIndex+1)
|
return nil, nil, fmt.Errorf("no valid offers in chunk")
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ymParameters.CampaignId != nil && *ymParameters.CampaignId > 0 {
|
if ymParameters.CampaignId != nil && *ymParameters.CampaignId > 0 {
|
||||||
ymParameters.SellingProgram = nil
|
ymParameters.SellingProgram = nil
|
||||||
ymParameters.Frequency = nil
|
ymParameters.Frequency = nil
|
||||||
ymParameters.Currency = nil
|
ymParameters.Currency = nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ymParameters.CampaignId = nil
|
ymParameters.CampaignId = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ymRequest := ymclient.NewCalculateTariffsRequest(*ymParameters, ymOffers)
|
ymRequest := ymclient.NewCalculateTariffsRequest(*ymParameters, ymOffers)
|
||||||
response, httpResp, err := client.TariffsAPI.CalculateTariffs(ctx).
|
response, httpResp, err := client.TariffsAPI.CalculateTariffs(context).
|
||||||
CalculateTariffsRequest(*ymRequest).
|
CalculateTariffsRequest(*ymRequest).
|
||||||
Execute()
|
Execute()
|
||||||
|
return response, httpResp, err
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
// processTariffChunk processes a single chunk of offers for tariff calculation
|
||||||
if httpResp != nil && httpResp.Body != nil {
|
func (r *apiRepository) processTariffChunk(ctx context.Context, client *ymclient.APIClient, ymParameters *ymclient.CalculateTariffsParametersDTO, offerChunk []*pb.CalculateProductTariffsRequest_Offer, chunkIndex int) (*pb.CalculateProductTariffsResponse, error) {
|
||||||
bodyData := make([]byte, 2048)
|
globalError := fmt.Errorf("failed to process chunk %d", chunkIndex+1)
|
||||||
_, httpErr := httpResp.Body.Read(bodyData)
|
for range 2 {
|
||||||
if httpErr == nil {
|
response, httpResp, err := r.makeTariffRequest(ctx, client, ymParameters, offerChunk)
|
||||||
fmt.Printf("Error response for chunk %d: %s\n", chunkIndex+1, string(bodyData))
|
|
||||||
|
if err != nil {
|
||||||
|
globalError = err
|
||||||
|
if httpResp == nil || httpResp.Body == nil {
|
||||||
|
return nil, fmt.Errorf("failed to call Yandex Market API for chunk %d: %w", chunkIndex+1, err)
|
||||||
}
|
}
|
||||||
|
bodyData, httpErr := io.ReadAll(httpResp.Body)
|
||||||
|
if httpErr != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response body for chunk %d: %w", chunkIndex+1, httpErr)
|
||||||
|
}
|
||||||
|
fmt.Printf("Error response for chunk %d: %s\n", chunkIndex+1, string(bodyData))
|
||||||
|
categoriesErrors := getCategoriesError(string(bodyData))
|
||||||
|
if len(categoriesErrors) == 0 {
|
||||||
|
return nil, fmt.Errorf("failed to call Yandex Market API for chunk %d: %w", chunkIndex+1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
offerChunk = filterOffersByCategories(offerChunk, categoriesErrors)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to call Yandex Market API for chunk %d: %w", chunkIndex+1, err)
|
|
||||||
|
|
||||||
|
if response == nil || response.Result == nil {
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Warning: received empty response for chunk %d\n", chunkIndex+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.convertResponseToProto(response), nil
|
||||||
}
|
}
|
||||||
|
return nil, globalError
|
||||||
if response == nil || response.Result == nil {
|
|
||||||
fmt.Printf("Warning: received empty response for chunk %d\n", chunkIndex+1)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.convertResponseToProto(response), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertOffersToYM converts protobuf offers to Yandex Market format
|
// convertOffersToYM converts protobuf offers to Yandex Market format
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
@@ -123,3 +124,10 @@ func HashArray[T Hashable](arr []T) (string, error) {
|
|||||||
|
|
||||||
return fmt.Sprintf("%x", h.Sum64()), nil
|
return fmt.Sprintf("%x", h.Sum64()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsDigit(v string) (bool, *int64) {
|
||||||
|
if val, err := strconv.ParseInt(v, 10, 64); err == nil {
|
||||||
|
return true, &val
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user