ClearanceCalc.php 6.1 KB

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