Add agent summary update functionality and improve user controller
- Introduced a new endpoint in UserController to update the agent summary, ensuring balance data is refreshed after transactions. - Implemented ForceUpdateSummaryImmediate method in IAgentGrain to allow immediate updates without cooldown checks. - Enhanced StartBotCommandHandler to force update the agent summary before starting the bot, ensuring accurate balance data. - Updated TypeScript API client to include the new update-agent-summary method for frontend integration.
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
using Managing.Api.Authorization;
|
using Managing.Api.Authorization;
|
||||||
using Managing.Api.Models.Requests;
|
using Managing.Api.Models.Requests;
|
||||||
|
using Managing.Application.Abstractions.Models;
|
||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.ManageBot.Commands;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@@ -18,6 +21,7 @@ public class UserController : BaseController
|
|||||||
private IConfiguration _config;
|
private IConfiguration _config;
|
||||||
private readonly IJwtUtils _jwtUtils;
|
private readonly IJwtUtils _jwtUtils;
|
||||||
private readonly IWebhookService _webhookService;
|
private readonly IWebhookService _webhookService;
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UserController"/> class.
|
/// Initializes a new instance of the <see cref="UserController"/> class.
|
||||||
@@ -26,13 +30,15 @@ public class UserController : BaseController
|
|||||||
/// <param name="userService">Service for user-related operations.</param>
|
/// <param name="userService">Service for user-related operations.</param>
|
||||||
/// <param name="jwtUtils">Utility for JWT token operations.</param>
|
/// <param name="jwtUtils">Utility for JWT token operations.</param>
|
||||||
/// <param name="webhookService">Service for webhook operations.</param>
|
/// <param name="webhookService">Service for webhook operations.</param>
|
||||||
|
/// <param name="mediator">Mediator for handling commands.</param>
|
||||||
public UserController(IConfiguration config, IUserService userService, IJwtUtils jwtUtils,
|
public UserController(IConfiguration config, IUserService userService, IJwtUtils jwtUtils,
|
||||||
IWebhookService webhookService)
|
IWebhookService webhookService, IMediator mediator)
|
||||||
: base(userService)
|
: base(userService)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_jwtUtils = jwtUtils;
|
_jwtUtils = jwtUtils;
|
||||||
_webhookService = webhookService;
|
_webhookService = webhookService;
|
||||||
|
_mediator = mediator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -158,7 +164,7 @@ public class UserController : BaseController
|
|||||||
var user = await GetUser();
|
var user = await GetUser();
|
||||||
// Map API request to DTO
|
// Map API request to DTO
|
||||||
// Note: IsGmxEnabled and DefaultExchange are not updatable via settings endpoint
|
// Note: IsGmxEnabled and DefaultExchange are not updatable via settings endpoint
|
||||||
var settingsDto = new Managing.Application.Abstractions.Models.UserSettingsDto
|
var settingsDto = new UserSettingsDto
|
||||||
{
|
{
|
||||||
LowEthAmountAlert = settings.LowEthAmountAlert,
|
LowEthAmountAlert = settings.LowEthAmountAlert,
|
||||||
EnableAutoswap = settings.EnableAutoswap,
|
EnableAutoswap = settings.EnableAutoswap,
|
||||||
@@ -174,4 +180,17 @@ public class UserController : BaseController
|
|||||||
var updatedUser = await _userService.UpdateUserSettings(user, settingsDto);
|
var updatedUser = await _userService.UpdateUserSettings(user, settingsDto);
|
||||||
return Ok(updatedUser);
|
return Ok(updatedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the agent summary by refreshing balance data and recalculating metrics.
|
||||||
|
/// Should be called after a topup/deposit to ensure the balance is up to date.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Success response.</returns>
|
||||||
|
[HttpPost("update-agent-summary")]
|
||||||
|
public async Task<ActionResult> UpdateAgentSummary()
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
await _mediator.Send(new UpdateAgentSummaryCommand(user));
|
||||||
|
return Ok(new { message = "Agent summary updated successfully" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,13 @@ namespace Managing.Application.Abstractions.Grains
|
|||||||
[OneWay]
|
[OneWay]
|
||||||
Task ForceUpdateSummary();
|
Task ForceUpdateSummary();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forces an immediate update of the agent summary without cooldown check (for critical updates like after topup)
|
||||||
|
/// Invalidates cached balance data to ensure fresh balance fetch
|
||||||
|
/// </summary>
|
||||||
|
[OneWay]
|
||||||
|
Task ForceUpdateSummaryImmediate();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the agent summary by recalculating from position data (used for initialization or manual refresh)
|
/// Updates the agent summary by recalculating from position data (used for initialization or manual refresh)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -176,6 +176,23 @@ public class AgentGrain : Grain, IAgentGrain
|
|||||||
await UpdateSummary();
|
await UpdateSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forces an immediate update of the agent summary without cooldown check (for critical updates like after topup)
|
||||||
|
/// Invalidates cached balance data to ensure fresh balance fetch
|
||||||
|
/// </summary>
|
||||||
|
public async Task ForceUpdateSummaryImmediate()
|
||||||
|
{
|
||||||
|
// Invalidate cached balance data to force fresh fetch
|
||||||
|
_state.State.CachedBalanceData = null;
|
||||||
|
await _state.WriteStateAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Force updating agent summary immediately for user {UserId} (cache invalidated)",
|
||||||
|
this.GetPrimaryKeyLong());
|
||||||
|
|
||||||
|
// Update summary immediately without cooldown check
|
||||||
|
await UpdateSummary();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the agent summary by recalculating from position data (used for initialization or manual refresh)
|
/// Updates the agent summary by recalculating from position data (used for initialization or manual refresh)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Managing.Domain.Users;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Managing.Application.ManageBot.Commands
|
||||||
|
{
|
||||||
|
public class UpdateAgentSummaryCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public User User { get; }
|
||||||
|
|
||||||
|
public UpdateAgentSummaryCommand(User user)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@ using Managing.Application.ManageBot.Commands;
|
|||||||
using Managing.Common;
|
using Managing.Common;
|
||||||
using Managing.Domain.Accounts;
|
using Managing.Domain.Accounts;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using static Managing.Common.Enums;
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
namespace Managing.Application.ManageBot
|
namespace Managing.Application.ManageBot
|
||||||
@@ -16,15 +17,22 @@ namespace Managing.Application.ManageBot
|
|||||||
private readonly IBotService _botService;
|
private readonly IBotService _botService;
|
||||||
private readonly ITradingService _tradingService;
|
private readonly ITradingService _tradingService;
|
||||||
private readonly IFlagsmithService _flagsmithService;
|
private readonly IFlagsmithService _flagsmithService;
|
||||||
|
private readonly ILogger<StartBotCommandHandler> _logger;
|
||||||
|
|
||||||
public StartBotCommandHandler(
|
public StartBotCommandHandler(
|
||||||
IAccountService accountService, IGrainFactory grainFactory, IBotService botService, ITradingService tradingService, IFlagsmithService flagsmithService)
|
IAccountService accountService,
|
||||||
|
IGrainFactory grainFactory,
|
||||||
|
IBotService botService,
|
||||||
|
ITradingService tradingService,
|
||||||
|
IFlagsmithService flagsmithService,
|
||||||
|
ILogger<StartBotCommandHandler> logger)
|
||||||
{
|
{
|
||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_grainFactory = grainFactory;
|
_grainFactory = grainFactory;
|
||||||
_botService = botService;
|
_botService = botService;
|
||||||
_tradingService = tradingService;
|
_tradingService = tradingService;
|
||||||
_flagsmithService = flagsmithService;
|
_flagsmithService = flagsmithService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BotStatus> Handle(StartBotCommand request, CancellationToken cancellationToken)
|
public async Task<BotStatus> Handle(StartBotCommand request, CancellationToken cancellationToken)
|
||||||
@@ -137,6 +145,19 @@ namespace Managing.Application.ManageBot
|
|||||||
$"Balance : {usdcBalance?.Value:F2 ?? 0} Available: {availableAllocation:F2} USDC.");
|
$"Balance : {usdcBalance?.Value:F2 ?? 0} Available: {availableAllocation:F2} USDC.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force update agent summary to ensure we have the latest balance before starting bot
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var agentGrain = _grainFactory.GetGrain<IAgentGrain>(request.User.Id);
|
||||||
|
await agentGrain.ForceUpdateSummaryImmediate();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log but don't fail - balance check already happened
|
||||||
|
// This is just to ensure summary is up to date before starting
|
||||||
|
_logger.LogWarning(ex, "Failed to update agent summary before starting bot for user {UserId}", request.User.Id);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var botGrain = _grainFactory.GetGrain<ILiveTradingBotGrain>(Guid.NewGuid());
|
var botGrain = _grainFactory.GetGrain<ILiveTradingBotGrain>(Guid.NewGuid());
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Managing.Application.Abstractions.Grains;
|
||||||
|
using Managing.Application.ManageBot.Commands;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Managing.Application.ManageBot
|
||||||
|
{
|
||||||
|
public class UpdateAgentSummaryCommandHandler : IRequestHandler<UpdateAgentSummaryCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IGrainFactory _grainFactory;
|
||||||
|
private readonly ILogger<UpdateAgentSummaryCommandHandler> _logger;
|
||||||
|
|
||||||
|
public UpdateAgentSummaryCommandHandler(
|
||||||
|
IGrainFactory grainFactory,
|
||||||
|
ILogger<UpdateAgentSummaryCommandHandler> logger)
|
||||||
|
{
|
||||||
|
_grainFactory = grainFactory;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Unit> Handle(UpdateAgentSummaryCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var agentGrain = _grainFactory.GetGrain<IAgentGrain>(request.User.Id);
|
||||||
|
await agentGrain.ForceUpdateSummaryImmediate();
|
||||||
|
|
||||||
|
_logger.LogInformation("Agent summary updated for user {UserId} after topup", request.User.Id);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error updating agent summary for user {UserId}", request.User.Id);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4461,6 +4461,46 @@ export class UserClient extends AuthorizedApiBase {
|
|||||||
}
|
}
|
||||||
return Promise.resolve<User>(null as any);
|
return Promise.resolve<User>(null as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user_UpdateAgentSummary(): Promise<FileResponse> {
|
||||||
|
let url_ = this.baseUrl + "/User/update-agent-summary";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_: RequestInit = {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/octet-stream"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||||
|
return this.http.fetch(url_, transformedOptions_);
|
||||||
|
}).then((_response: Response) => {
|
||||||
|
return this.processUser_UpdateAgentSummary(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processUser_UpdateAgentSummary(response: Response): Promise<FileResponse> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200 || status === 206) {
|
||||||
|
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||||
|
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
|
||||||
|
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
|
||||||
|
if (fileName) {
|
||||||
|
fileName = decodeURIComponent(fileName);
|
||||||
|
} else {
|
||||||
|
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||||
|
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||||
|
}
|
||||||
|
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<FileResponse>(null as any);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WhitelistClient extends AuthorizedApiBase {
|
export class WhitelistClient extends AuthorizedApiBase {
|
||||||
|
|||||||
Reference in New Issue
Block a user