mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-05-12 02:20:28 +08:00
feat(background): add face recognition functionality- Implement face recognition task type and processing
This commit is contained in:
@@ -38,6 +38,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddHostedService<QueuedHostedService>();
|
||||
services.AddSingleton<IStorageService, StorageService>();
|
||||
services.AddSingleton<PictureTaskProcessor>();
|
||||
services.AddSingleton<FaceRecognitionTaskProcessor>();
|
||||
services.AddSingleton<VisualRecognitionTaskProcessor>();
|
||||
services.AddSingleton<IDatabaseInitializer, DatabaseInitializer>();
|
||||
services.AddSingleton<IMappingService, MappingService>();
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Foxel.Models.DataBase
|
||||
{
|
||||
PictureProcessing = 0,
|
||||
VisualRecognition = 1,
|
||||
FaceRecognition = 2,
|
||||
}
|
||||
|
||||
public enum TaskExecutionStatus
|
||||
|
||||
24
Models/DataBase/Face.cs
Normal file
24
Models/DataBase/Face.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Foxel.Models.DataBase;
|
||||
|
||||
public class Face : BaseModel
|
||||
{
|
||||
public float[]? Embedding { get; set; }
|
||||
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int W { get; set; }
|
||||
public int H { get; set; }
|
||||
|
||||
[Range(0.0, 1.0)]
|
||||
public double FaceConfidence { get; set; }
|
||||
|
||||
public int PictureId { get; set; }
|
||||
|
||||
[StringLength(255)]
|
||||
public string? PersonName { get; set; }
|
||||
[ForeignKey("PictureId")]
|
||||
public Picture Picture { get; set; } = null!;
|
||||
}
|
||||
@@ -48,6 +48,8 @@ public class Picture : BaseModel
|
||||
public Album? Album { get; set; }
|
||||
|
||||
public ICollection<Favorite>? Favorites { get; set; }
|
||||
|
||||
public ICollection<Face>? Faces { get; set; }
|
||||
|
||||
public bool ContentWarning { get; set; } = false;
|
||||
public PermissionType Permission { get; set; } = PermissionType.Public;
|
||||
|
||||
@@ -15,6 +15,7 @@ public class MyDbContext(DbContextOptions<MyDbContext> options) : DbContext(opti
|
||||
public DbSet<Log> Logs { get; set; } = null!;
|
||||
public DbSet<BackgroundTask> BackgroundTasks { get; set; } = null!;
|
||||
public DbSet<StorageMode> StorageModes { get; set; } = null!;
|
||||
public DbSet<Face> Faces { get; set; } = null!;
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
@@ -108,6 +108,37 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
|
||||
return backgroundTask.Id;
|
||||
}
|
||||
|
||||
public async Task<Guid> QueueFaceRecognitionTaskAsync(FaceRecognitionPayload payload)
|
||||
{
|
||||
await using var dbContext = await _contextFactory.CreateDbContextAsync();
|
||||
var picture = await dbContext.Pictures.FindAsync(payload.PictureId);
|
||||
if (picture == null)
|
||||
{
|
||||
_logger.LogError("无法为不存在的图片 PictureId: {PictureId} 创建人脸识别任务", payload.PictureId);
|
||||
throw new KeyNotFoundException($"尝试为 PictureId: {payload.PictureId} 创建人脸识别任务时找不到图片");
|
||||
}
|
||||
|
||||
var backgroundTask = new BackgroundTask
|
||||
{
|
||||
Type = TaskType.FaceRecognition,
|
||||
Payload = JsonSerializer.Serialize(payload),
|
||||
UserId = payload.UserIdForPicture,
|
||||
RelatedEntityId = payload.PictureId,
|
||||
Status = TaskExecutionStatus.Pending,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
dbContext.BackgroundTasks.Add(backgroundTask);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
await _queue.Writer.WriteAsync(backgroundTask.Id);
|
||||
_logger.LogInformation("人脸识别任务已加入队列: TaskId={TaskId}, PictureId={PictureId}", backgroundTask.Id, payload.PictureId);
|
||||
|
||||
StartProcessor();
|
||||
|
||||
return backgroundTask.Id;
|
||||
}
|
||||
|
||||
public async Task<List<TaskDetailsDto>> GetUserTasksStatusAsync(int userId)
|
||||
{
|
||||
await using var dbContext = await _contextFactory.CreateDbContextAsync();
|
||||
@@ -132,7 +163,7 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
|
||||
taskName = "图片处理 (图片信息丢失)";
|
||||
}
|
||||
}
|
||||
else if (task.Type == TaskType.VisualRecognition && task.RelatedEntityId.HasValue) // Added for VisualRecognition
|
||||
else if (task.Type == TaskType.VisualRecognition && task.RelatedEntityId.HasValue)
|
||||
{
|
||||
var picture = await dbContext.Pictures.FindAsync(task.RelatedEntityId.Value);
|
||||
if (picture != null)
|
||||
@@ -144,6 +175,18 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
|
||||
taskName = "视觉识别 (图片信息丢失)";
|
||||
}
|
||||
}
|
||||
else if (task.Type == TaskType.FaceRecognition && task.RelatedEntityId.HasValue)
|
||||
{
|
||||
var picture = await dbContext.Pictures.FindAsync(task.RelatedEntityId.Value);
|
||||
if (picture != null)
|
||||
{
|
||||
taskName = $"人脸识别: {picture.Name}";
|
||||
}
|
||||
else
|
||||
{
|
||||
taskName = "人脸识别 (图片信息丢失)";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
taskName = $"任务: {task.Id} ({task.Type})"; // Generic name
|
||||
@@ -201,7 +244,7 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
|
||||
{
|
||||
await using var dbContext = await _contextFactory.CreateDbContextAsync();
|
||||
var unfinishedTasks = await dbContext.BackgroundTasks
|
||||
.Where(bt => (bt.Type == TaskType.PictureProcessing || bt.Type == TaskType.VisualRecognition) && // Added VisualRecognition
|
||||
.Where(bt => (bt.Type == TaskType.PictureProcessing || bt.Type == TaskType.VisualRecognition || bt.Type == TaskType.FaceRecognition) &&
|
||||
(bt.Status == TaskExecutionStatus.Pending || bt.Status == TaskExecutionStatus.Processing))
|
||||
.ToListAsync();
|
||||
|
||||
@@ -286,13 +329,16 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
|
||||
case TaskType.PictureProcessing:
|
||||
processor = scope.ServiceProvider.GetRequiredService<PictureTaskProcessor>();
|
||||
break;
|
||||
case TaskType.VisualRecognition: // Added case for VisualRecognition
|
||||
case TaskType.VisualRecognition:
|
||||
processor = scope.ServiceProvider.GetRequiredService<VisualRecognitionTaskProcessor>();
|
||||
break;
|
||||
case TaskType.FaceRecognition:
|
||||
processor = scope.ServiceProvider.GetRequiredService<FaceRecognitionTaskProcessor>();
|
||||
break;
|
||||
default:
|
||||
_logger.LogError("未找到任务类型 {TaskType} 的处理器: TaskId={TaskId}", taskToCheck.Type, taskToCheck.Id);
|
||||
await MarkTaskAsFailedByQueue(taskToCheck.Id, $"未找到任务类型 {taskToCheck.Type} 的处理器。");
|
||||
continue; // Continue to next task in queue
|
||||
continue;
|
||||
}
|
||||
await processor.ProcessAsync(taskToCheck); // Processor handles its own final status update
|
||||
}
|
||||
|
||||
@@ -23,6 +23,13 @@ public interface IBackgroundTaskQueue
|
||||
/// <returns>任务ID</returns>
|
||||
Task<Guid> QueueVisualRecognitionTaskAsync(VisualRecognitionPayload payload);
|
||||
|
||||
/// <summary>
|
||||
/// 将人脸识别任务添加到队列
|
||||
/// </summary>
|
||||
/// <param name="payload">人脸识别任务的Payload</param>
|
||||
/// <returns>任务ID</returns>
|
||||
Task<Guid> QueueFaceRecognitionTaskAsync(FaceRecognitionPayload payload);
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户的所有任务状态 (目前主要指图片处理任务)
|
||||
/// </summary>
|
||||
|
||||
281
Services/Background/Processors/FaceRecognitionTaskProcessor.cs
Normal file
281
Services/Background/Processors/FaceRecognitionTaskProcessor.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using Foxel.Models.DataBase;
|
||||
using Foxel.Services.Storage;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Foxel.Services.Background.Processors
|
||||
{
|
||||
public class FaceRecognitionPayload
|
||||
{
|
||||
public int PictureId { get; set; }
|
||||
public int? UserIdForPicture { get; set; }
|
||||
}
|
||||
|
||||
public class FaceRecognitionResponse
|
||||
{
|
||||
[JsonPropertyName("detector_backend")]
|
||||
public string DetectorBackend { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("recognition_model")]
|
||||
public string RecognitionModel { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("result")]
|
||||
public List<FaceResult> Result { get; set; } = new();
|
||||
}
|
||||
|
||||
public class FaceResult
|
||||
{
|
||||
[JsonPropertyName("embedding")]
|
||||
public float[] Embedding { get; set; } = Array.Empty<float>();
|
||||
|
||||
[JsonPropertyName("facial_area")]
|
||||
public FacialAreaResponse FacialArea { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("face_confidence")]
|
||||
public double FaceConfidence { get; set; }
|
||||
}
|
||||
|
||||
public class FacialAreaResponse
|
||||
{
|
||||
[JsonPropertyName("x")]
|
||||
public int X { get; set; }
|
||||
|
||||
[JsonPropertyName("y")]
|
||||
public int Y { get; set; }
|
||||
|
||||
[JsonPropertyName("w")]
|
||||
public int W { get; set; }
|
||||
|
||||
[JsonPropertyName("h")]
|
||||
public int H { get; set; }
|
||||
}
|
||||
|
||||
public class FaceRecognitionTaskProcessor : ITaskProcessor
|
||||
{
|
||||
private readonly IDbContextFactory<MyDbContext> _contextFactory;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<FaceRecognitionTaskProcessor> _logger;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string FaceApiUrl = "http://103.143.81.28:8066/represent";
|
||||
private const string ApiKey = "";
|
||||
|
||||
public FaceRecognitionTaskProcessor(
|
||||
IDbContextFactory<MyDbContext> contextFactory,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<FaceRecognitionTaskProcessor> logger,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task ProcessAsync(BackgroundTask backgroundTask)
|
||||
{
|
||||
if (backgroundTask.Payload == null)
|
||||
{
|
||||
await UpdateTaskStatusInDb(backgroundTask.Id, TaskExecutionStatus.Failed, 0, "任务 Payload 为空。");
|
||||
_logger.LogError("人脸识别任务 Payload 为空: TaskId={TaskId}", backgroundTask.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
FaceRecognitionPayload? payload;
|
||||
try
|
||||
{
|
||||
payload = JsonSerializer.Deserialize<FaceRecognitionPayload>(backgroundTask.Payload);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogError(ex, "无法解析人脸识别任务的 Payload: TaskId={TaskId}", backgroundTask.Id);
|
||||
await UpdateTaskStatusInDb(backgroundTask.Id, TaskExecutionStatus.Failed, 0, "Payload 解析失败。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload == null || payload.PictureId == 0)
|
||||
{
|
||||
_logger.LogError("人脸识别任务的 Payload 无效或缺少 PictureId: TaskId={TaskId}", backgroundTask.Id);
|
||||
await UpdateTaskStatusInDb(backgroundTask.Id, TaskExecutionStatus.Failed, 0, "Payload 无效或缺少 PictureId。");
|
||||
return;
|
||||
}
|
||||
|
||||
var pictureId = payload.PictureId;
|
||||
string tempImagePath = string.Empty;
|
||||
bool isTempFile = false;
|
||||
|
||||
await using var dbContext = await _contextFactory.CreateDbContextAsync();
|
||||
var currentBackgroundTaskState = await dbContext.BackgroundTasks.FindAsync(backgroundTask.Id);
|
||||
if (currentBackgroundTaskState == null)
|
||||
{
|
||||
_logger.LogError("在 FaceRecognitionTaskProcessor 中找不到后台任务: TaskId={TaskId}", backgroundTask.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
var picture = await dbContext.Pictures
|
||||
.Include(p => p.StorageMode)
|
||||
.FirstOrDefaultAsync(p => p.Id == pictureId);
|
||||
|
||||
try
|
||||
{
|
||||
await UpdateTaskStatusInDb(currentBackgroundTaskState.Id, TaskExecutionStatus.Processing, 10,
|
||||
currentBackgroundTaskState: currentBackgroundTaskState);
|
||||
|
||||
if (picture == null)
|
||||
{
|
||||
throw new Exception($"找不到ID为{pictureId}的图片。");
|
||||
}
|
||||
|
||||
if (picture.StorageMode == null || picture.StorageModeId < 0)
|
||||
{
|
||||
throw new Exception($"图片ID {pictureId} 缺少有效的 StorageMode 配置。");
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var storageService = scope.ServiceProvider.GetRequiredService<IStorageService>();
|
||||
|
||||
await UpdateTaskStatusInDb(currentBackgroundTaskState.Id, TaskExecutionStatus.Processing, 20,
|
||||
currentBackgroundTaskState: currentBackgroundTaskState);
|
||||
|
||||
// 下载图片文件用于人脸识别
|
||||
tempImagePath = await storageService.ExecuteAsync(picture.StorageModeId,
|
||||
provider => provider.DownloadFileAsync(picture.Path));
|
||||
isTempFile = true;
|
||||
|
||||
if (string.IsNullOrEmpty(tempImagePath) || !File.Exists(tempImagePath))
|
||||
{
|
||||
throw new Exception($"找不到用于人脸识别的图片文件: {tempImagePath}");
|
||||
}
|
||||
|
||||
await UpdateTaskStatusInDb(currentBackgroundTaskState.Id, TaskExecutionStatus.Processing, 40,
|
||||
currentBackgroundTaskState: currentBackgroundTaskState);
|
||||
|
||||
// 调用人脸识别 API
|
||||
var faceRecognitionResult = await CallFaceRecognitionApiAsync(tempImagePath);
|
||||
|
||||
await UpdateTaskStatusInDb(currentBackgroundTaskState.Id, TaskExecutionStatus.Processing, 70,
|
||||
currentBackgroundTaskState: currentBackgroundTaskState);
|
||||
|
||||
// 保存人脸数据到数据库
|
||||
if (faceRecognitionResult?.Result != null && faceRecognitionResult.Result.Any())
|
||||
{
|
||||
foreach (var faceResult in faceRecognitionResult.Result)
|
||||
{
|
||||
var face = new Face
|
||||
{
|
||||
PictureId = pictureId,
|
||||
Embedding = faceResult.Embedding,
|
||||
X = faceResult.FacialArea.X,
|
||||
Y = faceResult.FacialArea.Y,
|
||||
W = faceResult.FacialArea.W,
|
||||
H = faceResult.FacialArea.H,
|
||||
FaceConfidence = faceResult.FaceConfidence
|
||||
};
|
||||
|
||||
dbContext.Faces.Add(face);
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
_logger.LogInformation("为图片 {PictureId} 检测到 {FaceCount} 个人脸", pictureId, faceRecognitionResult.Result.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("图片 {PictureId} 未检测到人脸", pictureId);
|
||||
}
|
||||
|
||||
await UpdateTaskStatusInDb(currentBackgroundTaskState.Id, TaskExecutionStatus.Completed, 100,
|
||||
completedAt: DateTime.UtcNow, currentBackgroundTaskState: currentBackgroundTaskState);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "人脸识别任务失败: TaskId={TaskId}, PictureId={PictureId}",
|
||||
currentBackgroundTaskState.Id, pictureId);
|
||||
await UpdateTaskStatusInDb(currentBackgroundTaskState.Id, TaskExecutionStatus.Failed,
|
||||
currentBackgroundTaskState.Progress, ex.Message,
|
||||
currentBackgroundTaskState: currentBackgroundTaskState);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (isTempFile && File.Exists(tempImagePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(tempImagePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "删除临时人脸识别图片文件失败: {FilePath}", tempImagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<FaceRecognitionResponse?> CallFaceRecognitionApiAsync(string imagePath)
|
||||
{
|
||||
using var form = new MultipartFormDataContent();
|
||||
using var fileStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
|
||||
using var fileContent = new StreamContent(fileStream);
|
||||
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
|
||||
form.Add(fileContent, "file", Path.GetFileName(imagePath));
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, FaceApiUrl);
|
||||
request.Headers.Add("api-key", ApiKey);
|
||||
request.Content = form;
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"人脸识别 API 调用失败: {response.StatusCode}, {errorContent}");
|
||||
}
|
||||
|
||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<FaceRecognitionResponse>(jsonContent);
|
||||
}
|
||||
|
||||
private async Task UpdateTaskStatusInDb(Guid taskId, TaskExecutionStatus status, int progress,
|
||||
string? error = null, DateTime? startedAt = null, DateTime? completedAt = null,
|
||||
BackgroundTask? currentBackgroundTaskState = null)
|
||||
{
|
||||
await using var dbContext = await _contextFactory.CreateDbContextAsync();
|
||||
var taskToUpdate = currentBackgroundTaskState ?? await dbContext.BackgroundTasks.FindAsync(taskId);
|
||||
|
||||
if (taskToUpdate != null)
|
||||
{
|
||||
if (currentBackgroundTaskState != null &&
|
||||
dbContext.Entry(currentBackgroundTaskState).State == EntityState.Detached)
|
||||
{
|
||||
dbContext.BackgroundTasks.Attach(currentBackgroundTaskState);
|
||||
}
|
||||
|
||||
taskToUpdate.Status = status;
|
||||
taskToUpdate.Progress = progress;
|
||||
taskToUpdate.ErrorMessage = string.IsNullOrEmpty(error) ? taskToUpdate.ErrorMessage : error;
|
||||
if (startedAt.HasValue) taskToUpdate.StartedAt = startedAt;
|
||||
if (completedAt.HasValue) taskToUpdate.CompletedAt = completedAt;
|
||||
|
||||
if ((status == TaskExecutionStatus.Completed || status == TaskExecutionStatus.Failed) &&
|
||||
!taskToUpdate.StartedAt.HasValue)
|
||||
{
|
||||
taskToUpdate.StartedAt = taskToUpdate.CreatedAt;
|
||||
}
|
||||
|
||||
if (status == TaskExecutionStatus.Completed || status == TaskExecutionStatus.Failed)
|
||||
{
|
||||
taskToUpdate.CompletedAt ??= DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
_logger.LogInformation(
|
||||
"任务状态更新 (FaceRecognitionProcessor): TaskId={TaskId}, Status={Status}, Progress={Progress}%",
|
||||
taskId, status, progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("尝试在 FaceRecognitionProcessor 中更新不存在的任务状态: TaskId={TaskId}", taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -616,6 +616,14 @@ public class PictureService(
|
||||
UserIdForPicture = picture.UserId
|
||||
};
|
||||
await backgroundTaskQueue.QueueVisualRecognitionTaskAsync(visualRecognitionPayload);
|
||||
|
||||
// 添加人脸识别任务
|
||||
var faceRecognitionPayload = new Background.Processors.FaceRecognitionPayload
|
||||
{
|
||||
PictureId = picture.Id,
|
||||
UserIdForPicture = picture.UserId
|
||||
};
|
||||
await backgroundTaskQueue.QueueFaceRecognitionTaskAsync(faceRecognitionPayload);
|
||||
}
|
||||
|
||||
var pictureResponse = mappingService.MapPictureToResponse(picture);
|
||||
|
||||
@@ -188,6 +188,29 @@ const ConfigTabs: React.FC<ConfigTabsProps> = ({
|
||||
</div>
|
||||
</ConfigSection>
|
||||
</TabPane>
|
||||
<TabPane tab="人脸识别" key="facerecognition">
|
||||
<ConfigSection
|
||||
title="人脸识别服务配置"
|
||||
icon={<ApiOutlined />}
|
||||
description="配置人脸识别API服务的基本参数"
|
||||
isMobile={isMobile}
|
||||
>
|
||||
<Form form={formsMap.FaceRecognition} layout="vertical" size={isMobile ? "middle" : "large"}>
|
||||
{renderConfigFormItems(formsMap.FaceRecognition, "FaceRecognition", ['ApiEndpoint', 'ApiKey'])}
|
||||
<Divider style={{ margin: '12px 0 20px' }} />
|
||||
<Form.Item style={{ marginBottom: 0, textAlign: 'center' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={() => onSaveAllForGroup(formsMap.FaceRecognition, "FaceRecognition", ['ApiEndpoint', 'ApiKey'])}
|
||||
style={{ width: isMobile ? '100%' : '240px' }}
|
||||
>
|
||||
保存所有人脸识别配置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigSection>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -28,6 +28,10 @@ const allDescriptions: Record<string, Record<string, string>> = {
|
||||
TagGenerationPrompt: '用于从图片内容生成标签的提示词。请确保提示词包含返回JSON格式的指示,并且要求返回tags数组字段。',
|
||||
TagMatchingPrompt: '用于将描述内容与已有标签进行匹配的提示词。请确保提示词包含{\'{tagsText}\'}和{\'{description}\'}占位符,系统将会用实际的标签列表和描述内容替换这些占位符。'
|
||||
},
|
||||
FaceRecognition: {
|
||||
ApiEndpoint: '人脸识别服务的API端点地址',
|
||||
ApiKey: '人脸识别服务的API密钥'
|
||||
},
|
||||
Jwt: {
|
||||
SecretKey: 'JWT 加密密钥',
|
||||
Issuer: 'JWT 签发者',
|
||||
@@ -75,9 +79,11 @@ const System: React.FC = () => {
|
||||
const [authForm] = Form.useForm();
|
||||
const [appSettingsForm] = Form.useForm();
|
||||
const [uploadForm] = Form.useForm();
|
||||
const [faceRecognitionForm] = Form.useForm();
|
||||
|
||||
const formsMap: Record<string, any> = {
|
||||
AI: aiForm,
|
||||
FaceRecognition: faceRecognitionForm,
|
||||
Jwt: jwtForm,
|
||||
Authentication: authForm,
|
||||
AppSettings: appSettingsForm,
|
||||
|
||||
@@ -13,7 +13,8 @@ const { Title, Text } = Typography;
|
||||
// 定义任务类型映射
|
||||
const taskTypeDisplayMapping: { [key: number]: string } = {
|
||||
0: '图片处理',
|
||||
1: '视觉识别', // 新增视觉识别任务类型
|
||||
1: '视觉识别',
|
||||
2: '人脸识别', // 新增人脸识别任务类型
|
||||
};
|
||||
|
||||
const BackgroundTasks: React.FC = () => {
|
||||
@@ -127,7 +128,7 @@ const BackgroundTasks: React.FC = () => {
|
||||
dataIndex: 'taskName', // Changed dataIndex
|
||||
key: 'taskName',
|
||||
render: (text: string, record: TaskDetailsViewModel) => ( // Updated type and logic
|
||||
(record.taskType === 0 || record.taskType === 1) && record.relatedEntityId // 检查是否为图片处理或视觉识别任务
|
||||
(record.taskType === 0 || record.taskType === 1 || record.taskType === 2) && record.relatedEntityId // 检查是否为图片处理、视觉识别或人脸识别任务
|
||||
? <Link to={`/pictures/${record.relatedEntityId}`}>{text}</Link>
|
||||
: text
|
||||
),
|
||||
@@ -155,6 +156,7 @@ const BackgroundTasks: React.FC = () => {
|
||||
filters: [ // 可以为任务类型添加筛选器
|
||||
{ text: '图片处理', value: 0 },
|
||||
{ text: '视觉识别', value: 1 },
|
||||
{ text: '人脸识别', value: 2 }, // 新增人脸识别筛选器
|
||||
],
|
||||
onFilter: (value, record: TaskDetailsViewModel) => record.taskType === (value as number),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user