feat: 人证人脸对比
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
using Dlhis.Basic.Organization.Request;
|
||||||
|
using Dlhis.Basic.Organization.Response;
|
||||||
|
using Dlhis.Basic.Organization.Services;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop;
|
||||||
|
using Kurisu.AspNetCore.UnifyResultAndValidation.Aop;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人证人脸对比
|
||||||
|
/// </summary>
|
||||||
|
[Authorize]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class GainfoIdCardFaceCompareController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IGainfoIdCardFaceCompareService _service;
|
||||||
|
|
||||||
|
public GainfoIdCardFaceCompareController(IGainfoIdCardFaceCompareService service)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人证人脸对比记录分页
|
||||||
|
/// </summary>
|
||||||
|
[DisableApiLog]
|
||||||
|
[HttpPost("page")]
|
||||||
|
public async Task<Pagination<IdCardFaceCompareRecordResponse>> GetPageAsync(IdCardFaceComparePageRequest request)
|
||||||
|
{
|
||||||
|
return await _service.GetPageAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人证人脸对比
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("compare")]
|
||||||
|
[Transactional]
|
||||||
|
public async Task<IdCardFaceCompareRecordResponse> CompareAsync(IdCardFaceCompareRequest request)
|
||||||
|
{
|
||||||
|
return await _service.CompareAsync(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Dlhis.Entity.Basic.Enums;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Request;
|
||||||
|
|
||||||
|
public class IdCardFaceComparePageRequest : PageDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 厂区
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Factories { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人员类型
|
||||||
|
/// </summary>
|
||||||
|
public GainfoUserTypeEnumType? UserType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对比结果
|
||||||
|
/// </summary>
|
||||||
|
public IdCardFaceCompareResultEnumType? CompareResult { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Request;
|
||||||
|
|
||||||
|
public class IdCardFaceCompareRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户code
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "用户code不可为空")]
|
||||||
|
public string UserCode { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace Dlhis.Basic.Organization.Response;
|
||||||
|
|
||||||
|
public class AliyunId3MetaVerifyResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 请求ID
|
||||||
|
/// </summary>
|
||||||
|
public string RequestId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接口调用返回码,200表示接口调用成功
|
||||||
|
/// </summary>
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接口调用返回信息,仅表示接口是否异常
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 业务核验结果,1一致、2不一致、3查无记录
|
||||||
|
/// </summary>
|
||||||
|
public string BizCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 业务核验是否通过,来源于BizCode
|
||||||
|
/// </summary>
|
||||||
|
public bool? Passed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人像比对分值,来源于FaceDetail.verifyScore,可能为空
|
||||||
|
/// </summary>
|
||||||
|
public double? Similarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 阿里云原始响应
|
||||||
|
/// </summary>
|
||||||
|
public string RawResponse { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using Dlhis.Entity.Basic.Enums;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Response;
|
||||||
|
|
||||||
|
public class IdCardFaceCompareRecordResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 记录ID
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户code
|
||||||
|
/// </summary>
|
||||||
|
public string UserCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 姓名
|
||||||
|
/// </summary>
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人员类型
|
||||||
|
/// </summary>
|
||||||
|
public GainfoUserTypeEnumType UserType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 证件号
|
||||||
|
/// </summary>
|
||||||
|
public string IdNo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 部门名称
|
||||||
|
/// </summary>
|
||||||
|
public string DepartmentName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 厂区
|
||||||
|
/// </summary>
|
||||||
|
public string Factory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对比结果
|
||||||
|
/// </summary>
|
||||||
|
public IdCardFaceCompareResultEnumType CompareResult { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 业务核验是否通过,来源于阿里云ResultObject.BizCode
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsPassed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人像比对分值,来源于阿里云ResultObject.FaceDetail.verifyScore,可能为空
|
||||||
|
/// </summary>
|
||||||
|
public double? Similarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前用户累计对比次数
|
||||||
|
/// </summary>
|
||||||
|
public int CompareCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最近对比时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LastCompareTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 阿里云原始响应
|
||||||
|
/// </summary>
|
||||||
|
public string ApiResponse { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对比异常信息
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
}
|
||||||
218
Src/Dlhis.Basic/Organization/Services/IAliyunClient.cs
Normal file
218
Src/Dlhis.Basic/Organization/Services/IAliyunClient.cs
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Dlhis.Basic.Organization.Response;
|
||||||
|
using Kurisu.AspNetCore.CustomClass;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Services;
|
||||||
|
|
||||||
|
public interface IAliyunClient : IScopeDependency
|
||||||
|
{
|
||||||
|
Task<AliyunId3MetaVerifyResponse> Id3MetaVerifyAsync(
|
||||||
|
string identifyNum,
|
||||||
|
string userName,
|
||||||
|
string faceBase64,
|
||||||
|
string crop = "F");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AliyunClient : IAliyunClient
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly string _accessKeyId;
|
||||||
|
private readonly string _accessKeySecret;
|
||||||
|
|
||||||
|
private const string Endpoint = "cloudauth.aliyuncs.com";
|
||||||
|
private const string ApiVersion = "2019-03-07";
|
||||||
|
|
||||||
|
public AliyunClient(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger<AliyunClient> logger)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AliyunClient(string accessKeyId, string accessKeySecret)
|
||||||
|
{
|
||||||
|
_accessKeyId = accessKeyId ?? "LTAI5tAqyWKcBcSvzpc1ZM72";
|
||||||
|
_accessKeySecret = accessKeySecret ?? "YZMSqsmMGlGZuHw5WU2tqt0mmIBX6h";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用 Base64 图片进行三要素核验
|
||||||
|
/// </summary>
|
||||||
|
public async Task<AliyunId3MetaVerifyResponse> Id3MetaVerifyAsync(
|
||||||
|
string identifyNum,
|
||||||
|
string userName,
|
||||||
|
string faceBase64,
|
||||||
|
string crop = "F")
|
||||||
|
{
|
||||||
|
var accessKeyId = _accessKeyId ?? GetRequiredConfig("AliyunOptions:AccessKeyId", "Aliyun:AccessKeyId");
|
||||||
|
var accessKeySecret = _accessKeySecret ?? GetRequiredConfig("AliyunOptions:AccessKeySecret", "Aliyun:AccessKeySecret");
|
||||||
|
|
||||||
|
if (faceBase64.Contains(","))
|
||||||
|
{
|
||||||
|
faceBase64 = faceBase64[(faceBase64.IndexOf(',') + 1)..];
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameters = new SortedDictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "Action", "Id3MetaVerify" },
|
||||||
|
{ "Format", "JSON" },
|
||||||
|
{ "Version", ApiVersion },
|
||||||
|
{ "AccessKeyId", accessKeyId },
|
||||||
|
{ "SignatureMethod", "HMAC-SHA1" },
|
||||||
|
{ "SignatureVersion", "1.0" },
|
||||||
|
{ "SignatureNonce", Guid.NewGuid().ToString("N") },
|
||||||
|
{ "Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ") },
|
||||||
|
{ "ParamType", "normal" },
|
||||||
|
{ "IdentifyNum", identifyNum },
|
||||||
|
{ "UserName", userName },
|
||||||
|
{ "FacePicture", faceBase64 },
|
||||||
|
{ "Crop", crop }
|
||||||
|
};
|
||||||
|
|
||||||
|
var signature = ComputeSignature(parameters, "POST", accessKeySecret);
|
||||||
|
var url = $"https://{Endpoint}/?Signature={PercentEncode(signature)}";
|
||||||
|
var bodyString = BuildBodyString(parameters);
|
||||||
|
|
||||||
|
using var fallbackClient = _httpClientFactory == null ? new HttpClient() : null;
|
||||||
|
var client = _httpClientFactory?.CreateClient() ?? fallbackClient;
|
||||||
|
client.DefaultRequestHeaders.Add("Host", Endpoint);
|
||||||
|
var content = new StringContent(bodyString, Encoding.UTF8, "application/x-www-form-urlencoded");
|
||||||
|
var response = await client.PostAsync(url, content);
|
||||||
|
var rawResponse = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return ParseResponse(rawResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRequiredConfig(params string[] keys)
|
||||||
|
{
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
var value = _configuration.GetSection(key).Value;
|
||||||
|
if (!string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UserFriendlyException($"阿里云配置缺失:{string.Join("或", keys)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AliyunId3MetaVerifyResponse ParseResponse(string rawResponse)
|
||||||
|
{
|
||||||
|
var result = new AliyunId3MetaVerifyResponse
|
||||||
|
{
|
||||||
|
RawResponse = rawResponse
|
||||||
|
};
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(rawResponse))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = JsonConvert.DeserializeObject<AliyunId3MetaVerifyApiResponse>(rawResponse);
|
||||||
|
result.RequestId = response?.RequestId;
|
||||||
|
result.Code = response?.Code;
|
||||||
|
result.Message = response?.Message;
|
||||||
|
result.BizCode = response?.ResultObject?.BizCode;
|
||||||
|
result.Similarity = ParseVerifyScore(response?.ResultObject?.FaceDetail);
|
||||||
|
result.Passed = response?.ResultObject?.BizCode switch
|
||||||
|
{
|
||||||
|
"1" => true,
|
||||||
|
"2" or "3" => false,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
result.Message = "阿里云响应不是合法JSON";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double? ParseVerifyScore(string faceDetail)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(faceDetail))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var detail = JsonConvert.DeserializeObject<AliyunId3MetaVerifyFaceDetail>(faceDetail);
|
||||||
|
return detail?.VerifyScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ComputeSignature(SortedDictionary<string, string> parameters, string httpMethod, string accessKeySecret)
|
||||||
|
{
|
||||||
|
var canonicalized = new StringBuilder();
|
||||||
|
foreach (var kv in parameters)
|
||||||
|
{
|
||||||
|
if (kv.Key == "Signature")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canonicalized.Length > 0)
|
||||||
|
{
|
||||||
|
canonicalized.Append('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
canonicalized.Append(PercentEncode(kv.Key))
|
||||||
|
.Append('=')
|
||||||
|
.Append(PercentEncode(kv.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringToSign = httpMethod + "&" + PercentEncode("/") + "&" + PercentEncode(canonicalized.ToString());
|
||||||
|
var signKey = accessKeySecret + "&";
|
||||||
|
using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(signKey));
|
||||||
|
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
|
||||||
|
return Convert.ToBase64String(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildBodyString(SortedDictionary<string, string> parameters)
|
||||||
|
{
|
||||||
|
return string.Join("&", parameters
|
||||||
|
.Where(x => x.Key != "Signature")
|
||||||
|
.Select(x => PercentEncode(x.Key) + "=" + PercentEncode(x.Value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string PercentEncode(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uri.EscapeDataString(value)
|
||||||
|
.Replace("+", "%20")
|
||||||
|
.Replace("*", "%2A")
|
||||||
|
.Replace("%7E", "~");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AliyunId3MetaVerifyApiResponse
|
||||||
|
{
|
||||||
|
public string RequestId { get; set; }
|
||||||
|
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
public AliyunId3MetaVerifyResultObject ResultObject { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AliyunId3MetaVerifyResultObject
|
||||||
|
{
|
||||||
|
public string BizCode { get; set; }
|
||||||
|
|
||||||
|
public string FaceDetail { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AliyunId3MetaVerifyFaceDetail
|
||||||
|
{
|
||||||
|
[JsonProperty("verifyScore")]
|
||||||
|
public double? VerifyScore { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Dlhis.Basic.Organization.Request;
|
||||||
|
using Dlhis.Basic.Organization.Response;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Services;
|
||||||
|
|
||||||
|
public interface IGainfoIdCardFaceCompareService : IScopeDependency
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 人证人脸对比记录分页查询
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">分页查询条件</param>
|
||||||
|
/// <returns>人证人脸对比记录分页结果</returns>
|
||||||
|
Task<Pagination<IdCardFaceCompareRecordResponse>> GetPageAsync(IdCardFaceComparePageRequest request);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行人证人脸对比
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">人证人脸对比请求</param>
|
||||||
|
/// <returns>人证人脸对比记录</returns>
|
||||||
|
Task<IdCardFaceCompareRecordResponse> CompareAsync(IdCardFaceCompareRequest request);
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
using Dlhis.Basic.Organization.Request;
|
||||||
|
using Dlhis.Basic.Organization.Response;
|
||||||
|
using Dlhis.Entity.Basic.Entity;
|
||||||
|
using Dlhis.Entity.Basic.Enums;
|
||||||
|
using Kurisu.AspNetCore.Authentication.Abstractions;
|
||||||
|
using Kurisu.AspNetCore.CustomClass;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
|
||||||
|
using Kurisu.AspNetCore.Utils.Extensions;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic.Organization.Services.Implements;
|
||||||
|
|
||||||
|
public class GainfoIdCardFaceCompareService : IGainfoIdCardFaceCompareService
|
||||||
|
{
|
||||||
|
private readonly IDbContext _dbContext;
|
||||||
|
private readonly IDbContext _faceDbContext;
|
||||||
|
private readonly IAliyunClient _aliyunClient;
|
||||||
|
private readonly ICurrentUser _currentUser;
|
||||||
|
|
||||||
|
public GainfoIdCardFaceCompareService(
|
||||||
|
IDbContext dbContext,
|
||||||
|
IAliyunClient aliyunClient,
|
||||||
|
ICurrentUser currentUser)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_faceDbContext = dbContext.ChangeDb("face");
|
||||||
|
_aliyunClient = aliyunClient;
|
||||||
|
_currentUser = currentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Pagination<IdCardFaceCompareRecordResponse>> GetPageAsync(IdCardFaceComparePageRequest request)
|
||||||
|
{
|
||||||
|
var factories = GetFactories(request.Factories);
|
||||||
|
|
||||||
|
return await _dbContext.Queryable<IdCardFaceCompareRecordEntity>()
|
||||||
|
.LeftJoin<GainfoEntity>((record, gainfo) => record.UserCode == gainfo.Code)
|
||||||
|
.LeftJoin<DepartmentEntity>((record, gainfo, department) => gainfo.DepartmentNo == department.No)
|
||||||
|
.WhereIF(factories.Any(), (record, gainfo) => factories.Contains(gainfo.Factory))
|
||||||
|
.WhereIF(request.UserType.HasValue, record => record.UserType == request.UserType.Value)
|
||||||
|
.WhereIF(request.CompareResult.HasValue, record => record.CompareResult == request.CompareResult.Value)
|
||||||
|
.WhereIF(request.Search.IsPresent(), (record, gainfo) =>
|
||||||
|
record.UserCode == request.Search
|
||||||
|
|| record.IdNo == request.Search
|
||||||
|
|| record.UserName.Contains(request.Search)
|
||||||
|
|| gainfo.Name.Contains(request.Search))
|
||||||
|
.OrderBy(record => record.CreateTime, OrderByType.Desc)
|
||||||
|
.Select((record, gainfo, department) => new IdCardFaceCompareRecordResponse
|
||||||
|
{
|
||||||
|
Id = record.Id,
|
||||||
|
UserCode = record.UserCode,
|
||||||
|
UserName = record.UserName,
|
||||||
|
UserType = record.UserType,
|
||||||
|
IdNo = record.IdNo,
|
||||||
|
DepartmentName = department.Name,
|
||||||
|
Factory = gainfo.Factory,
|
||||||
|
CompareResult = record.CompareResult,
|
||||||
|
IsPassed = record.IsPassed,
|
||||||
|
Similarity = record.Similarity,
|
||||||
|
CompareCount = record.CompareCount,
|
||||||
|
LastCompareTime = record.LastCompareTime,
|
||||||
|
ApiResponse = record.ApiResponse,
|
||||||
|
ErrorMessage = record.ErrorMessage,
|
||||||
|
CreateTime = record.CreateTime
|
||||||
|
})
|
||||||
|
.ToPageListAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IdCardFaceCompareRecordResponse> CompareAsync(IdCardFaceCompareRequest request)
|
||||||
|
{
|
||||||
|
var gainfo = await _dbContext.Queryable<GainfoEntity>()
|
||||||
|
.Where(x => x.Code == request.UserCode)
|
||||||
|
.SingleAsync();
|
||||||
|
gainfo.ThrowIfNull("人员档案信息不存在");
|
||||||
|
gainfo.IdNo.IsPresent().ThrowIfFalse("人员证件号为空,无法对比");
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var userId = _currentUser.GetIntSubjectId();
|
||||||
|
var record = await _dbContext.Queryable<IdCardFaceCompareRecordEntity>()
|
||||||
|
.Where(x => x.UserCode == gainfo.Code)
|
||||||
|
.SingleAsync();
|
||||||
|
|
||||||
|
if (record == null)
|
||||||
|
{
|
||||||
|
record = new IdCardFaceCompareRecordEntity
|
||||||
|
{
|
||||||
|
UserCode = gainfo.Code,
|
||||||
|
UserType = gainfo.Type,
|
||||||
|
UserName = gainfo.Name,
|
||||||
|
IdNo = gainfo.IdNo,
|
||||||
|
CompareResult = IdCardFaceCompareResultEnumType.NotCompared,
|
||||||
|
CompareCount = 0,
|
||||||
|
CreateTime = now,
|
||||||
|
ModifiedTime = now,
|
||||||
|
CreatedBy = userId,
|
||||||
|
ModifiedBy = userId
|
||||||
|
};
|
||||||
|
|
||||||
|
await _dbContext.InsertAsync(record);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
record.UserType = gainfo.Type;
|
||||||
|
record.UserName = gainfo.Name;
|
||||||
|
record.IdNo = gainfo.IdNo;
|
||||||
|
record.CompareResult = IdCardFaceCompareResultEnumType.NotCompared;
|
||||||
|
record.ErrorMessage = null;
|
||||||
|
record.ModifiedTime = now;
|
||||||
|
record.ModifiedBy = userId;
|
||||||
|
await _dbContext.UpdateAsync(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
var face = await _faceDbContext.Queryable<GainfoFaceEntity>()
|
||||||
|
.Where(x => x.UserCode == gainfo.Code)
|
||||||
|
.Select(x => x.Face)
|
||||||
|
.SingleAsync();
|
||||||
|
|
||||||
|
if (!face.IsPresent())
|
||||||
|
{
|
||||||
|
record.ErrorMessage = "人脸照片尚未录入";
|
||||||
|
record.ModifiedTime = DateTime.Now;
|
||||||
|
record.ModifiedBy = userId;
|
||||||
|
await _dbContext.UpdateAsync(record);
|
||||||
|
throw new UserFriendlyException(record.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _aliyunClient.Id3MetaVerifyAsync(gainfo.IdNo, gainfo.Name, face);
|
||||||
|
record.CompareResult = IdCardFaceCompareResultEnumType.Compared;
|
||||||
|
record.ApiResponse = response.RawResponse;
|
||||||
|
record.IsPassed = response.Passed;
|
||||||
|
record.Similarity = response.Similarity;
|
||||||
|
record.CompareCount += 1;
|
||||||
|
record.LastCompareTime = now;
|
||||||
|
record.ModifiedTime = DateTime.Now;
|
||||||
|
record.ModifiedBy = userId;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
record.ErrorMessage = ex.Message;
|
||||||
|
record.ModifiedTime = DateTime.Now;
|
||||||
|
record.ModifiedBy = userId;
|
||||||
|
await _dbContext.UpdateAsync(record);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dbContext.UpdateAsync(record);
|
||||||
|
|
||||||
|
return await GetRecordAsync(record.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IdCardFaceCompareRecordResponse> GetRecordAsync(long id)
|
||||||
|
{
|
||||||
|
return await _dbContext.Queryable<IdCardFaceCompareRecordEntity>()
|
||||||
|
.LeftJoin<GainfoEntity>((record, gainfo) => record.UserCode == gainfo.Code)
|
||||||
|
.LeftJoin<DepartmentEntity>((record, gainfo, department) => gainfo.DepartmentNo == department.No)
|
||||||
|
.Where(record => record.Id == id)
|
||||||
|
.Select((record, gainfo, department) => new IdCardFaceCompareRecordResponse
|
||||||
|
{
|
||||||
|
Id = record.Id,
|
||||||
|
UserCode = record.UserCode,
|
||||||
|
UserName = record.UserName,
|
||||||
|
UserType = record.UserType,
|
||||||
|
IdNo = record.IdNo,
|
||||||
|
DepartmentName = department.Name,
|
||||||
|
Factory = gainfo.Factory,
|
||||||
|
CompareResult = record.CompareResult,
|
||||||
|
IsPassed = record.IsPassed,
|
||||||
|
Similarity = record.Similarity,
|
||||||
|
CompareCount = record.CompareCount,
|
||||||
|
LastCompareTime = record.LastCompareTime,
|
||||||
|
ApiResponse = record.ApiResponse,
|
||||||
|
ErrorMessage = record.ErrorMessage,
|
||||||
|
CreateTime = record.CreateTime
|
||||||
|
})
|
||||||
|
.SingleAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetFactories(List<string> requestFactories)
|
||||||
|
{
|
||||||
|
var currentFactories = _currentUser.GetUserClaim("tenants")?.Split(",").Where(x => x.IsPresent()).ToList() ?? new List<string>();
|
||||||
|
if (requestFactories == null || !requestFactories.Any())
|
||||||
|
{
|
||||||
|
return currentFactories;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestFactories.Where(x => currentFactories.Contains(x)).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using Dlhis.Entity.Basic.Enums;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.SqlSugar;
|
||||||
|
using SqlSugar;
|
||||||
|
using SqlSugar.DbConvert;
|
||||||
|
|
||||||
|
namespace Dlhis.Entity.Basic.Entity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人证人脸对比记录
|
||||||
|
/// </summary>
|
||||||
|
[SugarTable(TableName = "IdCardFaceCompareRecord", TableDescription = "人证人脸对比记录表")]
|
||||||
|
public class IdCardFaceCompareRecordEntity : SugarBaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户code
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = false, ColumnDescription = "用户code", UniqueGroupNameList = new[] { "idx_IdCardFaceCompareRecord_UserCode" })]
|
||||||
|
public string UserCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人员类型
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = false, ColumnDescription = "人员类型", ColumnDataType = "varchar(50)", SqlParameterDbType = typeof(EnumToStringConvert))]
|
||||||
|
public GainfoUserTypeEnumType UserType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 姓名
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "姓名")]
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 证件号
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "证件号", UniqueGroupNameList = new[] { "idx_IdCardFaceCompareRecord_IdNo" })]
|
||||||
|
public string IdNo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对比结果
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = false, ColumnDescription = "对比结果", ColumnDataType = "varchar(50)", SqlParameterDbType = typeof(EnumToStringConvert))]
|
||||||
|
public IdCardFaceCompareResultEnumType CompareResult { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// api响应结果
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "api响应结果", ColumnDataType = "longtext")]
|
||||||
|
public string ApiResponse { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否通过
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "是否通过", ColumnDataType = "bit")]
|
||||||
|
public bool? IsPassed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 相似度
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "相似度")]
|
||||||
|
public double? Similarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对比次数
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = false, ColumnDescription = "对比次数")]
|
||||||
|
public int CompareCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最近对比时间
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "最近对比时间")]
|
||||||
|
public DateTime? LastCompareTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 错误消息
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true, ColumnDescription = "错误消息", ColumnDataType = "text")]
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Dlhis.Entity.Basic.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人证人脸对比结果
|
||||||
|
/// </summary>
|
||||||
|
[Description("人证人脸对比结果")]
|
||||||
|
public enum IdCardFaceCompareResultEnumType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 未对比
|
||||||
|
/// </summary>
|
||||||
|
[Description("未对比")]
|
||||||
|
NotCompared = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已对比
|
||||||
|
/// </summary>
|
||||||
|
[Description("已对比")]
|
||||||
|
Compared = 1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user