feat(backend): add communication, tester, admin and middleman modules

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).
This commit is contained in:
Rafał Tyński
2026-10-04 03:49:45 +02:00
parent f4f3b3896a
commit fa1199539b
136 changed files with 2338 additions and 574 deletions
@@ -0,0 +1,18 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IAdminService
{
Task<IReadOnlyList<SubmissionResponse>> ListSubmissionsAsync(
string? status,
string? challengeId,
CancellationToken cancellationToken);
Task<KnowledgeItemResponse?> VerifyKnowledgeItemAsync(
string knowledgeItemId,
VerifyKnowledgeItemRequest request,
CancellationToken cancellationToken);
Task<AdminMetricsResponse> GetMetricsAsync(CancellationToken cancellationToken);
}
@@ -0,0 +1,12 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public enum CreateApplicationOutcome
{
Created,
CallNotFound,
CallNotOpen,
IdeaNotFound,
}
@@ -0,0 +1,8 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public sealed record CreateApplicationResult(
CreateApplicationOutcome Outcome,
ApplicationResponse? Application);
@@ -0,0 +1,18 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IApplicationService
{
Task<CreateApplicationResult> CreateAsync(
string userId,
CreateApplicationRequest request,
CancellationToken cancellationToken);
Task<ApplicationResponse?> GetAsync(string applicationId, CancellationToken cancellationToken);
Task<IReadOnlyList<ApplicationResponse>> ListForAuthorAsync(
string authorId,
CancellationToken cancellationToken);
}
@@ -6,6 +6,6 @@ public interface IAuditLogService
string? actorId,
string action,
string target,
IReadOnlyDictionary<string, object?> context,
IReadOnlyDictionary<string, string?> context,
CancellationToken cancellationToken);
}
@@ -0,0 +1,16 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IGrantCallService
{
Task<IReadOnlyList<GrantCallResponse>> ListAsync(string? status, CancellationToken cancellationToken);
Task<GrantCallResponse?> GetAsync(string grantCallId, CancellationToken cancellationToken);
Task<GrantCallResponse?> UpdateStatusAsync(
string grantCallId,
string status,
CancellationToken cancellationToken);
}
@@ -0,0 +1,12 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IIdeaAssistant
{
Task<IdeaAssistResponse?> AssistAsync(
string ideaId,
IdeaAssistRequest request,
CancellationToken cancellationToken);
}
@@ -0,0 +1,21 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IIdeaService
{
Task<IdeaResponse> CreateAsync(string authorId, CreateIdeaRequest request, CancellationToken cancellationToken);
Task<IdeaResponse?> GetAsync(string ideaId, CancellationToken cancellationToken);
Task<IReadOnlyList<IdeaResponse>> ListForAuthorAsync(string authorId, CancellationToken cancellationToken);
Task<IReadOnlyList<string>> ListDistinctAuthorIdsAsync(CancellationToken cancellationToken);
Task<IdeaResponse?> UpdateAsync(string ideaId, UpdateIdeaRequest request, CancellationToken cancellationToken);
Task<IdeaCardResponse?> GetCardAsync(string ideaId, CancellationToken cancellationToken);
Task<IdeaCardResponse?> UpdateCardAsync(string ideaId, IdeaCardRequest request, CancellationToken cancellationToken);
}
@@ -0,0 +1,27 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface INotificationService
{
Task NotifyStaffAsync(
string type,
IReadOnlyDictionary<string, string?> payload,
CancellationToken cancellationToken);
Task NotifyUserAsync(
string userId,
string type,
IReadOnlyDictionary<string, string?> payload,
CancellationToken cancellationToken);
Task<IReadOnlyList<NotificationResponse>> ListForUserAsync(
string userId,
CancellationToken cancellationToken);
Task<NotificationResponse?> MarkReadAsync(
string notificationId,
string userId,
CancellationToken cancellationToken);
}
@@ -1,11 +1,5 @@
namespace TarninaHub.Application.Abstractions;
public sealed record SubmissionEvent(
string Type,
string SubmissionId,
string? ActorId,
string Status,
DateTimeOffset At);
public interface ISubmissionEventPublisher
{
@@ -0,0 +1,30 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IThreadService
{
Task<IReadOnlyList<ThreadResponse>> ListForUserAsync(
string userId,
bool isStaff,
CancellationToken cancellationToken);
Task<ThreadResponse?> GetAsync(
string threadId,
string userId,
bool isStaff,
CancellationToken cancellationToken);
Task<ThreadResponse> CreateAsync(
string creatorId,
CreateThreadRequest request,
CancellationToken cancellationToken);
Task<MessageResponse?> AddMessageAsync(
string threadId,
string authorId,
bool isStaff,
CreateMessageRequest request,
CancellationToken cancellationToken);
}
@@ -2,9 +2,8 @@ using TarninaHub.Application.Models;
namespace TarninaHub.Application.Abstractions;
public interface ITokenService
{
TokenResult CreateToken(UserAccount user);
}
public sealed record TokenResult(string AccessToken, DateTimeOffset ExpiresAt);
@@ -1,59 +0,0 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IIdeaService
{
Task<IdeaResponse> CreateAsync(string authorId, CreateIdeaRequest request, CancellationToken cancellationToken);
Task<IdeaResponse?> GetAsync(string ideaId, CancellationToken cancellationToken);
Task<IReadOnlyList<IdeaResponse>> ListForAuthorAsync(string authorId, CancellationToken cancellationToken);
Task<IdeaResponse?> UpdateAsync(string ideaId, UpdateIdeaRequest request, CancellationToken cancellationToken);
Task<IdeaCardResponse?> GetCardAsync(string ideaId, CancellationToken cancellationToken);
Task<IdeaCardResponse?> UpdateCardAsync(string ideaId, IdeaCardRequest request, CancellationToken cancellationToken);
}
public interface IIdeaAssistant
{
Task<IdeaAssistResponse?> AssistAsync(
string ideaId,
IdeaAssistRequest request,
CancellationToken cancellationToken);
}
public interface IGrantCallService
{
Task<IReadOnlyList<GrantCallResponse>> ListAsync(string? status, CancellationToken cancellationToken);
Task<GrantCallResponse?> GetAsync(string grantCallId, CancellationToken cancellationToken);
}
public enum CreateApplicationOutcome
{
Created,
CallNotFound,
CallNotOpen,
IdeaNotFound,
}
public sealed record CreateApplicationResult(
CreateApplicationOutcome Outcome,
ApplicationResponse? Application);
public interface IApplicationService
{
Task<CreateApplicationResult> CreateAsync(
string userId,
CreateApplicationRequest request,
CancellationToken cancellationToken);
Task<ApplicationResponse?> GetAsync(string applicationId, CancellationToken cancellationToken);
Task<IReadOnlyList<ApplicationResponse>> ListForAuthorAsync(
string authorId,
CancellationToken cancellationToken);
}
@@ -0,0 +1,8 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IMiddlemanService
{
Task<MiddlemanResponse?> AdaptAsync(MiddlemanRequest request, CancellationToken cancellationToken);
}
@@ -0,0 +1,16 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface IPartnershipService
{
Task<IReadOnlyList<PartnershipResponse>> ListForUserAsync(
string userId,
bool isStaff,
CancellationToken cancellationToken);
Task<PartnershipResponse> CreateAsync(
string initiatorId,
CreatePartnershipRequest request,
CancellationToken cancellationToken);
}
@@ -0,0 +1,9 @@
namespace TarninaHub.Application.Abstractions;
public sealed record SubmissionEvent(
string Type,
string SubmissionId,
string? ActorId,
string Status,
DateTimeOffset At);
@@ -0,0 +1,16 @@
using TarninaHub.Application.Dtos;
namespace TarninaHub.Application.Abstractions;
public interface ITestFeedbackService
{
Task<TestFeedbackResponse?> CreateAsync(
string innovationId,
string authorId,
CreateTestFeedbackRequest request,
CancellationToken cancellationToken);
Task<IReadOnlyList<TestFeedbackResponse>> ListForInnovationAsync(
string innovationId,
CancellationToken cancellationToken);
}
@@ -0,0 +1,6 @@
using TarninaHub.Application.Models;
namespace TarninaHub.Application.Abstractions;
public sealed record TokenResult(string AccessToken, DateTimeOffset ExpiresAt);