分维度流水号取号器

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

实现"按主体/类型/年月等维度独立自增的业务编号"(如合同号 HT-MX-CG-202609-0037、订单号、工单号)的并发安全取号器。覆盖三层防重号设计、首行创建竞态用 SAVEPOINT 化解、唯一键必须用主键而非可空展示码、展示码规范化(组织编码自带连字符会破坏编号结构)、预览不占号语义,以及"20 并发零重号"的服务端真机验证方法。触发场景:要新增一套业务编号规则、要排查编号重号/跳号、或要把"先查后插"的取号逻辑改造成并发安全时加载本 skill。

一句话选型结论

按资料字段整理

分维度流水号取号器是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_62138798/sequence-number-allocator
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_62138798仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791623468
正文体量9.5 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
分维度流水号取号器当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

9.5 KB
模型实际读到的说明书原文,来自技能仓库

分维度流水号取号器(并发安全)

触发场景

  • 新增一套业务编号规则(合同号 / 订单号 / 工单号 / 结算单号)。
  • 排查编号重号或跳号(号被消耗但业务没落库)。
  • 现有实现是"先 SELECT MAX 再 +1",需要改成并发安全。

编号形态先定清楚

先写死结构,再决定每一段怎么来:

HT-{主体码}-{类型码}-{YYYYMM}-{4位流水}
└HT 固定前缀
  └MX 主体码 —— 展示值,可空、可重复(见下方"规范化")
     └CG 类型码 —— 只做格式校验,枚举 SSOT 放常量表,不硬编码在代码里
        └202609 周期维度
           └0037 流水 —— 事务内取,永久不可变

周期维度用「取号时的年月」,不是业务日期。跨月补号/补录会自然落到当前月份的号段,这是可接受的;若业务要求按业务日期归月,必须显式传入 period。

三层防重号(缺一层都可能出问题)

层 做法 兜住什么
① 唯一键维度 UNIQUE(dimension_1, dimension_2, period) 放在序列计数器表上 同维度并发串行化
② 行锁 + 同事务 SELECT ... FOR UPDATE 递增,且与业务写入同一事务 跨维度互不干扰;避免"烧号"
③ 业务表唯一索引 业务表的 xx_no 上加 UNIQUE,捕获冲突后重试(2–3 次) 代码缺陷 / 人工改库

🔴 维度必须用主键,不能用可空展示码

这是最常见的错误来源。反面例子:

# ✗ 错误:用 organization_code 做维度
UNIQUE(org_code, type_code, period)   # org_code 可空、无唯一约束

后果:① 未填 code 的组织 → 编号退化成 HT--CG-202609-0001(含空段);
② 两个组织填了同一个 code → 共享同一计数器,并发下必然重号。

# ✓ 正确:用主键做维度,展示码只用来拼串
UNIQUE(tenant_id, type_code, period)

🔴 与业务写入必须同事务

取号不得单独提交。若取号 commit 后业务写入失败,号被消耗而业务未落库 → 编号出现空洞(在审计/对账场景会被当成漏号调查)。

async def create_contract(session, ...):
    no = await next_contract_no(session, ...)   # 不加锁提交,只递增
    session.add(Contract(contract_no=no, ...))
    await session.commit()                      # 一次提交,同生共死

🔴 首行创建竞态:用 SAVEPOINT 化解

维度行首次取号时还不存在,两个并发事务会同时 INSERT,其中一个必然撞唯一键。
不能用 session.rollback() —— 那会把调用方正在进行的业务写入一起丢掉。要用保存点:

async def _insert_sequence_row(session, ...) -> Row | None:
    row = Sequence(...)
    try:
        async with session.begin_nested():   # SAVEPOINT
            session.add(row)
            await session.flush()
        return row
    except IntegrityError:
        return None                          # 败给并发,转加锁重读

随后必须用加锁读重新获取:

row = await _lock_sequence_row(session, ...)   # SELECT ... FOR UPDATE

为什么必须加锁读:MySQL 默认隔离级别是 REPEATABLE READ,普通 SELECT 走一致性快照,
看不到并发事务刚提交的行;而锁定读(FOR UPDATE)总是读取最新已提交版本。
用普通 SELECT 重读会取到 None,进而误判为"行不存在"。

完整流程:

row = await _lock_sequence_row(session, ...)
if row is None:
    row = await _insert_sequence_row(session, ...)
if row is None:
    row = await _lock_sequence_row(session, ...)   # 加锁重读
if row is None:
    raise SequenceAllocationError(...)             # 异常情况,需人工介入
row.current_seq += 1
await session.flush()
return build_no(..., row.current_seq)

展示码规范化(易踩)

组织编码类的展示字段常常自带分隔符(如 YX-001)。裸嵌入会破坏编号结构:

HT-YX-001-CG-202609-0037     ← 段数不定,无法解析

规范化为只含大写字母数字,并设长度上限:

cleaned = re.sub(r"[^A-Z0-9]", "", (code or "").strip().upper())[:8]
return cleaned or f"T{tenant_id}"        # 缺失/规范化后为空 → 回退

残留风险:规范化后可能撞码

两个组织编码规范化后相同(如 YX-001 与 YX001)→ 产出完全相同的编号串 →
第 ③ 层持续冲突且重试难收敛。这属数据治理,代码兜不住,必须提供上线前的检测 SQL:

SELECT UPPER(REPLACE(REPLACE(REPLACE(code,'-',''),' ',''),'_','')) AS norm_code,
       COUNT(*) n, GROUP_CONCAT(id) ids
FROM tenants WHERE code IS NOT NULL AND TRIM(code) <> ''
GROUP BY norm_code HAVING COUNT(*) > 1;     -- 结果应为空

流水位宽

按 4 位左补零,但超过 9999 不要截断。截断会破坏唯一性;编号列给足长度(如 VARCHAR(64))即可,
超过 4 位时自然变 5 位:

f"{seq:04d}"   # 9999 → '9999',10000 → '10000'(不截断)

预览接口(不占号)

表单上要显示"下一个编号",但不能真的占用。做法:不加锁、不递增、不建行,只读 current_seq + 1 拼串。

⚠️ 必须在接口与前端都说明:预览值仅供参考,并发下可能与实际取号不同,不得当承诺。
实际编号以创建接口的返回值为准。

服务分层(可迁移性)

取号服务只依赖本模块自己的模型,不要 import 组织/用户等其他模块的 Model。
展示码由调用方经端口(Port)解析后作为参数传入:

async def next_contract_no(session, *, tenant_id, contract_type,
                           tenant_code: str | None = None,   # 由调用方经端口取得
                           period: str | None = None) -> str:

好处:服务零外部依赖 → 单测不需要起外部表;将来模块剥离时不用改取号逻辑。
另外:不自行 commit(事务边界归端点层)、不读全局状态(当前时间用可注入的 now 参数)。

验证:20 并发零重号(必须真机跑)

纯函数部分本地可跑;并发部分必须连真库。若本地没有 DB 配置,在服务器上做隔离运行:

# 1) 复制线上 app 到临时目录(绝不动线上目录)
cp -r /var/www/<app>/app /tmp/ctest/app
# 2) 覆盖本次新增/修改的文件
cp -r overlay/app/contracts /tmp/ctest/app/
cp -f overlay/app/models/__init__.py /tmp/ctest/app/models/__init__.py
# 3) 用环境变量提供 DB 配置(不落盘密钥副本)
set -a; . /var/www/<app>/.env; set +a
export APP_DIR=/tmp/ctest
cd /tmp/ctest && /var/www/<app>/venv/bin/python /tmp/overlay/test_xxx.py

测试表用临时前缀(如 zzz_seqtest_):在测试进程里改 Model.__table__.name,再 table.create(sync_conn),
finally 里 DROP 并校验残留为 0。

断言清单:

[ ] N 个并发事务全部成功(无异常)
[ ] 返回编号去重后 = N
[ ] 业务表 COUNT(*) = N 且 COUNT(DISTINCT xx_no) = N
[ ] SQL 层 GROUP BY xx_no HAVING COUNT(*)>1 → 空
[ ] 计数器 current_seq = N
[ ] 序列计数器表仅 1 行(同维度)
[ ] 维度隔离:同主体异类型 / 异主体同类型,各自从 0001 起
[ ] 流水连续 1..N,无空洞、无 0 号
[ ] preview 连续两次相同,且未在计数器表建行
[ ] 临时表清理后残留 0

并发数要大于连接池上限才有意义。若用应用运行时连接池(常见 pool_size=5, max_overflow=5),
20 并发会排队、退化接近串行 —— 测试应使用独占引擎并放大池(如 pool_size=20, max_overflow=0),
以获得真实的锁竞争。

易错点清单

# 易错点 后果
1 唯一键用可空展示码做维度 空值致编号含空段;重复值致共享计数器、并发重号
2 取号后单独 commit 业务写入失败时"烧号",编号出现空洞
3 首行 INSERT 撞唯一键后 session.rollback() 连调用方的业务写入一起回滚,数据丢失
4 保存点回滚后用普通 SELECT 重读 REPEATABLE READ 快照看不到并发刚提交的行 → 误判不存在
5 展示码未规范化(保留连字符) 编号段数不定、无法解析
6 未做展示码重复检测 两组织产出相同编号串,唯一索引持续冲突
7 流水 str(seq).zfill(4)[:4] 之类截断 超 9999 后重号
8 预览接口真的递增/建行 "只是看一下"就消耗了号
9 类型码枚举硬编码在代码里 枚举变更要改代码;违背常量 SSOT
10 并发测试的并发数小于连接池上限 测出来"通过",实际是串行,没验证到竞态

常见问题

5 问
答案由本站依据条目字段整理
分维度流水号取号器怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_62138798/sequence-number-allocator。安装方式为未标注。装完刷新技能列表或重开客户端。

分维度流水号取号器支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

分维度流水号取号器权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

分维度流水号取号器收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

分维度流水号取号器用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。