namespace SimpleAdmin.Application; public class OnboardingTaskService : IOnboardingTaskService { private readonly ISqlSugarClient _db; private readonly IConfigService _configService; private readonly IOnboardingContractSnapshotService _snapshotService; public OnboardingTaskService( ISqlSugarClient db, IConfigService configService, IOnboardingContractSnapshotService snapshotService) { _db = db; _configService = configService; _snapshotService = snapshotService; } public async Task Create(OnboardingTaskCreateInput input) { var companyEnabled = await _db.Queryable() .AnyAsync(item => item.Id == input.CompanyId && item.Status == "enable"); if (!companyEnabled) { throw Oops.Bah("公司不存在或未启用"); } var positionEnabled = await _db.Queryable() .AnyAsync(item => item.Id == input.PositionId && item.CompanyId == input.CompanyId && item.Status == "enable"); if (!positionEnabled) { throw Oops.Bah("岗位不存在、未启用或不属于该公司"); } var scopes = await _db.Queryable() .Where(item => item.CompanyId == input.CompanyId && (item.PositionId == null || item.PositionId == input.PositionId)) .ToListAsync(); // 同一版本同时存在“全公司”和“指定岗位”范围时,岗位专属配置优先,避免同一合同生成两次。 scopes = scopes .GroupBy(item => item.TemplateVersionId) .Select(group => group.FirstOrDefault(item => item.PositionId == input.PositionId) ?? group.First()) .ToList(); if (scopes.Count == 0) { throw Oops.Bah("该公司和岗位尚未配置合同适用范围"); } var taskId = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(); var expiryConfig = await _configService.GetByConfigKey( CateGoryConst.CONFIG_BIZ_DEFINE, "onboard.link.expireHours"); if (expiryConfig == null || !int.TryParse(expiryConfig.ConfigValue, out var expiryHours) || expiryHours <= 0) { throw Oops.Bah("请在系统配置中设置 onboard.link.expireHours(正整数小时)"); } var expiresAt = DateTime.Now.AddHours(expiryHours); var token = Guid.NewGuid().ToString("N"); var task = new ObOnboardingTask { Id = taskId, TaskNo = $"OB{DateTime.Now:yyyyMMddHHmmss}{taskId.ToString()[^4..]}", CompanyId = input.CompanyId, PositionId = input.PositionId, AssignedReviewerUserId = input.AssignedReviewerUserId, LinkExpireAt = expiresAt, TaskStatus = OnboardingTaskStatus.PendingFill, Remark = input.Remark }; await _db.Ado.BeginTranAsync(); try { await _db.Insertable(task).ExecuteCommandAsync(); await _db.Insertable(new ObTaskLink { Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(), TaskId = taskId, TokenHash = Convert.ToHexString(global::System.Security.Cryptography.SHA256.HashData( global::System.Text.Encoding.UTF8.GetBytes(token))), ExpiresAt = expiresAt }).ExecuteCommandAsync(); var templateIds = scopes.Select(item => item.TemplateId).Distinct().ToList(); var templates = await _db.Queryable() .Where(item => templateIds.Contains(item.Id)) .ToListAsync(); var contracts = scopes.Select(scope => { var template = templates.First(item => item.Id == scope.TemplateId); return new ObTaskContract { Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(), TaskId = taskId, TemplateId = scope.TemplateId, TemplateVersionId = scope.TemplateVersionId, ContractName = template.TemplateName, ContractType = template.ContractType, Required = scope.Required }; }).ToList(); await _db.Insertable(contracts).ExecuteCommandAsync(); await _snapshotService.Build(taskId); await _db.Ado.CommitTranAsync(); } catch { await _db.Ado.RollbackTranAsync(); throw; } return new OnboardingTaskCreatedOutput { TaskId = taskId, TaskNo = task.TaskNo, LinkToken = token, ExpiresAt = expiresAt }; } }