1
This commit is contained in:
+265
@@ -0,0 +1,265 @@
|
||||
namespace SimpleAdmin.Application;
|
||||
|
||||
public class OnboardingPublicSubmissionService : IOnboardingPublicSubmissionService
|
||||
{
|
||||
private readonly ISqlSugarClient _db;
|
||||
private readonly IOnboardingContractSnapshotService _snapshotService;
|
||||
|
||||
public OnboardingPublicSubmissionService(
|
||||
ISqlSugarClient db,
|
||||
IOnboardingContractSnapshotService snapshotService)
|
||||
{
|
||||
_db = db;
|
||||
_snapshotService = snapshotService;
|
||||
}
|
||||
|
||||
public async Task SaveDynamicValues(OnboardingSaveDynamicValuesInput input)
|
||||
{
|
||||
var task = await GetEditableTask(input.Token);
|
||||
var requirements = await GetEffectiveFormRequirements(task);
|
||||
var fieldIds = input.Values.Select(item => item.FieldId).Distinct().ToList();
|
||||
var hasInapplicableField = fieldIds.Any(id => requirements.All(requirement => requirement.FieldId != id));
|
||||
if (hasInapplicableField)
|
||||
throw Oops.Bah("填写字段不适用于当前公司或岗位");
|
||||
|
||||
var fields = await _db.Queryable<ObFormField>()
|
||||
.Where(field => fieldIds.Contains(field.Id) && field.Status == "enable")
|
||||
.ToListAsync();
|
||||
foreach (var value in input.Values)
|
||||
{
|
||||
var field = fields.FirstOrDefault(item => item.Id == value.FieldId)
|
||||
?? throw Oops.Bah("填写字段不存在或已停用");
|
||||
var old = await _db.Queryable<ObTaskFormValue>()
|
||||
.FirstAsync(item => item.TaskId == task.Id && item.FieldId == field.Id);
|
||||
var row = new ObTaskFormValue
|
||||
{
|
||||
Id = old?.Id ?? global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||||
TaskId = task.Id,
|
||||
FieldId = field.Id,
|
||||
FieldCode = field.FieldCode,
|
||||
FieldName = field.FieldName,
|
||||
FieldValue = value.FieldValue,
|
||||
SubmitRound = task.SubmitCount + 1
|
||||
};
|
||||
|
||||
if (old == null)
|
||||
{
|
||||
await _db.Insertable(row).ExecuteCommandAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await _db.Updateable(row).ExecuteCommandAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveMaterial(OnboardingSaveMaterialInput input)
|
||||
{
|
||||
var task = await GetEditableTask(input.Token);
|
||||
var requirements = await GetEffectiveMaterialRequirements(task);
|
||||
var materialIsApplicable = requirements.Any(requirement => requirement.MaterialTypeId == input.MaterialTypeId);
|
||||
if (!materialIsApplicable)
|
||||
throw Oops.Bah("资料类型不适用于当前公司或岗位");
|
||||
|
||||
var material = await _db.Queryable<ObMaterialType>()
|
||||
.FirstAsync(item => item.Id == input.MaterialTypeId && item.Status == "enable")
|
||||
?? throw Oops.Bah("资料类型不存在或已停用");
|
||||
var fileExists = await _db.Queryable<SysFile>().AnyAsync(item => item.Id == input.FileId);
|
||||
if (!fileExists)
|
||||
throw Oops.Bah("上传文件不存在");
|
||||
|
||||
var materialCount = await _db.Queryable<ObTaskMaterial>()
|
||||
.CountAsync(item => item.TaskId == task.Id && item.MaterialTypeId == input.MaterialTypeId);
|
||||
if (materialCount >= material.MaxFileCount)
|
||||
throw Oops.Bah("该资料已达到最大上传数量");
|
||||
|
||||
await _db.Insertable(new ObTaskMaterial
|
||||
{
|
||||
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||||
TaskId = task.Id,
|
||||
MaterialTypeId = material.Id,
|
||||
MaterialName = material.MaterialName,
|
||||
FileId = input.FileId,
|
||||
SubmitRound = task.SubmitCount + 1,
|
||||
UploadedAt = DateTime.Now
|
||||
}).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public async Task ConfirmContract(OnboardingConfirmContractInput input)
|
||||
{
|
||||
var task = await GetEditableTask(input.Token);
|
||||
var contract = await _db.Queryable<ObTaskContract>()
|
||||
.FirstAsync(item => item.Id == input.TaskContractId && item.TaskId == task.Id)
|
||||
?? throw Oops.Bah("合同不存在");
|
||||
|
||||
contract.ReadAt ??= DateTime.Now;
|
||||
contract.ConfirmedAt = DateTime.Now;
|
||||
contract.ContractStatus = TaskContractStatus.Confirmed;
|
||||
await _db.Updateable(contract).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public async Task Submit(OnboardingSubmitInput input)
|
||||
{
|
||||
var task = await GetEditableTask(input.Token);
|
||||
var info = await _db.Queryable<ObTaskEmployeeInfo>()
|
||||
.FirstAsync(item => item.TaskId == task.Id);
|
||||
var basicInfoIncomplete = info == null
|
||||
|| string.IsNullOrWhiteSpace(info.EmployeeName)
|
||||
|| string.IsNullOrWhiteSpace(info.Phone)
|
||||
|| string.IsNullOrWhiteSpace(info.IdCardNumber);
|
||||
if (basicInfoIncomplete)
|
||||
throw Oops.Bah("请先完整填写基本信息");
|
||||
|
||||
await _snapshotService.Build(task.Id, info);
|
||||
|
||||
var requiredFields = (await GetEffectiveFormRequirements(task))
|
||||
.Where(item => item.Required)
|
||||
.Select(item => item.FieldId)
|
||||
.ToList();
|
||||
var values = await _db.Queryable<ObTaskFormValue>()
|
||||
.Where(item => item.TaskId == task.Id)
|
||||
.ToListAsync();
|
||||
var hasMissingRequiredField = requiredFields.Any(
|
||||
id => values.All(item => item.FieldId != id || string.IsNullOrWhiteSpace(item.FieldValue)));
|
||||
if (hasMissingRequiredField)
|
||||
throw Oops.Bah("仍有必填动态信息未填写");
|
||||
|
||||
var requiredMaterials = (await GetEffectiveMaterialRequirements(task))
|
||||
.Where(item => item.Required)
|
||||
.Select(item => item.MaterialTypeId)
|
||||
.ToList();
|
||||
var materials = await _db.Queryable<ObTaskMaterial>()
|
||||
.Where(item => item.TaskId == task.Id)
|
||||
.ToListAsync();
|
||||
var hasMissingRequiredMaterial = requiredMaterials.Any(
|
||||
id => materials.All(item => item.MaterialTypeId != id));
|
||||
if (hasMissingRequiredMaterial)
|
||||
throw Oops.Bah("仍有必传资料未上传");
|
||||
|
||||
await EnsureRequiredContractVariables(task);
|
||||
|
||||
var hasUnconfirmedRequiredContract = await _db.Queryable<ObTaskContract>()
|
||||
.AnyAsync(item => item.TaskId == task.Id && item.Required && item.ConfirmedAt == null);
|
||||
if (hasUnconfirmedRequiredContract)
|
||||
throw Oops.Bah("请确认阅读全部必签合同");
|
||||
|
||||
task.TaskStatus = OnboardingTaskStatus.Reviewing;
|
||||
task.SubmitCount++;
|
||||
task.LastSubmittedAt = DateTime.Now;
|
||||
task.RejectReason = null;
|
||||
await _db.Updateable(task).ExecuteCommandAsync();
|
||||
|
||||
await _db.Insertable(new ObTaskAudit
|
||||
{
|
||||
Id = global::SimpleAdmin.Core.Utils.CommonUtils.GetSingleId(),
|
||||
TaskId = task.Id,
|
||||
AuditRound = task.SubmitCount,
|
||||
AuditAction = OnboardingAuditAction.Submit,
|
||||
AuditOpinion = "员工提交入职资料",
|
||||
SnapshotJson = global::System.Text.Json.JsonSerializer.Serialize(new { info, values, materials }),
|
||||
AuditedAt = DateTime.Now
|
||||
}).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public async Task ValidateMaterialUpload(OnboardingMaterialUploadValidationInput input)
|
||||
{
|
||||
var task = await GetEditableTask(input.Token);
|
||||
var requirements = await GetEffectiveMaterialRequirements(task);
|
||||
var materialIsApplicable = requirements.Any(requirement => requirement.MaterialTypeId == input.MaterialTypeId);
|
||||
if (!materialIsApplicable)
|
||||
throw Oops.Bah("资料类型不适用于当前公司或岗位");
|
||||
|
||||
var material = await _db.Queryable<ObMaterialType>()
|
||||
.FirstAsync(item => item.Id == input.MaterialTypeId && item.Status == "enable")
|
||||
?? throw Oops.Bah("资料类型不存在或已停用");
|
||||
var suffix = Path.GetExtension(input.FileName).TrimStart('.').ToLowerInvariant();
|
||||
var allowedExtensions = (material.AllowedExtensions ?? string.Empty)
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Select(item => item.TrimStart('.').ToLowerInvariant());
|
||||
var suffixNotAllowed = allowedExtensions.Any() && !allowedExtensions.Contains(suffix);
|
||||
if (suffixNotAllowed)
|
||||
throw Oops.Bah("该资料不允许上传此文件类型");
|
||||
|
||||
var fileTooLarge = material.MaxFileSizeMb.HasValue
|
||||
&& input.FileLength > material.MaxFileSizeMb.Value * 1024L * 1024L;
|
||||
if (fileTooLarge)
|
||||
throw Oops.Bah("文件超过允许的大小");
|
||||
|
||||
var materialCount = await _db.Queryable<ObTaskMaterial>()
|
||||
.CountAsync(item => item.TaskId == task.Id && item.MaterialTypeId == input.MaterialTypeId);
|
||||
if (materialCount >= material.MaxFileCount)
|
||||
throw Oops.Bah("该资料已达到最大上传数量");
|
||||
}
|
||||
|
||||
private async Task EnsureRequiredContractVariables(ObOnboardingTask task)
|
||||
{
|
||||
var contracts = await _db.Queryable<ObTaskContract>()
|
||||
.Where(item => item.TaskId == task.Id)
|
||||
.ToListAsync();
|
||||
var contractIds = contracts.Select(contract => contract.Id).ToList();
|
||||
var templateVersionIds = contracts.Select(contract => contract.TemplateVersionId).ToList();
|
||||
var variables = await _db.Queryable<ObTaskContractVariable>()
|
||||
.Where(item => contractIds.Contains(item.TaskContractId))
|
||||
.ToListAsync();
|
||||
var definitions = await _db.Queryable<ObTemplateVariable>()
|
||||
.Where(item => templateVersionIds.Contains(item.TemplateVersionId) && item.Required)
|
||||
.ToListAsync();
|
||||
var missing = contracts
|
||||
.SelectMany(contract => definitions
|
||||
.Where(definition => definition.TemplateVersionId == contract.TemplateVersionId)
|
||||
.Select(definition => new { contract, definition }))
|
||||
.FirstOrDefault(item => string.IsNullOrWhiteSpace(
|
||||
variables.FirstOrDefault(variable => variable.TaskContractId == item.contract.Id
|
||||
&& variable.VariableCode == item.definition.VariableCode)
|
||||
?.VariableValue));
|
||||
if (missing != null)
|
||||
throw Oops.Bah($"合同“{missing.contract.ContractName}”的必填变量“{missing.definition.VariableName}”尚未填写");
|
||||
}
|
||||
|
||||
private async Task<List<ObFormRequirement>> GetEffectiveFormRequirements(ObOnboardingTask task)
|
||||
{
|
||||
var requirements = await _db.Queryable<ObFormRequirement>()
|
||||
.Where(item => item.CompanyId == task.CompanyId
|
||||
&& (item.PositionId == null || item.PositionId == task.PositionId))
|
||||
.ToListAsync();
|
||||
|
||||
return requirements
|
||||
.GroupBy(item => item.FieldId)
|
||||
.Select(group => group.FirstOrDefault(item => item.PositionId == task.PositionId) ?? group.First())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task<List<ObMaterialRequirement>> GetEffectiveMaterialRequirements(ObOnboardingTask task)
|
||||
{
|
||||
var requirements = await _db.Queryable<ObMaterialRequirement>()
|
||||
.Where(item => item.CompanyId == task.CompanyId
|
||||
&& (item.PositionId == null || item.PositionId == task.PositionId))
|
||||
.ToListAsync();
|
||||
|
||||
return requirements
|
||||
.GroupBy(item => item.MaterialTypeId)
|
||||
.Select(group => group.FirstOrDefault(item => item.PositionId == task.PositionId) ?? group.First())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task<ObOnboardingTask> GetEditableTask(string token)
|
||||
{
|
||||
var hash = Convert.ToHexString(
|
||||
global::System.Security.Cryptography.SHA256.HashData(
|
||||
global::System.Text.Encoding.UTF8.GetBytes(token)));
|
||||
var link = await _db.Queryable<ObTaskLink>().FirstAsync(item => item.TokenHash == hash)
|
||||
?? throw Oops.Bah("链接不存在");
|
||||
var linkUnavailable = link.LinkStatus != OnboardingLinkStatus.Active || link.ExpiresAt <= DateTime.Now;
|
||||
if (linkUnavailable)
|
||||
throw Oops.Bah("链接已失效");
|
||||
|
||||
var task = await _db.Queryable<ObOnboardingTask>().InSingleAsync(link.TaskId)
|
||||
?? throw Oops.Bah("入职任务不存在");
|
||||
var taskNotEditable = task.TaskStatus != OnboardingTaskStatus.PendingFill
|
||||
&& task.TaskStatus != OnboardingTaskStatus.Rejected;
|
||||
if (taskNotEditable)
|
||||
throw Oops.Bah("当前任务不可编辑");
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user