ClearanceCalc.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. namespace crmeb\services\async;
  3. use app\models\user\UserNotice;
  4. use app\admin\model\user\UserBill;
  5. use app\admin\model\user\User;
  6. use app\models\store\StoreOrder;
  7. use app\models\store\StoreOrderCartInfo;
  8. use crmeb\basic\BaseModel;
  9. use think\facade\Config;
  10. use think\facade\Log;
  11. use ActivityCalc;
  12. /**
  13. * 抢清仓活动
  14. *
  15. * 玩法:
  16. * 指定一个清仓分类. 该分类下的所有商品在一个周期内一起开奖,
  17. * 中奖的退款并赔款, 未中奖的安排发货.
  18. *
  19. * 在宣传中, 抢到的发货, 未抢到的退款并赔款.
  20. *
  21. * Class ClearanceCalc
  22. * @package crmeb\services\async
  23. */
  24. class ClearanceCalc {
  25. // 活动代号
  26. protected static $NAME = 'clr';
  27. public function calc() {
  28. $clearance_cate_id = Config::get('activity.clearance_cate_id');
  29. // 找到清仓场次中未参加活动的订单
  30. $products = ActivityCalc::getOrders($clearance_cate_id);
  31. if (count($products) <= 1) {
  32. Log::info('not enough order, stop clearance activity');
  33. return;
  34. }
  35. // 按商品处理订单
  36. $counter = 0;
  37. $left = [];
  38. $right = [];
  39. $left_spent = 0;
  40. $right_spent = 0;
  41. shuffle($products);
  42. shuffle($products);
  43. // 标记有多件商品的订单
  44. $orders = [];
  45. foreach($products as &$p) {
  46. $counter += 1;
  47. $ci = json_decode($p['cart_info'], true);
  48. $num = $ci['num']; // 购买个数
  49. $productInfo = isset($ci['productInfo']) ? $ci['productInfo'] : [];
  50. $attrInfo = isset($productInfo['attrInfo']) ? $productInfo['attrInfo'] : [];
  51. $price = isset($attrInfo['price']) ? $attrInfo['price'] : 0.0;
  52. $cost = isset($attrInfo['cost']) ? $attrInfo['cost'] : 0.0;
  53. $ppaid = bcmul($num, $price, 2);// product paid
  54. $pcost = bcmul($num, $cost, 2);
  55. $oid = $p['id'];
  56. $p['paid'] = $ppaid;
  57. $p['cost'] = $pcost;
  58. if (!isset($orders[$oid])) {
  59. $orders[$oid] = 1;
  60. } else {
  61. $orders[$oid] += 1;
  62. }
  63. if ($ppaid <= 1.0) {
  64. Log::warning("impossible price, order: ". $p['id']);
  65. continue;
  66. }
  67. if ($cost <= 0.1) {
  68. Log::warning("impossible cost, order: ". $p['id']);
  69. continue;
  70. }
  71. if ($counter % 2) {
  72. $left[] = $p;
  73. $left_spent = bcadd($left_spent, $ppaid, 2);
  74. } else {
  75. $right[] = $p;
  76. $right_spent = bcadd($right_spent, $ppaid, 2);
  77. }
  78. }
  79. // free memory
  80. $products = [];
  81. // 定输赢
  82. $diff_money = bcsub($left_spent, $right_spent, 2);
  83. if ($diff_money < 0.0) {
  84. $diff_money = -$diff_money;
  85. }
  86. $winners = $left;
  87. $losers = $right;
  88. if ($left_spent > $right_spent) {
  89. $winners = $right;
  90. $losers = $left;
  91. }
  92. // 结果处置
  93. foreach($winners as $p) {
  94. $single = $orders[$p['id']] == 1;
  95. $this->execute($p, $single, true);
  96. }
  97. foreach($losers as $p) {
  98. $single = $orders[$p['id']] == 1;
  99. $this->execute($p, $single, false);
  100. }
  101. }
  102. /**
  103. * 奖励分配
  104. *
  105. * @param $product 获奖订单中的商品
  106. * @param bool $single 是否是单种商品构成的订单
  107. * @param bool $win 是否中奖
  108. */
  109. protected function execute($product, $single=true, $win=true) {
  110. BaseModel::beginTrans();
  111. $result = 0;
  112. $reparation = 0.0;
  113. if ($win) {
  114. $result = 1;
  115. $reparation = bcmul(bcsub($product['paid'], $product['cost'], 2), 0.1, 2);
  116. }
  117. // 标记商品为已参与活动
  118. $r1 = StoreOrderCartInfo::where('oid', $product['id'])
  119. ->where('product_id', $product['product_id'])
  120. ->update([
  121. 'activity' => self::$NAME,
  122. 'result' => $result,
  123. 'reparation' => $reparation,
  124. ]);
  125. if (!$win) {
  126. return;
  127. }
  128. $refund_price = bcadd($product['paid'], $reparation, 2);
  129. // 订单全部商品都中奖的退单
  130. $r2 = true;
  131. if ($single) {
  132. $r2 = StoreOrder::where('id', $product['id'])->update([
  133. 'refund_price' => $refund_price,
  134. 'refund_status' => 2,
  135. ]);
  136. }
  137. // 中奖的 退款并赔款返回到佣金
  138. $r3 = UserBill::income('參加活动', $product['uid'], 'now_money', 'brokerage',
  139. $refund_price, $product['id'], bcadd($product['now_money'], $refund_price, 2),
  140. '订单退款' . $product['paid'] . '+' . $reparation . '元');
  141. $r4 = User::bcInc($product['uid'], 'brokerage_price', $refund_price, 'uid');
  142. $ok = $r1 && $r2 && $r3 && $r4;
  143. BaseModel::checkTrans($ok);
  144. if ($ok) {
  145. // 中奖用户发送中奖消息.
  146. UserNotice::sendNoticeTo($product['uid'], '您的订单已退款',
  147. '您好,由于活动商品库存有限,您的订单 ' . $product['store_name']
  148. . ' 未能成功分配. 该商品付款' . $product['paid'] . '元已如数退还, 并赔付您' . $reparation
  149. . '元作为补偿, 对您造成的不便我们深感抱歉. 同时感谢您对美天旺的喜爱和信任, 美天旺活动场将永远等您. 顺祝生活愉快.');
  150. }
  151. }
  152. }