namespace SimpleAdmin.Application;
///
/// 合同模板主档案;具体 DOCX 文件、变量与适用范围均挂在此档案下。
///
public class OnboardingContractTemplateService : DbRepository, IOnboardingContractTemplateService
{
private readonly IFileService _fileService;
private readonly IOnboardingDataPermissionService _dataPermission;
public OnboardingContractTemplateService(
IFileService fileService,
IOnboardingDataPermissionService dataPermission)
{
_fileService = fileService;
_dataPermission = dataPermission;
}
///
/// 分页查询合同模板主档案。
///
public async Task> Page(OnboardingContractTemplatePageInput input)
{
var query = Context.Queryable()
.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()
.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.GetSafeOrderBy("Id", "Status", "CreateTime", "UpdateTime", "CreateUser", "CreateUserId", "CompanyId", "DepartmentId", "TemplateCode", "TemplateName", "ContractType"))
.OrderByDescending(it => it.CreateTime)
.OrderBy(it => it.TemplateName)
.ToPagedListAsync(input.PageNum, input.PageSize);
await PopulateScopeDisplay(page.List.ToList());
return page;
}
///
/// 查询合同模板详情。
///
public async Task Detail(BaseIdInput input)
{
var template = await GetByIdAsync(input.Id) ?? throw Oops.Bah("合同模板不存在");
await PopulateScopeDisplay(new List { template });
return template;
}
///
/// 查询同时覆盖全部已选岗位的启用合同模板。
///
public async Task> ListAvailable(
OnboardingContractTemplateAvailableInput input)
{
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List();
if (input.PositionIds.Count == 0)
return new List();
await ValidateBusinessScope(input.CompanyId, input.DepartmentId, input.PositionIds);
var candidates = await Context.Queryable()
.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 now = DateTime.Now;
var candidateIds = candidates.Select(item => item.Id).ToList();
var availableVersionTemplateIds = (await Context.Queryable()
.Where(item => candidateIds.Contains(item.TemplateId))
.ToListAsync())
.Where(item => ContractTemplateVersionValidity.IsAvailable(item, now))
.Select(item => item.TemplateId)
.ToHashSet();
candidates = candidates
.Where(item => availableVersionTemplateIds.Contains(item.Id))
.ToList();
if (candidates.Count == 0)
return candidates;
candidateIds = candidates.Select(item => item.Id).ToList();
var relations = await Context.Queryable()
.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;
}
///
/// 新增合同模板主档案。
///
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;
}
}
///
/// 编辑合同模板主档案。
///
public async Task Edit(OnboardingContractTemplateEditInput input)
{
var template = await Detail(new BaseIdInput { Id = input.Id });
await _dataPermission.EnsureCanManage(
template.CreateUserId,
OnboardingPermissionCodes.ContractManageAll,
"合同模板");
NormalizeInput(input);
await ValidateBusinessScope(input.CompanyId, input.DepartmentId, input.PositionIds);
await ValidateReferencedOnboardingTemplates(input, template.Status);
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()
.Where(item => item.ContractTemplateId == input.Id)
.ExecuteCommandAsync();
await InsertPositionRelations(input.Id, input.PositionIds);
await Context.Ado.CommitTranAsync();
}
catch
{
await Context.Ado.RollbackTranAsync();
throw;
}
}
///
/// 删除未被入职模板或任务合同引用的模板(级联删除未使用的版本和变量)。
///
public async Task Delete(BaseIdListInput input)
{
if (input.Ids.Count == 0)
return;
var templates = await Context.Queryable()
.Where(item => input.Ids.Contains(item.Id))
.ToListAsync();
if (templates.Count != input.Ids.Distinct().Count())
throw Oops.Bah("合同模板不存在");
await _dataPermission.EnsureCanManage(
templates.Select(item => item.CreateUserId),
OnboardingPermissionCodes.ContractManageAll,
"合同模板");
var hasOnboardingTemplates = await Context.Queryable()
.AnyAsync(it => input.Ids.Contains(it.ContractTemplateId));
if (hasOnboardingTemplates)
throw Oops.Bah("模板已被入职模板引用,不能删除,请先在入职模板中移除关联");
var hasContracts = await Context.Queryable()
.AnyAsync(it => input.Ids.Contains(it.TemplateId));
if (hasContracts)
throw Oops.Bah("模板已生成入职任务合同,不能删除,可改为停用");
var versionIds = await Context.Queryable()
.Where(it => input.Ids.Contains(it.TemplateId))
.Select(it => it.Id)
.ToListAsync();
await Context.Ado.BeginTranAsync();
try
{
if (versionIds.Count > 0)
{
await Context.Deleteable()
.Where(it => versionIds.Contains(it.TemplateVersionId))
.ExecuteCommandAsync();
await Context.Deleteable()
.In(versionIds)
.ExecuteCommandAsync();
}
await Context.Deleteable()
.Where(item => input.Ids.Contains(item.ContractTemplateId))
.ExecuteCommandAsync();
await DeleteByIdsAsync(input.Ids.Cast