目录
[1. 使用 ThinkPHP Helper 助手函数(推荐)](#1. 使用 ThinkPHP Helper 助手函数(推荐))
[2. 使用 FastAdmin 自带随机函数(推荐)](#2. 使用 FastAdmin 自带随机函数(推荐))
[3. 自定义随机数生成函数](#3. 自定义随机数生成函数)
[1. 简单递归生成(适合小批量)](#1. 简单递归生成(适合小批量))
[2. 批量生成唯一防伪码(MySQL查重)](#2. 批量生成唯一防伪码(MySQL查重))
[六、基于 Redis 的唯一防伪码生成](#六、基于 Redis 的唯一防伪码生成)
[七、批量写入 insertAll() 使用规范](#七、批量写入 insertAll() 使用规范)
一、项目背景与防伪码要求
在防伪溯源项目中, 第一步就是创建防伪码。防伪码的设计需满足以下要求:
- 全局唯一性
- 16位纯数字字符串,随机生成
- 支持高效批量写入
二、核心实现思路
- 16位纯数字防伪码 由于PHP的int类型无法承载16位数字,直接生成字符串格式的16位数字,避免溢出丢失精度。
- 唯一性保障 采用"预生成 + 查重 + 批量插入"方案,结合MySQL唯一索引做最终兜底,双重防止重复。
- 高效批量插入 整理符合模型要求的二维数组,使用ThinkPHP模型的insertAll()高效插入,兼顾性能和框架规范。
三、数据库表设计(唯一性兜底)
确保防伪码字段为字符串类型,并添加唯一索引,防止重复:
php
CREATE TABLE `fa_secure_code` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`batch_no` varchar(32) DEFAULT '' COMMENT '批次',
`secure_code` varchar(32) DEFAULT '' COMMENT '防伪码',
`goods_id` int(11) DEFAULT NULL COMMENT '商品ID',
`status` tinyint(1) DEFAULT '1' COMMENT '状态 1正常 0 禁用',
`create_date` datetime DEFAULT NULL COMMENT '创建时间',
`update_date` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_secure_code` (`secure_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='防伪码';
四、16位数字防伪码生成方法
1. 使用 ThinkPHP Helper 助手函数(推荐)
php
// 安装依赖
composer require topthink/think-helper
// 生成随机字符串
$code = Str::random(16, 1);
2. 使用 FastAdmin 自带随机函数(推荐)
php
$code = Random::numeric(16);
3. 自定义随机数生成函数
php
function random_number(int $length = 16)
{
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= random_int(0, 9);
}
return $str;
}
五、防伪码批量生成与写入方案
1. 简单递归生成(适合小批量)
php
public static function createSecureCode(): string
{
$str = Str::random(16, 1);
$find = SecureCode::where('secure_code', $str)->find();
if ($find) {
return self::createSecureCode();
}
return $str;
}
public static function batchAdd($count, $goodsId)
{
$batch = date('YmdHis');
for ($i = 0; $i < $count; ++$i) {
transaction(function () use ($batch, $goodsId) {
$data = [
'batch_no' => $batch,
'goods_id' => $goodsId,
'status' => 1,
'secure_code' => SecureCode::createSecureCode(),
];
$secure = SecureCode::create($data);
});
}
}
2. 批量生成唯一防伪码(MySQL查重)
核心逻辑:
- 批量生成16位数字字符串
- 查重去重
- 整理数据格式
- insertAll()批量插入,处理唯一索引冲突
php
public static function batchAddV2($count, $goodsId)
{
// 初步生成
$code_list = SecureCode::getCodeList($count);
// 过滤重复的code
$ux_code_list = SecureCode::filterCodes($code_list);
while (\count($ux_code_list) < $count) {
$need_count = $count - \count($ux_code_list);
$_code_list = SecureCode::getCodeList($need_count);
$_ux_code_list = SecureCode::filterCodes($_code_list);
$ux_code_list = array_merge($ux_code_list, $_ux_code_list);
$ux_code_list = array_unique($ux_code_list);
}
$ux_code_list = \array_slice($ux_code_list, 0, $count);
}
public static function getCodeList(int $count, array $list = [])
{
$origin_count = \count($list);
$target_count = $count - $origin_count;
for ($i = 0; $i < $target_count; ++$i) {
$list[] = Str::random(16, 1);
}
$list = array_unique($list);
if (\count($list) < $count) {
return self::getCodeList($count, $list);
}
return \array_slice($list, 0, $count);
}
/**
* 过滤重复的code
*/
public static function filterCodes($code_list)
{
$codes = self::whereIn('bar_code', $code_list)->column('bar_code');
return array_filter($code_list, function ($code) use ($codes) {
return !\in_array($code, $codes);
});
}
六、基于 Redis 的唯一防伪码生成
核心优势:
- Redis Set 天然去重:SADD命令自动忽略重复,无需手动查重。
- 内存级高效操作:所有操作均在内存中,效率远高于MySQL的WHERE IN查询。
- 原子性保障:单线程模型,避免高并发下的重复。
- 双重兜底安全:Redis Set去重 + MySQL唯一索引兜底。
实现示例:
php
public static function batchAddV3($count, $goodsId)
{
$key = 'secure_code_list_set';
// 1. 数据一致性同步(兜底,避免Redis数据丢失)
$current_count = redis_utils()->sCard($key);
if (!$current_count) {
SecureCode::field('id,secure_code')
->chunk(1000, function ($list) use ($key) {
$dbCodes = array_column($list, 'secure_code');
redis_utils()->sAdd($key, ...$dbCodes);
});
}
$code_list = SecureCode::getCodeList($count);
$ux_code_list = SecureCode::redisFilterCodes($key, $code_list);
while (\count($ux_code_list) < $count) {
$need_count = $count - \count($ux_code_list);
$_code_list = SecureCode::getCodeList($need_count);
$_ux_code_list = SecureCode::redisFilterCodes($key, $_code_list);
$ux_code_list = array_merge($ux_code_list, $_ux_code_list);
$ux_code_list = array_unique($ux_code_list);
}
}
public static function redisFilterCodes($key, $code_list)
{
foreach ($code_list as $index => $code) {
if (redis_utils()->sIsMember($key, $code)) {
unset($code_list[$index]);
}
}
return array_unique($code_list);
}
七、批量写入 insertAll() 使用规范
使用ThinkPHP模型的insertAll()进行批量插入:
php
$data = [];
$batch = date('YmdHis');
for ($i = 0; $i < $count; ++$i) {
$data[] = [
'batch_no' => $batch,
'goods_id' => $goodsId,
'status' => 1,
'secure_code' => $ux_code_list[$i],
'create_date' => date('Y-m-d H:i:s'),
'update_date' => date('Y-m-d H:i:s'),
];
}
SecureCode::insertAll($data);
总结
- 防伪码生成需保证唯一性 与高效性,推荐使用字符串存储和唯一索引兜底。
- 批量生成时可结合MySQL查重或Redis Set天然去重,提升性能。
- 最终插入建议使用insertAll(),大幅提升批量操作效率。
如需进一步探讨,欢迎交流!