Implement step 17 modules: threads/messages, notifications with automation (submission.created, idea.created, grant_call.opened), innovation test feedback, admin panel (submissions, verify, metrics, grant call status), Middleman adaptation and partnerships. Split multi-type files into one type per file and replace object-valued dictionaries with string-valued maps in data contracts (migrations 0006/0007).
120 lines
4.2 KiB
C#
120 lines
4.2 KiB
C#
using SurrealDb.Net;
|
|
using TarninaHub.Application.Abstractions;
|
|
using TarninaHub.Application.Dtos;
|
|
using TarninaHub.Domain.Rules;
|
|
|
|
namespace TarninaHub.Infrastructure.Persistence;
|
|
|
|
public sealed class ApplicationService : IApplicationService
|
|
{
|
|
private const string SelectFields =
|
|
"type::string(id) AS id, type::string(grant_call) AS grant_call, type::string(idea) AS idea, " +
|
|
"content, status, created_at";
|
|
|
|
private readonly ISurrealDbClient _client;
|
|
private readonly IGrantCallService _grantCallService;
|
|
private readonly IIdeaService _ideaService;
|
|
|
|
public ApplicationService(
|
|
ISurrealDbClient client,
|
|
IGrantCallService grantCallService,
|
|
IIdeaService ideaService)
|
|
{
|
|
_client = client;
|
|
_grantCallService = grantCallService;
|
|
_ideaService = ideaService;
|
|
}
|
|
|
|
public async Task<CreateApplicationResult> CreateAsync(
|
|
string userId,
|
|
CreateApplicationRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var grantCall = await _grantCallService.GetAsync(request.GrantCallId, cancellationToken);
|
|
|
|
if (grantCall is null)
|
|
{
|
|
return new CreateApplicationResult(CreateApplicationOutcome.CallNotFound, null);
|
|
}
|
|
|
|
if (!ApplicationWindow.IsOpen(grantCall.Status, grantCall.OpensAt, grantCall.ClosesAt, DateTimeOffset.UtcNow))
|
|
{
|
|
return new CreateApplicationResult(CreateApplicationOutcome.CallNotOpen, null);
|
|
}
|
|
|
|
var idea = await _ideaService.GetAsync(request.IdeaId, cancellationToken);
|
|
|
|
if (idea is null)
|
|
{
|
|
return new CreateApplicationResult(CreateApplicationOutcome.IdeaNotFound, null);
|
|
}
|
|
|
|
var id = Guid.NewGuid().ToString("N");
|
|
var (grantTable, grantId) = SurrealRecordId.Split(request.GrantCallId);
|
|
var (ideaTable, ideaIdPart) = SurrealRecordId.Split(request.IdeaId);
|
|
|
|
var response = await _client.RawQuery(
|
|
"CREATE type::record('application', $id) SET " +
|
|
"grant_call = type::record($grantTable, $grantId), " +
|
|
"idea = type::record($ideaTable, $ideaId), " +
|
|
"content = $content, status = 'draft'; " +
|
|
$"SELECT {SelectFields} FROM type::record('application', $id) LIMIT 1;",
|
|
new Dictionary<string, object?>
|
|
{
|
|
["id"] = id,
|
|
["grantTable"] = grantTable,
|
|
["grantId"] = grantId,
|
|
["ideaTable"] = ideaTable,
|
|
["ideaId"] = ideaIdPart,
|
|
["content"] = request.Content ?? new Dictionary<string, string?>(),
|
|
},
|
|
cancellationToken);
|
|
response.EnsureAllOks();
|
|
|
|
var rows = response.GetValue<List<ApplicationRow>>(1) ?? [];
|
|
|
|
return new CreateApplicationResult(CreateApplicationOutcome.Created, Map(rows[0]));
|
|
}
|
|
|
|
public async Task<ApplicationResponse?> GetAsync(string applicationId, CancellationToken cancellationToken)
|
|
{
|
|
var (table, id) = SurrealRecordId.Split(applicationId);
|
|
|
|
var response = await _client.RawQuery(
|
|
$"SELECT {SelectFields} FROM type::record($table, $id) LIMIT 1;",
|
|
new Dictionary<string, object?> { ["table"] = table, ["id"] = id },
|
|
cancellationToken);
|
|
response.EnsureAllOks();
|
|
|
|
var rows = response.GetValue<List<ApplicationRow>>(0) ?? [];
|
|
|
|
return rows.Count > 0 ? Map(rows[0]) : null;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ApplicationResponse>> ListForAuthorAsync(
|
|
string authorId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var (table, id) = SurrealRecordId.Split(authorId);
|
|
|
|
var response = await _client.RawQuery(
|
|
$"SELECT {SelectFields} FROM application WHERE idea.author = type::record($table, $id) " +
|
|
"ORDER BY created_at DESC;",
|
|
new Dictionary<string, object?> { ["table"] = table, ["id"] = id },
|
|
cancellationToken);
|
|
response.EnsureAllOks();
|
|
|
|
var rows = response.GetValue<List<ApplicationRow>>(0) ?? [];
|
|
|
|
return rows.Select(Map).ToList();
|
|
}
|
|
|
|
private static ApplicationResponse Map(ApplicationRow row) => new(
|
|
row.Id,
|
|
row.GrantCall,
|
|
row.Idea,
|
|
row.Content,
|
|
row.Status,
|
|
row.CreatedAt);
|
|
}
|