Browse Source

update: 完成活动,单端测试幸运2021

joe 4 năm trước cách đây
mục cha
commit
e71a7d7a98

+ 3 - 1
app/admin/controller/Test.php

@@ -3,6 +3,7 @@
 namespace app\admin\controller;
 
 
+use crmeb\services\async\LuckyCalc;
 use crmeb\utils\Redis;
 
 class Test
@@ -16,6 +17,7 @@ class Test
 
     public function test()
     {
-
+        $calcer = new LuckyCalc;
+        $calcer->calc();
     }
 }

+ 10 - 0
app/models/system/SystemAwardHistory.php

@@ -0,0 +1,10 @@
+<?php
+namespace app\models\system;
+
+use crmeb\traits\ModelTrait;
+use crmeb\basic\BaseModel;
+
+class SystemAwardHistory extends BaseModel {
+
+    use ModelTrait;
+}

+ 4 - 4
config/activity.php

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

+ 192 - 4
crmeb/services/async/ActivityCalc.php

@@ -1,6 +1,14 @@
 <?php
+namespace crmeb\services\async;
 
+use app\admin\model\user\User;
+use app\admin\model\user\UserBill;
+use app\models\store\StoreOrder;
 use app\models\store\StoreOrderCartInfo;
+use app\models\system\SystemAwardHistory;
+use app\models\user\UserNotice;
+use crmeb\basic\BaseModel;
+use think\facade\Log;
 
 abstract class ActivityCalc {
     protected $ctx = [];
@@ -13,7 +21,7 @@ abstract class ActivityCalc {
      * @param $cate_id
      * @return array
      */
-    public static function getOrders($cate_id) {
+    protected static function getOrders($cate_id) {
         $products = StoreOrderCartInfo::alias('ci')
             ->join('store_order o', 'o.id=ci.oid')
             ->join('store_product p', 'ci.product_id=p.id')
@@ -23,13 +31,193 @@ abstract class ActivityCalc {
             ->where('o.refund_status', 0)
             ->where('o.is_del', 0)
             ->where('o.is_system_del', 0)
-            ->where('p.cart_id', $cate_id)
+            ->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();
         $products = $products ? $products->toArray() : [];
         return $products;
     }
 
-    abstract public function getId();
-    abstract public function getName();
+    public function calc() {
+        $cate_id = $this->getId();
+        $products = self::getOrders($cate_id);
+        if (count($products) <= 1) {
+            Log::warning('not enough order, stop activity:' . $this->getNameCN());
+            return;
+        }
+
+        // 按商品处理订单
+        $counter = 0;
+        $left = [];
+        $right = [];
+        $left_spent = 0;
+        $right_spent = 0;
+        $left_cost = 0;
+        $right_cost = 0;
+
+        shuffle($products);
+        shuffle($products);
+
+        // 标记有多件商品的订单, 再次统计到属于某个订单的商品时, 订单会重复
+        $orders = [];
+        foreach($products as &$p) {
+            $counter += 1;
+
+            $ci = json_decode($p['cart_info'], true);
+            $num = $ci['cart_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);
+            // orderId
+            $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 ($pcost <= 0.1) {
+                Log::warning("impossible cost, order: ". $p['id']);
+                continue;
+            }
+
+            // 按一个规则, 把商品分为两份, 其中一份中奖
+            if ($this->leaningJudge($counter, $p, $attrInfo)) {
+                $left[] = $p;
+                $left_spent = bcadd($left_spent, $ppaid, 2);
+                $left_cost = bcadd($left_cost, $pcost, 2);
+            } else {
+                $right[] = $p;
+                $right_spent = bcadd($right_spent, $ppaid, 2);
+                $right_cost = bcadd($right_cost, $pcost, 2);
+            }
+        } // foreach
+
+        // 利润
+        $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;
+        $winners = $left;
+        $losers = $right;
+        if ($left_profit > $right_profit) {
+            $winners = $right;
+            $losers = $left;
+        }
+
+        $result = $this->getResult($winners == $left);
+        // 保存开奖结果
+        SystemAwardHistory::create([
+            'activity' => $this->getName(),
+            'result' => $result,
+            'ts' => time(),
+            'order_num' => count($left) + count($right),
+            'winner_num' => count($winners),
+            'total_paid' => $left_spent + $right_spent,
+            'diff_paid' => $diff_money,
+            'profit' => $profit,
+            'rate' => $this->repRate(),
+        ]);
+
+        // 结果处置
+        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);
+        }
+
+        Log::warning('activity ' . $this->getNameCN() . ' calc finished. result:');
+    }
+
+    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), $this->repRate(), 2);
+        }
+        // 标记商品为已参与活动
+        $r1 = StoreOrderCartInfo::where('oid', $product['id'])
+            ->where('product_id', $product['product_id'])
+            ->update([
+                'activity' => $this->getName(),
+                'result' => $result,
+                'reparation' => $reparation,
+            ]);
+
+        if (!$win) {
+            BaseModel::checkTrans($r1);
+            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('活动' . $this->getNameCN(), $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) {
+            // 中奖用户发送中奖消息.
+            UserNotice::sendNoticeTo($product['uid'], '您的订单已退款',
+                '您好,由于活动商品库存有限,您的订单 ' . $product['store_name']
+                . ' 未能成功分配. 该商品付款' . $product['paid'] . '元已如数退还, 并赔付您' . $reparation
+                . '元作为补偿, 对您造成的不便我们深感抱歉. 同时感谢您对美天旺的喜爱和信任, 美天旺活动场永远等您. 顺祝生活愉快.');
+        }
+    }
+
+    // 活动所属的 $categoryId
+    abstract protected function getId();
+
+    // 活动英文名, 长度 <8
+    abstract protected function getName();
+
+    // 活动中文名
+    abstract protected function getNameCN();
+
+    // 赔款所占商品利润的百分比
+    abstract protected function repRate();
+
+    // 获得开奖结果 int
+    abstract protected function getResult($leftwin=true);
+    /**
+     * 根据此函数返回值来划分 getOrders() 返回的订单为两份, 其中一份中奖, 当本函数返回 true, $product 加入"左边"
+     *
+     * @param $index: $product 所在序号, 从 1 开始
+     * @param $product: getOrders() 返回列表中的单个元素
+     * @param $attr: 商品属性
+     * @return boolean true 加入左边
+     */
+    abstract protected function leaningJudge($index, $product, $attr);
 }

+ 19 - 141
crmeb/services/async/ClearanceCalc.php

@@ -1,15 +1,8 @@
 <?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;
-use ActivityCalc;
+use crmeb\services\async\ActivityCalc;
 
 /**
  * 抢清仓活动
@@ -23,148 +16,33 @@ use ActivityCalc;
  * Class ClearanceCalc
  * @package crmeb\services\async
  */
-class ClearanceCalc {
+class ClearanceCalc extends ActivityCalc{
 
     // 活动代号
     protected static $NAME = 'clr';
 
-    public function calc() {
-        $clearance_cate_id = Config::get('activity.clearance_cate_id');
-        // 找到清仓场次中未参加活动的订单
-        $products = ActivityCalc::getOrders($clearance_cate_id);
-        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;
-        }
-
-        // 结果处置
-        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);
-        }
+    protected function getId() {
+        return Config::get('activity.clearance_cate_id');
     }
 
-    /**
-     * 奖励分配
-     *
-     * @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,
-            ]);
+    protected function getName() {
+        return self::$NAME;
+    }
 
-        if (!$win) {
-            return;
-        }
+    protected function getNameCN() {
+        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');
+    protected function repRate() {
+        return 0.2;
+    }
 
-        $ok = $r1 && $r2 && $r3 && $r4;
-        BaseModel::checkTrans($ok);
+    protected function getResult($leftwin=true) {
+        return 0;
+    }
 
-        if ($ok) {
-            // 中奖用户发送中奖消息.
-            UserNotice::sendNoticeTo($product['uid'], '您的订单已退款',
-                '您好,由于活动商品库存有限,您的订单 ' . $product['store_name']
-                . ' 未能成功分配. 该商品付款' . $product['paid'] . '元已如数退还, 并赔付您' . $reparation
-                . '元作为补偿, 对您造成的不便我们深感抱歉. 同时感谢您对美天旺的喜爱和信任, 美天旺活动场将永远等您. 顺祝生活愉快.');
-        }
+    protected function leaningJudge($index, $product, $attr) {
+        return $index % 2 != 0;
     }
+
 }

+ 35 - 150
crmeb/services/async/LuckyCalc.php

@@ -1,173 +1,58 @@
 <?php
 namespace crmeb\services\async;
 
-use app\admin\model\user\User;
-use app\admin\model\user\UserBill;
-use app\models\store\StoreOrder;
-use app\models\store\StoreOrderCartInfo;
-use app\models\user\UserNotice;
-use crmeb\basic\BaseModel;
+use crmeb\services\async\ActivityCalc;
+use app\models\system\SystemAwardHistory;
 use think\facade\Config;
-use think\facade\Log;
-use ActivityCalc;
 
-class LuckyCalc {
+class LuckyCalc extends ActivityCalc{
 
     protected static $NAME = 'lucky';
 
-    private function searchActiveInSuk($suk) {
-        $parts = explode(",", $suk);
-        if (count($parts) <= 0) {
-            return false;
-        }
-        foreach ($parts as $part) {
-            $t = trim($part);
-            if ($t == 20 || $t == 21) {
-                return $t;
-            }
-        }
-
-        return false;
+    protected function getId() {
+        return Config::get('activity.lucky_cate_id');
     }
 
-    public function calc() {
-        $lucky_cate_id = Config::get('activity.lucky_cate_id');
-        $products = ActivityCalc::getOrders($lucky_cate_id);
-
-        if (count($products) <= 1) {
-            Log::info('not enough order, stop lucky activity');
-            return;
-        }
-
-        // split win & lose
-        shuffle($products);
-        shuffle($products);
-
-        $left = [];
-        $right = [];
-        $left_spent = 0.0;
-        $right_spent = 0.0;
-
-        $orders = [];
-        foreach ($products as &$p) {
-            $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;
+    protected function getName() {
+        return self::$NAME;
+    }
 
-            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;
-            }
-            $suk = isset($attrInfo['suk']) ? $attrInfo['suk'] : 'any';
-            $flag = $this->searchActiveInSuk($suk);
+    protected function getNameCN() {
+        return '幸运2021';
+    }
 
-            if ($flag == 20) {
-                $left[] = $p;
-                $left_spent = bcadd($left_spent, $ppaid, 2);
-            } else if ($flag == 21) {
-                $right[] = $p;
-                $right_spent = bcadd($right_spent, $ppaid, 2);
-            } else {
-                Log::error('cant find suk in lucky field. id=' . $oid);
-                continue;
-            }
-        }// foreach
+    protected function repRate() {
+        return 0.2;
+    }
 
-        $products = [];
+    protected function getResult($leftwin=true) {
+        return $leftwin ? 20 : 21;
+    }
 
-        $diff_money = bcsub($left_spent, $right_spent, 2);
-        if ($diff_money < 0.0) {
-            $diff_money = -$diff_money;
-        }
-        $open = 20;
-        $winners = $left;
-        $losers = $right;
-        if ($left_spent > $right_spent) {
-            $open = 21;
-            $winners = $right;
-            $losers = $left;
-        }
+    protected function leaningJudge($index, $product, $attr) {
+        $suk = isset($attr['suk']) ? $attr['suk'] : 'any';
+        $flag = $this->searchActiveInSuk($suk);
 
-        // 结果处置
-        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);
-        }
-    } // calc
+        return $flag == 20;
+    }
 
     /**
-     * 奖励分配
-     *
-     * @param $product  获奖订单中的商品
-     * @param bool $single  是否是单种商品构成的订单
-     * @param bool $win 是否中奖
+     * 从商品的 sku 信息中找到用户选择的 20/21
+     * @param $suk
+     * @return bool|string
      */
-    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;
+    private function searchActiveInSuk($suk) {
+        $parts = explode(",", $suk);
+        if (count($parts) <= 0) {
+            return false;
         }
-
-        $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,
-            ]);
+        foreach ($parts as $part) {
+            $t = trim($part);
+            if ($t == 20 || $t == 21) {
+                return $t;
+            }
         }
-        // 中奖的 退款并赔款返回到佣金
-        $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) {
-            // 中奖用户发送中奖消息.
-            UserNotice::sendNoticeTo($product['uid'], '您的订单已退款',
-                '您好,由于活动商品库存有限,您的订单 ' . $product['store_name']
-                . ' 未能成功分配. 该商品付款' . $product['paid'] . '元已如数退还, 并赔付您' . $reparation
-                . '元作为补偿, 对您造成的不便我们深感抱歉. 同时感谢您对美天旺的喜爱和信任, 美天旺活动场将永远为您守候. 顺祝生活愉快.');
-        }
+        return false;
     }
 }

+ 4 - 1
public/install/crmeb.sql

@@ -7818,7 +7818,10 @@ CREATE TABLE `eb_system_award_history` (
     `ts` int(11) unsigned NOT NULL COMMENT '时间戳',
     `order_num` int(10) NOT NULL COMMENT '订单总数',
     `winner_num` int(10) NOT NULL COMMENT '获奖订单数',
-    `earn` decimal(8,2) NOT NULL COMMENT '盈利金额',
+    `total_paid` decimal(8,2) NOT NULL COMMENT '参与订单总金额',
+    `profit` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '本次盈利',
+    `diff_paid` decimal(8,2) unsigned NOT NULL COMMENT '双方订单差值',
+    `rate` decimal(8,2) unsigned NOT NULL COMMENT '赔率',
     PRIMARY KEY (`id`) USING BTREE,
     KEY `activity` (`activity`) USING BTREE,
     KEY `ts` (`ts`) USING BTREE