This commit is contained in:
owenchen committed 2026-10-17 21:08:10 +08:00
1 parent 282d8ce583
commit 28dd3ffebd
229 files changed
+5765 -4786

No files matched your search

@@ -19,6 +19,7 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Globalization;
using System.Linq;
using static System.Net.Mime.MediaTypeNames;
using Image = SixLabors.ImageSharp.Image;
@@ -1129,6 +1130,103 @@ namespace CloudBuilder.Master.Service
repositoryFolderMap.InsertNow(map);
}
[PermissionOperate(name: MasterPermissionPolicy.DmsFile, operate: Operate.Insert)]
[UnitOfWork]
public FamilyPersonDisplayData UploadIdCardFile()
{
FamilyPersonDisplayData data = new FamilyPersonDisplayData();
//1) 先走原有分片上传落盘(FilePath 头指定 idcard 目录)
UploadFile();
//2) 取本次上传文件,server-to-server 调 OCR 识别服务(无浏览器跨域)
var file = httpContextAccessor.HttpContext?.Request?.Form?.Files?.FirstOrDefault();
if (file == null || file.Length <= 0) return data;
//文件名(xxxx-1-1.ext 分片名)与识别面:side 以普通表单字段传入,缺省取配置
data.IdCardFilename = file.FileName;
string side = ServiceSettingsPolicy.GetIdCardOcrSide(service);
if (httpContextAccessor.HttpContext.Request.Form.ContainsKey("side"))
{
var formSide = httpContextAccessor.HttpContext.Request.Form["side"].ToString();
if (!string.IsNullOrWhiteSpace(formSide)) side = formSide;
}
string apiUrl = ServiceSettingsPolicy.GetIdCardOcrApiUrl(service);
if (string.IsNullOrWhiteSpace(apiUrl)) return data;
try
{
using var recognizeStream = file.OpenReadStream();
using var form = new System.Net.Http.MultipartFormDataContent();
var fileContent = new System.Net.Http.StreamContent(recognizeStream);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(
string.IsNullOrWhiteSpace(file.ContentType) ? "image/jpeg" : file.ContentType);
form.Add(fileContent, "file", file.FileName);
form.Add(new System.Net.Http.StringContent(side), "side");
using var client = new System.Net.Http.HttpClient();
client.Timeout = TimeSpan.FromSeconds(ServiceSettingsPolicy.GetIdCardOcrTimeoutSeconds(service));
using var response = client.PostAsync(apiUrl, form).GetAwaiter().GetResult();
string json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (string.IsNullOrWhiteSpace(json)) return data;
using var doc = System.Text.Json.JsonDocument.Parse(json);
var root = doc.RootElement;
//识别服务返回 { code:0, data:{... } },code!=0 视为未识别到,仅返回文件名
int code = -1;
if (root.TryGetProperty("code", out var codeEl) && codeEl.ValueKind == System.Text.Json.JsonValueKind.Number)
code = codeEl.GetInt32();
if (code != 0) return data;
if (!root.TryGetProperty("data", out var info) || info.ValueKind != System.Text.Json.JsonValueKind.Object)
return data;
string GetStr(string key)
{
if (info.TryGetProperty(key, out var el))
{
if (el.ValueKind == System.Text.Json.JsonValueKind.String) return el.GetString();
if (el.ValueKind != System.Text.Json.JsonValueKind.Null && el.ValueKind != System.Text.Json.JsonValueKind.Undefined)
return el.ToString();
}
return null;
}
//常用名/民族/住址/身份证号/签发机关/有效期限(姓名映射到 CommonName 已在需求确认)
data.CommonName = GetStr("name");
data.Nation = GetStr("nation");
data.Address = GetStr("address");
data.IdNumber = GetStr("id_number");
data.Issuer = GetStr("issuer");
data.Validity = GetStr("validity");
//性别回传原始文本(如“男”),由前端按 GenderPolicy 匹配为策略编码,避免后端依赖运行时语言包
data.Gender = GetStr("gender");
//出生日期:OCR 返回多为 yyyyMMdd,解析为 DateTime(Kind=Unspecified,避免时区偏移)
var birth = GetStr("birth");
if (!string.IsNullOrWhiteSpace(birth))
{
var digits = new string(birth.Where(char.IsDigit).ToArray());
if (digits.Length >= 8
&& DateTime.TryParseExact(digits.Substring(0, 8), "yyyyMMdd",
System.Globalization.CultureInfo.InvariantCulture,
DateTimeStyles.None, out var birthDate))
{
data.BirthDate = birthDate;
}
}
}
catch (Exception ex)
{
//OCR 调用失败不影响图片已上传:记录日志,返回仅含文件名的 data,前端据此提示“未识别到信息”
Console.WriteLine($"[UploadIdCardFile] OCR recognize failed: {ex.Message}");
}
return data;
}
[PermissionOperate(name: MasterPermissionPolicy.DmsFile, operate: Operate.Insert)]
[UnitOfWork]
public void UploadFile()
@@ -1386,7 +1484,7 @@ namespace CloudBuilder.Master.Service
{
DmsFileEntity entity = repository.DetachedEntities.Where(x => x.FileId == fileId && x.CreatedBy == service.UserId).FirstOrDefault()!;
return FileTypePolicy.PackSourceVideoUrl(service, entity.CreatedBy, entity.OrderKey,entity.TranscodeIndc);
return FileTypePolicy.PackSourceVideoUrl(service, entity.CreatedBy, entity.OrderKey, entity.TranscodeIndc);
}
[PermissionOperate(name: MasterPermissionPolicy.DmsFile, operate: Operate.Find)]
@@ -1395,7 +1493,7 @@ namespace CloudBuilder.Master.Service
IRepository<DmsFileDeleteLogEntity> repositoryDmsFileDeleteLog = repository.GetRepository<IRepository<DmsFileDeleteLogEntity>>();
DmsFileDeleteLogEntity entity = repositoryDmsFileDeleteLog.DetachedEntities.Where(x => x.FileId == fileId && x.CreatedBy == service.UserId).FirstOrDefault()!;
return FileTypePolicy.PackSourceVideoUrl(service, entity.CreatedBy, entity.OrderKey,entity.TranscodeIndc);
return FileTypePolicy.PackSourceVideoUrl(service, entity.CreatedBy, entity.OrderKey, entity.TranscodeIndc);
}
private string PackParameters(DmsFileEntity dmsFile)
@@ -1436,14 +1534,14 @@ namespace CloudBuilder.Master.Service
}
para = string.Format(service.Configuration[ServiceSettingsPolicy.FfmpegArguments], mp4Url, "", fullName);
IRepository<SecurityScheduleTaskQueueEntity> logRepository = repository.GetRepository<IRepository<SecurityScheduleTaskQueueEntity>>();
SecurityScheduleTaskQueueEntity task = new SecurityScheduleTaskQueueEntity();
task.DocumentId = documentId;
task.TaskId = service.Configuration[ServiceSettingsPolicy.FfmpegPath];
task.TaskParam = para;
logRepository.InsertNow(task);
para = string.Format(service.Configuration[ServiceSettingsPolicy.FfmpegArguments], mp4Url, " -t 10", fullName);
@@ -26,6 +26,28 @@ namespace CloudBuilder.Master.Data
public string? AvatarUrl { get; set; }
public bool? IsCanAmend { get; set; } = false;
public bool? IsCanAmend { get; set; } = false;
// ===== 身份证 OCR 识别信息(transient 显示字段:识别后回填表单,随保存提交但不落 family_person 表) =====
/// <summary>民族(OCR nation)</summary>
public string? Nation { get; set; }
/// <summary>住址(OCR address)</summary>
public string? Address { get; set; }
/// <summary>公民身份号码(OCR id_number)</summary>
public string? IdNumber { get; set; }
/// <summary>签发机关(OCR issuer,仅背面)</summary>
public string? Issuer { get; set; }
/// <summary>有效期限(OCR validity,仅背面)</summary>
public string? Validity { get; set; }
/// <summary>身份证照片物理文件名(上传到 idcard 目录后回写,与头像 AvatarFilename 同口径)</summary>
public string? IdCardFilename { get; set; }
/// <summary>身份证照片预览 URL(本地临时预览,不入库)</summary>
public string? IdCardUrl { get; set; }
}
}
@@ -32,7 +32,7 @@ namespace CloudBuilder.Master.Service
DmsFolderData FindDmsFolderData();
void UploadFile();
void UploadDmsFile();
FamilyPersonDisplayData UploadIdCardFile();
string FindM3u8Path(long fileId);
string FindSourceVideoPath(long fileId);
@@ -103,6 +103,10 @@ return Task.Run(() => serviceRequest.PostAsJsonAsync<DmsFolderData> ("/DmsFile
{
Task.Run(() => serviceRequest.PostAsJsonAsync<DmsFileEntity,dynamic>("/DmsFile/MoveDmsFolder" , dmsFile)).GetAwaiter().GetResult(); }
public FamilyPersonDisplayData UploadIdCardFile()
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<FamilyPersonDisplayData> ("/DmsFile/UploadIdCardFile")).GetAwaiter().GetResult(); }
public void UploadFile()
{
Task.Run(() => serviceRequest.PostAsync("/DmsFile/UploadFile")).GetAwaiter().GetResult(); }
@@ -24,6 +24,28 @@ namespace CloudBuilder.Security.Policy
public const string FfmpegPictrue = "Ffmpeg:Pictrue";
public const string FfmpegArguments = "Ffmpeg:Arguments";
// 身份证 OCR 识别服务(server-to-server 调用,地址来自 appsettings.json,避免写死)
public const string IdCardOcrApiUrl = "IdCardOcr:ApiUrl";
public const string IdCardOcrSide = "IdCardOcr:Side";
public const string IdCardOcrTimeoutSeconds = "IdCardOcr:TimeoutSeconds";
public static string GetIdCardOcrApiUrl(IApplicationService service)
{
return service.Configuration[IdCardOcrApiUrl];
}
public static string GetIdCardOcrSide(IApplicationService service)
{
var side = service.Configuration[IdCardOcrSide];
return string.IsNullOrWhiteSpace(side) ? "auto" : side;
}
public static int GetIdCardOcrTimeoutSeconds(IApplicationService service)
{
var raw = service.Configuration[IdCardOcrTimeoutSeconds];
return int.TryParse(raw, out var seconds) && seconds > 0 ? seconds : 30;
}
public static string GetPhysicalFileDirectory(IApplicationService service)
{
return service.Configuration[PhysicalFileDirectory];
@@ -430,6 +430,27 @@ this.dmsFileService= dmsFileService;
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "DmsFile", operate: "Insert")]
[HttpPost]
[UnitOfWork]
public dynamic UploadIdCardFile()
{
try
{
var result = dmsFileService.UploadIdCardFile();
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "DmsFile", operate: "Insert")]
@@ -3,7 +3,7 @@
<Project>
<PropertyGroup>
<_PublishTargetUrl>C:\inetpub\wwwroot\dockwebapi2</_PublishTargetUrl>
<History>True|2026-09-23T05:19:13.0148919Z||;True|2026-09-23T13:16:07.8584304+08:00||;True|2026-09-22T23:03:13.6509491+08:00||;True|2026-09-22T16:07:15.9177123+08:00||;True|2026-09-22T11:19:19.6087109+08:00||;True|2026-09-22T11:09:24.9165065+08:00||;True|2026-09-21T22:40:07.1232044+08:00||;True|2026-09-21T22:35:06.9446571+08:00||;True|2026-09-21T22:27:42.1146825+08:00||;False|2026-09-21T22:27:17.6066573+08:00||;True|2026-09-21T21:01:22.8888913+08:00||;True|2026-09-21T19:15:03.4753747+08:00||;True|2026-09-21T18:08:30.0866986+08:00||;True|2026-09-21T16:51:19.8137872+08:00||;True|2026-09-21T16:30:01.0950896+08:00||;True|2026-09-21T15:09:00.2326737+08:00||;True|2026-09-21T14:47:09.6120256+08:00||;True|2026-09-21T14:16:35.3194409+08:00||;True|2026-09-21T13:54:58.2829712+08:00||;True|2026-09-21T13:36:45.3134499+08:00||;True|2026-09-19T09:48:52.0085130+08:00||;False|2026-09-19T09:47:56.1658214+08:00||;True|2026-09-17T18:50:15.9493380+08:00||;True|2026-09-17T18:11:11.5364547+08:00||;True|2026-09-17T18:07:27.7830758+08:00||;True|2026-09-17T17:58:16.5798560+08:00||;True|2026-09-17T15:37:30.9025614+08:00||;True|2026-09-17T15:25:46.9116292+08:00||;True|2026-09-17T15:05:18.6592403+08:00||;True|2026-09-17T14:57:28.7997437+08:00||;True|2026-09-17T14:36:00.6339386+08:00||;True|2026-09-17T13:56:43.2376873+08:00||;True|2026-09-16T13:56:51.8724546+08:00||;True|2026-09-15T20:44:46.6559899+08:00||;True|2026-09-13T19:02:59.5654245+08:00||;True|2026-09-10T09:14:56.2914193+08:00||;True|2026-09-09T16:40:16.6461154+08:00||;True|2026-09-08T13:55:23.1502435+08:00||;True|2026-09-08T08:04:53.4279263+08:00||;True|2026-09-07T08:48:17.4682304+08:00||;True|2026-09-04T14:39:08.7878670+08:00||;True|2026-09-04T14:35:48.2937877+08:00||;True|2026-09-04T11:45:16.1372876+08:00||;True|2026-09-04T11:42:59.9356436+08:00||;True|2026-09-03T16:05:52.5270232+08:00||;True|2026-09-03T14:52:56.8559144+08:00||;True|2026-09-02T11:08:23.8720038+08:00||;True|2026-09-02T10:58:16.8081797+08:00||;True|2026-08-26T13:34:12.3805892+08:00||;True|2026-08-25T08:31:30.7787234+08:00||;True|2026-08-20T13:10:46.4725468+08:00||;True|2026-08-20T13:10:28.3142576+08:00||;True|2026-08-19T16:10:27.3362887+08:00||;True|2026-08-19T16:01:12.6234971+08:00||;True|2026-08-19T15:42:46.6576265+08:00||;True|2026-08-19T13:39:59.9363522+08:00||;True|2026-08-19T13:36:53.0379313+08:00||;True|2026-08-19T13:31:36.0841398+08:00||;True|2026-08-19T11:44:37.5319626+08:00||;True|2026-08-19T09:36:45.6000047+08:00||;True|2026-08-19T09:34:36.8468631+08:00||;True|2026-08-19T08:35:41.2202300+08:00||;True|2026-08-18T16:20:59.0170916+08:00||;</History>
<History>True|2026-10-17T12:49:21.0401213Z||;True|2026-09-23T13:19:13.0148919+08:00||;True|2026-09-23T13:16:07.8584304+08:00||;True|2026-09-22T23:03:13.6509491+08:00||;True|2026-09-22T16:07:15.9177123+08:00||;True|2026-09-22T11:19:19.6087109+08:00||;True|2026-09-22T11:09:24.9165065+08:00||;True|2026-09-21T22:40:07.1232044+08:00||;True|2026-09-21T22:35:06.9446571+08:00||;True|2026-09-21T22:27:42.1146825+08:00||;False|2026-09-21T22:27:17.6066573+08:00||;True|2026-09-21T21:01:22.8888913+08:00||;True|2026-09-21T19:15:03.4753747+08:00||;True|2026-09-21T18:08:30.0866986+08:00||;True|2026-09-21T16:51:19.8137872+08:00||;True|2026-09-21T16:30:01.0950896+08:00||;True|2026-09-21T15:09:00.2326737+08:00||;True|2026-09-21T14:47:09.6120256+08:00||;True|2026-09-21T14:16:35.3194409+08:00||;True|2026-09-21T13:54:58.2829712+08:00||;True|2026-09-21T13:36:45.3134499+08:00||;True|2026-09-19T09:48:52.0085130+08:00||;False|2026-09-19T09:47:56.1658214+08:00||;True|2026-09-17T18:50:15.9493380+08:00||;True|2026-09-17T18:11:11.5364547+08:00||;True|2026-09-17T18:07:27.7830758+08:00||;True|2026-09-17T17:58:16.5798560+08:00||;True|2026-09-17T15:37:30.9025614+08:00||;True|2026-09-17T15:25:46.9116292+08:00||;True|2026-09-17T15:05:18.6592403+08:00||;True|2026-09-17T14:57:28.7997437+08:00||;True|2026-09-17T14:36:00.6339386+08:00||;True|2026-09-17T13:56:43.2376873+08:00||;True|2026-09-16T13:56:51.8724546+08:00||;True|2026-09-15T20:44:46.6559899+08:00||;True|2026-09-13T19:02:59.5654245+08:00||;True|2026-09-10T09:14:56.2914193+08:00||;True|2026-09-09T16:40:16.6461154+08:00||;True|2026-09-08T13:55:23.1502435+08:00||;True|2026-09-08T08:04:53.4279263+08:00||;True|2026-09-07T08:48:17.4682304+08:00||;True|2026-09-04T14:39:08.7878670+08:00||;True|2026-09-04T14:35:48.2937877+08:00||;True|2026-09-04T11:45:16.1372876+08:00||;True|2026-09-04T11:42:59.9356436+08:00||;True|2026-09-03T16:05:52.5270232+08:00||;True|2026-09-03T14:52:56.8559144+08:00||;True|2026-09-02T11:08:23.8720038+08:00||;True|2026-09-02T10:58:16.8081797+08:00||;True|2026-08-26T13:34:12.3805892+08:00||;True|2026-08-25T08:31:30.7787234+08:00||;True|2026-08-20T13:10:46.4725468+08:00||;True|2026-08-20T13:10:28.3142576+08:00||;True|2026-08-19T16:10:27.3362887+08:00||;True|2026-08-19T16:01:12.6234971+08:00||;True|2026-08-19T15:42:46.6576265+08:00||;True|2026-08-19T13:39:59.9363522+08:00||;True|2026-08-19T13:36:53.0379313+08:00||;True|2026-08-19T13:31:36.0841398+08:00||;True|2026-08-19T11:44:37.5319626+08:00||;True|2026-08-19T09:36:45.6000047+08:00||;True|2026-08-19T09:34:36.8468631+08:00||;True|2026-08-19T08:35:41.2202300+08:00||;True|2026-08-18T16:20:59.0170916+08:00||;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>
@@ -73,6 +73,11 @@
"Pictrue": " -i {0} -ss 00:00:2.000 -frames:v 1 -vf \"scale=1280:-2\" -q:v 4 {1}", //" -i {0} -ss 00:00:2.000 -frames:v 1 {1}",
"Arguments": "-i {0}{1} -c:v libx264 -preset veryfast -crf 26 -profile:v main -level 3.1 -vf \"scale='min(1280,iw)':-2\" -maxrate 1500k -bufsize 2400k -c:a aac -b:a 96k -ac 2 -ar 44100 -start_number 0 -hls_time 3 -hls_list_size 0 -hls_flags independent_segments -f hls {2}" //0-源路径,1-切片秒数,2-目标路径
},
"IdCardOcr": {
"ApiUrl": "https://web.youjiala.com/api/v1/idcard/recognize", //身份证 OCR 识别服务(同 docker 内网,server-to-server 调用,无浏览器跨域)
"Side": "auto", //识别面:auto/front/back
"TimeoutSeconds": 30
},
"CloudBuilder.Topshelf": {
"Path": "D:\\MAFAI\\CloudBuilder.Topshelf\\bin\\Debug\\net8.0\\CloudBuilder.Topshelf.exe" //图片压缩
},