Files
huachuang/docs/superpowers/specs/2026-07-15-codegen-design.md
2026-07-15 16:38:35 +08:00

14 KiB
Raw Blame History

编码生成功能设计规格

日期2026-07-15 分支feature/20260713/base-module 模块nl-module-base基础模块

一、需求概述

在 base 模块下实现通用编码生成引擎,支持固定前缀、日期、流水序号三段组合,通过规则配置化管理,满足订单编码(ORD202607150001)、入库单编码(CK202607150001)等业务场景。

1.1 范围

第一期实现:

  • 三种编码段类型固定前缀PREFIX、日期DATE、流水序号SEQUENCE
  • 序号控制:按天/按月/按年/全局重置、补位字符可自定义、最大值上限
  • 格式控制:分隔符配置、字母大小写转换、固定总长度约束
  • 并发唯一性:号段预分配 + 分布式锁实时两种策略混合
  • 规则管理CRUD + 缓存

本期不实现(预留扩展点):

  • 固定后缀段SUFFIX→ 与 PREFIX 逻辑相同,二期加 type 即可
  • 业务字段映射段BIZ_FIELD
  • 随机段RANDOM
  • 可视化拖拽配置
  • 多租户/数据隔离
  • 溢出告警LOG 打点即可,不做通知)

1.2 决策记录

决策项 选择 理由
前端范围 仅后端 API 聚焦核心引擎质量,前端可后续对接
并发方案 混合方案(号段预分配 + Lock4j 实时) 高性能 + 灵活切换
段类型 核心三种PREFIX/DATE/SEQUENCE 覆盖 80% 场景,其他预留扩展
存储模型 主表字段 + JSON 分段 基本属性可查询,分段配置灵活
重置维度 全部(天/月/年/全局) 实现成本差异不大,一次到位

二、数据库设计

2.1 编码规则表 base_code_rule

CREATE TABLE base_code_rule (
    id              BIGINT        NOT NULL AUTO_INCREMENT  COMMENT '主键',
    rule_name       VARCHAR(64)   NOT NULL                 COMMENT '规则名称',
    rule_code       VARCHAR(32)   NOT NULL                 COMMENT '规则编码(唯一标识,调用时传入)',
    segment_config  JSON          NOT NULL                 COMMENT '分段配置JSON数组',
    separator       VARCHAR(8)    DEFAULT ''               COMMENT '分隔符(- / _ . 或空)',
    letter_case     TINYINT       DEFAULT 0                COMMENT '字母大小写0=原样 1=全大写 2=全小写',
    total_length    INT           DEFAULT 0                COMMENT '固定总长度约束0=不限制)',
    seq_strategy    VARCHAR(16)   DEFAULT 'segment'        COMMENT '序号策略segment=号段预分配 lock=分布式锁实时',
    status          TINYINT       NOT NULL DEFAULT 0       COMMENT '状态0=启用 1=禁用',
    remark          VARCHAR(256)  DEFAULT ''               COMMENT '备注',
    creator         VARCHAR(64)   DEFAULT ''               COMMENT '创建者',
    create_time     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updater         VARCHAR(64)   DEFAULT ''               COMMENT '更新者',
    update_time     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    deleted         TINYINT       NOT NULL DEFAULT 0       COMMENT '是否删除',
    PRIMARY KEY (id),
    UNIQUE KEY uk_rule_code (rule_code)
) COMMENT '编码规则定义表';

2.2 流水号段表 base_code_sequence

CREATE TABLE base_code_sequence (
    id              BIGINT        NOT NULL AUTO_INCREMENT  COMMENT '主键',
    rule_id         BIGINT        NOT NULL                 COMMENT '规则ID关联 base_code_rule.id',
    reset_key       VARCHAR(64)   NOT NULL DEFAULT ''      COMMENT '重置维度键如按天20260715全局GLOBAL',
    current_value   BIGINT        NOT NULL DEFAULT 0       COMMENT '当前已分配的最大序号',
    max_value       BIGINT        NOT NULL DEFAULT 9999    COMMENT '序号最大值上限',
    creator         VARCHAR(64)   DEFAULT ''               COMMENT '创建者',
    create_time     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updater         VARCHAR(64)   DEFAULT ''               COMMENT '更新者',
    update_time     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    deleted         TINYINT       NOT NULL DEFAULT 0       COMMENT '是否删除',
    PRIMARY KEY (id),
    KEY idx_rule_reset (rule_id, reset_key)
) COMMENT '编码流水号段记录表';

2.3 segment_config JSON 结构

[
  { "type": "PREFIX",    "value": "ORD" },
  { "type": "DATE",      "format": "yyyyMMdd" },
  { "type": "SEQUENCE",  "resetBy": "DAY", "startAt": 1, "paddingLen": 4, "paddingChar": "0", "maxValue": 9999 }
]

2.4 段类型字段定义

段类型 字段 说明
PREFIX value 固定前缀字符串
DATE format Java 日期格式,如 yyyyMMddyyyyMMyyyy
SEQUENCE resetBy 重置维度:DAY/MONTH/YEAR/GLOBAL
SEQUENCE startAt 起始值,默认 1
SEQUENCE paddingLen 补位后总长度,如 4 → 0001
SEQUENCE paddingChar 补位字符,默认 "0",可任意字符串
SEQUENCE maxValue 序号最大值上限

三、包结构与模块划分

3.1 API 模块(nl-module-base-api

cn.code.nl.module.base
├── enums/
│   └── ErrorCodeConstants.java              # 追加 CODE_RULE_NOT_EXISTS 等
└── api/codegen/
    ├── CodeGenApi.java                       # Feign 接口,供其他模块调用
    └── dto/
        └── CodeGenerateReqDTO.java           # 编码生成请求ruleCode + bizParams

3.2 Server 模块(nl-module-base-server

cn.code.nl.module.base
├── controller/admin/codegen/
│   ├── CodeRuleController.java               # 规则 CRUD 接口
│   └── vo/
│       ├── CodeRuleSaveReqVO.java            # 新增/编辑请求
│       ├── CodeRulePageReqVO.java            # 分页查询请求
│       ├── CodeRuleRespVO.java               # 规则响应
│       └── CodeRuleSimpleRespVO.java         # 精简响应(下拉选项)
├── service/codegen/
│   ├── CodeRuleService.java                  # 规则管理接口
│   ├── CodeRuleServiceImpl.java              # 规则管理实现
│   ├── CodeGenService.java                   # 编码生成接口
│   ├── CodeGenServiceImpl.java               # 编码生成核心引擎
│   └── SequenceAllocator.java                # 号段预分配器
├── dal/dataobject/codegen/
│   ├── CodeRuleDO.java                       # 编码规则 DO
│   └── CodeSequenceDO.java                   # 流水号段 DO
└── dal/mysql/codegen/
    ├── CodeRuleMapper.java
    ├── CodeSequenceMapper.java
    └── CodeSequenceMapper.xml                # 流水号段的 UPDATE/DELETE SQL

四、核心流程设计

4.1 编码生成流程

外部调用 generate(ruleCode)
        │
        ▼
  ┌─ 查询规则缓存Redis──► 未命中时从 DB 加载并写缓存
  │
  ▼
  ┌─ 遍历分段配置,逐段生成:
  │   PREFIX 段  ──► 直接返回配置的固定值
  │   DATE 段    ──► 按 format 格式化当前日期
  │   SEQUENCE 段──► 调用 SequenceAllocator.allocate()
  │
  ▼
  ┌─ 拼装:段值之间插入 separator
  │
  ▼
  ┌─ 后处理letterCase 转换 / totalLength 校验
  │
  ▼
  返回编码字符串

4.2 SequenceAllocator 号段分配流程

allocate(ruleId, resetKey, segment, strategy)
        │
        ├── strategy = "lock" ──► Lock4j 加锁 → DB SELECT + UPDATE current_value+1 → 解锁 → 返回
        │
        └── strategy = "segment" ──► 先从内存 ConcurrentHashMap 取号段
                │
                ├── 命中且未用完 ──► 从当前号段取一个 → 返回
                │
                └── 未命中或耗尽 ──►
                        ├── Lock4j 加锁
                        ├── DB UPDATE current_value = current_value + stepstep=100
                        ├── 解锁
                        ├── 号段 [current_value-step+1, current_value] 放入本地缓存
                        └── 从缓存取一个返回

4.3 重置维度键计算

public String buildResetKey(ResetByEnum resetBy) {
    return switch (resetBy) {
        case DAY    -> LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); // "20260715"
        case MONTH  -> LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM")); // "202607"
        case YEAR   -> String.valueOf(Year.now().getValue()); // "2026"
        case GLOBAL -> "GLOBAL";
    };
}

不同 resetKeybase_code_sequence 表中为不同行,各周期流水自然隔离。

4.4 并发保障总结

策略 实现 性能 连续性 适用场景
segment(默认) Lock4j + DB UPDATE 预取 + 内存 CAS 允许断号 高并发订单编码
lock Lock4j + DB SELECT FOR UPDATE 实时 严格连续 低频关键编码

五、API 设计

5.1 规则管理Controller

方法 路径 说明
POST /base/code-rule/create 创建规则
PUT /base/code-rule/update 更新规则
DELETE /base/code-rule/delete?id= 删除规则
GET /base/code-rule/get?id= 获取规则详情
GET /base/code-rule/page 分页查询规则列表
GET /base/code-rule/simple-list 精简列表(下拉选项)

5.2 编码生成Feign API

方法 路径 说明
POST /api/base/code-gen/generate 生成编码

请求体:

{ "ruleCode": "ORDER_CODE" }

响应:纯字符串,如 "ORD202607150001"

5.3 规则 Save 请求体示例

{
  "ruleName": "订单编码",
  "ruleCode": "ORDER_CODE",
  "segments": [
    { "type": "PREFIX", "value": "ORD" },
    { "type": "DATE", "format": "yyyyMMdd" },
    { "type": "SEQUENCE", "resetBy": "DAY", "startAt": 1, "paddingLen": 4, "paddingChar": "0", "maxValue": 9999 }
  ],
  "separator": "",
  "letterCase": 1,
  "totalLength": 0,
  "seqStrategy": "segment",
  "status": 0,
  "remark": "订单编号生成规则"
}

六、核心类与方法清单

6.1 CodeRuleServiceImpl

方法 职责
createRule(CodeRuleSaveReqVO) 创建规则,校验 ruleCode 唯一性
updateRule(CodeRuleSaveReqVO) 更新规则,清除 Redis 缓存
deleteRule(Long id) 删除规则
getRule(Long id) 获取规则详情
getRuleByCode(String ruleCode) 根据 ruleCode 查规则(带 Redis 缓存)
getRulePage(CodeRulePageReqVO) 分页查询规则列表

6.2 CodeGenServiceImpl

方法 职责
generate(String ruleCode) 编码生成入口
buildSegment(CodeRuleDO, SegmentConfig) 分发到具体段类型处理
buildPrefixSegment(PrefixSegment) 返回固定前缀值
buildDateSegment(DateSegment) 按 format 格式化当前日期
buildSequenceSegment(CodeRuleDO, SequenceSegment) 委托 SequenceAllocator 分配序号
postProcess(String code, CodeRuleDO) 大小写转换 + 总长度校验

6.3 SequenceAllocator

方法 职责
allocate(Long ruleId, SequenceSegment, String strategy) 序号分配入口
allocateByLock(Long ruleId, String resetKey, SequenceSegment) Lock4j 锁 + DB 实时分配
allocateBySegment(Long ruleId, String resetKey, SequenceSegment) 号段预分配(内存 CAS + Lock4j + DB
buildResetKey(String resetBy) 根据重置维度计算 resetKey
formatSequence(long value, SequenceSegment) 补位格式化

6.4 数据对象

映射表 说明
CodeRuleDO base_code_rule segmentConfig 用 JacksonTypeHandler
CodeSequenceDO base_code_sequence ruleId + resetKey 联合定位

6.5 枚举

枚举 说明
SegmentTypeEnum PREFIX / DATE / SEQUENCE 段类型
ResetByEnum DAY / MONTH / YEAR / GLOBAL 重置维度
LetterCaseEnum NONE(0) / UPPER(1) / LOWER(2) 大小写
SeqStrategyEnum SEGMENT / LOCK 序号策略

6.6 错误码(追加到 ErrorCodeConstantscode 从 9 开始)

常量名 code 说明
CODE_RULE_NOT_EXISTS 9 编码规则不存在
CODE_RULE_CODE_DUPLICATE 10 规则编码已存在
CODE_GEN_SEQ_EXCEED_MAX 11 序号超出最大值上限
CODE_GEN_LENGTH_EXCEED 12 生成的编码超出总长度限制

七、缓存设计

缓存 Key 内容 TTL 失效时机
codegen:rule:{ruleCode} 规则 DO含分段配置 30 分钟 规则更新/删除时主动清除
内存号段 {ruleId}:{resetKey} 预分配号段区间 用完即弃 号段耗尽后自动刷新

八、边界与异常处理

场景 处理方式
规则不存在 抛出 CODE_RULE_NOT_EXISTS 业务异常
规则被禁用 抛出业务异常
序号达上限 Lock4j 锁内检测,达上限抛 CODE_GEN_SEQ_EXCEED_MAX,日志记录
生成后总长度超限 校验后抛 CODE_GEN_LENGTH_EXCEED
号段预分配时并发冲突 Lock4j 保证串行,不冲突
服务重启 内存号段丢失,从 DB 重新预取新的 range少量断号可接受
segment 策略下 DB 不可用 如已缓存号段则继续分配,耗尽后失败;全局不可用则抛异常

九、二期扩展预留

扩展项 实现方式
SUFFIX 段 SegmentTypeEnumSUFFIX,逻辑同 PREFIX
BIz_FIELD 段 CodeGenerateReqDTO.bizParams 中传入字段值,buildBizFieldSegment 读取
RANDOM 段 RANDOM 类型,配置位数和字符集(数字/字母/混合),用 SecureRandom 生成
溢出告警 AlertService 发钉钉通知,配置项控制