在开发中,尤其是涉及分层结构(如分类、目录、组织等)时,唯一的编码(Code)是一个常见需求。一个好的编码生成器需要确保代码的唯一性、可读性,并且能够随着层级的递增动态扩展。本文将探讨如何设计和实现一个通用的唯一 Code 生成方法。
场景分析
-
需求:
- 第一层的编码规则是
B+两位数字
(如B01
)。 - 第二层是
B+三位数字
(如B01001
),第三层以此类推。 - 编码需唯一,并支持动态生成。
- 子层级的编码需基于父层级的编码(如
B01
的子级为B01001
)。
- 第一层的编码规则是
-
常见的应用场景:
- 组织架构:如公司 -> 部门 -> 团队。
- 商品分类:如一级分类 -> 二级分类 -> 三级分类。
- 文件目录:如文件夹 -> 子文件夹 -> 文件。
设计要点
-
唯一性:
- 每个编码在其所属层级内必须唯一。
- 子层级编码必须包含父层级的编码。
-
动态扩展性:
- 层级数不固定,可根据需要自由扩展。
-
高效查询:
- 生成编码时,应尽量减少对数据库的读写操作,提高性能。
-
清晰的规则:
- 规则简单明了,便于开发和维护。
- 不允许有模糊的特殊字符或冗余的空格。
实现思路
-
规则定义:
- 第一层 :以
B
开头,后接两位数字(B01
)。 - 第二层 :在父级编码基础上追加三位数字(
B01001
)。 - 第三层 :继续追加三位数字(
B01001001
)。
- 第一层 :以
-
递增逻辑:
- 查询当前层级的最大编码。
- 在最大编码的末尾递增(如
001 -> 002
)。
-
默认值生成:
- 如果层级中无记录,则从
B01
或001
开始生成。
- 如果层级中无记录,则从
-
数据存储:
- 数据表需包含以下字段:
Code
(编码)Level
(层级)Pid
(父节点 ID,用于关联父层级)Name
(名称,用于展示)
- 数据表需包含以下字段:
代码实现
以下是使用 C# 的实现示例:
数据库服务层
csharp
public async Task<string> GetMaxCodeByLevelAsync(int level, Guid? parentId = null)
{
if (parentId.HasValue)
{
// 查询特定父节点下的最大 Code
return await _context.SpAdjustmentDicts
.Where(o => o.Level == level && o.Pid == parentId)
.OrderByDescending(o => o.Code)
.Select(o => o.Code)
.FirstOrDefaultAsync();
}
else
{
// 查询该层级的最大 Code
return await _context.SpAdjustmentDicts
.Where(o => o.Level == level)
.OrderByDescending(o => o.Code)
.Select(o => o.Code)
.FirstOrDefaultAsync();
}
}
生成编码方法
csharp
private async Task<string> GenerateCodeAsync(int level, Guid? parentId = null)
{
string maxCode = await GetMaxCodeByLevelAsync(level, parentId);
if (string.IsNullOrEmpty(maxCode))
{
if (parentId.HasValue)
{
// 子层级的默认起始值
var parent = await _context.SpAdjustmentDicts.FindAsync(parentId);
return parent.Code + "001";
}
else
{
// 第一层默认值
return "B01";
}
}
// 递增逻辑
if (parentId.HasValue)
{
// 子层级递增
int subNumber = int.Parse(maxCode.Substring(maxCode.Length - 3)) + 1;
return maxCode.Substring(0, maxCode.Length - 3) + subNumber.ToString("D3");
}
else
{
// 第一层递增
int majorNumber = int.Parse(maxCode.Substring(1)) + 1;
return "B" + majorNumber.ToString("D2");
}
}
记录校验与添加
csharp
private async Task<SpAdjustmentDict> EnsureRecordExistsAsync(
string name,
int level,
SysUser user,
Guid? parentId = null)
{
if (string.IsNullOrEmpty(name)) return null;
var existingRecord = parentId.HasValue
? await _adjustmentDictService.SelectByNameAndLevelAndPid(name, level, parentId.Value)
: await _adjustmentDictService.SelectByNameAndLevel(name, level);
if (existingRecord != null) return existingRecord;
string code = await GenerateCodeAsync(level, parentId);
var newRecord = new SpAdjustmentDict
{
Name = name,
Code = code,
Level = level,
Pid = parentId,
LogicDelFlg = false,
InsertBy = user.UserName,
UpdateBy = user.UserName,
InsertTime = DateTime.Now,
UpdateTime = DateTime.Now,
InsertById = user.Id,
UpdateById = user.Id
};
return await _adjustmentDictService.Add(newRecord);
}
优化与扩展
-
并发问题:
- 为防止并发导致重复编码,可在数据库中为
Code
列添加唯一约束。 - 使用事务或锁机制确保线程安全。
- 为防止并发导致重复编码,可在数据库中为
-
高性能需求:
- 考虑缓存每个层级的最大编码,减少数据库查询频率。
-
适配更多规则:
- 可以通过配置文件或参数传递动态调整编码规则(如修改前缀或位数)。
-
日志记录:
- 在编码生成过程中记录详细日志,便于排查问题。
总结
通过设计规则清晰、递增逻辑严谨的编码生成方法,可以轻松实现分层结构中的唯一编码需求。这种通用方法适用于多种场景,如组织架构、商品分类等。在实际应用中,根据业务特点调整规则与性能优化策略,可以使编码系统更加高效可靠。