85 lines
3.0 KiB
C#
85 lines
3.0 KiB
C#
using Management.Data;
|
|
using Management.Security;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Management.Controllers.Admin;
|
|
|
|
/// <summary>
|
|
/// Admin endpoints for audience-based allocation modifiers.
|
|
/// Table: dbo.tbAllocationModifier
|
|
/// Proc: dbo.spAllocationRecommend (list / update actions)
|
|
///
|
|
/// ENDPOINTS:
|
|
/// GET /api/admin/modifiers - List all modifiers
|
|
/// PUT /api/admin/modifiers/{id} - Update a modifier
|
|
/// POST /api/admin/modifiers/preview - Preview recommendation with factors
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/admin/modifiers")]
|
|
public sealed class AdminModifiersController : AdminControllerBase
|
|
{
|
|
public AdminModifiersController(SqlService sql, ClientContext client, ILogger<AdminModifiersController> log)
|
|
: base(sql, client, log) { }
|
|
|
|
[HttpGet]
|
|
public Task<IActionResult> List(CancellationToken ct)
|
|
=> CallProc("spAllocationRecommend", "list", new { }, ct);
|
|
|
|
[HttpPut("{id:int}")]
|
|
public Task<IActionResult> Update(int id, [FromBody] UpdateModifierRequest request, CancellationToken ct)
|
|
{
|
|
if (request?.PctAdjustment is < -50 or > 50)
|
|
return Task.FromResult(ValidationError("pctAdjustment must be between -50 and 50"));
|
|
|
|
Logger.LogWarning("[Admin] UpdateModifier | Id={Id} | By={User}", id, Client.Email);
|
|
|
|
return CallProc("spAllocationRecommend", "update", new
|
|
{
|
|
id,
|
|
pctAdjustment = request?.PctAdjustment,
|
|
minBudgetAdj = request?.MinBudgetAdj,
|
|
rationale = request?.Rationale?.Trim(),
|
|
isActive = request?.IsActive
|
|
}, ct);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preview a recommendation with given factors — same proc, recommend action.
|
|
/// Lets admins test how modifiers affect channel mix without going through the wizard.
|
|
/// </summary>
|
|
[HttpPost("preview")]
|
|
public Task<IActionResult> Preview([FromBody] PreviewRequest request, CancellationToken ct)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(request?.BusinessCategory))
|
|
return Task.FromResult(ValidationError("businessCategory is required"));
|
|
if (string.IsNullOrWhiteSpace(request?.Objective))
|
|
return Task.FromResult(ValidationError("objective is required"));
|
|
|
|
return CallProc("spAllocationRecommend", "recommend", new
|
|
{
|
|
businessCategory = request.BusinessCategory.Trim(),
|
|
objective = request.Objective.Trim(),
|
|
ageSkew = request.AgeSkew?.Trim(),
|
|
marketScope = request.MarketScope?.Trim()
|
|
}, ct);
|
|
}
|
|
}
|
|
|
|
// DTOs
|
|
|
|
public sealed class UpdateModifierRequest
|
|
{
|
|
public int? PctAdjustment { get; set; }
|
|
public int? MinBudgetAdj { get; set; }
|
|
public string? Rationale { get; set; }
|
|
public bool? IsActive { get; set; }
|
|
}
|
|
|
|
public sealed class PreviewRequest
|
|
{
|
|
public string? BusinessCategory { get; set; }
|
|
public string? Objective { get; set; }
|
|
public string? AgeSkew { get; set; }
|
|
public string? MarketScope { get; set; }
|
|
}
|