Files

62 lines
1.9 KiB
PHP
Raw Permalink Normal View History

<?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;
}
}