| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- <?php
- namespace App\Services;
- /**
- * 试卷ID生成器
- * 参考行业标准 Snowflake ID 思想
- */
- class PaperIdGenerator
- {
- // 基准时间:2020-01-01 00:00:00 (秒)
- private const EPOCH = 1577836800;
- // 12位数字的位分配(总计约2.8万亿个容量,足够使用200年)
- // 格式:时间戳(8位) + 序列号(2位) + 随机数(2位) = 12位数字
- private const TIMESTAMP_BITS = 35; // 时间戳(秒)- 可覆盖约34年
- private const SEQUENCE_BITS = 11; // 序列号 - 2048个值
- private const RANDOM_BITS = 11; // 随机数 - 2048个值
- /**
- * 生成12位数字ID
- * 格式:TTTTTsssssR(时间戳+序列号+随机数)
- *
- * @return string 12位数字字符串
- */
- public static function generate(): string
- {
- // 使用时间戳(分钟)而不是秒,以减少位数
- $timestamp = intdiv(time() - self::EPOCH, 60); // 从基准时间开始的分钟数
- $timestamp &= (1 << self::TIMESTAMP_BITS) - 1; // 截取指定位数
- // 同一分钟内使用序列号递增,避免并发重复
- static $lastTimestamp = 0;
- static $sequence = 0;
- if ($timestamp == $lastTimestamp) {
- $sequence = ($sequence + 1) & ((1 << self::SEQUENCE_BITS) - 1);
- // 序列号用完,等待下一分钟
- if ($sequence == 0) {
- do {
- $timestamp = intdiv(time() - self::EPOCH, 60);
- $timestamp &= (1 << self::TIMESTAMP_BITS) - 1;
- } while ($timestamp <= $lastTimestamp);
- }
- } else {
- $sequence = 0;
- }
- $lastTimestamp = $timestamp;
- // 添加随机数后缀避免猜测
- $random = random_int(0, 99); // 2位随机数:00-99
- // 组合:时间戳(8位) + 序列号(2位) + 随机数(2位) = 12位数字
- // 时间戳占8位(不够前面补0)
- $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);
- $id = $timestampStr . $sequenceStr . $randomStr;
- // 确保第一位不为0
- if ($id[0] === '0') {
- $id[0] = '1';
- }
- return $id;
- }
- /**
- * 验证12位数字ID格式
- *
- * @param string $id
- * @return bool
- */
- public static function validate(string $id): bool
- {
- return preg_match('/^[1-9]\d{11}$/', $id) === 1;
- }
- /**
- * 从ID提取时间戳
- *
- * @param string $id
- * @return int|null
- */
- public static function extractTimestamp(string $id): ?int
- {
- if (!self::validate($id)) {
- return null;
- }
- // 提取前8位时间戳(分钟),转换为秒
- $timestampMinutes = (int)substr($id, 0, 8);
- return $timestampMinutes * 60 + self::EPOCH;
- }
- /**
- * 批量生成不重复的ID
- *
- * @param int $count
- * @return array
- */
- public static function generateBatch(int $count): array
- {
- $ids = [];
- $attempts = 0;
- $maxAttempts = $count * 10;
- while (count($ids) < $count && $attempts < $maxAttempts) {
- $id = self::generate();
- if (!in_array($id, $ids)) {
- $ids[] = $id;
- }
- $attempts++;
- }
- return $ids;
- }
- }
|