Detail.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. namespace app\api\model;
  3. use think\Model;
  4. use traits\model\SoftDelete;
  5. class Detail extends Model
  6. {
  7. use SoftDelete;
  8. // 表名
  9. protected $name = 'box_detail';
  10. // 自动写入时间戳字段
  11. protected $autoWriteTimestamp = 'int';
  12. // 定义时间戳字段名
  13. protected $createTime = 'create_time';
  14. protected $updateTime = 'update_time';
  15. protected $deleteTime = 'delete_time';
  16. /**
  17. * 抽取一个
  18. * @param int $box_id 盲盒
  19. * @param array $except 排除的商品ID
  20. * @return mixed
  21. * @throws \Exception
  22. * @author fuyelk <fuyelk@fuyelk.com>
  23. */
  24. public static function getOne(int $box_id, array $except = [])
  25. {
  26. // 查询该盲盒中的全部商品ID
  27. $goodsIds = self::where('box_id', $box_id)->column('goods_id');
  28. // 找出这些商品中下架、缺货或已删除的商品
  29. $removeGoodsIds = Goods::withTrashed()->whereIn('id', $goodsIds)
  30. ->where(function ($query) use ($except) {
  31. $query->where('status', 'offline')->whereOr('stock', '<', 1)->whereOr('delete_time', 'not null')->whereOr('id', 'in', implode(',', $except));
  32. })->column('id');
  33. // 移除无效的商品,得到有效的商品ID
  34. $usefulGoodsIds = array_diff($goodsIds, $removeGoodsIds);
  35. if (empty($usefulGoodsIds)) {
  36. throw new \Exception('奖品不足');
  37. }
  38. // 查询有效商品的概率信息
  39. $prize = self::where('box_id', $box_id)->whereIn('goods_id', $usefulGoodsIds)->column('rate,goods_id', 'id');
  40. // Array ( [0] => 89.00 [1] => 89.00 [2] => 89.00 [3] => 89.00 [4] => 89.00 [5] => 89.00 )
  41. // 概率集合
  42. $prizeRate = array_column($prize, 'rate');
  43. // 商品ID集合
  44. $goodsList = array_column($prize, 'goods_id');
  45. return self::rand($prizeRate, $goodsList);
  46. }
  47. public static function getOnes(int $box_id, array $except = [])
  48. {
  49. // 查询该盲盒中的全部商品ID
  50. $goodsIds = self::where('box_id', $box_id)->column('goods_id');
  51. // 找出这些商品中下架、缺货或已删除的商品
  52. $removeGoodsIds = Goods::withTrashed()->whereIn('id', $goodsIds)
  53. ->where(function ($query) use ($except) {
  54. $query->where('status', 'offline')->whereOr('stock', '<', 1)->whereOr('delete_time', 'not null')->whereOr('id', 'in', implode(',', $except));
  55. })->column('id');
  56. // 移除无效的商品,得到有效的商品ID
  57. $usefulGoodsIds = array_diff($goodsIds, $removeGoodsIds);
  58. if (empty($usefulGoodsIds)) {
  59. throw new \Exception('奖品不足');
  60. }
  61. // 查询有效商品的概率信息
  62. $prize = self::where('box_id', $box_id)->whereIn('goods_id', $usefulGoodsIds)->column('rate,goods_id', 'id');
  63. // Array ( [0] => 89.00 [1] => 89.00 [2] => 89.00 [3] => 89.00 [4] => 89.00 [5] => 89.00 )
  64. // 概率集合
  65. $prizeRate = array_column($prize, 'rate');
  66. for ($i = 0; $i < count($prizeRate); ++ $i) {
  67. $prizeRate[$i] = 1;
  68. }
  69. // 商品ID集合
  70. $goodsList = array_column($prize, 'goods_id');
  71. return self::rand($prizeRate, $goodsList);
  72. }
  73. /**
  74. * 抽取多个
  75. * @param int $box_id 盲盒
  76. * @param int $box_id
  77. * @param int $num
  78. * @return array
  79. * @throws \Exception
  80. * @author fuyelk <fuyelk@fuyelk.com>
  81. */
  82. public static function getMore(int $box_id, int $num)
  83. {
  84. $goodsNum = [];
  85. $goodsIds = [];
  86. $except = [];
  87. while (--$num >= 0) {
  88. // 抽取商品
  89. $goods_id = self::getOne($box_id);
  90. // 第一次抽到,初始计数
  91. if (!isset($goodsNum[$goods_id])) {
  92. $goodsNum[$goods_id] = 0;
  93. }
  94. // 计一次数
  95. ++$goodsNum[$goods_id];
  96. $need = $goodsNum[$goods_id];
  97. $getNew = false; // 标记是否重新抽
  98. // 检查库存
  99. while (!self::checkStock($goods_id, $need)) {
  100. // 库存不够,则排除该商品,重新抽取
  101. $except[] = $goods_id;
  102. $goods_id = self::getOne($box_id, $except);
  103. $getNew = true;
  104. // 计算需要的数量
  105. $need = isset($goodsNum[$goods_id]) ? $goodsNum[$goods_id] + 1 : 1;
  106. }
  107. // 重新抽了,则计数
  108. if ($getNew) {
  109. if (!isset($goodsNum[$goods_id])) {
  110. $goodsNum[$goods_id] = 0;
  111. }
  112. $goodsNum[$goods_id]++;
  113. }
  114. $goodsIds[] = $goods_id;
  115. }
  116. return $goodsIds;
  117. }
  118. public static function getMores(int $box_id, int $num)
  119. {
  120. $goodsNum = [];
  121. $goodsIds = [];
  122. $except = [];
  123. while (--$num >= 0) {
  124. // 抽取商品
  125. $goods_id = self::getOnes($box_id);
  126. // 第一次抽到,初始计数
  127. if (!isset($goodsNum[$goods_id])) {
  128. $goodsNum[$goods_id] = 0;
  129. }
  130. // 计一次数
  131. ++$goodsNum[$goods_id];
  132. $need = $goodsNum[$goods_id];
  133. $getNew = false; // 标记是否重新抽
  134. // 检查库存
  135. while (!self::checkStock($goods_id, $need)) {
  136. // 库存不够,则排除该商品,重新抽取
  137. $except[] = $goods_id;
  138. $goods_id = self::getOnes($box_id, $except);
  139. $getNew = true;
  140. // 计算需要的数量
  141. $need = isset($goodsNum[$goods_id]) ? $goodsNum[$goods_id] + 1 : 1;
  142. }
  143. // 重新抽了,则计数
  144. if ($getNew) {
  145. if (!isset($goodsNum[$goods_id])) {
  146. $goodsNum[$goods_id] = 0;
  147. }
  148. $goodsNum[$goods_id]++;
  149. }
  150. $goodsIds[] = $goods_id;
  151. }
  152. return $goodsIds;
  153. }
  154. /**
  155. * 随机
  156. * @param array $rate 中奖概率集合:
  157. * <pre>
  158. * $rate = [
  159. * 0 => 10, // 第二个奖品概率10%
  160. * 1 => 5.88, // 第二个奖品概率5.88%
  161. * 1 => 35.60, // 第二个奖品概率35.6%
  162. * ];
  163. * </pre>
  164. * @param array $goods 奖品集合,顺序与rate字段一致:
  165. * $rate = [
  166. * 0 => '第一个奖品',
  167. * 1 => '第二个奖品',
  168. * 2 => '第三奖品',
  169. * ];
  170. * @return mixed
  171. * @author fuyelk <fuyelk@fuyelk.com>
  172. * @date 2021/07/10 21:08
  173. */
  174. private static function rand($rate = [], $goods = [])
  175. {
  176. // 将数据按概率降序排序
  177. array_multisort($rate, SORT_DESC, $goods);
  178. foreach ($rate as &$item) {
  179. $item = round($item, 2) * 100; // 扩大100倍避免小数
  180. }
  181. //奖项的设置和概率可以手动设置化;
  182. $total = array_sum($rate);
  183. foreach ($rate as $key => $value) {
  184. $randNumber = mt_rand(1, $total);
  185. if ($randNumber <= $value) {
  186. $notice = $goods[$key];
  187. break;
  188. } else {
  189. $total -= $value;
  190. }
  191. }
  192. return $notice;
  193. }
  194. /**
  195. * 检查库存是否低于需要的数量
  196. * @param int $goods_id 商品ID
  197. * @param int $need 需要的数量
  198. * @return bool
  199. */
  200. private static function checkStock($goods_id, $need)
  201. {
  202. // 需要1个,则不检查库存(因为奖品池要求至少有1个)
  203. if (1 == $need) return true;
  204. $stock = Goods::where('id', $goods_id)->value('stock');
  205. if (empty($stock) || $stock < $need) {
  206. return false;
  207. }
  208. return true;
  209. }
  210. }