From 9741481a625cf37d251786bf25d45a5a00154562 Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Wed, 26 Aug 2026 18:17:58 +0800 Subject: [PATCH] 111 --- Directory.Build.props | 5 +- .../OnboardingContractDocumentService.cs | 8 +-- .../IOnboardingPublicTaskService.cs | 8 ++- .../PublicTask/OnboardingPublicTaskService.cs | 52 +++++++++++++++++-- .../Task/Dto/OnboardingTaskPublicInput.cs | 21 ++++++++ .../SimpleAdmin.Application.xml | 14 ++++- .../OnboardingPublicTaskController.cs | 16 +++++- SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml | 7 ++- 8 files changed, 117 insertions(+), 14 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 527e357..9e0af59 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,6 +8,7 @@ - $(MSBuildProjectName).xml + + true - \ No newline at end of file + diff --git a/SimpleAdmin.Application/Services/Onboarding/ContractDocument/OnboardingContractDocumentService.cs b/SimpleAdmin.Application/Services/Onboarding/ContractDocument/OnboardingContractDocumentService.cs index bea8ef2..4adc6a1 100644 --- a/SimpleAdmin.Application/Services/Onboarding/ContractDocument/OnboardingContractDocumentService.cs +++ b/SimpleAdmin.Application/Services/Onboarding/ContractDocument/OnboardingContractDocumentService.cs @@ -61,18 +61,20 @@ public class OnboardingContractDocumentService : IOnboardingContractDocumentServ item => item.VariableCode, item => item.VariableValue ?? string.Empty, StringComparer.OrdinalIgnoreCase)); - var name = $"{task.TaskNo}_{contract.ContractName}.docx"; + var generatedAt = DateTime.Now; + // 在文件名中加入生成批次,避免手机系统把同名的新合同误认为历史下载文件。 + var name = $"{task.TaskNo}_{contract.ContractName}_{generatedAt:yyyyMMddHHmmssfff}.docx"; var fileId = await _fileService.StoreGeneratedFile( SysDictConst.FILE_ENGINE_ALIYUN_OSS, name, rendered); contract.GeneratedDocxFileId = fileId; - contract.GeneratedAt = DateTime.Now; + contract.GeneratedAt = generatedAt; contract.ContractStatus = task.TaskStatus == OnboardingTaskStatus.Approved ? TaskContractStatus.OfflinePendingSign : TaskContractStatus.PendingConfirm; await _db.Updateable(contract).ExecuteCommandAsync(); return fileId; } -} \ No newline at end of file +} diff --git a/SimpleAdmin.Application/Services/Onboarding/PublicTask/IOnboardingPublicTaskService.cs b/SimpleAdmin.Application/Services/Onboarding/PublicTask/IOnboardingPublicTaskService.cs index 0c5dbaa..18e000f 100644 --- a/SimpleAdmin.Application/Services/Onboarding/PublicTask/IOnboardingPublicTaskService.cs +++ b/SimpleAdmin.Application/Services/Onboarding/PublicTask/IOnboardingPublicTaskService.cs @@ -14,7 +14,13 @@ public interface IOnboardingPublicTaskService : ITransient /// Task SaveEmployeeInfo(OnboardingTaskPublicSaveInput input); /// - /// 校验任务归属后返回合同模板源文件 Id。 + /// 校验合同变量并提前生成候选人的合同 DOCX。 + /// + Task PrepareContract( + OnboardingPublicContractSourceInput input); + + /// + /// 校验任务归属后返回已经准备完成的合同文件 Id。 /// Task GetContractSourceFileId(OnboardingPublicContractSourceInput input); diff --git a/SimpleAdmin.Application/Services/Onboarding/PublicTask/OnboardingPublicTaskService.cs b/SimpleAdmin.Application/Services/Onboarding/PublicTask/OnboardingPublicTaskService.cs index ed59ffd..60b94ff 100644 --- a/SimpleAdmin.Application/Services/Onboarding/PublicTask/OnboardingPublicTaskService.cs +++ b/SimpleAdmin.Application/Services/Onboarding/PublicTask/OnboardingPublicTaskService.cs @@ -162,25 +162,71 @@ public class OnboardingPublicTaskService : IOnboardingPublicTaskService } /// - /// 根据员工当前已填写信息生成合同预览,并返回已替换变量的 DOCX 文件 Id。 + /// 根据员工当前已填写信息校验变量并提前生成合同 DOCX。 /// - public async Task GetContractSourceFileId(OnboardingPublicContractSourceInput input) + public async Task PrepareContract( + OnboardingPublicContractSourceInput input) { var link = await GetActiveLink(input.Token); var contract = await _db.Queryable() .FirstAsync(item => item.Id == input.TaskContractId && item.TaskId == link.TaskId) ?? throw Oops.Bah("合同不存在"); + // 员工资料保存时会清除旧文件 Id;仍有文件 Id 时还需确认文件记录没有被异常删除。 + if (contract.GeneratedDocxFileId.HasValue) + { + var generatedFileExists = await _db.Queryable() + .AnyAsync(item => item.Id == contract.GeneratedDocxFileId.Value); + if (!generatedFileExists) + throw Oops.Bah("已生成的合同文件不存在,请联系管理员重新生成"); + + return new OnboardingPublicContractPrepareOutput + { + FileId = contract.GeneratedDocxFileId.Value, + GeneratedAt = contract.GeneratedAt + }; + } + await _snapshotService.Build(link.TaskId); var fileId = await _contractDocumentService.Generate(new OnboardingGenerateContractInput { TaskContractId = contract.Id }); + var generatedAt = await _db.Queryable() + .Where(item => item.Id == contract.Id) + .Select(item => item.GeneratedAt) + .SingleAsync(); + return new OnboardingPublicContractPrepareOutput + { + FileId = fileId, + GeneratedAt = generatedAt + }; + } + + /// + /// 返回已经准备完成的合同文件,下载阶段不再执行变量校验和文档生成。 + /// + public async Task GetContractSourceFileId(OnboardingPublicContractSourceInput input) + { + var link = await GetActiveLink(input.Token); + var contract = await _db.Queryable() + .FirstAsync(item => item.Id == input.TaskContractId && item.TaskId == link.TaskId) + ?? throw Oops.Bah("合同不存在"); + var fileId = contract.GeneratedDocxFileId + ?? throw Oops.Bah("合同尚未准备完成,请刷新页面后重试"); + var fileExists = await _db.Queryable() + .AnyAsync(item => item.Id == fileId); + if (!fileExists) + throw Oops.Bah("合同文件不存在,请重新准备合同"); + contract.ReadAt ??= DateTime.Now; if (contract.ContractStatus == TaskContractStatus.PendingGenerate) contract.ContractStatus = TaskContractStatus.PendingConfirm; - await _db.Updateable(contract).ExecuteCommandAsync(); + + await _db.Updateable(contract) + .UpdateColumns(item => new { item.ReadAt, item.ContractStatus }) + .ExecuteCommandAsync(); return fileId; } diff --git a/SimpleAdmin.Application/Services/Onboarding/Task/Dto/OnboardingTaskPublicInput.cs b/SimpleAdmin.Application/Services/Onboarding/Task/Dto/OnboardingTaskPublicInput.cs index f4475de..7456768 100644 --- a/SimpleAdmin.Application/Services/Onboarding/Task/Dto/OnboardingTaskPublicInput.cs +++ b/SimpleAdmin.Application/Services/Onboarding/Task/Dto/OnboardingTaskPublicInput.cs @@ -28,6 +28,27 @@ public class OnboardingPublicContractSourceInput /// [IdNotNull(ErrorMessage = "任务合同Id不能为空")] public long TaskContractId { get; set; } + + /// + /// 当前生成文件的版本标识,仅用于区分手机下载缓存,不参与文件权限判断。 + /// + public long? Version { get; set; } +} + +/// +/// 员工端合同准备结果。 +/// +public class OnboardingPublicContractPrepareOutput +{ + /// + /// 当前已经生成并可下载的系统文件 Id,同时作为客户端下载版本号。 + /// + public long FileId { get; set; } + + /// + /// 合同文件生成时间;复用现有文件时返回原生成时间。 + /// + public DateTime? GeneratedAt { get; set; } } /// diff --git a/SimpleAdmin.Application/SimpleAdmin.Application.xml b/SimpleAdmin.Application/SimpleAdmin.Application.xml index bb56b7f..8619ae1 100644 --- a/SimpleAdmin.Application/SimpleAdmin.Application.xml +++ b/SimpleAdmin.Application/SimpleAdmin.Application.xml @@ -3182,9 +3182,14 @@ 保存候选人固定基础信息并刷新合同变量快照。 + + + 校验合同变量并提前生成候选人的合同 DOCX。 + + - 校验任务归属后返回合同模板源文件 Id。 + 校验任务归属后返回已经准备完成的合同文件 Id。 @@ -3207,9 +3212,14 @@ 保存候选人的固定基础信息并刷新尚未确认的合同变量快照。 + + + 根据员工当前已填写信息校验变量并提前生成合同 DOCX。 + + - 根据员工当前已填写信息生成合同预览,并返回已替换变量的 DOCX 文件 Id。 + 返回已经准备完成的合同文件,下载阶段不再执行变量校验和文档生成。 diff --git a/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicTaskController.cs b/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicTaskController.cs index e63ba17..0f8feb1 100644 --- a/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicTaskController.cs +++ b/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicTaskController.cs @@ -36,13 +36,25 @@ public class OnboardingPublicTaskController } /// - /// 下载根据当前任务资料动态生成并已替换变量的合同文件。 + /// 校验合同变量并提前生成候选人的合同文件。 + /// + [HttpPost("prepare-contract")] + public async Task PrepareContract( + [FromBody] OnboardingPublicContractSourceInput input) + { + return await _service.PrepareContract(input); + } + + /// + /// 下载已经准备完成并替换变量的合同文件。 /// [HttpGet("contract-source")] + [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public async Task ContractSource([FromQuery] OnboardingPublicContractSourceInput input) { var fileId = await _service.GetContractSourceFileId(input); - return await _fileService.Download(new BaseIdInput { Id = fileId }); + return await _fileService.Download(new BaseIdInput { Id = fileId }) + ?? throw Oops.Bah("合同文件不存在,请重新准备合同"); } /// diff --git a/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml b/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml index 123c174..0ded708 100644 --- a/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml +++ b/SimpleAdmin.Web.Core/SimpleAdmin.Web.Core.xml @@ -296,9 +296,14 @@ 保存候选人固定基础信息。 + + + 校验合同变量并提前生成候选人的合同文件。 + + - 下载根据当前任务资料动态生成并已替换变量的合同文件。 + 下载已经准备完成并替换变量的合同文件。