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

131 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
var rawMsg = resDoc?["message"]?.GetValue<string>() ?? "PrintDot 打印失败";
throw new InvalidOperationException(EnhanceErrorMessage(rawMsg));
}
}
/// <summary>
/// 将 PrintDot 返回的部分英文错误转换为带本地处理步骤的中文提示。
/// 与 web 端 printDotBridge.ts::enhancePrintDotErrorMessage 行为一致,方便桌面端用户自助排查。
/// </summary>
private static string EnhanceErrorMessage(string raw)
{
var m = (raw ?? string.Empty).Trim();
// 缺 SumatraPDF是 PrintDot 客户端最常见的初始化错误
if (System.Text.RegularExpressions.Regex.IsMatch(m, @"SumatraPDF\.exe not found", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
|| System.Text.RegularExpressions.Regex.IsMatch(m, "SUMATRAPDF_PATH", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
return m + "。\n本地处理PrintDot 依赖 SumatraPDF 静默打印 PDF。请安装 SumatraPDF 后任选其一:\n" +
"① 将 SumatraPDF.exe 放在 PrintDot 客户端 exe 同目录;\n" +
"② 或将 SumatraPDF 安装目录加入系统 PATH\n" +
"③ 或设置用户/系统环境变量 SUMATRAPDF_PATH 指向 SumatraPDF.exe 的完整路径;\n" +
"然后重启 PrintDot 桥接器即可。";
}
return m;
}
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());
}
}