a scoring model
This commit is contained in:
parent
bdfc8d4671
commit
8a46341755
17 changed files with 832 additions and 3 deletions
77
src/Yavsc.Org/Controllers/Kyc/ConsultantController.cs
Normal file
77
src/Yavsc.Org/Controllers/Kyc/ConsultantController.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// Yavsc.Controllers.Kyc/ConsultantController.cs
|
||||
namespace Yavsc.Controllers.Kyc
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Kyc;
|
||||
using Yavsc.Services.Kyc;
|
||||
using Yavsc.ViewModels.Kyc;
|
||||
|
||||
/// <summary>
|
||||
/// Accessible aux utilisateurs avec le rôle "Consultant".
|
||||
/// Ne voit qu'un score agrégé — jamais de déclarations, jamais d'identité.
|
||||
/// </summary>
|
||||
[Authorize(Roles = "Consultant")]
|
||||
[Route("kyc/score")]
|
||||
public class ConsultantController : Controller
|
||||
{
|
||||
private readonly ITrustTokenService _tokenService;
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public ConsultantController(
|
||||
ITrustTokenService tokenService,
|
||||
ApplicationDbContext db)
|
||||
{
|
||||
_tokenService = tokenService;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// POST kyc/score
|
||||
/// Body : { subjectEmail } ou { externalToken }
|
||||
/// Retourne uniquement le score agrégé.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Query(ScoreQueryModel input)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(ModelState);
|
||||
|
||||
TrustToken token = string.IsNullOrEmpty(input.ExternalToken)
|
||||
? await _tokenService.GetOrCreateFromEmailAsync(input.SubjectEmail)
|
||||
: await _tokenService.GetOrCreateFromTrustedPartyAsync(input.ExternalToken);
|
||||
|
||||
// Chargement du score avec stats agrégées — aucune déclaration exposée
|
||||
var stats = await _db.TrustDeclarations
|
||||
.Where(d => d.TrustTokenId == token.Id
|
||||
&& d.Status == ModerationStatus.Approved)
|
||||
.GroupBy(d => d.TrustTokenId)
|
||||
.Select(g => new {
|
||||
Count = g.Count(),
|
||||
LastUpdated = g.Max(d => d.SubmittedAt)
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var view = new TrustScoreView
|
||||
{
|
||||
Score = token.TrustScore ?? 50,
|
||||
Category = ScoreToCategory(token.TrustScore ?? 50),
|
||||
DeclarationCount = stats?.Count ?? 0,
|
||||
LastUpdated = stats?.LastUpdated ?? token.CreatedAt
|
||||
};
|
||||
|
||||
return View(view);
|
||||
}
|
||||
|
||||
private static string ScoreToCategory(int score) => score switch
|
||||
{
|
||||
>= 75 => "Fiable",
|
||||
>= 40 => "Prudence",
|
||||
_ => "Risque"
|
||||
};
|
||||
}
|
||||
}
|
||||
79
src/Yavsc.Org/Controllers/Kyc/DeclarantController.cs
Normal file
79
src/Yavsc.Org/Controllers/Kyc/DeclarantController.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Yavsc.Controllers.Kyc/DeclarantController.cs
|
||||
namespace Yavsc.Controllers.Kyc
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Kyc;
|
||||
using Yavsc.Services.Kyc;
|
||||
using Yavsc.ViewModels.Kyc;
|
||||
|
||||
/// <summary>
|
||||
/// Accessible à tout utilisateur authentifié.
|
||||
/// Le déclarant soumet une déclaration sur un sujet pseudonymisé.
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[Route("kyc/declare")]
|
||||
public class DeclarantController : Controller
|
||||
{
|
||||
private readonly IModerationPipelineService _pipeline;
|
||||
private readonly ITrustTokenService _tokenService;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public DeclarantController(
|
||||
IModerationPipelineService pipeline,
|
||||
ITrustTokenService tokenService,
|
||||
UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_pipeline = pipeline;
|
||||
_tokenService = tokenService;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET kyc/declare — formulaire de déclaration
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Index() => View();
|
||||
|
||||
/// <summary>
|
||||
/// POST kyc/declare
|
||||
/// Body : { subjectEmail, content, sentiment }
|
||||
/// Le subjectEmail est immédiatement transformé en token — jamais stocké.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Submit(DeclarationInputModel input)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return View("Index", input);
|
||||
|
||||
// Token du sujet — depuis email ou token tiers
|
||||
TrustToken subjectToken = string.IsNullOrEmpty(input.ExternalToken)
|
||||
? await _tokenService.GetOrCreateFromEmailAsync(input.SubjectEmail)
|
||||
: await _tokenService.GetOrCreateFromTrustedPartyAsync(input.ExternalToken);
|
||||
|
||||
// Token du déclarant — depuis son propre email confirmé
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (!await _userManager.IsEmailConfirmedAsync(user))
|
||||
return Forbid(); // Email non confirmé → pas de déclaration
|
||||
|
||||
var declarantToken = await _tokenService
|
||||
.GetOrCreateFromEmailAsync(user.Email);
|
||||
|
||||
await _pipeline.SubmitDeclarationAsync(
|
||||
subjectToken.Id,
|
||||
declarantToken.Id,
|
||||
input.Content,
|
||||
input.Sentiment);
|
||||
|
||||
return RedirectToAction("Submitted");
|
||||
}
|
||||
|
||||
[HttpGet("submitted")]
|
||||
public IActionResult Submitted() => View();
|
||||
}
|
||||
}
|
||||
76
src/Yavsc.Org/Controllers/Kyc/ModerationController.cs
Normal file
76
src/Yavsc.Org/Controllers/Kyc/ModerationController.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Yavsc.Controllers.Kyc/ModerationController.cs
|
||||
namespace Yavsc.Controllers.Kyc
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Yavsc.Server.Helpers;
|
||||
using Yavsc.Services.Kyc;
|
||||
using Yavsc.ViewModels.Kyc;
|
||||
|
||||
/// <summary>
|
||||
/// Accessible uniquement au rôle "Moderator".
|
||||
/// Voit les déclarations en attente + flags, prend les décisions.
|
||||
/// </summary>
|
||||
[Authorize(Roles = "Moderator")]
|
||||
[Route("kyc/moderation")]
|
||||
public class ModerationController : Controller
|
||||
{
|
||||
private readonly IModerationPipelineService _pipeline;
|
||||
|
||||
public ModerationController(IModerationPipelineService pipeline)
|
||||
{
|
||||
_pipeline = pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET kyc/moderation — queue de modération
|
||||
/// Flagged en tête, puis Pending par date de soumission.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var queue = await _pipeline.GetQueueAsync();
|
||||
return View(queue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// POST kyc/moderation/approve
|
||||
/// </summary>
|
||||
[HttpPost("approve")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Approve(ModerationDecisionModel input)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return RedirectToAction("Index");
|
||||
|
||||
var moderatorId = User.GetUserId();
|
||||
await _pipeline.ApproveAsync(input.DeclarationId, input.ScoreDelta, moderatorId);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// POST kyc/moderation/reject
|
||||
/// </summary>
|
||||
[HttpPost("reject")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Reject(ModerationDecisionModel input)
|
||||
{
|
||||
var moderatorId = User.GetUserId();
|
||||
await _pipeline.RejectAsync(input.DeclarationId, moderatorId);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// POST kyc/moderation/redact
|
||||
/// </summary>
|
||||
[HttpPost("redact")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Redact(ModerationDecisionModel input)
|
||||
{
|
||||
var moderatorId = User.GetUserId();
|
||||
await _pipeline.RedactAsync(input.DeclarationId, input.ScoreDelta, moderatorId);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue