LuckyDrawLogic.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop100%开源免费商用商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | gitee下载:https://gitee.com/likeshop_gitee
  10. // | github下载:https://github.com/likeshop-github
  11. // | 访问官网:https://www.likeshop.cn
  12. // | 访问社区:https://home.likeshop.cn
  13. // | 访问手册:http://doc.likeshop.cn
  14. // | 微信公众号:likeshop技术社区
  15. // | likeshop团队 版权所有 拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshopTeam
  18. // +----------------------------------------------------------------------
  19. namespace app\shopapi\logic;
  20. use app\common\enum\AccountLogEnum;
  21. use app\common\enum\GoodsEnum;
  22. use app\common\enum\LuckyDrawEnum;
  23. use app\common\enum\YesNoEnum;
  24. use app\common\logic\AccountLogLogic;
  25. use app\common\logic\BaseLogic;
  26. use app\common\model\Coupon;
  27. use app\common\model\Goods;
  28. use app\common\model\LuckyDraw;
  29. use app\common\model\LuckyDrawPrize;
  30. use app\common\model\LuckyDrawRecord;
  31. use app\common\model\Order;
  32. use app\common\model\User;
  33. use think\facade\Db;
  34. /**
  35. * 幸运抽奖
  36. */
  37. class LuckyDrawLogic extends BaseLogic
  38. {
  39. /**
  40. * @notes 查看活动信息
  41. * @author Tab
  42. * @date 2021/11/25 11:45
  43. */
  44. public static function activity($params)
  45. {
  46. $activity = LuckyDraw::findOrEmpty($params['id']);
  47. if ($activity->isEmpty()) {
  48. return [];
  49. }
  50. $prizes = LuckyDrawPrize::field('name,image,location,type,num,type_value')
  51. ->append(['type_desc'])
  52. ->where(['activity_id'=>$activity->id])
  53. ->order(['probability'=>'asc'])
  54. ->select()
  55. ->toArray();
  56. $prizesLists = [];
  57. foreach ($prizes as $key => $value) {
  58. $prizes[$key]['type_value_desc'] = '-';
  59. switch ($value['type']) {
  60. case 1:
  61. $prizes[$key]['type_value_desc'] = $value['type_value'].'积分';
  62. break;
  63. case 2:
  64. $prizes[$key]['type_value_desc'] = '优惠券';
  65. break;
  66. case 3:
  67. $prizes[$key]['type_value_desc'] = $value['type_value'].'元';
  68. break;
  69. case 4:
  70. $price = Goods::where(['id'=>$value['type_value']])->value('min_price');
  71. $prizes[$key]['type_value_desc'] = $price.'元';
  72. break;
  73. }
  74. if ($value['type'] != LuckyDrawEnum::NOT_WIN) {
  75. $prizesLists[] = $prizes[$key];
  76. }
  77. }
  78. $user = User::findOrEmpty($params['user_id']);
  79. $btn = self::btnInfo($activity);
  80. // 处理返回格式
  81. $prizes = self::formatReturn($prizes, $btn);
  82. //剩余抽奖次数
  83. $SurplusDrawNum = $activity->frequency;
  84. if ($activity->frequency_type == 1) {
  85. $userDrawNum = LuckyDrawRecord::where(['activity_id'=>$activity->id,'user_id'=>$params['user_id']])->whereDay('create_time')->count();
  86. $SurplusDrawNum = $SurplusDrawNum - $userDrawNum;
  87. }
  88. return [
  89. 'user_integral' => $user->user_integral,
  90. 'rule' => $activity->rule,
  91. 'show_winning_list' => $activity->show_winning_list,
  92. // 'limit' => self::limitInfo($activity),
  93. 'prizes' => $prizes,
  94. 'prizes_lists' => $prizesLists,
  95. 'frequency_type' => $activity->frequency_type,
  96. 'frequency' => $activity->frequency,
  97. 'surplus_draw_num' => $SurplusDrawNum,
  98. ];
  99. }
  100. /**
  101. * @notes 处理返回格式
  102. * @param $prizes
  103. * @param $btn
  104. * @author Tab
  105. * @date 2021/11/26 10:25
  106. */
  107. private static function formatReturn($prizes, $btn)
  108. {
  109. $prizes = array_column($prizes, null, 'location');
  110. // 前端需要的顺序 [1, 2, 3, 8, 抽奖按钮 ,4, 7, 6, 5];
  111. $sort = [1, 2, 3, 8, 4, 7, 6, 5];
  112. $data = [];
  113. foreach ($sort as $location) {
  114. $data[] = $prizes[$location];
  115. }
  116. // 插入抽奖按钮
  117. array_splice($data, 4, 0, [$btn]);
  118. return $data;
  119. }
  120. /**
  121. * @notes 抽奖按钮信息
  122. * @param $activity
  123. * @author Tab
  124. * @date 2021/11/25 11:57
  125. */
  126. private static function btnInfo($activity)
  127. {
  128. $btnInfo = [
  129. 'flag' => true,
  130. 'tips' => '开始抽奖'
  131. ];
  132. if ($activity->status == LuckyDrawEnum::END || $activity->end_time <= time()) {
  133. $btnInfo['flag'] = false;
  134. $btnInfo['tips'] = '活动已结束';
  135. }
  136. if ($activity->status == LuckyDrawEnum::WAIT || $activity->start_time > time()) {
  137. $btnInfo['flag'] = false;
  138. $btnInfo['tips'] = '活动未开始';
  139. }
  140. if ($activity->need_integral > 0) {
  141. $btnInfo['tips'] = '消耗' . $activity->need_integral . '积分';
  142. }
  143. return $btnInfo;
  144. }
  145. /**
  146. * @notes 抽奖次数限制
  147. * @param $activity
  148. * @author Tab
  149. * @date 2021/11/25 14:03
  150. */
  151. private static function limitInfo($activity)
  152. {
  153. $limitInfo = '不限制抽奖次数,具体请查看活动规则';
  154. if ($activity->frequency_type == 1) {
  155. $limitInfo = '每日可抽奖' . $activity->frequency .'次,具体请查看活动规则';
  156. }
  157. return $limitInfo;
  158. }
  159. /**
  160. * @notes 抽奖
  161. * @param $params
  162. * @author Tab
  163. * @date 2021/11/24 17:11
  164. */
  165. public static function lottery($params)
  166. {
  167. Db::startTrans();
  168. try {
  169. // 获取活动信息
  170. $activity = LuckyDraw::findOrEmpty($params['id']);
  171. // 获取用户信息
  172. $user = User::findOrEmpty($params['user_id']);
  173. // 获取奖品信息
  174. $prizes = self::prizeInfo($params['id']);
  175. // 随机抽奖
  176. $randomPrize = self::randomLottery($prizes);
  177. // 积分处理
  178. if ($activity->need_integral > 0) {
  179. // 扣减积分
  180. self::decIntegral($user, $activity->need_integral);
  181. // 记录账户流水
  182. AccountLogLogic::add($user->id, AccountLogEnum::INTEGRAL_DEC_LOTTERY,AccountLogEnum::DEC, $activity->need_integral, '', '幸运抽奖');
  183. }
  184. // 开奖处理
  185. $sendPrizeInfo = [
  186. 'flag' => false,
  187. 'msg' => ''
  188. ];
  189. if (in_array($randomPrize['type'], LuckyDrawEnum::WIN_PRIZE_TYPE)) {
  190. // 发奖
  191. $sendPrizeInfo = self::sendPrize($randomPrize, $user);
  192. // 扣减奖品数量
  193. self::decPrize($randomPrize);
  194. }
  195. // 增加抽奖记录
  196. self::addLotteryRecord($user, $activity, $randomPrize, $sendPrizeInfo);
  197. // 提交事务
  198. Db::commit();
  199. // 返回抽奖结果
  200. return [
  201. 'location' => $randomPrize['location'],
  202. 'tips' => $randomPrize['tips']
  203. ];
  204. } catch (\Exception $e) {
  205. Db::rollback();
  206. self::$error = $e->getMessage();
  207. return false;
  208. }
  209. }
  210. /**
  211. * @notes 提取奖品信息
  212. * @param $activityId
  213. * @author Tab
  214. * @date 2021/11/24 18:02
  215. */
  216. private static function prizeInfo($activityId)
  217. {
  218. $prizes = LuckyDrawPrize::where('activity_id', $activityId)->select()->toArray();
  219. // 属于中奖的奖品
  220. $winPrize = [];
  221. // 属于不中奖的奖品
  222. $notWinPrize = [];
  223. foreach ($prizes as $item) {
  224. if ($item['type'] == LuckyDrawEnum::NOT_WIN) {
  225. $notWinPrize[] = $item;
  226. continue;
  227. }
  228. // 生成概率范围
  229. $winPrizeCount = count($winPrize);
  230. if ($winPrizeCount == 0) {
  231. // 首个属于中奖的奖品
  232. $item['min_range'] = 1;
  233. $item['max_range'] = $item['probability'];
  234. } else {
  235. $item['min_range'] = $winPrize[$winPrizeCount-1]['max_range'] + 1;
  236. $item['max_range'] = $winPrize[$winPrizeCount-1]['max_range'] + $item['probability'];
  237. }
  238. $winPrize[] = $item;
  239. }
  240. return [
  241. 'win_prize' => $winPrize,
  242. 'not_win_prize' => $notWinPrize,
  243. ];
  244. }
  245. /**
  246. * @notes 随机抽奖
  247. * @param $prizes
  248. * @author Tab
  249. * @date 2021/11/24 18:10
  250. */
  251. private static function randomLottery($prizes)
  252. {
  253. //筛选奖品数量充足的奖品
  254. $winPrize = array_filter($prizes['win_prize'], function($item) {
  255. return $item['num'] > 0;
  256. });
  257. // 生成随机种子数
  258. $randInt = mt_rand(1, 10000);
  259. // 判断种子数命中哪个奖品,并且奖品数量充足
  260. if (!empty($winPrize)) {
  261. foreach($winPrize as $item) {
  262. if ($randInt >= $item['min_range'] && $randInt <= $item['max_range'] && $item['num'] > 0) {
  263. return $item;
  264. }
  265. }
  266. }
  267. if (empty($prizes['not_win_prize'])) {
  268. // throw new \Exception('奖品数量已抽完,请下次再来');
  269. if (empty($winPrize)) {
  270. throw new \Exception('奖品数量已抽完,请下次再来');
  271. } else {
  272. return self::randomLottery($prizes);
  273. }
  274. }
  275. // 随机返回一个属于未中奖的奖品
  276. $randInt = mt_rand(0, count($prizes['not_win_prize']) - 1);
  277. return $prizes['not_win_prize'][$randInt];
  278. }
  279. /**
  280. * @notes 扣减积分
  281. * @param $userId
  282. * @param $needIntegral
  283. * @author Tab
  284. * @date 2021/11/24 18:43
  285. */
  286. private static function decIntegral($user, $needIntegral)
  287. {
  288. $user->user_integral = $user->user_integral - $needIntegral;
  289. $user->save();
  290. }
  291. /**
  292. * @notes 发奖
  293. * @param $randomPrize
  294. * @author Tab
  295. * @date 2021/11/24 18:58
  296. */
  297. private static function sendPrize($randomPrize, $user)
  298. {
  299. // 发奖信息
  300. $sendPrizeInfo = [
  301. 'flag' => true,
  302. 'msg' => '发奖成功',
  303. ];
  304. switch ($randomPrize['type']) {
  305. case LuckyDrawEnum::INTEGRAL:
  306. // 发放积分
  307. self::sendIntegral($randomPrize, $user);
  308. // 记录账户流水
  309. AccountLogLogic::add($user->id, AccountLogEnum::INTEGRAL_INC_LOTTERY,AccountLogEnum::INC, (float)$randomPrize['type_value'], '', '幸运抽奖');
  310. break;
  311. case LuckyDrawEnum::COUPON:
  312. // 发放优惠券
  313. $params = [
  314. 'id' => (int)$randomPrize['type_value'],
  315. 'send_user_num' => 1,
  316. 'send_user' => [$user->id],
  317. ];
  318. $result = \app\adminapi\logic\marketing\CouponLogic::send($params);
  319. if ($result !== true) {
  320. $sendPrizeInfo['flag'] = false;
  321. $sendPrizeInfo['msg'] = $result;
  322. }
  323. break;
  324. case LuckyDrawEnum::BALANCE:
  325. // 发放余额
  326. self::sendBalance($randomPrize, $user);
  327. // 记录账户流水
  328. AccountLogLogic::add($user->id, AccountLogEnum::BNW_INC_LOTTERY,AccountLogEnum::INC, (float)$randomPrize['type_value'], '', '幸运抽奖');
  329. break;
  330. case LuckyDrawEnum::GOODS:
  331. // 待用户下单领取
  332. $sendPrizeInfo = [
  333. 'flag' => false,
  334. 'msg' => '待领取',
  335. ];
  336. break;
  337. }
  338. return $sendPrizeInfo;
  339. }
  340. /**
  341. * 发放积分
  342. */
  343. private static function sendIntegral($randomPrize, $user)
  344. {
  345. $user->user_integral = $user->user_integral + (float)$randomPrize['type_value'];
  346. $user->save();
  347. }
  348. /**
  349. * @notes 发放余额
  350. * @param $randomPrize
  351. * @param $user
  352. * @author Tab
  353. * @date 2021/11/25 9:52
  354. */
  355. private static function sendBalance($randomPrize, $user)
  356. {
  357. $user->user_money = $user->user_money + (float)$randomPrize['type_value'];
  358. $user->save();
  359. }
  360. /**
  361. * @notes 奖品数量减1
  362. * @param $randomPrize
  363. * @author Tab
  364. * @date 2021/11/25 9:45
  365. */
  366. private static function decPrize($randomPrize)
  367. {
  368. $prize = LuckyDrawPrize::findOrEmpty($randomPrize['id']);
  369. $prize->num = $prize->num - 1;
  370. $prize->save();
  371. }
  372. /**
  373. * @notes 添加抽奖记录
  374. * @author Tab
  375. * @date 2021/11/25 10:17
  376. */
  377. private static function addLotteryRecord($user, $activity, $randomPrize, $sendPrizeInfo)
  378. {
  379. $data = [
  380. 'user_id' => $user->id,
  381. 'activity_id' => $activity->id,
  382. 'prize_id' => $randomPrize['id'],
  383. 'prize_type' => $randomPrize['type'],
  384. 'is_send' => $sendPrizeInfo['flag'] ? YesNoEnum::YES : YesNoEnum::NO,
  385. 'remark' => $sendPrizeInfo['msg'],
  386. 'send_time' => $sendPrizeInfo['flag'] ? time() : null,
  387. ];
  388. LuckyDrawRecord::create($data);
  389. }
  390. /**
  391. * @notes 查看抽奖记录
  392. * @author Tab
  393. * @date 2021/11/25 10:55
  394. */
  395. public static function record($params)
  396. {
  397. $field = [
  398. 'ldr.id',
  399. 'ldr.is_send',
  400. 'ld.need_integral',
  401. 'ldp.name',
  402. 'ldp.type',
  403. 'ldp.type_value',
  404. 'ldp.image',
  405. 'ldr.create_time',
  406. 'ldr.send_time'
  407. ];
  408. $where = [
  409. 'ldr.activity_id' => $params['id'],
  410. 'ldr.user_id' => $params['user_id'],
  411. ];
  412. $lists = LuckyDrawRecord::alias('ldr')
  413. ->leftJoin('lucky_draw_prize ldp', 'ldp.id = ldr.prize_id')
  414. ->leftJoin('lucky_draw ld', 'ld.id = ldr.activity_id')
  415. ->field($field)
  416. ->where($where)
  417. ->where('ldr.prize_type', '<>', LuckyDrawEnum::NOT_WIN)
  418. ->order('ldr.id', 'desc')
  419. ->page($params['page_no'], $params['page_size'])
  420. ->select()
  421. ->toArray();
  422. $count = LuckyDrawRecord::alias('ldr')
  423. ->leftJoin('lucky_draw_prize ldp', 'ldp.id = ldr.prize_id')
  424. ->leftJoin('lucky_draw ld', 'ld.id = ldr.activity_id')
  425. ->field($field)
  426. ->where($where)
  427. ->count();
  428. foreach ($lists as &$item) {
  429. $item['title'] = self::formatTitle($item);
  430. $item['send_tips'] = !$item['is_send'] && $item['type'] != LuckyDrawEnum::NOT_WIN ? '自动领取失败,请联系客服人员' : '';
  431. $item['need_integral_tips'] = $item['need_integral'] > 0 ? '-'. $item['need_integral'] . '积分' : '';
  432. $item['send_time'] = empty($item['send_time']) ? '-' : date('Y-m-d H:i:s', $item['send_time']);
  433. //商品类型奖品,对奖后获取订单ID
  434. $item['order_id'] = 0;
  435. if ($item['is_send'] == 1) {
  436. switch ($item['type']) {
  437. case LuckyDrawEnum::GOODS:
  438. $item['order_id'] = Order::where(['draw_record_id'=>$item['id']])->value('id');
  439. break;
  440. }
  441. }
  442. }
  443. $data = [
  444. 'lists' => $lists,
  445. 'count' => $count,
  446. 'page_no' => $params['page_no'],
  447. 'page_size' => $params['page_size'],
  448. 'more' => is_more($count, $params['page_no'], $params['page_size'])
  449. ];
  450. return $data;
  451. }
  452. /**
  453. * @notes 格式化抽奖记录标题
  454. * @author Tab
  455. * @date 2021/11/25 11:10
  456. */
  457. private static function formatTitle($item)
  458. {
  459. $title = trim($item['name']);
  460. if ($item['type'] == LuckyDrawEnum::INTEGRAL) {
  461. $title .= '(' . $item['type_value'] . '积分)';
  462. }
  463. if ($item['type'] == LuckyDrawEnum::COUPON) {
  464. $couponName = Coupon::where('id', $item['type_value'])->value('name');
  465. $title .= empty($couponName) ? '(优惠券)' : '('.$couponName.')';
  466. }
  467. if ($item['type'] == LuckyDrawEnum::BALANCE) {
  468. $title .= '(' . $item['type_value'] . '元)';
  469. }
  470. return $title;
  471. }
  472. /**
  473. * @notes 格式化中奖名单标题
  474. * @param $item
  475. * @return string
  476. * @author Tab
  477. * @date 2021/11/25 14:32
  478. */
  479. private static function formatTitleWin($item)
  480. {
  481. $title = "恭喜";
  482. $userName = User::where('id', $item['user_id'])->value('nickname');
  483. // 隐私处理
  484. $userName = '**' . mb_substr($userName, -1 , 1);
  485. $title .= $userName . '抽中了';
  486. if ($item['type'] == LuckyDrawEnum::INTEGRAL) {
  487. $title .= $item['type_value'] . '积分';
  488. }
  489. if ($item['type'] == LuckyDrawEnum::COUPON) {
  490. $couponName = Coupon::where('id', $item['type_value'])->value('name');
  491. $title .= empty($couponName) ? '优惠券' : $couponName;
  492. }
  493. if ($item['type'] == LuckyDrawEnum::BALANCE) {
  494. $title .= $item['type_value'] . '元';
  495. }
  496. return $title;
  497. }
  498. /**
  499. * @notes 查看中奖名单
  500. * @author Tab
  501. * @date 2021/11/25 14:14
  502. */
  503. public static function winningList($params)
  504. {
  505. $field = [
  506. 'ldr.id',
  507. 'ldr.user_id',
  508. 'ldr.create_time',
  509. 'ldp.image',
  510. 'ldp.name',
  511. 'ldp.type',
  512. 'ldp.type_value',
  513. ];
  514. $where = [
  515. ['ldr.prize_type', '<>', LuckyDrawEnum::NOT_WIN],
  516. ['ldr.activity_id', '=', $params['id']],
  517. ];
  518. $lists = LuckyDrawRecord::alias('ldr')
  519. ->field($field)
  520. ->leftJoin('lucky_draw_prize ldp', 'ldp.id = ldr.prize_id')
  521. ->where($where)
  522. ->order('ldr.id', 'desc')
  523. ->page($params['page_no'], $params['page_size'])
  524. ->select()
  525. ->toArray();
  526. foreach ($lists as &$item) {
  527. $item['title'] = self::formatTitleWin($item);
  528. }
  529. $count = LuckyDrawRecord::alias('ldr')
  530. ->field($field)
  531. ->leftJoin('lucky_draw_prize ldp', 'ldp.id = ldr.prize_id')
  532. ->where($where)
  533. ->count();
  534. $data = [
  535. 'lists' => $lists,
  536. 'count' => $count,
  537. 'page_no' => $params['page_no'],
  538. 'page_size' => $params['page_size'],
  539. 'more' => is_more($count, $params['page_no'], $params['page_size'])
  540. ];
  541. return $data;
  542. }
  543. }