RuoVea.OmiApi.CodingRule 10.0.1.5

dotnet add package RuoVea.OmiApi.CodingRule --version 10.0.1.5
                    
NuGet\Install-Package RuoVea.OmiApi.CodingRule -Version 10.0.1.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RuoVea.OmiApi.CodingRule" Version="10.0.1.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.OmiApi.CodingRule" Version="10.0.1.5" />
                    
Directory.Packages.props
<PackageReference Include="RuoVea.OmiApi.CodingRule" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RuoVea.OmiApi.CodingRule --version 10.0.1.5
                    
#r "nuget: RuoVea.OmiApi.CodingRule, 10.0.1.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RuoVea.OmiApi.CodingRule@10.0.1.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RuoVea.OmiApi.CodingRule&version=10.0.1.5
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.OmiApi.CodingRule&version=10.0.1.5
                    
Install as a Cake Tool

RuoVea.OmiApi.CodingRule

单据编码规则 API —— 基于 .NET 6/7/8/9/10 的轻量、跨平台编码生成组件。支持「固定值 + 日期时间 + 流水号」自由组合,一键生成 PO20240430001 这类业务单据号。内置多租户、多语言(i18n)、种子数据,配合 SqlSugar 支持 MySql / SqlServer / PostgreSQL / SQLite / Oracle 等主流数据库。


目录


业务逻辑流程说明

编码生成的完整业务逻辑(字段树、分步流程、流水号循环归零、规则级补齐/截断与示例值),见组件目录下的文档:

查看业务逻辑流程说明


概览

功能矩阵

功能 说明
编码规则管理 主表 + 明细的 CRUD、批量删除、分页查询
编码生成 GET /BasCodingRule/SN/{code}?inputChar=xxx 一行调用生成单据号
四种规则段 Fixeds(固定值)/ RuleFixed(日期时间)/ Serial(流水号)/ InputChar(输入字符)
流水号控制 长度、起始值、步长、补位码、溢出自动重置、按周期循环归零
长度补齐 规则级最大长度 + 补齐字符 + 左/右补齐 + 超出截断;分段级长度截取
生成记录 每次生成写入记录表,唯一约束做重复检查,提供查询接口
多租户 实体实现 ITenantEntity,按租户隔离
多语言 内置 7 种语言资源,异常消息随 Accept-Language 切换
种子数据 首次初始化自动写入 11 条示例规则,覆盖固定值/日期/流水号/输入字符、流水号循环、规则级补齐、禁用等各种场景
动态 WebApi 无需 Controller,Service 方法自动暴露为 REST 端点

架构

┌────────────────────────────────────────────────────────┐
│                      调用方 (HTTP)                      │
│         GET /BasCodingRule/SN/PO  ──────────┐          │
└─────────────────────────────────────────────┼──────────┘
                                              ▼
┌────────────────────────────────────────────────────────┐
│            BasCodingRuleService (动态 WebApi)           │
│  GetPagesAsync / GetDataAsync / AddDataAsync / ...     │
│  GetSNAsync(code)                                      │
│     │                                                  │
│     ├─ 1. 按 Code 查主表(含明细导航)                  │
│     ├─ 2. 校验:禁用?明细为空?                        │
│     ├─ 3. 按 Sort 拼接各规则段 → 生成 SN                │
│     └─ 4. 回写 CurrentNumber / Example                 │
└─────────────────────────────────────────────┬──────────┘
                                              ▼
┌────────────────────────────────────────────────────────┐
│                  SqlSugar (SugarRepository)             │
│  BasCodingRule (主表)  ──1:N──  BasCodingRuleDetail     │
└────────────────────────────────────────────────────────┘

安装

.NET 8.0

dotnet add package RuoVea.OmiApi.CodingRule --version 8.0.1.4

.NET 10.0

dotnet add package RuoVea.OmiApi.CodingRule --version 10.0.1.3

依赖项

包 作用
RuoVea.DynamicWebApi 将 Service 自动映射为 REST 端点
RuoVea.ExSugar SqlSugar 仓储、UnitOfWork、CodeFirst
RuoVea.ExDto PageResult / EntityBaseIdDto 等通用 DTO
RuoVea.ExIdGen IdGenerator.Id 雪花 ID
RuoVea.ExConfig AppSettings 静态配置读取
Mapster 实体 ↔ DTO 映射
SqlSugarCore ORM(传递依赖)

以上均为传递依赖,dotnet add package 后自动带入,无需逐个安装。


30 秒快速开始

第 1 步 —— 配置数据库连接(appsettings.json)

{
  "ConnectionConfigs": [
    {
      "DbType": "Sqlite",
      "ConnectionString": "DataSource=./ruovea.db",
      "IsTenantIdFilter": true
    }
  ],
  "DbInitConfig": {
    "InitTable": true,
    "InitSeedData": true
  }
}
键 说明
DbInitConfig:InitTable true 时首次启动自动 CodeFirst.InitTables 建表
DbInitConfig:InitSeedData 配置项已保留;当前实现中种子数据随 InitTable 建表一起写入,本开关暂未独立生效

第 2 步 —— 注册服务(Program.cs)

/* 类库映射 API 组件 */
builder.Services.AddDynamicWebApi(options =>
{
    options.RemoveControllerPostfixes = new List<string> { "AppService", "Service" };
    options.RemovePrefix = new List<string> { "get", "post" };
});

builder.Services.AddSqlSugarSetup();        /* 扩展 ORM */
builder.Services.AddOmiCodingRuleSetup();   /* 编码规则服务 */
builder.Services.AddCodingRuleInitSetup();  /* 初始化表 + 种子数据 */

生产环境如需统一前缀,把 AddDynamicWebApi 换成带 options.DefaultApiPrefix = "/openapi/api" 的重载即可。

第 3 步 —— 启动并验证

首次启动后组件会自动:

  1. 检测 BasCodingRule 表是否存在 → 不存在则建 BasCodingRule + BasCodingRuleDetail 两张表
  2. 写入 11 条种子规则:基础三段式(PO/SO)、流水号循环(按年/月/天/分钟/传入字符)、输入字符段、规则级左/右补齐、高精度日期、禁用规则、前缀动态传入(DOC)等

打开 Swagger(分组 CodingRule),调用:

GET /BasCodingRule/SN/PO

返回:PO20240430001,再次调用返回 PO20240430002,依次递增。


核心场景

场景 1:创建「固定值 + 日期 + 流水号」编码规则

业务需求:采购订单号格式 PO + yyyyMMdd + 3 位流水,如 PO20240430001。

调用方                BasCodingRuleService              SqlSugar
  │  POST /BasCodingRule/AddData      │                    │
  │ ────────────────────────────────▶ │                    │
  │  { code:"PO", details:[...] }     │ 编码是否已存在?     │
  │                                   │ ─── IsAnyAsync ──▶ │
  │                                   │ ◀── false          │
  │                                   │ InsertNav(主+明细) │
  │                                   │ ─────────────────▶ │
  │  true                             │                    │
  │ ◀──────────────────────────────── │                    │
var input = new BasCodingRuleInputDto
{
    Name = "采购订单编码",
    Code = "PO",
    IsDisable = YesOrNot.N,
    BasCodingRuleDetailNav = new List<BasCodingRuleDetailInputDto>
    {
        new() { CodeRule = CodeRuleConst.Fixeds,   FixedValue = "PO",                  Sort = 1 },
        new() { CodeRule = CodeRuleConst.RuleFixed, FixedRule = FixedRuleConst.yyyyMMdd, Sort = 2 },
        new() { CodeRule = CodeRuleConst.Serial,   GlideLen = 3, GlideStep = 1,
                Glide = "0", StartNumber = 1,                                          Sort = 3 }
    }
};
await service.AddDataAsync(input);

❗ 注意:Code 全局唯一,重复提交会抛出 code_exists 异常(消息随 Accept-Language 本地化)。


场景 2:生成单据编码(流水号自动递增)

调用方                BasCodingRuleService                 SqlSugar
  │  GET /BasCodingRule/SN/PO         │                       │
  │ ─────────────────────────────────▶│                       │
  │                                   │ 查主表 + Includes 明细 │
  │                                   │ ────────────────────▶ │
  │                                   │ IsDisable == Y ?      │
  │                                   │ 明细为空 ?            │
  │                                   │                       │
  │                                   │ 按 Sort 拼接:         │
  │                                   │  "PO"                 │
  │                                   │  + DateTime.Now       │
  │                                   │      .ToString(...)   │
  │                                   │  + currentNumber      │
  │                                   │      .ToString("000") │
  │                                   │                       │
  │                                   │ 回写 CurrentNumber+1  │
  │                                   │      Example = sn     │
  │                                   │ ─── UpdateAsync ────▶ │
  │  "PO20240430001"                  │                       │
  │ ◀──────────────────────────────── │                       │
string sn = await service.GetSNAsync("PO");
// 第 1 次 → PO20240430001
// 第 2 次 → PO20240430002
// ...

场景 3:流水号溢出自动重置

当规则为 GlideLen = 3(最大值 10³ - 1 = 999)时:

当前值 998 → 生成 ...998 → CurrentNumber = 999
当前值 999 → 生成 ...999 → CurrentNumber = StartNumber(1)  ← 溢出重置
当前值 1   → 生成 ...001 → CurrentNumber = 1 + GlideStep

源码逻辑(BasCodingRuleService.GetSNAsync):

long maxNumber = (long)Math.Pow(10, serialRule.GlideLen.Value) - 1;
int step = serialRule.GlideStep ?? 1;
codingRule.CurrentNumber = currentNumber >= maxNumber
    ? (serialRule.StartNumber ?? 1)   // 溢出 → 回到起始值
    : currentNumber + step;           // 否则 → 按步长递增

❗ 注意:若明细里没有 Serial 段,则每次生成后主表 CurrentNumber 仍 +1,但该值不参与拼接。


场景 4:多规则段自由组合

规则段按 Sort 升序拼接,可任意组合:

Sort CodeRule 关键字段 生成片段
1 Fixeds FixedValue = "INV-" INV-
2 RuleFixed FixedRule = "yyyy" 2024
3 Fixeds FixedValue = "-" -
4 RuleFixed FixedRule = "MMdd" 0430
5 Serial GlideLen=4, Glide="0" 0007

生成结果:INV-2024-0430-0007


场景 5:输入字符段 —— 一条规则复用多个前缀

把前缀做成 InputChar 段,调用方通过 inputChar 传入不同前缀,即可用一条规则生成不同单据号(如 SO / PO / 任意前缀):

// 规则「通用单据编码」DOC = InputChar(前缀) + yyyyMMdd + 3 位流水
string sn1 = await service.GetSNAsync("DOC", "SO"); // → SO20260902001
string sn2 = await service.GetSNAsync("DOC", "PO"); // → PO20260902002

同理,inputChar 还可在流水号段开启 cycleMethod=INPUT_CHAR 时作为「分桶 key」,不同 inputChar 各自独立计数(与「场景 4」的固定前缀不同,前缀可动态变化)。


编码规则类型详解

四种规则类型(CodeRuleConst)

常量 值 说明 关键字段
Fixeds Fixeds 固定字符串,原样拼接 FixedValue
RuleFixed RuleFixed 日期时间,DateTime.Now.ToString(FixedRule) FixedRule
Serial Serial 自增流水号,支持补位 GlideLen / StartNumber / GlideStep / Glide / CycleFlag / CycleMethod
InputChar InputChar 调用方动态传入的字符串 由 GetSNAsync 的 inputChar 参数提供

内置日期格式常量(FixedRuleConst)

常量 值 示例输出
yyyyMMdd yyyyMMdd 20240430
yyMMdd yyMMdd 240430
yyyyMM yyyyMM 202404
yyyy yyyy 2024
yy yy 24
MMdd MMdd 0430
yyyyMMddHHmmss yyyyMMddHHmmss 20240430143045
yyMMddHHmmss yyMMddHHmmss 240430143045
HHmmss HHmmss 143045

除上述常量外,FixedRule 支持任意合法的 .NET 自定义日期格式串(如 yyyy-MM-dd、dd/MM/yyyy),由 DateTime.Now.ToString(format) 直接解析。

⚠️ 注意大小写:HH 为 24 小时制,hh 为 12 小时制;MM 为月份,mm 为分钟。

Serial 字段说明

字段 说明 示例
GlideLen 流水号位数,不足时用补位码在左侧补齐 长度 3、当前值 1 → 001
StartNumber 起始值,流水号从几开始计数;溢出重置后回到该值 起始值 1 → 第一个编码流水位为 001
GlideStep 步长,每次生成编码时流水号递增的值 步长 1 → 001、002、003
Glide 补位码(单字符),不足设定长度时左侧补齐 补位码 0、长度 3、值 12 → 012

若 Serial 段未配置 GlideLen 或 Glide,则退化为 yyyyMMddHHmmssfff 时间戳拼接,保证不阻塞生成。

流水号循环归零(CycleFlag / CycleMethod)

当 CycleFlag = true 时,流水号会在指定周期结束时自动回归 StartNumber(默认 1)。周期由 CycleMethod 决定:

常量 值 周期 周期标识示例
YEAR 1 按年 2026
MONTH 2 按月 202604
DAY 3 按天 20260406
HOUR 4 按小时 2026040614
MINUTE 5 按分钟 202604061430
INPUT_CHAR 10 按传入字符 inputChar 值

例如按天循环:当天生成 0001,次日自动归零,重新从 0001 开始。按传入字符循环时,不同 inputChar 各自独立计数。注意:主表「当前周期标识」CurrentCycle 仅记录最近一次周期,切回旧周期会从起始值重新累计(单桶连续使用场景下正确)。

长度补齐与截断

  • 分段级:每个分段的 Length 会截取超长内容(补零位数仍由流水号的 GlideLen 决定)。
  • 规则级:MaxLength + Padded + PaddedChar + PaddedMethod。启用补齐(Padded=true)时按补齐字符与左/右方式填充至最大长度(默认左补齐、补 0);未启用补齐但超出 MaxLength 时直接截断。

生成记录与重复检查

每次生成会写入 BasCodingRuleRecord 表(记录最终编码 Result、流水号 SerialNo、传入参数 InputChar),其 Result 唯一约束用于重复检查——生成的编码重复时抛出 code_duplicate。历史记录可通过记录服务 BasCodingRuleRecordService 的查询接口检索。


配置选项详解

DbInitConfig

键 类型 默认 说明
InitTable bool false 启动时检测并自动建表(含种子数据写入)
InitSeedData bool false 是否启用种子数据(当前实现中种子数据随 InitTable 一起写入,此开关在本组件内暂未独立判断)

ConnectionConfigs

键 说明
DbType MySql / SqlServer / Sqlite / Oracle / PostgreSQL / Dm 等
ConnectionString 数据库连接字符串
IsTenantIdFilter 多租户过滤开关,编码规则实体实现了 ITenantEntity,建议开启
IsDeleteFilter 软删除过滤,实体继承 EntityBase,建议开启

DI 注册三种重载

// 1. 从 AppSettings.Configuration 的 "DbInitConfig" 节点读取
services.AddOmiCodingRuleSetup();

// 2. 显式传入 IConfiguration 节点
services.AddOmiCodingRuleSetup(configuration.GetSection("DbInitConfig"));

// 3. 代码内联配置
services.AddOmiCodingRuleSetup(c => { c.InitTable = true; c.InitSeedData = true; });

三种重载均支持可选参数 ServiceLifetime(默认 Scoped)。

服务生命周期

服务 默认生命周期 说明
BasCodingRuleService Scoped 编码规则主表服务
BasCodingRuleDetailService Scoped 编码规则明细服务

API 接口速览

动态 WebApi 路由规则:去掉 Service 后缀作为控制器名,方法名去掉 Get/Post 前缀后按 Async 之前的动词映射 HTTP 方法。生产环境若配置了 DefaultApiPrefix = "/openapi/api",则所有路由前加该前缀。

BasCodingRuleService(编码规则)

HTTP 路由 方法 说明
GET /BasCodingRule/Pages GetPagesAsync 分页查询(含明细导航)
GET /BasCodingRule/Data GetDataAsync 按 Id 查详情(含明细)
GET /BasCodingRule/List GetListAsync 全部未删除列表
POST /BasCodingRule/AddData AddDataAsync 新增(主表 + 明细,事务)
POST /BasCodingRule/UpdateData UpdateDataAsync 更新(主表 + 明细,事务)
DELETE /BasCodingRule/Data DeleteDataAsync 删除(级联明细,事务)
POST /BasCodingRule/DeleteBatch DeleteBatchAsync 批量删除(级联明细,事务)
GET /BasCodingRule/SN/{code} GetSNAsync 生成单据编码(事务 + 差异日志)

BasCodingRuleDetailService(编码规则明细)

HTTP 路由 方法 说明
GET /BasCodingRuleDetail/Pages GetPagesAsync 分页查询
GET /BasCodingRuleDetail/Data GetDataAsync 按 Id 查详情
GET /BasCodingRuleDetail/List GetListAsync 按 codingRuleId 查明细列表(按 Sort 排序)
POST /BasCodingRuleDetail/AddData AddDataAsync 新增明细(事务)
POST /BasCodingRuleDetail/UpdateData UpdateDataAsync 更新明细(事务)
DELETE /BasCodingRuleDetail/Data DeleteDataAsync 删除明细
POST /BasCodingRuleDetail/DeleteBatch DeleteBatchAsync 批量删除明细(事务)

所有接口均带 [DisplayName] / [Description] 特性,便于 Swagger 文档生成与操作日志记录。


错误处理与日志

i18n 错误码一览

资源键 中文消息 触发场景
code_exists 编码已存在 新增 / 更新时 Code 与其他记录重复
record_not_exist 记录不存在 更新时按 Id 找不到记录
select_delete_records 请选择要删除的记录 批量删除传入空数组
code_required 编码规则编码不能为空 GetSNAsync 传入空白 code
rule_not_found 未找到编码规则 GetSNAsync 按 code 查不到记录
rule_disabled 编码规则已禁用 IsDisable == Y 仍调用 GetSNAsync
rule_detail_empty 编码规则未配置明细 主表无任何明细段时调用 GetSNAsync

调用方错误处理示例

try
{
    string sn = await service.GetSNAsync("PO");
}
catch (Exception ex)
{
    // 消息已按请求的 Accept-Language 自动本地化
    logger.LogWarning("生成编码失败: {Message}", ex.Message);
    throw;
}

生成编码的更新操作通过 UpdateAsync(codingRule, enableDiffLog: true, businessData: "生成单据编码") 写入差异日志,可在审计日志中追溯每次 SN 变化。


多语言(i18n)

组件内置 ResX 资源文件(Language/i18n.*.resx),异常消息自动按请求的 Accept-Language 头返回对应语言。

语言 Culture
简体中文(默认) zh-CN
繁体中文-香港 zh-HK
繁体中文-台湾 zh-TW
英语 en-US
日语 ja-JP
法语 fr-FR
越南语 vi-VN

代码中通过强类型静态类访问:

using RuoVea.OmiApi.CodingRule.Language;

throw new Exception(i18n.code_exists);

常见问题

⚠️ Q1:首次启动没有自动建表? 检查 appsettings.json 中 DbInitConfig:InitTable 是否为 true,并确认已调用 AddCodingRuleInitSetup()。建表在 Task.Run 中异步执行,首次启动后稍等片刻表即就绪。

⚠️ Q2:调用 /BasCodingRule/SN/PO 提示「未找到编码规则」? 确认:① 表已建好且种子数据已写入;② 传入的 code 与主表 Code 字段完全一致(区分大小写取决于数据库排序规则);③ 记录未被软删除。

❗ Q3:流水号到了 999 会怎样? 自动重置为 StartNumber(默认 1),见「场景 3」。若业务不允许重置,请加大 GlideLen。

❗ Q4:日期格式用 hh 还是 HH? HH = 24 小时制(推荐),hh = 12 小时制。MM = 月份,mm = 分钟,别写反。

⚠️ Q5:生产环境路由 404? 生产环境通常配置了 DefaultApiPrefix = "/openapi/api",完整路由应为 /openapi/api/BasCodingRule/SN/PO。

⚠️ Q6:更新主表时部分字段被置 null? 更新走 UpdateNav + IsIgnoreAllNullColumns = true,传 null 的字段不会被覆盖;但明细中想清空的字段请传空字符串而非 null。

❗ Q7:多租户下查不到数据? 实体带 [Tenant("0")] 且实现 ITenantEntity,请确认 ConnectionConfigs 中 IsTenantIdFilter: true 且当前请求租户与种子数据租户(SqlSugarConst.DefaultTenantId)一致。


许可证

MIT


相关包

包 说明
RuoVea.DynamicWebApi 动态 WebApi
RuoVea.ExSugar SqlSugar 扩展
RuoVea.ExDto 通用 DTO
RuoVea.ExIdGen 分布式 ID
RuoVea.ExConfig 配置读取
RuoVea.OmiApi.Dict 数据字典模块
RuoVea.OmiApi.Log 系统日志模块
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.1.5 93 9/2/2026
10.0.1.4 98 8/20/2026
10.0.1.3 102 8/16/2026
10.0.1.2 94 8/15/2026
10.0.1.1 101 8/15/2026
10.0.1 101 8/14/2026
10.0.0.2 106 7/24/2026
10.0.0.1 108 7/21/2026
8.0.1.6 92 9/2/2026
8.0.1.5 103 8/20/2026
8.0.1.4 95 8/18/2026
8.0.1.3 103 8/16/2026
8.0.1.2 94 8/15/2026
8.0.1.1 94 8/15/2026
8.0.1 103 8/14/2026
8.0.0.2 108 7/24/2026
8.0.0.1 104 7/21/2026
6.0.0.3 99 8/18/2026
6.0.0.2 102 7/24/2026
6.0.0.1 105 7/21/2026