Browse Source

fix: paper_id重复问题

大侠咬超人 1 week ago
parent
commit
aba795bfe2
1 changed files with 22 additions and 17 deletions
  1. 22 17
      app/Services/PaperIdGenerator.php

+ 22 - 17
app/Services/PaperIdGenerator.php

@@ -5,23 +5,26 @@ namespace App\Services;
 /**
  * 试卷ID生成器
  * 参考行业标准 Snowflake ID 思想
+ *
+ * 【修复】增强多进程并发安全:
+ * - 添加进程ID区分不同 PHP-FPM Worker
+ * - 增加随机数位数降低碰撞概率
  */
 class PaperIdGenerator
 {
     // 基准时间:2020-01-01 00:00:00 (秒)
     private const EPOCH = 1577836800;
 
-    // 12位数字的位分配(总计约2.8万亿个容量,足够使用200年)
-    // 格式:时间戳(8位) + 序列号(2位) + 随机数(2位) = 12位数字
+    // 15位数字的位分配
+    // 格式:时间戳(8位) + 进程ID(2位) + 序列号(2位) + 随机数(3位) = 15位数字
     private const TIMESTAMP_BITS = 35; // 时间戳(秒)- 可覆盖约34年
     private const SEQUENCE_BITS = 11;  // 序列号 - 2048个值
-    private const RANDOM_BITS = 11;    // 随机数 - 2048个值
 
     /**
-     * 生成12位数字ID
-     * 格式:TTTTTsssssR(时间戳+序列号+随机数)
+     * 生成15位数字ID(增强版,支持多进程并发)
+     * 格式:TTTTTTTT + PP + SS + RRR(时间戳 + 进程ID + 序列号 + 随机数)
      *
-     * @return string 12位数字字符串
+     * @return string 15位数字字符串
      */
     public static function generate(): string
     {
@@ -29,6 +32,9 @@ class PaperIdGenerator
         $timestamp = intdiv(time() - self::EPOCH, 60); // 从基准时间开始的分钟数
         $timestamp &= (1 << self::TIMESTAMP_BITS) - 1; // 截取指定位数
 
+        // 获取进程ID(区分不同的 PHP-FPM Worker)
+        $processId = getmypid() % 100; // 2位进程ID:00-99
+
         // 同一分钟内使用序列号递增,避免并发重复
         static $lastTimestamp = 0;
         static $sequence = 0;
@@ -48,18 +54,16 @@ class PaperIdGenerator
 
         $lastTimestamp = $timestamp;
 
-        // 添加随机数后缀避免猜测
-        $random = random_int(0, 99); // 2位随机数:00-99
+        // 添加随机数后缀避免猜测(3位:000-999,碰撞概率 0.1%)
+        $random = random_int(0, 999);
 
-        // 组合:时间戳(8位) + 序列号(2位) + 随机数(2位) = 12位数字
-        // 时间戳占8位(不够前面补0)
+        // 组合:时间戳(8位) + 进程ID(2位) + 序列号(2位) + 随机数(3位) = 15位数字
         $timestampStr = str_pad((string)$timestamp, 8, '0', STR_PAD_LEFT);
-        // 序列号占2位
-        $sequenceStr = str_pad((string)$sequence, 2, '0', STR_PAD_LEFT);
-        // 随机数占2位
-        $randomStr = str_pad((string)$random, 2, '0', STR_PAD_LEFT);
+        $processIdStr = str_pad((string)$processId, 2, '0', STR_PAD_LEFT);
+        $sequenceStr = str_pad((string)($sequence % 100), 2, '0', STR_PAD_LEFT); // 确保只取2位
+        $randomStr = str_pad((string)$random, 3, '0', STR_PAD_LEFT);
 
-        $id = $timestampStr . $sequenceStr . $randomStr;
+        $id = $timestampStr . $processIdStr . $sequenceStr . $randomStr;
 
         // 确保第一位不为0
         if ($id[0] === '0') {
@@ -70,14 +74,15 @@ class PaperIdGenerator
     }
 
     /**
-     * 验证12位数字ID格式
+     * 验证数字ID格式(支持12位旧格式和15位新格式)
      *
      * @param string $id
      * @return bool
      */
     public static function validate(string $id): bool
     {
-        return preg_match('/^[1-9]\d{11}$/', $id) === 1;
+        // 支持12位(旧)和15位(新)格式
+        return preg_match('/^[1-9]\d{11,14}$/', $id) === 1;
     }
 
     /**