添加项目文件。

This commit is contained in:
2026-05-09 17:06:30 +08:00
parent c60f5fe117
commit 720ef957d4
378 changed files with 14843 additions and 0 deletions
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="16.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FileService.Domain\FileService.Domain.csproj" />
<ProjectReference Include="..\IM.Commons\IM.Commons.csproj" />
<ProjectReference Include="..\IM.InitCommon\IM.InitCommon.csproj" />
</ItemGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
using FileService.Application.UploadFile;
using IM.Commons;
using Microsoft.Extensions.DependencyInjection;
namespace FileService.Application
{
public class ModueInit : IModuleInitializer
{
public void Initialize(IServiceCollection services)
{
services.AddScoped<UploadFileService>();
}
}
}
@@ -0,0 +1,12 @@
using FileService.Application.StorageContracts;
namespace FileService.Application.Ports
{
public interface IObjectStoragePort
{
string ProviderCode { get; }
public Task<InitiateUploadResult> InitUploadAsync(InitiateUploadCommand command,CancellationToken token);
public Task<PresignedUrl> GenerateUploadUrlAsync(GenerateUploadUrlCommand command, CancellationToken token);
public Task<CompleteUploadResult> CompleteUploadAsync(CompleteUploadCommand command, CancellationToken token);
}
}
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.Ports
{
public interface IObjectStorageRouter
{
IObjectStoragePort Route(string providerCode);
}
}
@@ -0,0 +1,12 @@
using FileService.Application.StorageContracts;
namespace FileService.Application.Ports
{
public interface IStorageRedisCache
{
Task SetAsync(UploadRuntimeCache upload);
Task DeleteAsync(string sessionId);
Task DeleteByTaskIdAsync(string taskId);
Task<UploadRuntimeCache?> GetAsync(string sessionId);
}
}
@@ -0,0 +1,69 @@
using FileService.Domain.ValueObjects;
namespace FileService.Application.StorageContracts
{
/// <summary>
///
/// </summary>
/// <param name="ProviderCode">储存提供商编号</param>
/// <param name="Bucket">储存桶名称</param>
/// <param name="ObjectKey">鉴权key</param>
/// <param name="ContentType"></param>
/// <param name="ContentLength"></param>
/// <param name="Metadata"></param>
public sealed record InitiateUploadCommand(
string ProviderCode,
string Bucket,
string ObjectKey,
string ContentType,
long ContentLength,
IReadOnlyDictionary<string, string>? Metadata = null);
public sealed record InitiateUploadResult(
string UploadSessionId,
StorageLocation Location);
/// <summary>
///
/// </summary>
/// <param name="ProviderCode"></param>
/// <param name="Bucket"></param>
/// <param name="ObjectKey"></param>
/// <param name="UploadSessionId"></param>
/// <param name="PartNumber"></param>
/// <param name="ExpiresIn"></param>
public sealed record GenerateUploadUrlCommand(
string ProviderCode,
string Bucket,
string ObjectKey,
string UploadSessionId,
int? PartNumber,
TimeSpan ExpiresIn);
public sealed record PresignedUrl(
string Url,
IReadOnlyDictionary<string, string> Headers,
DateTimeOffset ExpiresAt);
public sealed record CompleteUploadCommand(
string ProviderCode,
string Bucket,
string Region,
string ObjectKey,
string UploadSessionId,
IReadOnlyList<UploadPart> Parts);
public sealed record UploadPart(
int PartNumber,
string ETag,
long? Size = null,
string? Checksum = null);
public sealed record CompleteUploadResult(
StorageLocation Location,
string? ETag,
long Size,
string? Checksum = null,
string? VersionId = null);
}
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace FileService.Application.StorageContracts
{
public sealed class UploadRuntimeCache
{
public string TaskId { get; init; } = default!;
public string ProviderCode { get; init; } = default!;
public string UploadSessionId { get; init; } = default!;
public string Bucket { get; init; } = default!;
public string Region { get; init; } = default!;
public string ObjectKey { get; init; } = default!;
public long FileSize { get; init; }
public int TotalPartCount { get; init; }
public long UploadedBytes { get; private set; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset ExpireAt { get; init; }
public Dictionary<int, UploadPart> Parts { get; init; } = new();
public void AddOrUpdatePart(UploadPart part)
{
Parts[part.PartNumber] = part;
UploadedBytes = Parts.Sum(x => x.Value.Size ?? 0);
}
public bool IsCompleted()
{
return Parts.Count == TotalPartCount;
}
public string ToJson()
{
return JsonSerializer.Serialize(this);
}
public static UploadRuntimeCache? FromJson(string json)
{
return JsonSerializer.Deserialize<UploadRuntimeCache>(json);
}
}
}
@@ -0,0 +1,24 @@
using FileService.Domain;
using FileService.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.UploadFile
{
public class FileResponse
{
public Guid Id { get; set; }
public Guid OwnerId { get; set; }
public FileName FileName { get; set; }
public long FileSize { get; set; }
public ContentType ContentType { get; set; }
public FileState State { get; set; }
public StorageLocation StorageLocation { get; set; }
public CheckSum CheckSum { get; set; }
public DateTimeOffset Created { get; set; }
public DateTimeOffset Updated { get; set;
}
}
@@ -0,0 +1,20 @@
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.UploadFile
{
public class UploadFileMapperConfig:Profile
{
public UploadFileMapperConfig()
{
CreateMap<Domain.Entities.UploadFile, FileResponse>()
.ForMember(dest => dest.Created, opt => opt.MapFrom(src => src.CreationTime))
.ForMember(dest => dest.Updated, opt => opt.MapFrom(src => src.ModificationTime))
;
}
}
}
@@ -0,0 +1,29 @@
using AutoMapper;
using FileService.Domain.IReposities;
using IM.Commons;
namespace FileService.Application.UploadFile
{
public class UploadFileService
{
private readonly IUploadFileReposity reposity;
private readonly IMapper mapper;
public UploadFileService(IUploadFileReposity reposity, IMapper mapper)
{
this.reposity = reposity;
this.mapper = mapper;
}
public async Task<Result<FileResponse>> GetFileInfoAsync(Guid id)
{
var file = await reposity.FindByIdAsync(id);
if (file == null)
{
return Result.Fail<FileResponse>(ResultCode.FILE_NOT_FOUND);
}
return Result.Success(mapper.Map<FileResponse>(file));
}
}
}
@@ -0,0 +1,17 @@
using FileService.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.UploadFileTask
{
public class TaskInitResponse
{
public Guid TaskId { get; set; }
public string UploadSessionId { get; init; }
public StorageLocation StorageLocation { get; init; }
}
}
@@ -0,0 +1,53 @@
using AutoMapper;
using FileService.Application.Ports;
using FileService.Domain.Entities;
using FileService.Domain.IReposities;
using IM.Commons;
using IM.InitCommon;
using MassTransit.Internals;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.UploadFileTask
{
public class UploadFileTaskService
{
private readonly IUploadTaskReposity reposity;
private readonly IMapper mapper;
private readonly IObjectStorageRouter router;
private readonly IOptions<StorageOptions> options;
public UploadFileTaskService(IUploadTaskReposity reposity, IMapper mapper, IObjectStorageRouter router, IOptions<StorageOptions> options)
{
this.reposity = reposity;
this.mapper = mapper;
this.router = router;
this.options = options;
}
public async Task<Result<TaskInitResponse>> InitTaskAsync(UploadTaskInitCommand command)
{
CancellationToken cancellationToken = CancellationToken.None;
var task = command.ToUploadTask();
var storage = router.Route(options.Value.ProviderCode);
var initRes = await storage.InitUploadAsync(new StorageContracts.InitiateUploadCommand(
ProviderCode: options.Value.ProviderCode,
Bucket: options.Value.Bucket,
ObjectKey: options.Value.Endpoint,
ContentType:task.ContentType.Value,
ContentLength: command.FileSize,
null
), cancellationToken);
var res = mapper.Map<TaskInitResponse>(initRes);
res.TaskId = task.Id;
return Result.Success(res);
}
}
}
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.UploadFileTask
{
public record UploadTaskInitCommand(
Guid UploaderId,
Guid ConversationId, string FileName,
long FileSize,string contentType,
string checkSum
)
{
public Domain.Entities.UploadTask ToUploadTask()
{
return new Domain.Entities.UploadTask(
UploaderId,
ConversationId, FileName, FileSize, contentType,
null,new Domain.ValueObjects.CheckSum("md5", checkSum)
);
}
}
}
@@ -0,0 +1,21 @@
using AutoMapper;
using FileService.Application.StorageContracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FileService.Application.UploadFileTask
{
public class UploadTaskMapperConfig:Profile
{
public UploadTaskMapperConfig()
{
CreateMap<InitiateUploadResult, TaskInitResponse>()
.ForMember(dest => dest.StorageLocation, opt => opt.MapFrom(src => src.Location))
.ForMember(dest => dest.UploadSessionId, opt => opt.MapFrom(src => src.UploadSessionId))
;
}
}
}