579 lines
26 KiB
C#
579 lines
26 KiB
C#
namespace SimpleAdmin.Application;
|
|
|
|
/// <summary>
|
|
/// 实现基于入职模板创建任务及冻结配置快照的业务能力。
|
|
/// </summary>
|
|
public class OnboardingTaskService : IOnboardingTaskService
|
|
{
|
|
private readonly SqlSugarScope _db;
|
|
private readonly IConfigService _configService;
|
|
private readonly IOnboardingContractSnapshotService _snapshotService;
|
|
private readonly IOnboardingDataPermissionService _dataPermission;
|
|
|
|
public OnboardingTaskService(
|
|
IConfigService configService,
|
|
IOnboardingContractSnapshotService snapshotService,
|
|
IOnboardingDataPermissionService dataPermission)
|
|
{
|
|
_db = DbContext.DB;
|
|
_configService = configService;
|
|
_snapshotService = snapshotService;
|
|
_dataPermission = dataPermission;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 校验公司、岗位和入职模板后创建任务,并冻结合同及材料要求。
|
|
/// </summary>
|
|
public async Task<OnboardingTaskCreatedOutput> Create(OnboardingTaskCreateInput input)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(input.Remark))
|
|
throw Oops.Bah("请填写人事备注");
|
|
|
|
ValidateEmploymentType(input.EmploymentType);
|
|
|
|
var companyEnabled = await _db.Queryable<ObCompany>()
|
|
.AnyAsync(item => item.Id == input.CompanyId && item.Status == "enable");
|
|
if (!companyEnabled)
|
|
throw Oops.Bah("公司不存在或未启用");
|
|
|
|
var departmentEnabled = await _db.Queryable<ObDepartment>()
|
|
.AnyAsync(item => item.Id == input.DepartmentId && item.CompanyId == input.CompanyId && item.Status == "enable");
|
|
if (!departmentEnabled)
|
|
throw Oops.Bah("部门不存在、未启用或不属于该公司");
|
|
|
|
var positionEnabled = await _db.Queryable<ObPosition>()
|
|
.AnyAsync(item => item.Id == input.PositionId
|
|
&& item.CompanyId == input.CompanyId
|
|
&& item.DepartmentId == input.DepartmentId
|
|
&& item.Status == "enable");
|
|
if (!positionEnabled)
|
|
throw Oops.Bah("岗位不存在、未启用或不属于该部门/公司");
|
|
|
|
var onboardingTemplate = await _db.Queryable<ObOnboardingTemplate>()
|
|
.FirstAsync(item => item.Id == input.OnboardingTemplateId
|
|
&& item.CompanyId == input.CompanyId
|
|
&& item.DepartmentId == input.DepartmentId
|
|
&& item.Status == "enable")
|
|
?? throw Oops.Bah("入职模板不存在、未启用或不属于该公司、部门");
|
|
var templateSupportsPosition = await _db.Queryable<ObOnboardingTemplatePosition>()
|
|
.AnyAsync(item => item.OnboardingTemplateId == onboardingTemplate.Id
|
|
&& item.PositionId == input.PositionId);
|
|
if (!templateSupportsPosition)
|
|
throw Oops.Bah("所选入职模板不适用于该岗位");
|
|
|
|
var contractConfigs = await _db.Queryable<ObOnboardingTemplateContract>()
|
|
.Where(item => item.OnboardingTemplateId == onboardingTemplate.Id)
|
|
.OrderBy(item => item.SortCode)
|
|
.ToListAsync();
|
|
if (contractConfigs.Count == 0)
|
|
throw Oops.Bah("入职模板尚未配置合同文件");
|
|
|
|
var materialConfigs = await _db.Queryable<ObOnboardingTemplateMaterial>()
|
|
.Where(item => item.OnboardingTemplateId == onboardingTemplate.Id)
|
|
.OrderBy(item => item.SortCode)
|
|
.ToListAsync();
|
|
|
|
var templateIds = contractConfigs.Select(item => item.ContractTemplateId).Distinct().ToList();
|
|
var contractTemplates = await _db.Queryable<ObContractTemplate>()
|
|
.Where(item => templateIds.Contains(item.Id))
|
|
.ToListAsync();
|
|
if (contractTemplates.Count != templateIds.Count
|
|
|| contractTemplates.Any(item => item.CompanyId != input.CompanyId
|
|
|| item.DepartmentId != input.DepartmentId
|
|
|| !string.Equals(
|
|
item.Status,
|
|
"enable",
|
|
global::System.StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
throw Oops.Bah("入职模板包含未启用或不属于当前公司、部门的合同模板");
|
|
}
|
|
|
|
var contractPositionCount = await _db.Queryable<ObContractTemplatePosition>()
|
|
.CountAsync(item => templateIds.Contains(item.ContractTemplateId)
|
|
&& item.PositionId == input.PositionId);
|
|
if (contractPositionCount != templateIds.Count)
|
|
throw Oops.Bah("入职模板包含不适用于当前岗位的合同模板");
|
|
|
|
var versionIds = contractConfigs.Select(item => item.ContractTemplateVersionId).Distinct().ToList();
|
|
var enabledVersionCount = await _db.Queryable<ObContractTemplateVersion>()
|
|
.CountAsync(item => versionIds.Contains(item.Id)
|
|
&& item.VersionStatus == ContractTemplateVersionStatus.Enabled);
|
|
if (enabledVersionCount != versionIds.Count)
|
|
throw Oops.Bah("入职模板包含未启用的合同版本,请先调整模板配置");
|
|
|
|
var materialIds = materialConfigs.Select(item => item.MaterialTypeId).Distinct().ToList();
|
|
var materialTypes = materialIds.Count == 0
|
|
? new List<ObMaterialType>()
|
|
: await _db.Queryable<ObMaterialType>().Where(item => materialIds.Contains(item.Id)).ToListAsync();
|
|
if (materialTypes.Count != materialIds.Count)
|
|
throw Oops.Bah("入职模板包含不存在的材料类型,请先调整模板配置");
|
|
|
|
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 taskId = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
|
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,
|
|
DepartmentId = input.DepartmentId,
|
|
PositionId = input.PositionId,
|
|
EmploymentType = input.EmploymentType,
|
|
OnboardingTemplateId = onboardingTemplate.Id,
|
|
AssignedReviewerUserId = input.AssignedReviewerUserId,
|
|
LinkExpireAt = expiresAt,
|
|
TaskStatus = OnboardingTaskStatus.PendingFill,
|
|
Remark = input.Remark.Trim()
|
|
};
|
|
|
|
ValidateInitialContractDateRanges(input.ContractVariables);
|
|
|
|
await _db.Ado.BeginTranAsync();
|
|
try
|
|
{
|
|
await _db.Insertable(task).ExecuteCommandAsync();
|
|
await _db.Insertable(new ObTaskLink
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
PlainToken = token,
|
|
TokenHash = Convert.ToHexString(global::System.Security.Cryptography.SHA256.HashData(
|
|
global::System.Text.Encoding.UTF8.GetBytes(token))),
|
|
ExpiresAt = expiresAt
|
|
}).ExecuteCommandAsync();
|
|
|
|
// 初始化空白的员工入职登记表
|
|
await _db.Insertable(new ObTaskEmployeeInfo
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
Status = CommonStatusConst.ENABLE,
|
|
CreateTime = DateTime.Now
|
|
}).ExecuteCommandAsync();
|
|
|
|
var contracts = contractConfigs.Select(config =>
|
|
{
|
|
var template = contractTemplates.First(item => item.Id == config.ContractTemplateId);
|
|
return new ObTaskContract
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
TemplateId = config.ContractTemplateId,
|
|
TemplateVersionId = config.ContractTemplateVersionId,
|
|
ContractName = template.TemplateName,
|
|
ContractType = template.ContractType,
|
|
Required = config.Required,
|
|
PrintCopies = config.PrintCopies
|
|
};
|
|
}).ToList();
|
|
await _db.Insertable(contracts).ExecuteCommandAsync();
|
|
|
|
if (materialConfigs.Count > 0)
|
|
{
|
|
var materialSnapshots = materialConfigs.Select(config =>
|
|
{
|
|
var material = materialTypes.First(item => item.Id == config.MaterialTypeId);
|
|
return new ObTaskMaterialRequirement
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
MaterialTypeId = material.Id,
|
|
MaterialCode = material.MaterialCode,
|
|
MaterialName = material.MaterialName,
|
|
AllowedExtensions = material.AllowedExtensions,
|
|
MaxFileCount = material.MaxFileCount,
|
|
MaxFileSizeMb = material.MaxFileSizeMb,
|
|
Required = config.Required,
|
|
SortCode = config.SortCode
|
|
};
|
|
}).ToList();
|
|
await _db.Insertable(materialSnapshots).ExecuteCommandAsync();
|
|
}
|
|
|
|
await _snapshotService.Build(taskId);
|
|
await ApplyInitialContractVariables(input.ContractVariables, contracts);
|
|
await _db.Ado.CommitTranAsync();
|
|
}
|
|
catch
|
|
{
|
|
await _db.Ado.RollbackTranAsync();
|
|
throw;
|
|
}
|
|
|
|
return new OnboardingTaskCreatedOutput
|
|
{
|
|
TaskId = taskId,
|
|
TaskNo = task.TaskNo,
|
|
LinkToken = token,
|
|
ExpiresAt = expiresAt
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 复制原任务锁定的业务配置与人事参数,并原子化创建多条独立任务。
|
|
/// </summary>
|
|
public async Task<OnboardingTaskCopyOutput> Copy(OnboardingTaskCopyInput input)
|
|
{
|
|
const int maxCopyCount = 50;
|
|
if (input.Count < 1 || input.Count > maxCopyCount)
|
|
throw Oops.Bah($"复制数量必须在1到{maxCopyCount}之间");
|
|
|
|
input.Items ??= new List<OnboardingTaskCopyItemInput>();
|
|
if (input.Items.Count != input.Count)
|
|
throw Oops.Bah("复制数量与人事备注数量不一致");
|
|
|
|
var missingRemarkIndex = input.Items.FindIndex(item =>
|
|
string.IsNullOrWhiteSpace(item.Remark));
|
|
if (missingRemarkIndex >= 0)
|
|
throw Oops.Bah($"请填写第{missingRemarkIndex + 1}份任务的人事备注");
|
|
|
|
var sourceTask = await _db.Queryable<ObOnboardingTask>()
|
|
.InSingleAsync(input.SourceTaskId)
|
|
?? throw Oops.Bah("原入职任务不存在");
|
|
await _dataPermission.EnsureCanManage(
|
|
sourceTask.CreateUserId,
|
|
OnboardingPermissionCodes.TaskManageAll,
|
|
"入职任务");
|
|
if (!sourceTask.DepartmentId.HasValue)
|
|
throw Oops.Bah("原入职任务未设置部门,不能复制");
|
|
|
|
var companyEnabled = await _db.Queryable<ObCompany>()
|
|
.AnyAsync(item => item.Id == sourceTask.CompanyId && item.Status == "enable");
|
|
if (!companyEnabled)
|
|
throw Oops.Bah("原任务所属公司不存在或未启用");
|
|
|
|
var departmentEnabled = await _db.Queryable<ObDepartment>()
|
|
.AnyAsync(item => item.Id == sourceTask.DepartmentId.Value
|
|
&& item.CompanyId == sourceTask.CompanyId
|
|
&& item.Status == "enable");
|
|
if (!departmentEnabled)
|
|
throw Oops.Bah("原任务所属部门不存在或未启用");
|
|
|
|
var positionEnabled = await _db.Queryable<ObPosition>()
|
|
.AnyAsync(item => item.Id == sourceTask.PositionId
|
|
&& item.CompanyId == sourceTask.CompanyId
|
|
&& item.DepartmentId == sourceTask.DepartmentId.Value
|
|
&& item.Status == "enable");
|
|
if (!positionEnabled)
|
|
throw Oops.Bah("原任务所属岗位不存在或未启用");
|
|
|
|
var onboardingTemplateEnabled = await _db.Queryable<ObOnboardingTemplate>()
|
|
.AnyAsync(item => item.Id == sourceTask.OnboardingTemplateId
|
|
&& item.CompanyId == sourceTask.CompanyId
|
|
&& item.DepartmentId == sourceTask.DepartmentId.Value
|
|
&& item.Status == "enable");
|
|
if (!onboardingTemplateEnabled)
|
|
throw Oops.Bah("原任务使用的入职模板不存在或未启用");
|
|
|
|
var sourceContracts = await _db.Queryable<ObTaskContract>()
|
|
.Where(item => item.TaskId == sourceTask.Id)
|
|
.ToListAsync();
|
|
if (sourceContracts.Count == 0)
|
|
throw Oops.Bah("原入职任务没有合同快照,不能复制");
|
|
|
|
var versionIds = sourceContracts
|
|
.Select(item => item.TemplateVersionId)
|
|
.Distinct()
|
|
.ToList();
|
|
var enabledVersionCount = await _db.Queryable<ObContractTemplateVersion>()
|
|
.CountAsync(item => versionIds.Contains(item.Id)
|
|
&& item.VersionStatus == ContractTemplateVersionStatus.Enabled);
|
|
if (enabledVersionCount != versionIds.Count)
|
|
throw Oops.Bah("原任务包含已停用的合同版本,不能继续复制");
|
|
|
|
var sourceMaterialRequirements = await _db.Queryable<ObTaskMaterialRequirement>()
|
|
.Where(item => item.TaskId == sourceTask.Id)
|
|
.OrderBy(item => item.SortCode)
|
|
.ToListAsync();
|
|
var sourceContractIds = sourceContracts.Select(item => item.Id).ToList();
|
|
var sourceManualVariables = await _db.Queryable<ObTaskContractVariable>()
|
|
.Where(item => sourceContractIds.Contains(item.TaskContractId)
|
|
&& item.SourceType == ContractVariableSourceType.HrInput
|
|
&& item.IsManualOverride)
|
|
.ToListAsync();
|
|
|
|
var initialContractVariables = (
|
|
from contract in sourceContracts
|
|
join variable in sourceManualVariables
|
|
on contract.Id equals variable.TaskContractId
|
|
select new OnboardingInitialContractVariableInput
|
|
{
|
|
ContractTemplateVersionId = contract.TemplateVersionId,
|
|
VariableCode = variable.VariableCode,
|
|
VariableValue = variable.VariableValue
|
|
}).ToList();
|
|
ValidateInitialContractDateRanges(initialContractVariables);
|
|
|
|
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 outputs = new List<OnboardingTaskCreatedOutput>();
|
|
await _db.Ado.BeginTranAsync();
|
|
try
|
|
{
|
|
foreach (var item in input.Items)
|
|
{
|
|
var output = await CreateCopiedTask(
|
|
sourceTask,
|
|
sourceContracts,
|
|
sourceMaterialRequirements,
|
|
initialContractVariables,
|
|
expiryHours,
|
|
item.Remark.Trim());
|
|
outputs.Add(output);
|
|
}
|
|
|
|
await _db.Ado.CommitTranAsync();
|
|
}
|
|
catch
|
|
{
|
|
await _db.Ado.RollbackTranAsync();
|
|
throw;
|
|
}
|
|
|
|
return new OnboardingTaskCopyOutput
|
|
{
|
|
Tasks = outputs
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据原任务快照创建一条全新的待填写任务,不复制任何候选人业务数据。
|
|
/// </summary>
|
|
private async Task<OnboardingTaskCreatedOutput> CreateCopiedTask(
|
|
ObOnboardingTask sourceTask,
|
|
List<ObTaskContract> sourceContracts,
|
|
List<ObTaskMaterialRequirement> sourceMaterialRequirements,
|
|
List<OnboardingInitialContractVariableInput> initialContractVariables,
|
|
int expiryHours,
|
|
string remark)
|
|
{
|
|
var now = DateTime.Now;
|
|
var taskId = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
|
var expiresAt = now.AddHours(expiryHours);
|
|
var token = Guid.NewGuid().ToString("N");
|
|
var task = new ObOnboardingTask
|
|
{
|
|
Id = taskId,
|
|
TaskNo = $"OB{now:yyyyMMddHHmmss}{taskId.ToString()[^4..]}",
|
|
CompanyId = sourceTask.CompanyId,
|
|
DepartmentId = sourceTask.DepartmentId,
|
|
PositionId = sourceTask.PositionId,
|
|
EmploymentType = string.IsNullOrWhiteSpace(sourceTask.EmploymentType)
|
|
? EmployeeEmploymentType.FullTime
|
|
: sourceTask.EmploymentType,
|
|
OnboardingTemplateId = sourceTask.OnboardingTemplateId,
|
|
AssignedReviewerUserId = sourceTask.AssignedReviewerUserId,
|
|
LinkExpireAt = expiresAt,
|
|
TaskStatus = OnboardingTaskStatus.PendingFill,
|
|
Remark = remark
|
|
};
|
|
|
|
await _db.Insertable(task).ExecuteCommandAsync();
|
|
await _db.Insertable(new ObTaskLink
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
PlainToken = token,
|
|
TokenHash = Convert.ToHexString(global::System.Security.Cryptography.SHA256.HashData(
|
|
global::System.Text.Encoding.UTF8.GetBytes(token))),
|
|
ExpiresAt = expiresAt
|
|
}).ExecuteCommandAsync();
|
|
|
|
await _db.Insertable(new ObTaskEmployeeInfo
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
Status = CommonStatusConst.ENABLE,
|
|
CreateTime = now
|
|
}).ExecuteCommandAsync();
|
|
|
|
var copiedContracts = sourceContracts.Select(source => new ObTaskContract
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
TemplateId = source.TemplateId,
|
|
TemplateVersionId = source.TemplateVersionId,
|
|
ContractName = source.ContractName,
|
|
ContractType = source.ContractType,
|
|
Required = source.Required,
|
|
PrintCopies = source.PrintCopies
|
|
}).ToList();
|
|
await _db.Insertable(copiedContracts).ExecuteCommandAsync();
|
|
|
|
if (sourceMaterialRequirements.Count > 0)
|
|
{
|
|
var copiedRequirements = sourceMaterialRequirements.Select(source =>
|
|
new ObTaskMaterialRequirement
|
|
{
|
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
|
TaskId = taskId,
|
|
MaterialTypeId = source.MaterialTypeId,
|
|
MaterialCode = source.MaterialCode,
|
|
MaterialName = source.MaterialName,
|
|
AllowedExtensions = source.AllowedExtensions,
|
|
MaxFileCount = source.MaxFileCount,
|
|
MaxFileSizeMb = source.MaxFileSizeMb,
|
|
Required = source.Required,
|
|
SortCode = source.SortCode
|
|
}).ToList();
|
|
await _db.Insertable(copiedRequirements).ExecuteCommandAsync();
|
|
}
|
|
|
|
// 重新生成公司、系统和空员工变量,再覆盖原任务的人事参数。
|
|
await _snapshotService.Build(taskId);
|
|
await ApplyInitialContractVariables(initialContractVariables, copiedContracts);
|
|
|
|
return new OnboardingTaskCreatedOutput
|
|
{
|
|
TaskId = taskId,
|
|
TaskNo = task.TaskNo,
|
|
LinkToken = token,
|
|
ExpiresAt = expiresAt
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 校验并保存任务创建时填写的人事合同变量。
|
|
/// </summary>
|
|
private async Task ApplyInitialContractVariables(
|
|
List<OnboardingInitialContractVariableInput> inputs,
|
|
List<ObTaskContract> contracts)
|
|
{
|
|
inputs ??= new List<OnboardingInitialContractVariableInput>();
|
|
var versionIds = contracts.Select(item => item.TemplateVersionId).Distinct().ToList();
|
|
var definitions = await _db.Queryable<ObTemplateVariable>()
|
|
.Where(item => versionIds.Contains(item.TemplateVersionId)
|
|
&& item.HrEditable
|
|
&& (item.VariableType == "hr"
|
|
|| item.VariableType == "contract"
|
|
|| item.VariableType == "company"
|
|
|| item.VariableType == ContractVariableType.Contract
|
|
|| item.VariableType == ContractVariableType.Company
|
|
|| item.VariableCode.StartsWith("hr.")))
|
|
.ToListAsync();
|
|
|
|
var duplicateInput = inputs
|
|
.GroupBy(item => new { item.ContractTemplateVersionId, item.VariableCode })
|
|
.FirstOrDefault(group => group.Count() > 1);
|
|
if (duplicateInput != null)
|
|
throw Oops.Bah($"合同变量“{duplicateInput.Key.VariableCode}”重复提交");
|
|
|
|
foreach (var input in inputs)
|
|
{
|
|
var definitionExists = definitions.Any(item =>
|
|
item.TemplateVersionId == input.ContractTemplateVersionId
|
|
&& item.VariableCode == input.VariableCode);
|
|
if (!definitionExists)
|
|
throw Oops.Bah($"合同变量“{input.VariableCode}”不存在或不允许人事填写");
|
|
}
|
|
|
|
var missingRequired = definitions.FirstOrDefault(definition =>
|
|
{
|
|
if (!definition.Required)
|
|
return false;
|
|
|
|
var input = inputs.FirstOrDefault(item =>
|
|
item.ContractTemplateVersionId == definition.TemplateVersionId
|
|
&& item.VariableCode == definition.VariableCode);
|
|
return string.IsNullOrWhiteSpace(input?.VariableValue ?? definition.DefaultValue);
|
|
});
|
|
if (missingRequired != null)
|
|
throw Oops.Bah($"请填写合同变量“{missingRequired.VariableName}”");
|
|
|
|
if (inputs.Count == 0)
|
|
return;
|
|
|
|
var duplicateContractVersion = contracts
|
|
.GroupBy(item => item.TemplateVersionId)
|
|
.FirstOrDefault(group => group.Count() > 1);
|
|
if (duplicateContractVersion != null)
|
|
throw Oops.Bah("入职模板中存在重复的合同版本,请重新保存入职模板后再创建任务");
|
|
|
|
var contractByVersion = contracts.ToDictionary(item => item.TemplateVersionId);
|
|
var contractIds = contracts.Select(item => item.Id).ToList();
|
|
var rows = await _db.Queryable<ObTaskContractVariable>()
|
|
.Where(item => contractIds.Contains(item.TaskContractId))
|
|
.ToListAsync();
|
|
|
|
foreach (var input in inputs)
|
|
{
|
|
var contract = contractByVersion[input.ContractTemplateVersionId];
|
|
var row = rows.FirstOrDefault(item =>
|
|
item.TaskContractId == contract.Id && item.VariableCode == input.VariableCode)
|
|
?? throw Oops.Bah($"合同变量快照“{input.VariableCode}”不存在");
|
|
row.VariableValue = input.VariableValue;
|
|
row.SourceType = ContractVariableSourceType.HrInput;
|
|
row.IsManualOverride = true;
|
|
}
|
|
|
|
var changedRows = rows.Where(item => item.IsManualOverride).ToList();
|
|
if (changedRows.Count > 0)
|
|
await _db.Updateable(changedRows).ExecuteCommandAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 校验人事创建任务时填写的合同日期先后关系。
|
|
/// </summary>
|
|
internal static void ValidateInitialContractDateRanges(
|
|
List<OnboardingInitialContractVariableInput> inputs)
|
|
{
|
|
inputs ??= new List<OnboardingInitialContractVariableInput>();
|
|
foreach (var group in inputs.GroupBy(item => item.ContractTemplateVersionId))
|
|
{
|
|
ValidateDateRange(group, "hr.ContractStartDate", "hr.ContractEndDate", "合同");
|
|
ValidateDateRange(group, "hr.ProbationStartDate", "hr.ProbationEndDate", "试用期");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 校验同一合同版本内一组开始、结束日期。
|
|
/// </summary>
|
|
private static void ValidateDateRange(
|
|
IEnumerable<OnboardingInitialContractVariableInput> inputs,
|
|
string startCode,
|
|
string endCode,
|
|
string rangeName)
|
|
{
|
|
var startValue = inputs.FirstOrDefault(item =>
|
|
string.Equals(item.VariableCode, startCode, StringComparison.OrdinalIgnoreCase))?.VariableValue;
|
|
var endValue = inputs.FirstOrDefault(item =>
|
|
string.Equals(item.VariableCode, endCode, StringComparison.OrdinalIgnoreCase))?.VariableValue;
|
|
if (string.IsNullOrWhiteSpace(startValue) || string.IsNullOrWhiteSpace(endValue))
|
|
return;
|
|
|
|
if (!DateTime.TryParse(startValue, out var startDate))
|
|
throw Oops.Bah($"{rangeName}开始日期格式不正确");
|
|
if (!DateTime.TryParse(endValue, out var endDate))
|
|
throw Oops.Bah($"{rangeName}结束日期格式不正确");
|
|
if (endDate.Date < startDate.Date)
|
|
throw Oops.Bah($"{rangeName}结束日期不能早于开始日期");
|
|
}
|
|
|
|
private static void ValidateEmploymentType(string employmentType)
|
|
{
|
|
if (employmentType != EmployeeEmploymentType.FullTime
|
|
&& employmentType != EmployeeEmploymentType.PartTime)
|
|
{
|
|
throw Oops.Bah("用工类型不正确,请选择正式员工或兼职员工");
|
|
}
|
|
}
|
|
}
|