云
2 天以前 87733eb3623593da247c051593d64b8ff9e1d3a4
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java
@@ -25,6 +25,11 @@
    private static final String BAG_NO_SEQ_KEY = "mes:bag_no:seq";
    /**
     * 生产批次流水号自增 KEY 前缀(按年份隔离)
     */
    private static final String BATCH_NO_SEQ_KEY_PREFIX = "mes:pro_batch_code:seq:";
    /**
     * 单次批量取码的最大个数
     * <p>
     * 受 Redis 单条命令参数规模与响应体大小约束,过大反而增加单次阻塞时间。
@@ -60,6 +65,17 @@
                    + "local base = tonumber(ARGV[2]) "
                    + "if (not cur) or (tonumber(cur) < base) then redis.call('SET', KEYS[1], base) end "
                    + "return redis.call('INCRBY', KEYS[1], ARGV[1])",
            Long.class);
    /**
     * 原子分配生产批次流水号:先按库内当前最大流水校正基准值,再自增。
     * 手动录入较大批次号或直接导入数据后,下一次自动取号不会与之重复。
     */
    private static final RedisScript<Long> ALLOCATE_BATCH_NO_SCRIPT = new DefaultRedisScript<>(
            "local cur = redis.call('GET', KEYS[1]) "
                    + "local base = tonumber(ARGV[1]) "
                    + "if (not cur) or (tonumber(cur) < base) then redis.call('SET', KEYS[1], base) end "
                    + "return redis.call('INCR', KEYS[1])",
            Long.class);
    @Resource
@@ -117,6 +133,23 @@
    }
    /**
     * 原子分配生产批次流水号:先按库内当前最大流水校正基准值,再自增。
     *
     * @param year         批次号年份(4 位)
     * @param dbMaxSerial  数据库当年已用最大流水号,传 null 视为 0
     * @return 下一个流水号
     */
    public int allocateBatchNo(String year, Integer dbMaxSerial) {
        Long next = stringRedisTemplate.execute(ALLOCATE_BATCH_NO_SCRIPT,
                List.of(BATCH_NO_SEQ_KEY_PREFIX + year),
                String.valueOf(dbMaxSerial == null ? 0 : dbMaxSerial));
        if (next == null) {
            throw new IllegalStateException("分配生产批次流水号失败,请重试");
        }
        return next.intValue();
    }
    /**
     * 原子分配一段连续的袋序号,返回区间起始值。
     * <p>
     * 袋序号原先由「查库内最大值再 +1」得到,并发生成时两个请求会读到同一个最大值,