namespace SimpleAdmin.Application; /// /// 员工手机端公开入口,仅凭一次性任务链接访问入职资料。 /// public class OnboardingPublicTaskService : IOnboardingPublicTaskService { private readonly SqlSugarScope _db; private readonly IOnboardingContractSnapshotService _snapshotService; private readonly IOnboardingContractDocumentService _contractDocumentService; public OnboardingPublicTaskService( IOnboardingContractSnapshotService snapshotService, IOnboardingContractDocumentService contractDocumentService) { _db = DbContext.DB; _snapshotService = snapshotService; _contractDocumentService = contractDocumentService; } /// /// 通过有效 Token 获取任务创建时冻结的字段、材料和合同信息。 /// public async Task Detail(OnboardingTaskPublicAccessInput input) { var link = await GetActiveLink(input.Token); var task = await _db.Queryable().InSingleAsync(link.TaskId) ?? throw Oops.Bah("入职任务不存在"); link.AccessCount++; link.LastAccessedAt = DateTime.Now; await _db.Updateable(link).ExecuteCommandAsync(); var formRequirements = await _db.Queryable() .Where(item => item.TaskId == task.Id) .OrderBy(item => item.SortCode) .ToListAsync(); var materialRequirements = await _db.Queryable() .Where(item => item.TaskId == task.Id) .OrderBy(item => item.SortCode) .ToListAsync(); return new OnboardingTaskPublicDetailOutput { Task = task, EmployeeInfo = await _db.Queryable() .FirstAsync(item => item.TaskId == task.Id), Contracts = await _db.Queryable() .Where(item => item.TaskId == task.Id) .ToListAsync(), DynamicValues = await _db.Queryable() .Where(item => item.TaskId == task.Id) .ToListAsync(), Materials = await _db.Queryable() .Where(item => item.TaskId == task.Id) .ToListAsync(), FormFields = formRequirements.Select(requirement => new OnboardingPublicFormFieldOutput { FieldId = requirement.FieldId, FieldCode = requirement.FieldCode, FieldName = requirement.FieldName, DataType = requirement.DataType, OptionsJson = requirement.OptionsJson, Required = requirement.Required }).ToList(), MaterialRequirements = materialRequirements.Select(requirement => new OnboardingPublicMaterialOutput { MaterialTypeId = requirement.MaterialTypeId, MaterialName = requirement.MaterialName, AllowedExtensions = requirement.AllowedExtensions, MaxFileCount = requirement.MaxFileCount, Required = requirement.Required }).ToList() }; } /// /// 保存候选人的固定基础信息并刷新尚未确认的合同变量快照。 /// public async Task SaveEmployeeInfo(OnboardingTaskPublicSaveInput input) { var link = await GetActiveLink(input.Token); var task = await _db.Queryable().InSingleAsync(link.TaskId) ?? throw Oops.Bah("入职任务不存在"); if (task.TaskStatus != OnboardingTaskStatus.PendingFill && task.TaskStatus != OnboardingTaskStatus.Rejected) { throw Oops.Bah("当前任务不可编辑"); } var old = await _db.Queryable() .FirstAsync(item => item.TaskId == task.Id); var entity = input.Adapt(); entity.TaskId = task.Id; if (old == null) { entity.Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(); await _db.Insertable(entity).ExecuteCommandAsync(); } else { entity.Id = old.Id; await _db.Updateable(entity).ExecuteCommandAsync(); } await _snapshotService.Build(task.Id, entity, invalidateGeneratedPreview: true); } /// /// 根据员工当前已填写信息生成合同预览,并返回已替换变量的 DOCX 文件 Id。 /// 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("合同不存在"); await _snapshotService.Build(link.TaskId); var fileId = await _contractDocumentService.Generate(new OnboardingGenerateContractInput { TaskContractId = contract.Id }); contract.ReadAt ??= DateTime.Now; if (contract.ContractStatus == TaskContractStatus.PendingGenerate) contract.ContractStatus = TaskContractStatus.PendingConfirm; await _db.Updateable(contract).ExecuteCommandAsync(); return fileId; } private async Task GetActiveLink(string token) { if (string.IsNullOrWhiteSpace(token)) throw Oops.Bah("入职链接 Token 不能为空"); var hash = Convert.ToHexString( global::System.Security.Cryptography.SHA256.HashData( global::System.Text.Encoding.UTF8.GetBytes(token))); var link = await _db.Queryable().FirstAsync(item => item.TokenHash == hash) ?? throw Oops.Bah("链接不存在"); if (link.LinkStatus != OnboardingLinkStatus.Active || link.ExpiresAt <= DateTime.Now) throw Oops.Bah("链接已失效"); return link; } }