Skip to content
bitzorcas
中EN

Reference

Numbering 条件规则与分段 DSL

深入 TableName/Field 规则选择、优先级、JSON 条件、RuleId、六类分段、日期财年、分支映射和格式化陷阱。

Last updated

规则 DSL 决定“为哪个业务字段、在什么条件下、按什么段生成字符串”。当前 DSL 轻量且可测试,但错误配置多采用静默回退。

1. 候选规则加载

查询条件是 TableName、Field、IsActive、IsEnabled。Tenant 与软删由生成的 persistence adapter 过滤。Priority 越小越先求值。

是否全部失败

TableName + Field

加载 active/enabled rules

Priority 升序

Condition 满足?

选中第一条

下一条

RuleNotFound

Selector 的末尾虽有“回退空条件”代码,但空条件在前面的循环已经恒真。默认规则只有放在最后才符合预期。

2. RuleId 真实形状

运行时 RuleId 是:

{TableName}-{Field}-{RuleName}

设置表必须使用完全相同的字符串。模型注释中仍有只写 TableName+Field 的遗留表述,不能据此建资产。

3. JSON 条件

条件规则示例
{
"field": "CaseType",
"op": "Eq",
"value": "Civil"
}

JSON enum 使用 JsonStringEnumConverter。字段名在 EntityValues 中按字典 comparer 语义查找;一般字典默认大小写敏感,而值比较不区分大小写。

4. 四个操作符

操作符行为
Eqactual == value,忽略大小写
Ne不相等,忽略大小写
Invalue 按逗号分割、trim 后匹配
Containsactual 是否含 value,忽略大小写

属性不存在返回 false。属性值 null 转为空字符串,因此 Eq "" 可以命中 null。

5. 条件错误语义

非法 JSON 返回 false,不记录日志;反序列化结果为 null 时却返回 true。空白 JSON 也返回 true。ConditionScope 只持久化和 seed copy,当前选择器/缓存没有使用它。

建立条件规则时的调用方检查
public static class NumberingErrors
{
public static readonly Error ConditionInvalid =
Error.Validation("Numbering.Condition.Invalid", "Invalid condition");
}
// ① 生产资产应先反序列化并验证字段与操作符。
var condition = JsonSerializer.Deserialize<SequenceRuleCondition>(json);
if (condition is null || string.IsNullOrWhiteSpace(condition.Field))
return Result.Failure(NumberingErrors.ConditionInvalid);
// ② 用真实业务快照做命中测试,避免默认规则提前吞掉。
var matched = condition.IsSatisfiedBy(sampleEntityValues);
// ③ 默认空条件必须使用最大的 Priority。
EnsureDefaultRuleIsLast(rules);

这是规则发布前应增加的校验层;当前模块没有管理入口执行它。

6. Constant

Constant 原样返回 Value。空值会成为空段,但 string.Join 仍可能留下分隔符。未知 SectionType 也被 ParseSegmentType 映射为 Constant,可能把拼写错误变成看似成功的输出。

7. Date

Date 默认格式 yy,显式支持 yy、yyyy、yyyy-MM、yyyyMM,其他值直接传给 DateTime.ToString(format, InvariantCulture)。

格式错误可能抛出 FormatException。没有时区配置;使用 IAppClock.Now 的 DateTime 语义。

8. FiscalYear

财政年固定从 4 月开始:4–12 月取当前年份后两位,1–3 月取上一年份后两位。Date 段若 IsFiscalYear=true 也返回两位财年,忽略 DateFormat。

源码技术债务已指出起始月应配置化,目前不能声称支持任意企业财年。

9. Branch

Branch 从 FieldSource 获取 key,再用 SubstitutionValue JSON 字典映射。缺 FieldSource、映射 JSON 非法、key 缺失或未命中时,返回 Value;Value 为空则返回 FD。

所有失败都静默回退,可能把多个真实分支折叠进同一前缀。

Branch 映射示例
{
"SH": "SHA",
"BJ": "BJA",
"GZ": "GZA"
}

10. BusinessField

BusinessField 读取 FieldSource 对应值并调用 ToString()。字段不存在回退 Value;字段存在但值为 null 时输出空字符串。它没有格式、长度、字符集或敏感数据校验。

不要把客户姓名、证件号等敏感字段直接编入业务流水号。

11. SequenceNumber

SequenceNumber 用 D{Length} 左补零;Length <= 0 时默认 6。StartValue 虽存在于模型、配置和映射中,但 GetNextSequenceNumberAsync 未读取它,空前缀事实始终从 1 开始。

多个 SequenceNumber 段会输出同一个数值多次;当前没有校验“恰好一个计数段”。

12. Separator 与 Prefix

Assembler 对每个段按 Sort 遍历:非序号段同时进入 prefix/full,序号段只进入 full。最后分别 string.Join(separator, ...)。

当前示例规则的确定性输出
// ① 固定时钟让 Date 段可测试。
var clock = new FakeClock(new DateTime(2026, 7, 15));
// ② CASE + yy + 六位序号,分隔符为 "-"。
var (full, prefix) = SequenceSegmentAssembler.AssembleFull(
segments,
new Dictionary<string, object?>(),
clock,
sequenceNumber: 1,
separator: "-");
// ③ full = CASE-26-000001;prefix = CASE-26。

13. 规则发布门禁

发布前应拒绝:重复/负 Sort、无 SequenceNumber、多 SequenceNumber、未知类型、非法日期格式、非法 Branch JSON、空业务字段名、默认规则优先级不最大、重复优先级歧义、前缀超 128 或完整串超 128。

当前 CSV reader 与运行时都没有完整执行这些门禁。

14. 核查命令

Terminal window
# 条件与分段的全部纯算法。
sed -n '1,260p' src/Framework/BitzOrcas.Application/Numbering/SequenceRuleSelector.cs
sed -n '1,300p' src/Framework/BitzOrcas.Application/Numbering/SequenceSegmentAssembler.cs
# StartValue/ConditionScope 当前仅被携带,没有生成语义。
rg -n "StartValue|ConditionScope" src/Platform/Numbering src/Framework/BitzOrcas.Application/Numbering -g '*.cs'

模块总览 · 计数与补偿

100%

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