为特定公司的发票生成唯一序列号Php-Laravel
我正在构建一个Laravel应用程序来生成销售发票。 来自多个公司的多个人一次生成发票。 我有一个发票表格作为发票表格,其中id是主键,编号是唯一的为特定公司的发票生成唯一序列号Php-Laravel,php,mysql,laravel,laravel-6,Php,Mysql,Laravel,Laravel 6,我正在构建一个Laravel应用程序来生成销售发票。 来自多个公司的多个人一次生成发票。 我有一个发票表格作为发票表格,其中id是主键,编号是唯一的 id | company_id | series | number | amount 现在要生成发票号,我要做的是;对于一家特定的公司,计算并加上1 //assume $inv->prefix is a string which gives me series for particular company. $series = Invoi
id | company_id | series | number | amount
现在要生成发票号,我要做的是;对于一家特定的公司,计算并加上1
//assume $inv->prefix is a string which gives me series for particular company.
$series = Invoice::where('series', $inv->prefix)->max('number');
if(isset($series) && strlen($series) > 0){
$series += 1;
} else {
$series = 1;
}
$inv->number = $series;
现在的问题是,当两个用户试图同时为同一系列生成发票时,由于我的表中的数字列是唯一的,所以会出现重复错误
我能做点像这样的事吗
do{
$series = Invoice::where('series', $inv->prefix)->max('number');
if(isset($series) && strlen($series) > 0){
$series += 1;
} else {
$series = 1;
}
$inv->number = $series;
} while($inv->save())
有人能帮我吗。
如果出现重复条目异常,代码应再次显示计数,并尝试再次保存记录。我建议不要只是增加新InvoiceId的编号,而是为该项设置一个模式- 创建一个实用程序类-
class NumberUtility
{
/**
* This method generates the random number
*
* @param int $length
*
* @return int
*/
public static function getRandomNumber($length = 8): int
{
$intMin = (10 ** $length) / 10;
$intMax = (10 ** $length) - 1;
try {
$randomNumber = random_int($intMin, $intMax);
} catch (\Exception $exception) {
\Log::error('Failed to generate random number Retrying...');
\Log::debug(' Error: '.$exception->getMessage());
$randomNumber = self::getRandomNumber($length);
}
return $randomNumber;
}
}
现在创建一个获取唯一发票号的方法,如下所示-
public function getUniqueInvoiceNumber($companyId)
{
$randomNumber = NumberUtility::getRandomNumber(4);
$invoiceNumber = (string)$companyId.$randomNumber;
$exist = Invoice::where('number', $invoiceNumber)->first();
if ($exist) {
$invoiceNumber = $this->getUniqueInvoiceNumber($companyId);
}
return $invoiceNumber;
}
你可以用它来处理你的发票。有重复记录的机会非常低;尽管出于设计考虑,我还是选择了表锁定(参见完整答案)
来自多个公司的多个人一次生成发票 在这种情况下,随机化数据或增加数据并不是可行的方法。如果您是基于自定义模式生成发票,该模式将数据库中的数据用作变量,则生成重复发票的可能性很高。您应该为当前事务锁定正在使用的表。这将防止插入任何重复的数据。我不知道你的整个数据库结构,尽管我提供的是一个演示:
LOCK TABLES working_table WRITE, working_table2 WRITE;
INSERT INTO working_table ( ... )
VALUES ( ...,
(SELECT number
FROM working_table2
WHERE series = 'prefix'
ORDERBY number DESC
LIMIT 1)+1
);
UNLOCK TABLES;
然后可以像这样调用sql语句:
DB::select("
LOCK TABLES working_table WRITE, working_table2 WRITE;
INSERT INTO working_table ( column )
VALUES (
(SELECT number
FROM working_table2
WHERE series = ?
ORDERBY number DESC
LIMIT 1)+1
);
UNLOCK TABLES;
", [ 'param1' ]) // these are the parameters to prepare
注意:如果使用持久数据库连接,则必须处理Laravel应用程序引发的所有错误,因为未经处理的错误将停止脚本,而不会停止保持与数据库连接的子进程。取消对子进程的处理将保持表锁定!因为您的数据库正在为连接会话锁定它们。有关此行为的详细信息,请阅读。您可以使用PHP的uniqid()函数:
$series = uniqid(); //current time in microseconds
你最好使用全局锁定机制。例如,在伪代码
中,当资源(我猜是公司)存在锁时,睡眠时间为100ms;然后再次检查锁==>当锁不存在时,创建它==>执行原子操作(增量)==>释放锁
这样,一次只有一个客户端将为给定前缀递增序列,同时尝试将保持一段时间,然后才能递增。然后使用表锁定。然后用序列更改随机数。它会解决你的问题。
$series = uniqid(); //current time in microseconds