Explorar o código

update
完善开奖算法,加入简单的纵向匹配机制和新人鼓励机制
支持幸运番外A,B场次

joe %!s(int64=4) %!d(string=hai) anos
pai
achega
9862d256fa

+ 4 - 0
config/activity.php

@@ -18,6 +18,10 @@ return [
     'clearrance_daily_opens' => [],
     // 幸运活动 ID
     'lucky_cate_id' => 1,
+    // 幸运活动番外A ID
+    'lucky_a_cate_id' => 3,
+    // 幸运活动饭外B ID
+    'lucky_b_cate_id' => 4,
     // 幸运活动开奖时间表
     'lucky_daily_opens' => [],
 ];

+ 54 - 18
crmeb/services/async/ActivityCalc.php

@@ -8,6 +8,7 @@ use app\models\store\StoreOrderCartInfo;
 use app\models\system\SystemAwardHistory;
 use app\models\user\UserNotice;
 use crmeb\basic\BaseModel;
+use crmeb\utils\Redis;
 use think\facade\Log;
 
 
@@ -40,7 +41,8 @@ abstract class ActivityCalc {
             ->where('o.is_system_del', 0)
             ->where('p.cate_id', $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();
+            ->field('o.id, o.pay_price, ci.product_id, ci.cart_info, u.uid, u.now_money, u.pay_count, p.store_name')
+            ->select();
         $products = $products ? $products->toArray() : [];
         return $products;
     }
@@ -52,19 +54,21 @@ abstract class ActivityCalc {
         // 活动分类ID
         $cate_id = $this->getId();
         $products = self::getOrders($cate_id);
-        if (count($products) <= 1) {
-            Log::warning('not enough order, stop activity:' . $this->getNameCN());
+        $pNum = count($products);
+        $minNum = $this->getMinimalProductNum();
+        if ($pNum < $minNum) {
+            Log::warning("not enough order. product num: $pNum, $minNum needed, stop activity: $this->getNameCN()");
             return;
         }
 
         // 按商品处理订单
-        $counter = 0;
-        $left = [];
-        $right = [];
-        $left_spent = 0;
-        $right_spent = 0;
-        $left_cost = 0;
-        $right_cost = 0;
+        $counter = 0;       // 订单中商品计数器
+        $left = [];         // 左侧商品
+        $right = [];        // 右侧商品
+        $left_spent = 0;    // 左侧总价
+        $right_spent = 0;   // 右侧总价
+        $left_cost = 0;     // 左侧总成本
+        $right_cost = 0;    // 右侧总成本
 
         // 打乱商品顺序
         shuffle($products);
@@ -124,17 +128,46 @@ abstract class ActivityCalc {
         $left_profit = bcsub($left_spent, $left_cost, 2);
         $right_profit = bcsub($right_spent, $right_cost, 2);
         // 定输赢
-        $diff_money = bcsub($left_spent, $right_spent, 2);
-        if ($diff_money < 0.0) {
-            $diff_money = -$diff_money;
-        }
-        $profit = $left_profit;
+        $diff_money = abs(bcsub($left_spent, $right_spent, 2));
+        // if ($diff_money < 0.0) {
+        //     $diff_money = -$diff_money;
+        // }
+        // 假定左边胜利
+        $profit = $right_profit;
         $winners = $left;
         $losers = $right;
+        // 左边利润高则右边胜利
         if ($left_profit > $right_profit) {
             $winners = $right;
             $losers = $left;
-            $profit = $right_profit;
+            $profit = $left_profit;
+        }
+        // 如果只有 1 件商品
+        if ($pNum == 1) {
+            $KEY = 'activity:pool';
+            // 此时,订单必为 loser
+            // 40% 概率胜
+            $itWin = false; // 这单胜了 ?
+            $dice = mt_rand(0, 100); 
+            if ( $dice < 40) {
+                $itWin = true;
+            }
+
+            $remain = intval(Redis::hGet($KEY, $this->getName()));
+            if (!$itWin) {
+                // 如果为新用户,且池子有钱,让他胜
+                if ($remain > $profit && $products[0]['pay_count'] <= 1) {
+                    $itWin = true;
+                }
+            }
+            
+            if ($itWin) {
+                $profit = -$profit;
+                list($winners, $losers) = [$losers, $winners];
+            }
+            Redis::hSet($KEY, $this->getName(), $remain + $profit);
+
+            Log::warning("$this->getName(): single order[product]. dice:$dice, remain:$remain, pay_count:$products[0]['pay_count']");
         }
 
         $result = $this->getResult($winners == $left);
@@ -159,7 +192,7 @@ abstract class ActivityCalc {
             'rate' => $this->repRate(),
         ]);
 
-        // 结果处置
+        // 结果处置 胜方退款,败方发货
         foreach($winners as $p) {
             $single = $orders[$p['id']] == 1;
             $this->execute($p, $single, true);
@@ -169,7 +202,7 @@ abstract class ActivityCalc {
             $this->execute($p, $single, false);
         }
 
-        Log::warning('activity ' . $this->getNameCN() . ' calc finished. result:' . $result);
+        Log::warning("activity $this->getNameCN() calc finished. result: $result");
     }
 
     protected function execute($product, $single=true, $win=true) {
@@ -233,6 +266,9 @@ abstract class ActivityCalc {
     // 赔款所占商品利润的百分比
     abstract protected function repRate();
 
+    // 满足开奖的最小订单数
+    abstract protected function getMinimalProductNum();
+
     // 获得开奖结果 int
     abstract protected function getResult($leftwin=true);
     /**

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

@@ -38,6 +38,11 @@ class ClearanceCalc extends ActivityCalc{
         return 0.2;
     }
 
+    protected function getMinimalProductNum()
+    {
+        return 2;
+    }
+
     protected function getResult($leftwin=true) {
         return 0;
     }

+ 5 - 1
crmeb/services/async/LuckyCalc.php

@@ -2,7 +2,6 @@
 namespace crmeb\services\async;
 
 use crmeb\services\async\ActivityCalc;
-use app\models\system\SystemAwardHistory;
 use think\facade\Config;
 
 class LuckyCalc extends ActivityCalc{
@@ -25,6 +24,11 @@ class LuckyCalc extends ActivityCalc{
         return 0.2;
     }
 
+    protected function getMinimalProductNum()
+    {
+        return 1;
+    }
+
     protected function getResult($leftwin=true) {
         return $leftwin ? 20 : 21;
     }

+ 26 - 0
crmeb/services/async/LuckyExtACalc.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace crmeb\services\async;
+
+use crmeb\services\async\LuckyCalc;
+use think\facade\Config;
+
+class LuckyExtACalc extends LuckyCalc {
+    protected static $NAME = 'lucky_a';
+
+    protected function getId() {
+        return Config::get('activity.lucky_a_cate_id');
+    }
+
+    protected function getName() {
+        return self::$NAME;
+    }
+
+    protected function getNameCN() {
+        return '幸运番外A';
+    }
+
+    protected function repRate() {
+        return 0.2;
+    }
+}

+ 26 - 0
crmeb/services/async/LuckyExtBCalc.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace crmeb\services\async;
+
+use crmeb\services\async\LuckyCalc;
+use think\facade\Config;
+
+class LuckyExtBCalc extends LuckyCalc {
+    protected static $NAME = 'lucky_b';
+
+    protected function getId() {
+        return Config::get('activity.lucky_b_cate_id');
+    }
+
+    protected function getName() {
+        return self::$NAME;
+    }
+
+    protected function getNameCN() {
+        return '幸运番外B';
+    }
+
+    protected function repRate() {
+        return 0.2;
+    }
+}

+ 3 - 1
crmeb/subscribes/TimerSubscribe.php

@@ -11,6 +11,8 @@ use app\models\user\UserToken;
 use app\models\redis\SystemCarousel;
 use crmeb\services\async\ClearanceCalc;
 use crmeb\services\async\LuckyCalc;
+use crmeb\services\async\LuckyExtACalc;
+use crmeb\services\async\LuckyExtBCalc;
 use think\facade\Db;
 use think\facade\Log;
 use app\admin\model\system\SystemLog;
@@ -100,7 +102,7 @@ class TimerSubscribe
 
         if (intval($tm['tm_min']) == 0) {
             // 整点做活动
-            $activities = [new ClearanceCalc(), new LuckyCalc()];
+            $activities = [new ClearanceCalc(), new LuckyCalc(), new LuckyExtACalc(), new LuckyExtBCalc()];
             foreach($activities as $activity) {
                 $activity->calc();
             }