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 @@
保存候选人固定基础信息。
+
+
+ 校验合同变量并提前生成候选人的合同文件。
+
+
- 下载根据当前任务资料动态生成并已替换变量的合同文件。
+ 下载已经准备完成并替换变量的合同文件。