using OrderSupportTicket.Models;
using OrderSupportTicket.Models.Api;
using System;
using System.Threading.Tasks;
namespace OrderSupportTicket.Services;
///
/// 工单业务服务,聚合工单 API 客户端与 SSE 实时服务。
///
public sealed class TicketService : IDisposable
{
private readonly TicketApiService _api;
private readonly EventSourceTicketRealtimeService _realtime;
private bool _disposed;
///
/// 创建使用默认依赖的工单业务服务。
///
public TicketService(bool ownsRealtime = true)
: this(
new TicketApiService(),
new EventSourceTicketRealtimeService(),
ownsRealtime)
{
}
///
/// 创建工单业务服务。
///
/// 工单 API 客户端。
/// 工单实时通知服务。
///
/// 是否由当前实例负责实时连接的停止与释放;共享连接应传入 false。
///
public TicketService(
TicketApiService api,
EventSourceTicketRealtimeService realtime,
bool ownsRealtime = true)
{
_api = api ?? throw new ArgumentNullException(nameof(api));
_realtime = realtime ?? throw new ArgumentNullException(nameof(realtime));
OwnsRealtime = ownsRealtime;
_realtime.NotificationReceived += OnNotificationReceived;
_realtime.StateChanged += OnStateChanged;
}
///
/// 收到工单变更摘要通知时触发。
///
public event EventHandler NotificationReceived;
///
/// SSE 连接状态变化时触发。
///
public event EventHandler StateChanged;
///
/// 当前 SSE 连接状态。
///
public TicketRealtimeState RealtimeState => _realtime.State;
///
/// 是否由当前服务实例管理实时连接的生命周期。
///
public bool OwnsRealtime { get; }
///
/// 校验工单页运行参数。
///
public void ValidateOptions(TicketClientOptions options)
{
ThrowIfDisposed();
TicketApiService.ValidateOptions(options);
}
///
/// 查询工单摘要列表。
///
public Task GetWorkOrdersAsync(
TicketClientOptions options,
string wechatOrderId = null,
string status = null,
int limit = 20,
string cursor = null)
{
ThrowIfDisposed();
return _api.GetWorkOrdersAsync(
options,
wechatOrderId,
status,
limit,
cursor);
}
///
/// 查询指定订单的公开留言板。
///
public Task GetMessagesAsync(
TicketClientOptions options,
string appId,
string wechatOrderId,
string before = null,
int limit = 50)
{
ThrowIfDisposed();
return _api.GetMessagesAsync(
options,
appId,
wechatOrderId,
before,
limit);
}
///
/// 提交问题或创建工单。
///
public Task CreateWorkOrderAsync(
TicketClientOptions options,
TicketOrderOption order,
string description,
string clientRequestId = null)
{
ThrowIfDisposed();
return _api.CreateWorkOrderAsync(
options,
order,
description,
clientRequestId);
}
///
/// 提交客户留言。
///
public Task SendMessageAsync(
TicketClientOptions options,
WorkOrderSummaryDto workOrder,
string text,
string clientMessageId,
IList attachments = null)
{
ThrowIfDisposed();
return _api.SendMessageAsync(
options,
workOrder,
text,
clientMessageId,
attachments);
}
///
/// 申请单张图片的短时直传凭证。
///
public Task CreateImageUploadTicketAsync(
TicketClientOptions options,
string appId,
string wechatOrderId,
string fileName,
string mimeType,
long size,
string requestId = null)
{
ThrowIfDisposed();
return _api.CreateImageUploadTicketAsync(
options,
appId,
wechatOrderId,
fileName,
mimeType,
size,
requestId);
}
///
/// 将图片二进制直接上传到 COS,返回留言接口需要的回执。
///
public Task UploadImageAsync(
ImageUploadTicketDto upload,
string filePath)
{
ThrowIfDisposed();
return _api.UploadImageAsync(upload, filePath);
}
///
/// 标记当前订单留言已读。
///
public Task MarkReadAsync(
TicketClientOptions options,
string appId,
string wechatOrderId,
int throughSequenceNo)
{
ThrowIfDisposed();
return _api.MarkReadAsync(
options,
appId,
wechatOrderId,
throughSequenceNo);
}
///
/// 使用指定工单参数建立 SSE 连接。
///
public async Task StartRealtime(TicketClientOptions options)
{
ThrowIfDisposed();
ValidateOptions(options);
await _realtime.Start(options);
}
///
/// 停止 SSE 连接;共享连接由宿主管理时,调用方应先检查 。
///
public Task StopRealtimeAsync()
{
ThrowIfDisposed();
return _realtime.StopAsync();
}
///
/// 停止并重新建立 SSE 连接。
///
public async Task RestartRealtimeAsync(TicketClientOptions options)
{
ThrowIfDisposed();
await _realtime.StopAsync();
ThrowIfDisposed();
await StartRealtime(options);
}
public void Dispose()
{
if (_disposed)
return;
_disposed = true;
_realtime.NotificationReceived -= OnNotificationReceived;
_realtime.StateChanged -= OnStateChanged;
if (OwnsRealtime)
_realtime.Dispose();
}
private void OnNotificationReceived(
object sender,
TicketRealtimeNotificationEventArgs args)
{
NotificationReceived?.Invoke(this, args);
}
private void OnStateChanged(
object sender,
TicketRealtimeStateChangedEventArgs args)
{
StateChanged?.Invoke(this, args);
}
private void ThrowIfDisposed()
{
if (_disposed)
throw new ObjectDisposedException(nameof(TicketService));
}
}