Skip to content
bitzorcas
中EN

Concept

Numbering Business Sequence Engine

Source-verified handbook for conditional rules, six segment types, tenant counter facts, gap compensation, business-table continuation, CSV seeds, dual ORMs, and current product boundaries.

Last updated

Numbering implements Framework’s ISequenceNumberGenerator, selects rules from three tenant-scoped tables, assembles a string, and writes an allocation fact. It does not generate database primary keys and has no rule-management use case or administration UI, but it does expose a read-only governance query endpoint (see §12) that lets operators inspect rules and counter facts.

1. Current product surface

unique conflict, at most 3

SequenceNumberRequest

select conditional rule

load ordered segments

compute non-number prefix

read GenerateRecord MAX

assemble full value

optional business-table duplicate check

insert allocation fact

Beyond Infrastructure, the module now has Contracts and Application projects to expose the read-only governance query (see §12). Public ports and pure algorithms live in BitzOrcas.Application.Numbering; the API Host registers the implementation through AddBitzOrcasNumberingPlatform().

2. Input and output

Actual generation contract
// ① TableName + Field bind a rule; they are not permission to run dynamic SQL.
var request = new SequenceNumberRequest(
TableName: "DemoCase",
Field: "SerialId",
EntityValues: new Dictionary<string, object?>
{
["OfficeId"] = "SH",
["CaseType"] = "Civil"
},
IsEnabled: true,
ParentSequenceNumber: null,
BusinessSerialContext: null);
// ② Call the shared port; result contains string, counter, and prefix.
Result<SequenceNumberResult> result =
await sequenceNumbers.GenerateAsync(request, cancellationToken);

The request has no TenantId. Tenant context is applied by the underlying generated IEntitySet<T> adapters.

3. Three owner tables

TableResponsibility
SysSequenceNumberRulecondition, priority, separator, compensation for TableName/Field
SysSequenceNumberRuleSettingordered segments under a RuleId
SysSequenceNumberGenerateRecordallocated full string, number, prefix, activation, and parent facts

All inherit BizEntityBase and declare tenant and soft-delete metadata. They are rule/counter fact exceptions, not a template for restoring one entity/mapper per table.

4. Rule selection

The generator queries the same TableName/Field with IsActive && IsEnabled, orders candidates by Priority, and delegates to SequenceRuleSelector. Conditions support eq, ne, in, and contains against EntityValues.

An empty condition evaluates true inside the main loop, so the default must have the largest Priority. Otherwise it consumes rules that follow it.

5. Six segment types

SectionTypeOutput
Constantliteral Value
Datecurrent time in a .NET format; optionally fixed April fiscal logic
FiscalYeartwo-digit fiscal year
BranchJSON map from FieldSource, falling back to Value/FD
BusinessFieldEntityValues field, falling back to Value
SequenceNumbernumber padded to Length, default six

An unknown SectionType silently becomes Constant instead of returning a configuration error.

6. Shipped rules

Only two demo rules ship:

  • DemoCase.SerialId → CASE-{yy}-{seq6};
  • DemoInvoice.SerialId → INV-{yyyy}-{seq6}.

Both rules and six settings use TenantId 1000001. They are sample baseline data, not rules that automatically cover arbitrary business tables.

7. Counters and prefixes

Prefix is every non-SequenceNumber output joined by Separator, for example CASE-26. The generator reads the maximum number for Tenant + TableName + Field + Prefix, then adds one.

A changed Date, FiscalYear, Branch, or BusinessField prefix naturally starts a separate counter. There is no separate daily/monthly/yearly reset-policy field.

8. Uniqueness boundary

The database unique index is TenantId + TableName + Field + SequenceNumberStr. Concurrent calls can read the same MAX; one insert wins and the other recognizes a database exception by message text and retries.

Recognition matches several English phrases and SQL Server numbers. It is a brittle provider compatibility layer; unmatched exceptions escape.

9. Business-table continuation

Optional IBusinessSerialContext addresses two migrations: continue from business MAX when the fact table is empty, and check whether a generated full value already exists.

The business owner implements the callback; Numbering never constructs dynamic table SQL. The source tree currently contains no production business module calling ISequenceNumberGenerator; consumption evidence is primarily tests.

10. Gap compensation

With IsCompensate=true, the first attempt searches the smallest inactive fact under the same prefix, updates its activation flag, and returns it. No skipped value simply falls through to a new allocation.

Compensation does not call business-table duplicate checking and has no atomic claim condition. Concurrent calls may select the same inactive row.

11. Guarantees and non-guarantees

Current guaranteeDo not assume
three tenant/soft-delete metadata tablesmanagement API, permission, or audit
conditional rules and six segment algorithmsFeature enforcement in GenerateAsync
dual-ORM IEntitySet implementationatomic increment or distributed lock
unique full-string indexStartValue is honored
optional business continuation/checkcompensation is an atomic claim
three unique-conflict retriesevery provider error is recognized

12. Governance query endpoint

GET /api/numbering/rules (resource numbering/rules, Action Read) is an owner-local read-only governance report. Query parameters: Search (optional), Status (optional, values all/active/inactive/conditional, default all).

Returns NumberingGovernanceReport:

  • Rules: at most 200 rule summaries (overrun sets Truncated=true). Each NumberingRuleSummary carries RuleKey (shaped {Table}-{Field}-{RuleName}), Compensate, Conditional, Condition (controlled, with no runtime business values), Priority, ConditionScope, a human-readable Template (such as {date:yyyyMMdd}, {fiscalYear}, {branch}, {field:...}, {seq:n}), and per-segment detail (Segments, with a SubstitutionConfigured flag; the substitution body itself is not returned);
  • global counts: TotalRules, ActiveRules, ConditionalRules, CompensatingRules;
  • counter-fact totals: GeneratedRecords, ActivatedRecords, HeldRecords (generated but not activated);
  • GeneratedAt.

The read store NumberingGovernanceReadStore implements INumberingGovernanceReadStore and deliberately returns only capacity statistics and rule templates, not generated business numbers, parent numbers, or branch maps. It degrades to the fail-closed UnavailableNumberingGovernanceReadStore when not registered. Error codes: Numbering.Governance.StoreUnavailable (ServiceUnavailable), Numbering.Governance.InvalidQuery (Validation).

This endpoint is the same kind of owner-local read-only projection as the MasterData governance query: it answers which rules exist and how many numbers were generated, not whether a rule is correct or a sequence is gap-free.

13. Handbook map

14. Source inspection

Terminal window
# Generator, rule selection, and segment algorithms.
rg -n "GenerateAsync|SelectRule|AssembleFull" src/Platform/Numbering src/Framework/BitzOrcas.Application/Numbering -g '*.cs'
# Governance query endpoint and read-only report.
rg -n "GetNumberingGovernance|NumberingGovernanceReport|NumberingGovernanceReadStore" \
src/Platform/Numbering -g '*.cs'
# Current production consumption; expect registration but no business request construction.
rg -n "ISequenceNumberGenerator|SequenceNumberRequest" src -g '*.cs' --glob '!**/bin/**' --glob '!**/obj/**'

Back to platform modules · Persistence building block

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%