Skip to content

Commit a3313cb

Browse files
authored
Merge pull request #51 from EasyAbp/socket-update
Add SignalR-based real-time notification push support
2 parents 6ed488c + 45980fc commit a3313cb

15 files changed

Lines changed: 766 additions & 228 deletions

File tree

docs/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ An ABP module that helps define and track business processes.
2727

2828
2. Add `DependsOn(typeof(Abp.ProcessManagementXxxModule))` attribute to configure the module dependencies. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-module-dependencies))
2929

30+
3. Install the `@abp/signalr` npm package.
31+
32+
4. Add `app.UseWebSockets();` before `app.UseAuthentication();` in your middleware pipeline to enable WebSocket transport for SignalR.
33+
3034
## Usage
3135

3236
1. Define a process and states. For idempotency, stages can only transition from their father state.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using EasyAbp.ProcessManagement.Processes;
4+
using EasyAbp.ProcessManagement.UserGroups;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Volo.Abp.AspNetCore.Mvc;
7+
using Volo.Abp.Identity;
8+
using Volo.Abp.Timing;
9+
using Volo.Abp.Uow;
10+
11+
namespace EasyAbp.ProcessManagement.Controllers;
12+
13+
[Route("api/demo/notification")]
14+
public class DemoNotificationController : AbpController
15+
{
16+
private readonly IClock _clock;
17+
private readonly IdentityUserManager _identityUserManager;
18+
private readonly ProcessManager _processManager;
19+
private readonly IProcessRepository _processRepository;
20+
private readonly UserIdUserGroupContributor _userIdUserGroupContributor;
21+
22+
private static readonly string[] StateNames =
23+
["Ready", "Exporting", "Succeeded", "ExportFailed", "FailedToStartExporting"];
24+
25+
private static readonly ProcessStateFlag[] StateFlags =
26+
[ProcessStateFlag.Information, ProcessStateFlag.Running, ProcessStateFlag.Success, ProcessStateFlag.Failure, ProcessStateFlag.Failure];
27+
28+
private static readonly string[] SummaryTexts =
29+
[
30+
"Preparing to export...",
31+
"Loading the data...",
32+
"Congratulations! Export successful.",
33+
"Oops, the task failed!",
34+
"Could not start the exporting task."
35+
];
36+
37+
public DemoNotificationController(
38+
IClock clock,
39+
IdentityUserManager identityUserManager,
40+
ProcessManager processManager,
41+
IProcessRepository processRepository,
42+
UserIdUserGroupContributor userIdUserGroupContributor)
43+
{
44+
_clock = clock;
45+
_identityUserManager = identityUserManager;
46+
_processManager = processManager;
47+
_processRepository = processRepository;
48+
_userIdUserGroupContributor = userIdUserGroupContributor;
49+
}
50+
51+
[HttpPost]
52+
[Route("random")]
53+
[UnitOfWork]
54+
public virtual async Task<IActionResult> CreateRandomAsync()
55+
{
56+
var adminUser = await _identityUserManager.FindByNameAsync("admin");
57+
if (adminUser == null)
58+
{
59+
return NotFound("Admin user not found.");
60+
}
61+
62+
var now = _clock.Now;
63+
var random = new Random();
64+
var index = random.Next(StateNames.Length);
65+
66+
var groupKey = await _userIdUserGroupContributor.CreateGroupKeyAsync(adminUser.Id.ToString());
67+
68+
var process = await _processManager.CreateAsync(
69+
new CreateProcessModel("FakeExport", null, groupKey), now);
70+
71+
// Advance through the state machine based on the target state.
72+
// State machine: Ready -> FailedToStartExporting
73+
// Ready -> Exporting -> Succeeded | ExportFailed
74+
var targetState = StateNames[index];
75+
76+
if (targetState == "FailedToStartExporting")
77+
{
78+
// Branches directly from Ready
79+
await _processManager.UpdateStateAsync(process,
80+
new UpdateProcessStateModel(now.AddSeconds(1), targetState,
81+
targetState, StateFlags[index], SummaryTexts[index]));
82+
}
83+
else if (targetState != "Ready")
84+
{
85+
// Must go through Exporting first
86+
await _processManager.UpdateStateAsync(process,
87+
new UpdateProcessStateModel(now.AddSeconds(1), "Exporting",
88+
"Loading data", ProcessStateFlag.Running, "Loading the data..."));
89+
90+
if (targetState != "Exporting")
91+
{
92+
await _processManager.UpdateStateAsync(process,
93+
new UpdateProcessStateModel(now.AddSeconds(2), targetState,
94+
targetState, StateFlags[index], SummaryTexts[index]));
95+
}
96+
}
97+
98+
await _processRepository.InsertAsync(process, true);
99+
100+
return Ok(new
101+
{
102+
ProcessId = process.Id,
103+
StateName = process.StateName,
104+
StateFlag = process.StateFlag.ToString(),
105+
Summary = process.StateSummaryText
106+
});
107+
}
108+
}

host/EasyAbp.ProcessManagement.Web.Unified/ProcessManagementWebUnifiedModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ public async override Task OnApplicationInitializationAsync(ApplicationInitializ
235235
app.UseHttpsRedirection();
236236
app.UseRouting();
237237
app.MapAbpStaticAssets();
238+
app.UseWebSockets();
238239
app.UseAuthentication();
239240

240241
if (MultiTenancyConsts.IsEnabled)

host/EasyAbp.ProcessManagement.Web.Unified/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"name": "my-app",
44
"private": true,
55
"dependencies": {
6-
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.0.1"
6+
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.0.1",
7+
"@abp/signalr": "~5.0.1"
78
}
89
}

0 commit comments

Comments
 (0)