LotteryLogic.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <?php
  2. namespace addons\Sudoku\common\logic;
  3. use addons\Coupon\common\enums\AddonsCouponEnum;
  4. use addons\Coupon\common\models\AddonsCouponCatRelate;
  5. use addons\Coupon\common\models\AddonsCouponUserRelate;
  6. use addons\Sudoku\common\enums\SudokuEnum;
  7. use addons\Sudoku\common\models\AddonsSudoku;
  8. use addons\Sudoku\common\models\AddonsSudokuLog;
  9. use common\enums\AddonsEnum;
  10. use common\enums\OrderStatusEnum;
  11. use common\enums\RedisKeyEnum;
  12. use common\enums\UserWalletEnum;
  13. use common\helpers\LockHelper;
  14. use common\helpers\StringHelper;
  15. use common\models\goods\Goods;
  16. use common\models\order\Order;
  17. use common\models\order\OrderDetail;
  18. use common\models\user\UserAddress;
  19. use common\models\user\UserWallet;
  20. use common\traits\ErrorTrait;
  21. use Faker\Provider\Address;
  22. use PHPUnit\Util\Exception;
  23. use services\common\UserWalletService;
  24. use yii;
  25. /**
  26. * Class LotteryLogic
  27. * @Author: ltm
  28. * @DateTime: 2021/12/23 0023 10:21
  29. * @Copyright: copyright (c) 2021 广东七件事集团
  30. * @package addons\Sudoku\common\logic
  31. */
  32. class LotteryLogic
  33. {
  34. use ErrorTrait;
  35. /**
  36. * @Author: ltm
  37. * @DateTime: 2021/12/23 0023 10:21
  38. * @Copyright: copyright (c) 2021 广东七件事集团
  39. * @param $data['score'] 积分
  40. * @param $data['user_id'] 会员ID
  41. * @return bool
  42. */
  43. public function execute($data)
  44. {
  45. $config = Yii::$app->services->setting->addons->get($data['mall_id'], SudokuEnum::ADDONS_NAME, 'basic');
  46. //统计会员剩余次数,和积分是否足
  47. $user_wallet_model = UserWallet::findOne(['user_id' => $data['user_id']]);
  48. $data['score'] = $user_wallet_model['score'];
  49. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_SUDOKU , $data['user_id']);
  50. $identification = uniqid();
  51. try {
  52. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new Exception('当前访问人数过多');
  53. //检验活动时间
  54. if(strtotime($config['start_at']) > time()) throw new Exception("活动未开始");
  55. if(strtotime($config['end_at']) < time()) throw new Exception("活动已结束");
  56. $this->checkUser($config,$data);
  57. //检验奖品是否都有效
  58. $prizeList = $this->checkprize($data);
  59. //计算中奖
  60. $prize_id = $this->computeWin($prizeList);
  61. //添加抽奖纪录
  62. $res = $this->savePrizeLog($prize_id,$data,$prizeList,$config);
  63. LockHelper::ulock($lock_key,$identification);
  64. return $res;
  65. } catch (Exception $e) {
  66. LockHelper::ulock($lock_key,$identification);
  67. $this->setError($e->getMessage());
  68. return false;
  69. }
  70. }
  71. //抽奖纪录
  72. public function savePrizeLog($prize_id,$data,$prizeList,$config){
  73. $trans = Yii::$app->db->beginTransaction();
  74. try {
  75. $prize_data = null;// 中奖的奖品
  76. $return['msg'] = '';
  77. $think_data = [];// 谢谢参与奖品初始化
  78. foreach ($prizeList as $k => $v) {
  79. if($v['id'] == $prize_id) {
  80. $prize_data = $v;
  81. }
  82. if ($v['type'] == 5) {// 获取一个谢谢参与奖品
  83. $think_data = $v;
  84. }
  85. }
  86. // 如果获取ID是系统补全的话,则直接返回谢谢参与
  87. if ($prize_id == 0 && empty($prize_data)) {
  88. $prize_data = $think_data ?? ['type' => 5, 'id' => 0];
  89. }
  90. // 当次奖品属于无效奖品时,则会把谢谢参与奖品给替换上去
  91. if (!empty($prize_data['is_valid'])) {
  92. $prize_data = $think_data ? $think_data : ['type' => 5,'id' => 0];
  93. }
  94. $savedata['mall_id'] = $data['mall_id'];
  95. $savedata['sudoku_id'] = $prize_data['id'];
  96. $savedata['user_id'] = $data['user_id'];
  97. $savedata['type'] = $prize_data['type'];
  98. $savedata['created_at'] = time();
  99. switch ($prize_data['type']){
  100. case '1': //奖励金额
  101. $savedata['status'] = 1;
  102. $savedata['receive_at'] = time();
  103. $savedata['red_packet_amount'] = $prize_data['red_packet_amount'];
  104. $return['msg'] = '恭喜您获得'.$prize_data['red_packet_amount'].'元';
  105. $this->stock($prize_data['id']);//减去库存
  106. $this->balanceOrScore($data,$prize_data['red_packet_amount'],$prize_data['score'],1);
  107. break;
  108. case '2': //奖励优惠卷
  109. $savedata['status'] = 1;
  110. $savedata['coupon_id'] = $prize_data['coupon_id'];
  111. $give_coupon_arr[$prize_data['coupon_id']] = 1;
  112. $savedata['receive_at'] = time(); //var_dump($prize_data);
  113. $return['msg'] = '恭喜您获得一张'.$prize_data['coupon']['name'].'优惠券';
  114. $this->stock($prize_data['id']);//减去库存
  115. $coupon_res = AddonsCouponUserRelate::giveCoupon($data['mall_id'],$data['user_id'],$give_coupon_arr,AddonsCouponEnum::SEND_FROM9);
  116. if ($coupon_res === false) {// 优惠券发放失败则直接返回谢谢参与
  117. $savedata['type'] = 5;
  118. $return['msg'] = '谢谢参与';
  119. if (!empty($think_data)) {
  120. $savedata['sudoku_id'] = $think_data['id'];
  121. $prize_data = $think_data;
  122. }
  123. }
  124. break;
  125. case '3': //奖励积分
  126. $savedata['status'] = 1;
  127. $savedata['score'] = $prize_data['score'];
  128. $savedata['receive_at'] = time();
  129. $return['msg'] = '恭喜您获得'.$prize_data['score'].'积分';
  130. $this->stock($prize_data['id']);//减去库存
  131. $this->balanceOrScore($data,null,$prize_data['score'],2);
  132. break;
  133. case '4': //奖励商品
  134. $savedata['goods_id'] = $prize_data['goods_id'];
  135. $savedata['status'] = 0;
  136. $return['msg'] = '获得'.$prize_data['goods']['goods_name'].'商品一件,快去领取吧';
  137. $this->stock($prize_data['id']);//减去库存
  138. break;
  139. case '5': //谢谢参与
  140. $savedata['status'] = 1;
  141. $savedata['receive_at'] = time();
  142. $return['msg'] = '谢谢参与';
  143. break;
  144. }
  145. $return['id'] = $prize_data['id'];
  146. //抽奖扣除积分
  147. $this->useScore($data,$config['use_point']);
  148. //保存记录
  149. if(empty($savedata['red_packet_amount'])) $savedata['red_packet_amount'] = 0;
  150. if(empty($savedata['score'])) $savedata['score'] = 0;
  151. $this->saveSodokuLog($savedata);
  152. $trans->commit();
  153. return $return;
  154. } catch (Exception $e) {
  155. $trans->rollBack();
  156. $this->setError($e->getMessage());
  157. Yii::error('Sodoku savePrizeLog error:'.$e->getMessage().',line:'.$e->getLine());
  158. return false;
  159. }
  160. }
  161. //减去九宫格库存
  162. public function stock($id){
  163. $sudoku_data = AddonsSudoku::findOne([['id' => $id]]);
  164. $sudoku_data->stock = $sudoku_data->stock - 1;
  165. $sudoku_data->save();
  166. }
  167. //九宫格抽奖消耗积分
  168. public function useScore($data,$score){
  169. $insert_wallet [] = ['mall_id' => $data['mall_id'], 'user_id' => $data['user_id'],
  170. 'is_frozen' => false,
  171. 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE,
  172. 'money' => '-'.$score,
  173. 'desc' => '九宫格抽奖消耗积分',
  174. 'source_table' => SudokuEnum::ADDONS_NAME,
  175. 'source_table_id' => $data['user_id'],
  176. 'from_type' => UserWalletEnum::FROM_SUDOKU_DEDUCT,
  177. 'capital_type' => UserWalletEnum::DEDUCT,
  178. ];
  179. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  180. //if ($res === false) throw new \Exception(UserWalletService::getStaticError());
  181. }
  182. //赠送余额
  183. public function balanceOrScore($data,$balance,$score,$type){
  184. $insert_wallet [] = ['mall_id' => $data['mall_id'], 'user_id' => $data['user_id'],
  185. 'is_frozen' => false,
  186. 'wallet_type' => $type==1 ? UserWalletService::WALLET_TYPE_BALANCE : UserWalletService::WALLET_TYPE_SCORE,
  187. 'money' => $type==1 ? $balance : $score,
  188. 'desc' => $type==1 ? "九宫格抽奖奖励余额" : "九宫格抽奖奖励积分",
  189. 'source_table' => SudokuEnum::ADDONS_NAME,
  190. 'source_table_id' => $data['user_id'],
  191. 'from_type' => UserWalletEnum::FROM_SUDOKU_DEDUCT,
  192. 'capital_type' => UserWalletEnum::GIVE,
  193. ];
  194. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  195. //if ($res === false) return $this->error(UserWallet::getStaticError());
  196. }
  197. //保存纪录
  198. public function saveSodokuLog($savedata){
  199. $res = AddonsSudokuLog::setData($savedata);
  200. if ($res === false) throw new Exception(AddonsSudokuLog::getStaticError());
  201. return true;
  202. }
  203. //判断会员资格
  204. public function checkUser($config,$data){
  205. //统计会员剩余次数
  206. if($config['type']==1){
  207. $params['where'][] = ['>','created_at',strtotime(date('Y-m-d'))];
  208. }
  209. $params['where'][] = ['=','user_id',$data['user_id']];
  210. $count_times = AddonsSudokuLog::count($params);
  211. $limit_times = $config['lucky_number'] - $count_times;
  212. if($limit_times<=0){
  213. throw new Exception("次数不足");
  214. }
  215. if($config['use_point']>$data['score']){
  216. $score_name = \Yii::$app->services->setting->mall->get($data['mall_id'], 'score.score_name') ?? '积分';
  217. throw new Exception($score_name."不足");
  218. }
  219. return true;
  220. }
  221. //检验奖品
  222. public function checkprize($data){
  223. $params['where'][] = ['mall_id'=>$data['mall_id']];
  224. $params['order'] = 'id asc';
  225. $params['with'] = ['goods','coupon'];
  226. $list = AddonsSudoku::lists($params,true);
  227. if($list){
  228. foreach ($list as $k => $v){
  229. if($v['stock'] <= 0 || $v['probability'] <= 0){
  230. $list[$k]['is_valid'] = '1'; //没库存,概率0
  231. continue;
  232. }
  233. if($v['stock'] <= 0 && $v['type'] != 5) $list[$k]['is_valid'] = '1'; //九宫格设定的库存已经消耗完
  234. //判断优惠卷是否有效和是否有库存
  235. if($v['type']==2){
  236. if($v['coupon']){
  237. if($v['coupon']['status'] < 1){
  238. $list[$k]['is_valid'] = '1'; //优惠卷已经失效
  239. }
  240. }else{
  241. $list[$k]['is_valid'] = '1'; //优惠卷异常,查询不到结果
  242. }
  243. }
  244. //判断商品是否有效,否有库存是否售卖
  245. if($v['type']==4){
  246. if($v['goods']){ //var_dump($v['goods']);die;
  247. if($v['goods']['status'] == '-1') $list[$k]['is_valid'] = '1';//商品已经下架
  248. if($v['goods']['stock'] <= 0) $list[$k]['is_valid'] = '1';//商品库存不足
  249. if($v['goods']['is_on_sale'] == 0) $list[$k]['is_valid'] = '1';//商品已经下架
  250. }else{
  251. $list[$k]['is_valid'] = '1'; //优惠卷异常,查询不到结果
  252. }
  253. }
  254. }
  255. }
  256. return $list;
  257. }
  258. //计算中奖奖品
  259. public function randomsum($prizeList){
  260. $is_valid_sum = 0;
  261. $randomarray = []; //var_dump($prizeList);
  262. $num = 100;
  263. $thank_id = '';
  264. foreach ($prizeList as $k => $v){
  265. if(!$v['is_valid']){ //有效奖品
  266. $max_value = $v['probability'] * $num;
  267. if($max_value>0){
  268. for ($i=0;$i<$max_value;$i++) {
  269. $randomarray[] = $v['id'];
  270. }
  271. }
  272. }else{
  273. $is_valid_sum = $is_valid_sum + $v['probability'];
  274. }
  275. if($v['type'] == 5) $thank_id = $v['id'];
  276. }
  277. if($is_valid_sum > 0 && $thank_id) for ($i=0;$i<$is_valid_sum * $num;$i++) $randomarray[] = $thank_id;
  278. shuffle($randomarray);
  279. if(!$randomarray) throw new Exception("您来晚了,奖品已经抽取完");
  280. return $randomarray[array_rand($randomarray,1)];
  281. }
  282. /**
  283. * 根据概率计算可中奖的奖品
  284. * @Author: lun
  285. * @DateTime: 2023/8/22 0022 15:50
  286. * @Copyright: copyright (c) 2021 广东七件事集团
  287. * @param $prizeList
  288. * @return int|string
  289. */
  290. public function computeWin($prizeList)
  291. {
  292. $arr = [];
  293. foreach ($prizeList as $item) {
  294. $arr[$item['id']] = $item['probability'];
  295. $list[$item['id']] = $item;
  296. }
  297. $rid = $this->getRand($arr);
  298. return $rid;
  299. }
  300. /**
  301. * 获取中奖奖品id
  302. * @Author: lun
  303. * @DateTime: 2023/8/22 0022 15:51
  304. * @Copyright: copyright (c) 2021 广东七件事集团
  305. * @param $proArr
  306. * @return int|string
  307. */
  308. public function getRand($proArr)
  309. {
  310. if (empty($proArr)) return '';
  311. // 计算总概率
  312. $totalProbability = array_sum($proArr);
  313. if (empty($totalProbability)) return '';
  314. $proList = [];
  315. // 设置需要生成的数组数
  316. foreach ($proArr as $key => $value) {
  317. if (!is_numeric($value)) continue;
  318. // 去除小数点概率
  319. $value = intval($value);
  320. $valueList = array_fill(0, $value, $key);
  321. $proList = array_merge($proList, $valueList);
  322. }
  323. // 当客户设置概率总和小于100时,补全概率
  324. if (count($proList) < 100) $proList = array_merge(array_fill(0, 100 - count($proList), '0'), $proList);
  325. // 打乱数组进行随机抽取
  326. shuffle($proList);
  327. $randomNumber = array_rand($proList);
  328. if (empty($randomNumber)) return '';
  329. return $proList[$randomNumber];
  330. }
  331. }