Биллинг расчет ставок
API Azure Rate Card возвращает поле MeterRates (см. Документацию). Azure UsageAggregate дает количество (см. Документацию).
По лазурной странице поддержки. Это форум, чтобы задавать вопросы.
Итак, как применяются тарифы счетчиков?
Примерные показатели счетчика:
{"0":20, "100":15, "200":10}
Если у меня есть количество 175, это сумма 100*20 + 75*15
или же 175*15
?
Зачем указывать включенное количество?
Пример: rates:{"0":23}
с учетом количества 10 можно выразить в виде показателей:
{"0":0, "10":23}
3 ответа
примерные показатели счетчика: {"0":20, "100":15, "200":10}
если у меня есть количество 175, это сумма 100*20 + 75*15 или 175*15?
Ценообразование является многоуровневым. Поэтому, когда вы получаете ставки, это, по сути, говорит вам, что:
- от
0 - 99
единицы, ставка за единицу20
- от
100 - 199
единицы, ставка за единицу15
- от
200
единиц и выше, ставка за единицу10
Исходя из этой логики, ваш расчет должен быть:
99 * 20 + 75 * 15 = 3105
Одна вещь, которая смущает меня, хотя это верхний предел. Приведенный выше расчет основан на информации, которую я получил от команды биллинга Azure. Что смутило меня, так это то, что произойдет, если скажут потребление 99.5
единицы? Для первого 99
единицы это хорошо, но я не уверен, как дополнительный 0.5
единицы будут рассчитаны.
Гуарав понимает суть проблемы и поэтому я отметил это как ответ. Исходя из этого, я разработал следующий код для реализации логики. Он делится на две части:
- Создание списка сегментов по ставкам счетчиков
- Обработка количества с помощью списка сегментов для определения количества
Следующая функция создает список сегментов (каждый объект блока представляет собой простое POCO со свойствами Min, Max и Rate). Список привязан к объекту счетчика, который имеет другие свойства из API прейскуранта.
private Dictionary<int, RateBucket> ParseRateBuckets(string rates)
{
dynamic dRates = JsonConvert.DeserializeObject(rates);
var rateContainer = (JContainer)dRates;
var buckets = new Dictionary<int, RateBucket>();
var bucketNumber = 0;
foreach (var jToken in rateContainer.Children())
{
var jProperty = jToken as JProperty;
if (jProperty != null)
{
var bucket = new RateBucket
{
Min = Convert.ToDouble(jProperty.Name),
Rate = Convert.ToDouble(jProperty.Value.ToString())
};
if (bucketNumber > 0)
buckets[bucketNumber - 1].Max = bucket.Min;
buckets.Add(bucketNumber, bucket);
}
bucketNumber++;
}
return buckets;
}
Вторая функция использует объект счетчика с двумя полезными свойствами: список сегментов и включенное количество. Согласно документации по прейскуранту (как я ее прочел), вы не начнете считать оплачиваемое количество, пока не превысите включенное количество. Я уверен, что есть некоторый рефакторинг, который мог бы быть сделан здесь, но упорядоченная обработка контейнеров является ключевым моментом.
Я думаю, что я обратился к вопросу о количестве, признав, что это двойное число, а не целое число. Следовательно, количество, связанное с любым отдельным сегментом, является разницей между максимальным объемом и минимальным объемом (если только мы не заполнили только частичное ведро).
private double CalculateUsageCost(RateCardMeter meter, double quantity)
{
var amount = 0.0;
quantity -= meter.IncludedQuantity;
if (quantity > 0)
{
for (var i = 0; i < meter.RateBuckets.Count; i++)
{
var bucket = meter.RateBuckets[i];
if (quantity > bucket.Min)
{
if (bucket.Max.HasValue && quantity > bucket.Max)
amount += (bucket.Max.Value - bucket.Min)*bucket.Rate;
else
amount += (quantity - bucket.Min)*bucket.Rate;
}
}
}
return amount;
}
Наконец, в документации неясно, сколько времени уходит на уровни. Если я получу скидку на основе количества, за какой период времени я соберу количество? API использования позволяет мне получать данные ежедневно или ежечасно. Я хочу получать свои данные ежечасно, чтобы я мог соотносить свои расходы с временем суток. Но когда уместно действительно рассчитать счет? Кажется, что ежечасно неправильно, ежедневно может работать, но это может быть уместно только в течение всего месяца.
Недавно я только что сделал эту похожую задачу. Ниже приведен мой пример (я думаю, что вы можете использовать регулярные выражения для удаления этих символов, а не как я, используя замену). Первая функция анализирует информационную строку тарифа для генерации коллекции пары ключ-значение, а вторая функция используется для расчета общей цены.
private Dictionary<float, double> GetRatesDetail(string r)
{
Dictionary<float, double> pairs = null;
if(string.IsNullOrEmpty(r) || r.Length <=2)
{
pairs = new Dictionary<float, double>();
pairs.Add(0, 0);
}
else
{
pairs = r.Replace("{", "").Replace("}", "").Split(',')
.Select(value => value.Split(':'))
.ToDictionary(pair => float.Parse(pair[0].Replace("\"", "")), pair => double.Parse(pair[1]));
}
return pairs;
}
public decimal Process(Dictionary<float, double> rates, decimal quantity)
{
double ret = 0;
foreach (int key in rates.Keys.OrderByDescending(k => k))
{
if (quantity >= key)
{
ret += ((double)quantity - key) * rates[key];
quantity = key;
}
}
return (decimal)ret;
}