Files
OnboardXAdmin/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoContractRequestBuilder.cs
T
2026-11-10 09:53:14 +08:00

207 lines
8.1 KiB
C#

namespace SimpleAdmin.Application;
/// <summary>发起签约前按文件冻结的签字、盖章要求。</summary>
internal sealed record OnboardingSigningDocument(
string DocumentId,
string EmployeeSignKeyword,
string CompanySealKeyword,
bool? CompanySealEnabled,
bool? AcrossPageSealEnabled);
/// <summary>构造契约锁请求;无数据库和网络访问,员工签字始终覆盖全部文件1。</summary>
internal static class QiyuesuoContractRequestBuilder
{
internal static bool CompanySealEnabled(bool? enabled) => enabled ?? true;
internal static bool AcrossPageSealEnabled(bool? enabled, QiyuesuoSettingOptions setting) =>
enabled ?? setting.EnableAcrossPageSeal;
internal static bool RequiresCompanySign(
bool? companySealEnabled,
bool? acrossPageSealEnabled,
QiyuesuoSettingOptions setting) =>
CompanySealEnabled(companySealEnabled) || AcrossPageSealEnabled(acrossPageSealEnabled, setting);
internal static object BuildDraft(QiyuesuoCreateDraftInput input)
{
long sealId = 0;
if (input.RequiresCompanySign && !long.TryParse(input.SealId, out sealId))
throw Oops.Bah("契约锁 SealId 配置格式不正确");
var signatories = new List<object>
{
new
{
tenantType = "PERSONAL",
tenantName = input.EmployeeName,
receiver = new
{
name = input.EmployeeName,
contact = input.EmployeeMobile,
contactType = "MOBILE"
},
serialNo = 1,
signatoryNo = "employee",
actions = new object[]
{
new
{
type = "PERSONAL",
name = "员工本人签署",
serialNo = 0,
actionNo = "employee_sign"
}
}
}
};
if (input.RequiresCompanySign)
{
signatories.Add(new
{
tenantType = "COMPANY",
tenantName = input.CompanyName,
receiver = new
{
name = input.CreatorName,
contact = input.CreatorMobile,
contactType = "MOBILE"
},
serialNo = 2,
signatoryNo = "company",
actions = new object[]
{
new
{
type = "COMPANY",
name = "企业人工盖章",
serialNo = 0,
autoSign = false,
sealId = input.SealId,
corpSealIds = new[] { sealId },
actionNo = "company_seal"
}
}
});
}
return new
{
bizId = input.BizId,
subject = input.Subject,
tenantName = input.CompanyName,
description = "OnboardX 入职电子签",
send = false,
callbackUrl = string.IsNullOrWhiteSpace(input.CallbackUrl) ? null : input.CallbackUrl.Trim(),
category = new { id = input.CategoryId },
creator = new
{
name = input.CreatorName,
contact = input.CreatorMobile,
contactType = "MOBILE"
},
signatories
};
}
internal static List<QiyuesuoStamperInput> BuildStampers(
IReadOnlyList<OnboardingSigningDocument> documents,
string personalSignatoryId,
string companyActionId,
string sealId,
QiyuesuoSettingOptions setting)
{
if (documents.Count == 0)
throw Oops.Bah("当前电子签流程没有可签署的文件");
if (documents.Any(item => string.IsNullOrWhiteSpace(item.DocumentId)))
throw Oops.Bah("契约锁未返回签署文档 Id");
if (string.IsNullOrWhiteSpace(personalSignatoryId))
throw Oops.Bah("契约锁未返回员工签署方 Id");
if (documents.Any(item => RequiresCompanySign(item.CompanySealEnabled, item.AcrossPageSealEnabled, setting))
&& string.IsNullOrWhiteSpace(companyActionId))
throw Oops.Bah("契约锁草稿未返回企业盖章动作");
var stampers = new List<QiyuesuoStamperInput>();
foreach (var document in documents)
{
if (string.IsNullOrWhiteSpace(document.EmployeeSignKeyword))
throw Oops.Bah("请配置员工签名定位关键字");
stampers.Add(new QiyuesuoStamperInput
{
SignatoryId = personalSignatoryId,
Type = "PERSONAL",
DocumentId = document.DocumentId,
Keyword = document.EmployeeSignKeyword,
KeywordIndex = Math.Max(1, setting.EmployeeSignKeywordIndex)
});
if (CompanySealEnabled(document.CompanySealEnabled))
{
if (string.IsNullOrWhiteSpace(document.CompanySealKeyword))
throw Oops.Bah("需要公司盖章的文件未配置企业盖章定位关键字");
stampers.Add(new QiyuesuoStamperInput
{
ActionId = companyActionId,
Type = "COMPANY",
DocumentId = document.DocumentId,
SealId = sealId,
Keyword = document.CompanySealKeyword,
KeywordIndex = Math.Max(1, setting.CompanySealKeywordIndex),
OffsetX = setting.CompanySealOffsetX,
OffsetY = setting.CompanySealOffsetY
});
}
}
// 合并骑缝章也只能包含已开启该开关的文件,不能把只需个人签字的材料混入。
var acrossDocuments = documents
.Where(item => AcrossPageSealEnabled(item.AcrossPageSealEnabled, setting))
.Select(item => item.DocumentId)
.ToList();
if (acrossDocuments.Count == 0)
return stampers;
var acrossType = setting.AcrossPageSealType?.Trim().ToUpperInvariant();
if (acrossType is not ("ALL_DOCUMENT" or "SINGLE_DOCUMENT"))
throw Oops.Bah("契约锁骑缝章模式只能配置为 ALL_DOCUMENT 或 SINGLE_DOCUMENT");
if (acrossType == "ALL_DOCUMENT")
{
stampers.Add(new QiyuesuoStamperInput
{
ActionId = companyActionId,
Type = "ACROSS_PAGE",
DocumentId = acrossDocuments[0],
DocumentIds = string.Join(",", acrossDocuments),
SealId = sealId,
AcrossType = acrossType
});
}
else
{
foreach (var documentId in acrossDocuments)
{
stampers.Add(new QiyuesuoStamperInput
{
ActionId = companyActionId,
Type = "ACROSS_PAGE",
DocumentId = documentId,
SealId = sealId,
AcrossType = acrossType
});
}
}
return stampers;
}
internal static string ResolvePendingStatus(QiyuesuoContractResult contract)
{
var employeeSigned = contract.Signatories.Any(item =>
string.Equals(item.TenantType, "PERSONAL", StringComparison.OrdinalIgnoreCase)
&& string.Equals(item.Status, "SIGNED", StringComparison.OrdinalIgnoreCase));
var hasCompanyAction = contract.Signatories.Any(item =>
string.Equals(item.TenantType, "COMPANY", StringComparison.OrdinalIgnoreCase)
&& item.Actions.Any(action => string.Equals(action.Type, "COMPANY", StringComparison.OrdinalIgnoreCase)));
// 纯员工签字流程等待平台 COMPLETE 回执,不制造不存在的企业盖章阶段。
return employeeSigned && hasCompanyAction
? ElectronicSignStatus.PendingCompanySeal
: ElectronicSignStatus.PendingEmployeeSign;
}
}