This commit is contained in:
wule committed 2026-10-20 14:14:00 +08:00
commit 0b94c43fab
13 files changed
+600 -573

No files matched your search

+6 -9
View File
@@ -106,10 +106,10 @@ namespace _29WxOrderWpf
pddBuyerOrderReadCtrl.Visibility = Visibility.Visible;
tbBuyerOrderReadCtrl.Visibility = Visibility.Visible;
pddBuyerSelectCtrl.Visibility = Visibility.Visible;
Task.Run(() =>
Task.Run(async () =>
{
GetServerShop();
StartTicketRealtimeMonitor();//启动工单监控
GetServerShop();
await StartTicketRealtimeMonitor();//启动工单监控
GetShopSupplier(GlobalConfig.ShopLoginList.ToList());
});
Task.Run(() =>
@@ -609,12 +609,12 @@ namespace _29WxOrderWpf
_ticketMonitor.MarkAllRead();
}
private void StartTicketRealtimeMonitor()
private async Task StartTicketRealtimeMonitor()
{
try
{
var options = GetTicketOptions().Clone();
_ticketMonitor.Start(options);
await _ticketMonitor.Start(options);
UpdateOrderSupportBadge();
_ = LoadHistoricalTicketUnreadAsync(options);
}
@@ -637,7 +637,7 @@ namespace _29WxOrderWpf
}
catch (Exception ex)
{
LogSave.Add("TicketUnreadLoadError:" + ex);
NLogger.Instance.Error("TicketUnreadLoadError:" + ex);
}
}
@@ -652,9 +652,6 @@ namespace _29WxOrderWpf
AppIds = GlobalConfig.ShopLoginList.Select(a => a.ShopId).ToList(),
Orders = []
};
//OrderSupportTicketView
// .CreateDefaultOptions(BaseClass.UserName)
// .Clone();
+1 -1
View File
@@ -37,7 +37,7 @@ namespace AllBase
/// 版本ID
/// </summary>
public static int VersionCount = 337;
public static int VersionCount = 339;
public static string ServerUrl { get; set; } = "http://1.13.195.131:9000";
public static string DebugUrl { get; set; } = "http://localhost:9000";
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using WxOrder.Framework.Helpers;
namespace LaunchDarkly.EventSource
{
@@ -114,6 +115,7 @@ namespace LaunchDarkly.EventSource
/// This is never null; if logging is not configured, it will be <c>LaunchDarkly.Logging.Logs.None</c>.
/// </remarks>
/// <seealso cref="ConfigurationBuilder.Logger(Logger)"/>
public ILogger Logger { get; }
/// <summary>
/// The maximum amount of time to wait before attempting to reconnect.
@@ -174,13 +176,14 @@ namespace LaunchDarkly.EventSource
{
Uri = builder._uri;
var logger = builder._logger;
BackoffResetThreshold = builder._backoffResetThreshold;
HttpClient = builder._httpClient;
HttpMessageHandler = (builder._httpClient != null) ? null : builder._httpMessageHandler;
InitialRetryDelay = builder._initialRetryDelay;
LastEventId = builder._lastEventId;
Logger = logger;
MaxRetryDelay = builder._maxRetryDelay;
Method = builder._method;
PreferDataAsUtf8Bytes = builder._preferDataAsUtf8Bytes;
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading;
using WxOrder.Framework.Helpers;
namespace LaunchDarkly.EventSource
{
@@ -30,6 +31,7 @@ namespace LaunchDarkly.EventSource
internal TimeSpan _initialRetryDelay = Configuration.DefaultInitialRetryDelay;
internal TimeSpan _backoffResetThreshold = Configuration.DefaultBackoffResetThreshold;
internal string _lastEventId;
internal ILogger _logger;
internal IDictionary<string, string> _requestHeaders = new Dictionary<string, string>();
internal HttpMessageHandler _httpMessageHandler;
internal HttpClient _httpClient;
@@ -230,45 +232,6 @@ namespace LaunchDarkly.EventSource
return this;
}
/// <summary>
/// Sets the logging implementation to be used for all EventSource log output.
/// </summary>
/// <remarks>
/// <para>
/// This uses the <c>ILogAdapter</c> abstraction from the <c>LaunchDarkly.Logging</c> library,
/// which provides several basic implementations such as <c>Logs.ToConsole</c> and an integration
/// with the .NET Core logging framework. For more about this and about adapters to other logging
/// frameworks, see <a href="https://github.com/launchdarkly/dotnet-logging"><c>LaunchDarkly.Logging</c></a>.
/// </para>
/// <para>
/// <c>LaunchDarkly.Logging</c> defines logging levels of Debug, Info, Warn, and Error. If you do not
/// want detailed Debug-level logging, use the <c>Level()</c> modifier to set a minimum level of Info
/// or above, as shown in the code example (unless you are using an adapter to another logging
/// framework that has its own way of doing log filtering).
/// </para>
/// <para>
/// Log messages will use <see cref="Configuration.DefaultLoggerName"/> as the logger name. If you
/// want to specify a different logger name, use <see cref="Logger(Logging.Logger)"/>.
/// </para>
/// <para>
/// If you don't specify <see cref="LogAdapter(ILogAdapter)"/> or <see cref="Logger(Logging.Logger)"/>,
/// EventSource will not do any logging.
/// </para>
/// </remarks>
/// <example>
/// using LaunchDarkly.Logging;
///
/// // Send log output to the console (standard error), suppressing Debug messages
/// var config = new ConfigurationBuilder(uri).
/// LogAdapter(Logs.ToConsole.Level(LogLevel.Info)).
/// Build();
/// </example>
/// <param name="logAdapter">a <c>LaunchDarkly.Logging.ILogAdapter</c></param>
/// <returns>the builder</returns>
public ConfigurationBuilder LogAdapter()
{
return this;
}
/// <summary>
/// Sets a custom logger to be used for all EventSource log output.
@@ -297,8 +260,9 @@ namespace LaunchDarkly.EventSource
/// </example>
/// <param name="logger">a <c>LaunchDarkly.Logging.Logger</c> instance</param>
/// <returns>the builder</returns>
public ConfigurationBuilder Logger()
public ConfigurationBuilder Logger(ILogger logger)
{
_logger = logger;
return this;
}
File diff suppressed because it is too large. Load diff
@@ -7,6 +7,7 @@ using System.Net.Http.Headers;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using WxOrder.Framework.Helpers;
using static LaunchDarkly.EventSource.AsyncHelpers;
@@ -23,6 +24,7 @@ namespace LaunchDarkly.EventSource
private readonly Configuration _configuration;
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
private const string UserAgentProduct = "DotNetClient";
internal static readonly string UserAgentVersion = ((AssemblyInformationalVersionAttribute)typeof(EventSource)
@@ -48,10 +50,11 @@ namespace LaunchDarkly.EventSource
#region Constructors
internal EventSourceService(Configuration configuration, HttpClient httpClient)
internal EventSourceService(Configuration configuration, HttpClient httpClient, ILogger logger)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
#endregion
@@ -85,16 +88,20 @@ namespace LaunchDarkly.EventSource
CancellationToken cancellationToken
)
{
_logger.Debug(string.Format("Making {0} request to EventSource URI {1}",
_configuration.Method ?? HttpMethod.Get,
_configuration.Uri));
var request = CreateHttpRequestMessage(_configuration.Uri, lastEventId);
this._configuration.HttpRequestModifier?.Invoke(request);
using (var response = await _httpClient.SendAsync(request,
using (var response = await _httpClient.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken).ConfigureAwait(false))
{
ValidateResponse(response);
_logger.Debug(string.Format("Response status: {0}", (int)response.StatusCode));
ValidateResponse(response);
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
@@ -109,10 +116,12 @@ namespace LaunchDarkly.EventSource
if (_configuration.PreferDataAsUtf8Bytes)
{
_logger.Debug("Reading UTF-8 stream without string conversion");
await ProcessResponseFromUtf8StreamAsync(processResponseLineUTF8, stream, cancellationToken);
}
else
{
_logger.Debug("Reading stream with string conversion");
using (var reader = new StreamReader(stream, Encoding.UTF8,
detectEncodingFromByteOrderMarks: true, bufferSize: ReadBufferSize))
{
@@ -5,6 +5,8 @@ using OrderSupportTicket.Models;
using OrderSupportTicket.Models.Api;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using WxOrder.Framework.Helpers;
@@ -31,7 +33,7 @@ public sealed class EventSourceTicketRealtimeService
/// </summary>
public TicketRealtimeState State { get; private set; } = TicketRealtimeState.Disconnected;
public void Start(TicketClientOptions options)
public async Task Start(TicketClientOptions options)
{
if (_disposed)
throw new ObjectDisposedException(nameof(EventSourceTicketRealtimeService));
@@ -40,9 +42,8 @@ public sealed class EventSourceTicketRealtimeService
TicketApiService.ValidateOptions(options);
StopCore();
var uri = BuildEventsUri(options);
var source = new EventSource(uri);
var config = BuildEventsUri(options);
var source = new EventSource(config);
source.Opened += OnOpened;
source.Closed += OnClosed;
source.MessageReceived += OnMessageReceived;
@@ -55,7 +56,7 @@ public sealed class EventSourceTicketRealtimeService
}
SetState(TicketRealtimeState.Connecting, "连接中…");
_ = RunAsync(source);
await RunAsync(source);
}
public Task StopAsync()
@@ -77,7 +78,6 @@ public sealed class EventSourceTicketRealtimeService
{
try
{
NLogger.Instance.Info("工单服务连接成");
await source.StartAsync();
lock (_sync)
{
@@ -182,22 +182,53 @@ public sealed class EventSourceTicketRealtimeService
new TicketRealtimeStateChangedEventArgs(state, message));
}
private static Uri BuildEventsUri(TicketClientOptions options)
//private static Uri BuildEventsUri(TicketClientOptions options)
//{
// var appIds = options.AppIds
// .Where(a => !string.IsNullOrWhiteSpace(a))
// .Distinct(StringComparer.Ordinal)
// .Select(a => "appId=" + Uri.EscapeDataString(a.Trim()));
// var query = new[]
// {
// "purchaseAccount=" + Uri.EscapeDataString(options.UserName)
// }.Concat(appIds);
// return new Uri(
// TicketApiService.ServerUrl
// + EventsPath
// + "?"
// + string.Join("&", query),
// UriKind.Absolute);
//}
private static Configuration BuildEventsUri(TicketClientOptions options)
{
var appIds = options.AppIds
.Where(a => !string.IsNullOrWhiteSpace(a))
.Distinct(StringComparer.Ordinal)
.Select(a => "appId=" + Uri.EscapeDataString(a.Trim()));
.Select(a => Uri.EscapeDataString(a.Trim()));
var query = new[]
{
"purchaseAccount=" + Uri.EscapeDataString(options.UserName)
}.Concat(appIds);
return new Uri(
};
var uri = new Uri(
TicketApiService.ServerUrl
+ EventsPath
+ "?"
+ string.Join("&", query),
UriKind.Absolute);
ConfigurationBuilder builder = Configuration.Builder(uri);
builder.RequestHeader("X-AppIds", $"[\"{string.Join("\",\"", appIds)}\"]");
builder.Logger(NLogger.Instance);
//var proxy = new WebProxy("http://127.0.0.1:8081"); //
//var handler = new HttpClientHandler
//{
// Proxy = proxy,
// UseProxy = true
//};
//var client = new HttpClient(handler);
//builder.HttpClient(client);
builder._responseStartTimeout = TimeSpan.FromSeconds(30);
return builder.Build();
}
}
@@ -372,8 +372,8 @@ public sealed class TicketApiService
.ToList();
if (normalized.Count == 0)
throw new ArgumentException("appIds 至少需要一个 APPID。", nameof(appIds));
if (normalized.Count > 500)
throw new ArgumentException("appIds 最多支持 50 个 APPID。", nameof(appIds));
//if (normalized.Count > 5000)
// throw new ArgumentException("appIds 最多支持 50 个 APPID。", nameof(appIds));
foreach (var appId in normalized)
ValidateAppId(appId);
return normalized;
@@ -91,7 +91,7 @@ public sealed class TicketRealtimeMonitor : IDisposable
/// <summary>
/// 启动或复用实时连接。相同账号和 APPID 重复调用不会重建连接。
/// </summary>
public void Start(TicketClientOptions options)
public async Task Start(TicketClientOptions options)
{
if (_disposed)
throw new ObjectDisposedException(nameof(TicketRealtimeMonitor));
@@ -113,7 +113,7 @@ public sealed class TicketRealtimeMonitor : IDisposable
_optionsFingerprint = fingerprint;
}
RealtimeService.Start(options);
await RealtimeService.Start(options);
}
/// <summary>
+3 -3
View File
@@ -167,11 +167,11 @@ public sealed class TicketService : IDisposable
/// <summary>
/// 使用指定工单参数建立 SSE 连接。
/// </summary>
public void StartRealtime(TicketClientOptions options)
public async Task StartRealtime(TicketClientOptions options)
{
ThrowIfDisposed();
ValidateOptions(options);
_realtime.Start(options);
await _realtime.Start(options);
}
/// <summary>
@@ -191,7 +191,7 @@ public sealed class TicketService : IDisposable
ThrowIfDisposed();
await _realtime.StopAsync();
ThrowIfDisposed();
StartRealtime(options);
await StartRealtime(options);
}
public void Dispose()
@@ -714,7 +714,7 @@ public sealed class OrderSupportTicketViewModel : BindableBaseEx, IDialogAware,
|| (_ticketService.OwnsRealtime && !_initialized);
if (shouldStartRealtime)
{
_ticketService.StartRealtime(_options);
await _ticketService.StartRealtime(_options);
started = true;
}
+13
View File
@@ -0,0 +1,13 @@
namespace WxOrder.Framework.Helpers;
public interface ILogger
{
void Debug(string message, Exception exception = null);
void Dispose();
void Error(string message, Exception exception = null);
void Fatal(string message, Exception exception = null);
void Info(string message, Exception exception = null);
void Log(string message, LogMessageLevel level, Exception exception = null);
void Trace(string message, Exception exception = null);
void Warn(string message, Exception exception = null);
}
+3 -3
View File
@@ -32,7 +32,7 @@ namespace WxOrder.Framework.Helpers
/// 日志文件按日期分目录存放,并在跨天时自动切换:
/// {logDirectory}/yyyy-MM/yyyy-MM-dd.log,例如 log/2022-12/2022-12-12.log
/// </summary>
public sealed class NLogger : IDisposable
public sealed class NLogger : IDisposable, ILogger
{
private const string DefaultName = "Default";
@@ -77,9 +77,9 @@ namespace WxOrder.Framework.Helpers
/// <summary> 日志名称(写入每行日志中,用于区分来源,不再决定文件名)。 </summary>
public string Name { get; }
public bool IsDebugEnabled { get; set; } = true;
public bool IsDebugEnabled { get; set; } = false;
public bool IsDebugWriteLineEnabled { get; set; } = true;
public bool IsDebugWriteLineEnabled { get; set; } = false;
public LogMessageLevel MinimumLevel { get; set; } = LogMessageLevel.Trace;