Implement bidirectional customer conversations with customer-based grouping and order tagging

This commit is contained in:
sysadmin
2025-08-27 19:18:46 +01:00
parent 3f4789730c
commit 027a3fd0c4
25 changed files with 794 additions and 70 deletions

View File

@@ -87,6 +87,21 @@ public static class ServiceCollectionExtensions
var options = serviceProvider.GetRequiredService<IOptions<LittleShopClientOptions>>().Value;
return new RetryPolicyHandler(logger, options.MaxRetryAttempts);
});
services.AddHttpClient<IMessageService, MessageService>((serviceProvider, client) =>
{
var options = serviceProvider.GetRequiredService<IOptions<LittleShopClientOptions>>().Value;
client.BaseAddress = new Uri(options.BaseUrl);
client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.AddHttpMessageHandler<ErrorHandlingMiddleware>()
.AddHttpMessageHandler(serviceProvider =>
{
var logger = serviceProvider.GetRequiredService<ILogger<RetryPolicyHandler>>();
var options = serviceProvider.GetRequiredService<IOptions<LittleShopClientOptions>>().Value;
return new RetryPolicyHandler(logger, options.MaxRetryAttempts);
});
// Register the main client
services.AddScoped<ILittleShopClient, LittleShopClient>();

View File

@@ -10,6 +10,7 @@ public interface ILittleShopClient
ICatalogService Catalog { get; }
IOrderService Orders { get; }
ICustomerService Customers { get; }
IMessageService Messages { get; }
}
public class LittleShopClient : ILittleShopClient
@@ -18,16 +19,19 @@ public class LittleShopClient : ILittleShopClient
public ICatalogService Catalog { get; }
public IOrderService Orders { get; }
public ICustomerService Customers { get; }
public IMessageService Messages { get; }
public LittleShopClient(
IAuthenticationService authenticationService,
ICatalogService catalogService,
IOrderService orderService,
ICustomerService customerService)
ICustomerService customerService,
IMessageService messageService)
{
Authentication = authenticationService;
Catalog = catalogService;
Orders = orderService;
Customers = customerService;
Messages = messageService;
}
}

View File

@@ -0,0 +1,24 @@
namespace LittleShop.Client.Models;
public class CustomerMessage
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public long TelegramUserId { get; set; }
public string Subject { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public MessageType Type { get; set; }
public bool IsUrgent { get; set; }
public string? OrderReference { get; set; }
public DateTime CreatedAt { get; set; }
}
public enum MessageType
{
OrderUpdate = 0,
PaymentReminder = 1,
ShippingInfo = 2,
CustomerService = 3,
Marketing = 4,
SystemAlert = 5
}

View File

@@ -0,0 +1,11 @@
using LittleShop.Client.Models;
namespace LittleShop.Client.Services;
public interface IMessageService
{
Task<List<CustomerMessage>> GetPendingMessagesAsync(string platform = "Telegram");
Task<bool> MarkMessageAsSentAsync(Guid messageId, string? platformMessageId = null);
Task<bool> MarkMessageAsFailedAsync(Guid messageId, string reason);
Task<bool> CreateCustomerMessageAsync(object messageData);
}

View File

@@ -0,0 +1,87 @@
using System.Net.Http.Json;
using Microsoft.Extensions.Logging;
using LittleShop.Client.Models;
namespace LittleShop.Client.Services;
public class MessageService : IMessageService
{
private readonly HttpClient _httpClient;
private readonly ILogger<MessageService> _logger;
public MessageService(HttpClient httpClient, ILogger<MessageService> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public async Task<List<CustomerMessage>> GetPendingMessagesAsync(string platform = "Telegram")
{
try
{
var response = await _httpClient.GetAsync($"api/bot/messages/pending?platform={platform}");
if (response.IsSuccessStatusCode)
{
var messages = await response.Content.ReadFromJsonAsync<List<CustomerMessage>>();
return messages ?? new List<CustomerMessage>();
}
_logger.LogWarning("Failed to get pending messages: {StatusCode}", response.StatusCode);
return new List<CustomerMessage>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting pending messages");
return new List<CustomerMessage>();
}
}
public async Task<bool> MarkMessageAsSentAsync(Guid messageId, string? platformMessageId = null)
{
try
{
var url = $"api/bot/messages/{messageId}/mark-sent";
if (!string.IsNullOrEmpty(platformMessageId))
{
url += $"?platformMessageId={platformMessageId}";
}
var response = await _httpClient.PostAsync(url, null);
return response.IsSuccessStatusCode;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error marking message {MessageId} as sent", messageId);
return false;
}
}
public async Task<bool> MarkMessageAsFailedAsync(Guid messageId, string reason)
{
try
{
var response = await _httpClient.PostAsJsonAsync($"api/bot/messages/{messageId}/mark-failed", reason);
return response.IsSuccessStatusCode;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error marking message {MessageId} as failed", messageId);
return false;
}
}
public async Task<bool> CreateCustomerMessageAsync(object messageData)
{
try
{
var response = await _httpClient.PostAsJsonAsync("api/bot/messages/customer-create", messageData);
return response.IsSuccessStatusCode;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating customer message");
return false;
}
}
}