Skip to content
bitzorcas
中EN

Recipe

律所案件立案与利益冲突:多租户四阶动态业务校验实战

深度结合法律科技实战,以律所案件立案(Matter Intake)与利益冲突审查(Conflict of Interest Check)为核心用例,带你掌握从基础参数校验、组合级联风控到无代码动态策略的完整落地。

Last updated

在许多企业级 SaaS 系统的早期演进中,开发团队经常陷入**“租户校验分支污染”的泥潭**:

  • 代码级硬编码污染:为了满足不同租户的个性化风控要求,开发者在同一个验证器中写满了 if (tenantId == "1000001") { ... } 甚至 When(t => ...),导致核心验证逻辑迅速劣化为无法测试的混沌状态;
  • 发版依赖与运维阻断:当大客户提出“针对涉案标的额 $\ge 5000$ 万元的跨国仲裁,必须强制校验并上传《利益冲突豁免函(Conflict Waiver)》”时,研发团队竟不得不全量构建、回归测试并重新发布整个后端 API 镜像;
  • 执业道德与监管零容忍:在法律科技与金融级系统中,若代理了与既有受托客户存在对抗性利益冲突的对方当事人,将直接面临监管吊销执业资格与民事巨额索赔。

BitzOrcas.Modern 独创“4 阶动态业务校验黄金范式(4-Tier Validation Pattern)”。它将验证职责严格解耦为四个层级:

  1. Tier 1 (全局基础通用规则):编译期绑定的强类型基础不变量;
  2. Tier 2(A) (组合与级联策略):针对特定行业形态的复合型跨实体阻断规则;
  3. Tier 2(B) (租户动态热配置策略):无需重新发版即可通过配置中心秒级热更新的阈值策略;
  4. Tier 3 (现场实施无代码引擎):交付与技术支持人员在运营控制台直接配置的高阶 JSON 正则校验。

本教程将以真实的律所民商事诉讼立案与利益冲突阻断审查为用例,带你完整实现一套生产级的多租户四阶动态校验体系。

多租户四阶动态校验执行全景

Tier 2:租户定制化策略扩展基础规则通过全部通过全部通过全部通过校验失败校验失败校验失败校验失败

1. 入站立案命令 (CreateMatterIntakeCommand)

2. 解析可信租户律所上下文 (ICurrentTenant)

3. Tier 1:全局通用基础规则 (IRequestRule)
非空校验、标的额大于零、委托人与对方当事人不可同名

4. 分派租户特化策略 (ITenantValidationStrategy.GetTenantRules)

5. Tier 2(A):涉外/诉讼级联硬编码策略 (Composite Conflict Strategy)

6. Tier 2(B):租户热配置参数驱动策略 (Hot-Config Lookback Strategy)

7. Tier 3:现场交付无代码规则引擎 (No-Code Rule Engine)

8. 全部校验通过,放行进入业务 Handler 执行持久化

9. 规则违背立即短路:输出 RFC 9457 Problem Details (零数据库接触)


第一步:定义立案命令契约(Command Contract)

立案命令定义了标的额、委托方、争议相对方、涉外标记及可选的豁免证明标识:

src/Modules/Business/Legal/Application/Commands/CreateMatterIntake/CreateMatterIntakeCommand.cs
using BitzOrcas.Application.Authorization;
using BitzOrcas.Domain.Abstractions;
using BitzOrcas.Domain.Results;
using BitzOrcas.Endpoint.Attributes;
using Mediator;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake;
public enum MatterCategory
{
Litigation = 1, // 民商事诉讼
CrossBorder = 2, // 涉外争议与跨国仲裁
NonLitigation = 3 // 非诉与企业合规
}
[GenerateEndpoint(HttpRoute.Post, "/api/legal/matters", Tag = "LegalMatter")]
public sealed record CreateMatterIntakeCommand(
string MatterTitle,
string ClientName,
string OpposingParty,
decimal ClaimAmount,
MatterCategory Category,
string? ConflictWaiverDocumentId = null) : ICommand<Result<string>>, IAuthorizedRequest
{
public ResourceDescriptor Resource { get; } = new("Legal", "Matter");
public AuthorizationAction Action { get; } = AuthorizationAction.Create;
}

第二步:实现 Tier 1 全局基础规则(Universal Base Rules)

所有租户必须强制遵守的通用不变量(如标题非空、标的额必须为正数、委托人不可与相对人同名),通过实现 IRequestRule<TRequest> 承载:

src/Modules/Business/Legal/Application/Commands/CreateMatterIntake/Rules/LegalValidationErrors.cs
using BitzOrcas.Domain.Results;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
/// <summary>
/// 案件立案校验稳定错误契约
/// </summary>
public static class LegalValidationErrors
{
/// <summary>
/// 案件标的名称非法
/// </summary>
public static readonly Error InvalidTitle =
Error.Validation("Legal.Validation.InvalidTitle", "案件标的名称不能为空且长度不得超过 200 个字符。");
/// <summary>
/// 委托当事人与相对方名称必填
/// </summary>
public static readonly Error PartiesRequired =
Error.Validation("Legal.Validation.PartiesRequired", "案件委托当事人与争议相对方名称均属于必填项。");
/// <summary>
/// 当事人与相对方为同一主体冲突
/// </summary>
public static readonly Error AdversaryConflict =
Error.Conflict("Legal.Validation.AdversaryConflict", "委托人与相对方主体同名,存在根本性法定利益冲突。");
/// <summary>
/// 争议标的金额必须大于零
/// </summary>
public static readonly Error InvalidAmount =
Error.Validation("Legal.Validation.InvalidAmount", "争议标的金额必须为大于零的数值。");
/// <summary>
/// 跨国仲裁业务大额争议必须提供豁免函
/// </summary>
public static readonly Error WaiverRequired =
Error.Validation("Legal.Validation.WaiverRequired", "涉案标的额大于等于 5,000 万元的跨国仲裁业务,必须级联提交客户签署的《利益冲突豁免函(Conflict Waiver)》。");
}
src/Modules/Business/Legal/Application/Commands/CreateMatterIntake/Rules/CreateMatterIntakeBaseRule.cs
using BitzOrcas.Application.Validation;
using BitzOrcas.Domain.Results;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
/// <summary>
/// Tier 1:所有租户强制生效的不可变基础规则
/// </summary>
public sealed class CreateMatterIntakeBaseRule : IRequestRule<CreateMatterIntakeCommand>
{
public ValueTask<Result> ValidateAsync(CreateMatterIntakeCommand request, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(request.MatterTitle) || request.MatterTitle.Length > 200)
{
return ValueTask.FromResult(Result.Failure(LegalValidationErrors.InvalidTitle));
}
if (string.IsNullOrWhiteSpace(request.ClientName) || string.IsNullOrWhiteSpace(request.OpposingParty))
{
return ValueTask.FromResult(Result.Failure(LegalValidationErrors.PartiesRequired));
}
// 基础不变量:原告与被告不可为同一法律主体
if (string.Equals(request.ClientName.Trim(), request.OpposingParty.Trim(), StringComparison.OrdinalIgnoreCase))
{
return ValueTask.FromResult(Result.Failure(LegalValidationErrors.AdversaryConflict));
}
if (request.ClaimAmount <= 0)
{
return ValueTask.FromResult(Result.Failure(LegalValidationErrors.InvalidAmount));
}
return ValueTask.FromResult(Result.Success());
}
}

第三步:实现 Tier 2 租户特化策略(ITenantValidationStrategy)

在 BitzOrcas.Modern 中,多租户差异化逻辑不侵入基础规则。通过实现 ITenantValidationStrategy<TRequest>,系统会在运行时根据当前租户快照(ICurrentTenant)动态拉取专属于该租户的追加规则集:

src/Modules/Business/Legal/Application/Commands/CreateMatterIntake/Strategies/TenantMatterValidationStrategy.cs
using BitzOrcas.Application.Abstractions.Tenancy;
using BitzOrcas.Application.Validation;
using BitzOrcas.Domain.Results;
using BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Strategies;
/// <summary>
/// Tier 2:租户定制化多层级校验策略分派器
/// </summary>
public sealed class TenantMatterValidationStrategy : ITenantValidationStrategy<CreateMatterIntakeCommand>
{
public IReadOnlyList<IRequestRule<CreateMatterIntakeCommand>> GetTenantRules(ICurrentTenant currentTenant)
{
var tenant = currentTenant.Tenant;
if (!tenant.IsAvailable) return [];
var rules = new List<IRequestRule<CreateMatterIntakeCommand>>();
// 租户 1000001(红圈涉外所):大额案件必须上传冲突豁免书,且标的额超过 5000 万必须通过涉外审查
if (tenant.EffectiveTenantId == "1000001")
{
rules.Add(new HighValueCrossBorderWaiverRule());
}
return rules;
}
}
/// <summary>
/// Tier 2(A):特定大所的组合级联风控规则
/// </summary>
internal sealed class HighValueCrossBorderWaiverRule : IRequestRule<CreateMatterIntakeCommand>
{
public ValueTask<Result> ValidateAsync(CreateMatterIntakeCommand request, CancellationToken cancellationToken)
{
// 规则:当案件为涉外争议且标的额超过 5,000 万元时,必须提供经公证的利益冲突豁免书
if (request.Category == MatterCategory.CrossBorder && request.ClaimAmount >= 50_000_000.00m)
{
if (string.IsNullOrWhiteSpace(request.ConflictWaiverDocumentId))
{
return ValueTask.FromResult(Result.Failure(LegalValidationErrors.WaiverRequired));
}
}
return ValueTask.FromResult(Result.Success());
}
}

第四步:端到端多租户差异化校验集成测试

编写集成测试,验证租户特化策略在隔离环境下的真实表现:

tests/BitzOrcas.Integration.Tests/Legal/MultiTenantValidationIntegrationTests.cs
using System.Net;
using System.Net.Http.Json;
using BitzOrcas.Domain.Results;
using BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake;
using BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
using Shouldly;
using Xunit;
public sealed class MultiTenantValidationIntegrationTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly CustomWebApplicationFactory _factory;
public MultiTenantValidationIntegrationTests(CustomWebApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task StandardTenant_SubmittingHighValueMatter_WithoutWaiver_ShouldSucceed()
{
// 1. 使用普通中小律所租户(租户 1000002)发起 6000 万诉讼
var client = _factory.CreateAuthenticatedClient(tenantId: "1000002", role: "SuperAdmin");
var command = new CreateMatterIntakeCommand(
MatterTitle: "常规工程款结算纠纷诉讼",
ClientName: "某建筑工程有限公司",
OpposingParty: "某地方城市建设发展集团",
ClaimAmount: 60_000_000.00m,
Category: MatterCategory.CrossBorder,
ConflictWaiverDocumentId: null); // 中小律所未开启强制豁免策略
// 2. 发起请求
var response = await client.PostAsJsonAsync("/api/legal/matters", command);
// 3. 断言成功:普通租户不受 Tier 2(A) 特化策略约束
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
[Fact]
public async Task RedCircleTenant_SubmittingHighValueMatter_WithoutWaiver_ShouldBeBlockedByTier2Rule()
{
// 1. 使用红圈涉外大所租户(租户 1000001)发起同样的 6000 万涉外诉讼
var client = _factory.CreateAuthenticatedClient(tenantId: "1000001", role: "SuperAdmin");
var command = new CreateMatterIntakeCommand(
MatterTitle: "跨国离岸投资并购侵权纠纷",
ClientName: "某离岸投资基金会",
OpposingParty: "某跨国控股财团",
ClaimAmount: 60_000_000.00m,
Category: MatterCategory.CrossBorder,
ConflictWaiverDocumentId: null); // 未上传豁免函
// 2. 发起请求
var response = await client.PostAsJsonAsync("/api/legal/matters", command);
// 3. 断言阻断:Tier 2 策略生效,短路返回 400 Bad Request
response.StatusCode.ShouldBe(HttpStatusCode.BadRequest);
var result = await response.Content.ReadFromJsonAsync<Result<string>>();
result.ShouldNotBeNull();
result.IsFailure.ShouldBeTrue();
result.Error.Code.ShouldBe(LegalValidationErrors.WaiverRequired.Code);
}
}

4 阶动态校验设计核心收益

  1. 核心逻辑零污染:通用不变量(Tier 1)与租户特化策略(Tier 2)物理隔离,消除脆弱的 if (tenantId == ...) 坏味道;
  2. 渐进式能力追加:基础规则通过后,策略分派器才追加执行高阶策略,保证了最快响应与极佳的短路性能;
  3. 零发版热适应:通过配置提供者与规则引擎,运营人员在后台修改阈值即可全网生效,真正实现企业级云原生敏捷。

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%