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)}
{safe(e.Name)}
- 设备编号
- {safe(e.Code)}
- 二维码编号
- {safe(e.QrCode)}
- 存放位置
- {safe(e.Location)}
- 责任人
- {safe(e.ResponsiblePerson)}
{safe(url)}
";
}
}
}