*/ public static function getOne(int $box_id, array $except = []) { // 查询该盲盒中的全部商品ID $goodsIds = self::where('box_id', $box_id)->column('goods_id'); // 找出这些商品中下架、缺货或已删除的商品 $removeGoodsIds = Goods::withTrashed()->whereIn('id', $goodsIds) ->where(function ($query) use ($except) { $query->where('status', 'offline')->whereOr('stock', '<', 1)->whereOr('delete_time', 'not null')->whereOr('id', 'in', implode(',', $except)); })->column('id'); // 移除无效的商品,得到有效的商品ID $usefulGoodsIds = array_diff($goodsIds, $removeGoodsIds); if (empty($usefulGoodsIds)) { throw new \Exception('奖品不足'); } // 查询有效商品的概率信息 $prize = self::where('box_id', $box_id)->whereIn('goods_id', $usefulGoodsIds)->column('rate,goods_id', 'id'); // Array ( [0] => 89.00 [1] => 89.00 [2] => 89.00 [3] => 89.00 [4] => 89.00 [5] => 89.00 ) // 概率集合 $prizeRate = array_column($prize, 'rate'); // 商品ID集合 $goodsList = array_column($prize, 'goods_id'); return self::rand($prizeRate, $goodsList); } public static function getOnes(int $box_id, array $except = []) { // 查询该盲盒中的全部商品ID $goodsIds = self::where('box_id', $box_id)->column('goods_id'); // 找出这些商品中下架、缺货或已删除的商品 $removeGoodsIds = Goods::withTrashed()->whereIn('id', $goodsIds) ->where(function ($query) use ($except) { $query->where('status', 'offline')->whereOr('stock', '<', 1)->whereOr('delete_time', 'not null')->whereOr('id', 'in', implode(',', $except)); })->column('id'); // 移除无效的商品,得到有效的商品ID $usefulGoodsIds = array_diff($goodsIds, $removeGoodsIds); if (empty($usefulGoodsIds)) { throw new \Exception('奖品不足'); } // 查询有效商品的概率信息 $prize = self::where('box_id', $box_id)->whereIn('goods_id', $usefulGoodsIds)->column('rate,goods_id', 'id'); // Array ( [0] => 89.00 [1] => 89.00 [2] => 89.00 [3] => 89.00 [4] => 89.00 [5] => 89.00 ) // 概率集合 $prizeRate = array_column($prize, 'rate'); for ($i = 0; $i < count($prizeRate); ++ $i) { $prizeRate[$i] = 1; } // 商品ID集合 $goodsList = array_column($prize, 'goods_id'); return self::rand($prizeRate, $goodsList); } /** * 抽取多个 * @param int $box_id 盲盒 * @param int $box_id * @param int $num * @return array * @throws \Exception * @author fuyelk */ public static function getMore(int $box_id, int $num) { $goodsNum = []; $goodsIds = []; $except = []; while (--$num >= 0) { // 抽取商品 $goods_id = self::getOne($box_id); // 第一次抽到,初始计数 if (!isset($goodsNum[$goods_id])) { $goodsNum[$goods_id] = 0; } // 计一次数 ++$goodsNum[$goods_id]; $need = $goodsNum[$goods_id]; $getNew = false; // 标记是否重新抽 // 检查库存 while (!self::checkStock($goods_id, $need)) { // 库存不够,则排除该商品,重新抽取 $except[] = $goods_id; $goods_id = self::getOne($box_id, $except); $getNew = true; // 计算需要的数量 $need = isset($goodsNum[$goods_id]) ? $goodsNum[$goods_id] + 1 : 1; } // 重新抽了,则计数 if ($getNew) { if (!isset($goodsNum[$goods_id])) { $goodsNum[$goods_id] = 0; } $goodsNum[$goods_id]++; } $goodsIds[] = $goods_id; } return $goodsIds; } public static function getMores(int $box_id, int $num) { $goodsNum = []; $goodsIds = []; $except = []; while (--$num >= 0) { // 抽取商品 $goods_id = self::getOnes($box_id); // 第一次抽到,初始计数 if (!isset($goodsNum[$goods_id])) { $goodsNum[$goods_id] = 0; } // 计一次数 ++$goodsNum[$goods_id]; $need = $goodsNum[$goods_id]; $getNew = false; // 标记是否重新抽 // 检查库存 while (!self::checkStock($goods_id, $need)) { // 库存不够,则排除该商品,重新抽取 $except[] = $goods_id; $goods_id = self::getOnes($box_id, $except); $getNew = true; // 计算需要的数量 $need = isset($goodsNum[$goods_id]) ? $goodsNum[$goods_id] + 1 : 1; } // 重新抽了,则计数 if ($getNew) { if (!isset($goodsNum[$goods_id])) { $goodsNum[$goods_id] = 0; } $goodsNum[$goods_id]++; } $goodsIds[] = $goods_id; } return $goodsIds; } /** * 随机 * @param array $rate 中奖概率集合: *
     * $rate = [
     *     0 => 10, // 第二个奖品概率10%
     *     1 => 5.88, // 第二个奖品概率5.88%
     *     1 => 35.60, // 第二个奖品概率35.6%
     * ];
     * 
* @param array $goods 奖品集合,顺序与rate字段一致: * $rate = [ * 0 => '第一个奖品', * 1 => '第二个奖品', * 2 => '第三奖品', * ]; * @return mixed * @author fuyelk * @date 2021/07/10 21:08 */ private static function rand($rate = [], $goods = []) { // 将数据按概率降序排序 array_multisort($rate, SORT_DESC, $goods); foreach ($rate as &$item) { $item = round($item, 2) * 100; // 扩大100倍避免小数 } //奖项的设置和概率可以手动设置化; $total = array_sum($rate); foreach ($rate as $key => $value) { $randNumber = mt_rand(1, $total); if ($randNumber <= $value) { $notice = $goods[$key]; break; } else { $total -= $value; } } return $notice; } /** * 检查库存是否低于需要的数量 * @param int $goods_id 商品ID * @param int $need 需要的数量 * @return bool */ private static function checkStock($goods_id, $need) { // 需要1个,则不检查库存(因为奖品池要求至少有1个) if (1 == $need) return true; $stock = Goods::where('id', $goods_id)->value('stock'); if (empty($stock) || $stock < $need) { return false; } return true; } }