630 lines
26 KiB
C#
630 lines
26 KiB
C#
namespace SimpleAdmin.Application;
|
||
|
||
/// <summary>
|
||
/// 合同模板主档案;具体 DOCX 文件、变量与适用范围均挂在此档案下。
|
||
/// </summary>
|
||
public class OnboardingContractTemplateService : DbRepository<ObContractTemplate>, IOnboardingContractTemplateService
|
||
{
|
||
private readonly IFileService _fileService;
|
||
|
||
public OnboardingContractTemplateService(IFileService fileService)
|
||
{
|
||
_fileService = fileService;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询合同模板主档案。
|
||
/// </summary>
|
||
public async Task<SqlSugarPagedList<ObContractTemplate>> Page(OnboardingContractTemplatePageInput input)
|
||
{
|
||
var query = Context.Queryable<ObContractTemplate>()
|
||
.WhereIF(input.CompanyId.HasValue, item => item.CompanyId == input.CompanyId.Value)
|
||
.WhereIF(input.DepartmentId.HasValue, item => item.DepartmentId == input.DepartmentId.Value)
|
||
.WhereIF(
|
||
!string.IsNullOrWhiteSpace(input.TemplateName),
|
||
it => it.TemplateName.Contains(input.TemplateName) || it.TemplateCode.Contains(input.TemplateName))
|
||
.WhereIF(!string.IsNullOrWhiteSpace(input.ContractType), it => it.ContractType == input.ContractType)
|
||
.WhereIF(!string.IsNullOrWhiteSpace(input.Status), it => it.Status == input.Status);
|
||
|
||
if (input.PositionId.HasValue)
|
||
{
|
||
var templateIds = await Context.Queryable<ObContractTemplatePosition>()
|
||
.Where(item => item.PositionId == input.PositionId.Value)
|
||
.Select(item => item.ContractTemplateId)
|
||
.ToListAsync();
|
||
query = query.Where(item => templateIds.Contains(item.Id));
|
||
}
|
||
|
||
var page = await query
|
||
.OrderByIF(!string.IsNullOrWhiteSpace(input.SortField), $"{input.SortField} {input.SortOrder}")
|
||
.OrderBy(it => it.TemplateName)
|
||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||
await PopulateScopeDisplay(page.List.ToList());
|
||
return page;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询合同模板详情。
|
||
/// </summary>
|
||
public async Task<ObContractTemplate> Detail(BaseIdInput input)
|
||
{
|
||
var template = await GetByIdAsync(input.Id) ?? throw Oops.Bah("合同模板不存在");
|
||
await PopulateScopeDisplay(new List<ObContractTemplate> { template });
|
||
return template;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询同时覆盖全部已选岗位的启用合同模板。
|
||
/// </summary>
|
||
public async Task<List<ObContractTemplate>> ListAvailable(
|
||
OnboardingContractTemplateAvailableInput input)
|
||
{
|
||
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
||
if (input.PositionIds.Count == 0)
|
||
return new List<ObContractTemplate>();
|
||
|
||
await ValidateBusinessScope(input.CompanyId, input.DepartmentId, input.PositionIds);
|
||
|
||
var candidates = await Context.Queryable<ObContractTemplate>()
|
||
.Where(item => item.CompanyId == input.CompanyId
|
||
&& item.DepartmentId == input.DepartmentId
|
||
&& item.Status == "enable")
|
||
.OrderBy(item => item.TemplateName)
|
||
.ToListAsync();
|
||
if (candidates.Count == 0)
|
||
return candidates;
|
||
|
||
var candidateIds = candidates.Select(item => item.Id).ToList();
|
||
var relations = await Context.Queryable<ObContractTemplatePosition>()
|
||
.Where(item => candidateIds.Contains(item.ContractTemplateId))
|
||
.ToListAsync();
|
||
var availableIds = relations
|
||
.GroupBy(item => item.ContractTemplateId)
|
||
.Where(group => input.PositionIds.All(positionId =>
|
||
group.Any(item => item.PositionId == positionId)))
|
||
.Select(group => group.Key)
|
||
.ToHashSet();
|
||
var availableTemplates = candidates
|
||
.Where(item => availableIds.Contains(item.Id))
|
||
.ToList();
|
||
await PopulateScopeDisplay(availableTemplates);
|
||
return availableTemplates;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增合同模板主档案。
|
||
/// </summary>
|
||
public async Task Add(OnboardingContractTemplateAddInput input)
|
||
{
|
||
NormalizeInput(input);
|
||
await ValidateBusinessScope(input.CompanyId, input.DepartmentId, input.PositionIds);
|
||
await EnsureCodeUnique(input.TemplateCode, 0);
|
||
|
||
var templateId = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
||
var entity = BuildEntity(templateId, input);
|
||
await Context.Ado.BeginTranAsync();
|
||
try
|
||
{
|
||
await Context.Insertable(entity).ExecuteCommandAsync();
|
||
await InsertPositionRelations(templateId, input.PositionIds);
|
||
await Context.Ado.CommitTranAsync();
|
||
}
|
||
catch
|
||
{
|
||
await Context.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 编辑合同模板主档案。
|
||
/// </summary>
|
||
public async Task Edit(OnboardingContractTemplateEditInput input)
|
||
{
|
||
await Detail(new BaseIdInput { Id = input.Id });
|
||
NormalizeInput(input);
|
||
await ValidateBusinessScope(input.CompanyId, input.DepartmentId, input.PositionIds);
|
||
await ValidateReferencedOnboardingTemplates(input);
|
||
await EnsureCodeUnique(input.TemplateCode, input.Id);
|
||
|
||
var entity = BuildEntity(input.Id, input);
|
||
await Context.Ado.BeginTranAsync();
|
||
try
|
||
{
|
||
await Context.Updateable(entity)
|
||
.IgnoreColumns(ignoreAllNullColumns: true)
|
||
.ExecuteCommandAsync();
|
||
await Context.Deleteable<ObContractTemplatePosition>()
|
||
.Where(item => item.ContractTemplateId == input.Id)
|
||
.ExecuteCommandAsync();
|
||
await InsertPositionRelations(input.Id, input.PositionIds);
|
||
await Context.Ado.CommitTranAsync();
|
||
}
|
||
catch
|
||
{
|
||
await Context.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除未被入职模板或任务合同引用的模板(级联删除未使用的版本和变量)。
|
||
/// </summary>
|
||
public async Task Delete(BaseIdListInput input)
|
||
{
|
||
if (input.Ids.Count == 0)
|
||
return;
|
||
|
||
var hasOnboardingTemplates = await Context.Queryable<ObOnboardingTemplateContract>()
|
||
.AnyAsync(it => input.Ids.Contains(it.ContractTemplateId));
|
||
if (hasOnboardingTemplates)
|
||
throw Oops.Bah("模板已被入职模板引用,不能删除,请先在入职模板中移除关联");
|
||
|
||
var hasContracts = await Context.Queryable<ObTaskContract>()
|
||
.AnyAsync(it => input.Ids.Contains(it.TemplateId));
|
||
if (hasContracts)
|
||
throw Oops.Bah("模板已生成入职任务合同,不能删除,可改为停用");
|
||
|
||
var versionIds = await Context.Queryable<ObContractTemplateVersion>()
|
||
.Where(it => input.Ids.Contains(it.TemplateId))
|
||
.Select(it => it.Id)
|
||
.ToListAsync();
|
||
|
||
await Context.Ado.BeginTranAsync();
|
||
try
|
||
{
|
||
if (versionIds.Count > 0)
|
||
{
|
||
await Context.Deleteable<ObTemplateVariable>()
|
||
.Where(it => versionIds.Contains(it.TemplateVersionId))
|
||
.ExecuteCommandAsync();
|
||
await Context.Deleteable<ObContractTemplateVersion>()
|
||
.In(versionIds)
|
||
.ExecuteCommandAsync();
|
||
}
|
||
|
||
await Context.Deleteable<ObContractScope>()
|
||
.Where(it => input.Ids.Contains(it.TemplateId))
|
||
.ExecuteCommandAsync();
|
||
|
||
await Context.Deleteable<ObContractTemplatePosition>()
|
||
.Where(item => input.Ids.Contains(item.ContractTemplateId))
|
||
.ExecuteCommandAsync();
|
||
|
||
await DeleteByIdsAsync(input.Ids.Cast<object>().ToArray());
|
||
await Context.Ado.CommitTranAsync();
|
||
}
|
||
catch
|
||
{
|
||
await Context.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除未被入职模板或任务使用的合同版本。
|
||
/// </summary>
|
||
public async Task DeleteVersion(BaseIdInput input)
|
||
{
|
||
var version = await Context.Queryable<ObContractTemplateVersion>().InSingleAsync(input.Id)
|
||
?? throw Oops.Bah("合同版本不存在");
|
||
|
||
var isUsedInOnboardingTemplate = await Context.Queryable<ObOnboardingTemplateContract>()
|
||
.AnyAsync(it => it.ContractTemplateVersionId == version.Id);
|
||
if (isUsedInOnboardingTemplate)
|
||
throw Oops.Bah("该版本已被入职模板引用,不能删除,请先在入职模板中移除关联");
|
||
|
||
var isUsedInTask = await Context.Queryable<ObTaskContract>()
|
||
.AnyAsync(it => it.TemplateVersionId == version.Id);
|
||
if (isUsedInTask)
|
||
throw Oops.Bah("该版本已用于实际入职任务,不能删除,可改为停用");
|
||
|
||
await Context.Ado.BeginTranAsync();
|
||
try
|
||
{
|
||
await Context.Deleteable<ObTemplateVariable>()
|
||
.Where(it => it.TemplateVersionId == version.Id)
|
||
.ExecuteCommandAsync();
|
||
await Context.Deleteable<ObContractScope>()
|
||
.Where(it => it.TemplateVersionId == version.Id)
|
||
.ExecuteCommandAsync();
|
||
await Context.Deleteable<ObContractTemplateVersion>()
|
||
.In(version.Id)
|
||
.ExecuteCommandAsync();
|
||
await Context.Ado.CommitTranAsync();
|
||
}
|
||
catch
|
||
{
|
||
await Context.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询指定合同模板的版本。
|
||
/// </summary>
|
||
public async Task<SqlSugarPagedList<ObContractTemplateVersion>> VersionPage(OnboardingContractVersionPageInput input)
|
||
{
|
||
return await Context.Queryable<ObContractTemplateVersion>()
|
||
.Where(it => it.TemplateId == input.TemplateId)
|
||
.WhereIF(!string.IsNullOrWhiteSpace(input.VersionStatus), it => it.VersionStatus == input.VersionStatus)
|
||
.OrderByDescending(it => it.CreateTime)
|
||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增合同模板 DOCX 版本。
|
||
/// </summary>
|
||
public async Task AddVersion(OnboardingContractVersionAddInput input)
|
||
{
|
||
await Detail(new BaseIdInput { Id = input.TemplateId });
|
||
var file = await Context.Queryable<SysFile>().InSingleAsync(input.SourceDocxFileId)
|
||
?? throw Oops.Bah("DOCX 源文件不存在");
|
||
if (!string.Equals(file.Suffix, "docx", StringComparison.OrdinalIgnoreCase))
|
||
throw Oops.Bah("合同模板仅支持 DOCX 文件");
|
||
|
||
var versionExists = await Context.Queryable<ObContractTemplateVersion>()
|
||
.AnyAsync(item => item.TemplateId == input.TemplateId && item.VersionNo == input.VersionNo);
|
||
if (versionExists)
|
||
throw Oops.Bah("该模板版本号已存在");
|
||
|
||
var sourceResult = await _fileService.Download(new BaseIdInput { Id = file.Id })
|
||
?? throw Oops.Bah("无法读取 DOCX 源文件");
|
||
await using var sourceStream = sourceResult.FileStream;
|
||
using var sourceBytes = new MemoryStream();
|
||
await sourceStream.CopyToAsync(sourceBytes);
|
||
var bytes = sourceBytes.ToArray();
|
||
var variableCodes = DocxTemplateProcessor.ExtractVariableCodes(bytes);
|
||
|
||
// 严格比对变量字典库,防止手误和非法占位符
|
||
var dictList = await Context.Queryable<ObVariableDefinition>().ToListAsync();
|
||
var duplicateDefinition = dictList
|
||
.GroupBy(item => item.VariableCode, StringComparer.OrdinalIgnoreCase)
|
||
.FirstOrDefault(group => group.Count() > 1);
|
||
if (duplicateDefinition != null)
|
||
{
|
||
throw Oops.Bah(
|
||
$"变量字典编码“{duplicateDefinition.Key}”存在重复数据,请先清理后再导入合同版本");
|
||
}
|
||
|
||
var dictMap = dictList.ToDictionary(it => it.VariableCode, it => it, StringComparer.OrdinalIgnoreCase);
|
||
var undefinedCodes = variableCodes.Where(code => !dictMap.ContainsKey(code)).ToList();
|
||
if (undefinedCodes.Count > 0)
|
||
{
|
||
throw Oops.Bah($"DOCX 模板包含未在字典库中注册的占位符:{string.Join(",", undefinedCodes)}。请检查拼写或先在【变量字典】中添加该变量。");
|
||
}
|
||
|
||
var versionId = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
||
var version = input.Adapt<ObContractTemplateVersion>();
|
||
version.Id = versionId;
|
||
version.VersionStatus = ContractTemplateVersionStatus.Draft;
|
||
version.FileHash = string.IsNullOrWhiteSpace(input.FileHash)
|
||
? Convert.ToHexString(global::System.Security.Cryptography.SHA256.HashData(bytes))
|
||
: input.FileHash;
|
||
|
||
var variables = variableCodes.Select((code, index) =>
|
||
{
|
||
var def = dictMap.TryGetValue(code, out var d) ? d : null;
|
||
return BuildDetectedVariable(versionId, code, def, index);
|
||
}).ToList();
|
||
|
||
await Context.Ado.BeginTranAsync();
|
||
try
|
||
{
|
||
await Context.Insertable(version).ExecuteCommandAsync();
|
||
if (variables.Count > 0)
|
||
await Context.Insertable(variables).ExecuteCommandAsync();
|
||
await Context.Ado.CommitTranAsync();
|
||
}
|
||
catch
|
||
{
|
||
await Context.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启用或停用合同模板版本。
|
||
/// </summary>
|
||
public async Task SetVersionStatus(OnboardingContractVersionStatusInput input)
|
||
{
|
||
if (input.VersionStatus != ContractTemplateVersionStatus.Enabled
|
||
&& input.VersionStatus != ContractTemplateVersionStatus.Disabled)
|
||
{
|
||
throw Oops.Bah("版本状态仅支持启用或停用");
|
||
}
|
||
|
||
var version = await Context.Queryable<ObContractTemplateVersion>().InSingleAsync(input.Id)
|
||
?? throw Oops.Bah("模板版本不存在");
|
||
|
||
if (input.VersionStatus == ContractTemplateVersionStatus.Enabled)
|
||
{
|
||
var hasUnresolvedVariables = await Context.Queryable<ObTemplateVariable>()
|
||
.AnyAsync(item => item.TemplateVersionId == input.Id
|
||
&& item.VariableType == ContractVariableType.Unresolved);
|
||
if (hasUnresolvedVariables)
|
||
throw Oops.Bah("仍有未识别来源的合同变量,请逐项确认后再启用");
|
||
}
|
||
|
||
version.VersionStatus = input.VersionStatus;
|
||
await Context.Updateable(version).ExecuteCommandAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询指定模板版本的变量定义。
|
||
/// </summary>
|
||
public async Task<SqlSugarPagedList<ObTemplateVariable>> VariablePage(OnboardingTemplateVariablePageInput input)
|
||
{
|
||
return await Context.Queryable<ObTemplateVariable>()
|
||
.Where(it => it.TemplateVersionId == input.TemplateVersionId)
|
||
.OrderBy(it => it.SortCode)
|
||
.OrderBy(it => it.VariableCode)
|
||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||
}
|
||
/// <summary>
|
||
/// 编辑模板版本中自动识别出的变量,不允许修改 DOCX 占位符编码。
|
||
/// </summary>
|
||
public async Task EditVariable(OnboardingTemplateVariableEditInput input)
|
||
{
|
||
var variable = await Context.Queryable<ObTemplateVariable>().InSingleAsync(input.Id)
|
||
?? throw Oops.Bah("模板变量不存在");
|
||
if (variable.TemplateVersionId != input.TemplateVersionId)
|
||
throw Oops.Bah("不允许变更变量所属版本");
|
||
if (!string.Equals(variable.VariableCode, input.VariableCode, StringComparison.Ordinal))
|
||
throw Oops.Bah("DOCX 占位符编码由系统识别,不能修改");
|
||
|
||
var isUsedInTask = await Context.Queryable<ObTaskContract>()
|
||
.AnyAsync(it => it.TemplateVersionId == input.TemplateVersionId);
|
||
if (isUsedInTask)
|
||
throw Oops.Bah("该版本已生成实际入职任务合同,变量配置已锁定");
|
||
|
||
var entity = input.Adapt<ObTemplateVariable>();
|
||
entity.VariableCode = variable.VariableCode;
|
||
await Context.Updateable(entity).ExecuteCommandAsync();
|
||
}
|
||
|
||
private static ObTemplateVariable BuildDetectedVariable(long versionId, string code, ObVariableDefinition def, int index)
|
||
{
|
||
var separatorIndex = code.IndexOf('.');
|
||
var prefix = separatorIndex > 0 ? code[..separatorIndex].ToLowerInvariant() : string.Empty;
|
||
var sourceField = separatorIndex > 0 ? code[(separatorIndex + 1)..] : code;
|
||
var variableType = prefix switch
|
||
{
|
||
"employee" => ContractVariableType.Employee,
|
||
"company" => ContractVariableType.Company,
|
||
"hr" => ContractVariableType.Contract,
|
||
"system" => ContractVariableType.System,
|
||
_ => ContractVariableType.Unresolved
|
||
};
|
||
|
||
var varName = def != null && !string.IsNullOrWhiteSpace(def.VariableName) ? def.VariableName : sourceField;
|
||
var dataType = def != null && !string.IsNullOrWhiteSpace(def.DataType) ? def.DataType : InferDataType(sourceField);
|
||
|
||
return new ObTemplateVariable
|
||
{
|
||
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||
TemplateVersionId = versionId,
|
||
VariableCode = code,
|
||
VariableName = varName,
|
||
VariableType = variableType,
|
||
DataType = dataType,
|
||
EmployeeField = variableType is ContractVariableType.Employee or ContractVariableType.Company
|
||
? sourceField
|
||
: null,
|
||
Required = def?.Required ?? true,
|
||
HrEditable = variableType == ContractVariableType.Contract,
|
||
SortCode = (index + 1) * 10,
|
||
Remark = def?.Remark
|
||
};
|
||
}
|
||
|
||
private static string InferDataType(string sourceField)
|
||
{
|
||
var normalized = sourceField.ToLowerInvariant();
|
||
if (normalized.Contains("date") || normalized.EndsWith("at"))
|
||
return "date";
|
||
if (normalized.Contains("salary")
|
||
|| normalized.Contains("amount")
|
||
|| normalized.Contains("wage")
|
||
|| normalized.Contains("copies"))
|
||
{
|
||
return "decimal";
|
||
}
|
||
|
||
return "string";
|
||
}
|
||
|
||
private async Task EnsureCodeUnique(string code, long id)
|
||
{
|
||
var exists = await Context.Queryable<ObContractTemplate>()
|
||
.AnyAsync(it => it.TemplateCode == code && it.Id != id);
|
||
if (exists)
|
||
throw Oops.Bah("模板编码已存在");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标准化合同模板保存参数并执行基础业务校验。
|
||
/// </summary>
|
||
private static void NormalizeInput(OnboardingContractTemplateAddInput input)
|
||
{
|
||
input.TemplateCode = input.TemplateCode?.Trim();
|
||
input.TemplateName = input.TemplateName?.Trim();
|
||
input.ContractType = input.ContractType?.Trim();
|
||
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
||
|
||
if (string.IsNullOrWhiteSpace(input.TemplateCode)
|
||
|| string.IsNullOrWhiteSpace(input.TemplateName)
|
||
|| string.IsNullOrWhiteSpace(input.ContractType))
|
||
{
|
||
throw Oops.Bah("请填写模板编码、模板名称和合同类型");
|
||
}
|
||
|
||
if (input.PositionIds.Count == 0)
|
||
throw Oops.Bah("请至少选择一个适用岗位");
|
||
if (input.Status != "enable" && input.Status != "disable")
|
||
throw Oops.Bah("合同模板状态不正确");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 校验公司、部门、岗位属于同一条启用的业务层级。
|
||
/// </summary>
|
||
private async Task ValidateBusinessScope(
|
||
long companyId,
|
||
long departmentId,
|
||
List<long> positionIds)
|
||
{
|
||
var companyEnabled = await Context.Queryable<ObCompany>()
|
||
.AnyAsync(item => item.Id == companyId && item.Status == "enable");
|
||
if (!companyEnabled)
|
||
throw Oops.Bah("所属公司不存在或未启用");
|
||
|
||
var departmentEnabled = await Context.Queryable<ObDepartment>()
|
||
.AnyAsync(item => item.Id == departmentId
|
||
&& item.CompanyId == companyId
|
||
&& item.Status == "enable");
|
||
if (!departmentEnabled)
|
||
throw Oops.Bah("所属部门不存在、未启用或不属于该公司");
|
||
|
||
var positionCount = await Context.Queryable<ObPosition>()
|
||
.CountAsync(item => positionIds.Contains(item.Id)
|
||
&& item.CompanyId == companyId
|
||
&& item.DepartmentId == departmentId
|
||
&& item.Status == "enable");
|
||
if (positionCount != positionIds.Count)
|
||
throw Oops.Bah("存在未启用或不属于该公司、部门的岗位");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 防止修改合同模板归属后破坏已经保存的入职模板配置。
|
||
/// </summary>
|
||
private async Task ValidateReferencedOnboardingTemplates(
|
||
OnboardingContractTemplateEditInput input)
|
||
{
|
||
var relations = await Context.Queryable<ObOnboardingTemplateContract>()
|
||
.Where(item => item.ContractTemplateId == input.Id)
|
||
.ToListAsync();
|
||
if (relations.Count == 0)
|
||
return;
|
||
|
||
var onboardingTemplateIds = relations
|
||
.Select(item => item.OnboardingTemplateId)
|
||
.Distinct()
|
||
.ToList();
|
||
var onboardingTemplates = await Context.Queryable<ObOnboardingTemplate>()
|
||
.Where(item => onboardingTemplateIds.Contains(item.Id))
|
||
.ToListAsync();
|
||
var positionRelations = await Context.Queryable<ObOnboardingTemplatePosition>()
|
||
.Where(item => onboardingTemplateIds.Contains(item.OnboardingTemplateId))
|
||
.ToListAsync();
|
||
|
||
foreach (var template in onboardingTemplates)
|
||
{
|
||
var positionIds = positionRelations
|
||
.Where(item => item.OnboardingTemplateId == template.Id)
|
||
.Select(item => item.PositionId)
|
||
.Distinct()
|
||
.ToList();
|
||
var departmentMatches = !template.DepartmentId.HasValue
|
||
|| template.DepartmentId.Value == input.DepartmentId;
|
||
var scopeMatches = template.CompanyId == input.CompanyId
|
||
&& departmentMatches
|
||
&& positionIds.All(input.PositionIds.Contains);
|
||
if (!scopeMatches)
|
||
{
|
||
throw Oops.Bah(
|
||
$"合同模板仍被入职模板“{template.TemplateName}”引用,当前归属调整会使其失效,请先调整入职模板");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构建合同模板主档案实体。
|
||
/// </summary>
|
||
private static ObContractTemplate BuildEntity(
|
||
long templateId,
|
||
OnboardingContractTemplateAddInput input)
|
||
{
|
||
return new ObContractTemplate
|
||
{
|
||
Id = templateId,
|
||
CompanyId = input.CompanyId,
|
||
DepartmentId = input.DepartmentId,
|
||
TemplateCode = input.TemplateCode,
|
||
TemplateName = input.TemplateName,
|
||
ContractType = input.ContractType,
|
||
Description = input.Description,
|
||
Status = input.Status
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存合同模板适用岗位关系。
|
||
/// </summary>
|
||
private async Task InsertPositionRelations(long templateId, List<long> positionIds)
|
||
{
|
||
var relations = positionIds.Select(positionId => new ObContractTemplatePosition
|
||
{
|
||
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||
ContractTemplateId = templateId,
|
||
PositionId = positionId
|
||
}).ToList();
|
||
await Context.Insertable(relations).ExecuteCommandAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 回填合同模板的公司、部门和适用岗位展示信息。
|
||
/// </summary>
|
||
private async Task PopulateScopeDisplay(List<ObContractTemplate> templates)
|
||
{
|
||
if (templates.Count == 0)
|
||
return;
|
||
|
||
var templateIds = templates.Select(item => item.Id).ToList();
|
||
var companyIds = templates
|
||
.Where(item => item.CompanyId.HasValue)
|
||
.Select(item => item.CompanyId.Value)
|
||
.Distinct()
|
||
.ToList();
|
||
var departmentIds = templates
|
||
.Where(item => item.DepartmentId.HasValue)
|
||
.Select(item => item.DepartmentId.Value)
|
||
.Distinct()
|
||
.ToList();
|
||
var companies = companyIds.Count == 0
|
||
? new List<ObCompany>()
|
||
: await Context.Queryable<ObCompany>()
|
||
.Where(item => companyIds.Contains(item.Id))
|
||
.ToListAsync();
|
||
var departments = departmentIds.Count == 0
|
||
? new List<ObDepartment>()
|
||
: await Context.Queryable<ObDepartment>()
|
||
.Where(item => departmentIds.Contains(item.Id))
|
||
.ToListAsync();
|
||
var relations = await Context.Queryable<ObContractTemplatePosition>()
|
||
.Where(item => templateIds.Contains(item.ContractTemplateId))
|
||
.ToListAsync();
|
||
var positionIds = relations.Select(item => item.PositionId).Distinct().ToList();
|
||
var positions = positionIds.Count == 0
|
||
? new List<ObPosition>()
|
||
: await Context.Queryable<ObPosition>()
|
||
.Where(item => positionIds.Contains(item.Id))
|
||
.ToListAsync();
|
||
|
||
foreach (var template in templates)
|
||
{
|
||
template.CompanyName = companies
|
||
.FirstOrDefault(item => item.Id == template.CompanyId)?.CompanyName;
|
||
template.DepartmentName = departments
|
||
.FirstOrDefault(item => item.Id == template.DepartmentId)?.DepartmentName;
|
||
template.PositionIds = relations
|
||
.Where(item => item.ContractTemplateId == template.Id)
|
||
.Select(item => item.PositionId)
|
||
.ToList();
|
||
template.PositionNames = template.PositionIds
|
||
.Select(positionId => positions.FirstOrDefault(item => item.Id == positionId)?.PositionName)
|
||
.Where(name => !string.IsNullOrWhiteSpace(name))
|
||
.ToList();
|
||
}
|
||
}
|
||
}
|