Files
jinko.yikatong/Src/Dlhis.Basic/Organization/Services/Implements/GainfoFaceService.cs

464 lines
17 KiB
C#

using Dlhis.Basic.Device.Services;
using Dlhis.Basic.Organization.Api;
using Dlhis.Basic.Organization.Api.Request;
using Dlhis.Basic.Organization.Request;
using Dlhis.Basic.Organization.Response;
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.Jip.Api;
using Dlhis.Jip.Api.Requests;
using Dlhis.Sys.Api;
using Kurisu.AspNetCore.Authentication.Abstractions;
using Kurisu.AspNetCore.CustomClass;
using Kurisu.AspNetCore.DataAccess.Dto;
using Kurisu.AspNetCore.DataAccess.Extensions;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.AspNetCore.UnifyResultAndValidation.Abstractions;
using Kurisu.AspNetCore.Utils.Extensions;
using Mapster;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
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<GainfoFaceService> _logger;
private readonly IConfiguration _configuration;
private readonly ICurrentUser _currentUser;
private readonly IDevicePermissionService _devicePermissionService;
private readonly IFactorySettingService _factorySettingService;
private readonly IHainingApi _hainingApi;
private readonly IInfoCheckApi _infoCheckApi;
private readonly IJinkoJipApi _jinkoJipApi;
private readonly ITenantApi _tenantApi;
private readonly IHikService _hikService;
private readonly IDataProtectionProvider _dataProtectionProvider;
public GainfoFaceService(IGainfoStore gainfoStore,
IDbContext dbContext,
ILogger<GainfoFaceService> logger,
IConfiguration configuration,
ICurrentUser currentUser,
IDevicePermissionService devicePermissionService,
IFactorySettingService factorySettingService,
IHainingApi hainingApi,
IInfoCheckApi infoCheckApi,
IJinkoJipApi jinkoJipApi,
ITenantApi tenantApi,
IHikService hikService,
IDataProtectionProvider dataProtectionProvider)
{
_logger = logger;
_gainfoStore = gainfoStore;
_dbContext = dbContext;
_faceDbContext = dbContext.ChangeDb("face");
_configuration = configuration;
_currentUser = currentUser;
_devicePermissionService = devicePermissionService;
_factorySettingService = factorySettingService;
_hainingApi = hainingApi;
_infoCheckApi = infoCheckApi;
_jinkoJipApi = jinkoJipApi;
_tenantApi = tenantApi;
_hikService = hikService;
_dataProtectionProvider = dataProtectionProvider;
}
public async Task<GainfoFacePageSummaryResponse> GetPageAsync(GainfoFacePageRequest request)
{
var tenantIds = _currentUser.GetUserClaim("tenants").Split(",").ToList();
var factories = request.Factories;
factories = factories.Where(x => tenantIds.Contains(x)).ToList();
var userCodes = await _dbContext.Queryable<GainfoEntity>().Where(x => factories.Contains(x.Factory)).Select(x => x.Code).ToListAsync();
var existsFaceCodes = await _faceDbContext.Queryable<GainfoFaceEntity>().Where(x => userCodes.Contains(x.UserCode)).Select(x => x.UserCode).ToListAsync();
var list = await _dbContext.Queryable<GainfoEntity>()
.LeftJoin<DepartmentEntity>((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)
.OrderBy(x => x.CreateTime, OrderByType.Desc)
.Select((x, d) => new GainfoFacePageResponse
{
UserCode = x.Code,
UserName = x.Name,
DepartmentName = d.Name,
Exists = false,
Factory = x.Factory
})
.ToListAsync();
var page = list.ToPage(request);
page.Data.ForEach(x => { x.Exists = existsFaceCodes.Contains(x.UserCode); });
try
{
var tenants = await _tenantApi.GetTenantsInternalAsync();
page.Data.ForEach(x => { x.FactoryName = tenants.FirstOrDefault(p => p.Code == x.Factory)?.Name; });
}
catch (Exception e)
{
_logger.LogError(e, "处理tenant错误:{msg}", e.Message);
}
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)
{
//ignore
}
var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.Working)
.Where(x => x.IdNo == request.UserNo)
.SingleAsync();
gainfo.ThrowIfNull("人员信息不存在");
var face = await _faceDbContext.Queryable<GainfoFaceEntity>().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, "照片上传生成");
}
public async Task<string> 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<GainfoEntity>().Where(x => x.IdNo == idno).Select(x => x.Code).FirstAsync();
code ??= idno;
var face = await _faceDbContext.Queryable<GainfoFaceEntity>()
.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<int>();
var key = $"face:{idno}";
var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit));
return faceUrl + token;
}
public async Task<IActionResult> 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<GainfoFaceEntity>().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<IApiResult> 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, gainfo.Factory);
var face = await _faceDbContext.Queryable<GainfoFaceEntity>().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 DefaultApiResult.Success("操作成功");
}
var faceSimilaritySetting = await _dbContext.Queryable<BasicTypeEntity>().Where(x => x.Type == "faceSimilarity").SingleAsync();
if (faceSimilaritySetting != null)
{
var faceSimilarity = await _hikService.GetSimilarityAsync(new GainfoFaceValidateRequest
{
SrcFacePicBinaryData = face.Face,
DistFacePicBinaryData = request.Face
});
var acceptSimilarity = faceSimilaritySetting.Code.Adapt<double>();
var isError = faceSimilarity < acceptSimilarity;
await _dbContext.InsertAsync(new HikValidateResultEntity
{
UserCode = face.UserCode,
Similarity = faceSimilarity,
AcceptSimilarity = acceptSimilarity,
CurrentFace = face.Face,
UploadFace = request.Face
});
if (isError)
{
return DefaultApiResult.Error($"系统检测非本人更新,请重试(相似度{faceSimilarity})");
}
}
face.Face = request.Face;
await _faceDbContext.UpdateAsync(face);
}
try
{
var r = await _jinkoJipApi.SyncFaceToSapAsync(new SyncFaceRequest
{
UserCode = face.UserCode,
Base64 = face.Face
});
if (r.OLX == "S")
{
await _faceDbContext.Updateable<GainfoFaceEntity>()
.SetColumns(x => x.IsSync == true)
.Where(x => x.UserCode == face.UserCode)
.ExecuteCommandAsync();
}
}
catch (Exception e)
{
_logger.LogError(e, "上传face数据到sap失败:{message}", e.Message);
}
try
{
await _hainingApi.UploadAsync(new HainingApiUploadRequest()
{
Code = gainfo.Code,
Base64 = request.Face
});
}
catch (Exception e)
{
_logger.LogError(e, "上传face数据到老系统失败:{message}", e.Message);
}
//生成任务
await _devicePermissionService.GenerateDeviceTasksAsync(gainfo.No, taskType, "照片上传生成");
return DefaultApiResult.Success("操作成功");
}
public async Task UploadFacesAsync(List<GainfoUploadFaceRequest> requests)
{
foreach (var request in requests)
{
var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.Working).Where(x => x.Code == request.UserNo).FirstAsync();
gainfo.ThrowIfNull("人员档案信息不存在");
var face = await _faceDbContext.Queryable<GainfoFaceEntity>().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,
CreatedBy = _currentUser.GetIntSubjectId(),
CreateTime = DateTime.Now,
ModifiedBy = _currentUser.GetIntSubjectId(),
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, "照片上传生成");
}
}
public async Task<string> GetFaceByUserCodeAsync(string userCode)
{
var face = await _faceDbContext.Queryable<GainfoFaceEntity>()
.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<int>();
var key = $"face:{userCode}";
var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit));
return faceUrl + token;
}
public async Task<Dictionary<string, string>> GetFaceByUserCodesAsync(GetFaceByUserCodesRequest request)
{
var faceDictionary = new Dictionary<string, string>();
foreach (var userCode in request.UserCodes)
{
try
{
var faceUrl = await GetFaceByUserCodeAsync(userCode);
faceDictionary.Add(userCode, faceUrl);
}
catch (Exception)
{
//ignore
}
}
return faceDictionary;
}
/// <inheritdoc />
public async Task<Pagination<FaceValidationResponse>> GetUploadPageRecordsAsync(FaceValidationRequest request)
{
return await _dbContext.Queryable<HikValidateResultEntity>()
.LeftJoin<GainfoEntity>((x, ga) => x.UserCode == ga.Code)
.WhereIF(request.IsSuccess.HasValue && request.IsSuccess.Value, x => x.AcceptSimilarity >= x.Similarity)
.WhereIF(request.IsSuccess is false, x => x.AcceptSimilarity < x.Similarity)
.WhereIF(request.Search.IsPresent(), (x, ga) => x.UserCode == request.Search || ga.Name.Contains(request.Search))
.Select((x, ga) => new FaceValidationResponse
{
UserCode = x.UserCode,
UserName = ga.Name,
Similarity = x.Similarity,
AcceptSimilarity = x.AcceptSimilarity,
CurrentFace = x.CurrentFace,
UploadFace = x.UploadFace,
CreateTime = x.CreateTime
})
.ToPageListAsync(request);
}
//*********************************************************************************************************************************************
/// <summary>
/// 人脸质量检测
/// </summary>
/// <param name="base64Image"></param>
/// <param name="factory"></param>
/// <exception cref="UserFriendlyException"></exception>
private async Task DetectFaceImageQualityAsync(string base64Image, string factory = null)
{
var currentFactory = factory ?? _currentUser.GetStringTenantId();
var factorySetting = await _factorySettingService.GetFactorySettingAsync(currentFactory, false);
if (factorySetting.FaceImageDetection.Enable)
{
List<string> 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;
}
catch (Exception ex)
{
_logger.LogError(ex, "人脸质量检测失败:{message}", ex.Message);
return;
}
r.IsPresent().ThrowIfTrue($"{r.Join("\r\n")}");
}
}
}