111
This commit is contained in:
@@ -11,9 +11,9 @@
|
|||||||
namespace SimpleAdmin.Application;
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 审核通过后生成的正式员工档案,是入职流程的最终业务数据。
|
/// 审核通过后生成的员工档案,是入职流程的最终业务数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarTable("ob_employee", TableDescription = "正式员工档案")]
|
[SugarTable("ob_employee", TableDescription = "员工档案")]
|
||||||
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||||
public class ObEmployee : BaseEntity
|
public class ObEmployee : BaseEntity
|
||||||
{
|
{
|
||||||
@@ -59,6 +59,21 @@ public class ObEmployee : BaseEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long PositionId { get; set; }
|
public long PositionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用工类型:full_time 正式用工,part_time 兼职用工。
|
||||||
|
/// </summary>
|
||||||
|
public string EmploymentType { get; set; } = EmployeeEmploymentType.FullTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转正状态:probation 试用期、regular 已转正、not_applicable 不适用。
|
||||||
|
/// </summary>
|
||||||
|
public string RegularizationStatus { get; set; } = EmployeeRegularizationStatus.Probation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 社保状态:unknown 未维护、not_purchased 未购买、purchased 已购买、stopped 已停保、not_applicable 不适用。
|
||||||
|
/// </summary>
|
||||||
|
public string SocialInsuranceStatus { get; set; } = EmployeeSocialInsuranceStatus.Unknown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 所属岗位名称,仅用于接口展示,不映射数据库字段。
|
/// 所属岗位名称,仅用于接口展示,不映射数据库字段。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工人事材料附件,可关联转正记录及后续扩展的人事业务。
|
||||||
|
/// </summary>
|
||||||
|
[SugarTable("ob_employee_attachment", TableDescription = "员工人事材料附件")]
|
||||||
|
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||||
|
public class ObEmployeeAttachment : BaseEntity
|
||||||
|
{
|
||||||
|
public long EmployeeId { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
|
public long? ChangeRecordId { get; set; }
|
||||||
|
|
||||||
|
public string BusinessType { get; set; }
|
||||||
|
|
||||||
|
public long FileId { get; set; }
|
||||||
|
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
public DateTime UploadedAt { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
public string FileSuffix { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
public string FileSizeInfo { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工人事信息变更记录,保留转正及后续扩展业务的前后快照。
|
||||||
|
/// </summary>
|
||||||
|
[SugarTable("ob_employee_change_record", TableDescription = "员工人事信息变更记录")]
|
||||||
|
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||||
|
public class ObEmployeeChangeRecord : BaseEntity
|
||||||
|
{
|
||||||
|
public long EmployeeId { get; set; }
|
||||||
|
|
||||||
|
public string ChangeType { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
|
public DateTime? EffectiveDate { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
|
public string BeforeJson { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
|
||||||
|
public string AfterJson { get; set; }
|
||||||
|
|
||||||
|
public string Remark { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(IsIgnore = true)]
|
||||||
|
public List<ObEmployeeAttachment> Attachments { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 正式员工的一份合同在审核建档时冻结的业务快照。
|
||||||
|
/// 常用日期和薪资使用独立列,全部标准及自定义变量同时保存在 VariablesJson 中。
|
||||||
|
/// </summary>
|
||||||
|
[SugarTable("ob_employee_contract_snapshot", TableDescription = "员工合同快照")]
|
||||||
|
[Tenant(SqlSugarConst.DB_DEFAULT)]
|
||||||
|
public class ObEmployeeContractSnapshot : BaseEntity
|
||||||
|
{
|
||||||
|
/// <summary>正式员工 Id。</summary>
|
||||||
|
public long EmployeeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>来源入职任务 Id。</summary>
|
||||||
|
public long TaskId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>来源任务合同 Id,同一任务合同只允许生成一份员工快照。</summary>
|
||||||
|
public long TaskContractId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同模板 Id。</summary>
|
||||||
|
public long TemplateId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同模板版本 Id。</summary>
|
||||||
|
public long TemplateVersionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同名称快照。</summary>
|
||||||
|
public string ContractName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同类型快照。</summary>
|
||||||
|
public string ContractType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同开始日期。</summary>
|
||||||
|
public DateTime? ContractStartDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同结束日期。</summary>
|
||||||
|
public DateTime? ContractEndDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同期限原始文本。</summary>
|
||||||
|
public string ContractTerm { get; set; }
|
||||||
|
|
||||||
|
/// <summary>试用期开始日期。</summary>
|
||||||
|
public DateTime? ProbationStartDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>试用期结束日期。</summary>
|
||||||
|
public DateTime? ProbationEndDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>试用期期限原始文本。</summary>
|
||||||
|
public string ProbationTerm { get; set; }
|
||||||
|
|
||||||
|
/// <summary>明确使用 hr.ProbationSalary 保存的试用薪资;不从其他工资项目推算。</summary>
|
||||||
|
public decimal? ProbationSalary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>基本工资。</summary>
|
||||||
|
public decimal? BaseSalary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>岗位工资。</summary>
|
||||||
|
public decimal? PositionSalary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>绩效津贴。</summary>
|
||||||
|
public decimal? PerformanceAllowance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>全勤奖。</summary>
|
||||||
|
public decimal? AttendanceBonus { get; set; }
|
||||||
|
|
||||||
|
/// <summary>转正后薪资。</summary>
|
||||||
|
public decimal? RegularSalary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>工作地点或城市。</summary>
|
||||||
|
public string WorkCity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同中的岗位名称。</summary>
|
||||||
|
public string PositionName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>合同中的员工工号。</summary>
|
||||||
|
public string EmployeeNo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 以 VariableCode 为键保存该合同当时的全部变量元数据和值。
|
||||||
|
/// 导出时通过稳定编码判断变量是否存在,不依赖可修改的中文名称。
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDataType = "json", IsNullable = false)]
|
||||||
|
public string VariablesJson { get; set; }
|
||||||
|
|
||||||
|
/// <summary>快照结构版本。</summary>
|
||||||
|
public int SnapshotVersion { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>快照生成时间。</summary>
|
||||||
|
public DateTime SnapshotAt { get; set; }
|
||||||
|
}
|
||||||
@@ -33,6 +33,11 @@ public class ObOnboardingTask : BaseEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long PositionId { get; set; }
|
public long PositionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用工类型:full_time 正式用工,part_time 兼职用工。
|
||||||
|
/// </summary>
|
||||||
|
public string EmploymentType { get; set; } = EmployeeEmploymentType.FullTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建任务时锁定的入职模板 Id。
|
/// 创建任务时锁定的入职模板 Id。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -106,4 +111,4 @@ public class ObOnboardingTask : BaseEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(IsIgnore = true)]
|
[SugarColumn(IsIgnore = true)]
|
||||||
public string OnboardingTemplateName { get; set; }
|
public string OnboardingTemplateName { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public static class OnboardingTaskStatus
|
|||||||
public const string Rejected = "rejected";
|
public const string Rejected = "rejected";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已通过,系统已经生成正式员工档案。
|
/// 已通过,系统已经生成员工档案。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Approved = "approved";
|
public const string Approved = "approved";
|
||||||
|
|
||||||
@@ -144,6 +144,55 @@ public static class TaskContractStatus
|
|||||||
public const string Archived = "archived";
|
public const string Archived = "archived";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工用工类型。
|
||||||
|
/// </summary>
|
||||||
|
public static class EmployeeEmploymentType
|
||||||
|
{
|
||||||
|
public const string FullTime = "full_time";
|
||||||
|
public const string PartTime = "part_time";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工转正状态,仅由后台人事操作改变,不根据合同日期自动推算。
|
||||||
|
/// </summary>
|
||||||
|
public static class EmployeeRegularizationStatus
|
||||||
|
{
|
||||||
|
public const string Probation = "probation";
|
||||||
|
public const string Regular = "regular";
|
||||||
|
public const string NotApplicable = "not_applicable";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工社保状态。
|
||||||
|
/// </summary>
|
||||||
|
public static class EmployeeSocialInsuranceStatus
|
||||||
|
{
|
||||||
|
public const string Unknown = "unknown";
|
||||||
|
public const string NotPurchased = "not_purchased";
|
||||||
|
public const string Purchased = "purchased";
|
||||||
|
public const string Stopped = "stopped";
|
||||||
|
public const string NotApplicable = "not_applicable";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工人事变更类型。
|
||||||
|
/// </summary>
|
||||||
|
public static class EmployeeChangeType
|
||||||
|
{
|
||||||
|
public const string HrInfoUpdate = "hr_info_update";
|
||||||
|
public const string Regularization = "regularization";
|
||||||
|
public const string RegularizationRevoke = "regularization_revoke";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工附件业务类型。
|
||||||
|
/// </summary>
|
||||||
|
public static class EmployeeAttachmentBusinessType
|
||||||
|
{
|
||||||
|
public const string Regularization = "regularization";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 电子签服务商。
|
/// 电子签服务商。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ public class OnboardingAuditService : IOnboardingAuditService
|
|||||||
private readonly SqlSugarScope _db;
|
private readonly SqlSugarScope _db;
|
||||||
private readonly IConfigService _config;
|
private readonly IConfigService _config;
|
||||||
private readonly IOnboardingContractSnapshotService _snapshotService;
|
private readonly IOnboardingContractSnapshotService _snapshotService;
|
||||||
|
private readonly IOnboardingEmployeeContractSnapshotService _employeeContractSnapshotService;
|
||||||
|
|
||||||
public OnboardingAuditService(
|
public OnboardingAuditService(
|
||||||
IConfigService config,
|
IConfigService config,
|
||||||
IOnboardingContractSnapshotService snapshotService)
|
IOnboardingContractSnapshotService snapshotService,
|
||||||
|
IOnboardingEmployeeContractSnapshotService employeeContractSnapshotService)
|
||||||
{
|
{
|
||||||
_db = DbContext.DB;
|
_db = DbContext.DB;
|
||||||
_config = config;
|
_config = config;
|
||||||
_snapshotService = snapshotService;
|
_snapshotService = snapshotService;
|
||||||
|
_employeeContractSnapshotService = employeeContractSnapshotService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,6 +54,13 @@ public class OnboardingAuditService : IOnboardingAuditService
|
|||||||
employee.CompanyId = task.CompanyId;
|
employee.CompanyId = task.CompanyId;
|
||||||
employee.DepartmentId = task.DepartmentId;
|
employee.DepartmentId = task.DepartmentId;
|
||||||
employee.PositionId = task.PositionId;
|
employee.PositionId = task.PositionId;
|
||||||
|
employee.EmploymentType = string.IsNullOrWhiteSpace(task.EmploymentType)
|
||||||
|
? EmployeeEmploymentType.FullTime
|
||||||
|
: task.EmploymentType;
|
||||||
|
employee.RegularizationStatus = employee.EmploymentType == EmployeeEmploymentType.PartTime
|
||||||
|
? EmployeeRegularizationStatus.NotApplicable
|
||||||
|
: EmployeeRegularizationStatus.Probation;
|
||||||
|
employee.SocialInsuranceStatus = EmployeeSocialInsuranceStatus.Unknown;
|
||||||
if (task.DepartmentId.HasValue)
|
if (task.DepartmentId.HasValue)
|
||||||
{
|
{
|
||||||
var dept = await _db.Queryable<ObDepartment>().InSingleAsync(task.DepartmentId.Value);
|
var dept = await _db.Queryable<ObDepartment>().InSingleAsync(task.DepartmentId.Value);
|
||||||
@@ -96,6 +106,7 @@ public class OnboardingAuditService : IOnboardingAuditService
|
|||||||
|
|
||||||
await _snapshotService.Build(task.Id, employeeInfo);
|
await _snapshotService.Build(task.Id, employeeInfo);
|
||||||
await _db.Insertable(employee).ExecuteCommandAsync();
|
await _db.Insertable(employee).ExecuteCommandAsync();
|
||||||
|
await _employeeContractSnapshotService.Build(employee.Id, task.Id);
|
||||||
|
|
||||||
task.EmployeeId = employee.Id;
|
task.EmployeeId = employee.Id;
|
||||||
task.TaskStatus = OnboardingTaskStatus.Approved;
|
task.TaskStatus = OnboardingTaskStatus.Approved;
|
||||||
@@ -197,7 +208,7 @@ public class OnboardingAuditService : IOnboardingAuditService
|
|||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.ToList();
|
.ToList();
|
||||||
if (normalizedValues.Count == 0)
|
if (normalizedValues.Count == 0)
|
||||||
throw Oops.Bah("签署材料参数“合同开始日期”尚未填写,无法生成正式员工档案");
|
throw Oops.Bah("签署材料参数“合同开始日期”尚未填写,无法生成员工档案");
|
||||||
|
|
||||||
var parsedDates = new List<DateTime>();
|
var parsedDates = new List<DateTime>();
|
||||||
foreach (var value in normalizedValues)
|
foreach (var value in normalizedValues)
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 统一校验入职业务中的可编辑编码,避免非法字符或超长内容落到数据库后才报 500。
|
||||||
|
/// </summary>
|
||||||
|
internal static class OnboardingCodeValidator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 规范化并校验业务编码。编码必须以字母开头,可包含字母、数字、点、下划线和短横线。
|
||||||
|
/// </summary>
|
||||||
|
public static string Normalize(string code, string fieldName, int maxLength)
|
||||||
|
{
|
||||||
|
var normalized = code?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(normalized))
|
||||||
|
throw Oops.Bah($"{fieldName}不能为空");
|
||||||
|
|
||||||
|
if (normalized.Length > maxLength)
|
||||||
|
throw Oops.Bah($"{fieldName}长度不能超过 {maxLength} 个字符");
|
||||||
|
|
||||||
|
if (!global::System.Text.RegularExpressions.Regex.IsMatch(
|
||||||
|
normalized,
|
||||||
|
@"^[A-Za-z][A-Za-z0-9._-]*$"))
|
||||||
|
{
|
||||||
|
throw Oops.Bah($"{fieldName}必须以字母开头,只能包含字母、数字、点、下划线和短横线");
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,6 +102,12 @@ public class OnboardingCompanyService : DbRepository<ObCompany>, IOnboardingComp
|
|||||||
|
|
||||||
private static void NormalizeAndValidateQiyuesuoConfig(ObCompany company)
|
private static void NormalizeAndValidateQiyuesuoConfig(ObCompany company)
|
||||||
{
|
{
|
||||||
|
company.CompanyCode = OnboardingCodeValidator.Normalize(company.CompanyCode, "公司编码", 50);
|
||||||
|
company.CompanyName = company.CompanyName?.Trim();
|
||||||
|
company.LegalName = company.LegalName?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(company.CompanyName))
|
||||||
|
throw Oops.Bah("公司名称不能为空");
|
||||||
|
|
||||||
company.QiyuesuoCategoryId = company.QiyuesuoCategoryId?.Trim();
|
company.QiyuesuoCategoryId = company.QiyuesuoCategoryId?.Trim();
|
||||||
company.QiyuesuoSealId = company.QiyuesuoSealId?.Trim();
|
company.QiyuesuoSealId = company.QiyuesuoSealId?.Trim();
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -450,13 +450,12 @@ public class OnboardingContractTemplateService : DbRepository<ObContractTemplate
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void NormalizeInput(OnboardingContractTemplateAddInput input)
|
private static void NormalizeInput(OnboardingContractTemplateAddInput input)
|
||||||
{
|
{
|
||||||
input.TemplateCode = input.TemplateCode?.Trim();
|
input.TemplateCode = OnboardingCodeValidator.Normalize(input.TemplateCode, "签署材料模板编码", 50);
|
||||||
input.TemplateName = input.TemplateName?.Trim();
|
input.TemplateName = input.TemplateName?.Trim();
|
||||||
input.ContractType = input.ContractType?.Trim();
|
input.ContractType = input.ContractType?.Trim();
|
||||||
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(input.TemplateCode)
|
if (string.IsNullOrWhiteSpace(input.TemplateName)
|
||||||
|| string.IsNullOrWhiteSpace(input.TemplateName)
|
|
||||||
|| string.IsNullOrWhiteSpace(input.ContractType))
|
|| string.IsNullOrWhiteSpace(input.ContractType))
|
||||||
{
|
{
|
||||||
throw Oops.Bah("请填写模板编码、模板名称和合同类型");
|
throw Oops.Bah("请填写模板编码、模板名称和合同类型");
|
||||||
|
|||||||
+3
-4
@@ -133,12 +133,11 @@ public class OnboardingDepartmentService : DbRepository<ObDepartment>, IOnboardi
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void NormalizeInput(OnboardingDepartmentAddInput input)
|
private static void NormalizeInput(OnboardingDepartmentAddInput input)
|
||||||
{
|
{
|
||||||
input.DepartmentCode = input.DepartmentCode?.Trim();
|
input.DepartmentCode = OnboardingCodeValidator.Normalize(input.DepartmentCode, "部门编码", 50);
|
||||||
input.DepartmentName = input.DepartmentName?.Trim();
|
input.DepartmentName = input.DepartmentName?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(input.DepartmentCode)
|
if (string.IsNullOrWhiteSpace(input.DepartmentName))
|
||||||
|| string.IsNullOrWhiteSpace(input.DepartmentName))
|
|
||||||
{
|
{
|
||||||
throw Oops.Bah("请填写部门编码和部门名称");
|
throw Oops.Bah("请填写部门名称");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.Status != "enable" && input.Status != "disable")
|
if (input.Status != "enable" && input.Status != "disable")
|
||||||
|
|||||||
+5
-5
@@ -138,7 +138,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
|
|||||||
{
|
{
|
||||||
var employee = await _db.Queryable<ObEmployee>()
|
var employee = await _db.Queryable<ObEmployee>()
|
||||||
.InSingleAsync(task.EmployeeId.Value)
|
.InSingleAsync(task.EmployeeId.Value)
|
||||||
?? throw Oops.Bah("正式员工档案不存在,不能发起电子签");
|
?? throw Oops.Bah("员工档案不存在,不能发起电子签");
|
||||||
if (string.IsNullOrWhiteSpace(employee.EmployeeName))
|
if (string.IsNullOrWhiteSpace(employee.EmployeeName))
|
||||||
throw Oops.Bah("正式员工姓名为空,不能发起电子签");
|
throw Oops.Bah("正式员工姓名为空,不能发起电子签");
|
||||||
if (string.IsNullOrWhiteSpace(employee.Phone))
|
if (string.IsNullOrWhiteSpace(employee.Phone))
|
||||||
@@ -1417,7 +1417,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (differentFields.Count > 0)
|
if (differentFields.Count > 0)
|
||||||
throw Oops.Bah($"正式员工档案的{string.Join("、", differentFields)}与审核通过的入职登记资料不一致,请先核对并修正后再发起电子签");
|
throw Oops.Bah($"员工档案的{string.Join("、", differentFields)}与审核通过的入职登记资料不一致,请先核对并修正后再发起电子签");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string NormalizeIdentityValue(string value)
|
private static string NormalizeIdentityValue(string value)
|
||||||
@@ -1468,7 +1468,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
|
|||||||
private static void ValidateTaskCanSign(ObOnboardingTask task)
|
private static void ValidateTaskCanSign(ObOnboardingTask task)
|
||||||
{
|
{
|
||||||
if (!task.EmployeeId.HasValue)
|
if (!task.EmployeeId.HasValue)
|
||||||
throw Oops.Bah("入职任务尚未生成正式员工档案,不能发起电子签");
|
throw Oops.Bah("入职任务尚未生成员工档案,不能发起电子签");
|
||||||
if (task.TaskStatus != OnboardingTaskStatus.Approved
|
if (task.TaskStatus != OnboardingTaskStatus.Approved
|
||||||
&& task.TaskStatus != OnboardingTaskStatus.Archived)
|
&& task.TaskStatus != OnboardingTaskStatus.Archived)
|
||||||
{
|
{
|
||||||
@@ -1527,7 +1527,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
|
|||||||
bool processAllowed)
|
bool processAllowed)
|
||||||
{
|
{
|
||||||
if (!task.EmployeeId.HasValue)
|
if (!task.EmployeeId.HasValue)
|
||||||
return "审核通过并生成正式员工档案后才可发起电子签";
|
return "审核通过并生成员工档案后才可发起电子签";
|
||||||
if (!taskAllowed)
|
if (!taskAllowed)
|
||||||
return "当前入职任务状态不能发起电子签";
|
return "当前入职任务状态不能发起电子签";
|
||||||
if (!processAllowed)
|
if (!processAllowed)
|
||||||
@@ -1542,7 +1542,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
|
|||||||
bool reinitiateAllowed)
|
bool reinitiateAllowed)
|
||||||
{
|
{
|
||||||
if (!task.EmployeeId.HasValue)
|
if (!task.EmployeeId.HasValue)
|
||||||
return "审核通过并生成正式员工档案后才可重新发起电子签";
|
return "审核通过并生成员工档案后才可重新发起电子签";
|
||||||
if (!taskAllowed)
|
if (!taskAllowed)
|
||||||
return "当前入职任务状态不能重新发起电子签";
|
return "当前入职任务状态不能重新发起电子签";
|
||||||
if (process == null)
|
if (process == null)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace SimpleAdmin.Application;
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -29,6 +31,84 @@ public class OnboardingEmployeePageInput : BasePageInput
|
|||||||
/// 岗位 Id。
|
/// 岗位 Id。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long? PositionId { get; set; }
|
public long? PositionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用工类型。
|
||||||
|
/// </summary>
|
||||||
|
public string EmploymentType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转正状态。
|
||||||
|
/// </summary>
|
||||||
|
public string RegularizationStatus { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 社保状态。
|
||||||
|
/// </summary>
|
||||||
|
public string SocialInsuranceStatus { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑员工人事信息。
|
||||||
|
/// </summary>
|
||||||
|
public class OnboardingEmployeeHrUpdateInput
|
||||||
|
{
|
||||||
|
[IdNotNull(ErrorMessage = "员工Id不能为空")]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "请选择用工类型")]
|
||||||
|
public string EmploymentType { get; set; }
|
||||||
|
|
||||||
|
public string ContractNature { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "请选择社保状态")]
|
||||||
|
public string SocialInsuranceStatus { get; set; }
|
||||||
|
|
||||||
|
public string SocialInsuranceCompany { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工转正办理参数。
|
||||||
|
/// </summary>
|
||||||
|
public class OnboardingEmployeeRegularizeInput
|
||||||
|
{
|
||||||
|
[IdNotNull(ErrorMessage = "员工Id不能为空")]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "请选择转正日期")]
|
||||||
|
public DateTime? RegularizationDate { get; set; }
|
||||||
|
|
||||||
|
public string Remark { get; set; }
|
||||||
|
|
||||||
|
public List<long> FileIds { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 撤销员工转正参数。
|
||||||
|
/// </summary>
|
||||||
|
public class OnboardingEmployeeRegularizationRevokeInput
|
||||||
|
{
|
||||||
|
[IdNotNull(ErrorMessage = "员工Id不能为空")]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "请填写撤销原因")]
|
||||||
|
public string Remark { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 正式员工档案导出结果。
|
||||||
|
/// </summary>
|
||||||
|
public class OnboardingEmployeeExportFile
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Excel 文件字节。
|
||||||
|
/// </summary>
|
||||||
|
public byte[] FileBytes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载文件名。
|
||||||
|
/// </summary>
|
||||||
|
public string FileName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -56,6 +136,12 @@ public class OnboardingEmployeeDetailOutput
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ObTaskContract> Contracts { get; set; } = new List<ObTaskContract>();
|
public List<ObTaskContract> Contracts { get; set; } = new List<ObTaskContract>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 审核建档时冻结的员工合同业务快照,包含常用字段及全部自定义变量。
|
||||||
|
/// </summary>
|
||||||
|
public List<ObEmployeeContractSnapshot> ContractSnapshots { get; set; }
|
||||||
|
= new List<ObEmployeeContractSnapshot>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 员工入职时上传的身份证、学历证明、体检报告等资料。
|
/// 员工入职时上传的身份证、学历证明、体检报告等资料。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -71,4 +157,10 @@ public class OnboardingEmployeeDetailOutput
|
|||||||
/// 提交与审核记录列表。
|
/// 提交与审核记录列表。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ObTaskAudit> Audits { get; set; } = new List<ObTaskAudit>();
|
public List<ObTaskAudit> Audits { get; set; } = new List<ObTaskAudit>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 员工转正及人事维护历史。
|
||||||
|
/// </summary>
|
||||||
|
public List<ObEmployeeChangeRecord> ChangeRecords { get; set; }
|
||||||
|
= new List<ObEmployeeChangeRecord>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace SimpleAdmin.Application;
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 定义正式员工档案查询业务能力。
|
/// 定义员工档案查询和人事维护业务能力。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IOnboardingEmployeeService : ITransient
|
public interface IOnboardingEmployeeService : ITransient
|
||||||
{
|
{
|
||||||
@@ -10,6 +10,22 @@ public interface IOnboardingEmployeeService : ITransient
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Task<SqlSugarPagedList<ObEmployee>> Page(OnboardingEmployeePageInput input);
|
Task<SqlSugarPagedList<ObEmployee>> Page(OnboardingEmployeePageInput input);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 按当前列表筛选条件导出员工明细。
|
||||||
|
/// </summary>
|
||||||
|
Task<OnboardingEmployeeExportFile> Export(OnboardingEmployeePageInput input);
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑员工用工、社保及合同性质等人事信息。
|
||||||
|
/// </summary>
|
||||||
|
Task UpdateHrInfo(OnboardingEmployeeHrUpdateInput input);
|
||||||
|
/// <summary>
|
||||||
|
/// 人工办理员工转正。
|
||||||
|
/// </summary>
|
||||||
|
Task Regularize(OnboardingEmployeeRegularizeInput input);
|
||||||
|
/// <summary>
|
||||||
|
/// 撤销一次员工转正,历史记录和附件继续保留。
|
||||||
|
/// </summary>
|
||||||
|
Task RevokeRegularization(OnboardingEmployeeRegularizationRevokeInput input);
|
||||||
|
/// <summary>
|
||||||
/// 查询正式员工及其来源任务、合同和审核记录。
|
/// 查询正式员工及其来源任务、合同和审核记录。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<OnboardingEmployeeDetailOutput> Detail(BaseIdInput input);
|
Task<OnboardingEmployeeDetailOutput> Detail(BaseIdInput input);
|
||||||
|
|||||||
+1008
-13
File diff suppressed because it is too large
Load Diff
+10
@@ -0,0 +1,10 @@
|
|||||||
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定义正式员工合同快照归档能力。
|
||||||
|
/// </summary>
|
||||||
|
public interface IOnboardingEmployeeContractSnapshotService : ITransient
|
||||||
|
{
|
||||||
|
/// <summary>冻结一名员工来源任务中的全部合同和变量。</summary>
|
||||||
|
Task Build(long employeeId, long taskId);
|
||||||
|
}
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace SimpleAdmin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在正式员工建档时,将每份任务合同及其全部变量冻结为长期业务快照。
|
||||||
|
/// </summary>
|
||||||
|
public class OnboardingEmployeeContractSnapshotService : IOnboardingEmployeeContractSnapshotService
|
||||||
|
{
|
||||||
|
private readonly SqlSugarScope _db;
|
||||||
|
|
||||||
|
public OnboardingEmployeeContractSnapshotService()
|
||||||
|
{
|
||||||
|
_db = DbContext.DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task Build(long employeeId, long taskId)
|
||||||
|
{
|
||||||
|
var contracts = await _db.Queryable<ObTaskContract>()
|
||||||
|
.Where(item => item.TaskId == taskId)
|
||||||
|
.OrderBy(item => item.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
if (contracts.Count == 0)
|
||||||
|
throw Oops.Bah("入职任务没有合同,无法生成员工合同快照");
|
||||||
|
|
||||||
|
var contractIds = contracts.Select(item => item.Id).ToList();
|
||||||
|
var allVariables = await _db.Queryable<ObTaskContractVariable>()
|
||||||
|
.Where(item => contractIds.Contains(item.TaskContractId))
|
||||||
|
.OrderBy(item => item.SortCode)
|
||||||
|
.OrderBy(item => item.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var snapshots = new List<ObEmployeeContractSnapshot>();
|
||||||
|
|
||||||
|
foreach (var contract in contracts)
|
||||||
|
{
|
||||||
|
var variables = allVariables
|
||||||
|
.Where(item => item.TaskContractId == contract.Id)
|
||||||
|
.OrderBy(item => item.SortCode)
|
||||||
|
.ThenBy(item => item.Id)
|
||||||
|
.ToList();
|
||||||
|
var valueMap = variables
|
||||||
|
.GroupBy(item => item.VariableCode, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(
|
||||||
|
group => group.Key,
|
||||||
|
group => group.Last().VariableValue,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
var variableSnapshot = variables
|
||||||
|
.GroupBy(item => item.VariableCode, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(
|
||||||
|
group => group.Key,
|
||||||
|
group =>
|
||||||
|
{
|
||||||
|
// 历史任务若意外存在重复变量,以排序后的最后一条最终值为准,避免建档时报 500。
|
||||||
|
var item = group.Last();
|
||||||
|
return (object)new
|
||||||
|
{
|
||||||
|
variableName = item.VariableName,
|
||||||
|
variableType = item.VariableType,
|
||||||
|
dataType = item.DataType,
|
||||||
|
variableValue = item.VariableValue,
|
||||||
|
sourceType = item.SourceType,
|
||||||
|
isManualOverride = item.IsManualOverride,
|
||||||
|
sortCode = item.SortCode
|
||||||
|
};
|
||||||
|
},
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
snapshots.Add(new ObEmployeeContractSnapshot
|
||||||
|
{
|
||||||
|
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||||||
|
EmployeeId = employeeId,
|
||||||
|
TaskId = taskId,
|
||||||
|
TaskContractId = contract.Id,
|
||||||
|
TemplateId = contract.TemplateId,
|
||||||
|
TemplateVersionId = contract.TemplateVersionId,
|
||||||
|
ContractName = contract.ContractName,
|
||||||
|
ContractType = contract.ContractType,
|
||||||
|
ContractStartDate = ParseDate(GetValue(valueMap, "hr.ContractStartDate")),
|
||||||
|
ContractEndDate = ParseDate(GetValue(valueMap, "hr.ContractEndDate")),
|
||||||
|
ContractTerm = GetValue(valueMap, "hr.ContractTerm"),
|
||||||
|
ProbationStartDate = ParseDate(GetValue(valueMap, "hr.ProbationStartDate")),
|
||||||
|
ProbationEndDate = ParseDate(GetValue(valueMap, "hr.ProbationEndDate")),
|
||||||
|
ProbationTerm = GetValue(valueMap, "hr.ProbationTerm"),
|
||||||
|
ProbationSalary = ParseAmount(GetValue(valueMap, "hr.ProbationSalary")),
|
||||||
|
BaseSalary = ParseAmount(GetValue(valueMap, "hr.BaseSalary")),
|
||||||
|
PositionSalary = ParseAmount(GetValue(valueMap, "hr.PositionSalary")),
|
||||||
|
PerformanceAllowance = ParseAmount(GetValue(valueMap, "hr.PerformanceAllowance")),
|
||||||
|
AttendanceBonus = ParseAmount(GetValue(valueMap, "hr.AttendanceBonus")),
|
||||||
|
RegularSalary = ParseAmount(GetValue(valueMap, "hr.RegularSalary")),
|
||||||
|
WorkCity = GetValue(valueMap, "hr.WorkCity"),
|
||||||
|
PositionName = GetValue(valueMap, "hr.PositionName"),
|
||||||
|
EmployeeNo = GetValue(valueMap, "hr.EmployeeNo"),
|
||||||
|
VariablesJson = global::System.Text.Json.JsonSerializer.Serialize(variableSnapshot),
|
||||||
|
SnapshotVersion = 1,
|
||||||
|
SnapshotAt = now,
|
||||||
|
Status = CommonStatusConst.ENABLE,
|
||||||
|
CreateTime = now,
|
||||||
|
CreateUserId = UserManager.UserId,
|
||||||
|
CreateUser = UserManager.Name,
|
||||||
|
IsDelete = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build 只在审核建档事务内执行;先删后建使异常重试保持幂等。
|
||||||
|
await _db.Deleteable<ObEmployeeContractSnapshot>()
|
||||||
|
.Where(item => item.TaskId == taskId)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
await _db.Insertable(snapshots).ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetValue(
|
||||||
|
IReadOnlyDictionary<string, string> valueMap,
|
||||||
|
string variableCode)
|
||||||
|
{
|
||||||
|
return valueMap.TryGetValue(variableCode, out var value) && !string.IsNullOrWhiteSpace(value)
|
||||||
|
? value.Trim()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTime? ParseDate(string value)
|
||||||
|
{
|
||||||
|
return DateTime.TryParse(value, out var date) ? date.Date : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal? ParseAmount(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var normalized = value
|
||||||
|
.Replace("¥", string.Empty)
|
||||||
|
.Replace("¥", string.Empty)
|
||||||
|
.Replace("元", string.Empty)
|
||||||
|
.Trim();
|
||||||
|
if (decimal.TryParse(
|
||||||
|
normalized,
|
||||||
|
NumberStyles.Number | NumberStyles.AllowCurrencySymbol,
|
||||||
|
CultureInfo.GetCultureInfo("zh-CN"),
|
||||||
|
out var amount)
|
||||||
|
|| decimal.TryParse(
|
||||||
|
normalized,
|
||||||
|
NumberStyles.Number,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
out amount))
|
||||||
|
{
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-1
@@ -27,6 +27,11 @@ public class OnboardingEntryConfigService : IOnboardingEntryConfigService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task SaveFormField(OnboardingFormFieldSaveInput input)
|
public async Task SaveFormField(OnboardingFormFieldSaveInput input)
|
||||||
{
|
{
|
||||||
|
input.FieldCode = OnboardingCodeValidator.Normalize(input.FieldCode, "字段编码", 100);
|
||||||
|
input.FieldName = input.FieldName?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(input.FieldName))
|
||||||
|
throw Oops.Bah("字段名称不能为空");
|
||||||
|
|
||||||
var fieldCodeQuery = _db.Queryable<ObFormField>()
|
var fieldCodeQuery = _db.Queryable<ObFormField>()
|
||||||
.Where(x => x.FieldCode == input.FieldCode);
|
.Where(x => x.FieldCode == input.FieldCode);
|
||||||
if (input.Id.HasValue)
|
if (input.Id.HasValue)
|
||||||
@@ -115,6 +120,10 @@ public class OnboardingEntryConfigService : IOnboardingEntryConfigService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task SaveMaterialType(OnboardingMaterialTypeSaveInput input)
|
public async Task SaveMaterialType(OnboardingMaterialTypeSaveInput input)
|
||||||
{
|
{
|
||||||
|
input.MaterialCode = OnboardingCodeValidator.Normalize(input.MaterialCode, "资料编码", 100);
|
||||||
|
input.MaterialName = input.MaterialName?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(input.MaterialName))
|
||||||
|
throw Oops.Bah("资料名称不能为空");
|
||||||
if (input.MaxFileCount <= 0)
|
if (input.MaxFileCount <= 0)
|
||||||
throw Oops.Bah("最大文件数量必须大于0");
|
throw Oops.Bah("最大文件数量必须大于0");
|
||||||
|
|
||||||
@@ -169,4 +178,4 @@ public class OnboardingEntryConfigService : IOnboardingEntryConfigService
|
|||||||
|
|
||||||
await _db.Deleteable<ObMaterialType>().In(input.Ids).ExecuteCommandAsync();
|
await _db.Deleteable<ObMaterialType>().In(input.Ids).ExecuteCommandAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-10
@@ -92,12 +92,22 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
|||||||
NormalizeAndValidateInput(input);
|
NormalizeAndValidateInput(input);
|
||||||
await ValidateReferences(input);
|
await ValidateReferences(input);
|
||||||
|
|
||||||
var templateId = input.Id ?? global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
var codeOwner = await _db.Queryable<ObOnboardingTemplate>()
|
||||||
var codeExists = await _db.Queryable<ObOnboardingTemplate>()
|
.ClearFilter()
|
||||||
.AnyAsync(item => item.TemplateCode == input.TemplateCode && item.Id != templateId);
|
.FirstAsync(item => item.TemplateCode == input.TemplateCode
|
||||||
if (codeExists)
|
&& (!input.Id.HasValue || item.Id != input.Id.Value));
|
||||||
|
if (codeOwner != null && !codeOwner.IsDelete)
|
||||||
throw Oops.Bah("入职模板编码已存在");
|
throw Oops.Bah("入职模板编码已存在");
|
||||||
|
|
||||||
|
if (input.Id.HasValue && codeOwner != null && codeOwner.IsDelete)
|
||||||
|
throw Oops.Bah("该入职模板编码属于一条已删除记录,请通过新增模板重新启用该编码");
|
||||||
|
|
||||||
|
// 新增时如果编码命中历史软删除记录,复用原 Id 并恢复,避免唯一索引直接抛 500。
|
||||||
|
var isRestore = !input.Id.HasValue && codeOwner != null && codeOwner.IsDelete;
|
||||||
|
var templateId = isRestore
|
||||||
|
? codeOwner.Id
|
||||||
|
: input.Id ?? global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId();
|
||||||
|
|
||||||
var entity = new ObOnboardingTemplate
|
var entity = new ObOnboardingTemplate
|
||||||
{
|
{
|
||||||
Id = templateId,
|
Id = templateId,
|
||||||
@@ -106,15 +116,17 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
|||||||
CompanyId = input.CompanyId,
|
CompanyId = input.CompanyId,
|
||||||
DepartmentId = input.DepartmentId,
|
DepartmentId = input.DepartmentId,
|
||||||
Description = input.Description,
|
Description = input.Description,
|
||||||
Status = input.Status
|
Status = input.Status,
|
||||||
|
IsDelete = false
|
||||||
};
|
};
|
||||||
|
|
||||||
await _db.Ado.BeginTranAsync();
|
await _db.Ado.BeginTranAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (input.Id.HasValue)
|
if (input.Id.HasValue || isRestore)
|
||||||
{
|
{
|
||||||
var exists = await _db.Queryable<ObOnboardingTemplate>().AnyAsync(item => item.Id == templateId);
|
var exists = isRestore || await _db.Queryable<ObOnboardingTemplate>()
|
||||||
|
.AnyAsync(item => item.Id == templateId);
|
||||||
if (!exists)
|
if (!exists)
|
||||||
throw Oops.Bah("入职模板不存在");
|
throw Oops.Bah("入职模板不存在");
|
||||||
await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
|
await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
|
||||||
@@ -189,7 +201,10 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
|||||||
|
|
||||||
private static void NormalizeAndValidateInput(OnboardingTemplateSaveInput input)
|
private static void NormalizeAndValidateInput(OnboardingTemplateSaveInput input)
|
||||||
{
|
{
|
||||||
input.TemplateCode = input.TemplateCode?.Trim();
|
input.TemplateCode = OnboardingCodeValidator.Normalize(
|
||||||
|
input.TemplateCode,
|
||||||
|
"入职模板编码",
|
||||||
|
100);
|
||||||
input.TemplateName = input.TemplateName?.Trim();
|
input.TemplateName = input.TemplateName?.Trim();
|
||||||
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
input.PositionIds = input.PositionIds?.Distinct().ToList() ?? new List<long>();
|
||||||
input.Contracts ??= new List<OnboardingTemplateContractInput>();
|
input.Contracts ??= new List<OnboardingTemplateContractInput>();
|
||||||
@@ -208,8 +223,8 @@ public class OnboardingTemplateService : IOnboardingTemplateService
|
|||||||
.Select(group => group.First())
|
.Select(group => group.First())
|
||||||
.ToList() ?? new List<OnboardingTemplateMaterialInput>();
|
.ToList() ?? new List<OnboardingTemplateMaterialInput>();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(input.TemplateCode) || string.IsNullOrWhiteSpace(input.TemplateName))
|
if (string.IsNullOrWhiteSpace(input.TemplateName))
|
||||||
throw Oops.Bah("请填写入职模板编码和名称");
|
throw Oops.Bah("请填写入职模板名称");
|
||||||
if (input.CompanyId <= 0)
|
if (input.CompanyId <= 0)
|
||||||
throw Oops.Bah("请选择所属公司");
|
throw Oops.Bah("请选择所属公司");
|
||||||
if (input.DepartmentId <= 0)
|
if (input.DepartmentId <= 0)
|
||||||
|
|||||||
@@ -168,12 +168,11 @@ public class OnboardingPositionService : DbRepository<ObPosition>, IOnboardingPo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void NormalizeInput(OnboardingPositionAddInput input)
|
private static void NormalizeInput(OnboardingPositionAddInput input)
|
||||||
{
|
{
|
||||||
input.PositionCode = input.PositionCode?.Trim();
|
input.PositionCode = OnboardingCodeValidator.Normalize(input.PositionCode, "岗位编码", 50);
|
||||||
input.PositionName = input.PositionName?.Trim();
|
input.PositionName = input.PositionName?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(input.PositionCode)
|
if (string.IsNullOrWhiteSpace(input.PositionName))
|
||||||
|| string.IsNullOrWhiteSpace(input.PositionName))
|
|
||||||
{
|
{
|
||||||
throw Oops.Bah("请填写岗位编码和岗位名称");
|
throw Oops.Bah("请填写岗位名称");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.Status != "enable" && input.Status != "disable")
|
if (input.Status != "enable" && input.Status != "disable")
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ public class OnboardingTaskCreateInput
|
|||||||
[IdNotNull(ErrorMessage = "岗位Id不能为空")]
|
[IdNotNull(ErrorMessage = "岗位Id不能为空")]
|
||||||
public long PositionId { get; set; }
|
public long PositionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用工类型:full_time 正式用工,part_time 兼职用工。
|
||||||
|
/// </summary>
|
||||||
|
public string EmploymentType { get; set; } = EmployeeEmploymentType.FullTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 本次任务使用的入职模板 Id。
|
/// 本次任务使用的入职模板 Id。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public class OnboardingTaskService : IOnboardingTaskService
|
|||||||
if (string.IsNullOrWhiteSpace(input.Remark))
|
if (string.IsNullOrWhiteSpace(input.Remark))
|
||||||
throw Oops.Bah("请填写人事备注");
|
throw Oops.Bah("请填写人事备注");
|
||||||
|
|
||||||
|
ValidateEmploymentType(input.EmploymentType);
|
||||||
|
|
||||||
var companyEnabled = await _db.Queryable<ObCompany>()
|
var companyEnabled = await _db.Queryable<ObCompany>()
|
||||||
.AnyAsync(item => item.Id == input.CompanyId && item.Status == "enable");
|
.AnyAsync(item => item.Id == input.CompanyId && item.Status == "enable");
|
||||||
if (!companyEnabled)
|
if (!companyEnabled)
|
||||||
@@ -123,6 +125,7 @@ public class OnboardingTaskService : IOnboardingTaskService
|
|||||||
CompanyId = input.CompanyId,
|
CompanyId = input.CompanyId,
|
||||||
DepartmentId = input.DepartmentId,
|
DepartmentId = input.DepartmentId,
|
||||||
PositionId = input.PositionId,
|
PositionId = input.PositionId,
|
||||||
|
EmploymentType = input.EmploymentType,
|
||||||
OnboardingTemplateId = onboardingTemplate.Id,
|
OnboardingTemplateId = onboardingTemplate.Id,
|
||||||
AssignedReviewerUserId = input.AssignedReviewerUserId,
|
AssignedReviewerUserId = input.AssignedReviewerUserId,
|
||||||
LinkExpireAt = expiresAt,
|
LinkExpireAt = expiresAt,
|
||||||
@@ -366,6 +369,9 @@ public class OnboardingTaskService : IOnboardingTaskService
|
|||||||
CompanyId = sourceTask.CompanyId,
|
CompanyId = sourceTask.CompanyId,
|
||||||
DepartmentId = sourceTask.DepartmentId,
|
DepartmentId = sourceTask.DepartmentId,
|
||||||
PositionId = sourceTask.PositionId,
|
PositionId = sourceTask.PositionId,
|
||||||
|
EmploymentType = string.IsNullOrWhiteSpace(sourceTask.EmploymentType)
|
||||||
|
? EmployeeEmploymentType.FullTime
|
||||||
|
: sourceTask.EmploymentType,
|
||||||
OnboardingTemplateId = sourceTask.OnboardingTemplateId,
|
OnboardingTemplateId = sourceTask.OnboardingTemplateId,
|
||||||
AssignedReviewerUserId = sourceTask.AssignedReviewerUserId,
|
AssignedReviewerUserId = sourceTask.AssignedReviewerUserId,
|
||||||
LinkExpireAt = expiresAt,
|
LinkExpireAt = expiresAt,
|
||||||
@@ -553,4 +559,13 @@ public class OnboardingTaskService : IOnboardingTaskService
|
|||||||
if (endDate.Date < startDate.Date)
|
if (endDate.Date < startDate.Date)
|
||||||
throw Oops.Bah($"{rangeName}结束日期不能早于开始日期");
|
throw Oops.Bah($"{rangeName}结束日期不能早于开始日期");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ValidateEmploymentType(string employmentType)
|
||||||
|
{
|
||||||
|
if (employmentType != EmployeeEmploymentType.FullTime
|
||||||
|
&& employmentType != EmployeeEmploymentType.PartTime)
|
||||||
|
{
|
||||||
|
throw Oops.Bah("用工类型不正确,请选择正式员工或兼职员工");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-5
@@ -2,37 +2,112 @@ using System.ComponentModel;
|
|||||||
namespace SimpleAdmin.Web.Core;
|
namespace SimpleAdmin.Web.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提供正式员工档案相关 HTTP 接口。
|
/// 提供员工档案及人事维护相关 HTTP 接口。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiDescriptionSettings("Application", Tag = "入职正式员工档案")]
|
[ApiDescriptionSettings("Application", Tag = "入职员工档案")]
|
||||||
[Route("biz/onboarding/employee")]
|
[Route("biz/onboarding/employee")]
|
||||||
[RolePermission]
|
[RolePermission]
|
||||||
public class OnboardingEmployeeController
|
public class OnboardingEmployeeController
|
||||||
{
|
{
|
||||||
private readonly IOnboardingEmployeeService _service;
|
private readonly IOnboardingEmployeeService _service;
|
||||||
|
private readonly IFileService _fileService;
|
||||||
|
|
||||||
public OnboardingEmployeeController(IOnboardingEmployeeService service)
|
public OnboardingEmployeeController(
|
||||||
|
IOnboardingEmployeeService service,
|
||||||
|
IFileService fileService)
|
||||||
{
|
{
|
||||||
_service = service;
|
_service = service;
|
||||||
|
_fileService = fileService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分页查询正式员工档案。
|
/// 分页查询正式员工档案。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("page")]
|
[HttpGet("page")]
|
||||||
[DisplayName("分页查询正式员工档案")]
|
[DisplayName("分页查询员工档案")]
|
||||||
public async Task<dynamic> Page([FromQuery] OnboardingEmployeePageInput input)
|
public async Task<dynamic> Page([FromQuery] OnboardingEmployeePageInput input)
|
||||||
{
|
{
|
||||||
return await _service.Page(input);
|
return await _service.Page(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按当前列表筛选条件导出员工基本信息和合同快照。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("export")]
|
||||||
|
[DisplayName("导出员工明细")]
|
||||||
|
public async Task<FileContentResult> Export([FromBody] OnboardingEmployeePageInput input)
|
||||||
|
{
|
||||||
|
var file = await _service.Export(input);
|
||||||
|
return new FileContentResult(
|
||||||
|
file.FileBytes,
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||||
|
{
|
||||||
|
FileDownloadName = file.FileName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询正式员工详情及来源记录。
|
/// 查询正式员工详情及来源记录。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("detail")]
|
[HttpGet("detail")]
|
||||||
[DisplayName("查询正式员工详情")]
|
[DisplayName("查询员工档案详情")]
|
||||||
public async Task<dynamic> Detail([FromQuery] BaseIdInput input)
|
public async Task<dynamic> Detail([FromQuery] BaseIdInput input)
|
||||||
{
|
{
|
||||||
return await _service.Detail(input);
|
return await _service.Detail(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑员工用工、社保及合同性质等人事信息。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("hr-info/update")]
|
||||||
|
[DisplayName("编辑员工人事信息")]
|
||||||
|
public async Task UpdateHrInfo([FromBody] OnboardingEmployeeHrUpdateInput input)
|
||||||
|
{
|
||||||
|
await _service.UpdateHrInfo(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传转正等员工人事材料。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("attachment/upload")]
|
||||||
|
[Consumes("multipart/form-data")]
|
||||||
|
[RequestSizeLimit(20 * 1024 * 1024)]
|
||||||
|
[DisplayName("上传员工人事材料")]
|
||||||
|
public async Task<long> UploadAttachment([FromForm] IFormFile file)
|
||||||
|
{
|
||||||
|
if (file == null || file.Length == 0)
|
||||||
|
throw Oops.Bah("请选择需要上传的材料文件");
|
||||||
|
|
||||||
|
var allowedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
".pdf", ".doc", ".docx", ".xls", ".xlsx",
|
||||||
|
".jpg", ".jpeg", ".png", ".webp", ".zip"
|
||||||
|
};
|
||||||
|
var extension = Path.GetExtension(file.FileName);
|
||||||
|
if (!allowedExtensions.Contains(extension))
|
||||||
|
throw Oops.Bah("仅支持 PDF、Word、Excel、图片或 ZIP 文件");
|
||||||
|
|
||||||
|
return await _fileService.UploadFile(SysDictConst.FILE_ENGINE_ALIYUN_OSS, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 人工办理员工转正。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("regularization/confirm")]
|
||||||
|
[DisplayName("办理员工转正")]
|
||||||
|
public async Task Regularize([FromBody] OnboardingEmployeeRegularizeInput input)
|
||||||
|
{
|
||||||
|
await _service.Regularize(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 撤销员工转正。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("regularization/revoke")]
|
||||||
|
[DisplayName("撤销员工转正")]
|
||||||
|
public async Task RevokeRegularization(
|
||||||
|
[FromBody] OnboardingEmployeeRegularizationRevokeInput input)
|
||||||
|
{
|
||||||
|
await _service.RevokeRegularization(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
-- 员工合同快照:一名正式员工的一份任务合同对应一条记录。
|
||||||
|
-- 常用字段独立存储用于筛选导出,VariablesJson 按 VariableCode 保存全部标准及自定义变量。
|
||||||
|
-- 适用数据库:MySQL 8.0+
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `ob_employee_contract_snapshot` (
|
||||||
|
`Id` BIGINT NOT NULL COMMENT '主键',
|
||||||
|
`EmployeeId` BIGINT NOT NULL COMMENT '正式员工Id',
|
||||||
|
`TaskId` BIGINT NOT NULL COMMENT '来源入职任务Id',
|
||||||
|
`TaskContractId` BIGINT NOT NULL COMMENT '来源任务合同Id',
|
||||||
|
`TemplateId` BIGINT NOT NULL COMMENT '合同模板Id',
|
||||||
|
`TemplateVersionId` BIGINT NOT NULL COMMENT '合同模板版本Id',
|
||||||
|
`ContractName` VARCHAR(300) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '合同名称快照',
|
||||||
|
`ContractType` VARCHAR(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '合同类型快照',
|
||||||
|
`ContractStartDate` DATE DEFAULT NULL COMMENT '合同开始日期',
|
||||||
|
`ContractEndDate` DATE DEFAULT NULL COMMENT '合同结束日期',
|
||||||
|
`ContractTerm` VARCHAR(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '合同期限原始文本',
|
||||||
|
`ProbationStartDate` DATE DEFAULT NULL COMMENT '试用期开始日期',
|
||||||
|
`ProbationEndDate` DATE DEFAULT NULL COMMENT '试用期结束日期',
|
||||||
|
`ProbationTerm` VARCHAR(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '试用期期限原始文本',
|
||||||
|
`ProbationSalary` DECIMAL(18,2) DEFAULT NULL COMMENT '试用薪资,对应hr.ProbationSalary,不自动推算',
|
||||||
|
`BaseSalary` DECIMAL(18,2) DEFAULT NULL COMMENT '基本工资',
|
||||||
|
`PositionSalary` DECIMAL(18,2) DEFAULT NULL COMMENT '岗位工资',
|
||||||
|
`PerformanceAllowance` DECIMAL(18,2) DEFAULT NULL COMMENT '绩效津贴',
|
||||||
|
`AttendanceBonus` DECIMAL(18,2) DEFAULT NULL COMMENT '全勤奖',
|
||||||
|
`RegularSalary` DECIMAL(18,2) DEFAULT NULL COMMENT '转正后薪资',
|
||||||
|
`WorkCity` VARCHAR(300) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '工作地点或城市',
|
||||||
|
`PositionName` VARCHAR(300) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '合同岗位名称',
|
||||||
|
`EmployeeNo` VARCHAR(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '合同员工工号',
|
||||||
|
`VariablesJson` JSON NOT NULL COMMENT '按VariableCode索引的完整合同变量快照',
|
||||||
|
`SnapshotVersion` INT NOT NULL DEFAULT 1 COMMENT '快照结构版本',
|
||||||
|
`SnapshotAt` DATETIME(3) NOT NULL COMMENT '快照生成时间',
|
||||||
|
`ExtJson` LONGTEXT COLLATE utf8mb4_unicode_ci COMMENT '扩展信息',
|
||||||
|
`Status` VARCHAR(20) COLLATE utf8mb4_unicode_ci DEFAULT 'enable' COMMENT '记录状态',
|
||||||
|
`CreateTime` DATETIME(3) DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`UpdateTime` DATETIME(3) DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`CreateUserId` BIGINT DEFAULT NULL COMMENT '创建者Id',
|
||||||
|
`UpdateUserId` BIGINT DEFAULT NULL COMMENT '修改者Id',
|
||||||
|
`CreateUser` VARCHAR(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人',
|
||||||
|
`UpdateUser` VARCHAR(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人',
|
||||||
|
`IsDelete` TINYINT(1) DEFAULT 0 COMMENT '软删除',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
UNIQUE KEY `uk_ob_employee_contract_snapshot_contract` (`TaskContractId`),
|
||||||
|
KEY `idx_ob_employee_contract_snapshot_employee` (`EmployeeId`, `IsDelete`),
|
||||||
|
KEY `idx_ob_employee_contract_snapshot_task` (`TaskId`, `IsDelete`),
|
||||||
|
KEY `idx_ob_employee_contract_snapshot_dates` (`ContractStartDate`, `ContractEndDate`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='正式员工合同业务快照';
|
||||||
|
|
||||||
|
-- 为已经审核建档且仍保留任务合同变量的员工补建快照。
|
||||||
|
INSERT IGNORE INTO `ob_employee_contract_snapshot` (
|
||||||
|
`Id`, `EmployeeId`, `TaskId`, `TaskContractId`, `TemplateId`, `TemplateVersionId`,
|
||||||
|
`ContractName`, `ContractType`, `ContractStartDate`, `ContractEndDate`, `ContractTerm`,
|
||||||
|
`ProbationStartDate`, `ProbationEndDate`, `ProbationTerm`, `ProbationSalary`,
|
||||||
|
`BaseSalary`, `PositionSalary`, `PerformanceAllowance`, `AttendanceBonus`, `RegularSalary`,
|
||||||
|
`WorkCity`, `PositionName`, `EmployeeNo`, `VariablesJson`, `SnapshotVersion`, `SnapshotAt`,
|
||||||
|
`Status`, `CreateTime`, `CreateUserId`, `CreateUser`, `IsDelete`
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
contract.`Id`,
|
||||||
|
task.`EmployeeId`,
|
||||||
|
task.`Id`,
|
||||||
|
contract.`Id`,
|
||||||
|
contract.`TemplateId`,
|
||||||
|
contract.`TemplateVersionId`,
|
||||||
|
contract.`ContractName`,
|
||||||
|
contract.`ContractType`,
|
||||||
|
CASE
|
||||||
|
WHEN TRIM(variable.`ContractStartDateText`) REGEXP '^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$'
|
||||||
|
THEN STR_TO_DATE(TRIM(variable.`ContractStartDateText`), '%Y-%m-%d')
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN TRIM(variable.`ContractEndDateText`) REGEXP '^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$'
|
||||||
|
THEN STR_TO_DATE(TRIM(variable.`ContractEndDateText`), '%Y-%m-%d')
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
variable.`ContractTerm`,
|
||||||
|
CASE
|
||||||
|
WHEN TRIM(variable.`ProbationStartDateText`) REGEXP '^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$'
|
||||||
|
THEN STR_TO_DATE(TRIM(variable.`ProbationStartDateText`), '%Y-%m-%d')
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN TRIM(variable.`ProbationEndDateText`) REGEXP '^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$'
|
||||||
|
THEN STR_TO_DATE(TRIM(variable.`ProbationEndDateText`), '%Y-%m-%d')
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
variable.`ProbationTerm`,
|
||||||
|
CASE WHEN TRIM(variable.`ProbationSalaryText`) REGEXP '^-?[0-9]+([.][0-9]+)?$'
|
||||||
|
THEN CAST(TRIM(variable.`ProbationSalaryText`) AS DECIMAL(18,2)) ELSE NULL END,
|
||||||
|
CASE WHEN TRIM(variable.`BaseSalaryText`) REGEXP '^-?[0-9]+([.][0-9]+)?$'
|
||||||
|
THEN CAST(TRIM(variable.`BaseSalaryText`) AS DECIMAL(18,2)) ELSE NULL END,
|
||||||
|
CASE WHEN TRIM(variable.`PositionSalaryText`) REGEXP '^-?[0-9]+([.][0-9]+)?$'
|
||||||
|
THEN CAST(TRIM(variable.`PositionSalaryText`) AS DECIMAL(18,2)) ELSE NULL END,
|
||||||
|
CASE WHEN TRIM(variable.`PerformanceAllowanceText`) REGEXP '^-?[0-9]+([.][0-9]+)?$'
|
||||||
|
THEN CAST(TRIM(variable.`PerformanceAllowanceText`) AS DECIMAL(18,2)) ELSE NULL END,
|
||||||
|
CASE WHEN TRIM(variable.`AttendanceBonusText`) REGEXP '^-?[0-9]+([.][0-9]+)?$'
|
||||||
|
THEN CAST(TRIM(variable.`AttendanceBonusText`) AS DECIMAL(18,2)) ELSE NULL END,
|
||||||
|
CASE WHEN TRIM(variable.`RegularSalaryText`) REGEXP '^-?[0-9]+([.][0-9]+)?$'
|
||||||
|
THEN CAST(TRIM(variable.`RegularSalaryText`) AS DECIMAL(18,2)) ELSE NULL END,
|
||||||
|
variable.`WorkCity`,
|
||||||
|
variable.`PositionName`,
|
||||||
|
variable.`EmployeeNo`,
|
||||||
|
COALESCE(variable.`VariablesJson`, JSON_OBJECT()),
|
||||||
|
1,
|
||||||
|
COALESCE(task.`AuditedAt`, employee.`CreateTime`, NOW(3)),
|
||||||
|
'enable',
|
||||||
|
COALESCE(employee.`CreateTime`, NOW(3)),
|
||||||
|
employee.`CreateUserId`,
|
||||||
|
employee.`CreateUser`,
|
||||||
|
0
|
||||||
|
FROM `ob_onboarding_task` task
|
||||||
|
INNER JOIN `ob_employee` employee
|
||||||
|
ON employee.`Id` = task.`EmployeeId`
|
||||||
|
INNER JOIN `ob_task_contract` contract
|
||||||
|
ON contract.`TaskId` = task.`Id`
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
row_value.`TaskContractId`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ContractStartDate' THEN row_value.`VariableValue` END) AS `ContractStartDateText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ContractEndDate' THEN row_value.`VariableValue` END) AS `ContractEndDateText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ContractTerm' THEN row_value.`VariableValue` END) AS `ContractTerm`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ProbationStartDate' THEN row_value.`VariableValue` END) AS `ProbationStartDateText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ProbationEndDate' THEN row_value.`VariableValue` END) AS `ProbationEndDateText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ProbationTerm' THEN row_value.`VariableValue` END) AS `ProbationTerm`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.ProbationSalary' THEN row_value.`VariableValue` END) AS `ProbationSalaryText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.BaseSalary' THEN row_value.`VariableValue` END) AS `BaseSalaryText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.PositionSalary' THEN row_value.`VariableValue` END) AS `PositionSalaryText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.PerformanceAllowance' THEN row_value.`VariableValue` END) AS `PerformanceAllowanceText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.AttendanceBonus' THEN row_value.`VariableValue` END) AS `AttendanceBonusText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.RegularSalary' THEN row_value.`VariableValue` END) AS `RegularSalaryText`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.WorkCity' THEN row_value.`VariableValue` END) AS `WorkCity`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.PositionName' THEN row_value.`VariableValue` END) AS `PositionName`,
|
||||||
|
MAX(CASE WHEN row_value.`VariableCode` = 'hr.EmployeeNo' THEN row_value.`VariableValue` END) AS `EmployeeNo`,
|
||||||
|
JSON_OBJECTAGG(
|
||||||
|
row_value.`VariableCode`,
|
||||||
|
JSON_OBJECT(
|
||||||
|
'variableName', row_value.`VariableName`,
|
||||||
|
'variableType', row_value.`VariableType`,
|
||||||
|
'dataType', row_value.`DataType`,
|
||||||
|
'variableValue', row_value.`VariableValue`,
|
||||||
|
'sourceType', row_value.`SourceType`,
|
||||||
|
'isManualOverride', row_value.`IsManualOverride`,
|
||||||
|
'sortCode', row_value.`SortCode`
|
||||||
|
)
|
||||||
|
) AS `VariablesJson`
|
||||||
|
FROM `ob_task_contract_variable` row_value
|
||||||
|
WHERE row_value.`IsDelete` = 0
|
||||||
|
GROUP BY row_value.`TaskContractId`
|
||||||
|
) variable
|
||||||
|
ON variable.`TaskContractId` = contract.`Id`
|
||||||
|
WHERE task.`EmployeeId` IS NOT NULL
|
||||||
|
AND task.`IsDelete` = 0
|
||||||
|
AND employee.`IsDelete` = 0
|
||||||
|
AND contract.`IsDelete` = 0;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS `员工合同快照数量`,
|
||||||
|
COUNT(DISTINCT `EmployeeId`) AS `已归档员工数量`,
|
||||||
|
COUNT(DISTINCT `TaskContractId`) AS `已归档合同数量`
|
||||||
|
FROM `ob_employee_contract_snapshot`
|
||||||
|
WHERE `IsDelete` = 0;
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
-- 员工用工类型、人工转正、社保状态及人事变更材料。
|
||||||
|
-- 适用数据库:MySQL 8.0+
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS ob_upgrade_employee_hr_management;
|
||||||
|
DELIMITER $$
|
||||||
|
|
||||||
|
CREATE PROCEDURE ob_upgrade_employee_hr_management()
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_onboarding_task'
|
||||||
|
AND COLUMN_NAME = 'EmploymentType'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE ob_onboarding_task
|
||||||
|
ADD COLUMN EmploymentType VARCHAR(20) COLLATE utf8mb4_unicode_ci
|
||||||
|
NOT NULL DEFAULT 'full_time' COMMENT '用工类型:full_time正式、part_time兼职'
|
||||||
|
AFTER PositionId;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee'
|
||||||
|
AND COLUMN_NAME = 'EmploymentType'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE ob_employee
|
||||||
|
ADD COLUMN EmploymentType VARCHAR(20) COLLATE utf8mb4_unicode_ci
|
||||||
|
NOT NULL DEFAULT 'full_time' COMMENT '用工类型:full_time正式、part_time兼职'
|
||||||
|
AFTER PositionId;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee'
|
||||||
|
AND COLUMN_NAME = 'RegularizationStatus'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE ob_employee
|
||||||
|
ADD COLUMN RegularizationStatus VARCHAR(20) COLLATE utf8mb4_unicode_ci
|
||||||
|
NOT NULL DEFAULT 'probation' COMMENT '转正状态:probation、regular、not_applicable'
|
||||||
|
AFTER EmploymentType;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee'
|
||||||
|
AND COLUMN_NAME = 'SocialInsuranceStatus'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE ob_employee
|
||||||
|
ADD COLUMN SocialInsuranceStatus VARCHAR(20) COLLATE utf8mb4_unicode_ci
|
||||||
|
NOT NULL DEFAULT 'unknown' COMMENT '社保状态:unknown、not_purchased、purchased、stopped、not_applicable'
|
||||||
|
AFTER RegularizationStatus;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.TABLES
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee_contract_snapshot'
|
||||||
|
) AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee_contract_snapshot'
|
||||||
|
AND COLUMN_NAME = 'ProbationSalary'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE ob_employee_contract_snapshot
|
||||||
|
ADD COLUMN ProbationSalary DECIMAL(18,2) DEFAULT NULL
|
||||||
|
COMMENT '试用薪资,对应hr.ProbationSalary,不自动推算'
|
||||||
|
AFTER ProbationTerm;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
CALL ob_upgrade_employee_hr_management();
|
||||||
|
DROP PROCEDURE IF EXISTS ob_upgrade_employee_hr_management;
|
||||||
|
|
||||||
|
-- 历史任务及员工默认按正式用工处理;历史转正日期仅用于一次性初始化状态。
|
||||||
|
UPDATE ob_onboarding_task
|
||||||
|
SET EmploymentType = 'full_time'
|
||||||
|
WHERE EmploymentType IS NULL OR EmploymentType NOT IN ('full_time', 'part_time');
|
||||||
|
|
||||||
|
UPDATE ob_employee
|
||||||
|
SET
|
||||||
|
EmploymentType = CASE
|
||||||
|
WHEN EmploymentType = 'part_time' THEN 'part_time'
|
||||||
|
ELSE 'full_time'
|
||||||
|
END,
|
||||||
|
RegularizationStatus = CASE
|
||||||
|
WHEN EmploymentType = 'part_time' THEN 'not_applicable'
|
||||||
|
WHEN RegularizationDate IS NOT NULL THEN 'regular'
|
||||||
|
ELSE 'probation'
|
||||||
|
END,
|
||||||
|
SocialInsuranceStatus = CASE
|
||||||
|
WHEN SocialInsuranceStatus IN (
|
||||||
|
'unknown', 'not_purchased', 'purchased', 'stopped', 'not_applicable'
|
||||||
|
) THEN SocialInsuranceStatus
|
||||||
|
ELSE 'unknown'
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ob_employee_change_record (
|
||||||
|
Id BIGINT NOT NULL COMMENT '主键',
|
||||||
|
EmployeeId BIGINT NOT NULL COMMENT '员工Id',
|
||||||
|
ChangeType VARCHAR(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更类型',
|
||||||
|
EffectiveDate DATE DEFAULT NULL COMMENT '业务生效日期',
|
||||||
|
BeforeJson LONGTEXT COLLATE utf8mb4_unicode_ci COMMENT '变更前快照',
|
||||||
|
AfterJson LONGTEXT COLLATE utf8mb4_unicode_ci COMMENT '变更后快照',
|
||||||
|
Remark VARCHAR(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
|
||||||
|
ExtJson LONGTEXT COLLATE utf8mb4_unicode_ci COMMENT '扩展信息',
|
||||||
|
Status VARCHAR(20) COLLATE utf8mb4_unicode_ci DEFAULT 'enable' COMMENT '记录状态',
|
||||||
|
CreateTime DATETIME(3) DEFAULT NULL COMMENT '创建时间',
|
||||||
|
UpdateTime DATETIME(3) DEFAULT NULL COMMENT '更新时间',
|
||||||
|
CreateUserId BIGINT DEFAULT NULL COMMENT '创建者Id',
|
||||||
|
UpdateUserId BIGINT DEFAULT NULL COMMENT '修改者Id',
|
||||||
|
CreateUser VARCHAR(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人',
|
||||||
|
UpdateUser VARCHAR(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人',
|
||||||
|
IsDelete TINYINT(1) DEFAULT 0 COMMENT '软删除',
|
||||||
|
PRIMARY KEY (Id),
|
||||||
|
KEY idx_ob_employee_change_record_employee (EmployeeId, IsDelete, CreateTime),
|
||||||
|
KEY idx_ob_employee_change_record_type (ChangeType, EffectiveDate)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工人事信息变更记录';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ob_employee_attachment (
|
||||||
|
Id BIGINT NOT NULL COMMENT '主键',
|
||||||
|
EmployeeId BIGINT NOT NULL COMMENT '员工Id',
|
||||||
|
ChangeRecordId BIGINT DEFAULT NULL COMMENT '关联人事变更记录Id',
|
||||||
|
BusinessType VARCHAR(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '附件业务类型',
|
||||||
|
FileId BIGINT NOT NULL COMMENT '系统文件Id',
|
||||||
|
FileName VARCHAR(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '上传时文件名快照',
|
||||||
|
UploadedAt DATETIME(3) NOT NULL COMMENT '上传关联时间',
|
||||||
|
ExtJson LONGTEXT COLLATE utf8mb4_unicode_ci COMMENT '扩展信息',
|
||||||
|
Status VARCHAR(20) COLLATE utf8mb4_unicode_ci DEFAULT 'enable' COMMENT '记录状态',
|
||||||
|
CreateTime DATETIME(3) DEFAULT NULL COMMENT '创建时间',
|
||||||
|
UpdateTime DATETIME(3) DEFAULT NULL COMMENT '更新时间',
|
||||||
|
CreateUserId BIGINT DEFAULT NULL COMMENT '创建者Id',
|
||||||
|
UpdateUserId BIGINT DEFAULT NULL COMMENT '修改者Id',
|
||||||
|
CreateUser VARCHAR(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人',
|
||||||
|
UpdateUser VARCHAR(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人',
|
||||||
|
IsDelete TINYINT(1) DEFAULT 0 COMMENT '软删除',
|
||||||
|
PRIMARY KEY (Id),
|
||||||
|
KEY idx_ob_employee_attachment_employee (EmployeeId, BusinessType, IsDelete),
|
||||||
|
KEY idx_ob_employee_attachment_change (ChangeRecordId, IsDelete),
|
||||||
|
KEY idx_ob_employee_attachment_file (FileId)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工人事材料附件';
|
||||||
|
|
||||||
|
-- 标准试用薪资变量;已有同编码历史数据时恢复并统一为内置数值变量。
|
||||||
|
INSERT INTO ob_variable_definition (
|
||||||
|
Id, VariableCode, VariableName, VariableCategory, DataType,
|
||||||
|
IsBuiltIn, Required, SortCode, Remark, Status,
|
||||||
|
CreateTime, IsDelete
|
||||||
|
) VALUES (
|
||||||
|
841400000000115, 'hr.ProbationSalary', '试用薪资(元)', 'hr', 'number',
|
||||||
|
1, 0, 335, '合同约定试用期薪资,不自动推算', 'enable',
|
||||||
|
NOW(), 0
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
VariableName = VALUES(VariableName),
|
||||||
|
VariableCategory = 'hr',
|
||||||
|
DataType = 'number',
|
||||||
|
IsBuiltIn = 1,
|
||||||
|
SortCode = 335,
|
||||||
|
Remark = VALUES(Remark),
|
||||||
|
Status = 'enable',
|
||||||
|
IsDelete = 0;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
(SELECT COUNT(*) FROM ob_employee_change_record) AS '员工人事变更记录数',
|
||||||
|
(SELECT COUNT(*) FROM ob_employee_attachment) AS '员工人事附件数',
|
||||||
|
(SELECT COUNT(*) FROM ob_variable_definition
|
||||||
|
WHERE VariableCode = 'hr.ProbationSalary' AND IsDelete = 0) AS '试用薪资标准变量数';
|
||||||
@@ -49,6 +49,35 @@ BEGIN
|
|||||||
START TRANSACTION;
|
START TRANSACTION;
|
||||||
|
|
||||||
-- 一、删除任务运行数据及快照。
|
-- 一、删除任务运行数据及快照。
|
||||||
|
-- 兼容尚未执行员工人事管理迁移的旧数据库。
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM information_schema.TABLES
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee_attachment'
|
||||||
|
) THEN
|
||||||
|
DELETE FROM ob_employee_attachment;
|
||||||
|
END IF;
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM information_schema.TABLES
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'ob_employee_change_record'
|
||||||
|
) THEN
|
||||||
|
DELETE FROM ob_employee_change_record;
|
||||||
|
END IF;
|
||||||
|
-- 兼容尚未执行员工合同快照迁移的旧数据库。
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM `information_schema`.`TABLES`
|
||||||
|
WHERE `TABLE_SCHEMA` = DATABASE()
|
||||||
|
AND `TABLE_NAME` = 'ob_employee_contract_snapshot'
|
||||||
|
) THEN
|
||||||
|
SET @delete_employee_contract_snapshot_sql = 'DELETE FROM `ob_employee_contract_snapshot`';
|
||||||
|
PREPARE delete_employee_contract_snapshot_statement FROM @delete_employee_contract_snapshot_sql;
|
||||||
|
EXECUTE delete_employee_contract_snapshot_statement;
|
||||||
|
DEALLOCATE PREPARE delete_employee_contract_snapshot_statement;
|
||||||
|
END IF;
|
||||||
DELETE FROM `ob_task_operation_log`;
|
DELETE FROM `ob_task_operation_log`;
|
||||||
DELETE FROM `ob_task_audit`;
|
DELETE FROM `ob_task_audit`;
|
||||||
DELETE FROM `ob_task_material`;
|
DELETE FROM `ob_task_material`;
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ BEGIN
|
|||||||
'ob_electronic_sign_event',
|
'ob_electronic_sign_event',
|
||||||
'ob_electronic_sign_document',
|
'ob_electronic_sign_document',
|
||||||
'ob_electronic_sign_process',
|
'ob_electronic_sign_process',
|
||||||
|
'ob_employee_attachment',
|
||||||
|
'ob_employee_change_record',
|
||||||
|
'ob_employee_contract_snapshot',
|
||||||
'ob_task_operation_log',
|
'ob_task_operation_log',
|
||||||
'ob_task_audit',
|
'ob_task_audit',
|
||||||
'ob_task_material',
|
'ob_task_material',
|
||||||
@@ -61,6 +64,9 @@ BEGIN
|
|||||||
'ob_electronic_sign_event',
|
'ob_electronic_sign_event',
|
||||||
'ob_electronic_sign_document',
|
'ob_electronic_sign_document',
|
||||||
'ob_electronic_sign_process',
|
'ob_electronic_sign_process',
|
||||||
|
'ob_employee_attachment',
|
||||||
|
'ob_employee_change_record',
|
||||||
|
'ob_employee_contract_snapshot',
|
||||||
'ob_task_operation_log',
|
'ob_task_operation_log',
|
||||||
'ob_task_audit',
|
'ob_task_audit',
|
||||||
'ob_task_material',
|
'ob_task_material',
|
||||||
@@ -166,6 +172,9 @@ BEGIN
|
|||||||
'ob_electronic_sign_event',
|
'ob_electronic_sign_event',
|
||||||
'ob_electronic_sign_document',
|
'ob_electronic_sign_document',
|
||||||
'ob_electronic_sign_process',
|
'ob_electronic_sign_process',
|
||||||
|
'ob_employee_attachment',
|
||||||
|
'ob_employee_change_record',
|
||||||
|
'ob_employee_contract_snapshot',
|
||||||
'ob_task_operation_log',
|
'ob_task_operation_log',
|
||||||
'ob_task_audit',
|
'ob_task_audit',
|
||||||
'ob_task_material',
|
'ob_task_material',
|
||||||
@@ -199,6 +208,9 @@ WHERE `TABLE_SCHEMA` = DATABASE()
|
|||||||
'ob_electronic_sign_event',
|
'ob_electronic_sign_event',
|
||||||
'ob_electronic_sign_document',
|
'ob_electronic_sign_document',
|
||||||
'ob_electronic_sign_process',
|
'ob_electronic_sign_process',
|
||||||
|
'ob_employee_attachment',
|
||||||
|
'ob_employee_change_record',
|
||||||
|
'ob_employee_contract_snapshot',
|
||||||
'ob_task_operation_log',
|
'ob_task_operation_log',
|
||||||
'ob_task_audit',
|
'ob_task_audit',
|
||||||
'ob_task_material',
|
'ob_task_material',
|
||||||
|
|||||||
Reference in New Issue
Block a user