From 83854ce901f02c29ebdd8c63e3641cb64dda33bf Mon Sep 17 00:00:00 2001
From: fuenmao <980740792@qq.com>
Date: Thu, 15 Oct 2026 17:41:55 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=85=AC=E5=8F=B8=E7=9B=96?=
=?UTF-8?q?=E7=AB=A0=E4=B8=8E=E9=AA=91=E7=BC=9D=E7=AB=A0=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Onboarding/ObContractTemplateVersion.cs | 6 +
.../Onboarding/ObElectronicSignDocument.cs | 6 +
.../Entity/Onboarding/ObTaskContract.cs | 6 +
.../Options/QiyuesuoSettingOptions.cs | 4 +-
...ontractTemplateVersionSealConfiguration.cs | 25 +++
.../Dto/OnboardingContractVersionInput.cs | 22 ++
.../IOnboardingContractTemplateService.cs | 5 +
.../OnboardingContractTemplateService.cs | 28 ++-
.../Dto/OnboardingElectronicSignOutput.cs | 6 +
.../OnboardingElectronicSignService.cs | 131 ++++-------
.../ElectronicSign/Qiyuesuo/QiyuesuoClient.cs | 77 +------
.../Qiyuesuo/QiyuesuoClientModels.cs | 3 +
.../QiyuesuoContractRequestBuilder.cs | 206 ++++++++++++++++++
.../Onboarding/Task/OnboardingTaskService.cs | 5 +
.../OnboardingContractTemplateController.cs | 10 +
...0261015_contract_version_seal_switches.sql | 75 +++++++
tests/SecurityRegression/Program.cs | 132 +++++++++++
tests/SecurityRegression/README.md | 6 +
.../SecurityRegression.csproj | 7 +-
tests/SecurityRegression/Stubs.cs | 13 +-
20 files changed, 607 insertions(+), 166 deletions(-)
create mode 100644 SimpleAdmin.Application/Services/Onboarding/Common/ContractTemplateVersionSealConfiguration.cs
create mode 100644 SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoContractRequestBuilder.cs
create mode 100644 database/migrations/20261015_contract_version_seal_switches.sql
diff --git a/SimpleAdmin.Application/Entity/Onboarding/ObContractTemplateVersion.cs b/SimpleAdmin.Application/Entity/Onboarding/ObContractTemplateVersion.cs
index c2f750c..3c9222b 100644
--- a/SimpleAdmin.Application/Entity/Onboarding/ObContractTemplateVersion.cs
+++ b/SimpleAdmin.Application/Entity/Onboarding/ObContractTemplateVersion.cs
@@ -52,6 +52,12 @@ public class ObContractTemplateVersion : BaseEntity
///
public string VersionStatus { get; set; } = ContractTemplateVersionStatus.Draft;
+ /// 本版本是否需要公司盖章;历史空值沿用需要盖章。
+ public bool? CompanySealEnabled { get; set; }
+
+ /// 本版本是否需要骑缝章;历史空值沿用系统骑缝章配置。
+ public bool? AcrossPageSealEnabled { get; set; }
+
///
/// 业务备注。
///
diff --git a/SimpleAdmin.Application/Entity/Onboarding/ObElectronicSignDocument.cs b/SimpleAdmin.Application/Entity/Onboarding/ObElectronicSignDocument.cs
index 0c3e104..6e51b19 100644
--- a/SimpleAdmin.Application/Entity/Onboarding/ObElectronicSignDocument.cs
+++ b/SimpleAdmin.Application/Entity/Onboarding/ObElectronicSignDocument.cs
@@ -52,6 +52,12 @@ public class ObElectronicSignDocument : BaseEntity
///
public string CompanySealKeyword { get; set; }
+ /// 发起本次签约时冻结的公司盖章要求。
+ public bool? CompanySealEnabled { get; set; }
+
+ /// 发起本次签约时冻结的骑缝章要求。
+ public bool? AcrossPageSealEnabled { get; set; }
+
///
/// 契约锁员工签署动作 Id。
///
diff --git a/SimpleAdmin.Application/Entity/Onboarding/ObTaskContract.cs b/SimpleAdmin.Application/Entity/Onboarding/ObTaskContract.cs
index 0d1d126..24aff58 100644
--- a/SimpleAdmin.Application/Entity/Onboarding/ObTaskContract.cs
+++ b/SimpleAdmin.Application/Entity/Onboarding/ObTaskContract.cs
@@ -52,6 +52,12 @@ public class ObTaskContract : BaseEntity
///
public int PrintCopies { get; set; } = 1;
+ /// 任务创建时冻结的公司盖章要求;历史空值沿用需要盖章。
+ public bool? CompanySealEnabled { get; set; }
+
+ /// 任务创建时冻结的骑缝章要求;历史空值沿用系统配置。
+ public bool? AcrossPageSealEnabled { get; set; }
+
///
/// 任务合同当前状态。
///
diff --git a/SimpleAdmin.Application/Options/QiyuesuoSettingOptions.cs b/SimpleAdmin.Application/Options/QiyuesuoSettingOptions.cs
index b9c9a9d..030974d 100644
--- a/SimpleAdmin.Application/Options/QiyuesuoSettingOptions.cs
+++ b/SimpleAdmin.Application/Options/QiyuesuoSettingOptions.cs
@@ -86,12 +86,12 @@ public class QiyuesuoSettingOptions
public decimal CompanySealOffsetY { get; set; }
///
- /// 是否在后台确认企业盖章时同时加盖骑缝章。
+ /// 历史版本未单独设置开关时,是否在企业盖章时加盖骑缝章;新版本显式开关优先。
///
public bool EnableAcrossPageSeal { get; set; } = true;
///
- /// 骑缝章文档范围:ALL_DOCUMENT(全部文档合并)或 SINGLE_DOCUMENT(每份文档单独)。
+ /// 骑缝章方式:ALL_DOCUMENT(所选文档合并)或 SINGLE_DOCUMENT(所选文档各自独立)。
///
public string AcrossPageSealType { get; set; } = "ALL_DOCUMENT";
diff --git a/SimpleAdmin.Application/Services/Onboarding/Common/ContractTemplateVersionSealConfiguration.cs b/SimpleAdmin.Application/Services/Onboarding/Common/ContractTemplateVersionSealConfiguration.cs
new file mode 100644
index 0000000..297ad39
--- /dev/null
+++ b/SimpleAdmin.Application/Services/Onboarding/Common/ContractTemplateVersionSealConfiguration.cs
@@ -0,0 +1,25 @@
+using SqlSugar;
+
+namespace SimpleAdmin.Application;
+
+/// 版本盖章配置局部更新,避免覆盖另一个开关、版本状态或历史任务快照。
+internal static class ContractTemplateVersionSealConfiguration
+{
+ internal static IUpdateable BuildUpdate(
+ ISqlSugarClient db,
+ OnboardingContractVersionSealInput input)
+ {
+ if (input.Id <= 0)
+ throw Oops.Bah("版本Id不能为空");
+ if (!input.CompanySealEnabled.HasValue && !input.AcrossPageSealEnabled.HasValue)
+ throw Oops.Bah("请指定需要修改的盖章开关");
+
+ var update = db.Updateable()
+ .Where(item => item.Id == input.Id);
+ if (input.CompanySealEnabled.HasValue)
+ update = update.SetColumns(item => item.CompanySealEnabled == input.CompanySealEnabled);
+ if (input.AcrossPageSealEnabled.HasValue)
+ update = update.SetColumns(item => item.AcrossPageSealEnabled == input.AcrossPageSealEnabled);
+ return update;
+ }
+}
diff --git a/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/Dto/OnboardingContractVersionInput.cs b/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/Dto/OnboardingContractVersionInput.cs
index d768607..c67df45 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/Dto/OnboardingContractVersionInput.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/Dto/OnboardingContractVersionInput.cs
@@ -55,6 +55,12 @@ public class OnboardingContractVersionAddInput
///
public DateTime? ExpiryDate { get; set; }
+ /// 本版本是否需要公司盖章;旧客户端未传时沿用原规则。
+ public bool? CompanySealEnabled { get; set; }
+
+ /// 本版本是否需要骑缝章;旧客户端未传时沿用系统配置。
+ public bool? AcrossPageSealEnabled { get; set; }
+
///
/// 业务备注。
///
@@ -78,3 +84,19 @@ public class OnboardingContractVersionStatusInput
[global::System.ComponentModel.DataAnnotations.Required(ErrorMessage = "版本状态不能为空")]
public string VersionStatus { get; set; }
}
+
+///
+/// 修改合同模板版本的盖章配置;未传的开关保持不变。
+///
+public class OnboardingContractVersionSealInput
+{
+ /// 合同版本 Id。
+ [IdNotNull(ErrorMessage = "版本Id不能为空")]
+ public long Id { get; set; }
+
+ /// 是否需要公司盖章。
+ public bool? CompanySealEnabled { get; set; }
+
+ /// 是否需要骑缝章。
+ public bool? AcrossPageSealEnabled { get; set; }
+}
diff --git a/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/IOnboardingContractTemplateService.cs b/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/IOnboardingContractTemplateService.cs
index 87a2f76..ccb7ab7 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/IOnboardingContractTemplateService.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/IOnboardingContractTemplateService.cs
@@ -50,6 +50,11 @@ public interface IOnboardingContractTemplateService : ITransient
///
Task SetVersionStatus(OnboardingContractVersionStatusInput input);
+ ///
+ /// 修改合同模板版本的盖章配置,仅影响后续新建任务。
+ ///
+ Task SetVersionSeals(OnboardingContractVersionSealInput input);
+
///
/// 删除未被入职模板或任务使用的合同版本。
///
diff --git a/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/OnboardingContractTemplateService.cs b/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/OnboardingContractTemplateService.cs
index 1ff7981..4f36bcc 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/OnboardingContractTemplateService.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ContractTemplate/OnboardingContractTemplateService.cs
@@ -272,11 +272,21 @@ public class OnboardingContractTemplateService : DbRepository
public async Task> VersionPage(OnboardingContractVersionPageInput input)
{
- return await Context.Queryable()
+ var page = await Context.Queryable()
.Where(it => it.TemplateId == input.TemplateId)
.WhereIF(!string.IsNullOrWhiteSpace(input.VersionStatus), it => it.VersionStatus == input.VersionStatus)
.OrderByDescending(it => it.CreateTime)
.ToPagedListAsync(input.PageNum, input.PageSize);
+
+ // 仅解析返回值,不回写历史空值,使列表开关显示真实生效的规则。
+ var setting = App.GetConfig("QiyuesuoSetting", true)
+ ?? new QiyuesuoSettingOptions();
+ foreach (var version in page.List)
+ {
+ version.CompanySealEnabled = QiyuesuoContractRequestBuilder.CompanySealEnabled(version.CompanySealEnabled);
+ version.AcrossPageSealEnabled = QiyuesuoContractRequestBuilder.AcrossPageSealEnabled(version.AcrossPageSealEnabled, setting);
+ }
+ return page;
}
///
@@ -395,7 +405,21 @@ public class OnboardingContractTemplateService : DbRepository new { item.VersionStatus, item.UpdateTime, item.UpdateUserId, item.UpdateUser })
+ .ExecuteCommandAsync();
+ }
+
+ ///
+ /// 修改版本盖章开关;任务和电子签约已经保存独立快照,不随版本变更。
+ ///
+ public async Task SetVersionSeals(OnboardingContractVersionSealInput input)
+ {
+ var version = await Context.Queryable().InSingleAsync(input.Id)
+ ?? throw Oops.Bah("模板版本不存在");
+ await EnsureTemplateCanManage(version.TemplateId);
+
+ await ContractTemplateVersionSealConfiguration.BuildUpdate(Context, input).ExecuteCommandAsync();
}
///
diff --git a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Dto/OnboardingElectronicSignOutput.cs b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Dto/OnboardingElectronicSignOutput.cs
index 3e7bc54..9233331 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Dto/OnboardingElectronicSignOutput.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Dto/OnboardingElectronicSignOutput.cs
@@ -164,4 +164,10 @@ public class OnboardingElectronicSignDocumentOutput
/// 当前管理员流程中是否已生成过员工签后预览。
public bool EmployeeSignedPreviewed { get; set; }
+
+ /// 本份文件是否需要公司盖章。
+ public bool CompanySealEnabled { get; set; }
+
+ /// 本份文件是否需要骑缝章。
+ public bool AcrossPageSealEnabled { get; set; }
}
diff --git a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/OnboardingElectronicSignService.cs b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/OnboardingElectronicSignService.cs
index 2fa8c3f..8d8d6e1 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/OnboardingElectronicSignService.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/OnboardingElectronicSignService.cs
@@ -805,7 +805,10 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
SealId = process.SealId,
CreatorName = process.CreatorName,
CreatorMobile = process.CreatorMobile,
- CallbackUrl = _setting.CallbackUrl
+ CallbackUrl = _setting.CallbackUrl,
+ RequiresCompanySign = taskContracts.Any(item =>
+ QiyuesuoContractRequestBuilder.RequiresCompanySign(
+ item.CompanySealEnabled, item.AcrossPageSealEnabled, _setting))
});
process.ProviderContractId = GetContractId(providerContract);
process.ProviderStatus = providerContract.Status ?? "DRAFT";
@@ -860,7 +863,9 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
DocumentSort = index + 1,
OriginalDocxFileId = originalFileId,
EmployeeSignKeyword = _setting.EmployeeSignKeyword.Trim(),
- CompanySealKeyword = _setting.CompanySealKeyword.Trim(),
+ CompanySealKeyword = _setting.CompanySealKeyword?.Trim(),
+ CompanySealEnabled = QiyuesuoContractRequestBuilder.CompanySealEnabled(taskContract.CompanySealEnabled),
+ AcrossPageSealEnabled = QiyuesuoContractRequestBuilder.AcrossPageSealEnabled(taskContract.AcrossPageSealEnabled, _setting),
Status = CommonStatusConst.ENABLE,
CreateTime = DateTime.Now,
CreateUserId = process.CreateUserId,
@@ -879,7 +884,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
}
///
- /// 为每份文档分别配置员工签名与企业盖章位置,然后一次性发送整个流程。
+ /// 全部文档配置员工签名,按每份文件的快照配置企业盖章及骑缝章位置。
///
private async Task SendProcess(
ObElectronicSignProcess process,
@@ -891,79 +896,40 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
var personalSignatory = providerContract.Signatories.FirstOrDefault(item =>
string.Equals(item.TenantType, "PERSONAL", StringComparison.OrdinalIgnoreCase))
?? throw Oops.Bah("契约锁草稿未返回员工签署方");
- var companySignatory = providerContract.Signatories.FirstOrDefault(item =>
- string.Equals(item.TenantType, "COMPANY", StringComparison.OrdinalIgnoreCase))
- ?? throw Oops.Bah("契约锁草稿未返回企业签署方");
- var companyAction = companySignatory.Actions.FirstOrDefault(item =>
- string.Equals(item.Type, "COMPANY", StringComparison.OrdinalIgnoreCase))
- ?? throw Oops.Bah("契约锁草稿未返回企业盖章动作");
- var personalSignatoryId = GetSignatoryId(personalSignatory);
- var companyActionId = GetActionId(companyAction);
- var stampers = new List();
var orderedDocuments = documents.OrderBy(item => item.DocumentSort).ToList();
+ var requiresCompanySign = orderedDocuments.Any(item =>
+ QiyuesuoContractRequestBuilder.RequiresCompanySign(
+ item.CompanySealEnabled, item.AcrossPageSealEnabled, _setting));
+ var companyAction = providerContract.Signatories
+ .Where(item => string.Equals(item.TenantType, "COMPANY", StringComparison.OrdinalIgnoreCase))
+ .SelectMany(item => item.Actions)
+ .FirstOrDefault(item => string.Equals(item.Type, "COMPANY", StringComparison.OrdinalIgnoreCase));
+ if (requiresCompanySign && companyAction == null)
+ throw Oops.Bah("契约锁草稿未返回企业盖章动作");
+ if (!requiresCompanySign && companyAction != null)
+ throw Oops.Bah("本次材料仅需员工签字,但契约锁业务分类仍预设企业盖章节点,请先调整该业务分类的签署流程");
+
+ var personalSignatoryId = GetSignatoryId(personalSignatory);
+ var companyActionId = companyAction == null ? null : GetActionId(companyAction);
+ var signingDocuments = orderedDocuments.Select(item =>
+ new OnboardingSigningDocument(
+ item.ProviderDocumentId,
+ item.EmployeeSignKeyword,
+ item.CompanySealKeyword,
+ item.CompanySealEnabled,
+ item.AcrossPageSealEnabled)).ToList();
+ var stampers = QiyuesuoContractRequestBuilder.BuildStampers(
+ signingDocuments, personalSignatoryId, companyActionId, process.SealId, _setting);
+
foreach (var document in orderedDocuments)
{
document.EmployeeActionId = personalSignatoryId;
- document.CompanyActionId = companyActionId;
+ document.CompanyActionId = QiyuesuoContractRequestBuilder.RequiresCompanySign(
+ document.CompanySealEnabled, document.AcrossPageSealEnabled, _setting)
+ ? companyActionId
+ : null;
document.UpdateTime = DateTime.Now;
await _db.Updateable(document).ExecuteCommandAsync();
- stampers.Add(new QiyuesuoStamperInput
- {
- SignatoryId = personalSignatoryId,
- Type = "PERSONAL",
- DocumentId = document.ProviderDocumentId,
- Keyword = document.EmployeeSignKeyword,
- KeywordIndex = Math.Max(1, _setting.EmployeeSignKeywordIndex)
- });
- stampers.Add(new QiyuesuoStamperInput
- {
- ActionId = companyActionId,
- Type = "COMPANY",
- DocumentId = document.ProviderDocumentId,
- SealId = process.SealId,
- Keyword = document.CompanySealKeyword,
- KeywordIndex = Math.Max(1, _setting.CompanySealKeywordIndex),
- OffsetX = _setting.CompanySealOffsetX,
- OffsetY = _setting.CompanySealOffsetY
- });
- }
-
- if (_setting.EnableAcrossPageSeal)
- {
- var providerDocumentIds = orderedDocuments
- .Select(item => item.ProviderDocumentId)
- .Where(item => !string.IsNullOrWhiteSpace(item))
- .ToList();
- if (providerDocumentIds.Count == 0)
- throw Oops.Bah("契约锁未返回可用于骑缝章的合同文档 Id");
-
- var acrossType = _setting.AcrossPageSealType.Trim().ToUpperInvariant();
- if (acrossType == "ALL_DOCUMENT")
- {
- stampers.Add(new QiyuesuoStamperInput
- {
- ActionId = companyActionId,
- Type = "ACROSS_PAGE",
- DocumentId = providerDocumentIds[0],
- DocumentIds = string.Join(",", providerDocumentIds),
- SealId = process.SealId,
- AcrossType = acrossType
- });
- }
- else
- {
- foreach (var providerDocumentId in providerDocumentIds)
- {
- stampers.Add(new QiyuesuoStamperInput
- {
- ActionId = companyActionId,
- Type = "ACROSS_PAGE",
- DocumentId = providerDocumentId,
- SealId = process.SealId,
- AcrossType = acrossType
- });
- }
- }
}
var sent = await _qiyuesuoClient.Send(new QiyuesuoSendContractInput
@@ -1179,7 +1145,9 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
DocumentName = item.ContractName,
DocumentSort = index + 1,
OriginalDocxFileId = item.GeneratedDocxFileId ?? 0,
- SignedPdfFileId = item.GeneratedPdfFileId
+ SignedPdfFileId = item.GeneratedPdfFileId,
+ CompanySealEnabled = QiyuesuoContractRequestBuilder.CompanySealEnabled(item.CompanySealEnabled),
+ AcrossPageSealEnabled = QiyuesuoContractRequestBuilder.AcrossPageSealEnabled(item.AcrossPageSealEnabled, _setting)
}).ToList();
return result;
}
@@ -1219,6 +1187,8 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
DocumentSort = item.DocumentSort,
OriginalDocxFileId = item.OriginalDocxFileId,
SignedPdfFileId = item.SignedPdfFileId,
+ CompanySealEnabled = QiyuesuoContractRequestBuilder.CompanySealEnabled(item.CompanySealEnabled),
+ AcrossPageSealEnabled = QiyuesuoContractRequestBuilder.AcrossPageSealEnabled(item.AcrossPageSealEnabled, _setting),
EmployeeSignedPreviewed = previewedDocumentIds.Contains(item.Id)
}).ToList();
}
@@ -1236,7 +1206,9 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
DocumentName = item.ContractName,
DocumentSort = index + 1,
OriginalDocxFileId = item.GeneratedDocxFileId ?? 0,
- SignedPdfFileId = item.GeneratedPdfFileId
+ SignedPdfFileId = item.GeneratedPdfFileId,
+ CompanySealEnabled = QiyuesuoContractRequestBuilder.CompanySealEnabled(item.CompanySealEnabled),
+ AcrossPageSealEnabled = QiyuesuoContractRequestBuilder.AcrossPageSealEnabled(item.AcrossPageSealEnabled, _setting)
}).ToList();
}
return result;
@@ -1431,15 +1403,7 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
if (status == "DRAFT")
return ElectronicSignStatus.Draft;
- var personal = providerContract.Signatories.FirstOrDefault(item =>
- string.Equals(item.TenantType, "PERSONAL", StringComparison.OrdinalIgnoreCase));
- var employeeSigned = string.Equals(
- personal?.Status,
- "SIGNED",
- StringComparison.OrdinalIgnoreCase);
- return employeeSigned
- ? ElectronicSignStatus.PendingCompanySeal
- : ElectronicSignStatus.PendingEmployeeSign;
+ return QiyuesuoContractRequestBuilder.ResolvePendingStatus(providerContract);
}
private static bool IsTerminal(string status)
@@ -1588,10 +1552,9 @@ public class OnboardingElectronicSignService : IOnboardingElectronicSignService
{
throw Oops.Bah("请配置契约锁企业成员姓名和手机号");
}
- if (string.IsNullOrWhiteSpace(_setting.EmployeeSignKeyword)
- || string.IsNullOrWhiteSpace(_setting.CompanySealKeyword))
+ if (string.IsNullOrWhiteSpace(_setting.EmployeeSignKeyword))
{
- throw Oops.Bah("请配置员工签名和企业盖章定位关键字");
+ throw Oops.Bah("请配置员工签名定位关键字");
}
if (_setting.CompanySealOffsetX <= -1m || _setting.CompanySealOffsetX >= 1m
|| _setting.CompanySealOffsetY <= -1m || _setting.CompanySealOffsetY >= 1m)
diff --git a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClient.cs b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClient.cs
index 1028451..ee216e0 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClient.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClient.cs
@@ -40,84 +40,9 @@ public class QiyuesuoClient : IQiyuesuoClient
public async Task CreateDraft(
QiyuesuoCreateDraftInput input)
{
- if (!long.TryParse(input.SealId, out var sealId))
- throw Oops.Bah("契约锁 SealId 配置格式不正确");
-
- var body = new
- {
- bizId = input.BizId,
- subject = input.Subject,
- tenantName = input.CompanyName,
- description = "OnboardX 入职电子签",
- send = false,
- callbackUrl = NullIfBlank(input.CallbackUrl),
- category = new
- {
- id = input.CategoryId
- },
- creator = new
- {
- name = input.CreatorName,
- contact = input.CreatorMobile,
- contactType = "MOBILE"
- },
- signatories = new 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"
- }
- }
- },
- 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 await PostJson(
"/v2/contract/draft",
- body);
+ QiyuesuoContractRequestBuilder.BuildDraft(input));
}
///
diff --git a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClientModels.cs b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClientModels.cs
index ffade02..2ee5aa7 100644
--- a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClientModels.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoClientModels.cs
@@ -34,6 +34,9 @@ public class QiyuesuoCreateDraftInput
/// 契约锁服务器回调地址。
public string CallbackUrl { get; set; }
+
+ /// 是否需要企业签章节点;仅员工签字的流程不创建该节点。
+ public bool RequiresCompanySign { get; set; } = true;
}
///
diff --git a/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoContractRequestBuilder.cs b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoContractRequestBuilder.cs
new file mode 100644
index 0000000..857bd10
--- /dev/null
+++ b/SimpleAdmin.Application/Services/Onboarding/ElectronicSign/Qiyuesuo/QiyuesuoContractRequestBuilder.cs
@@ -0,0 +1,206 @@
+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