-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from EasyAbp/state-update
State update refactor
- Loading branch information
Showing
14 changed files
with
336 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
...cessManagement.Domain/EasyAbp/ProcessManagement/Options/UndefinedProcessStateException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Volo.Abp; | ||
|
||
namespace EasyAbp.ProcessManagement.Options; | ||
|
||
public class UndefinedProcessStateException : AbpException | ||
{ | ||
public UndefinedProcessStateException(string stateName, string processName) : base( | ||
$"State `{stateName}` is undefined for the process `{processName}`") | ||
{ | ||
} | ||
} |
5 changes: 4 additions & 1 deletion
5
....Domain/EasyAbp/ProcessManagement/ProcessStateHistories/IProcessStateHistoryRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using Volo.Abp.Domain.Repositories; | ||
|
||
namespace EasyAbp.ProcessManagement.ProcessStateHistories; | ||
|
||
public interface IProcessStateHistoryRepository : IRepository<ProcessStateHistory, Guid> | ||
{ | ||
} | ||
Task<List<ProcessStateHistory>> GetHistoriesByStateNameAsync(Guid processId, string stateName); | ||
} |
31 changes: 0 additions & 31 deletions
31
...Domain/EasyAbp/ProcessManagement/ProcessStateHistories/ProcessStateChangedEventHandler.cs
This file was deleted.
Oops, something went wrong.
13 changes: 13 additions & 0 deletions
13
...sManagement.Domain/EasyAbp/ProcessManagement/Processes/InvalidStateUpdateTimeException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
using Volo.Abp; | ||
|
||
namespace EasyAbp.ProcessManagement.Processes; | ||
|
||
public class InvalidStateUpdateTimeException : AbpException | ||
{ | ||
public InvalidStateUpdateTimeException(string stateName, string processName, Guid processId) : base( | ||
$"Failed to update to the state `{stateName}` for the process " + | ||
$"`{processName}`(id: {processId}) since the StateUpdateTime is less than the current") | ||
{ | ||
} | ||
} |
100 changes: 83 additions & 17 deletions
100
src/EasyAbp.ProcessManagement.Domain/EasyAbp/ProcessManagement/Processes/ProcessManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,115 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using EasyAbp.ProcessManagement.Options; | ||
using EasyAbp.ProcessManagement.ProcessStateHistories; | ||
using Microsoft.Extensions.Options; | ||
using Volo.Abp; | ||
using Volo.Abp.Domain.Services; | ||
using Volo.Abp.Uow; | ||
|
||
namespace EasyAbp.ProcessManagement.Processes; | ||
|
||
public class ProcessManager : DomainService | ||
{ | ||
protected ProcessManagementOptions Options { get; } | ||
protected ProcessManagementOptions Options => | ||
LazyServiceProvider.LazyGetRequiredService<IOptions<ProcessManagementOptions>>().Value; | ||
|
||
public ProcessManager(IOptions<ProcessManagementOptions> options) | ||
{ | ||
Options = options.Value; | ||
} | ||
protected IProcessStateHistoryRepository ProcessStateHistoryRepository => LazyServiceProvider | ||
.LazyGetRequiredService<IProcessStateHistoryRepository>(); | ||
|
||
public virtual Task<Process> CreateAsync(CreateProcessModel model, DateTime now) | ||
public virtual async Task<Process> CreateAsync(CreateProcessModel model, DateTime now) | ||
{ | ||
var processDefinition = Options.GetProcessDefinition(model.ProcessName); | ||
|
||
var id = GuidGenerator.Create(); | ||
|
||
return Task.FromResult(new Process(id, CurrentTenant.Id, processDefinition, now, model.GroupKey, | ||
model.CorrelationId ?? id.ToString(), model)); | ||
var process = new Process(id, CurrentTenant.Id, processDefinition, now, model.GroupKey, | ||
model.CorrelationId ?? id.ToString(), model); | ||
|
||
await RecordStateHistoryAsync(process.Id, process); | ||
|
||
return process; | ||
} | ||
|
||
public virtual Task UpdateStateAsync(Process process, IProcessState nextState) | ||
[UnitOfWork] | ||
public virtual async Task UpdateStateAsync(Process process, IProcessState nextState) | ||
{ | ||
if (nextState.StateName != process.StateName) | ||
{ | ||
var processDefinition = Options.GetProcessDefinition(process.ProcessName); | ||
await UpdateToDifferentStateAsync(process, nextState); | ||
} | ||
else | ||
{ | ||
await UpdateStateCustomInfoAsync(process, nextState); | ||
} | ||
} | ||
|
||
var nextStates = processDefinition.GetChildrenStateNames(process.StateName); | ||
[UnitOfWork] | ||
protected virtual async Task UpdateToDifferentStateAsync(Process process, IProcessState state) | ||
{ | ||
var processDefinition = Options.GetProcessDefinition(process.ProcessName); | ||
|
||
var availableStates = processDefinition.GetChildrenStateNames(process.StateName); | ||
|
||
if (!nextStates.Contains(nextState.StateName)) | ||
if (availableStates.Contains(state.StateName)) | ||
{ | ||
if (state.StateUpdateTime <= process.StateUpdateTime) | ||
{ | ||
throw new AbpException( | ||
$"The specified state `{nextState.StateName}` is invalid for the process `{process.ProcessName}`"); | ||
throw new InvalidStateUpdateTimeException(state.StateName, process.ProcessName, process.Id); | ||
} | ||
|
||
process.SetState(state); | ||
|
||
await RecordStateHistoryAsync(process.Id, state); | ||
} | ||
else | ||
{ | ||
// get or throw. | ||
processDefinition.GetState(state.StateName); | ||
|
||
process.SetState(nextState); | ||
/* If this incoming state is a descendant of the current state, it will be accepted in the future. | ||
* So we throw an exception and skip handling it this time. | ||
* The next time the event handling is attempted, it may succeed. | ||
*/ | ||
if (processDefinition.IsDescendantState(state.StateName, process.StateName)) | ||
{ | ||
throw new UpdatingToFutureStateException(state.StateName, process.ProcessName, process.Id); | ||
} | ||
|
||
/* | ||
* Or, the process has been updated to this incoming state before, we just record the state history. | ||
*/ | ||
if ((await ProcessStateHistoryRepository.GetHistoriesByStateNameAsync( | ||
process.Id, state.StateName)).Count != 0) | ||
{ | ||
await RecordStateHistoryAsync(process.Id, state); | ||
return; | ||
} | ||
|
||
/* | ||
* Otherwise, this incoming state will never succeed, we don't handle it. | ||
*/ | ||
throw new UpdatingToNonDescendantStateException(state.StateName, process.ProcessName, process.Id); | ||
} | ||
} | ||
|
||
return Task.CompletedTask; | ||
protected virtual async Task UpdateStateCustomInfoAsync(Process process, IProcessState state) | ||
{ | ||
/* If it receives a state update event out of order (event.StateUpdateTime < process.StateUpdateTime), | ||
* we will only add a new state history entity without updating the process entity properties. | ||
*/ | ||
if (state.StateUpdateTime > process.StateUpdateTime) | ||
{ | ||
process.SetState(state); | ||
} | ||
|
||
await RecordStateHistoryAsync(process.Id, state); | ||
} | ||
|
||
[UnitOfWork] | ||
protected virtual async Task<ProcessStateHistory> RecordStateHistoryAsync(Guid processId, IProcessState state) | ||
{ | ||
return await ProcessStateHistoryRepository.InsertAsync( | ||
new ProcessStateHistory(GuidGenerator.Create(), CurrentTenant.Id, processId, state), true); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
...ssManagement.Domain/EasyAbp/ProcessManagement/Processes/UpdatingToFutureStateException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using Volo.Abp; | ||
|
||
namespace EasyAbp.ProcessManagement.Processes; | ||
|
||
public class UpdatingToFutureStateException : AbpException | ||
{ | ||
public UpdatingToFutureStateException(string stateName, string processName, Guid processId) : base( | ||
$"Failed to update to the state `{stateName}` for the process `{processName}` (id: {processId}) since " + | ||
$"it's not a child. However, it's a descendant state, this error may be caused by the event disorder, " + | ||
$"so the next time the event handling is attempted, it may succeed.") | ||
{ | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...ement.Domain/EasyAbp/ProcessManagement/Processes/UpdatingToNonDescendantStateException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
using Volo.Abp; | ||
|
||
namespace EasyAbp.ProcessManagement.Processes; | ||
|
||
public class UpdatingToNonDescendantStateException : AbpException | ||
{ | ||
public UpdatingToNonDescendantStateException(string stateName, string processName, Guid processId) : base( | ||
$"Skipping the event handling because the specified state `{stateName}` is not a descendant state of " + | ||
$"the current state for the process `{processName}` (id: {processId})") | ||
{ | ||
} | ||
} |
15 changes: 13 additions & 2 deletions
15
...cessManagement/EntityFrameworkCore/ProcessStateHistories/ProcessStateHistoryRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,31 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using EasyAbp.ProcessManagement.ProcessStateHistories; | ||
using Microsoft.EntityFrameworkCore; | ||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; | ||
using Volo.Abp.EntityFrameworkCore; | ||
|
||
namespace EasyAbp.ProcessManagement.EntityFrameworkCore.ProcessStateHistories; | ||
|
||
public class ProcessStateHistoryRepository : EfCoreRepository<IProcessManagementDbContext, ProcessStateHistory, Guid>, IProcessStateHistoryRepository | ||
public class ProcessStateHistoryRepository : EfCoreRepository<IProcessManagementDbContext, ProcessStateHistory, Guid>, | ||
IProcessStateHistoryRepository | ||
{ | ||
public ProcessStateHistoryRepository(IDbContextProvider<IProcessManagementDbContext> dbContextProvider) : base(dbContextProvider) | ||
public ProcessStateHistoryRepository(IDbContextProvider<IProcessManagementDbContext> dbContextProvider) : base( | ||
dbContextProvider) | ||
{ | ||
} | ||
|
||
public override async Task<IQueryable<ProcessStateHistory>> WithDetailsAsync() | ||
{ | ||
return (await GetQueryableAsync()).IncludeDetails(); | ||
} | ||
|
||
public virtual async Task<List<ProcessStateHistory>> GetHistoriesByStateNameAsync(Guid processId, string stateName) | ||
{ | ||
return await (await GetQueryableAsync()) | ||
.Where(x => x.ProcessId == processId && x.StateName == stateName) | ||
.ToListAsync(); | ||
} | ||
} |
20 changes: 0 additions & 20 deletions
20
test/EasyAbp.ProcessManagement.Domain.Tests/Processes/ProcessDomainTests.cs
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.