flush(true); echo ' 自动更新优惠券状态任务开始执行 ... ' . PHP_EOL; // 获取当前系统所有可用的 mall_id $malls = Mall::getEnableMallList(); $mall_ids = array_column($malls, 'id'); echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL; foreach ($mall_ids as $mall_id) { echo '开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL; $obj = new AddonConfig(); $name = ($obj->info)['name']; $is_install = MallAddons::isInstall($mall_id, $name); if (!$is_install) return ExitCode::OK; // 更新优惠券主表 $lock_name_main = 'qimall:update_coupon_status:main:mall_id:' . $mall_id; $lock_val_main = md5($lock_name_main); $lock_main = LockHelper::lock($lock_name_main, $lock_val_main, 60); $lock_main && self::updateMainCoupons($mall_id); LockHelper::uLock($lock_name_main, $lock_val_main); // 更新会员已经领取的优惠券的状态 $lock_name_user = 'qimall:update_coupon_status:user:mall_id:' . $mall_id; $lock_val_user = md5($lock_name_user); $lock_user = LockHelper::lock($lock_name_user, $lock_val_user, 60); $lock_user && self::updateUserCoupons($mall_id); LockHelper::uLock($lock_name_user, $lock_val_user); } echo ' 自动更新优惠券状态任务执行完毕 ... ' . PHP_EOL; return ExitCode::OK; } catch (\Exception $e) { LockHelper::uLock($lock_name_main, $lock_val_main); LockHelper::uLock($lock_name_user, $lock_val_user); $errmsg = ' 自动更新优惠券状态任务执行出错, errmsg => ' . $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine(); echo $errmsg . PHP_EOL; \Yii::error($errmsg);\Yii::getLogger()->flush(true); return false; } } /** * 优惠券相关统计,定时器调用执行 * * 3 * * * * */ public function actionCouponStatistics() { \Yii::error('优惠券相关统计任务开始执行 ... ');\Yii::getLogger()->flush(true); try { echo ' 优惠券相关统计任务开始执行 ... ' . PHP_EOL; // 获取当前系统所有可用的 mall_id $malls = Mall::getEnableMallList(); $mall_ids = array_column($malls, 'id'); echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL; foreach ($mall_ids as $mall_id) { echo '开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL; // $mall_id = 5; $obj = new AddonConfig(); $name = ($obj->info)['name']; $is_install = MallAddons::isInstall($mall_id, $name); if (!$is_install) return ExitCode::OK; $lock_name = 'qimall:coupon_statistics:mall_id:' . $mall_id; $lock_val = md5($lock_name); $lock = LockHelper::lock($lock_name, $lock_val, 60); if ($lock) { // 优惠券相关数量 self::statNumber($mall_id); // 订单使用相关统计 self::statOrder($mall_id); // 计算优惠券使用率 // self::calUseProbability($mall_id); } LockHelper::uLock($lock_name, $lock_val); } echo ' 优惠券相关统计任务执行完毕' . PHP_EOL; return ExitCode::OK; } catch (\Exception $e) { LockHelper::uLock($lock_name, $lock_val); $errmsg = $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine(); \Yii::error($errmsg);\Yii::getLogger()->flush(true); return false; } } /** * 统计商品使用优惠券相关数据,定时器调用执行 * * 3 * * * * */ public function actionCouponGoodStat() { \Yii::error('统计商品使用优惠券相关数据任务开始执行 ...');\Yii::getLogger()->flush(true); try { echo ' 统计商品使用优惠券相关数据任务开始执行 ... ' . PHP_EOL; // 获取当前系统所有可用的 mall_id $malls = Mall::getEnableMallList(); $mall_ids = array_column($malls, 'id'); echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL; foreach ($mall_ids as $mall_id) { echo ' 开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL; // $mall_id = 5; $obj = new AddonConfig(); $name = ($obj->info)['name']; $is_install = MallAddons::isInstall($mall_id, $name); if (!$is_install) return ExitCode::OK; $lock_name = 'qimall:coupon_good_stat:mall_id:' . $mall_id; $lock_val = md5($lock_name); $lock = LockHelper::lock($lock_name, $lock_val, 60); if ($lock) { // // 统计 某商品使用该优惠券下单的总数量 // self::statBookNumber($mall_id, 'book_number', 'booking_number'); // // 统计 使用该优惠券已支付的该商品数量 // self::statBookNumber($mall_id, 'payed_number', 'payed_number'); // // 统计 该商品使用该优惠券下单的总人数 // self::statBookNumber($mall_id, 'user_number', 'booking_user_number'); // // 统计 使用该优惠券已购买该商品人数 // self::statBookNumber($mall_id, 'buyed_number', 'buyed_user_number'); // 统计使用优惠券购买商品相关数量 self::statBookNumber($mall_id); } else { echo ' 获取锁失败' . PHP_EOL; } LockHelper::uLock($lock_name, $lock_val); } echo ' 统计商品使用优惠券相关数据任务执行完毕' . PHP_EOL; return ExitCode::OK; } catch (\Exception $e) { LockHelper::uLock($lock_name, $lock_val); $errmsg = $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine(); \Yii::error($errmsg);\Yii::getLogger()->flush(true); return false; } } /** * 优惠券过期前提醒,定时器调用执行 * * * 4 * * * */ /* public function actionExpireRemind() { try { $url = '/pages/user/coupon/coupon'; $h5_url = \Yii::$app->params['web_url'] . "/h5/#" . $url; // 获取当前系统所有可用的 mall_id $malls = Mall::getEnableMallList(); $mall_ids = array_column($malls, 'id'); echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL; foreach ($mall_ids as $mall_id) { echo '开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL; // $mall_id = 5; $obj = new AddonConfig(); $name = ($obj->info)['name']; $is_install = MallAddons::isInstall($mall_id, $name); if (!$is_install) return ExitCode::OK; $WechatTemplateService = new WechatTemplateService($mall_id); $platform = $WechatTemplateService->getPlatForm(); $reminds = CouponRemind::find() ->alias('cr') ->join('LEFT JOIN', Coupon::tableName() . ' as c', 'cr.coupon_id = c.id') ->select('cr.*,c.remind_days') ->where(['cr.mall_id' => $mall_id, 'status' => 1, 'c.is_remind' => 1]) ->asArray() ->all(); if (!empty($reminds)) { foreach ($reminds as $remind) { $time_node = $remind['remind_days'] * 86400; if ($time_node > $remind['expire_time']) { // 现在到优惠券过期的时间小于三天,发消息提醒 $send_data = [ 'first' => '您已经拉取的优惠券', 'keyword1' => $remind['name'], 'keyword2' => $remind['content'], 'remark' => '即将过期,请尽快使用。' ]; $WechatTemplateService->send($remind['user_id'], WechatTemplateService::TEM_KEY['coupon_expire']['tem_key'], $h5_url, $send_data, $platform,$url); } // 睡眠 1s,防止腾讯封禁 sleep(1); } } } echo ' 发送过期提醒任务执行完毕 ... ' . PHP_EOL; return true; } catch (\Exception $e) { $errmsg = $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine(); \Yii::error($errmsg);\Yii::getLogger()->flush(true); return false; } } */ /** * @name: * @msg: 更新优惠券主表中的状态 * @param {int} $mall_id * @return {*} */ private static function updateMainCoupons(int $mall_id) { $query = AddonsCoupon::find()->where(['mall_id' => $mall_id, 'status' => 1, 'expire_type' => 2])->andWhere(['!=', 'coupon_status', 4]); $curr_time = time(); $receiving_ids = []; $finished_receive_ids = []; foreach ($query->each(500) as $coupon) { if ($coupon->begin_at <= $curr_time && $coupon->end_at > $curr_time) { // 优惠券领取中 $receiving_ids[] = $coupon->id; } elseif ($coupon->end_at <= $curr_time) { $finished_receive_ids[] = $coupon->id; } } if (!empty($receiving_ids)) { $res = AddonsCoupon::updateAll(['coupon_status' => 2], ['id' => $receiving_ids, 'mall_id' => $mall_id]); if ($res === false) { \Yii::error('更新优惠券状态失败:res => ' . var_export($res, true) . ', mall_id => ' . $mall_id . ', receiving_ids => ' . var_export($receiving_ids, true) . ', errmsg => ' . AddonsCoupon::getStaticError()); \Yii::getLogger()->flush(true); } } if (!empty($finished_receive_ids)) { $res = AddonsCoupon::updateAll(['coupon_status' => 3], ['id' => $finished_receive_ids, 'mall_id' => $mall_id]); $res_remaind = AddonsCouponRemind::updateAll(['send_status' => 3], ['coupon_id' => $finished_receive_ids, 'mall_id' => $mall_id]); $res_uc = AddonsCouponUserRelate::updateAll(['coupon_status' => 3], ['coupon_id' => $finished_receive_ids, 'mall_id' => $mall_id]); if ($res === false || $res_remaind === false || $res_uc === false) { \Yii::error('更新优惠券状态失败:res => ' . var_export($res, true) . ', res_remaind => ' . var_export($res_remaind, true) . ', res_uc => ' . var_export($res_uc, true) . ', mall_id => ' . $mall_id . ', finished_receive_ids => ' . var_export($finished_receive_ids, true) . ', ac_errmsg => ' . AddonsCoupon::getStaticCodeError() . ', cr_errmsg => ' . AddonsCouponRemind::getStaticError() . ', cu_errmsg => ' . AddonsCouponUserRelate::getStaticError()); \Yii::getLogger()->flush(true); } } } /** * @name: * @msg: 更新会员已经领取的优惠券的状态 * @param {int} $mall_id * @return {*} */ private static function updateUserCoupons(int $mall_id) { $time = time(); $query = AddonsCouponUserRelate::find() ->where(['mall_id' => $mall_id, 'is_use' => 0, 'status' => 1, 'is_force_failure' => 0, 'coupon_status' => AddonsCouponEnum::STATUS_RECEIVING]) ->andWhere(['or', ['is_giving' => 0], ['is_giving' => 1, 'giving_status' => 1]]) ->andWhere(['<=', 'end_time', $time]); foreach ($query->each(100) as $user_coupon) { $user_coupon->coupon_status = AddonsCouponEnum::STATUS_EXPIRED; $user_coupon->updated_at = $time; $res = $user_coupon->save(); if (!$res) { $errmsg = ' 更新优惠券状态失败:res => ' . var_export($res, true) . ', mall_id => ' . $mall_id . ', id => ' . $user_coupon->id; echo $errmsg . PHP_EOL; \Yii::error($errmsg);\Yii::getLogger()->flush(true); } } } /** * @name: * @msg: 统计商品使用优惠券相关数据 * @param {int} $mall_id * @param {string} $type * @param {string} $field * @return {*} */ private static function statBookNumber(int $mall_id) { $coupon_ids = AddonsCoupon::find()->select('id')->where(['mall_id' => $mall_id, 'status' => 1])->column(); $coupon_use_log = AddonsCouponUseLog::find() ->select('order_id,coupon_id,user_coupon_id') ->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED, 'coupon_id' => $coupon_ids]) ->asArray() ->indexBy('order_id') ->all(); $order_ids = array_column($coupon_use_log, 'order_id'); $query = Order::find() ->alias('or') ->select('or.user_id,or.id,or.pay_status,od.goods_id') ->join('LEFT JOIN', OrderDetail::tableName() . ' as od', 'od.order_id = or.id') ->where(['or.id' => $order_ids, 'or.status' => StatusEnum::ENABLED, 'od.status' => StatusEnum::ENABLED]); $stat_datas = []; foreach ($query->asArray()->each(500) as $order_info) { $coupon_id = $coupon_use_log[$order_info['id']]['coupon_id'] ?? 0; $key = $coupon_id . '_' . $order_info['goods_id']; // 下单数量 if (!isset($stat_datas[$key]['booking_number'])) $stat_datas[$key]['booking_number'] = 0; $stat_datas[$key]['booking_number'] += 1; // 支付数量 if (!isset($stat_datas[$key]['payed_number'])) $stat_datas[$key]['payed_number'] = 0; if ($order_info['pay_status'] == OrderStatusEnum::PAY) $stat_datas[$key]['payed_number'] += 1; // 下单总人数 if (!isset($stat_datas[$key]['booking_user_number'])) $stat_datas[$key]['booking_user_number'] = 0; $stat_datas[$key]['booking_user_number'] += 1; // 购买总人数 if (!isset($stat_datas[$key]['buyed_user_number'])) $stat_datas[$key]['buyed_user_number'] = 0; if ($order_info['pay_status'] == OrderStatusEnum::PAY) $stat_datas[$key]['buyed_user_number'] += 1; } // var_dump($stat_datas); !empty($stat_datas) && self::updateCouponGoodStat($stat_datas, $mall_id); } /** * @name: * @msg: 更新优惠券商品统计表 * @param {array} $data * @param {int} $mall_id * @param {*} $field * @return {*} */ private static function updateCouponGoodStat(array $data, int $mall_id) { if (!empty($data)) { foreach ($data as $key => $dv) { $keys = explode('_', $key); $coupon_good_stat = AddonsCouponGoodStatistics::findOne(['mall_id' => $mall_id, 'coupon_id' => $keys[0], 'good_id' => $keys[1]]); if (empty($coupon_good_stat)) { $coupon_good_stat = new AddonsCouponGoodStatistics(); $data = [ 'mall_id' => $mall_id, 'coupon_id' => $keys[0], 'good_id' => $keys[1], 'created_at' => time(), ]; $coupon_good_stat->attributes = $data; } // dd($coupon_good_stat); foreach ($dv as $dk => $val) { $coupon_good_stat->$dk = $val; } $res = $coupon_good_stat->save(); if (!$res) { $errmsg = ' 插入/更新已领取数量失败, res => ' . $res . ', data => ' . var_export($data, true); echo $errmsg . PHP_EOL; \Yii::error($errmsg);\Yii::getLogger()->flush(true); } } } } /** * @name: * @msg: 统计数量 * @param {int} $mall_id * @param {array} $conditions * @param {string} $field * @return {*} */ private static function statNumber(int $mall_id) { echo ' 统计相关数量 ... ' . PHP_EOL; $used_query = AddonsCouponUserRelate::find()->where(['mall_id' => $mall_id, 'status' => 1]); $user_coupons = []; foreach ($used_query->each(500) as $use_coupon) { $coupon_id = $use_coupon->coupon_id; // 统计已领取数量 if (!isset($user_coupons[$coupon_id]['received_number'])) $user_coupons[$coupon_id]['received_number'] = 0; $user_coupons[$coupon_id]['received_number'] += 1; // 统计已使用数量 if (!isset($user_coupons[$coupon_id]['used_number'])) $user_coupons[$coupon_id]['used_number'] = 0; $use_coupon->is_use == 1 && $user_coupons[$coupon_id]['used_number'] += 1; // 统计优惠券被转赠的次数 if (!isset($user_coupons[$coupon_id]['giveing_times'])) $user_coupons[$coupon_id]['giveing_times'] = 0; $use_coupon->is_giving == 1 && $user_coupons[$coupon_id]['giveing_times'] += 1; // 记录领取会员id $received_user_ids[$coupon_id][] = $use_coupon->user_id; // 记录使用会员id $use_coupon->is_use == 1 && $used_user_ids[$coupon_id][] = $use_coupon->user_id; // 记录转赠优惠券的会员id $use_coupon->is_giving == 1 && $giving_user_ids[$coupon_id][] = $use_coupon->user_id; } // 统计领取会员数量 if (!empty($received_user_ids)) { foreach ($received_user_ids as $coupon_id => $user_ids) { $counts = count(array_flip(array_flip($user_ids))); $user_coupons[$coupon_id]['receive_user_number'] = $counts; } } // 统计用券会员数量 if (!empty($used_user_ids)) { foreach ($used_user_ids as $coupon_id => $user_ids) { $counts = count(array_flip(array_flip($user_ids))); $user_coupons[$coupon_id]['use_user_number'] = $counts; } } // 统计转赠优惠券的会员数量 if (!empty($giving_user_ids)) { foreach ($giving_user_ids as $coupon_id => $user_ids) { $counts = count(array_flip(array_flip($user_ids))); $user_coupons[$coupon_id]['giving_user_number'] = $counts; } } !empty($user_coupons) && self::updateCouponStat($user_coupons, $mall_id); echo ' 统计相关数量完成 ... ' . PHP_EOL; } /** * @name: * @msg: 统计订单相关数据 * @param int $mall_id * @param string $type 统计类型,total_discount:累计优惠总金额;total_number:使用优惠券支付的订单总数量;total_real_payed:使用优惠券后实际支付的总金额;total_payed:使用优惠券支付的订单原价总金额 * @param string $field * @return {*} */ private static function statOrder($mall_id) { echo ' 统计订单相关数据 ... ' . PHP_EOL; $coupon_ids = AddonsCoupon::find()->select('id')->where(['mall_id' => $mall_id, 'status' => 1])->column(); // var_dump('coupon_ids:', $coupon_ids); $coupon_use_log = AddonsCouponUseLog::find() ->select('order_id,coupon_id,user_coupon_id') ->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED, 'coupon_id' => $coupon_ids]) ->asArray() ->indexBy('user_coupon_id') ->all(); // var_dump($coupon_use_log); $order_ids = array_column($coupon_use_log, 'order_id'); // var_dump('order_ids:', $order_ids); $query = Order::find() ->alias('or') ->select('or.pay_money,or.original_goods_price,om.coupon_discount_money,om.user_coupon_id') ->join('LEFT JOIN', OrderDetail::tableName() . ' as od', 'od.order_id = or.id') ->join('LEFT JOIN', OrderMarketing::tableName() . ' as om', 'om.order_detail_id = od.id') ->where(['or.id' => $order_ids, 'or.status' => StatusEnum::ENABLED, 'om.status' => StatusEnum::ENABLED]); $stat_datas = []; foreach ($query->asArray()->each(500) as $order_info) { if (empty($order_info['user_coupon_id'])) continue; $user_coupon_info = json_decode($order_info['user_coupon_id'], true); foreach ($user_coupon_info as $user_coupon_id => $discount_money) { $coupon_id = $coupon_use_log[$user_coupon_id]['coupon_id'] ?? 0; // 累计优惠总金额 if (!isset($stat_datas[$coupon_id]['total_discount_amount'])) $stat_datas[$coupon_id]['total_discount_amount'] = 0; $stat_datas[$coupon_id]['total_discount_amount'] = bcadd($stat_datas[$coupon_id]['total_discount_amount'], $discount_money, 2); // 优惠券下单总数量 if (!isset($stat_datas[$coupon_id]['use_order_number'])) $stat_datas[$coupon_id]['use_order_number'] = 0; $stat_datas[$coupon_id]['use_order_number'] += 1; // 实际支付总金额 if (!isset($stat_datas[$coupon_id]['total_use_coupon_payed_amount'])) $stat_datas[$coupon_id]['total_use_coupon_payed_amount'] = 0; $stat_datas[$coupon_id]['total_use_coupon_payed_amount'] = bcadd($stat_datas[$coupon_id]['total_use_coupon_payed_amount'], $order_info['pay_money'], 2); // 原价总金额 if (!isset($stat_datas[$coupon_id]['total_payed_amount'])) $stat_datas[$coupon_id]['total_payed_amount'] = 0; $stat_datas[$coupon_id]['total_payed_amount'] = bcadd($stat_datas[$coupon_id]['total_payed_amount'], $order_info['original_goods_price']); } } // var_dump($stat_datas); if (!empty($stat_datas)) { self::updateCouponStat($stat_datas, $mall_id); } echo ' 订单相关数据统计完成 ... ' . PHP_EOL; } /** * @name: * @msg: 更新 jxmall_coupon_statistics 表数据 * @param {array} $data 统计得到的结果 * @param {int} $mall_id * @param {string} $field 更新字段名 * @return {*} */ private static function updateCouponStat(array $data, int $mall_id) { if (!empty($data)) { foreach ($data as $coupon_id => $dv) { $coupon_stat = AddonsCouponStatistics::findOne(['mall_id' => $mall_id, 'coupon_id' => $coupon_id]); if (empty($coupon_stat)) { $coupon_stat = new AddonsCouponStatistics(); $data = [ 'mall_id' => $mall_id, 'coupon_id' => $coupon_id, 'created_at' => time() ]; $coupon_stat->attributes = $data; } foreach ($dv as $key => $value) { $coupon_stat->$key = $value; } // var_dump($coupon_stat); $res = $coupon_stat->save(); if (!$res) { $errmsg = ' 插入/更新已领取数量失败, res => ' . $res . ', data => ' . var_export($data, true); echo $errmsg . PHP_EOL; \Yii::error($errmsg);\Yii::getLogger()->flush(true); } } } } /** * @name: * @msg: 计算优惠券使用率 * @param {int} $mall_id * @return {*} */ private static function calUseProbability(int $mall_id) { $coupon_stats = AddonsCouponStatistics::findAll(['mall_id' => $mall_id]); // dd($coupon_stats); if (!empty($coupon_stats)) { foreach ($coupon_stats as $coupon_stat) { $coupon_stat->use_probability = bcdiv($coupon_stat->used_number, $coupon_stat->received_number, 2); $res = $coupon_stat->save(); if (!$res) { $errmsg = ' 计算&更新优惠券使用率出错:res => ' . var_export($res, true) . ', errmsg => ' . $coupon_stat->getErrorMessage() . ', mall_id => ' . $mall_id . ', coupon_id => ' . $coupon_stat->coupon_id; echo $errmsg . PHP_EOL; \Yii::error($errmsg);\Yii::getLogger()->flush(true); } } } } }