$page]); $backgroundImgs = isset($setting['background_imgs']) && $setting['background_imgs'] ? json_decode($setting['background_imgs'], true) : ''; $list['background_imgs'] = $backgroundImgs; $list['oppty'] = $setting->oppty > self::$log ? $setting->oppty - self::$log : 0; $list['deplete_integral_num'] = $setting->deplete_integral_num; //积分检测 $walletData = UserWallet::findOne(['user_id' => $userData->id]); $list['user_score'] = $walletData->score ?? 0; return $list; } catch (\ErrorException $errorException) { throw new \ErrorException($errorException->getMessage()); } catch (\Exception $exception) { throw new \Exception($exception->getMessage()); } } /** * @param $mallId * @param $userData * @return array * @throws \ErrorException */ public static function getData($mallId, $userData) { try { $userId = $userData->id ?? 0; $setting = CommonScratchLogic::getSetting($mallId); if (empty($setting)) { throw new \ErrorException('规则尚未配置'); } //记录数 self::$log = self::getLogCount($setting, $userData); $list['background_imgs'] = isset($setting['background_imgs']) && $setting['background_imgs'] ? json_decode($setting['background_imgs'], true) : ''; $list['oppty'] = $setting->oppty > self::$log ? $setting->oppty - self::$log : 0; $list['deplete_integral_num'] = $setting->deplete_integral_num; //积分检测 $walletData = UserWallet::findOne(['user_id' => $userId]); $list['user_score'] = $walletData->score ?? 0; return $list; } catch (\ErrorException $errorException) { throw new \ErrorException($errorException->getMessage()); } catch (\Exception $exception) { throw new \Exception($exception->getMessage()); } } /** * @param $mallId * @param $userData * @param int $page * @return array|false|mixed|void * @throws \ErrorException */ public static function getLogList($mallId, $userData, $page = 1) { try { $params = [ 'with' => [ 'user' => function ($q) { $q->select('id,nickname,avatar_url'); }, 'goods' => function ($q) { $q->select('id,goods_name,price'); }, 'coupon' => function ($q) { $q->select('id,name,sub_price'); }, ], 'where' => [ ['=', 'mall_id', $mallId], ['=', 'user_id', $userData->id], ], 'page' => $postData['page'] ?? 1, 'limit' => $postData['limit'] ?? 10, 'order' => $postData['order'] ?? 'id desc', ]; if (!empty($postData['type'])) { $params['where'][] = ['=', 'type', $postData['type']]; } else { $params['where'][] = ['in', 'type', [1, 2, 3, 4, 5]]; } $pageData = ScratchLogModel::listPage($params, false); if ($pageData === false) throw new \ErrorException(ScratchModel::getStaticError()); $returnData = $data = []; foreach ($pageData['list'] as $val) { $data["goods"] = null; if ($val["type"] == ScratchModel::TYPE_GOODS) { $data['goods'] = $val['goods']; $data['goods']['attr_id'] = $val['attr_id']; $name = $val['goods']['goods_name'] ?? ''; $title = "您已刮中商品" . $name . "奖励"; } else if ($val["type"] == ScratchModel::TYPE_COUPON) { $name = $val['coupon']['name'] ?? ''; $val['price'] = $val['coupon']['sub_price'] ?? 0; $title = "您已刮中优惠券 " . $name . " 奖励"; } else { $name = CommonScratchLogic::getNewName($val); $title = "您已刮中" . $name . "奖励"; } $goodsId = $val["goods_id"]; $data['id'] = $val['id']; $data['status'] = $val['status']; $data["scratch_id"] = $val["scratch_id"]; $data["goods_id"] = $goodsId; $data['name'] = $name; $data['title'] = $title; $data["type"] = intval($val["type"]); $data["price"] = floatval($val["price"]); $data["raffled_at"] = !empty($val["raffled_at"]) ? date("Y-m-d H:i:s", $val["raffled_at"]) : date("Y-m-d H:i:s", $val["created_at"]); $returnData["list"][] = $data; } $returnData["pagination"] = $pageData['pagination']; $returnData["page_count"] = $pageData['page_count']; return $returnData; } catch (\ErrorException $errorException) { throw new \ErrorException($errorException->getMessage()); } catch (\Exception $exception) { throw new \Exception($exception->getMessage()); } } /** * @param $id * @return array|false|mixed|void * @throws \ErrorException */ public static function getEdit($id, $mallId) { try { } catch (\ErrorException $errorException) { throw new \ErrorException($errorException->getMessage()); } catch (\Exception $exception) { throw new \Exception($exception->getMessage()); } } /** * @param $setting * @param $userData * @throws \Exception */ private static function validated($setting, $userData) { //记录数 self::$log = self::getLogCount($setting, $userData); //时间检测 配置信息 if ($setting->start_at > date('Y-m-d H:i:s') || $setting->end_at < date('Y-m-d H:i:s')) { throw new \Exception('活动未开始或已过期'); } //积分检测 if ($setting->deplete_integral_num > 0) { $walletData = UserWallet::findOne(['user_id' => $userData->id]); $integral = $walletData->score ?? 0; if ($integral < $setting->deplete_integral_num) { $score_name = \Yii::$app->services->setting->mall->get($setting->mall_id, 'score.score_name') ?? '积分'; throw new \Exception($score_name.'不足'); } } //机会检测 if (self::$log >= $setting->oppty) { throw new \Exception("抽奖机会不足"); } } /** * @param $setting * @param $userData * @throws \Exception */ private static function getLogCount($setting, $userData) { $userId = $userData->id ?? 0; $query = ScratchLogModel::find()->where([ 'AND', ['mall_id' => $setting->mall_id], ['user_id' => $userId], // ['<>', 'status', 0] ]); $todayStart = strtotime(date("Y-m-d 00:00:00")); $todayEnd = strtotime(date("Y-m-d 23:59:59")); if ($setting->type == ScratchSettingModel::TYPE_DAY) { $query->andWhere([ 'AND', ['>=', 'created_at', $todayStart], ['<=', 'created_at', $todayEnd], ]); } elseif ($setting->type == ScratchSettingModel::TYPE_USER) { $query->andWhere([ 'AND', ['>=', 'created_at', strtotime($setting->start_at)], ['<=', 'created_at', strtotime($setting->end_at)], ]); } //记录数 $count = $query->count(); return !empty($count) ? $count : 0; } /** * @param $item * @return bool */ private static function verify($item) { if ($item->type == ScratchModel::TYPE_COUPON) { $coupon = ScratchCouponModel::find() ->where(['id' => $item->coupon_id, 'status' => ScratchEnum::ENABLED]) ->select(['id', 'coupon_status', 'status']) ->one(); if (empty($coupon)) { return false; } //优惠券状态,1:未开始,2:领取中,3:已过期,4:已失效(后台操作失效) if ($coupon->coupon_status > 2) { return false; } } if ($item->type == ScratchModel::TYPE_GOODS) { $goodsAttr = GoodsAttr::find() ->where(['id' => $item->attr_id, 'status' => 1]) ->select(['id', 'goods_id', 'stock']) ->one(); if (!$goodsAttr) { return false; } //库存不足 if ($goodsAttr->stock <= 0) { return false; } } return true; } /** * 中奖 * @param $setting * @param $userData * @return array|array[]|object|object[]|string[] * @throws \yii\db\Exception */ private static function lottery($setting, $userData) { $scratch = ScratchModel::findAll([ 'mall_id' => $setting['mall_id'], 'status' => ScratchEnum::ENABLED ]); $suc = []; foreach ($scratch as $v) { if ($v->stock > 0 && self::verify($v)) { $suc[$v->id] = $v->stock; } } $max = array_sum($suc); $rand = self::randomNum(1, 10000); if ($rand < $setting->probability && $max > 0) { $id = self::getRand($suc); /* @var Scratch $scratch */ $scratch = ScratchModel::find() ->where([ 'mall_id' => $userData->mall_id, 'id' => $id, 'status' => ScratchEnum::ENABLED, ]) ->with("coupon") ->with("goods") ->one(); $price = floatval($scratch->price); if ($scratch->type == ScratchModel::TYPE_BALANCE) { $price = floatval($scratch->balance); } $scratchLog = new ScratchLogModel(); $scratchLog->mall_id = $userData->mall_id; $scratchLog->user_id = $userData->id; $scratchLog->scratch_id = $scratch->id; $scratchLog->status = ScratchLogModel::STATUS_PRE; $scratchLog->type = $scratch->type; $scratchLog->num = !empty($scratch->num) ? intval($scratch->num) : 1; $scratchLog->price = $price; $scratchLog->detail = ''; if ($scratchLog->type == ScratchModel::TYPE_GOODS) { $scratchLog->goods_id = $scratch->goods->id; $scratchLog->attr_id = $scratch->attr_id; $scratchLog->price = $scratch->goods->price; } elseif ($scratchLog->type == ScratchModel::TYPE_COUPON) { $scratchLog->coupon_id = $scratch->coupon_id; } $t = \Yii::$app->db->beginTransaction(); $sql = 'select * from ' . ScratchModel::tableName() . ' where mall_id = ' . $userData->mall_id . ' and id = ' . $id . ' and status = ' . ScratchModel::STATUS_ON . ' for update'; $form = \Yii::$app->db->createCommand($sql)->queryOne(); if ($form['stock'] > 0) { $scratch->stock = $form['stock'] - 1; if (!$scratch->save()) { $t->rollBack(); throw new \Exception($scratch->getErrorMessage()); } } else { $scratchLog->type = ScratchModel::TYPE_THANKS; $scratchLog->detail = ''; $scratchLog->num = 0; $scratchLog->price = 0; } if ($scratchLog->save()) { $t->commit(); $scratchLog = \yii\helpers\ArrayHelper::toArray($scratchLog); if ($scratchLog["type"] == ScratchModel::TYPE_GOODS) { $goodsData = CommonScratchLogic::getNewName($scratchLog, "end"); $name = $goodsData["name"]; $pic = $goodsData["pic"]; } else { $name = CommonScratchLogic::getNewName1($scratch->mall_id, $scratch); $pic = $scratch->pic; } $scratchLog['name'] = $name; $scratchLog['pic'] = $pic; return $scratchLog; } else { $t->rollBack(); throw new \Exception($scratchLog->getErrorMessage()); } } else { $scratchLog = new ScratchLogModel(); $scratchLog->mall_id = $userData->mall_id; $scratchLog->user_id = $userData->id; $scratchLog->scratch_id = 0; $scratchLog->detail = ''; $scratchLog->status = 0; $scratchLog->price = 0; $scratchLog->type = ScratchModel::TYPE_THANKS; if ($scratchLog->save()) { $scratchLog = \yii\helpers\ArrayHelper::toArray($scratchLog); $scratchLog['name'] = CommonScratchLogic::getNewName($scratchLog); $scratchLog['pic'] = ""; return $scratchLog; } else { throw new \Exception($scratchLog->getErrorMessage()); } } } /** * @return array|bool * @throws \Exception */ public static function toReceive($userData, $id) { $setting = CommonScratchLogic::getSetting($userData->mall_id); if (!$setting) { throw new \Exception('规则尚未配置'); } self::validated($setting, $userData); $scratchLog = ScratchLogModel::findOne([ 'mall_id' => $userData->mall_id, 'user_id' => $userData->id, 'status' => 1, 'id' => $id ]); return self::receive($setting, $scratchLog, $userData); } /** * 领取 * @param $setting * @param $scratchLog * @param $userData * @return array|bool */ public static function receive($setting, $scratchLog, $userData) { try { $scratchLog = ScratchLogModel::find()->where(['id' => $scratchLog['id'] ?? 0])->one(); if (empty($scratchLog)) { throw new \Exception('数据异常'); } //扣积分 if ($setting->deplete_integral_num > 0) { $desc = '刮刮卡消耗' . (int)$setting->deplete_integral_num . '积分'; $insert_wallet [] = [ 'mall_id' => $userData->mall_id, 'user_id' => $userData->id, 'is_frozen' => false, 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE, 'money' => -(int)$setting->deplete_integral_num, 'desc' => $desc, 'source_table' => 'addons_scratch_log', 'source_table_id' => $scratchLog->id, 'from_type' => UserWalletEnum::CONSUME, 'capital_type' => UserWalletEnum::DEDUCT, ]; $res = UserWalletService::batchHandleByQueue($insert_wallet); if ($res === false) { throw new \Exception(UserWalletService::getStaticError()); }; } $scratchLog->status = ScratchLogModel::STATUS_NO_RECEIVED; if ($scratchLog->save()) { //兑奖 return self::send($scratchLog); } else { throw new \Exception($scratchLog->getErrorMessage()); } } catch (\Exception $e) { throw new \Exception($e->getMessage()); } } /** * 领取 * @param $scratchLog * @return array|bool * @throws \yii\db\Exception */ public static function send($scratchLog) { $status = ScratchLogModel::STATUS_RECEIVED; $raffled_at = time(); //红包 if ($scratchLog->type == ScratchModel::TYPE_REDPACKET) { throw new \Exception('红包领取失败,该功能还未上线'); $money = (float)$scratchLog->price; if ($money <= 0) { return true; } $t = \Yii::$app->db->beginTransaction(); $desc = '刮刮卡获赠' . $money . '元'; //状态 $scratchLog->status = $status; $scratchLog->raffled_at = $raffled_at; if ($scratchLog->save()) { $currency = RedPacketUser::getCurrency(); $res = CurrencyWallet::addLog($currency, \Yii::$app->user->id, \Yii::$app->mall->id, $money, $desc, 'scratch_give', 'scratch_log', $scratchLog->id, false); if ($res === false) { $t->rollBack(); throw new \Exception($scratchLog->getErrorMessage()); } $t->commit(); return [ 'code' => 0, 'msg' => '红包领取成功', 'data' => $scratchLog->id ]; } else { $t->rollBack(); throw new \Exception($scratchLog->getErrorMessage()); } } //余额 if ($scratchLog->type == ScratchModel::TYPE_BALANCE) { if (floatval($scratchLog->price) <= 0) { return true; } $t = \Yii::$app->db->beginTransaction(); $desc = '刮刮卡获赠' . (float)$scratchLog->price . '元'; $insert_wallet [] = [ 'mall_id' => $scratchLog->mall_id, 'user_id' => $scratchLog->user_id, 'is_frozen' => false, 'wallet_type' => UserWalletService::WALLET_TYPE_BALANCE, 'money' => (float)$scratchLog->price, 'desc' => $desc, 'source_table' => 'addons_scratch_log', 'source_table_id' => $scratchLog->id, 'from_type' => UserWalletEnum::REWARD, 'capital_type' => UserWalletEnum::GIVE, ]; $res = UserWalletService::batchHandleByQueue($insert_wallet); if ($res === false) { throw new \Exception(UserWalletService::getStaticError()); }; //状态 $scratchLog->status = $status; $scratchLog->raffled_at = $raffled_at; if ($scratchLog->save()) { $t->commit(); return [ 'code' => 0, 'msg' => '余额领取成功', 'data' => $scratchLog->id ]; } else { $t->rollBack(); throw new \Exception($scratchLog->getErrorMessage()); } } //优惠券 if ($scratchLog->type == ScratchModel::TYPE_COUPON) { $t = \Yii::$app->db->beginTransaction(); // 赠送优惠券 $give_coupon_arr = AddonsCouponUserRelate::giveCoupon( $scratchLog->mall_id, $scratchLog->user_id, [$scratchLog->coupon_id => $scratchLog->num], AddonsCouponEnum::SEND_FROM10 ); $userRelate = AddonsCouponUserRelate::find() ->where([ 'mall_id' => $scratchLog->mall_id, 'user_id' => $scratchLog->user_id, 'coupon_id' => $scratchLog->coupon_id, ]) ->asArray() ->orderBy('id desc') ->select('id') ->one(); //添加会员领取优惠券日志 CouponRelationModel::setData([ 'mall_id' => $scratchLog->mall_id, 'user_id' => $scratchLog->user_id, 'scratch_log_id' => $scratchLog->id, 'user_coupon_id' => $userRelate['id'] ?? 0 ]); //状态 $scratchLog->status = $status; $scratchLog->raffled_at = $raffled_at; if ($scratchLog->save()) { $t->commit(); return [ 'code' => 0, 'msg' => '优惠券领取成功', 'data' => $scratchLog->id ]; } else { $t->rollBack(); throw new \Exception($scratchLog->getErrorMessage()); } } //积分 if ($scratchLog->type == ScratchModel::TYPE_SCORE) { $t = \Yii::$app->db->beginTransaction(); $desc = '刮刮卡获赠' . (int)$scratchLog->num . '积分'; $insert_wallet [] = [ 'mall_id' => $scratchLog->mall_id, 'user_id' => $scratchLog->user_id, 'is_frozen' => false, 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE, 'money' => (int)$scratchLog->num, 'desc' => $desc, 'source_table' => 'addons_scratch_log', 'source_table_id' => $scratchLog->id, 'from_type' => UserWalletEnum::REWARD, 'capital_type' => UserWalletEnum::GIVE, ]; $res = UserWalletService::batchHandleByQueue($insert_wallet); if ($res === false) { throw new \Exception(UserWalletService::getStaticError()); }; //状态 $scratchLog->status = $status; $scratchLog->raffled_at = $raffled_at; if ($scratchLog->save()) { $t->commit(); return [ 'code' => 0, 'msg' => '积分领取成功', 'data' => $scratchLog->id ]; } else { $t->rollBack(); throw new \Exception($scratchLog->getErrorMessage()); } } if ($scratchLog->type == ScratchModel::TYPE_GOODS) { return [ 'code' => 0, 'msg' => '商品领取成功', 'data' => $scratchLog->id ]; } if ($scratchLog->type == ScratchModel::TYPE_THANKS) { $scratchLog->status = $status; $scratchLog->raffled_at = $raffled_at; $scratchLog->save(); return [ 'code' => 0, 'msg' => '领取成功', 'data' => $scratchLog->id ]; } } /** * @param $min * @param $max * @return int */ private static function randomNum($min, $max) { return mt_rand() % ($max - $min + 1) - $min; } private static function getRand($probability) { $max = array_sum($probability); foreach ($probability as $key => $val) { $rand_number = self::randomNum(1, $max); if ($rand_number <= $val) { return $key; } else { $max -= $val; } } } /** * 刮卡 * @param $mallId * @param $userData * @return mixed * @throws \ErrorException */ public static function scratchCard($mallId, $userData) { try { $setting = CommonScratchLogic::getSetting($mallId); if (empty($setting)) { throw new \ErrorException('规则尚未配置'); } /** * 验证数据 */ self::validated($setting, $userData); /** * 抽奖算法 */ $scratchLog = self::lottery($setting, $userData); /** * 领取 */ self::receive($setting, $scratchLog, $userData); $pic = ""; $id = $scratchLog["scratch_id"]; $scratch = ScratchModel::findOne($id); if (!empty($scratch)) { $pic = $scratch->pic; } $scratchLog["pic"] = $pic; $scratchLog["price"] = floatval($scratchLog["price"]); $scratchLog["created_at"] = date("Y-m-d H:i:s", $scratchLog["created_at"]); return [ 'list' => $scratchLog, ]; } catch (\Exception $ex) { \Yii::error("scratchForm errorMessage=" . $ex->getFile() . ";LINE:" . $ex->getLine() . ";message:" . $ex->getMessage()); throw new \Exception($ex->getMessage()); } } }