using Model; using Model.Dto.Asset; using Model.Entity.Asset; using ORM; using QRCoder; using Service.Interface; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Service.Implement { /// /// 设备二维码 服务实现 /// 扫码直达 URL 约定:{baseUrl}/asset/ledger/equipment?id={equipmentId} /// baseUrl 由 Controller 从 HTTP 请求(scheme://host)传入;如需固定域名可改为读 sys_param 的 site_base_url /// public class QrCodeService : IQrCodeService { public async Task> GenerateAsync(long equipmentId, string baseUrl) { if (equipmentId <= 0) return Result.Error("设备Id无效"); try { var entity = await LoadEquipmentAsync(equipmentId); if (entity == null) return Result.Error("设备不存在或已被删除"); var url = BuildQrUrl(baseUrl, equipmentId); if (string.IsNullOrEmpty(entity.QrCodeUrl)) { await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.QrCodeUrl == url) .Where(x => x.Id == equipmentId).ExecuteCommandAsync(); entity.QrCodeUrl = url; } return Result.Success(ToDto(entity)); } catch (Exception ex) { return Result.Error("生成二维码失败", ex); } } public async Task> GenerateImageAsync(long equipmentId, string baseUrl) { if (equipmentId <= 0) return Result.Error("设备Id无效"); try { var entity = await LoadEquipmentAsync(equipmentId); if (entity == null) return Result.Error("设备不存在或已被删除"); var url = string.IsNullOrEmpty(entity.QrCodeUrl) ? BuildQrUrl(baseUrl, equipmentId) : entity.QrCodeUrl; if (string.IsNullOrEmpty(entity.QrCodeUrl)) { await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.QrCodeUrl == url) .Where(x => x.Id == equipmentId).ExecuteCommandAsync(); entity.QrCodeUrl = url; } var png = EncodePng(url); return Result.Success(png); } catch (Exception ex) { return Result.Error("生成二维码图片失败", ex); } } public async Task>> BatchGenerateAsync(long[] equipmentIds, string baseUrl) { if (equipmentIds == null || equipmentIds.Length == 0) return Result>.Error("设备Id列表不能为空"); var results = new List(); foreach (var id in equipmentIds.Distinct()) { var r = await GenerateAsync(id, baseUrl); results.Add(new QrCodeBatchResultDto { EquipmentId = id.ToString(), EquipmentCode = r.Data?.EquipmentCode, EquipmentName = r.Data?.EquipmentName, QrCodeUrl = r.Data?.QrCodeUrl, Success = r.IsSuccess, Message = r.IsSuccess ? "成功" : r.Msg }); } return Result>.Success(results); } public async Task>> GetListPagedAsync(int pageIndex, int pageSize, RefAsync total, string? keyword = null, bool qrOnly = false) { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0) .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.Code!.Contains(keyword!) || x.Name!.Contains(keyword!)) .WhereIF(qrOnly, x => x.QrCodeUrl != null && x.QrCodeUrl != "") .OrderBy(x => x.Code) .ToPageListAsync(pageIndex, pageSize, total); return Result>.Success(list.Select(ToDto).ToList()); } catch (Exception ex) { return Result>.Error("查询二维码列表失败", ex); } } public async Task> GetPrintHtmlAsync(long equipmentId, string baseUrl) { if (equipmentId <= 0) return Result.Error("设备Id无效"); try { var entity = await LoadEquipmentAsync(equipmentId); if (entity == null) return Result.Error("设备不存在或已被删除"); var url = string.IsNullOrEmpty(entity.QrCodeUrl) ? BuildQrUrl(baseUrl, equipmentId) : entity.QrCodeUrl; var png = EncodePng(url); var base64 = Convert.ToBase64String(png); var html = BuildPrintHtml(entity, url, base64); return Result.Success(html); } catch (Exception ex) { return Result.Error("生成打印页失败", ex); } } public async Task BindRfidAsync(long equipmentId, string rfidCode) { if (equipmentId <= 0) return Result.Error("设备Id无效"); if (string.IsNullOrWhiteSpace(rfidCode)) return Result.Error("RFID 编号不能为空"); try { var exists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == equipmentId && x.IsDel == 0).AnyAsync(); if (!exists) return Result.Error("设备不存在或已被删除"); await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.RfidCode == rfidCode) .Where(x => x.Id == equipmentId).ExecuteCommandAsync(); return Result.Success(); } catch (Exception ex) { return Result.Error("绑定 RFID 失败", ex); } } // ==================== 私有工具 ==================== private static async Task LoadEquipmentAsync(long id) => await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); private static QrCodeDto ToDto(EquipmentEntity e) => new() { EquipmentId = e.Id.ToString(), EquipmentCode = e.Code, EquipmentName = e.Name, Location = e.Location, QrCode = e.QrCode, QrCodeUrl = e.QrCodeUrl, RfidCode = e.RfidCode }; private static string BuildQrUrl(string baseUrl, long equipmentId) { var b = (baseUrl ?? string.Empty).TrimEnd('/'); return $"{b}/asset/ledger/equipment?id={equipmentId}"; } private static byte[] EncodePng(string content) { using var gen = new QRCodeGenerator(); var data = gen.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q); var png = new PngByteQRCode(data); return png.GetGraphic(20); // 20 px per module,便于扫描识别 } private static string BuildPrintHtml(EquipmentEntity e, string url, string base64) { // 简单 A4 打印模板:左侧二维码,右侧设备信息;浏览器 window.print() 即可 var safe = (string? s) => System.Net.WebUtility.HtmlEncode(s ?? string.Empty); return $@" 设备二维码 - {safe(e.Name)}
QR

{safe(e.Name)}

设备编号
{safe(e.Code)}
二维码编号
{safe(e.QrCode)}
存放位置
{safe(e.Location)}
责任人
{safe(e.ResponsiblePerson)}
{safe(url)}
"; } } }