using Dlhis.Basic.Device.Services; using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; using Dlhis.Entity.Basic.Dto; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Device.Enums; using Dlhis.InfoCheck.Api; using Dlhis.InfoCheck.Api.Requests; using Dlhis.Scheduled.Api; using Dlhis.Scheduled.Api.Request; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.CustomClass; using Kurisu.AspNetCore.DataAccess.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.Utils.Extensions; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; namespace Dlhis.Basic.Organization.Services.Implements; public class GainfoFaceService : IGainfoFaceService { private readonly IGainfoStore _gainfoStore; private readonly IDbContext _dbContext; private readonly IDbContext _faceDbContext; private readonly ILogger _logger; private readonly IConfiguration _configuration; private readonly ICurrentUser _currentUser; private readonly IHangfireApi _hangfireApi; private readonly IDevicePermissionService _devicePermissionService; private readonly IFactorySettingService _factorySettingService; private readonly IInfoCheckApi _infoCheckApi; private readonly IDataProtectionProvider _dataProtectionProvider; public GainfoFaceService(IGainfoStore gainfoStore, IDbContext dbContext, ILogger logger, IConfiguration configuration, ICurrentUser currentUser, IHangfireApi hangfireApi, IDevicePermissionService devicePermissionService, IFactorySettingService factorySettingService, IInfoCheckApi infoCheckApi, IDataProtectionProvider dataProtectionProvider) { _gainfoStore = gainfoStore; _dbContext = dbContext; _faceDbContext = dbContext.ChangeDb("face"); _logger = logger; _configuration = configuration; _currentUser = currentUser; _hangfireApi = hangfireApi; _devicePermissionService = devicePermissionService; _factorySettingService = factorySettingService; _infoCheckApi = infoCheckApi; _dataProtectionProvider = dataProtectionProvider; } public async Task GetPageAsync(GainfoFacePageRequest request) { var factories = request.Factories; var userCodes = await _dbContext.Queryable().Where(x => factories.Contains(x.Factory)).Select(x => x.Code).ToListAsync(); var existsFaceCodes = await _faceDbContext.Queryable().Where(x => userCodes.Contains(x.UserCode)).Select(x => x.UserCode).ToListAsync(); var list = await _dbContext.Queryable() .LeftJoin((x, d) => x.DepartmentNo == d.No) .Where(x => factories.Contains(x.Factory)) .WhereIF(request.Exists.HasValue && request.Exists.Value, x => existsFaceCodes.Contains(x.Code)) .WhereIF(request.Exists.HasValue && !request.Exists.Value, x => !existsFaceCodes.Contains(x.Code)) .WhereIF(request.Search.IsPresent(), x => x.Code == request.Search) .Select((x, d) => new GainfoFacePageResponse { UserCode = x.Code, UserName = x.Name, DepartmentName = d.Name, Exists = false }) .ToListAsync(); var page = list.ToPage(request); page.Data.ForEach(x => { x.Exists = existsFaceCodes.Contains(x.UserCode); }); return new GainfoFacePageSummaryResponse { Total = userCodes.Count, Exists = existsFaceCodes.Count, Page = page }; } public async Task UploadExternalFaceAsync(GainfoUploadFaceRequest request) { var protector = _dataProtectionProvider.CreateProtector("gainfo:external:idno"); try { request.UserNo = protector.Unprotect(request.UserNo); } catch (Exception) { throw new UserFriendlyException("凭证有误,无法获取人脸数"); } var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.External) .Where(x => x.IdNo == request.UserNo) .SingleAsync(); gainfo ??= await _gainfoStore.Queryable(GainfoStatusEnumType.Working) .Where(x => x.IdNo == request.UserNo) .SingleAsync(); gainfo.ThrowIfNull("人员信息不存在"); var face = await _faceDbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); var taskType = face == null ? DeviceTaskEnumType.Add : DeviceTaskEnumType.Update; if (face == null) { face = new GainfoFaceEntity { UserCode = gainfo.Code, Face = request.Face }; await _faceDbContext.InsertAsync(face); } else { face.Face = request.Face; face.ModifiedBy = 1; face.ModifiedTime = DateTime.Now; await _faceDbContext.UpdateAsync(face); } await _devicePermissionService.GenerateDeviceTasksAsync(gainfo.No, taskType, DeviceTaskSourceEnumType.Photo); } public async Task GetExternalFaceAsync(string idno) { var protector = _dataProtectionProvider.CreateProtector("gainfo:external:idno"); try { idno = protector.Unprotect(idno); } catch (Exception) { throw new UserFriendlyException("凭证有误,无法获取人脸数"); } var code = await _dbContext.Queryable().Where(x => x.IdNo == idno).Select(x => x.Code).FirstAsync(); code ??= idno; var face = await _faceDbContext.Queryable() .Where(x => x.UserCode == code) .SingleAsync(); face.ThrowIfNull("人脸照片尚未录入"); var timeLimitedDataProtector = _dataProtectionProvider.CreateProtector("down:face").ToTimeLimitedDataProtector(); var faceUrl = _configuration.GetSection("FaceUrlOptions:Url").Value; var limit = _configuration.GetSection("FaceUrlOptions:Limit").Get(); var key = $"face:{idno}"; var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit)); return faceUrl + token; } public async Task GetFaceAsync(string token) { var timeLimitedDataProtector = _dataProtectionProvider.CreateProtector("down:face").ToTimeLimitedDataProtector(); try { var password = timeLimitedDataProtector.Unprotect(token); var userCode = password.Split(':')[1]; var face = await _faceDbContext.Queryable().Where(x => x.UserCode == userCode).SingleAsync(); if (face == null) { return new NotFoundResult(); } return new FileContentResult(Convert.FromBase64String(face.Face), "image/png"); } catch (Exception ex) { _logger.LogError(ex, "获取人脸失败:{message}", ex.Message); return new NotFoundResult(); } } public async Task UploadFaceAsync(GainfoUploadFaceRequest request) { var gainfo = await _gainfoStore.Queryable().Where(x => x.No == request.UserNo || x.Code == request.UserNo).SingleAsync(); gainfo.ThrowIfNull("人员档案信息不存在"); await DetectFaceImageQualityAsync(request.Face); var face = await _faceDbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); var taskType = face == null ? DeviceTaskEnumType.Add : DeviceTaskEnumType.Update; if (face == null) { face = new GainfoFaceEntity { UserCode = gainfo.Code, Face = request.Face }; await _faceDbContext.InsertAsync(face); } else { if (face.Face == request.Face) { _logger.LogWarning("相同的人脸照片更新忽略"); return; } face.Face = request.Face; await _faceDbContext.UpdateAsync(face); } try { await _hangfireApi.SyncFaceToSapAsync(new FaceUploadToSapRequest { UserCode = face.UserCode, Base64 = face.Face }); } catch (Exception e) { _logger.LogError(e, "上传face数据到sap失败:{message}", e.Message); } //生成任务 await _devicePermissionService.GenerateDeviceTasksAsync(gainfo.No, taskType, DeviceTaskSourceEnumType.Photo); } public async Task UploadFacesAsync(List requests) { foreach (var request in requests) { var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.Working).Where(x => x.Code == request.UserNo).FirstAsync(); gainfo ??= await _gainfoStore.Queryable(GainfoStatusEnumType.External).Where(x => x.Code == request.UserNo).FirstAsync(); gainfo.ThrowIfNull("人员档案信息不存在"); var face = await _faceDbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); var taskType = face == null ? DeviceTaskEnumType.Add : DeviceTaskEnumType.Update; if (face == null) { face = new GainfoFaceEntity { UserCode = gainfo.Code, Face = request.Face }; face.CreatedBy = _currentUser.GetIntSubjectId(); face.CreateTime = DateTime.Now; face.ModifiedBy = _currentUser.GetIntSubjectId(); face.ModifiedTime = DateTime.Now; await _faceDbContext.InsertAsync(face); } else { if (face.Face == request.Face) { _logger.LogWarning("相同的人脸照片更新忽略"); return; } face.ModifiedBy = _currentUser.GetIntSubjectId(); face.ModifiedTime = DateTime.Now; face.Face = request.Face; await _faceDbContext.UpdateAsync(face); } //生成任务 await _devicePermissionService.GenerateDeviceTasksAsync(gainfo.No, taskType, DeviceTaskSourceEnumType.Photo); } } public async Task GetFaceByUserCodeAsync(string userCode) { var face = await _faceDbContext.Queryable() .Where(x => x.UserCode == userCode) .SingleAsync(); face.ThrowIfNull("人脸照片尚未录入"); var timeLimitedDataProtector = _dataProtectionProvider.CreateProtector("down:face").ToTimeLimitedDataProtector(); var faceUrl = _configuration.GetSection("FaceUrlOptions:Url").Value; var limit = _configuration.GetSection("FaceUrlOptions:Limit").Get(); var key = $"face:{userCode}"; var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit)); return faceUrl + token; } public async Task> GetFaceByUserCodesAsync(GetFaceByUserCodesRequest request) { var faceDictionary = new Dictionary(); foreach (var userCode in request.UserCodes) { try { var faceUrl = await GetFaceByUserCodeAsync(userCode); faceDictionary.Add(userCode, faceUrl); } catch (Exception) { //ignore } } return faceDictionary; } //********************************************************************************************************************************************* /// /// 人脸质量检测 /// /// /// /// private async Task DetectFaceImageQualityAsync(string base64Image, string factory = null) { var currentFactory = factory ?? _currentUser.GetStringTenantId(); var factorySetting = await _factorySettingService.GetFactorySettingAsync(currentFactory); if (factorySetting.FaceImageDetection.Enable) { List r; try { var result = await _infoCheckApi.FaceDetectAsync(new FaceDetectRequest { Base64 = base64Image, CheckList = factorySetting.FaceImageDetection.Detections.Select(x => new FaceDetectCheckRequest { Type = (int)x.Type, Level = (int)x.Level }).ToList() }); r = result.Data; } catch (Exception) { return; } r.IsPresent().ThrowIfTrue($"{r.Join("\r\n")}"); } } }