Files
qhmes/yy-admin-master/YY.Admin.Services/Service/Print/PrintDotService.cs

110 lines
4.0 KiB
C#

using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.Configuration;
using YY.Admin.Core.Services;
namespace YY.Admin.Services.Service.Print;
/// <summary>
/// PrintDot 本地桥接器 WebSocket 客户端。
/// URL 从 appsettings.json 的 PrintDot:Url 读取,可在运行时通过 <see cref="PrintDotSettings"/> 覆盖。
/// </summary>
public class PrintDotService : IPrintDotService
{
private readonly IConfiguration _config;
private static readonly JsonSerializerOptions JsonOpts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
public PrintDotService(IConfiguration config)
{
_config = config;
}
private string ResolveWsUrl()
{
var url = PrintDotSettings.Current?.WsUrl
?? _config.GetValue<string>("PrintDot:Url")
?? "ws://127.0.0.1:1122/ws";
return url.TrimEnd('/');
}
public async Task<IReadOnlyList<PrintDotPrinter>> GetPrintersAsync(CancellationToken ct = default)
{
var wsUrl = ResolveWsUrl();
using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri(wsUrl), ct);
// 连接后服务端立即推送 printer_list
var json = await ReceiveTextAsync(ws, ct);
var doc = JsonNode.Parse(json);
if (doc?["type"]?.GetValue<string>() == "printer_list")
{
var arr = doc["data"]?.AsArray();
if (arr != null)
{
return arr
.Where(n => n != null)
.Select(n => new PrintDotPrinter(
Name: n!["name"]?.GetValue<string>()?.Trim() ?? string.Empty,
IsDefault: n["isDefault"]?.GetValue<bool>() ?? false))
.Where(p => !string.IsNullOrWhiteSpace(p.Name))
.ToList();
}
}
return [];
}
public async Task PrintAsync(string printerName, string pdfBase64, string jobName = "QH-MES", int copies = 1, CancellationToken ct = default)
{
// 去掉 data: 前缀
var content = pdfBase64.Trim();
var comma = content.IndexOf(',');
if (content.StartsWith("data:", StringComparison.OrdinalIgnoreCase) && comma >= 0)
content = content[(comma + 1)..];
var payload = new
{
printer = printerName,
content,
job = new { name = jobName, copies = Math.Max(1, copies) }
};
var wsUrl = ResolveWsUrl();
using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri(wsUrl), ct);
// 先等服务端推送 printer_list 再发任务
await ReceiveTextAsync(ws, ct);
var msg = JsonSerializer.Serialize(payload, JsonOpts);
await ws.SendAsync(Encoding.UTF8.GetBytes(msg), WebSocketMessageType.Text, true, ct);
// 等待打印结果(最多 3 分钟)
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromMinutes(3));
while (ws.State == WebSocketState.Open)
{
var response = await ReceiveTextAsync(ws, cts.Token);
var resDoc = JsonNode.Parse(response);
var status = resDoc?["status"]?.GetValue<string>();
if (status == null) continue;
if (status == "success") return;
throw new InvalidOperationException(resDoc?["message"]?.GetValue<string>() ?? "PrintDot 打印失败");
}
}
private static async Task<string> ReceiveTextAsync(ClientWebSocket ws, CancellationToken ct)
{
var buffer = new ArraySegment<byte>(new byte[64 * 1024]);
using var ms = new System.IO.MemoryStream();
WebSocketReceiveResult result;
do
{
result = await ws.ReceiveAsync(buffer, ct);
ms.Write(buffer.Array!, buffer.Offset, result.Count);
} while (!result.EndOfMessage);
return Encoding.UTF8.GetString(ms.ToArray());
}
}