如何使PHP代码代码分配更可靠?

时间:2019-06-28 20:13:10

标签: php ticket-system

我正在创建票务系统。我希望每个条目在指定范围内都是唯一的数字。我已经创建了函数,其中将包括这些函数以演示如何处理事物。如果我一次性购买所有门票,该功能通常会有3或4个重复号码。

我尝试过此操作,但如果有人一次购买所有门票,最终失败,并得到一些重复。我认为,如果一次也购买几个,可能会发生这种情况。这需要更强大。

已购买票证,该票证返回已获取的一组数字

function takenNumbers($drawID){
    $connect = new mysqli("localhost", "root", "", "dream");
    $stmt = $connect->prepare("SELECT * FROM transaction WHERE DrawID = ?");
    $stmt->bind_param("i", $drawID);
    $stmt->execute();
    $result = $stmt->get_result();

    $tickets = array();
    while($row = $result->fetch_assoc()){
        $id = $row['ID'];
        $tickets[] = $row['TicketNumber'];
    }

    return $tickets;
}

未获取的数字也会在数组中返回

function freeNumbers($drawID){
    $minTickets = 1;
    $maxTickets = totalTickets($drawID);
    $takenNumbers = takenNumbers($drawID);
    $freeNumbers = array();
    $allTickets = range(1, $maxTickets);
    $freeNumbers = array_values(array_diff($allTickets, $takenNumbers));

    return $freeNumbers;  
}

然后我有一个基于这些功能的随机票证生成器

function randomTicket($drawID){
    $num = freeNumbers($drawID);
    $random = array_rand($num, 1);
    return $random;
}

付款处理后,我调用此函数与randomTicket一起插入数据库。

for($i = 0; $i < $quantity; $i++){
    echo paymentMade($paymentId, $token, $payerID, $drawID) . "<br>";
}

我收到重复的邮件。这在实时应用程序中不会发生。我尝试了很多事情,发现这是一个很容易编程的问题。任何和所有输入将不胜感激。预先感谢大家。

1 个答案:

答案 0 :(得分:1)

您的数据库是这里的真理源,它是所有可能的票证编号和已分配的票证的存储库。

当您尝试在Web应用程序中执行此操作时,在有多个并发用户的情况下,总是存在冲突的可能性,而当您的应用程序分布在多个服务器上时,则更可能发生冲突。

也就是说,最好找到一种数据库解决方案来发行这些票证。

首先要实现的是在交易表上记录票证号码的UNIQUE INDEX,然后数据库将始终保持号码的完整性:

  

您尚未提供完整的架构,因此我假设您的transaction表同时具有DrawIDTicketNumber

CREATE UNIQUE INDEX UX_DrawTicketNumbers
ON transaction (DrawID,TicketNo);

现在,当您插入重复的票证编号时,数据库将使操作失败。一个基本的开始方法是简单地处理故障,并使用当前逻辑继续尝试下一个数字,直到它起作用为止。

您可以使用CTE直接从数据库中查询下一个票证号:

  MySQL自版本8(于2016年发布)开始支持

WITH,如果您使用的是旧版本,则可以在混凝土表或临时表中填充所有可能的票证编号,以代替此{{ 1}}子句

WITH
  

默认情况下,MYSQL将递归限制为1000,因此您可能需要将当前会话递归限制设置为大于或等于 SET @DrawID := 1; -- set your drawId here SELECT TotalTickets into @MaxTicketNo FROM Draw WHERE ID = @DrawID; WITH RECURSIVE TicketNumbers (n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM TicketNumbers WHERE n < @MaxTicketNo ) SELECT n FROM TicketNumbers WHERE n NOT IN (SELECT TicketNo FROM transaction WHERE DrawID = @DrawID) ORDER BY RAND() LIMIT 1;

的数字。
totalTickets

现在,您可以利用数据库的原子性来获取上述查询的结果,并直接在您的插入语句中使用它,在使用CTE的SQL Server中,我将为事务更新做类似的事情:(我可以使其无法在DB Fiddle中使用,但该概念应该可以使用)

SET SESSION cte_max_recursion_depth = 1000000;

tl; dr;

为确保原子性,请找到一种方法将此过程移至数据库,创建存储过程以封装分配票证号并将其与付款确认过程合并的过程。