62 lines
1.9 KiB
PHP
62 lines
1.9 KiB
PHP
<?php
|
||
|
||
namespace App\Service\business;
|
||
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
/**
|
||
* 业务单号生成(清单 QD_ / 订单 DD_)
|
||
*
|
||
* 随机串剔除 0/O/1/I/L 这些肉眼分不清的字符——单号会被打印在纸质报价单上人工抄录。
|
||
* 唯一索引兜底,撞号就重试;重试仍失败宁可报错,也不要为了成功而降级成可能重复的号。
|
||
*/
|
||
class SerialNoService
|
||
{
|
||
private static mixed $_instance;
|
||
|
||
private const ALPHABET = '23456789ABCDEFGHJKMNPQRSTUVWXYZ';
|
||
|
||
public const PREFIX_LIST = 'QD_';
|
||
public const PREFIX_ORDER = 'DD_';
|
||
|
||
public static function getInstance(): null|static
|
||
{
|
||
$name = get_called_class();
|
||
if (!isset(self::$_instance[$name])) {
|
||
self::$_instance[$name] = new static();
|
||
}
|
||
return self::$_instance[$name];
|
||
}
|
||
|
||
/**
|
||
* 生成一个库内不存在的单号
|
||
*
|
||
* @param string $prefix self::PREFIX_*
|
||
* @param string $table business 连接下的表名(不带前缀)
|
||
* @param string $column 单号字段
|
||
*/
|
||
public function generate(string $prefix, string $table, string $column, int $tries = 10): string
|
||
{
|
||
$day = date('Ymd');
|
||
for ($i = 0; $i < $tries; $i++) {
|
||
$no = $prefix . $day . $this->randomPart(6);
|
||
$exists = DB::connection('business')->table($table)->where($column, $no)->exists();
|
||
if (!$exists) {
|
||
return $no;
|
||
}
|
||
}
|
||
// 连续撞 10 次说明随机源或并发量出了问题,静默返回可能重复的号比抛错危险得多
|
||
throw new \RuntimeException('单号生成失败,请重试');
|
||
}
|
||
|
||
private function randomPart(int $length): string
|
||
{
|
||
$max = strlen(self::ALPHABET) - 1;
|
||
$out = '';
|
||
for ($i = 0; $i < $length; $i++) {
|
||
$out .= self::ALPHABET[random_int(0, $max)];
|
||
}
|
||
return $out;
|
||
}
|
||
}
|