设计自增流水规则表

  • 创建存储流水号状态的表,包含规则的信息
CREATE TABLE `invoice_sequence` (
  `rule_code` varchar(20) NOT NULL COMMENT '规则编码',
  `prefix` varchar(20) NOT NULL COMMENT '流水号前缀',
  `date_format` varchar(20) NOT NULL COMMENT '日期格式',
  `number_length` int DEFAULT NULL COMMENT '流水号长度',
  `current_date_str` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '当前日期',
  `current_number` int DEFAULT NULL COMMENT '流水号位置',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注信息',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `create_by` varchar(255) DEFAULT NULL COMMENT '创建人',
  PRIMARY KEY (`rule_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='通用自定义自增规则流水号表';
  • 初始化数据
INSERT INTO `invoice_sequence` (`rule_code`, `prefix`, `date_format`, `number_length`, `current_date`, `current_number`, `remark`)
VALUES ('FLRK', 'FLRK', '%Y%m', 4, '202201', 0, '废料入库单据流水号');
  • 重点来了 获取流水id函数(过程)
create
    definer = root@`%` procedure GenerateNewInvoiceNumber(IN p_rule_code varchar(20))
BEGIN
    -- 定义接值的参数
    DECLARE v_number_length INT; #流水长度
    DECLARE v_prefix VARCHAR(20); #流水前缀
    DECLARE v_date_format VARCHAR(20); #时间格式化方式
    DECLARE v_current_date VARCHAR(20); #上次生成流水的格式化时间
    DECLARE v_current_number INT; #上次生成流水的值
    DECLARE v_count INT;
    #是否存在当前流水规则的CODE
    -- 设置事务超时时间为 10 秒
    SET innodb_lock_wait_timeout = 10;
    START TRANSACTION;

    -- 预先获取规则信息
    -- 获取流水长度,流水前缀,时间格式化字符串,流水最新的时间
    SELECT `number_length`,
           `prefix`,
           `date_format`,
           `current_date_str`,
           `current_number`,
           count(1)
    INTO
        v_number_length, v_prefix, v_date_format,v_current_date ,v_current_number,v_count
    FROM `invoice_sequence`
    WHERE `rule_code` = p_rule_code FOR
    UPDATE;

    IF v_count = 0 THEN #如果CODE未被查询出来
        SIGNAL SQLSTATE '45001'
            SET MESSAGE_TEXT = "当前单据CODE不存在配置表中";
    END IF;

    -- 生成新流水号
    SET @newNumber = 0;

    SET @tempDataStr = DATE_FORMAT(NOW(), v_date_format);
    -- 判断当前日期是否与存储的日期一致
    IF v_current_date = @tempDataStr THEN
        SET @newNumber = v_current_number + 1;
        -- 更新表中的状态
        UPDATE `invoice_sequence` SET `current_number` = @newNumber WHERE `rule_code` = p_rule_code;
    ELSE
        -- 获取当前流水号
        SET @newNumber = 1;
        -- 更新表中的状态
        UPDATE `invoice_sequence`
        SET `current_number`   = @newNumber,
            `current_date_str` = @tempDataStr
        WHERE `rule_code` = p_rule_code;
    END IF;

    -- 格式化新流水号为指定长度
    SET @formattedNewNumber = LPAD(@newNumber, v_number_length, '0');

    -- 提交事务
    COMMIT;
    -- 恢复默认事务超时时间
    SET innodb_lock_wait_timeout = 50;
    -- 最终生成的流水号
    SELECT CONCAT(v_prefix, DATE_FORMAT(NOW(), v_date_format), @formattedNewNumber) AS `new_invoice_number`;
END;
  • 测试获取流水号
    image-1706583331900

image-1706583367253

  • 并发场景测试
    开启10个线程并发生成流水id
    image-1706583723805
    顺利生成流水id
    image-1706584274931