Files
OnboardXAdmin/SimpleAdmin.Application/Services/Onboarding/ContractDocument/OnboardingContractDocumentService.cs
T
2026-08-27 11:09:20 +08:00

90 lines
4.3 KiB
C#

namespace SimpleAdmin.Application;
/// <summary>
/// 实现入职合同预览和归档 DOCX 文件生成能力。
/// </summary>
public class OnboardingContractDocumentService : IOnboardingContractDocumentService
{
private readonly SqlSugarScope _db;
private readonly IFileService _fileService;
public OnboardingContractDocumentService(IFileService fileService)
{
_db = DbContext.DB;
_fileService = fileService;
}
/// <summary>
/// 根据锁定的模板版本和任务变量快照生成已替换变量的合同 DOCX。
/// </summary>
public async Task<long> Generate(OnboardingGenerateContractInput input)
{
var contract = await _db.Queryable<ObTaskContract>().InSingleAsync(input.TaskContractId)
?? throw Oops.Bah("任务合同不存在");
var task = await _db.Queryable<ObOnboardingTask>().InSingleAsync(contract.TaskId)
?? throw Oops.Bah("入职任务不存在");
var statusAllowsGeneration = task.TaskStatus == OnboardingTaskStatus.PendingFill
|| task.TaskStatus == OnboardingTaskStatus.Rejected
|| task.TaskStatus == OnboardingTaskStatus.Reviewing
|| task.TaskStatus == OnboardingTaskStatus.Approved;
if (!statusAllowsGeneration)
throw Oops.Bah("当前任务状态不能生成合同");
if (contract.GeneratedDocxFileId.HasValue)
return contract.GeneratedDocxFileId.Value;
var version = await _db.Queryable<ObContractTemplateVersion>().InSingleAsync(contract.TemplateVersionId)
?? throw Oops.Bah("合同版本不存在");
var source = await _db.Queryable<SysFile>().InSingleAsync(version.SourceDocxFileId)
?? throw Oops.Bah("合同源文件不存在");
var definitions = await _db.Queryable<ObTemplateVariable>()
.Where(item => item.TemplateVersionId == version.Id)
.ToListAsync();
var variables = await _db.Queryable<ObTaskContractVariable>()
.Where(item => item.TaskContractId == contract.Id)
.ToListAsync();
var duplicateVariable = variables
.GroupBy(item => item.VariableCode, StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(group => group.Count() > 1);
if (duplicateVariable != null)
{
throw Oops.Bah(
$"合同变量快照“{duplicateVariable.Key}”存在重复数据,请联系管理员修复后重试");
}
var missing = definitions
.Where(item => item.Required)
.FirstOrDefault(definition => string.IsNullOrWhiteSpace(
variables.FirstOrDefault(variable => variable.VariableCode == definition.VariableCode)?.VariableValue));
if (missing != null)
throw Oops.Bah($"合同变量“{missing.VariableName}”尚未填写,暂时不能预览合同");
var sourceResult = await _fileService.Download(new BaseIdInput { Id = source.Id })
?? throw Oops.Bah("无法读取合同源文件");
await using var sourceStream = sourceResult.FileStream;
using var sourceBytes = new MemoryStream();
await sourceStream.CopyToAsync(sourceBytes);
var rendered = DocxTemplateProcessor.Render(
sourceBytes.ToArray(),
variables.ToDictionary(
item => item.VariableCode,
item => item.VariableValue ?? string.Empty,
StringComparer.OrdinalIgnoreCase));
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 = generatedAt;
contract.ContractStatus = task.TaskStatus == OnboardingTaskStatus.Approved
? TaskContractStatus.OfflinePendingSign
: TaskContractStatus.PendingConfirm;
await _db.Updateable(contract).ExecuteCommandAsync();
return fileId;
}
}