This commit is contained in:
fuenmao
2026-08-24 11:28:51 +08:00
parent 33f2f3f103
commit 01328d567c
42 changed files with 1277 additions and 0 deletions
@@ -0,0 +1,27 @@
namespace SimpleAdmin.Application;
public class OnboardingContractScopePageInput : BasePageInput
{
public long? CompanyId { get; set; }
public long? PositionId { get; set; }
public long? TemplateId { get; set; }
}
public class OnboardingContractScopeAddInput
{
[IdNotNull(ErrorMessage = "公司Id不能为空")]
public long CompanyId { get; set; }
public long? PositionId { get; set; }
[IdNotNull(ErrorMessage = "模板Id不能为空")]
public long TemplateId { get; set; }
[IdNotNull(ErrorMessage = "模板版本Id不能为空")]
public long TemplateVersionId { get; set; }
public bool Required { get; set; } = true;
public int? SortCode { get; set; }
}
public class OnboardingContractScopeEditInput : OnboardingContractScopeAddInput
{
[IdNotNull(ErrorMessage = "适用范围Id不能为空")]
public long Id { get; set; }
}
@@ -0,0 +1,26 @@
namespace SimpleAdmin.Application;
public class OnboardingContractTemplatePageInput : BasePageInput
{
public string TemplateName { get; set; }
public string ContractType { get; set; }
public string Status { get; set; }
}
public class OnboardingContractTemplateAddInput
{
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "模板编码不能为空")]
public string TemplateCode { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "模板名称不能为空")]
public string TemplateName { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "合同类型不能为空")]
public string ContractType { get; set; }
public string Description { get; set; }
public string Status { get; set; } = "enable";
}
public class OnboardingContractTemplateEditInput : OnboardingContractTemplateAddInput
{
[IdNotNull(ErrorMessage = "模板Id不能为空")]
public long Id { get; set; }
}
@@ -0,0 +1,30 @@
namespace SimpleAdmin.Application;
public class OnboardingContractVersionPageInput : BasePageInput
{
[IdNotNull(ErrorMessage = "模板Id不能为空")]
public long TemplateId { get; set; }
public string VersionStatus { get; set; }
}
public class OnboardingContractVersionAddInput
{
[IdNotNull(ErrorMessage = "模板Id不能为空")]
public long TemplateId { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "版本号不能为空")]
public string VersionNo { get; set; }
[IdNotNull(ErrorMessage = "DOCX源文件不能为空")]
public long SourceDocxFileId { get; set; }
public string FileHash { get; set; }
public DateTime? EffectiveDate { get; set; }
public DateTime? ExpiryDate { get; set; }
public string Remark { get; set; }
}
public class OnboardingContractVersionStatusInput
{
[IdNotNull(ErrorMessage = "版本Id不能为空")]
public long Id { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "版本状态不能为空")]
public string VersionStatus { get; set; }
}
@@ -0,0 +1,35 @@
namespace SimpleAdmin.Application;
public class OnboardingTemplateVariablePageInput : BasePageInput
{
[IdNotNull(ErrorMessage = "模板版本Id不能为空")]
public long TemplateVersionId { get; set; }
}
public class OnboardingTemplateVariableAddInput
{
[IdNotNull(ErrorMessage = "模板版本Id不能为空")]
public long TemplateVersionId { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "变量编码不能为空")]
public string VariableCode { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "变量名称不能为空")]
public string VariableName { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "变量类型不能为空")]
public string VariableType { get; set; }
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "数据类型不能为空")]
public string DataType { get; set; }
public string EmployeeField { get; set; }
public string DefaultValue { get; set; }
public string OptionsJson { get; set; }
public string FormatRule { get; set; }
public bool Required { get; set; }
public bool HrEditable { get; set; }
public int? SortCode { get; set; }
public string Remark { get; set; }
}
public class OnboardingTemplateVariableEditInput : OnboardingTemplateVariableAddInput
{
[IdNotNull(ErrorMessage = "变量Id不能为空")]
public long Id { get; set; }
}
@@ -0,0 +1,19 @@
namespace SimpleAdmin.Application;
public interface IOnboardingContractTemplateService : ITransient
{
Task<SqlSugarPagedList<ObContractTemplate>> Page(OnboardingContractTemplatePageInput input);
Task<ObContractTemplate> Detail(BaseIdInput input);
Task Add(OnboardingContractTemplateAddInput input);
Task Edit(OnboardingContractTemplateEditInput input);
Task Delete(BaseIdListInput input);
Task<SqlSugarPagedList<ObContractTemplateVersion>> VersionPage(OnboardingContractVersionPageInput input);
Task AddVersion(OnboardingContractVersionAddInput input);
Task SetVersionStatus(OnboardingContractVersionStatusInput input);
Task<SqlSugarPagedList<ObTemplateVariable>> VariablePage(OnboardingTemplateVariablePageInput input);
Task AddVariable(OnboardingTemplateVariableAddInput input);
Task EditVariable(OnboardingTemplateVariableEditInput input);
Task DeleteVariable(BaseIdListInput input); Task<SqlSugarPagedList<ObContractScope>> ScopePage(OnboardingContractScopePageInput input);
Task AddScope(OnboardingContractScopeAddInput input);
Task EditScope(OnboardingContractScopeEditInput input);
Task DeleteScope(BaseIdListInput input);}
@@ -0,0 +1,149 @@
namespace SimpleAdmin.Application;
/// <summary>合同模板主档案;具体 DOCX 文件、变量与适用范围均挂在此档案下。</summary>
public class OnboardingContractTemplateService : DbRepository<ObContractTemplate>, IOnboardingContractTemplateService
{
public async Task<SqlSugarPagedList<ObContractTemplate>> Page(OnboardingContractTemplatePageInput input)
{
return await Context.Queryable<ObContractTemplate>()
.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)
.OrderByIF(!string.IsNullOrWhiteSpace(input.SortField), $"{input.SortField} {input.SortOrder}")
.OrderBy(it => it.TemplateName)
.ToPagedListAsync(input.PageNum, input.PageSize);
}
public async Task<ObContractTemplate> Detail(BaseIdInput input) => await GetByIdAsync(input.Id) ?? throw Oops.Bah("合同模板不存在");
public async Task Add(OnboardingContractTemplateAddInput input)
{
await EnsureCodeUnique(input.TemplateCode, 0);
await InsertAsync(input.Adapt<ObContractTemplate>());
}
public async Task Edit(OnboardingContractTemplateEditInput input)
{
await Detail(new BaseIdInput { Id = input.Id });
await EnsureCodeUnique(input.TemplateCode, input.Id);
await UpdateAsync(input.Adapt<ObContractTemplate>());
}
public async Task Delete(BaseIdListInput input)
{
if (input.Ids.Count == 0) return;
if (await Context.Queryable<ObContractTemplateVersion>().AnyAsync(it => input.Ids.Contains(it.TemplateId))) throw Oops.Bah("模板已有版本,不能删除");
if (await Context.Queryable<ObContractScope>().AnyAsync(it => input.Ids.Contains(it.TemplateId))) throw Oops.Bah("模板已配置适用范围,不能删除");
if (await Context.Queryable<ObTaskContract>().AnyAsync(it => input.Ids.Contains(it.TemplateId))) throw Oops.Bah("模板已生成入职合同,不能删除");
await DeleteByIdsAsync(input.Ids.Cast<object>().ToArray());
}
private async Task EnsureCodeUnique(string code, long id)
{
if (await Context.Queryable<ObContractTemplate>().AnyAsync(it => it.TemplateCode == code && it.Id != id)) throw Oops.Bah("模板编码已存在");
}
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);
}
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文件");
if (await Context.Queryable<ObContractTemplateVersion>().AnyAsync(it => it.TemplateId == input.TemplateId && it.VersionNo == input.VersionNo)) throw Oops.Bah("该模板版本号已存在");
await Context.Insertable(input.Adapt<ObContractTemplateVersion>()).ExecuteCommandAsync();
}
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 && !await Context.Queryable<ObTemplateVariable>().AnyAsync(it => it.TemplateVersionId == input.Id)) throw Oops.Bah("请先配置模板变量后再启用");
version.VersionStatus = input.VersionStatus;
await Context.Updateable(version).ExecuteCommandAsync();
}
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);
}
public async Task AddVariable(OnboardingTemplateVariableAddInput input)
{
await EnsureVersionDraft(input.TemplateVersionId);
await EnsureVariableUnique(input.TemplateVersionId, input.VariableCode, 0);
await Context.Insertable(input.Adapt<ObTemplateVariable>()).ExecuteCommandAsync();
}
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("不允许变更变量所属版本");
await EnsureVersionDraft(input.TemplateVersionId);
await EnsureVariableUnique(input.TemplateVersionId, input.VariableCode, input.Id);
await Context.Updateable(input.Adapt<ObTemplateVariable>()).ExecuteCommandAsync();
}
public async Task DeleteVariable(BaseIdListInput input)
{
if (input.Ids.Count == 0) return;
var versionIds = await Context.Queryable<ObTemplateVariable>().Where(it => input.Ids.Contains(it.Id)).Select(it => it.TemplateVersionId).ToListAsync();
foreach (var versionId in versionIds.Distinct()) await EnsureVersionDraft(versionId);
await Context.Deleteable<ObTemplateVariable>().In(input.Ids).ExecuteCommandAsync();
}
private async Task EnsureVersionDraft(long versionId)
{
var version = await Context.Queryable<ObContractTemplateVersion>().InSingleAsync(versionId) ?? throw Oops.Bah("模板版本不存在");
if (version.VersionStatus != ContractTemplateVersionStatus.Draft) throw Oops.Bah("只有草稿版本可维护变量");
}
private async Task EnsureVariableUnique(long versionId, string code, long id)
{
if (await Context.Queryable<ObTemplateVariable>().AnyAsync(it => it.TemplateVersionId == versionId && it.VariableCode == code && it.Id != id)) throw Oops.Bah("该版本变量编码已存在");
} public async Task<SqlSugarPagedList<ObContractScope>> ScopePage(OnboardingContractScopePageInput input)
{
return await Context.Queryable<ObContractScope>()
.WhereIF(input.CompanyId.HasValue, it => it.CompanyId == input.CompanyId.Value)
.WhereIF(input.PositionId.HasValue, it => it.PositionId == input.PositionId.Value)
.WhereIF(input.TemplateId.HasValue, it => it.TemplateId == input.TemplateId.Value)
.OrderBy(it => it.SortCode).ToPagedListAsync(input.PageNum, input.PageSize);
}
public async Task AddScope(OnboardingContractScopeAddInput input)
{
await EnsureScopeValid(input.CompanyId, input.PositionId, input.TemplateId, input.TemplateVersionId);
await EnsureScopeUnique(input.CompanyId, input.PositionId, input.TemplateVersionId, 0);
await Context.Insertable(input.Adapt<ObContractScope>()).ExecuteCommandAsync();
}
public async Task EditScope(OnboardingContractScopeEditInput input)
{
if (!await Context.Queryable<ObContractScope>().AnyAsync(it => it.Id == input.Id)) throw Oops.Bah("合同适用范围不存在");
await EnsureScopeValid(input.CompanyId, input.PositionId, input.TemplateId, input.TemplateVersionId);
await EnsureScopeUnique(input.CompanyId, input.PositionId, input.TemplateVersionId, input.Id);
await Context.Updateable(input.Adapt<ObContractScope>()).ExecuteCommandAsync();
}
public async Task DeleteScope(BaseIdListInput input)
{
if (input.Ids.Count > 0) await Context.Deleteable<ObContractScope>().In(input.Ids).ExecuteCommandAsync();
}
private async Task EnsureScopeValid(long companyId, long? positionId, long templateId, long versionId)
{
if (!await Context.Queryable<ObCompany>().AnyAsync(it => it.Id == companyId && it.Status == "enable")) throw Oops.Bah("所属公司不存在或未启用");
if (positionId.HasValue && !await Context.Queryable<ObPosition>().AnyAsync(it => it.Id == positionId.Value && it.CompanyId == companyId && it.Status == "enable")) throw Oops.Bah("岗位不存在、未启用或不属于该公司");
var version = await Context.Queryable<ObContractTemplateVersion>().InSingleAsync(versionId) ?? throw Oops.Bah("合同版本不存在");
if (version.TemplateId != templateId || version.VersionStatus != ContractTemplateVersionStatus.Enabled) throw Oops.Bah("只能配置该模板的已启用合同版本");
}
private async Task EnsureScopeUnique(long companyId, long? positionId, long versionId, long id)
{
if (await Context.Queryable<ObContractScope>().AnyAsync(it => it.CompanyId == companyId && it.PositionId == positionId && it.TemplateVersionId == versionId && it.Id != id)) throw Oops.Bah("该公司、岗位和合同版本的适用范围已存在");
}}