irc-netcore-api/IRC.Core.SCP/Service/FileUploadRecordService.cs

317 lines
8.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2026-03-10 06:15:17Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using AutoMapper;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.SCP;
using IRaCIS.Core.SCP.Service;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System.Drawing;
using System.Threading.Channels;
using System.Threading.Tasks;
using ZiggyCreatures.Caching.Fusion;
namespace IRaCIS.Core.SCP.Service;
public class FileUploadRecordAddOrEdit
{
public Guid? Id { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public string FileType { get; set; }
public string Path { get; set; }
public string UploadBatchId { get; set; }
public BatchDataType BatchDataType { get; set; }
public string StudyCode { get; set; }
public Guid? TrialId { get; set; }
public Guid? SubjectId { get; set; }
public Guid? SubjectVisitId { get; set; }
public Guid? DicomStudyId { get; set; }
public Guid? NoneDicomStudyId { get; set; }
public string FileMarkId { get; set; }
public int? Priority { get; set; }
public string IP { get; set; }
public bool? IsNeedSync { get; set; }
public string UploadRegion { get; set; }
public string TargetRegion { get; set; }
}
public interface IFileUploadRecordService
{
Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord);
}
[ApiExplorerSettings(GroupName = "Common")]
public class FileUploadRecordService(IRepository<FileUploadRecord> _fileUploadRecordRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<ObjectStoreServiceOptions> options,
IFusionCache _fusionCache, IRepository<Trial> _trialRepository, FileSyncQueue _fileSyncQueue) : BaseService, IFileUploadRecordService
{
ObjectStoreServiceOptions ObjectStoreServiceConfig = options.CurrentValue;
public async Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord)
{
addOrEditFileUploadRecord.IP = _userInfo.IP;
if (ObjectStoreServiceConfig.IsOpenStoreSync && _userInfo.Domain.IsNotNullOrEmpty())
{
var find = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.Domain == _userInfo.Domain);
if (find != null)
{
addOrEditFileUploadRecord.UploadRegion = find.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = find.TargetRegion;
}
else
{
//前后端调试的时候,上传的时候域名不对应,自动按照后端配置设置上传区域和同步区域
var apiDefalut = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.UploadRegion == ObjectStoreServiceConfig.ApiDeployRegion);
if (apiDefalut != null)
{
addOrEditFileUploadRecord.UploadRegion = apiDefalut.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = apiDefalut.TargetRegion;
}
}
}
if (addOrEditFileUploadRecord.TrialId != null)
{
var trialDataStore = await _fusionCache.GetOrSetAsync(CacheKeys.TrialDataStoreType(addOrEditFileUploadRecord.TrialId.Value), async _ =>
{
return await _trialRepository.Where(t => t.Id == addOrEditFileUploadRecord.TrialId).Select(t => t.TrialDataStoreType)
.FirstOrDefaultAsync();
},
TimeSpan.FromDays(7)
);
//项目配置了,那么就设置需要同步
if (trialDataStore == TrialDataStore.MUtiCenter)
{
addOrEditFileUploadRecord.IsNeedSync = true;
addOrEditFileUploadRecord.Priority = 0;
}
else
{
addOrEditFileUploadRecord.IsNeedSync = false;
//addOrEditFileUploadRecord.TargetRegion = "";
}
}
else
{
//系统文件,默认同步
addOrEditFileUploadRecord.IsNeedSync = true;
addOrEditFileUploadRecord.Priority = 0;
}
var entity = await _fileUploadRecordRepository.InsertOrUpdateAsync(addOrEditFileUploadRecord, true);
if (addOrEditFileUploadRecord.IsNeedSync == true)
{
_fileSyncQueue.Enqueue(entity.Id, addOrEditFileUploadRecord.Priority ?? 0);
}
return ResponseOutput.Ok(entity.Id.ToString());
}
}
public sealed class FileSyncQueue
{
/// <summary>
/// 优先级队列(仅负责排序)
/// </summary>
private readonly PriorityQueue<Guid, int> _queue = new();
/// <summary>
/// 当前等待中的任务(唯一真实数据)
/// key = Guid
/// value = 最新 priority
/// </summary>
private readonly Dictionary<Guid, int> _waiting = new();
/// <summary>
/// 正在执行的任务(防止重复执行)
/// </summary>
private readonly HashSet<Guid> _running = new();
/// <summary>
/// worker 等待信号
/// </summary>
private readonly SemaphoreSlim _signal = new(0);
private readonly object _lock = new();
// ============================================================
// Enqueue
// ============================================================
/// <summary>
/// 入队(同 Guid 会覆盖优先级)
/// </summary>
public void Enqueue(Guid id, int priority)
{
bool needSignal = false;
lock (_lock)
{
// 如果正在执行,忽略(防止重复)
if (_running.Contains(id))
return;
// 是否新任务(用于减少 signal 风暴)
if (!_waiting.ContainsKey(id))
needSignal = true;
// 更新为最新优先级(最后一次为准)
_waiting[id] = priority; //等价于添加或者更新
// PriorityQueue 无法更新节点
// 允许旧节点存在Dequeue 时过滤
_queue.Enqueue(id, -priority);
}
// 只有新增任务才唤醒 worker
if (needSignal)
_signal.Release();
}
// ============================================================
// Dequeue
// ============================================================
/// <summary>
/// 获取一个待执行任务(无任务时自动等待)
/// </summary>
public async Task<Guid> DequeueAsync(CancellationToken ct)
{
while (true)
{
await _signal.WaitAsync(ct);
lock (_lock)
{
while (_queue.Count > 0)
{
var id = _queue.Dequeue();
// 已被覆盖或取消
if (!_waiting.TryGetValue(id, out _))
continue;
// 标记为运行中
_waiting.Remove(id);
_running.Add(id);
return id;
}
}
}
}
// ============================================================
// Complete
// ============================================================
/// <summary>
/// 任务执行完成(必须调用)
/// </summary>
public void Complete(Guid id)
{
lock (_lock)
{
_running.Remove(id);
}
}
// ============================================================
// Snapshot
// ============================================================
/// <summary>
/// 当前等待中的任务快照
/// </summary>
public Guid[] Snapshot()
{
lock (_lock)
{
return _waiting.Keys.ToArray();
}
}
// ============================================================
// 状态信息(调试用)
// ============================================================
public int WaitingCount
{
get
{
lock (_lock)
return _waiting.Count;
}
}
public int RunningCount
{
get
{
lock (_lock)
return _running.Count;
}
}
}