From ccefd9e7cd0dfbaabca90addab7982ad66b267f3 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 15 Jul 2026 16:38:35 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=8A=9F=E8=83=BD=E8=AE=BE=E8=AE=A1=E8=A7=84?= =?UTF-8?q?=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../specs/2026-07-15-codegen-design.md | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-15-codegen-design.md diff --git a/docs/superpowers/specs/2026-07-15-codegen-design.md b/docs/superpowers/specs/2026-07-15-codegen-design.md new file mode 100644 index 00000000..74aac249 --- /dev/null +++ b/docs/superpowers/specs/2026-07-15-codegen-design.md @@ -0,0 +1,359 @@ +# 编码生成功能设计规格 + +> 日期: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` + +```sql +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` + +```sql +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 结构 + +```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 日期格式,如 `yyyyMMdd`、`yyyyMM`、`yyyy` | +| 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 + step(step=100) + ├── 解锁 + ├── 号段 [current_value-step+1, current_value] 放入本地缓存 + └── 从缓存取一个返回 +``` + +### 4.3 重置维度键计算 + +```java +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"; + }; +} +``` + +不同 `resetKey` 在 `base_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` | 生成编码 | + +请求体: +```json +{ "ruleCode": "ORDER_CODE" } +``` + +响应:纯字符串,如 `"ORD202607150001"` + +### 5.3 规则 Save 请求体示例 + +```json +{ + "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 错误码(追加到 ErrorCodeConstants,code 从 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 段 | 在 `SegmentTypeEnum` 加 `SUFFIX`,逻辑同 PREFIX | +| BIz_FIELD 段 | 在 `CodeGenerateReqDTO.bizParams` 中传入字段值,`buildBizFieldSegment` 读取 | +| RANDOM 段 | 加 `RANDOM` 类型,配置位数和字符集(数字/字母/混合),用 `SecureRandom` 生成 | +| 溢出告警 | 加 `AlertService` 发钉钉通知,配置项控制 |