一个电子商务解决方案或付费网络服务的良好订单ID方案是什么?

7
考虑以下几点:
a) 您需要一些保密性(例如不想让所有人知道您收到了多少订单)。
b) 您需要一个检查位数字(例如使用Verhoeff算法)以便在扫描条形码时轻松检测拼写错误并帮助处理错误。
c) 您需要考虑时间,以便消费者可以按顺序排序订单。
d) 应该是全数字还是十六进制等等?
e) 您需要为支持团队提供足够的信息,以便他们能够识别订单,而无需询问电子邮件等信息,因为涉及安全问题。
我想听听大家的意见。
PS:任何为解决此问题而设计的算法也将被视为对我有效的答案。

1
"一个旨在解决这个问题的 PHP 或 MySQL 代码片段将被视为我所接受的有效答案" - 这是说“请发送代码”的另一种方式吗? - Mitch Wheat
不,但如果有人能够以优美的方式解决这个问题,我会很高兴。我已经编辑了文本,以避免其他人对我的意图产生误解。 - Henrique Vicente
4个回答

5

这是我的解决方案。

有一个三部分的x-y-z,其中x是时间戳,y是随机代码,z是由x和y连接产生的检查数字。但为了简化(使其更小),x和y以自定义基数给出,而不是数字基数10,但z仍以基数10给出。

以下是您可以使用此方法获取的ID示例:

  • LP9NTX-8D41-QW6R-9
  • LP9NTY-5H3L-BFS7-5
  • LP9NTZ-RWL3-D619-8
  • LP9NVB-BW74-788W-6
  • LP9NVW-G17D-4911-8

因此,您可以按时间戳排序(如果您不知道数字基数的确切含义,请注意它以“递增字母数字”顺序进行)。

为此,我使用了base58的数字和大写字母(最后使用小写或大写都没关系),这是没有一些混淆字符的base62。Flickr、bit.ly等网站使用base58制作Twitter“友好”链接等。

下面的Verhoeff::calcsum是Dahnielson的Verhoeff的Dihedral Group D5 Check。我所做的唯一编辑就是将他的代码放在一个类里面,所以它完全相同。

这里有一些代码:(从上面的行中略微修改)

<?php
    $time_divisor = 3;
    $base = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";//consider using another base **see note below**
    $lower_limit = 50000;//just to avoiding to confuse the user with a lower number
    $upper_limit = 1291467968;//1291467968 == ZZZZZZ in this base I used
    //you can check the limit with base_decode("ZZZZZZ", $base);
    $ptime = (int)($_SERVER['REQUEST_TIME']/$time_divisor);//or time();
    $rand1 = mt_rand($lower_limit, $upper_limi);
    $rand2 = mt_rand($lower_limit, $upper_limi);
    $ptime_b = base_encode($time, $base);
    $rand1_b = base_encode($rand1, $base);
    $rand2_b = base_encode($rand2, $base);

    $order_id = $ptime_b.$rand1_b.$rand2_b.Verhoeff::calcsum($time.$rand1.$rand2);
    echo $order_id;
?>

我刚写完这篇文章,又想到了可能出错的另一种情况。我记得你不希望消费者感到被侮辱。所以即使像'f?ck'或'4ss'这样的脏话最终出现可能是可以接受的(而且几乎肯定会出现),但明确的脏话(例如将前一个单词中的'4'换成'a')绝对不行。因此,我建议你使用以下备用的基础/上限:

<?php
    $lower_limit = 27000;//=2111
    $upper_limit = 809999;//=ZZZZ
    $base = "123456789BCDFGHJKLMNPQRSTVWXYZ";//erased -a -e -u
?>

请注意,如果您尝试使用更大的数字,您将达到PHP的上限和mt_rand限制,可以通过mt_getrandmax()查看。此外,我想说的是,就我所见,mt_rand的熵已足够。
如果您需要更大的随机数,我建议只需添加第三部分,例如mt_rand(i, j);其中i和j是您的基数的最小和最大值,这将增加您的订单号长度$num-chars(实际上我已经做到了,使用上述配置)。
在数据库方面,它是一个唯一字段,以避免冲突。
谢谢大家。

1

你觉得随机字符串的长度怎么样?使用那些不容易与其他字符混淆的字符,以便在电话中阅读时更加清晰。因此,在每个订单的通话中,可以这样说:

    public static string GetRandomString(int length)
    {
        char[] chars = "ACDEFGHJKMNPQRTWXY34679".ToCharArray();
        var crypto = new RNGCryptoServiceProvider();
        var data = new byte[length];
        crypto.GetNonZeroBytes(data);
        var result = new StringBuilder(length);
        for (int i = 0; i < data.Length; i++)
        {
            result.Append(chars[data[i] % chars.Length]);
        }

        return result.ToString();
    }

从上述方法返回的8个字符中猜测的概率是1/282429536481。而且你将通过唯一约束在数据库中保持完整性,对吧?


“对订单进行排序的问题怎么样?” - Kevin

1

java.util.UUID.randomUUID()


0
你可以使用一个GUID。为了将其缩小为客户友好的字符串,值得通过Base32转换器运行该值,这将导致包含26个A-Z和数字2-7的字符串。
为了满足您的其他标准,您可以在guid值后附加一个校验位,并且您可能需要在数据库中拥有一个单独的真正增量订单号来保证唯一性并允许自然索引/排序。

你只需要GUID的前几个字符就能够缩小实际条目的范围,就像Git只需要前4-5个字符来唯一标识提交一样。你只需要向客户询问更多字符,直到与他们的名称匹配。 - user177800

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接