feat: enhance tariff processing with error handling and category filtering
This commit is contained in:
		@@ -3,9 +3,13 @@ package products
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sipro-mps/internal/redis"
 | 
			
		||||
	"sipro-mps/pkg/utils"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	pb "sipro-mps/api/generated/v1/yandexmarket/products"
 | 
			
		||||
@@ -15,6 +19,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"git.denco.store/fakz9/yandex-go-client"
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
	"github.com/tidwall/gjson"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -246,46 +251,90 @@ func (r *apiRepository) CalculateProductTariffs(ctx context.Context, marketplace
 | 
			
		||||
func (r *apiRepository) setTariffsRateLimit() {
 | 
			
		||||
	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 (r *apiRepository) processTariffChunk(ctx context.Context, client *ymclient.APIClient, ymParameters *ymclient.CalculateTariffsParametersDTO, offerChunk []*pb.CalculateProductTariffsRequest_Offer, chunkIndex int) (*pb.CalculateProductTariffsResponse, error) {
 | 
			
		||||
func filterOffersByCategories(offers []*pb.CalculateProductTariffsRequest_Offer, categories []int64) []*pb.CalculateProductTariffsRequest_Offer {
 | 
			
		||||
	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)
 | 
			
		||||
	if len(ymOffers) == 0 {
 | 
			
		||||
		fmt.Printf("Skipping chunk %d: no valid offers\n", chunkIndex+1)
 | 
			
		||||
		return nil, nil
 | 
			
		||||
		return nil, nil, fmt.Errorf("no valid offers in chunk")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ymParameters.CampaignId != nil && *ymParameters.CampaignId > 0 {
 | 
			
		||||
		ymParameters.SellingProgram = nil
 | 
			
		||||
		ymParameters.Frequency = nil
 | 
			
		||||
		ymParameters.Currency = nil
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		ymParameters.CampaignId = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ymRequest := ymclient.NewCalculateTariffsRequest(*ymParameters, ymOffers)
 | 
			
		||||
	response, httpResp, err := client.TariffsAPI.CalculateTariffs(ctx).
 | 
			
		||||
	response, httpResp, err := client.TariffsAPI.CalculateTariffs(context).
 | 
			
		||||
		CalculateTariffsRequest(*ymRequest).
 | 
			
		||||
		Execute()
 | 
			
		||||
	return response, httpResp, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if httpResp != nil && httpResp.Body != nil {
 | 
			
		||||
			bodyData := make([]byte, 2048)
 | 
			
		||||
			_, httpErr := httpResp.Body.Read(bodyData)
 | 
			
		||||
			if httpErr == nil {
 | 
			
		||||
				fmt.Printf("Error response for chunk %d: %s\n", chunkIndex+1, string(bodyData))
 | 
			
		||||
// processTariffChunk processes a single chunk of offers for tariff calculation
 | 
			
		||||
func (r *apiRepository) processTariffChunk(ctx context.Context, client *ymclient.APIClient, ymParameters *ymclient.CalculateTariffsParametersDTO, offerChunk []*pb.CalculateProductTariffsRequest_Offer, chunkIndex int) (*pb.CalculateProductTariffsResponse, error) {
 | 
			
		||||
	globalError := fmt.Errorf("failed to process chunk %d", chunkIndex+1)
 | 
			
		||||
	for range 2 {
 | 
			
		||||
		response, httpResp, err := r.makeTariffRequest(ctx, client, ymParameters, offerChunk)
 | 
			
		||||
 | 
			
		||||
		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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	return nil, globalError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertOffersToYM converts protobuf offers to Yandex Market format
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user