在企业应用开发中,我们经常需要生成类似 BZ -240704-0001 这种“业务编号”,它通常具有以下特点:

  • 前缀:代表业务类型,如 BZ 表示包装

  • 日期:年月日格式,通常为 yyMMdd

  • 序列号:当天内递增,如 00010002

本文介绍一个支持 自动去重、递增编号、通用字段提取 的工具类,并提供了三种调用方式,适配不同场景。

📦 工具类源码:

package com.kakarote.pm.common;import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.stereotype.Component;import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;/*** 生成唯一业务编号,格式如 FK-240703-0001,支持重试避免重复*/
@Component
public class DocumentCodeGeneratorUtil {/*** 自动生成唯一编号(支持重试,防止重复)* @param prefix        编号前缀,如 "FK"* @param service       MyBatis-Plus 的 Service 对象,用于执行数据库查询* @param columnGetter  要生成编号的字段引用,如 Entity::getDocumentCode* @param <T>           实体类* @return              唯一编号,例如 FK-240704-0001*/private static final int MAX_RETRY = 5;public <T> String generateUniqueCode(String prefix, IService<T> service, SFunction<T, String> columnGetter) {int retry = 0;while (retry < MAX_RETRY) {String code = generateCodeByDatePrefix(prefix, service, columnGetter);int count = service.lambdaQuery().eq(columnGetter, code).count();if (count == 0) {return code;}retry++;}throw new RuntimeException("编号生成失败:连续5次生成重复编号,请稍后重试!");}private <T> String generateCodeByDatePrefix(String prefix, IService<T> service, SFunction<T, String> columnGetter) {String currentDate = new SimpleDateFormat("yyMMdd").format(new Date());String prefixWithDate = prefix + "-" + currentDate + "-";List<T> list = service.lambdaQuery().likeRight(columnGetter, prefixWithDate).orderByDesc(columnGetter).last("limit 1").list();int nextSeq = 1;if (!list.isEmpty()) {try {T entity = list.get(0);String fieldName = getFieldName(columnGetter);String maxCode = (String) PropertyUtils.getProperty(entity, fieldName);String[] parts = maxCode.split("-");if (parts.length == 3) {nextSeq = Integer.parseInt(parts[2]) + 1;}} catch (Exception e) {throw new RuntimeException("反射获取字段值失败", e);}}return prefixWithDate + String.format("%04d", nextSeq);}/*** 通过 SerializedLambda 获取字段名, 例:User::getName => name*/private <T> String getFieldName(SFunction<T, ?> fn) throws Exception {Method writeReplace = fn.getClass().getDeclaredMethod("writeReplace");writeReplace.setAccessible(true);SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(fn);String implMethodName = serializedLambda.getImplMethodName();if (implMethodName.startsWith("get")) {return Introspector.decapitalize(implMethodName.substring(3));} else if (implMethodName.startsWith("is")) {return Introspector.decapitalize(implMethodName.substring(2));}return implMethodName;}
}

 使用方式(3种场景) 

场景 1:在当前 ServiceImpl 内部调用(推荐)

@Autowired

private DocumentCodeGeneratorUtil documentCodeGeneratorUtil;

@Override

public void savePack() {    

String code = documentCodeGeneratorUtil.generateUniqueCode("BZ", this, PmPack::getDocumentCode);    

pmPack.setDocumentCode(code);    

save(pmPack);

}

说明:this 是当前类,已继承 BaseServiceImpl,本身就是 IService<PmPack>
例如:

如果没有就采用方式二的本身的service调用就行

场景 2:在其他类(如 Controller)中调用

@Autowired
private PmPackService pmPackService;

@Autowired
private DocumentCodeGeneratorUtil documentCodeGeneratorUtil;

public void createPackFromController() {
String code = documentCodeGeneratorUtil.generateUniqueCode("BZ", pmPackService, PmPack::getDocumentCode);
}

场景 3:使用 Spring 上下文动态获取(非推荐,仅限无法注入时)

PmPackService service = SpringContextHolder.getBean(PmPackService.class);

String code = documentCodeGeneratorUtil.generateUniqueCode("BZ", service, PmPack::getDocumentCode);

 你需要实现 SpringContextHolder

@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext context;

    @Override
public void setApplicationContext(ApplicationContext ctx) {
context = ctx;
}

    public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
}

🔧 所需依赖(pom.xml)

// MyBatis Plus 核心依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>

// BeanUtils(反射读取字段值)

<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency> 

示例输出

假设今天是 2024年7月4日,编号前缀为 FK,数据库已有最大编号为:

BZ-240704-0001  以此类推第二天重置
如果重复,会自动重试最多 5 次。

例如:

优点总结

功能支持情况
日期前缀
自增长序列
多表复用
自动重试去重
支持 Lambda 字段提取
支持多个业务类型前缀

结语:

 这个工具类已经在多个模块(如:付款利息、包装、检验)中实际应用,稳定可靠,适配 MyBatis-Plus 体系,简洁灵活。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88025.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88025.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/88025.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端相关性能优化笔记

1.打开速度怎么变快 - 首屏加载优化2.再次打开速度怎么变快 - 缓存优化了3.操作怎么才顺滑 - 渲染优化4.动画怎么保证流畅 - 长任务拆分2.1 首屏加载指标细化:1.FP(First Paint 首次绘制) 2.FCP(First contentful Paint 首次内容绘制)&#xff0c;FP 到 FCP 中间其实主要是 SPA…

7.7晚自习作业

实操作业02&#xff1a;Spark核心开发 作业说明 请严格按照步骤操作&#xff0c;并将最终结果文件&#xff08;命名为&#xff1a;sparkcore_result.txt&#xff09;于20点前上传。结果文件需包含每一步的关键命令执行结果文本输出。 一、数据读取与转换操作 上传账户数据$…

手机FunASR识别SIM卡通话占用内存和运行性能分析

手机FunASR识别SIM卡通话占用内存和运行性能分析 --本地AI电话机器人 上一篇&#xff1a;手机无网离线使用FunASR识别SIM卡语音通话内容 下一篇&#xff1a;手机通话语音离线ASR识别商用和优化方向 一、前言 书接上一文《阿里FunASR本地断网离线识别模型简析》&#xff0c;…

虚幻引擎Unreal Engine5恐怖游戏设计制作教程,从入门到精通从零开始完整项目开发实战详细讲解中英字幕

和大家分享一个以前收集的UE5虚幻引擎恐怖游戏开发教程&#xff0c;这是国外一个大神制作的视频教程&#xff0c;教程从零开始到制作出一款完整的游戏。内容讲解全面&#xff0c;如蓝图基础知识讲解、角色控制、高级交互系统、高级库存系统、物品检查、恐怖环境氛围设计、过场动…

多人协同开发时Git使用命令

拉取仓库代码 # 拉取远程仓库至本地tar_dir路径 git clone gitgithub.com:your-repo.git target_dir # 默认是拉取远程master分支&#xff0c;下面拉取并切换到自己需要开发的分支上 # 假设自己需要开发的分支是/feature/my_branch分支 git checkout -b feature/my_branch orig…

线性表——双向链表

线性表——双向链表1. 双向链表的实现1.1 简单图例1.2 结点的定义1.3 新结点的创建1.4 链表的初始化1.5 结点的插入1.5.1 头部插入&#xff08;头插&#xff09;1.5.2 尾部插入&#xff08;尾插&#xff09;1.5.3 任意位置&#xff08;前&#xff09;插入1.6 结点的删除1.6.1 头…

Java后端技术博客汇总文档

文章目录 前言Java后端汇总链接Java基础知识点数据结构算法&#xff08;Java实现&#xff09;算法知识点合集算法刷题算法竞赛AcWing课程蓝桥杯AB组辅导课合集&#xff08;更新中…&#xff09; 源码分析redission 数据库SQL ServerMySQLRedis -Canal JUC并发编程JVMNetty日志框…

QT 菜单栏设计使用方法

目录 常用设置函数 多个QAction的单选设置 ​​​​​​​菜单相关类 ​​​​​​​ 系统菜单的生成和响应 使用代码添加系统菜单 使用UI设计器设计系统菜单 使用Qt设计及界面时&#xff0c;常用的两种方式添加菜单&#xff0c;第一使用UI界面添加&#xff0c;第二种 在…

AIGC领域AI艺术,打造个性化艺术作品

AIGC领域AI艺术,打造个性化艺术作品 关键词:AIGC、AI艺术、生成对抗网络、个性化创作、深度学习、艺术风格迁移、创意计算 摘要:本文深入探讨了AIGC(人工智能生成内容)在艺术创作领域的应用,重点分析了如何利用AI技术打造个性化艺术作品。文章从技术原理出发,详细解析了生…

基于Flask+Jinja2的快捷教务系统(后端链接到新版正方教务系统)

快捷教务系统&#xff08;Easy Educational Administration Management System, EasyEAMS&#xff09; 项目简介 EasyEAMS 是一个基于 Flask Jinja2 的现代化教务系统 Web 应用。学生可通过网页端登录&#xff0c;在线查询个人信息、成绩、课表、学业生涯、通知、选课等。系…

EDM自动化与出海独立开发实用教程

随着互联网全球化发展&#xff0c;越来越多的独立开发者&#xff08;Indie Developer&#xff09;选择将自己的产品推向海外市场。如何高效地获客、激活用户、提升转化率&#xff0c;成为出海过程中必须解决的问题。EDM&#xff08;电子邮件营销&#xff09;自动化&#xff0c;…

「日拱一码」017 深度学习常用库——TensorFlow

目录 基础操作 张量操作&#xff1a; tf.constant 用于创建常量张量 tf.Variable 用于创建可训练的变量张量 tf.reshape 可改变张量的形状 tf.concat 可将多个张量沿指定维度拼接 tf.split 则可将张量沿指定维度分割 数学运算&#xff1a; tf.add 张量的加运算 tf.su…

ARM DStream仿真器脚本常用命令

以下是ARM DStream仿真器脚本中常用的命令及其功能分类&#xff0c;结合调试流程和典型应用场景整理&#xff1a; ⚙️ 一、连接与初始化命令 connect 建立与目标设备的连接&#xff0c;需指定接口类型&#xff08;如JTAG/SWD&#xff09;和处理器核心。 示例&#xff1a;conne…

vscode 调试unity

lanch.json { “version”: “0.2.0”, “configurations”: [ { “name”: “Attach to Unity”, “type”: “vstuc”, “request”: “attach” } ] }

金融IT入门知识点

银行金融IT核心知识点全解析&#xff1a;架构、技术与实践 一、金融IT的战略地位与行业特性 金融IT作为银行业务的核心支撑体系&#xff0c;其发展水平直接决定了银行服务的效率、安全性与创新能力。截至 2025年&#xff0c;中国银行业线上化业务占比已达97%&#xff0c;手机银…

C++——手撕智能指针、单例模式、线程池、String

智能指针今天我们来学习一下C中的智能指针&#xff0c;如果有人不知道C中的智能指针的概念的话&#xff1a;C智能指针是一种基于RAII&#xff08;Resource Acquisition Is Initialization&#xff0c;资源获取即初始化&#xff09;机制的高级内存管理工具&#xff0c;用于自动化…

Mybatis----留言板

基础项目&#xff1a;留言板 截止到目前为止&#xff0c;我们已经学习了 Spring&#xff08;只学习了DI&#xff09;、Spring MVC、SpringBoot、Mybatis 这些知识了&#xff0c;已经满足了做简单项目的基本要求了&#xff0c;所以接下来我们就从0到1实现表白墙项目。 需求分析…

Web-API-day3 DOM事件进阶

一、 事件流 1.事件冒泡 const fa document.querySelector(.father)const son document.querySelector(.son)document.addEventListener(click, function () {alert(我是爷爷)})fa.addEventListener(click, function () {alert(我是爸爸)})son.addEventListener(click, fun…

小波增强型KAN网络 + SHAP可解释性分析(Pytorch实现)

效果一览一、传统KAN网络的痛点与突破 1. 传统KAN的局限性 传统Kolmogorov-Arnold网络&#xff08;KAN&#xff09;虽在理论上有可靠的多变量函数逼近能力&#xff0c;但存在显著瓶颈&#xff1a; 计算效率低&#xff1a;训练速度慢于MLP&#xff0c;资源消耗大&#xff0c;尤其…

tomcat部署多个端口以及制定路径部署-vue3

vue3项目tomcat部署记录 使用hash路由 字符串拼接的图片地址可以使用import.meta.env.BASE_URL 默认8080 如果部署地址为8080/xc 则设置 vite.config.js中设置base为’/xc/’ outDir设置为xc 打包产物直接拖到webapps目录下 如果另开一个端口 如8081 设置根目录访问 conf/ser…