当前位置: 首页 > news >正文

业务单据号每日重置后从1开始

概述


如果你从事过类似B端进销存系统相关的开发工作,一定会遇到一个需求:

业务单据号每天重置,后几位每天从1开始递增。

比如说门店每日的订货单:

  • DH202407010001
  • DH202407010002
  • DH202407010003
  • DH202407010004
头两个字母DH表示订货;
中间6位是年份和日期
后四位是自增的

到了第二天的时候,订货单据号会重置重新从1开始:

  • DH202407020001
  • DH202407020002
  • DH202407020003
  • DH202407020004

有线下门店的各个餐饮、茶饮店门店端系统的单据,基本都是使用如上的规则。

解决方案讨论


处理这个需求之前,需要先了解这个需求背后隐含的2个问题:

  • 单据号当天不能重复,就算在【分布式环境】且有【并发】的情况下也不能重复;
  • 单据号每天都需要重置。

如果你到网络上查询解决方案,可能搜索到的大概方案如下:

  • 使用Redis或其他缓存系统中的原子操作;
  • 使用分布式ID生成器,如Snowflake算法;
  • 使用存储过程、触发器等
  • 等等。。。。。。

这些我都觉得要么太复杂要么太贵了,就比如说使用Redis和MQ的,完全没有必要因为这个小需求,把重量级的Redis和MQ引入进来,且买Redis和MQ示例也是需要钱的。

需要结合当时的业务实际情况和技术团队技术栈情况,使用合适的技术。

解决方案:使用JAVA+mysql+定时任务


由于公司的门店不多,几百家,流量不大,就算有并发,瞬间并发数也是非常低的。是可以直接使用mysql来实现的。

主要的设计思路如下:

  • 用一张mysql表,建立自增id,确保在【当天】不重复;
  • 使用定时任务,每天凌晨的时候清理掉(TRUNCATE)数据,确保第二天id又从1开始。

这套方案虽然简单粗暴,但它已稳定运行快一年了,暂时未出现过任何问题。算是一套价格便宜又实惠稳定的技术方案了。

具体的实操代码


目前团队用的技术是基于SpringCloud Alibaba +DDD的,会使用到阿里相关的技术组件和涉及到DDD相关的内容。

建立docs_day_id_generator表以及PO对象

CREATE TABLE `docs_day_id_generator` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id',`created_date` date NOT NULL COMMENT '创建日期',`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (`id`),KEY `idx_create_date` (`created_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='单据id生成'

对应的po对象:

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;/*** 单据id生成器PO*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("docs_day_id_generator")
public class DocsDayIdGeneratorPO {/*** 自增id*/private Long id;/*** 创建日期*/private Date createdDate;
}

DDD仓储层和领域层实现

剩下的只要在DDD的仓储层和domain层定义相关的接口和实现即可。

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;/*** 单据id生成器-仓储接口层实现类*/
@RequiredArgsConstructor
@Repository
public class DocsDayIdGeneratorRepositoryImpl implements DocsDayIdGeneratorRepository {private final DocsDayIdGeneratorMapper docsDayIdGeneratorMapper;@Overridepublic Long createDocsSequence(Date currentDate) {DocsDayIdGeneratorPO poInsert = DocsDayIdGeneratorPO.builder().createdDate(currentDate).build();docsDayIdGeneratorMapper.insert(poInsert);return poInsert.getId();}
}

仓储层的实现比较简单,只需要构建一个DocsDayIdGeneratorPO记录,插入到表里即可,生成的自增id会存储在po对象的id字段里,直接返回即可。

领域服务层则需要接收一个模块编码,并生成对应模块的单据号,比如订货单据号,盘点单据号,退货单据号等。

import lombok.AllArgsConstructor;
import lombok.Getter;/*** 模块枚举*/
@Getter
@AllArgsConstructor
public enum ModuleEnum {ORDER("order", "订货","DH"),CHECK("check", "盘点","PD"),REFUND("refund", "退货","TH");/*** 编码*/private final String code;/*** 描述*/private final String desc;/*** 单据前缀*/private final String prefix;
}
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.Date;/*** id生成器-领域层接口实现类*/
@RequiredArgsConstructor
@Slf4j
@Service
public class DocsDayIdGeneratorDomainServiceImpl implements DocsDayIdGeneratorDomainService {public final DocsDayIdGeneratorRepository docsDayIdGeneratorRepository;@Overridepublic String createDocsCode(ModuleEnum moduleEnum) {Long code = docsDayIdGeneratorRepository.createDocsSequence(new Date());/*单据号规则:前两位是模块编码,接下来六位是日期,后四位是自增id,不足4位,则在前面补零*/return moduleEnum.getPrefix() + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + String.format("%04d", code);}
}

定时任务每天凌晨重置

市面上定时任务的产品非常多,出于统一性,我这边的团队用的是阿里的Schedulex2.0。只要阿里有相关的产品,都是优先选择阿里体系的。

定时任务处理器

import com.alibaba.schedulerx.worker.domain.JobContext;
import com.alibaba.schedulerx.worker.processor.JavaProcessor;
import com.alibaba.schedulerx.worker.processor.ProcessResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** 重置单据号自增id,每天从1开始*/@RequiredArgsConstructor
@Component
@Slf4j
public class DocsIdResetJobProcessor extends JavaProcessor {private final DocsDayIdGeneratorDomainService docsDayIdGeneratorDomainService;@Overridepublic ProcessResult process(JobContext context) {try {docsDayIdGeneratorDomainService.resetdDocsSequence();return new ProcessResult(true);}catch (Exception e){return new ProcessResult(false,e.getMessage());}}
}

resetdDocsSequence方法的实现超级简单,就是一个truncate一下table即可。

 <update id="resetdDocsSequence">truncate table docs_day_id_generator;</update>

定时任务的cron表达式如下:

0 0 0 * * ?

至于阿里Schedulex2.0界面上相关的配置,这个在网上很容易找到,这里就不赘述了。

总结


选择符合当前自己团队实际情况的技术即可,不一定要高大上的。


http://www.mrgr.cn/news/15461.html

相关文章:

  • 商标权-系统架构师(五十八)
  • VBA学习(67):Excel VBA 提取数字/自定义工作表函数/正则表达式/批量提取电话号码
  • Java和C#哪个更适合大型项目?
  • 自建远程桌面RustDesk服务器(CentOS配置,保姆级案例)
  • 代码搭建应用zion
  • 第三讲__简答题
  • Furion项目的单元测试
  • 数字文创产业:用科技讲述文化故事的新方式
  • Kafka3.x 使用 KRaft 模式部署 不依赖 ZooKeeper
  • 笔记整理—什么是uboot内核
  • 三耐环保家族控股99.17%:分红6000多万再补流,董事长董秘一年3次被警示
  • Thread.join()
  • 《机器学习》 贝叶斯分类器 原理、参数讲解及代码演示
  • CANoe入门(三) :CANoe全仿真阶段,模拟数据和信号
  • assert()在solidity的运用,模糊测试案例
  • Chainlit接入FastGpt接口完美对接,实现全新的用户聊天界面
  • Spring框架中PathMatchingResourcePatternResolver解析资源路径并匹配资源模式
  • SQL 对版本进行排序遇到的问题
  • 构建高可用性Nginx:配置策略与最佳实践
  • java中的Opencv:Opencv简介与开发环境部署