138 lines
5.3 KiB
C#
138 lines
5.3 KiB
C#
namespace Gateway.Models;
|
|
|
|
// ════════════════════════════════════════════════
|
|
// Request: Client → Gateway
|
|
// ════════════════════════════════════════════════
|
|
|
|
public sealed class ChannelForecastRequest
|
|
{
|
|
/// <summary>Advertising objective: awareness, traffic, leads, sales</summary>
|
|
public string Objective { get; set; } = "traffic";
|
|
|
|
/// <summary>Business category from wizard Step 1</summary>
|
|
public string? BusinessCategory { get; set; }
|
|
|
|
/// <summary>Keywords from URL analysis (Step 1)</summary>
|
|
public List<string> Keywords { get; set; } = new();
|
|
|
|
/// <summary>Geo targeting from audience step</summary>
|
|
public ForecastGeoTargeting? GeoTargeting { get; set; }
|
|
|
|
/// <summary>Audience parameters from Step 2</summary>
|
|
public ForecastAudience? Audience { get; set; }
|
|
|
|
/// <summary>Monthly budget in whole dollars</summary>
|
|
public decimal MonthlyBudget { get; set; }
|
|
|
|
/// <summary>Channels to estimate (defaults to all selected)</summary>
|
|
public List<string>? Channels { get; set; }
|
|
}
|
|
|
|
public sealed class ForecastGeoTargeting
|
|
{
|
|
public List<string>? ZipCodes { get; set; }
|
|
public double? RadiusMiles { get; set; }
|
|
public List<long>? GeoTargetIds { get; set; }
|
|
}
|
|
|
|
public sealed class ForecastAudience
|
|
{
|
|
public int? AgeMin { get; set; }
|
|
public int? AgeMax { get; set; }
|
|
public List<string>? Genders { get; set; }
|
|
public List<string>? Interests { get; set; }
|
|
}
|
|
|
|
// ════════════════════════════════════════════════
|
|
// Response: Gateway → Client (normalized)
|
|
// ════════════════════════════════════════════════
|
|
|
|
public sealed class ChannelForecastResponse
|
|
{
|
|
public bool Ok { get; set; } = true;
|
|
public string Objective { get; set; } = string.Empty;
|
|
public decimal TotalBudget { get; set; }
|
|
public List<ChannelEstimate> Channels { get; set; } = new();
|
|
public ForecastRecommendation? Recommendation { get; set; }
|
|
public ForecastMeta Metadata { get; set; } = new();
|
|
}
|
|
|
|
public sealed class ChannelEstimate
|
|
{
|
|
public string Provider { get; set; } = string.Empty;
|
|
public int AllocationPercent { get; set; }
|
|
public decimal AllocatedBudget { get; set; }
|
|
public ChannelEstimateMetrics Estimates { get; set; } = new();
|
|
public double EfficiencyScore { get; set; }
|
|
public string StrengthLabel { get; set; } = string.Empty;
|
|
public string Confidence { get; set; } = "none";
|
|
public string DataSource { get; set; } = "none";
|
|
}
|
|
|
|
public sealed class ChannelEstimateMetrics
|
|
{
|
|
public double Impressions { get; set; }
|
|
public double? Reach { get; set; }
|
|
public double Clicks { get; set; }
|
|
public double Conversions { get; set; }
|
|
public decimal AvgCpc { get; set; }
|
|
public decimal AvgCpm { get; set; }
|
|
public decimal? EstimatedCpa { get; set; }
|
|
public double Ctr { get; set; }
|
|
}
|
|
|
|
public sealed class ForecastRecommendation
|
|
{
|
|
public string Summary { get; set; } = string.Empty;
|
|
public List<string> Highlights { get; set; } = new();
|
|
}
|
|
|
|
public sealed class ForecastMeta
|
|
{
|
|
public DateTimeOffset GeneratedAt { get; set; } = DateTimeOffset.UtcNow;
|
|
public string ForecastPeriod { get; set; } = "30 days";
|
|
}
|
|
|
|
// ════════════════════════════════════════════════
|
|
// Objective-weighted scoring
|
|
// ════════════════════════════════════════════════
|
|
|
|
public sealed class MetricWeights
|
|
{
|
|
public double Reach { get; }
|
|
public double Impressions { get; }
|
|
public double Cpm { get; }
|
|
public double Clicks { get; }
|
|
public double Cpc { get; }
|
|
public double Ctr { get; }
|
|
public double Conversions { get; }
|
|
public double Cpa { get; }
|
|
|
|
public MetricWeights(double reach, double impressions, double cpm,
|
|
double clicks, double cpc, double ctr, double conversions, double cpa)
|
|
{
|
|
Reach = reach; Impressions = impressions; Cpm = cpm;
|
|
Clicks = clicks; Cpc = cpc; Ctr = ctr;
|
|
Conversions = conversions; Cpa = cpa;
|
|
}
|
|
}
|
|
|
|
public static class ObjectiveWeights
|
|
{
|
|
public static readonly Dictionary<string, MetricWeights> Weights = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
// reach imp cpm clicks cpc ctr conv cpa
|
|
["awareness"] = new MetricWeights(0.35, 0.25, 0.20, 0.05, 0.05, 0.05, 0.00, 0.00),
|
|
["traffic"] = new MetricWeights(0.05, 0.10, 0.10, 0.30, 0.30, 0.15, 0.00, 0.00),
|
|
["leads"] = new MetricWeights(0.05, 0.05, 0.05, 0.15, 0.15, 0.10, 0.25, 0.20),
|
|
["sales"] = new MetricWeights(0.05, 0.05, 0.05, 0.10, 0.10, 0.10, 0.30, 0.25),
|
|
};
|
|
|
|
/// <summary>Fallback: balanced weights if objective not recognized</summary>
|
|
public static readonly MetricWeights Default =
|
|
new(0.10, 0.10, 0.10, 0.20, 0.20, 0.10, 0.10, 0.10);
|
|
|
|
public static MetricWeights For(string objective)
|
|
=> Weights.TryGetValue(objective, out var w) ? w : Default;
|
|
}
|