diff --git a/FileService.Application/StorageContracts/UploadRuntimeCache.cs b/FileService.Application/StorageContracts/UploadRuntimeCache.cs index fcd398b..db55f0a 100644 --- a/FileService.Application/StorageContracts/UploadRuntimeCache.cs +++ b/FileService.Application/StorageContracts/UploadRuntimeCache.cs @@ -33,6 +33,23 @@ namespace FileService.Application.StorageContracts public Dictionary Parts { get; init; } = new(); + public UploadRuntimeCache(string taskId, string providerCode, + string uploadSessionId, string bucket, string region, + string objectKey, long fileSize, int totalPartCount, + DateTimeOffset? expireAt = null) + { + TaskId = taskId; + ProviderCode = providerCode; + UploadSessionId = uploadSessionId; + Bucket = bucket; + Region = region; + ObjectKey = objectKey; + FileSize = fileSize; + TotalPartCount = totalPartCount; + ExpireAt = expireAt ?? DateTime.MaxValue; + CreatedAt = DateTime.Now; + } + public void AddOrUpdatePart(UploadPart part) { Parts[part.PartNumber] = part; diff --git a/FileService.Application/UploadFileTask/UploadFileTaskService.cs b/FileService.Application/UploadFileTask/UploadFileTaskService.cs index d78a11d..2991bf2 100644 --- a/FileService.Application/UploadFileTask/UploadFileTaskService.cs +++ b/FileService.Application/UploadFileTask/UploadFileTaskService.cs @@ -14,26 +14,23 @@ using System.Threading.Tasks; namespace FileService.Application.UploadFileTask { - public class UploadFileTaskService + public class UploadFileTaskService(IUploadTaskReposity reposity, + IMapper mapper, IObjectStorageRouter router, + IOptions options, IStorageRedisCache redis) { - private readonly IUploadTaskReposity reposity; - private readonly IMapper mapper; - private readonly IObjectStorageRouter router; - private readonly IOptions options; - - public UploadFileTaskService(IUploadTaskReposity reposity, IMapper mapper, IObjectStorageRouter router, IOptions options) - { - this.reposity = reposity; - this.mapper = mapper; - this.router = router; - this.options = options; - } + private readonly IUploadTaskReposity reposity = reposity; + private readonly IMapper mapper = mapper; + private readonly IObjectStorageRouter router = router; + private readonly IOptions options = options; + private readonly IStorageRedisCache redis = redis; public async Task> InitTaskAsync(UploadTaskInitCommand command) { CancellationToken cancellationToken = CancellationToken.None; var task = command.ToUploadTask(); - var storage = router.Route(options.Value.ProviderCode); + + var storageOption = options.Value.Providers[options.Value.DefaultProviderCode]; + var storage = router.Route(storageOption.ProviderCode); var initRes = await storage.InitUploadAsync(new StorageContracts.InitiateUploadCommand( ProviderCode: options.Value.ProviderCode, Bucket: options.Value.Bucket, @@ -45,6 +42,20 @@ namespace FileService.Application.UploadFileTask var res = mapper.Map(initRes); res.TaskId = task.Id; + reposity.Create(task); + + await redis.SetAsync(new StorageContracts.UploadRuntimeCache( + taskId: task.Id.ToString(), + providerCode: storage.ProviderCode, + uploadSessionId: res.UploadSessionId, + bucket: storageOption.Bucket, + region: storageOption.Region, + objectKey: storageOption.Endpoint, + fileSize: task.FileSize, + totalPartCount: (int)(task.FileSize % storageOption.DefaultPartSizeBytes > 0 ? + (task.FileSize / storageOption.DefaultPartSizeBytes) + 1 : + task.FileSize / storageOption.DefaultPartSizeBytes) + )); return Result.Success(res); diff --git a/FileService.WebApi/Controllers/FileTask/FileTaskController.cs b/FileService.WebApi/Controllers/FileTask/FileTaskController.cs index b7635f3..a3e03c0 100644 --- a/FileService.WebApi/Controllers/FileTask/FileTaskController.cs +++ b/FileService.WebApi/Controllers/FileTask/FileTaskController.cs @@ -1,6 +1,10 @@ -using Microsoft.AspNetCore.Authorization; +using FileService.Application.UploadFileTask; +using FileService.Infrastructure; +using IM.ASPNETCore; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; namespace FileService.WebApi.Controllers.FileTask { @@ -9,11 +13,27 @@ namespace FileService.WebApi.Controllers.FileTask [ApiController] public class FileTaskController : ControllerBase { + private readonly UploadFileTaskService service; + + public FileTaskController(UploadFileTaskService service) + { + this.service = service; + } [HttpPost("init")] - public async Task Init() + [UnitOfWork(typeof(FileDbContext))] + public async Task Init(FileTaskInitRequest request) { - + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); + var res = await service.InitTaskAsync(new UploadTaskInitCommand( + UploaderId: Guid.Parse(userId), + ConversationId: request.ConversationId, + FileName: request.FileName, + FileSize: request.FileSize, + contentType: request.ContentType, + checkSum: request.CheckSum + )); + return Ok(res); } } } diff --git a/FileService.WebApi/Controllers/FileTask/FileTaskInitRequest.cs b/FileService.WebApi/Controllers/FileTask/FileTaskInitRequest.cs new file mode 100644 index 0000000..2918aff --- /dev/null +++ b/FileService.WebApi/Controllers/FileTask/FileTaskInitRequest.cs @@ -0,0 +1,18 @@ +using FluentValidation; + +namespace FileService.WebApi.Controllers.FileTask +{ + public class FileTaskInitRequest + { + public Guid ConversationId { get; set; } + public string FileName { get; set; } + public long FileSize { get; set; } + public string ContentType { get; set; } + public string CheckSum { get; set; } + + } + public class FileTaskInitRequestValidator: AbstractValidator + { + + } +} diff --git a/IM.InitCommon/StorageOptions.cs b/IM.InitCommon/StorageOptions.cs index 51d5617..7bbf3d9 100644 --- a/IM.InitCommon/StorageOptions.cs +++ b/IM.InitCommon/StorageOptions.cs @@ -7,6 +7,12 @@ using System.Threading.Tasks; namespace IM.InitCommon { public class StorageOptions + { + public string DefaultProviderCode { get; set; } + public Dictionary Providers { get; set; } + } + + public class StorageProviderOptions { public string ProviderCode { get; init; } = default!; @@ -37,6 +43,7 @@ namespace IM.InitCommon public long MaxObjectSizeBytes { get; init; } = 1024L * 1024 * 1024; public int MinPartSizeBytes { get; init; } = 5 * 1024 * 1024; + public int DefaultPartSizeBytes { get; init; } = 5 * 1024 * 1024; public int MaxPartCount { get; init; } = 10_000; } diff --git a/IM_API_NEW.sln b/IM_API_NEW.sln index 845d4a6..b99ab74 100644 --- a/IM_API_NEW.sln +++ b/IM_API_NEW.sln @@ -65,8 +65,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileService.WebApi", "FileS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileService.Application", "FileService.Application\FileService.Application.csproj", "{A05B43F3-3391-4ACC-A8BD-B9B7AEABC90B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MessageService (2)", "MessageService (2)", "{9FA3D6BD-1EC1-3BA5-80CB-CE02773A58D5}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/jenkinsfile b/jenkinsfile index 35f14f4..25cc7d3 100644 --- a/jenkinsfile +++ b/jenkinsfile @@ -1,15 +1,19 @@ pipeline { - agent { label '构建机1' } + agent any + + parameters { + booleanParam( + name: 'FORCE_BUILD_ALL', + defaultValue: false, + description: '强制构建所有镜像' + ) + } environment { IMAGE_PREFIX = "im" IMAGE_TAG = "${BUILD_NUMBER}" - DOCKER_BUILDKIT = "1" - - // 如果你有 Harbor,可以改成: - // REGISTRY = "harbor.xxx.com/im" - REGISTRY = "reg.nxsir.cn/im" + REGISTRY = "" } stages { @@ -22,21 +26,23 @@ pipeline { stage('检测变更') { steps { script { - def diffCmd = ''' - if git rev-parse HEAD~1 >/dev/null 2>&1; then - git diff --name-only HEAD~1 HEAD - else - git ls-files - fi - ''' + if (params.FORCE_BUILD_ALL || env.BUILD_NUMBER == '1') { + env.BUILD_ALL = "true" + env.CHANGED_FILES = "FIRST_BUILD" + echo "首次构建或手动强制构建,构建全部镜像" + } else { + env.BUILD_ALL = "false" - env.CHANGED_FILES = sh( - script: diffCmd, - returnStdout: true - ).trim() + env.CHANGED_FILES = sh( + script: ''' + git diff --name-only HEAD~1 HEAD + ''', + returnStdout: true + ).trim() - echo "变更文件:" - echo env.CHANGED_FILES + echo "变更文件:" + echo env.CHANGED_FILES + } } } } @@ -129,7 +135,7 @@ pipeline { def changedFiles = env.CHANGED_FILES.split("\\n") as List services.each { svc -> - def needBuild = changedFiles.any { file -> + def needBuild = env.BUILD_ALL == "true" || changedFiles.any { file -> svc.paths.any { path -> file.startsWith(path) } @@ -147,16 +153,6 @@ pipeline { -t ${latestName} \ -f ${svc.dockerfile} . """ - - if (env.REGISTRY?.trim()) { - sh """ - docker tag ${imageName} ${REGISTRY}/${svc.image}:${IMAGE_TAG} - docker tag ${latestName} ${REGISTRY}/${svc.image}:latest - - docker push ${REGISTRY}/${svc.image}:${IMAGE_TAG} - docker push ${REGISTRY}/${svc.image}:latest - """ - } } else { echo "跳过构建:${svc.name},没有相关代码变更" } @@ -165,14 +161,4 @@ pipeline { } } } - - post { - success { - echo "流水线执行成功" - } - - failure { - echo "流水线执行失败" - } - } } \ No newline at end of file