Files
29WxOrder/OrderSupportTicket/Services/TicketService.cs
T
2026-10-20 17:03:23 +08:00

265 lines
7.1 KiB
C#

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