Browse Source

refactor(organization.plan): improvements after review

pull/90/head
berkansasmaz 5 years ago
parent
commit
37f0ce4837
No known key found for this signature in database GPG Key ID: 3AD81E2CA002230D
  1. 13
      src/EventHub.Application/Organizations/OrganizationAppService.cs
  2. 24
      src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs
  3. 4
      src/EventHub.Domain/Events/EventManager.cs
  4. 2
      src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs
  5. 11
      src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs
  6. 11
      src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs
  7. 34
      src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs
  8. 6
      src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs
  9. 27
      src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs
  10. 11
      src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs
  11. 4
      test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs

13
src/EventHub.Application/Organizations/OrganizationAppService.cs

@ -7,6 +7,7 @@ using EventHub.Organizations.PaymentRequests;
using EventHub.Organizations.Plans; using EventHub.Organizations.Plans;
using EventHub.Users; using EventHub.Users;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.BlobStoring; using Volo.Abp.BlobStoring;
@ -23,22 +24,22 @@ namespace EventHub.Organizations
private readonly OrganizationManager _organizationManager; private readonly OrganizationManager _organizationManager;
private readonly IBlobContainer<OrganizationProfilePictureContainer> _organizationBlobContainer; private readonly IBlobContainer<OrganizationProfilePictureContainer> _organizationBlobContainer;
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly IPlanInfoDefinitionStore _planInfoDefinitionStore; private readonly IOptions<PlanInfoOptions> _planInfoOptions;
public OrganizationAppService( public OrganizationAppService(
IRepository<Organization, Guid> organizationRepository, IRepository<Organization, Guid> organizationRepository,
IOrganizationMembershipRepository organizationMembershipsRepository, IOrganizationMembershipRepository organizationMembershipsRepository,
OrganizationManager organizationManager, OrganizationManager organizationManager,
IBlobContainer<OrganizationProfilePictureContainer> organizationBlobContainer, IBlobContainer<OrganizationProfilePictureContainer> organizationBlobContainer,
IUserRepository userRepository, IUserRepository userRepository,
IPlanInfoDefinitionStore planInfoDefinitionStore) IOptions<PlanInfoOptions> planInfoOptions)
{ {
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_organizationMembershipsRepository = organizationMembershipsRepository; _organizationMembershipsRepository = organizationMembershipsRepository;
_organizationManager = organizationManager; _organizationManager = organizationManager;
_organizationBlobContainer = organizationBlobContainer; _organizationBlobContainer = organizationBlobContainer;
_userRepository = userRepository; _userRepository = userRepository;
_planInfoDefinitionStore = planInfoDefinitionStore; _planInfoOptions = planInfoOptions;
} }
[Authorize] [Authorize]
@ -171,8 +172,8 @@ namespace EventHub.Organizations
[Authorize] [Authorize]
public async Task<List<PlanInfoDefinitionDto>> GetPlanInfosAsync() public async Task<List<PlanInfoDefinitionDto>> GetPlanInfosAsync()
{ {
var planInfoDefinitions = await _planInfoDefinitionStore.GetPlanInfosAsync(); var planInfoDefinitions = _planInfoOptions.Value.GetPlanInfos();
return ObjectMapper.Map<List<PlanInfoDefinition>, List<PlanInfoDefinitionDto>>(planInfoDefinitions); return await Task.FromResult(ObjectMapper.Map<List<PlanInfoDefinition>, List<PlanInfoDefinitionDto>>(planInfoDefinitions));
} }
private async Task SaveProfilePictureAsync(Guid id, IRemoteStreamContent streamContent) private async Task SaveProfilePictureAsync(Guid id, IRemoteStreamContent streamContent)

24
src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs

@ -41,7 +41,8 @@ namespace EventHub.Organizations
var queryable = await organizationRepository.GetQueryableAsync(); var queryable = await organizationRepository.GetQueryableAsync();
var query = queryable.Where(x => var query = queryable.Where(x =>
x.IsSendPaidEnrollmentReminderEmail == false && x.IsSendPaidEnrollmentReminderEmail == false &&
x.PaidEnrollmentEndDate <= oneMonthLater x.PaidEnrollmentEndDate <= oneMonthLater &&
x.PlanType != OrganizationPlanType.Free
); );
var organizations = await asyncExecuter.ToListAsync(query); var organizations = await asyncExecuter.ToListAsync(query);
@ -50,16 +51,13 @@ namespace EventHub.Organizations
{ {
try try
{ {
if (organization.PlanType != OrganizationPlanType.Free) if (organization.PaidEnrollmentEndDate >= clock.Now)
{ {
if (organization.PaidEnrollmentEndDate >= clock.Now) await organizationPlanEndDateNotifier.NotifyAsync(organization);
{
await organizationPlanEndDateNotifier.NotifyAsync(organization);
}
organization.IsSendPaidEnrollmentReminderEmail = true;
await organizationRepository.UpdateAsync(organization);
} }
organization.IsSendPaidEnrollmentReminderEmail = true;
await organizationRepository.UpdateAsync(organization);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -72,7 +70,8 @@ namespace EventHub.Organizations
{ {
var queryable = await organizationRepository.GetQueryableAsync(); var queryable = await organizationRepository.GetQueryableAsync();
var query = queryable.Where(x => var query = queryable.Where(x =>
x.PaidEnrollmentEndDate >= clock.Now x.PaidEnrollmentEndDate > clock.Now.Date &&
x.PlanType != OrganizationPlanType.Free
); );
var organizations = await asyncExecuter.ToListAsync(query); var organizations = await asyncExecuter.ToListAsync(query);
@ -81,11 +80,6 @@ namespace EventHub.Organizations
{ {
try try
{ {
if (organization.PlanType == OrganizationPlanType.Free)
{
continue;
}
organization.SetFreeToPlanType(); organization.SetFreeToPlanType();
organization.IsSendPaidEnrollmentReminderEmail = false; organization.IsSendPaidEnrollmentReminderEmail = false;
await organizationRepository.UpdateAsync(organization); await organizationRepository.UpdateAsync(organization);

4
src/EventHub.Domain/Events/EventManager.cs

@ -35,7 +35,7 @@ namespace EventHub.Events
DateTime endTime, DateTime endTime,
string description) string description)
{ {
if (!await _planFeatureManager.CanCreateNewEvent(organization.Id)) if (!await _planFeatureManager.CanCreateNewEventAsync(organization.Id))
{ {
throw new BusinessException(EventHubErrorCodes.CannotCreateNewEvent); throw new BusinessException(EventHubErrorCodes.CannotCreateNewEvent);
} }
@ -53,7 +53,7 @@ namespace EventHub.Events
public async Task AddTrackAsync(Event @event, string name) public async Task AddTrackAsync(Event @event, string name)
{ {
if (!await _planFeatureManager.CanAddNewTrack(@event)) if (!await _planFeatureManager.CanAddNewTrackAsync(@event))
{ {
throw new BusinessException(EventHubErrorCodes.CannotAddNewTrack); throw new BusinessException(EventHubErrorCodes.CannotAddNewTrack);
} }

2
src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs

@ -52,7 +52,7 @@ namespace EventHub.Events.Registrations
} }
} }
if (!await _planFeatureManager.CanRegisterToEvent(@event)) if (!await _planFeatureManager.CanRegisterToEventAsync(@event))
{ {
throw new BusinessException(EventHubErrorCodes.CannotRegisterToEvent); throw new BusinessException(EventHubErrorCodes.CannotRegisterToEvent);
} }

11
src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs

@ -7,17 +7,22 @@ namespace EventHub.Organizations.Plans;
public class FeatureOfPlanDefinition public class FeatureOfPlanDefinition
{ {
[CanBeNull] [CanBeNull]
[Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Range(0, uint.MaxValue)]
public uint? MaxAllowedEventsCountInOneYear { get; set; } public uint? MaxAllowedEventsCountInOneYear { get; set; }
[CanBeNull] [CanBeNull]
[Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Range(0, uint.MaxValue)]
public uint? MaxAllowedTracksCountInOneEvent { get; set; } public uint? MaxAllowedTracksCountInOneEvent { get; set; }
[CanBeNull] [CanBeNull]
[Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Range(0, uint.MaxValue)]
public uint? MaxAllowedAttendeesCountInOneEvent { get; set; } public uint? MaxAllowedAttendeesCountInOneEvent { get; set; }
[NotNull] [NotNull]
public List<string> AdditionalFeatureInfos { get; set; } public List<string> AdditionalFeatureInfos { get; set; }
public FeatureOfPlanDefinition()
{
AdditionalFeatureInfos = new List<string>();
}
} }

11
src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs

@ -1,11 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EventHub.Organizations.Plans;
public interface IPlanInfoDefinitionStore
{
Task<List<PlanInfoDefinition>> GetPlanInfosAsync();
Task<PlanInfoDefinition> GetPlanInfoByTypeAsync(OrganizationPlanType type);
}

34
src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs

@ -2,60 +2,60 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Events; using EventHub.Events;
using EventHub.Events.Registrations; using EventHub.Events.Registrations;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Timing;
namespace EventHub.Organizations.Plans; namespace EventHub.Organizations.Plans;
public class PlanFeatureManager : ITransientDependency public class PlanFeatureManager : ITransientDependency
{ {
private readonly IPlanInfoDefinitionStore _planInfoDefinitionStore; private readonly IOptions<PlanInfoOptions> _planInfoOptions;
private readonly IRepository<Organization, Guid> _organizationRepository; private readonly IRepository<Organization, Guid> _organizationRepository;
private readonly IRepository<Event, Guid> _eventRepository; private readonly IRepository<Event, Guid> _eventRepository;
private readonly IEventRegistrationRepository _eventRegistrationRepository; private readonly IEventRegistrationRepository _eventRegistrationRepository;
private readonly IClock _clock;
public PlanFeatureManager( public PlanFeatureManager(
IPlanInfoDefinitionStore planInfoDefinitionStore, IOptions<PlanInfoOptions> planInfoOptions,
IRepository<Organization, Guid> organizationRepository, IRepository<Organization, Guid> organizationRepository,
IRepository<Event, Guid> eventRepository, IRepository<Event, Guid> eventRepository,
IEventRegistrationRepository eventRegistrationRepository) IEventRegistrationRepository eventRegistrationRepository,
IClock clock)
{ {
_planInfoDefinitionStore = planInfoDefinitionStore; _planInfoOptions = planInfoOptions;
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_eventRepository = eventRepository; _eventRepository = eventRepository;
_eventRegistrationRepository = eventRegistrationRepository; _eventRegistrationRepository = eventRegistrationRepository;
_clock = clock;
} }
public virtual async Task<bool> CanCreateNewEvent(Guid organizationId) public virtual async Task<bool> CanCreateNewEventAsync(Guid organizationId)
{ {
var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(organizationId); var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(organizationId);
var totalEventCountByOrganizationInThisYear = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId && x.CreationTime.Year == _clock.Now.Year);
var totalEventCountByOrganization = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId && x.CreationTime.Year < DateTime.Now.Year + 1 && x.CreationTime.Year > DateTime.Now.Year - 1); return currentPlanOfOrganization.Feature.MaxAllowedEventsCountInOneYear > totalEventCountByOrganizationInThisYear;
totalEventCountByOrganization++;
return currentPlanOfOrganization.Feature.MaxAllowedEventsCountInOneYear >= totalEventCountByOrganization;
} }
public virtual async Task<bool> CanAddNewTrack(Event @event) public virtual async Task<bool> CanAddNewTrackAsync(Event @event)
{ {
var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId);
var tracksCount = @event.Tracks.Count; var tracksCount = @event.Tracks.Count;
tracksCount++; return currentPlanOfOrganization.Feature.MaxAllowedTracksCountInOneEvent > tracksCount;
return currentPlanOfOrganization.Feature.MaxAllowedTracksCountInOneEvent >= tracksCount;
} }
public virtual async Task<bool> CanRegisterToEvent(Event @event) public virtual async Task<bool> CanRegisterToEventAsync(Event @event)
{ {
var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId);
var attendeesCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); var attendeesCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id);
attendeesCount++; return currentPlanOfOrganization.Feature.MaxAllowedAttendeesCountInOneEvent > attendeesCount;
return currentPlanOfOrganization.Feature.MaxAllowedAttendeesCountInOneEvent >= attendeesCount;
} }
private async Task<PlanInfoDefinition> GetCurrentPlanByOrganizationIdAsync(Guid organizationId) private async Task<PlanInfoDefinition> GetCurrentPlanByOrganizationIdAsync(Guid organizationId)
{ {
var organization = await _organizationRepository.GetAsync(organizationId); var organization = await _organizationRepository.GetAsync(organizationId);
return await _planInfoDefinitionStore.GetPlanInfoByTypeAsync(organization.PlanType); return _planInfoOptions.Value.GetPlanInfoByType(organization.PlanType);
} }
} }

6
src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs

@ -17,17 +17,17 @@ public class PlanInfoDefinition
public bool IsActive { get; set; } public bool IsActive { get; set; }
[Range(1.0, 100.0, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Range(1.0, 100.0)]
public decimal Price { get; set; } = 1.0M; public decimal Price { get; set; } = 1.0M;
public bool IsExtendable { get; set; } public bool IsExtendable { get; set; }
[CanBeNull] [CanBeNull]
[Range(0, 12, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Range(0, 12)]
public int? CanBeExtendedAfterHowManyMonths { get; set; } public int? CanBeExtendedAfterHowManyMonths { get; set; }
[CanBeNull] [CanBeNull]
[Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Range(1, 24)]
public int? OnePaidEnrollmentPeriodAsMonth { get; set; } = 12; public int? OnePaidEnrollmentPeriodAsMonth { get; set; } = 12;
public FeatureOfPlanDefinition Feature { get; set; } public FeatureOfPlanDefinition Feature { get; set; }

27
src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs

@ -1,27 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace EventHub.Organizations.Plans;
public class PlanInfoDefinitionStore : IPlanInfoDefinitionStore, ITransientDependency
{
protected PlanInfoOptions PlanInfoOptions { get; }
public PlanInfoDefinitionStore(IOptions<PlanInfoOptions> planInfoOptions)
{
PlanInfoOptions = planInfoOptions.Value;
}
public async Task<List<PlanInfoDefinition>> GetPlanInfosAsync()
{
return await Task.FromResult(PlanInfoOptions.Infos);
}
public async Task<PlanInfoDefinition> GetPlanInfoByTypeAsync(OrganizationPlanType type)
{
return await Task.FromResult(PlanInfoOptions.Infos.SingleOrDefault(x => x.PlanType == type));
}
}

11
src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace EventHub.Organizations.Plans; namespace EventHub.Organizations.Plans;
@ -27,4 +28,14 @@ public class PlanInfoOptions
return this; return this;
} }
public List<PlanInfoDefinition> GetPlanInfos()
{
return Infos.Where(x => x.IsActive).ToList();
}
public PlanInfoDefinition GetPlanInfoByType(OrganizationPlanType type)
{
return Infos.SingleOrDefault(x => x.IsActive && x.PlanType == type);
}
} }

4
test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs

@ -58,8 +58,8 @@ namespace EventHub.Events.Registrations
var clock = Substitute.For<IClock>(); var clock = Substitute.For<IClock>();
clock.Now.Returns(DateTime.Now); clock.Now.Returns(DateTime.Now);
var planFeatureManager = Substitute.For<PlanFeatureManager>(null, null, null, null); var planFeatureManager = Substitute.For<PlanFeatureManager>(null, null, null, null, null);
planFeatureManager.CanRegisterToEvent(@event).Returns(Task.FromResult(true)); planFeatureManager.CanRegisterToEventAsync(@event).Returns(Task.FromResult(true));
var registrationManager = new EventRegistrationManager( var registrationManager = new EventRegistrationManager(
repository, guidGenerator, clock, planFeatureManager repository, guidGenerator, clock, planFeatureManager

Loading…
Cancel
Save