Explorar o código

实现抢单活动

joe %!s(int64=4) %!d(string=hai) anos
pai
achega
13498d89a1

+ 1 - 1
app/api/controller/user/UserNotificationController.php

@@ -40,7 +40,7 @@ class UserNotificationController {
             $icon = DictCoin::where('symbol', $symbol)->value('icon');
             Cache::set($symbol, $icon);
         }
-        
+
         $defStatus = [
             'progress'=> 0,
             'symbol'=>$symbol,

+ 18 - 0
config/activity.php

@@ -0,0 +1,18 @@
+<?php
+
+return [
+    // 根分类ID
+    'root_cate_id' => 1,
+    // 挖矿活动ID
+    'mining_cate_id' => 199,
+    // 免单活动ID
+    'free_cate_id' => 4,
+    // 清仓活动ID
+    'clearance_cate_id' => 2,
+    // 清仓开奖时间表
+    'clearrance_daily_opens' => [],
+    // 幸运活动 ID
+    'lucky_cate_id' => 3,
+    // 幸运活动开奖时间表
+    'lucky_daily_opens' => [],
+];

+ 0 - 2
config/app.php

@@ -56,6 +56,4 @@ return [
     // 每 @mining_sec_unit 挖币 @mining_num_per_unit
     'mining_sec_unit' => 5,
     'mining_num_per_unit' => 0.01,
-
-    //
 ];

+ 5 - 0
crmeb/services/async/ActivitiesCalc.php

@@ -0,0 +1,5 @@
+<?php
+
+abstract class ActivitiesCalc {
+
+}

+ 183 - 0
crmeb/services/async/ClearanceCalc.php

@@ -0,0 +1,183 @@
+<?php
+namespace crmeb\services\async;
+
+use app\models\user\UserNotice;
+use app\admin\model\user\UserBill;
+use app\admin\model\user\User;
+use app\models\store\StoreOrder;
+use app\models\store\StoreOrderCartInfo;
+use crmeb\basic\BaseModel;
+use think\facade\Config;
+use think\facade\Log;
+
+/**
+ * 抢清仓活动
+ *
+ * 玩法:
+ *      指定一个清仓分类. 该分类下的所有商品在一个周期内一起开奖,
+ *      中奖的退款并赔款, 未中奖的安排发货.
+ *
+ *      在宣传中, 抢到的发货, 未抢到的退款并赔款.
+ *
+ * Class ClearanceCalc
+ * @package crmeb\services\async
+ */
+class ClearanceCalc {
+
+    // 活动代号
+    protected static $NAME = 'clr';
+
+    public function calc() {
+        $clearance_cate_id = Config::get('activity.clearance_cate_id');
+        // 找到清仓场次中未参加活动的订单
+        $products = StoreOrderCartInfo::alias('ci')
+            ->join('store_order o', 'o.id=ci.oid')
+            ->join('store_product p', 'ci.product_id=p.id')
+            ->join('user u', 'u.uid = o.uid')
+            ->where('o.paid', 1)
+            ->where('o.status', '>=', 0)
+            ->where('o.refund_status', 0)
+            ->where('o.is_del', 0)
+            ->where('o.is_system_del', 0)
+            ->where('p.cart_id', $clearance_cate_id)
+            ->where('ci.activity', '')
+            ->field('o.id, o.pay_price, ci.product_id, ci.cart_info, u.uid, u.now_money, p.store_name')->select();
+        $products = $products ? $products->toArray() : [];
+
+        if (count($products) <= 1) {
+            Log::info('not enough order, stop clearance activity');
+            return;
+        }
+
+        // 按商品处理订单
+        $counter = 0;
+        $left = [];
+        $right = [];
+        $left_spent = 0;
+        $right_spent = 0;
+
+        shuffle($products);
+        shuffle($products);
+
+        // 标记有多件商品的订单
+        $orders = [];
+        foreach($products as $p) {
+            $counter += 1;
+
+            $ci = json_decode($p['cart_info'], true);
+            $num = $ci['num'];  // 购买个数
+            $productInfo = isset($ci['productInfo']) ? $ci['productInfo'] : [];
+            $attrInfo = isset($productInfo['attrInfo']) ? $productInfo['attrInfo'] : [];
+            $price = isset($attrInfo['price']) ? $attrInfo['price'] : 0.0;
+            $cost = isset($attrInfo['cost']) ? $attrInfo['cost'] : 0.0;
+            $ppaid = bcmul($num, $price, 2);// product paid
+            $pcost = bcmul($num, $cost, 2);
+            $oid = $p['id'];
+            $p['paid'] = $ppaid;
+            $p['cost'] = $pcost;
+
+            if (!isset($orders[$oid])) {
+                $orders[$oid] = 1;
+            } else {
+                $orders[$oid] += 1;
+            }
+
+            if ($ppaid <= 1.0) {
+                Log::warning("impossible price, order: ". $p['id']);
+                continue;
+            }
+            if ($cost <= 0.1) {
+                Log::warning("impossible cost, order: ". $p['id']);
+                continue;
+            }
+
+            if ($counter % 2) {
+                $left[] = $p;
+                $left_spent = bcadd($left_spent, $ppaid, 2);
+            } else {
+                $right[] = $p;
+                $right_spent = bcadd($right_spent, $ppaid, 2);
+            }
+        }
+        // free memory
+        $products = [];
+
+        // 定输赢
+        $diff_money = bcsub($left_spent, $right_spent, 2);
+        if ($diff_money < 0.0) {
+            $diff_money = -$diff_money;
+        }
+        $winners = $left;
+        $losers = $right;
+        if ($left_spent > $right_spent) {
+            $winners = $right;
+            $losers = $left;
+        }
+
+        // 结果处置
+        $single = 0;
+        foreach($winners as $p) {
+            $single = $orders[$p['id']] == 1;
+            $this->execute($p, $single, true);
+        }
+        foreach($losers as $p) {
+            $single = $orders[$p['id']] == 1;
+            $this->execute($p, $single, false);
+        }
+    }
+
+    /**
+     * 奖励分配
+     *
+     * @param $product  获奖订单中的商品
+     * @param bool $single  是否是单种商品构成的订单
+     * @param bool $win 是否中奖
+     */
+    protected function execute($product, $single=true, $win=true) {
+        BaseModel::beginTrans();
+        $result = 0;
+        $reparation = 0.0;
+        if ($win) {
+            $result = 1;
+            $reparation = bcmul(bcsub($product['paid'], $product['cost'], 2), 0.1, 2);
+        }
+        // 标记商品为已参与活动
+        $r1 = StoreOrderCartInfo::where('oid', $product['id'])
+            ->where('product_id', $product['product_id'])
+            ->update([
+                'activity' => self::$NAME,
+                'result' => $result,
+                'reparation' => $reparation,
+            ]);
+
+        if (!$win) {
+            return;
+        }
+
+        $refund_price = bcadd($product['paid'], $reparation, 2);
+        // 订单全部商品都中奖的退单
+        $r2 = true;
+        if ($single) {
+            $r2 = StoreOrder::where('id', $product['id'])->update([
+                'refund_price' => $refund_price,
+                'refund_status' => 2,
+            ]);
+        }
+        // 中奖的 退款并赔款返回到佣金
+        $r3 = UserBill::income('參加活动', $product['uid'], 'now_money', 'brokerage',
+            $refund_price, $product['id'], bcadd($product['now_money'], $refund_price, 2),
+            '订单退款' . $product['paid'] . '+' . $reparation . '元');
+        $r4 = User::bcInc($product['uid'], 'brokerage_price', $refund_price, 'uid');
+
+        $ok = $r1 && $r2 && $r3 && $r4;
+        BaseModel::checkTrans($ok);
+
+        if ($ok) {
+            // 中奖用户发送中奖消息. TODO
+            UserNotice::sendNoticeTo($product['uid'], '您的订单已退款',
+                '您好,由于活动商品库存有限,您的订单 ' . $product['store_name']
+                . ' 未能成功分配. 该商品付款' . $product['paid'] . '元已如数退还, 并赔付您' . $reparation
+                . '元作为补偿, 对您造成的不便我们深感抱歉. 同时感谢您对美天旺的喜爱和信任, 美天旺活动场将永远等您. 顺祝生活愉快.');
+        }
+    }
+}