代码优化
This commit is contained in:
@@ -17,6 +17,18 @@ namespace SimpleAdmin.Application;
|
||||
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||
public class ObContractTemplate : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 合同模板所属入职业务公司 Id。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板所属入职业务部门 Id。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板业务编码。
|
||||
/// </summary>
|
||||
@@ -36,4 +48,28 @@ public class ObContractTemplate : BaseEntity
|
||||
/// 模板用途说明。
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属公司名称,仅用于接口展示。
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string CompanyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门名称,仅用于接口展示。
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string DepartmentName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位 Id 列表,仅用于接口展示和编辑回显。
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<long> PositionIds { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位名称列表,仅用于接口展示。
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<string> PositionNames { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板适用岗位关系,用于限制合同模板可被哪些岗位的入职模板选择。
|
||||
/// </summary>
|
||||
[SugarTable("ob_contract_template_position", TableDescription = "合同模板适用岗位")]
|
||||
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||
public class ObContractTemplatePosition : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 合同模板主档案 Id。
|
||||
/// </summary>
|
||||
public long ContractTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位 Id。
|
||||
/// </summary>
|
||||
public long PositionId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务部门档案,属于某家入职公司。
|
||||
/// </summary>
|
||||
[SugarTable("ob_department", TableDescription = "入职业务部门")]
|
||||
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||
public class ObDepartment : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属入职业务公司 Id。
|
||||
/// </summary>
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门业务编码。
|
||||
/// </summary>
|
||||
public virtual string DepartmentCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称。
|
||||
/// </summary>
|
||||
public virtual string DepartmentName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序码。
|
||||
/// </summary>
|
||||
public int SortCode { get; set; } = 99;
|
||||
|
||||
/// <summary>
|
||||
/// 业务备注。
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属公司名称 (展示用)
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string CompanyName { get; set; }
|
||||
}
|
||||
@@ -37,6 +37,12 @@ public class ObEmployee : BaseEntity
|
||||
/// </summary>
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属入职业务部门 Id。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门名称。
|
||||
/// </summary>
|
||||
|
||||
@@ -22,6 +22,12 @@ public class ObOnboardingTask : BaseEntity
|
||||
/// </summary>
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属入职业务部门 Id。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入职任务所属岗位 Id。
|
||||
/// </summary>
|
||||
@@ -83,6 +89,12 @@ public class ObOnboardingTask : BaseEntity
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string CompanyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称 (展示用)
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string DepartmentName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位名称 (展示用)
|
||||
/// </summary>
|
||||
@@ -94,5 +106,4 @@ public class ObOnboardingTask : BaseEntity
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string OnboardingTemplateName { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,14 @@ public class ObOnboardingTemplate : BaseEntity
|
||||
/// </summary>
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属入职业务部门 Id。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板用途和适用范围说明。
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务岗位档案,保存岗位默认合同期限、试用期及业务参数。
|
||||
/// 入职业务岗位档案,保存岗位所属部门、默认合同期限、试用期及业务参数。
|
||||
/// </summary>
|
||||
[SugarTable("ob_position", TableDescription = "入职业务岗位")]
|
||||
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||
@@ -22,6 +22,12 @@ public class ObPosition : BaseEntity
|
||||
/// </summary>
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属入职业务部门 Id。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位业务编码。
|
||||
/// </summary>
|
||||
@@ -51,4 +57,16 @@ public class ObPosition : BaseEntity
|
||||
/// 业务备注。
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属公司名称 (展示用)
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string CompanyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门名称 (展示用)
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public string DepartmentName { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实现入职审核业务能力。
|
||||
/// 实现入职任务审核与退回业务能力。
|
||||
/// </summary>
|
||||
public class OnboardingAuditService : IOnboardingAuditService
|
||||
{
|
||||
@@ -47,8 +47,17 @@ public class OnboardingAuditService : IOnboardingAuditService
|
||||
var employee = employeeInfo.Adapt<ObEmployee>();
|
||||
employee.Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
||||
employee.CompanyId = task.CompanyId;
|
||||
employee.DepartmentId = task.DepartmentId;
|
||||
employee.PositionId = task.PositionId;
|
||||
employee.DepartmentName = employeeInfo.OnboardingDepartment;
|
||||
if (task.DepartmentId.HasValue)
|
||||
{
|
||||
var dept = await _db.Queryable<ObDepartment>().InSingleAsync(task.DepartmentId.Value);
|
||||
employee.DepartmentName = dept?.DepartmentName ?? employeeInfo.OnboardingDepartment;
|
||||
}
|
||||
else
|
||||
{
|
||||
employee.DepartmentName = employeeInfo.OnboardingDepartment;
|
||||
}
|
||||
employee.College = employeeInfo.GraduatedSchool;
|
||||
employee.BirthDate = ParseDate(employeeInfo.Birthday);
|
||||
employee.ContactAddress = employeeInfo.CurrentAddress;
|
||||
@@ -68,13 +77,26 @@ public class OnboardingAuditService : IOnboardingAuditService
|
||||
await _db.Ado.BeginTranAsync();
|
||||
try
|
||||
{
|
||||
var updatedRows = await _db.Updateable<ObOnboardingTask>()
|
||||
.SetColumns(item => new ObOnboardingTask
|
||||
{
|
||||
EmployeeId = employee.Id,
|
||||
TaskStatus = OnboardingTaskStatus.Approved,
|
||||
AuditedAt = DateTime.Now
|
||||
})
|
||||
.Where(item => item.Id == task.Id
|
||||
&& item.TaskStatus == OnboardingTaskStatus.Reviewing
|
||||
&& item.EmployeeId == null)
|
||||
.ExecuteCommandAsync();
|
||||
if (updatedRows != 1)
|
||||
throw Oops.Bah("该入职任务已被其他审核人处理,请刷新后查看");
|
||||
|
||||
await _snapshotService.Build(task.Id, employeeInfo);
|
||||
await _db.Insertable(employee).ExecuteCommandAsync();
|
||||
|
||||
task.EmployeeId = employee.Id;
|
||||
task.TaskStatus = OnboardingTaskStatus.Approved;
|
||||
task.AuditedAt = DateTime.Now;
|
||||
await _db.Updateable(task).ExecuteCommandAsync();
|
||||
await WriteAudit(task, OnboardingAuditAction.Approve, input.AuditOpinion);
|
||||
await _db.Ado.CommitTranAsync();
|
||||
}
|
||||
@@ -96,10 +118,34 @@ public class OnboardingAuditService : IOnboardingAuditService
|
||||
}
|
||||
|
||||
var task = await GetAuditableTask(input.TaskId);
|
||||
task.TaskStatus = OnboardingTaskStatus.Rejected;
|
||||
task.RejectReason = input.AuditOpinion;
|
||||
await _db.Updateable(task).ExecuteCommandAsync();
|
||||
await WriteAudit(task, OnboardingAuditAction.Reject, input.AuditOpinion);
|
||||
await _db.Ado.BeginTranAsync();
|
||||
try
|
||||
{
|
||||
var auditedAt = DateTime.Now;
|
||||
var updatedRows = await _db.Updateable<ObOnboardingTask>()
|
||||
.SetColumns(item => new ObOnboardingTask
|
||||
{
|
||||
TaskStatus = OnboardingTaskStatus.Rejected,
|
||||
AuditedAt = auditedAt,
|
||||
RejectReason = input.AuditOpinion
|
||||
})
|
||||
.Where(item => item.Id == task.Id
|
||||
&& item.TaskStatus == OnboardingTaskStatus.Reviewing)
|
||||
.ExecuteCommandAsync();
|
||||
if (updatedRows != 1)
|
||||
throw Oops.Bah("该入职任务已被其他审核人处理,请刷新后查看");
|
||||
|
||||
task.TaskStatus = OnboardingTaskStatus.Rejected;
|
||||
task.AuditedAt = auditedAt;
|
||||
task.RejectReason = input.AuditOpinion;
|
||||
await WriteAudit(task, OnboardingAuditAction.Reject, input.AuditOpinion);
|
||||
await _db.Ado.CommitTranAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _db.Ado.RollbackTranAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ObOnboardingTask> GetAuditableTask(long taskId)
|
||||
@@ -113,19 +159,8 @@ public class OnboardingAuditService : IOnboardingAuditService
|
||||
|
||||
var config = await _config.GetByConfigKey(CateGoryConst.CONFIG_BIZ_DEFINE, "onboard.reviewer.userIds")
|
||||
?? throw Oops.Bah("请配置入职审核人");
|
||||
using var configJson = global::System.Text.Json.JsonDocument.Parse(config.ConfigValue);
|
||||
// 审核人由全局配置与公司专属配置合并得出,不依赖独立业务表。
|
||||
var reviewerIds = new List<long>();
|
||||
if (configJson.RootElement.TryGetProperty("all", out var allReviewers))
|
||||
{
|
||||
reviewerIds.AddRange(allReviewers.EnumerateArray().Select(item => item.GetInt64()));
|
||||
}
|
||||
|
||||
if (configJson.RootElement.TryGetProperty("company", out var companyReviewers)
|
||||
&& companyReviewers.TryGetProperty(task.CompanyId.ToString(), out var reviewers))
|
||||
{
|
||||
reviewerIds.AddRange(reviewers.EnumerateArray().Select(item => item.GetInt64()));
|
||||
}
|
||||
var reviewerIds = OnboardingReviewerConfigParser.Parse(config.ConfigValue, task.CompanyId);
|
||||
|
||||
if (!reviewerIds.Contains(UserManager.UserId))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 解析入职审核人系统配置,统一支持全局审核人和公司专属审核人。
|
||||
/// </summary>
|
||||
internal static class OnboardingReviewerConfigParser
|
||||
{
|
||||
private const string ConfigKey = "onboard.reviewer.userIds";
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定公司的全部有效审核人用户 Id。
|
||||
/// </summary>
|
||||
public static HashSet<long> Parse(string configValue, long companyId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(configValue))
|
||||
throw Oops.Bah($"系统配置 {ConfigKey} 不能为空");
|
||||
|
||||
try
|
||||
{
|
||||
using var document = global::System.Text.Json.JsonDocument.Parse(configValue);
|
||||
if (document.RootElement.ValueKind != global::System.Text.Json.JsonValueKind.Object)
|
||||
throw new global::System.Text.Json.JsonException();
|
||||
|
||||
var reviewerIds = new HashSet<long>();
|
||||
if (document.RootElement.TryGetProperty("all", out var allReviewers))
|
||||
AddReviewers(allReviewers, reviewerIds);
|
||||
|
||||
if (document.RootElement.TryGetProperty("company", out var companyReviewers))
|
||||
{
|
||||
if (companyReviewers.ValueKind != global::System.Text.Json.JsonValueKind.Object)
|
||||
throw new global::System.Text.Json.JsonException();
|
||||
|
||||
if (companyReviewers.TryGetProperty(companyId.ToString(), out var reviewers))
|
||||
AddReviewers(reviewers, reviewerIds);
|
||||
}
|
||||
|
||||
return reviewerIds;
|
||||
}
|
||||
catch (global::System.Text.Json.JsonException)
|
||||
{
|
||||
throw Oops.Bah(
|
||||
$"系统配置 {ConfigKey} 格式不正确,应为包含 all 数组和 company 对象的 JSON");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 JSON 数组中的审核人 Id 加入集合,并拒绝非整数配置。
|
||||
/// </summary>
|
||||
private static void AddReviewers(
|
||||
global::System.Text.Json.JsonElement element,
|
||||
HashSet<long> reviewerIds)
|
||||
{
|
||||
if (element.ValueKind != global::System.Text.Json.JsonValueKind.Array)
|
||||
throw new global::System.Text.Json.JsonException();
|
||||
|
||||
foreach (var reviewer in element.EnumerateArray())
|
||||
{
|
||||
if (!reviewer.TryGetInt64(out var reviewerId) || reviewerId <= 0)
|
||||
throw new global::System.Text.Json.JsonException();
|
||||
|
||||
reviewerIds.Add(reviewerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,21 @@ public class OnboardingCompanyService : DbRepository<ObCompany>, IOnboardingComp
|
||||
if (hasPositions)
|
||||
throw Oops.Bah("公司下仍有岗位,不能删除");
|
||||
|
||||
var hasDepartments = await Context.Queryable<ObDepartment>()
|
||||
.AnyAsync(item => input.Ids.Contains(item.CompanyId));
|
||||
if (hasDepartments)
|
||||
throw Oops.Bah("公司下仍有部门,不能删除");
|
||||
|
||||
var hasOnboardingTemplates = await Context.Queryable<ObOnboardingTemplate>()
|
||||
.AnyAsync(item => input.Ids.Contains(item.CompanyId));
|
||||
if (hasOnboardingTemplates)
|
||||
throw Oops.Bah("公司已被入职模板引用,不能删除");
|
||||
|
||||
var hasContractTemplates = await Context.Queryable<ObContractTemplate>()
|
||||
.AnyAsync(item => item.CompanyId.HasValue && input.Ids.Contains(item.CompanyId.Value));
|
||||
if (hasContractTemplates)
|
||||
throw Oops.Bah("公司已被合同模板引用,不能删除");
|
||||
|
||||
var hasTasks = await Context.Queryable<ObOnboardingTask>()
|
||||
.AnyAsync(it => input.Ids.Contains(it.CompanyId));
|
||||
if (hasTasks)
|
||||
|
||||
+9
@@ -42,6 +42,15 @@ public class OnboardingContractDocumentService : IOnboardingContractDocumentServ
|
||||
var variables = await _db.Queryable<ObTaskContractVariable>()
|
||||
.Where(item => item.TaskContractId == contract.Id)
|
||||
.ToListAsync();
|
||||
var duplicateVariable = variables
|
||||
.GroupBy(item => item.VariableCode, StringComparer.OrdinalIgnoreCase)
|
||||
.FirstOrDefault(group => group.Count() > 1);
|
||||
if (duplicateVariable != null)
|
||||
{
|
||||
throw Oops.Bah(
|
||||
$"合同变量快照“{duplicateVariable.Key}”存在重复数据,请联系管理员修复后重试");
|
||||
}
|
||||
|
||||
var missing = definitions
|
||||
.Where(item => item.Required)
|
||||
.FirstOrDefault(definition => string.IsNullOrWhiteSpace(
|
||||
|
||||
+4
-1
@@ -136,7 +136,10 @@ public class OnboardingContractSnapshotService : IOnboardingContractSnapshotServ
|
||||
if (!string.IsNullOrWhiteSpace(str)) return str;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (global::System.Text.Json.JsonException)
|
||||
{
|
||||
throw Oops.Bah("员工补充变量数据格式异常,请重新保存入职登记表后再生成合同");
|
||||
}
|
||||
}
|
||||
|
||||
return dynamicValues.TryGetValue(sourceField, out var dynamicValue)
|
||||
|
||||
+56
@@ -5,6 +5,21 @@ namespace SimpleAdmin.Application;
|
||||
/// </summary>
|
||||
public class OnboardingContractTemplatePageInput : BasePageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属公司 Id。
|
||||
/// </summary>
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门 Id。
|
||||
/// </summary>
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位 Id。
|
||||
/// </summary>
|
||||
public long? PositionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板名称。
|
||||
/// </summary>
|
||||
@@ -26,6 +41,23 @@ public class OnboardingContractTemplatePageInput : BasePageInput
|
||||
/// </summary>
|
||||
public class OnboardingContractTemplateAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属公司 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "所属公司不能为空")]
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "所属部门不能为空")]
|
||||
public long DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板适用的岗位 Id 列表。
|
||||
/// </summary>
|
||||
public List<long> PositionIds { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 合同模板业务编码。
|
||||
/// </summary>
|
||||
@@ -66,3 +98,27 @@ public class OnboardingContractTemplateEditInput : OnboardingContractTemplateAdd
|
||||
[IdNotNull(ErrorMessage = "模板Id不能为空")]
|
||||
public long Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询公司、部门及岗位可选合同模板参数。
|
||||
/// </summary>
|
||||
public class OnboardingContractTemplateAvailableInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属公司 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "所属公司不能为空")]
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "所属部门不能为空")]
|
||||
public long DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入职模板已选择的岗位 Id 列表。
|
||||
/// 返回结果必须同时适用于这些岗位。
|
||||
/// </summary>
|
||||
public List<long> PositionIds { get; set; } = new();
|
||||
}
|
||||
|
||||
+6
-1
@@ -15,6 +15,11 @@ public interface IOnboardingContractTemplateService : ITransient
|
||||
/// </summary>
|
||||
Task<ObContractTemplate> Detail(BaseIdInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 查询同时适用于指定公司、部门和全部已选岗位的启用合同模板。
|
||||
/// </summary>
|
||||
Task<List<ObContractTemplate>> ListAvailable(OnboardingContractTemplateAvailableInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 新增合同模板主档案。
|
||||
/// </summary>
|
||||
@@ -59,4 +64,4 @@ public interface IOnboardingContractTemplateService : ITransient
|
||||
/// 编辑并确认模板版本中的变量定义。
|
||||
/// </summary>
|
||||
Task EditVariable(OnboardingTemplateVariableEditInput input);
|
||||
}
|
||||
}
|
||||
|
||||
+295
-5
@@ -17,15 +17,30 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
/// </summary>
|
||||
public async Task<SqlSugarPagedList<ObContractTemplate>> Page(OnboardingContractTemplatePageInput input)
|
||||
{
|
||||
return await Context.Queryable<ObContractTemplate>()
|
||||
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)
|
||||
.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>
|
||||
@@ -33,7 +48,47 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
/// </summary>
|
||||
public async Task<ObContractTemplate> Detail(BaseIdInput input)
|
||||
{
|
||||
return await GetByIdAsync(input.Id) ?? throw Oops.Bah("合同模板不存在");
|
||||
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>
|
||||
@@ -41,8 +96,24 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
/// </summary>
|
||||
public async Task Add(OnboardingContractTemplateAddInput input)
|
||||
{
|
||||
NormalizeInput(input);
|
||||
await ValidateBusinessScope(input.CompanyId, input.DepartmentId, input.PositionIds);
|
||||
await EnsureCodeUnique(input.TemplateCode, 0);
|
||||
await InsertAsync(input.Adapt<ObContractTemplate>());
|
||||
|
||||
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>
|
||||
@@ -51,8 +122,29 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
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);
|
||||
await UpdateAsync(input.Adapt<ObContractTemplate>());
|
||||
|
||||
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>
|
||||
@@ -95,6 +187,10 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
.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();
|
||||
}
|
||||
@@ -182,6 +278,15 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
|
||||
// 严格比对变量字典库,防止手误和非法占位符
|
||||
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)
|
||||
@@ -336,4 +441,189 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务部门分页查询参数。
|
||||
/// </summary>
|
||||
public class OnboardingDepartmentPageInput : BasePageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 入职业务公司 Id。
|
||||
/// </summary>
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门编码。
|
||||
/// </summary>
|
||||
public virtual string DepartmentCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称。
|
||||
/// </summary>
|
||||
public virtual string DepartmentName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启用状态。
|
||||
/// </summary>
|
||||
public string Status { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增入职业务部门参数。
|
||||
/// </summary>
|
||||
public class OnboardingDepartmentAddInput : ObDepartment
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属公司 Id。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "所属公司不能为空")]
|
||||
public new long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门业务编码。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "部门编码不能为空")]
|
||||
public override string DepartmentCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 部门名称。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "部门名称不能为空")]
|
||||
public override string DepartmentName { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑入职业务部门参数。
|
||||
/// </summary>
|
||||
public class OnboardingDepartmentEditInput : OnboardingDepartmentAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 业务数据 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "部门Id不能为空")]
|
||||
public override long Id { get; set; }
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 定义入职业务部门业务能力。
|
||||
/// </summary>
|
||||
public interface IOnboardingDepartmentService : ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页查询入职业务部门。
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<ObDepartment>> Page(OnboardingDepartmentPageInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定公司下全部已启用部门列表。
|
||||
/// </summary>
|
||||
Task<List<ObDepartment>> ListEnabled(long companyId);
|
||||
|
||||
/// <summary>
|
||||
/// 查询部门详情。
|
||||
/// </summary>
|
||||
Task<ObDepartment> Detail(BaseIdInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 新增入职业务部门。
|
||||
/// </summary>
|
||||
Task Add(OnboardingDepartmentAddInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 编辑入职业务部门。
|
||||
/// </summary>
|
||||
Task Edit(OnboardingDepartmentEditInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除部门。
|
||||
/// </summary>
|
||||
Task Delete(BaseIdListInput input);
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实现入职业务部门业务能力。
|
||||
/// </summary>
|
||||
public class OnboardingDepartmentService : DbRepository<ObDepartment>, IOnboardingDepartmentService
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页查询入职业务部门。
|
||||
/// </summary>
|
||||
public async Task<SqlSugarPagedList<ObDepartment>> Page(OnboardingDepartmentPageInput input)
|
||||
{
|
||||
var list = await Context.Queryable<ObDepartment>()
|
||||
.WhereIF(input.CompanyId.HasValue, it => it.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.DepartmentCode), it => it.DepartmentCode.Contains(input.DepartmentCode))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.DepartmentName), it => it.DepartmentName.Contains(input.DepartmentName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Status), it => it.Status == input.Status)
|
||||
.OrderByIF(!string.IsNullOrWhiteSpace(input.SortField), $"{input.SortField} {input.SortOrder}")
|
||||
.OrderBy(it => it.SortCode)
|
||||
.OrderBy(it => it.DepartmentName)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
|
||||
if (list.List != null && list.List.Any())
|
||||
{
|
||||
var companyIds = list.List.Select(d => d.CompanyId).Distinct().ToList();
|
||||
var companies = await Context.Queryable<ObCompany>()
|
||||
.Where(c => companyIds.Contains(c.Id))
|
||||
.ToListAsync();
|
||||
var companyMap = companies.ToDictionary(c => c.Id, c => c.CompanyName);
|
||||
|
||||
foreach (var item in list.List)
|
||||
{
|
||||
if (companyMap.TryGetValue(item.CompanyId, out var cName))
|
||||
{
|
||||
item.CompanyName = cName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定公司下全部已启用部门列表。
|
||||
/// </summary>
|
||||
public Task<List<ObDepartment>> ListEnabled(long companyId)
|
||||
{
|
||||
return Context.Queryable<ObDepartment>()
|
||||
.Where(it => it.CompanyId == companyId && it.Status == "enable")
|
||||
.OrderBy(it => it.SortCode)
|
||||
.OrderBy(it => it.DepartmentName)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询部门详情。
|
||||
/// </summary>
|
||||
public async Task<ObDepartment> Detail(BaseIdInput input)
|
||||
{
|
||||
return await GetByIdAsync(input.Id) ?? throw Oops.Bah("部门不存在");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增入职业务部门。
|
||||
/// </summary>
|
||||
public async Task Add(OnboardingDepartmentAddInput input)
|
||||
{
|
||||
NormalizeInput(input);
|
||||
await EnsureCompanyExists(input.CompanyId);
|
||||
await EnsureCodeUnique(input.CompanyId, input.DepartmentCode, 0);
|
||||
await InsertAsync(input.Adapt<ObDepartment>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑入职业务部门。
|
||||
/// </summary>
|
||||
public async Task Edit(OnboardingDepartmentEditInput input)
|
||||
{
|
||||
NormalizeInput(input);
|
||||
var department = await Detail(new BaseIdInput { Id = input.Id });
|
||||
await EnsureCompanyCanChange(department, input.CompanyId);
|
||||
await EnsureCompanyExists(input.CompanyId);
|
||||
await EnsureCodeUnique(input.CompanyId, input.DepartmentCode, input.Id);
|
||||
await UpdateAsync(input.Adapt<ObDepartment>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除未被岗位或任务引用的部门。
|
||||
/// </summary>
|
||||
public async Task Delete(BaseIdListInput input)
|
||||
{
|
||||
if (input.Ids.Count == 0)
|
||||
return;
|
||||
|
||||
var hasPositions = await Context.Queryable<ObPosition>()
|
||||
.AnyAsync(it => it.DepartmentId.HasValue && input.Ids.Contains(it.DepartmentId.Value));
|
||||
if (hasPositions)
|
||||
throw Oops.Bah("部门下已有岗位,不能删除");
|
||||
|
||||
var hasTasks = await Context.Queryable<ObOnboardingTask>()
|
||||
.AnyAsync(it => it.DepartmentId.HasValue && input.Ids.Contains(it.DepartmentId.Value));
|
||||
if (hasTasks)
|
||||
throw Oops.Bah("部门已有入职任务,不能删除");
|
||||
|
||||
var hasEmployees = await Context.Queryable<ObEmployee>()
|
||||
.AnyAsync(it => it.DepartmentId.HasValue && input.Ids.Contains(it.DepartmentId.Value));
|
||||
if (hasEmployees)
|
||||
throw Oops.Bah("部门已有正式员工,不能删除");
|
||||
|
||||
var hasOnboardingTemplates = await Context.Queryable<ObOnboardingTemplate>()
|
||||
.AnyAsync(item => item.DepartmentId.HasValue && input.Ids.Contains(item.DepartmentId.Value));
|
||||
if (hasOnboardingTemplates)
|
||||
throw Oops.Bah("部门已被入职模板引用,不能删除");
|
||||
|
||||
var hasContractTemplates = await Context.Queryable<ObContractTemplate>()
|
||||
.AnyAsync(item => item.DepartmentId.HasValue && input.Ids.Contains(item.DepartmentId.Value));
|
||||
if (hasContractTemplates)
|
||||
throw Oops.Bah("部门已被合同模板引用,不能删除");
|
||||
|
||||
await DeleteByIdsAsync(input.Ids.Cast<object>().ToArray());
|
||||
}
|
||||
|
||||
private async Task EnsureCompanyExists(long companyId)
|
||||
{
|
||||
var exists = await Context.Queryable<ObCompany>()
|
||||
.AnyAsync(it => it.Id == companyId && it.Status == "enable");
|
||||
if (!exists)
|
||||
throw Oops.Bah("所属公司不存在、已删除或未启用");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标准化部门输入并拒绝非法状态。
|
||||
/// </summary>
|
||||
private static void NormalizeInput(OnboardingDepartmentAddInput input)
|
||||
{
|
||||
input.DepartmentCode = input.DepartmentCode?.Trim();
|
||||
input.DepartmentName = input.DepartmentName?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(input.DepartmentCode)
|
||||
|| string.IsNullOrWhiteSpace(input.DepartmentName))
|
||||
{
|
||||
throw Oops.Bah("请填写部门编码和部门名称");
|
||||
}
|
||||
|
||||
if (input.Status != "enable" && input.Status != "disable")
|
||||
throw Oops.Bah("部门状态不正确");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 已产生下级或业务引用的部门不允许直接变更所属公司。
|
||||
/// </summary>
|
||||
private async Task EnsureCompanyCanChange(ObDepartment department, long companyId)
|
||||
{
|
||||
if (department.CompanyId == companyId)
|
||||
return;
|
||||
|
||||
var hasPosition = await Context.Queryable<ObPosition>()
|
||||
.AnyAsync(item => item.DepartmentId == department.Id);
|
||||
var hasTask = await Context.Queryable<ObOnboardingTask>()
|
||||
.AnyAsync(item => item.DepartmentId == department.Id);
|
||||
var hasEmployee = await Context.Queryable<ObEmployee>()
|
||||
.AnyAsync(item => item.DepartmentId == department.Id);
|
||||
var hasOnboardingTemplate = await Context.Queryable<ObOnboardingTemplate>()
|
||||
.AnyAsync(item => item.DepartmentId == department.Id);
|
||||
var hasContractTemplate = await Context.Queryable<ObContractTemplate>()
|
||||
.AnyAsync(item => item.DepartmentId == department.Id);
|
||||
if (hasPosition || hasTask || hasEmployee || hasOnboardingTemplate || hasContractTemplate)
|
||||
throw Oops.Bah("部门已有岗位、模板、任务或员工引用,不能直接变更所属公司");
|
||||
}
|
||||
|
||||
private async Task EnsureCodeUnique(long companyId, string code, long id)
|
||||
{
|
||||
var exists = await Context.Queryable<ObDepartment>()
|
||||
.AnyAsync(it => it.CompanyId == companyId && it.DepartmentCode == code && it.Id != id);
|
||||
if (exists)
|
||||
throw Oops.Bah("该公司下部门编码已存在");
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,12 @@ public class OnboardingEmployeePageInput : BasePageInput
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位 Id;为空时表示公司级配置。
|
||||
/// 入职业务部门 Id。
|
||||
/// </summary>
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位 Id。
|
||||
/// </summary>
|
||||
public long? PositionId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,33 +1,61 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>仅展示审核通过后创建的正式员工档案;入职资料的来源任务可追溯。</summary>
|
||||
/// <summary>
|
||||
/// 实现正式员工档案查询业务能力。
|
||||
/// 实现正式员工档案查询业务能力,仅展示审核通过后创建的正式员工。
|
||||
/// </summary>
|
||||
public class OnboardingEmployeeService : IOnboardingEmployeeService
|
||||
{
|
||||
private readonly SqlSugarScope _db;
|
||||
public OnboardingEmployeeService() => _db = DbContext.DB;
|
||||
|
||||
public OnboardingEmployeeService()
|
||||
{
|
||||
_db = DbContext.DB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询审核通过后生成的正式员工档案。
|
||||
/// </summary>
|
||||
public Task<SqlSugarPagedList<ObEmployee>> Page(OnboardingEmployeePageInput input) => _db.Queryable<ObEmployee>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.EmployeeName), x => x.EmployeeName.Contains(input.EmployeeName) || x.IdCardNumber.Contains(input.EmployeeName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Phone), x => x.Phone.Contains(input.Phone))
|
||||
.WhereIF(input.CompanyId.HasValue, x => x.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(input.PositionId.HasValue, x => x.PositionId == input.PositionId.Value)
|
||||
.OrderByDescending(x => x.CreateTime).ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
public Task<SqlSugarPagedList<ObEmployee>> Page(OnboardingEmployeePageInput input)
|
||||
{
|
||||
return _db.Queryable<ObEmployee>()
|
||||
.WhereIF(
|
||||
!string.IsNullOrWhiteSpace(input.EmployeeName),
|
||||
item => item.EmployeeName.Contains(input.EmployeeName)
|
||||
|| item.IdCardNumber.Contains(input.EmployeeName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Phone), item => item.Phone.Contains(input.Phone))
|
||||
.WhereIF(input.CompanyId.HasValue, item => item.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(input.DepartmentId.HasValue, item => item.DepartmentId == input.DepartmentId.Value)
|
||||
.WhereIF(input.PositionId.HasValue, item => item.PositionId == input.PositionId.Value)
|
||||
.OrderByDescending(item => item.CreateTime)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询正式员工及其来源任务、合同和审核记录。
|
||||
/// </summary>
|
||||
public async Task<OnboardingEmployeeDetailOutput> Detail(BaseIdInput input)
|
||||
{
|
||||
var employee = await _db.Queryable<ObEmployee>().InSingleAsync(input.Id) ?? throw Oops.Bah("正式员工不存在");
|
||||
var employee = await _db.Queryable<ObEmployee>().InSingleAsync(input.Id)
|
||||
?? throw Oops.Bah("正式员工不存在");
|
||||
var task = await _db.Queryable<ObOnboardingTask>().FirstAsync(x => x.EmployeeId == employee.Id);
|
||||
var contracts = task == null ? new List<ObTaskContract>() : await _db.Queryable<ObTaskContract>().Where(x => x.TaskId == task.Id).ToListAsync();
|
||||
var audits = task == null ? new List<ObTaskAudit>() : await _db.Queryable<ObTaskAudit>().Where(x => x.TaskId == task.Id).OrderByDescending(x => x.AuditedAt).ToListAsync();
|
||||
return new OnboardingEmployeeDetailOutput { Employee = employee, Task = task, Contracts = contracts, Audits = audits };
|
||||
var contracts = task == null
|
||||
? new List<ObTaskContract>()
|
||||
: await _db.Queryable<ObTaskContract>()
|
||||
.Where(item => item.TaskId == task.Id)
|
||||
.ToListAsync();
|
||||
var audits = task == null
|
||||
? new List<ObTaskAudit>()
|
||||
: await _db.Queryable<ObTaskAudit>()
|
||||
.Where(item => item.TaskId == task.Id)
|
||||
.OrderByDescending(item => item.AuditedAt)
|
||||
.ToListAsync();
|
||||
|
||||
return new OnboardingEmployeeDetailOutput
|
||||
{
|
||||
Employee = employee,
|
||||
Task = task,
|
||||
Contracts = contracts,
|
||||
Audits = audits
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+64
-30
@@ -25,10 +25,32 @@ public class OnboardingTaskLifecycleService : IOnboardingTaskLifecycleService
|
||||
throw Oops.Bah("仅审核通过任务可归档");
|
||||
}
|
||||
|
||||
task.TaskStatus = OnboardingTaskStatus.Archived;
|
||||
task.ArchivedAt = DateTime.Now;
|
||||
await _db.Updateable(task).ExecuteCommandAsync();
|
||||
await Log(task, OnboardingAuditAction.Archive, input.AuditOpinion);
|
||||
await _db.Ado.BeginTranAsync();
|
||||
try
|
||||
{
|
||||
var archivedAt = DateTime.Now;
|
||||
var updatedRows = await _db.Updateable<ObOnboardingTask>()
|
||||
.SetColumns(item => new ObOnboardingTask
|
||||
{
|
||||
TaskStatus = OnboardingTaskStatus.Archived,
|
||||
ArchivedAt = archivedAt
|
||||
})
|
||||
.Where(item => item.Id == task.Id
|
||||
&& item.TaskStatus == OnboardingTaskStatus.Approved)
|
||||
.ExecuteCommandAsync();
|
||||
if (updatedRows != 1)
|
||||
throw Oops.Bah("该入职任务状态已变化,请刷新后重试");
|
||||
|
||||
task.TaskStatus = OnboardingTaskStatus.Archived;
|
||||
task.ArchivedAt = archivedAt;
|
||||
await Log(task, OnboardingAuditAction.Archive, input.AuditOpinion);
|
||||
await _db.Ado.CommitTranAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _db.Ado.RollbackTranAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -37,22 +59,43 @@ public class OnboardingTaskLifecycleService : IOnboardingTaskLifecycleService
|
||||
public async Task Revoke(OnboardingAuditActionInput input)
|
||||
{
|
||||
var task = await Authorize(input.TaskId);
|
||||
if (task.TaskStatus == OnboardingTaskStatus.Approved || task.TaskStatus == OnboardingTaskStatus.Archived)
|
||||
if (task.TaskStatus == OnboardingTaskStatus.Approved
|
||||
|| task.TaskStatus == OnboardingTaskStatus.Archived
|
||||
|| task.TaskStatus == OnboardingTaskStatus.Revoked)
|
||||
{
|
||||
throw Oops.Bah("已通过或已归档任务不能撤销");
|
||||
throw Oops.Bah("已通过、已归档或已撤销任务不能再次撤销");
|
||||
}
|
||||
|
||||
task.TaskStatus = OnboardingTaskStatus.Revoked;
|
||||
await _db.Updateable(task).ExecuteCommandAsync();
|
||||
await _db.Updateable<ObTaskLink>()
|
||||
.SetColumns(item => new ObTaskLink
|
||||
{
|
||||
LinkStatus = OnboardingLinkStatus.Revoked,
|
||||
RevokedAt = DateTime.Now
|
||||
})
|
||||
.Where(item => item.TaskId == task.Id && item.LinkStatus == OnboardingLinkStatus.Active)
|
||||
.ExecuteCommandAsync();
|
||||
await Log(task, OnboardingAuditAction.Revoke, input.AuditOpinion);
|
||||
await _db.Ado.BeginTranAsync();
|
||||
try
|
||||
{
|
||||
var originalStatus = task.TaskStatus;
|
||||
var revokedAt = DateTime.Now;
|
||||
var updatedRows = await _db.Updateable<ObOnboardingTask>()
|
||||
.SetColumns(item => item.TaskStatus == OnboardingTaskStatus.Revoked)
|
||||
.Where(item => item.Id == task.Id && item.TaskStatus == originalStatus)
|
||||
.ExecuteCommandAsync();
|
||||
if (updatedRows != 1)
|
||||
throw Oops.Bah("该入职任务状态已变化,请刷新后重试");
|
||||
|
||||
await _db.Updateable<ObTaskLink>()
|
||||
.SetColumns(item => new ObTaskLink
|
||||
{
|
||||
LinkStatus = OnboardingLinkStatus.Revoked,
|
||||
RevokedAt = revokedAt
|
||||
})
|
||||
.Where(item => item.TaskId == task.Id
|
||||
&& item.LinkStatus == OnboardingLinkStatus.Active)
|
||||
.ExecuteCommandAsync();
|
||||
task.TaskStatus = OnboardingTaskStatus.Revoked;
|
||||
await Log(task, OnboardingAuditAction.Revoke, input.AuditOpinion);
|
||||
await _db.Ado.CommitTranAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _db.Ado.RollbackTranAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,6 +129,9 @@ public class OnboardingTaskLifecycleService : IOnboardingTaskLifecycleService
|
||||
await _db.Deleteable<ObTaskMaterialRequirement>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObTaskMaterial>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObTaskEmployeeInfo>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObTaskFormRequirement>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObTaskFormValue>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObTaskOperationLog>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObTaskAudit>().Where(x => x.TaskId == task.Id).ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObOnboardingTask>().In(task.Id).ExecuteCommandAsync();
|
||||
await _db.Ado.CommitTranAsync();
|
||||
@@ -103,19 +149,7 @@ public class OnboardingTaskLifecycleService : IOnboardingTaskLifecycleService
|
||||
?? throw Oops.Bah("入职任务不存在");
|
||||
var config = await _config.GetByConfigKey(CateGoryConst.CONFIG_BIZ_DEFINE, "onboard.reviewer.userIds")
|
||||
?? throw Oops.Bah("请配置入职审核人");
|
||||
|
||||
using var configJson = global::System.Text.Json.JsonDocument.Parse(config.ConfigValue);
|
||||
var reviewerIds = new List<long>();
|
||||
if (configJson.RootElement.TryGetProperty("all", out var allReviewers))
|
||||
{
|
||||
reviewerIds.AddRange(allReviewers.EnumerateArray().Select(item => item.GetInt64()));
|
||||
}
|
||||
|
||||
if (configJson.RootElement.TryGetProperty("company", out var companyReviewers)
|
||||
&& companyReviewers.TryGetProperty(task.CompanyId.ToString(), out var reviewers))
|
||||
{
|
||||
reviewerIds.AddRange(reviewers.EnumerateArray().Select(item => item.GetInt64()));
|
||||
}
|
||||
var reviewerIds = OnboardingReviewerConfigParser.Parse(config.ConfigValue, task.CompanyId);
|
||||
|
||||
if (!reviewerIds.Contains(UserManager.UserId))
|
||||
{
|
||||
|
||||
+1
-12
@@ -50,18 +50,7 @@ public class OnboardingMaterialReviewService : IOnboardingMaterialReviewService
|
||||
{
|
||||
var config = await _config.GetByConfigKey(CateGoryConst.CONFIG_BIZ_DEFINE, "onboard.reviewer.userIds")
|
||||
?? throw Oops.Bah("请配置入职审核人");
|
||||
using var configJson = global::System.Text.Json.JsonDocument.Parse(config.ConfigValue);
|
||||
var reviewerIds = new List<long>();
|
||||
if (configJson.RootElement.TryGetProperty("all", out var allReviewers))
|
||||
{
|
||||
reviewerIds.AddRange(allReviewers.EnumerateArray().Select(item => item.GetInt64()));
|
||||
}
|
||||
|
||||
if (configJson.RootElement.TryGetProperty("company", out var companyReviewers)
|
||||
&& companyReviewers.TryGetProperty(task.CompanyId.ToString(), out var reviewers))
|
||||
{
|
||||
reviewerIds.AddRange(reviewers.EnumerateArray().Select(item => item.GetInt64()));
|
||||
}
|
||||
var reviewerIds = OnboardingReviewerConfigParser.Parse(config.ConfigValue, task.CompanyId);
|
||||
|
||||
if (!reviewerIds.Contains(UserManager.UserId))
|
||||
{
|
||||
|
||||
+17
-1
@@ -15,6 +15,11 @@ public class OnboardingTemplatePageInput : BasePageInput
|
||||
/// </summary>
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门 Id。
|
||||
/// </summary>
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位 Id。
|
||||
/// </summary>
|
||||
@@ -51,6 +56,12 @@ public class OnboardingTemplateSaveInput
|
||||
/// </summary>
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "所属部门不能为空")]
|
||||
public long DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位 Id 列表。
|
||||
/// </summary>
|
||||
@@ -165,6 +176,11 @@ public class OnboardingTemplateOutput : ObOnboardingTemplate
|
||||
/// </summary>
|
||||
public string CompanyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门名称。
|
||||
/// </summary>
|
||||
public string DepartmentName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用岗位 Id 列表。
|
||||
/// </summary>
|
||||
@@ -210,4 +226,4 @@ public class OnboardingTemplateDetailOutput : OnboardingTemplateOutput
|
||||
/// 上传材料配置明细。
|
||||
/// </summary>
|
||||
public List<ObOnboardingTemplateMaterial> Materials { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -26,7 +26,10 @@ public interface IOnboardingTemplateService : ITransient
|
||||
Task Delete(BaseIdListInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 查询公司岗位可使用的入职模板。
|
||||
/// 查询公司、部门和岗位可使用的入职模板。
|
||||
/// </summary>
|
||||
Task<List<OnboardingTemplateOutput>> ListEnabled(long companyId, long positionId);
|
||||
}
|
||||
Task<List<OnboardingTemplateOutput>> ListEnabled(
|
||||
long companyId,
|
||||
long departmentId,
|
||||
long positionId);
|
||||
}
|
||||
|
||||
+86
-10
@@ -20,6 +20,7 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword),
|
||||
item => item.TemplateCode.Contains(input.Keyword) || item.TemplateName.Contains(input.Keyword))
|
||||
.WhereIF(input.CompanyId.HasValue, item => item.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(input.DepartmentId.HasValue, item => item.DepartmentId == input.DepartmentId.Value)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Status), item => item.Status == input.Status);
|
||||
|
||||
if (input.PositionId.HasValue)
|
||||
@@ -62,6 +63,8 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
TemplateName = summary.TemplateName,
|
||||
CompanyId = summary.CompanyId,
|
||||
CompanyName = summary.CompanyName,
|
||||
DepartmentId = summary.DepartmentId,
|
||||
DepartmentName = summary.DepartmentName,
|
||||
Description = summary.Description,
|
||||
Status = summary.Status,
|
||||
CreateTime = summary.CreateTime,
|
||||
@@ -101,6 +104,7 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
TemplateCode = input.TemplateCode.Trim(),
|
||||
TemplateName = input.TemplateName.Trim(),
|
||||
CompanyId = input.CompanyId,
|
||||
DepartmentId = input.DepartmentId,
|
||||
Description = input.Description,
|
||||
Status = input.Status
|
||||
};
|
||||
@@ -149,9 +153,6 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
foreach (var id in input.Ids)
|
||||
await DeleteRelations(id);
|
||||
|
||||
await _db.Deleteable<ObOnboardingTemplate>()
|
||||
.Where(it => input.Ids.Contains(it.Id))
|
||||
.ExecuteCommandAsync();
|
||||
await _db.Updateable<ObOnboardingTemplate>()
|
||||
.SetColumns(it => it.IsDelete == true)
|
||||
.Where(it => input.Ids.Contains(it.Id))
|
||||
@@ -167,7 +168,10 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<OnboardingTemplateOutput>> ListEnabled(long companyId, long positionId)
|
||||
public async Task<List<OnboardingTemplateOutput>> ListEnabled(
|
||||
long companyId,
|
||||
long departmentId,
|
||||
long positionId)
|
||||
{
|
||||
var templateIds = await _db.Queryable<ObOnboardingTemplatePosition>()
|
||||
.Where(item => item.PositionId == positionId)
|
||||
@@ -175,6 +179,7 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
.ToListAsync();
|
||||
var templates = await _db.Queryable<ObOnboardingTemplate>()
|
||||
.Where(item => item.CompanyId == companyId
|
||||
&& item.DepartmentId == departmentId
|
||||
&& item.Status == "enable"
|
||||
&& templateIds.Contains(item.Id))
|
||||
.OrderBy(item => item.TemplateName)
|
||||
@@ -187,10 +192,13 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
input.TemplateCode = input.TemplateCode?.Trim();
|
||||
input.TemplateName = input.TemplateName?.Trim();
|
||||
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
||||
input.Contracts = input.Contracts?
|
||||
.GroupBy(item => item.ContractTemplateVersionId)
|
||||
.Select(group => group.First())
|
||||
.ToList() ?? new List<OnboardingTemplateContractInput>();
|
||||
input.Contracts ??= new List<OnboardingTemplateContractInput>();
|
||||
var duplicateContract = input.Contracts
|
||||
.GroupBy(item => item.ContractTemplateId)
|
||||
.FirstOrDefault(group => group.Count() > 1);
|
||||
if (duplicateContract != null)
|
||||
throw Oops.Bah("同一份合同模板在入职模板中只能选择一个版本");
|
||||
|
||||
input.FormFields = input.FormFields?
|
||||
.GroupBy(item => item.FieldId)
|
||||
.Select(group => group.First())
|
||||
@@ -204,10 +212,19 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
throw Oops.Bah("请填写入职模板编码和名称");
|
||||
if (input.CompanyId <= 0)
|
||||
throw Oops.Bah("请选择所属公司");
|
||||
if (input.DepartmentId <= 0)
|
||||
throw Oops.Bah("请选择所属部门");
|
||||
if (input.PositionIds.Count == 0)
|
||||
throw Oops.Bah("请至少选择一个适用岗位");
|
||||
if (input.PositionIds.Any(positionId => positionId <= 0))
|
||||
throw Oops.Bah("适用岗位数据不正确");
|
||||
if (input.Contracts.Count == 0)
|
||||
throw Oops.Bah("请至少选择一份合同文件");
|
||||
if (input.Contracts.Any(item => item.ContractTemplateId <= 0
|
||||
|| item.ContractTemplateVersionId <= 0))
|
||||
{
|
||||
throw Oops.Bah("合同模板或版本数据不正确");
|
||||
}
|
||||
if (input.Status != "enable" && input.Status != "disable")
|
||||
throw Oops.Bah("入职模板状态不正确");
|
||||
if (input.Contracts.Any(item => item.PrintCopies <= 0))
|
||||
@@ -221,12 +238,20 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
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 positionCount = await _db.Queryable<ObPosition>()
|
||||
.CountAsync(item => input.PositionIds.Contains(item.Id)
|
||||
&& item.CompanyId == input.CompanyId
|
||||
&& item.DepartmentId == input.DepartmentId
|
||||
&& item.Status == "enable");
|
||||
if (positionCount != input.PositionIds.Count)
|
||||
throw Oops.Bah("存在不属于该公司或未启用的岗位");
|
||||
throw Oops.Bah("存在未启用或不属于该公司、部门的岗位");
|
||||
|
||||
var versionIds = input.Contracts.Select(item => item.ContractTemplateVersionId).ToList();
|
||||
var versions = await _db.Queryable<ObContractTemplateVersion>()
|
||||
@@ -239,8 +264,43 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
throw Oops.Bah("合同模板与版本不匹配");
|
||||
}
|
||||
|
||||
var contractTemplateIds = input.Contracts
|
||||
.Select(item => item.ContractTemplateId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
var contractTemplates = await _db.Queryable<ObContractTemplate>()
|
||||
.Where(item => contractTemplateIds.Contains(item.Id))
|
||||
.ToListAsync();
|
||||
if (contractTemplates.Count != contractTemplateIds.Count
|
||||
|| contractTemplates.Any(item => item.CompanyId != input.CompanyId
|
||||
|| item.DepartmentId != input.DepartmentId))
|
||||
{
|
||||
throw Oops.Bah("存在不属于当前公司、部门的合同模板");
|
||||
}
|
||||
|
||||
var contractPositionRelations = await _db.Queryable<ObContractTemplatePosition>()
|
||||
.Where(item => contractTemplateIds.Contains(item.ContractTemplateId))
|
||||
.ToListAsync();
|
||||
var unsupportedContract = contractTemplateIds.FirstOrDefault(contractTemplateId =>
|
||||
{
|
||||
var supportedPositionIds = contractPositionRelations
|
||||
.Where(item => item.ContractTemplateId == contractTemplateId)
|
||||
.Select(item => item.PositionId)
|
||||
.ToHashSet();
|
||||
return input.PositionIds.Any(positionId => !supportedPositionIds.Contains(positionId));
|
||||
});
|
||||
if (unsupportedContract > 0)
|
||||
{
|
||||
var contractName = contractTemplates
|
||||
.First(item => item.Id == unsupportedContract)
|
||||
.TemplateName;
|
||||
throw Oops.Bah($"合同模板“{contractName}”不能覆盖当前选择的全部岗位");
|
||||
}
|
||||
|
||||
if (input.Status == "enable")
|
||||
{
|
||||
if (contractTemplates.Any(item => item.Status != "enable"))
|
||||
throw Oops.Bah("启用入职模板前,请先启用全部合同模板");
|
||||
if (versions.Any(item => item.VersionStatus != ContractTemplateVersionStatus.Enabled))
|
||||
throw Oops.Bah("启用入职模板前,请先启用全部合同版本");
|
||||
}
|
||||
@@ -302,6 +362,9 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
await _db.Deleteable<ObOnboardingTemplateMaterial>()
|
||||
.Where(item => item.OnboardingTemplateId == templateId)
|
||||
.ExecuteCommandAsync();
|
||||
await _db.Deleteable<ObOnboardingTemplateFormField>()
|
||||
.Where(item => item.OnboardingTemplateId == templateId)
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
private async Task<List<OnboardingTemplateOutput>> BuildOutputs(List<ObOnboardingTemplate> templates)
|
||||
@@ -311,9 +374,19 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
|
||||
var templateIds = templates.Select(item => item.Id).ToList();
|
||||
var companyIds = templates.Select(item => item.CompanyId).Distinct().ToList();
|
||||
var departmentIds = templates
|
||||
.Where(item => item.DepartmentId.HasValue)
|
||||
.Select(item => item.DepartmentId.Value)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
var companies = await _db.Queryable<ObCompany>()
|
||||
.Where(item => companyIds.Contains(item.Id))
|
||||
.ToListAsync();
|
||||
var departments = departmentIds.Count == 0
|
||||
? new List<ObDepartment>()
|
||||
: await _db.Queryable<ObDepartment>()
|
||||
.Where(item => departmentIds.Contains(item.Id))
|
||||
.ToListAsync();
|
||||
var relations = await _db.Queryable<ObOnboardingTemplatePosition>()
|
||||
.Where(item => templateIds.Contains(item.OnboardingTemplateId))
|
||||
.ToListAsync();
|
||||
@@ -338,6 +411,9 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
TemplateName = template.TemplateName,
|
||||
CompanyId = template.CompanyId,
|
||||
CompanyName = companies.FirstOrDefault(item => item.Id == template.CompanyId)?.CompanyName,
|
||||
DepartmentId = template.DepartmentId,
|
||||
DepartmentName = departments
|
||||
.FirstOrDefault(item => item.Id == template.DepartmentId)?.DepartmentName,
|
||||
Description = template.Description,
|
||||
Status = template.Status,
|
||||
CreateTime = template.CreateTime,
|
||||
@@ -353,4 +429,4 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
||||
};
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -1,4 +1,4 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务岗位分页查询参数。
|
||||
@@ -10,6 +10,11 @@ public class OnboardingPositionPageInput : BasePageInput
|
||||
/// </summary>
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务部门 Id。
|
||||
/// </summary>
|
||||
public long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位编码。
|
||||
/// </summary>
|
||||
@@ -21,7 +26,7 @@ public class OnboardingPositionPageInput : BasePageInput
|
||||
public virtual string PositionName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启用状态或状态筛选条件。
|
||||
/// 启用状态。
|
||||
/// </summary>
|
||||
public string Status { get; set; }
|
||||
}
|
||||
@@ -31,6 +36,18 @@ public class OnboardingPositionPageInput : BasePageInput
|
||||
/// </summary>
|
||||
public class OnboardingPositionAddInput : ObPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属公司 Id。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "所属公司不能为空")]
|
||||
public new long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属部门 Id。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "所属部门不能为空")]
|
||||
public new long? DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 岗位业务编码。
|
||||
/// </summary>
|
||||
@@ -54,4 +71,4 @@ public class OnboardingPositionEditInput : OnboardingPositionAddInput
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "岗位Id不能为空")]
|
||||
public override long Id { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 定义入职业务岗位业务能力。
|
||||
@@ -9,24 +9,34 @@ public interface IOnboardingPositionService : ITransient
|
||||
/// 分页查询入职业务岗位。
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<ObPosition>> Page(OnboardingPositionPageInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定公司下全部已启用岗位。
|
||||
/// </summary>
|
||||
Task<List<ObPosition>> ListEnabled(long companyId);
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定部门下全部已启用岗位。
|
||||
/// </summary>
|
||||
Task<List<ObPosition>> ListEnabledByDepartment(long departmentId);
|
||||
|
||||
/// <summary>
|
||||
/// 查询岗位详情。
|
||||
/// </summary>
|
||||
Task<ObPosition> Detail(BaseIdInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 新增入职业务岗位。
|
||||
/// </summary>
|
||||
Task Add(OnboardingPositionAddInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 编辑入职业务岗位。
|
||||
/// </summary>
|
||||
Task Edit(OnboardingPositionEditInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 删除未被任务或正式员工引用的岗位。
|
||||
/// </summary>
|
||||
Task Delete(BaseIdListInput input);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实现入职业务岗位业务能力。
|
||||
@@ -10,14 +10,45 @@ public class OnboardingPositionService : DbRepository<ObPosition>, IOnboardingPo
|
||||
/// </summary>
|
||||
public async Task<SqlSugarPagedList<ObPosition>> Page(OnboardingPositionPageInput input)
|
||||
{
|
||||
return await Context.Queryable<ObPosition>()
|
||||
var list = await Context.Queryable<ObPosition>()
|
||||
.WhereIF(input.CompanyId.HasValue, it => it.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(input.DepartmentId.HasValue, it => it.DepartmentId == input.DepartmentId.Value)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.PositionCode), it => it.PositionCode.Contains(input.PositionCode))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.PositionName), it => it.PositionName.Contains(input.PositionName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Status), it => it.Status == input.Status)
|
||||
.OrderByIF(!string.IsNullOrWhiteSpace(input.SortField), $"{input.SortField} {input.SortOrder}")
|
||||
.OrderBy(it => it.PositionName)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
|
||||
if (list.List != null && list.List.Any())
|
||||
{
|
||||
var companyIds = list.List.Select(p => p.CompanyId).Distinct().ToList();
|
||||
var departmentIds = list.List.Where(p => p.DepartmentId.HasValue).Select(p => p.DepartmentId.Value).Distinct().ToList();
|
||||
|
||||
var companies = await Context.Queryable<ObCompany>()
|
||||
.Where(c => companyIds.Contains(c.Id))
|
||||
.ToListAsync();
|
||||
var departments = departmentIds.Count == 0
|
||||
? new List<ObDepartment>()
|
||||
: await Context.Queryable<ObDepartment>().Where(d => departmentIds.Contains(d.Id)).ToListAsync();
|
||||
|
||||
var companyMap = companies.ToDictionary(c => c.Id, c => c.CompanyName);
|
||||
var departmentMap = departments.ToDictionary(d => d.Id, d => d.DepartmentName);
|
||||
|
||||
foreach (var item in list.List)
|
||||
{
|
||||
if (companyMap.TryGetValue(item.CompanyId, out var cName))
|
||||
{
|
||||
item.CompanyName = cName;
|
||||
}
|
||||
if (item.DepartmentId.HasValue && departmentMap.TryGetValue(item.DepartmentId.Value, out var dName))
|
||||
{
|
||||
item.DepartmentName = dName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +62,17 @@ public class OnboardingPositionService : DbRepository<ObPosition>, IOnboardingPo
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定部门下全部已启用岗位。
|
||||
/// </summary>
|
||||
public Task<List<ObPosition>> ListEnabledByDepartment(long departmentId)
|
||||
{
|
||||
return Context.Queryable<ObPosition>()
|
||||
.Where(it => it.DepartmentId == departmentId && it.Status == "enable")
|
||||
.OrderBy(it => it.PositionName)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询岗位详情。
|
||||
/// </summary>
|
||||
@@ -44,7 +86,12 @@ public class OnboardingPositionService : DbRepository<ObPosition>, IOnboardingPo
|
||||
/// </summary>
|
||||
public async Task Add(OnboardingPositionAddInput input)
|
||||
{
|
||||
NormalizeInput(input);
|
||||
if (!input.DepartmentId.HasValue)
|
||||
throw Oops.Bah("请选择所属部门");
|
||||
|
||||
await EnsureCompanyExists(input.CompanyId);
|
||||
await EnsureDepartmentExists(input.CompanyId, input.DepartmentId.Value);
|
||||
await EnsureCodeUnique(input.CompanyId, input.PositionCode, 0);
|
||||
await InsertAsync(input.Adapt<ObPosition>());
|
||||
}
|
||||
@@ -54,8 +101,14 @@ public class OnboardingPositionService : DbRepository<ObPosition>, IOnboardingPo
|
||||
/// </summary>
|
||||
public async Task Edit(OnboardingPositionEditInput input)
|
||||
{
|
||||
await Detail(new BaseIdInput { Id = input.Id });
|
||||
NormalizeInput(input);
|
||||
if (!input.DepartmentId.HasValue)
|
||||
throw Oops.Bah("请选择所属部门");
|
||||
|
||||
var position = await Detail(new BaseIdInput { Id = input.Id });
|
||||
await EnsureScopeCanChange(position, input.CompanyId, input.DepartmentId.Value);
|
||||
await EnsureCompanyExists(input.CompanyId);
|
||||
await EnsureDepartmentExists(input.CompanyId, input.DepartmentId.Value);
|
||||
await EnsureCodeUnique(input.CompanyId, input.PositionCode, input.Id);
|
||||
await UpdateAsync(input.Adapt<ObPosition>());
|
||||
}
|
||||
@@ -78,14 +131,75 @@ public class OnboardingPositionService : DbRepository<ObPosition>, IOnboardingPo
|
||||
if (hasEmployees)
|
||||
throw Oops.Bah("岗位已有正式员工,不能删除");
|
||||
|
||||
var hasOnboardingTemplates = await Context.Queryable<ObOnboardingTemplatePosition>()
|
||||
.AnyAsync(item => input.Ids.Contains(item.PositionId));
|
||||
if (hasOnboardingTemplates)
|
||||
throw Oops.Bah("岗位已被入职模板引用,不能删除");
|
||||
|
||||
var hasContractTemplates = await Context.Queryable<ObContractTemplatePosition>()
|
||||
.AnyAsync(item => input.Ids.Contains(item.PositionId));
|
||||
if (hasContractTemplates)
|
||||
throw Oops.Bah("岗位已被合同模板引用,不能删除");
|
||||
|
||||
await DeleteByIdsAsync(input.Ids.Cast<object>().ToArray());
|
||||
}
|
||||
|
||||
private async Task EnsureCompanyExists(long companyId)
|
||||
{
|
||||
var exists = await Context.Queryable<ObCompany>().AnyAsync(it => it.Id == companyId);
|
||||
var exists = await Context.Queryable<ObCompany>()
|
||||
.AnyAsync(it => it.Id == companyId && it.Status == "enable");
|
||||
if (!exists)
|
||||
throw Oops.Bah("所属公司不存在或已删除");
|
||||
throw Oops.Bah("所属公司不存在、已删除或未启用");
|
||||
}
|
||||
|
||||
private async Task EnsureDepartmentExists(long companyId, long departmentId)
|
||||
{
|
||||
var exists = await Context.Queryable<ObDepartment>()
|
||||
.AnyAsync(it => it.Id == departmentId
|
||||
&& it.CompanyId == companyId
|
||||
&& it.Status == "enable");
|
||||
if (!exists)
|
||||
throw Oops.Bah("所属部门不存在、未启用或不属于该公司");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标准化岗位输入并拒绝非法状态。
|
||||
/// </summary>
|
||||
private static void NormalizeInput(OnboardingPositionAddInput input)
|
||||
{
|
||||
input.PositionCode = input.PositionCode?.Trim();
|
||||
input.PositionName = input.PositionName?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(input.PositionCode)
|
||||
|| string.IsNullOrWhiteSpace(input.PositionName))
|
||||
{
|
||||
throw Oops.Bah("请填写岗位编码和岗位名称");
|
||||
}
|
||||
|
||||
if (input.Status != "enable" && input.Status != "disable")
|
||||
throw Oops.Bah("岗位状态不正确");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 已被业务配置或历史数据引用的岗位不允许直接改变公司、部门归属。
|
||||
/// </summary>
|
||||
private async Task EnsureScopeCanChange(
|
||||
ObPosition position,
|
||||
long companyId,
|
||||
long departmentId)
|
||||
{
|
||||
if (position.CompanyId == companyId && position.DepartmentId == departmentId)
|
||||
return;
|
||||
|
||||
var hasTask = await Context.Queryable<ObOnboardingTask>()
|
||||
.AnyAsync(item => item.PositionId == position.Id);
|
||||
var hasEmployee = await Context.Queryable<ObEmployee>()
|
||||
.AnyAsync(item => item.PositionId == position.Id);
|
||||
var hasOnboardingTemplate = await Context.Queryable<ObOnboardingTemplatePosition>()
|
||||
.AnyAsync(item => item.PositionId == position.Id);
|
||||
var hasContractTemplate = await Context.Queryable<ObContractTemplatePosition>()
|
||||
.AnyAsync(item => item.PositionId == position.Id);
|
||||
if (hasTask || hasEmployee || hasOnboardingTemplate || hasContractTemplate)
|
||||
throw Oops.Bah("岗位已被模板、任务或员工引用,不能直接变更所属公司、部门");
|
||||
}
|
||||
|
||||
private async Task EnsureCodeUnique(long companyId, string code, long id)
|
||||
|
||||
-33
@@ -1,38 +1,5 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 单个动态字段填写值。
|
||||
/// </summary>
|
||||
public class OnboardingDynamicValueInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 动态字段 Id。
|
||||
/// </summary>
|
||||
public long FieldId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 动态字段填写值。
|
||||
/// </summary>
|
||||
public string FieldValue { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量保存员工动态字段参数。
|
||||
/// </summary>
|
||||
public class OnboardingSaveDynamicValuesInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 员工填写链接中的明文 Token。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.DataAnnotations.Required]
|
||||
public string Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 待保存的字段或变量值集合。
|
||||
/// </summary>
|
||||
public List<OnboardingDynamicValueInput> Values { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存员工已上传材料参数。
|
||||
/// </summary>
|
||||
|
||||
-5
@@ -5,11 +5,6 @@ namespace SimpleAdmin.Application;
|
||||
/// </summary>
|
||||
public interface IOnboardingPublicSubmissionService : ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存候选人填写的动态字段值。
|
||||
/// </summary>
|
||||
Task SaveDynamicValues(OnboardingSaveDynamicValuesInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 关联候选人已经上传的系统文件与材料类型。
|
||||
/// </summary>
|
||||
|
||||
-9
@@ -14,15 +14,6 @@ public class OnboardingPublicSubmissionService : IOnboardingPublicSubmissionServ
|
||||
_snapshotService = snapshotService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存候选人填写的动态补充字段值。
|
||||
/// </summary>
|
||||
public async Task SaveDynamicValues(OnboardingSaveDynamicValuesInput input)
|
||||
{
|
||||
await GetEditableTask(input.Token);
|
||||
throw Oops.Bah("新版入职登记表不再使用旧动态字段接口,请通过员工基础信息接口保存补充变量");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关联已上传 OSS 文件与任务材料要求。
|
||||
/// </summary>
|
||||
|
||||
@@ -128,6 +128,8 @@ public class OnboardingPublicTaskService : IOnboardingPublicTaskService
|
||||
throw Oops.Bah("当前任务不可编辑");
|
||||
}
|
||||
|
||||
ValidateEmployeeInfo(input);
|
||||
|
||||
var old = await _db.Queryable<ObTaskEmployeeInfo>()
|
||||
.FirstAsync(item => item.TaskId == task.Id);
|
||||
var entity = input.Adapt<ObTaskEmployeeInfo>();
|
||||
@@ -161,6 +163,46 @@ public class OnboardingPublicTaskService : IOnboardingPublicTaskService
|
||||
await _snapshotService.Build(task.Id, entity, invalidateGeneratedPreview: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验候选人核心身份信息,避免非法格式进入合同和正式员工档案。
|
||||
/// </summary>
|
||||
private static void ValidateEmployeeInfo(OnboardingTaskPublicSaveInput input)
|
||||
{
|
||||
input.EmployeeName = input.EmployeeName?.Trim();
|
||||
input.Phone = input.Phone?.Trim();
|
||||
input.IdCardNumber = input.IdCardNumber?.Trim().ToUpperInvariant();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.EmployeeName) || input.EmployeeName.Length > 100)
|
||||
throw Oops.Bah("员工姓名不能为空且不能超过 100 个字符");
|
||||
if (!global::System.Text.RegularExpressions.Regex.IsMatch(
|
||||
input.Phone ?? string.Empty,
|
||||
"^1[3-9]\\d{9}$"))
|
||||
{
|
||||
throw Oops.Bah("请输入正确的 11 位手机号码");
|
||||
}
|
||||
|
||||
if (!global::System.Text.RegularExpressions.Regex.IsMatch(
|
||||
input.IdCardNumber ?? string.Empty,
|
||||
"^\\d{17}[0-9X]$"))
|
||||
{
|
||||
throw Oops.Bah("请输入正确的 18 位身份证号码");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.CustomVariablesJson))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using var document = global::System.Text.Json.JsonDocument.Parse(input.CustomVariablesJson);
|
||||
if (document.RootElement.ValueKind != global::System.Text.Json.JsonValueKind.Object)
|
||||
throw new global::System.Text.Json.JsonException();
|
||||
}
|
||||
catch (global::System.Text.Json.JsonException)
|
||||
{
|
||||
throw Oops.Bah("员工补充变量格式不正确,请刷新页面后重新填写");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据员工当前已填写信息校验变量并提前生成合同 DOCX。
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 创建新员工入职任务参数。
|
||||
@@ -11,11 +11,18 @@ public class OnboardingTaskCreateInput
|
||||
[IdNotNull(ErrorMessage = "公司Id不能为空")]
|
||||
public long CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务部门 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "部门Id不能为空")]
|
||||
public long DepartmentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入职任务所属岗位 Id。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "岗位Id不能为空")]
|
||||
public long PositionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 本次任务使用的入职模板 Id。
|
||||
/// </summary>
|
||||
@@ -89,4 +96,4 @@ public class OnboardingTaskCreatedOutput
|
||||
/// 员工填写链接过期时间。
|
||||
/// </summary>
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -28,18 +28,25 @@ public class OnboardingTaskService : IOnboardingTaskService
|
||||
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("岗位不存在、未启用或不属于该公司");
|
||||
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("入职模板不存在、未启用或不属于该公司");
|
||||
?? throw Oops.Bah("入职模板不存在、未启用或不属于该公司、部门");
|
||||
var templateSupportsPosition = await _db.Queryable<ObOnboardingTemplatePosition>()
|
||||
.AnyAsync(item => item.OnboardingTemplateId == onboardingTemplate.Id
|
||||
&& item.PositionId == input.PositionId);
|
||||
@@ -62,6 +69,20 @@ public class OnboardingTaskService : IOnboardingTaskService
|
||||
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
|
||||
|| item.Status != "enable"))
|
||||
{
|
||||
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)
|
||||
@@ -94,6 +115,7 @@ public class OnboardingTaskService : IOnboardingTaskService
|
||||
Id = taskId,
|
||||
TaskNo = $"OB{DateTime.Now:yyyyMMddHHmmss}{taskId.ToString()[^4..]}",
|
||||
CompanyId = input.CompanyId,
|
||||
DepartmentId = input.DepartmentId,
|
||||
PositionId = input.PositionId,
|
||||
OnboardingTemplateId = onboardingTemplate.Id,
|
||||
AssignedReviewerUserId = input.AssignedReviewerUserId,
|
||||
@@ -236,6 +258,12 @@ public class OnboardingTaskService : IOnboardingTaskService
|
||||
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>()
|
||||
|
||||
+7
-2
@@ -1,4 +1,4 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 入职任务分页查询参数。
|
||||
@@ -19,6 +19,11 @@ public class OnboardingTaskPageInput : BasePageInput
|
||||
/// 入职业务公司 Id。
|
||||
/// </summary>
|
||||
public long? CompanyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入职业务部门 Id。
|
||||
/// </summary>
|
||||
public long? DepartmentId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,4 +76,4 @@ public class OnboardingTaskMaterialDetailOutput : ObTaskMaterial
|
||||
/// 材料文件后缀,用于判断是否支持图片预览。
|
||||
/// </summary>
|
||||
public string FileSuffix { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实现入职任务查询业务能力。
|
||||
@@ -23,6 +23,7 @@ public class OnboardingTaskQueryService : IOnboardingTaskQueryService
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.TaskNo), item => item.TaskNo.Contains(input.TaskNo))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.TaskStatus), item => item.TaskStatus == input.TaskStatus)
|
||||
.WhereIF(input.CompanyId.HasValue, item => item.CompanyId == input.CompanyId.Value)
|
||||
.WhereIF(input.DepartmentId.HasValue, item => item.DepartmentId == input.DepartmentId.Value)
|
||||
.OrderByDescending(item => item.CreateTime)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
|
||||
@@ -191,20 +192,26 @@ public class OnboardingTaskQueryService : IOnboardingTaskQueryService
|
||||
{
|
||||
var taskList = tasks.ToList();
|
||||
var companyIds = taskList.Select(t => t.CompanyId).Distinct().ToList();
|
||||
var departmentIds = taskList.Where(t => t.DepartmentId.HasValue).Select(t => t.DepartmentId.Value).Distinct().ToList();
|
||||
var positionIds = taskList.Select(t => t.PositionId).Distinct().ToList();
|
||||
var templateIds = taskList.Select(t => t.OnboardingTemplateId).Distinct().ToList();
|
||||
|
||||
var companies = await _db.Queryable<ObCompany>().Where(c => companyIds.Contains(c.Id)).ToListAsync();
|
||||
var departments = departmentIds.Count == 0
|
||||
? new List<ObDepartment>()
|
||||
: await _db.Queryable<ObDepartment>().Where(d => departmentIds.Contains(d.Id)).ToListAsync();
|
||||
var positions = await _db.Queryable<ObPosition>().Where(p => positionIds.Contains(p.Id)).ToListAsync();
|
||||
var templates = await _db.Queryable<ObOnboardingTemplate>().Where(t => templateIds.Contains(t.Id)).ToListAsync();
|
||||
|
||||
var companyMap = companies.ToDictionary(c => c.Id, c => c.CompanyName);
|
||||
var departmentMap = departments.ToDictionary(d => d.Id, d => d.DepartmentName);
|
||||
var positionMap = positions.ToDictionary(p => p.Id, p => p.PositionName);
|
||||
var templateMap = templates.ToDictionary(t => t.Id, t => t.TemplateName);
|
||||
|
||||
foreach (var task in taskList)
|
||||
{
|
||||
if (companyMap.TryGetValue(task.CompanyId, out var cName)) task.CompanyName = cName;
|
||||
if (task.DepartmentId.HasValue && departmentMap.TryGetValue(task.DepartmentId.Value, out var dName)) task.DepartmentName = dName;
|
||||
if (positionMap.TryGetValue(task.PositionId, out var pName)) task.PositionName = pName;
|
||||
if (templateMap.TryGetValue(task.OnboardingTemplateId, out var tName)) task.OnboardingTemplateName = tName;
|
||||
}
|
||||
|
||||
+23
-1
@@ -1,10 +1,32 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 合同变量字典服务。
|
||||
/// </summary>
|
||||
public interface IOnboardingVariableDefinitionService : ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页查询合同变量定义。
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<ObVariableDefinition>> Page(OnboardingVariableDefinitionPageInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 查询全部合同变量定义。
|
||||
/// </summary>
|
||||
Task<List<ObVariableDefinition>> ListAll();
|
||||
|
||||
/// <summary>
|
||||
/// 新增合同变量定义。
|
||||
/// </summary>
|
||||
Task Add(OnboardingVariableDefinitionAddInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 编辑合同变量定义。
|
||||
/// </summary>
|
||||
Task Edit(OnboardingVariableDefinitionEditInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 删除非内置合同变量定义。
|
||||
/// </summary>
|
||||
Task Delete(BaseIdListInput input);
|
||||
}
|
||||
}
|
||||
|
||||
+48
-1
@@ -2,32 +2,79 @@
|
||||
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 合同变量字典分页查询参数。
|
||||
/// </summary>
|
||||
public class OnboardingVariableDefinitionPageInput : BasePageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 变量编码或名称关键字。
|
||||
/// </summary>
|
||||
public string Keyword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量分类:employee、hr、company 或 system。
|
||||
/// </summary>
|
||||
public string VariableCategory { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增合同变量定义参数。
|
||||
/// </summary>
|
||||
public class OnboardingVariableDefinitionAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 完整变量编码,例如 employee.EmployeeName 或 hr.BaseSalary。
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "变量编码不能为空")]
|
||||
public string VariableCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 后台展示的变量名称。
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "变量名称不能为空")]
|
||||
public string VariableName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量分类:employee、hr、company 或 system。
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "变量分类不能为空")]
|
||||
public string VariableCategory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据类型:string、number 或 date。
|
||||
/// </summary>
|
||||
public string DataType { get; set; } = "string";
|
||||
|
||||
/// <summary>
|
||||
/// 未提供变量值时使用的默认值。
|
||||
/// </summary>
|
||||
public string DefaultValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否必须填写。
|
||||
/// </summary>
|
||||
public bool Required { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 展示排序值。
|
||||
/// </summary>
|
||||
public int SortCode { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// 变量用途说明。
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑合同变量定义参数。
|
||||
/// </summary>
|
||||
public class OnboardingVariableDefinitionEditInput : OnboardingVariableDefinitionAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 变量定义主键。
|
||||
/// </summary>
|
||||
[IdNotNull(ErrorMessage = "Id不能为空")]
|
||||
public long Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
+68
-11
@@ -1,26 +1,37 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实现合同变量字典的查询和维护。
|
||||
/// </summary>
|
||||
public class OnboardingVariableDefinitionService : IOnboardingVariableDefinitionService
|
||||
{
|
||||
private readonly SqlSugarScope _db;
|
||||
private static bool _initialized = false;
|
||||
private static readonly object _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化合同变量字典服务。
|
||||
/// </summary>
|
||||
public OnboardingVariableDefinitionService()
|
||||
{
|
||||
_db = DbContext.DB;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<SqlSugarPagedList<ObVariableDefinition>> Page(OnboardingVariableDefinitionPageInput input)
|
||||
{
|
||||
return await _db.Queryable<ObVariableDefinition>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), it => it.VariableCode.Contains(input.Keyword) || it.VariableName.Contains(input.Keyword))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.VariableCategory), it => it.VariableCategory == input.VariableCategory)
|
||||
.WhereIF(
|
||||
!string.IsNullOrWhiteSpace(input.Keyword),
|
||||
item => item.VariableCode.Contains(input.Keyword)
|
||||
|| item.VariableName.Contains(input.Keyword))
|
||||
.WhereIF(
|
||||
!string.IsNullOrWhiteSpace(input.VariableCategory),
|
||||
item => item.VariableCategory == input.VariableCategory)
|
||||
.OrderBy(it => it.SortCode)
|
||||
.OrderBy(it => it.Id)
|
||||
.ToPagedListAsync(input.PageNum, input.PageSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<ObVariableDefinition>> ListAll()
|
||||
{
|
||||
return await _db.Queryable<ObVariableDefinition>()
|
||||
@@ -29,26 +40,34 @@ public class OnboardingVariableDefinitionService : IOnboardingVariableDefinition
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task Add(OnboardingVariableDefinitionAddInput input)
|
||||
{
|
||||
var code = input.VariableCode.Trim();
|
||||
var exists = await _db.Queryable<ObVariableDefinition>().AnyAsync(it => it.VariableCode == code);
|
||||
NormalizeAndValidate(input);
|
||||
|
||||
var exists = await _db.Queryable<ObVariableDefinition>()
|
||||
.AnyAsync(item => item.VariableCode == input.VariableCode);
|
||||
if (exists)
|
||||
throw Oops.Bah($"变量编码“{code}”已存在");
|
||||
throw Oops.Bah($"变量编码“{input.VariableCode}”已存在");
|
||||
|
||||
var entity = input.Adapt<ObVariableDefinition>();
|
||||
entity.VariableCode = code;
|
||||
entity.IsBuiltIn = false;
|
||||
await _db.Insertable(entity).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task Edit(OnboardingVariableDefinitionEditInput input)
|
||||
{
|
||||
NormalizeAndValidate(input);
|
||||
|
||||
var entity = await _db.Queryable<ObVariableDefinition>().InSingleAsync(input.Id)
|
||||
?? throw Oops.Bah("变量不存在");
|
||||
|
||||
if (!string.Equals(entity.VariableCode, input.VariableCode, StringComparison.Ordinal))
|
||||
throw Oops.Bah("变量编码创建后不能修改");
|
||||
|
||||
entity.VariableName = input.VariableName;
|
||||
entity.DataType = input.DataType ?? "string";
|
||||
entity.DataType = input.DataType;
|
||||
entity.DefaultValue = input.DefaultValue;
|
||||
entity.Required = input.Required;
|
||||
entity.SortCode = input.SortCode;
|
||||
@@ -61,9 +80,11 @@ public class OnboardingVariableDefinitionService : IOnboardingVariableDefinition
|
||||
await _db.Updateable(entity).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task Delete(BaseIdListInput input)
|
||||
{
|
||||
if (input.Ids.Count == 0) return;
|
||||
if (input.Ids.Count == 0)
|
||||
return;
|
||||
|
||||
var hasBuiltIn = await _db.Queryable<ObVariableDefinition>()
|
||||
.Where(it => input.Ids.Contains(it.Id) && it.IsBuiltIn)
|
||||
@@ -75,4 +96,40 @@ public class OnboardingVariableDefinitionService : IOnboardingVariableDefinition
|
||||
.Where(it => input.Ids.Contains(it.Id))
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 规范化并校验合同变量定义,防止无效编码在生成合同时才暴露。
|
||||
/// </summary>
|
||||
private static void NormalizeAndValidate(OnboardingVariableDefinitionAddInput input)
|
||||
{
|
||||
input.VariableCode = input.VariableCode?.Trim();
|
||||
input.VariableName = input.VariableName?.Trim();
|
||||
input.VariableCategory = input.VariableCategory?.Trim().ToLowerInvariant();
|
||||
input.DataType = string.IsNullOrWhiteSpace(input.DataType)
|
||||
? "string"
|
||||
: input.DataType.Trim().ToLowerInvariant();
|
||||
input.DefaultValue = input.DefaultValue?.Trim();
|
||||
input.Remark = input.Remark?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.VariableCode))
|
||||
throw Oops.Bah("变量编码不能为空");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.VariableName))
|
||||
throw Oops.Bah("变量名称不能为空");
|
||||
|
||||
var validCategories = new[] { "employee", "hr", "company", "system" };
|
||||
if (!validCategories.Contains(input.VariableCategory))
|
||||
throw Oops.Bah("变量分类只能是员工填写、人事填写、公司信息或系统生成");
|
||||
|
||||
var codePattern = $@"^{input.VariableCategory}\.[A-Za-z][A-Za-z0-9_]*$";
|
||||
var codeIsValid = global::System.Text.RegularExpressions.Regex.IsMatch(
|
||||
input.VariableCode,
|
||||
codePattern);
|
||||
if (!codeIsValid)
|
||||
throw Oops.Bah($"变量编码必须以“{input.VariableCategory}.”开头,后半段只能包含字母、数字和下划线");
|
||||
|
||||
var validDataTypes = new[] { "string", "number", "date" };
|
||||
if (!validDataTypes.Contains(input.DataType))
|
||||
throw Oops.Bah("数据类型只能是文本、数字或日期");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user