317 lines
8.8 KiB
C#
317 lines
8.8 KiB
C#
|
||
//--------------------------------------------------------------------
|
||
// 此代码由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;
|
||
}
|
||
}
|
||
} |