123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- <?php
- namespace app\api\model;
- use think\Model;
- use traits\model\SoftDelete;
- class Detail extends Model
- {
- use SoftDelete;
- // 表名
- protected $name = 'box_detail';
- // 自动写入时间戳字段
- protected $autoWriteTimestamp = 'int';
- // 定义时间戳字段名
- protected $createTime = 'create_time';
- protected $updateTime = 'update_time';
- protected $deleteTime = 'delete_time';
- /**
- * 抽取一个
- * @param int $box_id 盲盒
- * @param array $except 排除的商品ID
- * @return mixed
- * @throws \Exception
- * @author fuyelk <fuyelk@fuyelk.com>
- */
- 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 <fuyelk@fuyelk.com>
- */
- 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 中奖概率集合:
- * <pre>
- * $rate = [
- * 0 => 10, // 第二个奖品概率10%
- * 1 => 5.88, // 第二个奖品概率5.88%
- * 1 => 35.60, // 第二个奖品概率35.6%
- * ];
- * </pre>
- * @param array $goods 奖品集合,顺序与rate字段一致:
- * $rate = [
- * 0 => '第一个奖品',
- * 1 => '第二个奖品',
- * 2 => '第三奖品',
- * ];
- * @return mixed
- * @author fuyelk <fuyelk@fuyelk.com>
- * @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;
- }
- }
|