namespace SimpleAdmin.Application; /// 发起签约前按文件冻结的签字、盖章要求。 internal sealed record OnboardingSigningDocument( string DocumentId, string EmployeeSignKeyword, string CompanySealKeyword, bool? CompanySealEnabled, bool? AcrossPageSealEnabled); /// 构造契约锁请求;无数据库和网络访问,员工签字始终覆盖全部文件。 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 { 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 BuildStampers( IReadOnlyList 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(); 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; } }