CouponCrontabController.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. <?php
  2. namespace addons\Coupon\console\controllers;
  3. use addons\Coupon\AddonConfig;
  4. use addons\Boss\common\logic\CommissionLogLogic;
  5. use addons\Coupon\common\enums\AddonsCouponEnum;
  6. use addons\Coupon\common\models\AddonsCoupon;
  7. use addons\Coupon\common\models\AddonsCouponGoodStatistics;
  8. use addons\Coupon\common\models\AddonsCouponRemind;
  9. use addons\Coupon\common\models\AddonsCouponShareLog;
  10. use addons\Coupon\common\models\AddonsCouponStatistics;
  11. use addons\Coupon\common\models\AddonsCouponUseLog;
  12. use addons\Coupon\common\models\AddonsCouponUserRelate;
  13. use common\enums\OrderStatusEnum;
  14. use common\enums\StatusEnum;
  15. use common\helpers\LockHelper;
  16. use common\helpers\wechat\WechatTemplate;
  17. use common\models\mall\Mall;
  18. use common\models\mall\MallAddons;
  19. use common\models\order\Order;
  20. use common\models\order\OrderDetail;
  21. use common\models\order\OrderMarketing;
  22. use yii\console\Controller;
  23. use yii\console\ExitCode;
  24. class CouponCrontabController extends Controller
  25. {
  26. /**
  27. * 自动更新优惠券状态,定时器调用执行
  28. *
  29. */
  30. // 0 */1 * * * *
  31. public function actionUpdateCouponStatus()
  32. {
  33. try {
  34. // \Yii::error('自动更新优惠券状态任务开始执行 ... ');
  35. // \Yii::getLogger()->flush(true);
  36. echo ' 自动更新优惠券状态任务开始执行 ... ' . PHP_EOL;
  37. // 获取当前系统所有可用的 mall_id
  38. $malls = Mall::getEnableMallList();
  39. $mall_ids = array_column($malls, 'id');
  40. echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL;
  41. foreach ($mall_ids as $mall_id) {
  42. echo '开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL;
  43. $obj = new AddonConfig();
  44. $name = ($obj->info)['name'];
  45. $is_install = MallAddons::isInstall($mall_id, $name);
  46. if (!$is_install) continue;
  47. // 更新优惠券主表
  48. $lock_name_main = 'qimall:update_coupon_status:main:mall_id:' . $mall_id;
  49. $lock_val_main = md5($lock_name_main);
  50. $lock_main = LockHelper::lock($lock_name_main, $lock_val_main, 60);
  51. $lock_main && self::updateMainCoupons($mall_id);
  52. LockHelper::uLock($lock_name_main, $lock_val_main);
  53. // 更新会员已经领取的优惠券的状态
  54. $lock_name_user = 'qimall:update_coupon_status:user:mall_id:' . $mall_id;
  55. $lock_val_user = md5($lock_name_user);
  56. $lock_user = LockHelper::lock($lock_name_user, $lock_val_user, 60);
  57. $lock_user && self::updateUserCoupons($mall_id);
  58. LockHelper::uLock($lock_name_user, $lock_val_user);
  59. }
  60. echo ' 自动更新优惠券状态任务执行完毕 ... ' . PHP_EOL;
  61. return ExitCode::OK;
  62. } catch (\Exception $e) {
  63. LockHelper::uLock($lock_name_main, $lock_val_main);
  64. LockHelper::uLock($lock_name_user, $lock_val_user);
  65. $errmsg = ' 自动更新优惠券状态任务执行出错, errmsg => ' . $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine();
  66. echo $errmsg . PHP_EOL;
  67. \Yii::error($errmsg);
  68. \Yii::getLogger()->flush(true);
  69. return false;
  70. }
  71. }
  72. /**
  73. * 优惠券相关统计,定时器调用执行
  74. *
  75. */
  76. // 0 */3 * * * *
  77. public function actionCouponStatistics()
  78. {
  79. // \Yii::error('优惠券相关统计任务开始执行 ... ');
  80. // \Yii::getLogger()->flush(true);
  81. try {
  82. echo ' 优惠券相关统计任务开始执行 ... ' . PHP_EOL;
  83. // 获取当前系统所有可用的 mall_id
  84. $malls = Mall::getEnableMallList();
  85. $mall_ids = array_column($malls, 'id');
  86. echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL;
  87. foreach ($mall_ids as $mall_id) {
  88. echo '开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL;
  89. // $mall_id = 5;
  90. $obj = new AddonConfig();
  91. $name = ($obj->info)['name'];
  92. $is_install = MallAddons::isInstall($mall_id, $name);
  93. if (!$is_install) continue;
  94. $lock_name = 'qimall:coupon_statistics:mall_id:' . $mall_id;
  95. $lock_val = md5($lock_name);
  96. $lock = LockHelper::lock($lock_name, $lock_val, 60);
  97. if ($lock) {
  98. // 优惠券相关数量
  99. self::statNumber($mall_id);
  100. // 订单使用相关统计
  101. self::statOrder($mall_id);
  102. // 计算优惠券使用率
  103. self::calUseProbability($mall_id);
  104. }
  105. LockHelper::uLock($lock_name, $lock_val);
  106. }
  107. echo ' 优惠券相关统计任务执行完毕' . PHP_EOL;
  108. return ExitCode::OK;
  109. } catch (\Exception $e) {
  110. LockHelper::uLock($lock_name, $lock_val);
  111. $errmsg = $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine();
  112. \Yii::error($errmsg);
  113. \Yii::getLogger()->flush(true);
  114. return false;
  115. }
  116. }
  117. /**
  118. * 统计商品使用优惠券相关数据,定时器调用执行
  119. *
  120. */
  121. // 0 */3 * * * *
  122. public function actionCouponGoodStat()
  123. {
  124. // \Yii::error('统计商品使用优惠券相关数据任务开始执行 ...');
  125. // \Yii::getLogger()->flush(true);
  126. try {
  127. echo ' 统计商品使用优惠券相关数据任务开始执行 ... ' . PHP_EOL;
  128. // 获取当前系统所有可用的 mall_id
  129. $malls = Mall::getEnableMallList();
  130. $mall_ids = array_column($malls, 'id');
  131. echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL;
  132. foreach ($mall_ids as $mall_id) {
  133. echo ' 开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL;
  134. // $mall_id = 5;
  135. $obj = new AddonConfig();
  136. $name = ($obj->info)['name'];
  137. $is_install = MallAddons::isInstall($mall_id, $name);
  138. if (!$is_install) {
  139. echo ' 未安装优惠券应用' . PHP_EOL;
  140. continue;
  141. }
  142. $lock_name = 'qimall:coupon_good_stat:mall_id:' . $mall_id;
  143. $lock_val = md5($lock_name);
  144. $lock = LockHelper::lock($lock_name, $lock_val, 60);
  145. if ($lock) {
  146. // 统计使用优惠券购买商品相关数量
  147. self::statBookNumber($mall_id);
  148. } else {
  149. echo ' 获取锁失败' . PHP_EOL;
  150. }
  151. LockHelper::uLock($lock_name, $lock_val);
  152. }
  153. echo ' 统计商品使用优惠券相关数据任务执行完毕' . PHP_EOL;
  154. return ExitCode::OK;
  155. } catch (\Exception $e) {
  156. LockHelper::uLock($lock_name, $lock_val);
  157. $errmsg = $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine();
  158. \Yii::error($errmsg);
  159. \Yii::getLogger()->flush(true);
  160. return false;
  161. }
  162. }
  163. /**
  164. * 优惠券过期前提醒,定时器调用执行
  165. */
  166. // 0 0 */4 * * *
  167. public function actionExpireRemind()
  168. {
  169. try {
  170. // 获取当前系统所有可用的 mall_id
  171. $malls = Mall::getEnableMallList();
  172. $mall_ids = array_column($malls, 'id');
  173. echo ' 获取到的 mall_id => ' . implode(', ', $mall_ids) . PHP_EOL;
  174. foreach ($mall_ids as $mall_id) {
  175. echo '开始更新商城的优惠券:mall_id => ' . $mall_id . PHP_EOL;
  176. // $mall_id = 5;
  177. $obj = new AddonConfig();
  178. $name = ($obj->info)['name'];
  179. $is_install = MallAddons::isInstall($mall_id, $name);
  180. if (!$is_install) continue;
  181. $page = 1;
  182. $limit = 500;
  183. do {
  184. $offset = ($page - 1) * $limit;
  185. $page++;
  186. $reminds = AddonsCouponRemind::find()
  187. ->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED, 'send_status' => 1])
  188. ->limit($limit)
  189. ->offset($offset)
  190. ->all();
  191. if (!empty($reminds)) {
  192. foreach ($reminds as $remind) {
  193. $coupon = AddonsCoupon::findOne(['id' => $remind->coupon_id, 'mall_id' => $mall_id, 'is_remind' => 1]);
  194. if (empty($coupon)) continue;
  195. $time_node = $remind->remind_days * 86400;
  196. if ($time_node > $remind->expire_time) {
  197. // 现在到优惠券过期的时间小于三天,发消息提醒
  198. $params = [
  199. 'mall_id' => $mall_id,
  200. 'user_id' => $remind->user_id,
  201. 'first' => '您已经领取的优惠券',
  202. 'keywords' => [
  203. 'keyword1' => $coupon->name,
  204. ],
  205. 'remark' => '即将过期,请尽快使用。'
  206. ];
  207. self::sendWechatMessage($params);
  208. }
  209. // 睡眠 1s,防止腾讯封禁
  210. sleep(1);
  211. }
  212. }
  213. } while ($reminds);
  214. }
  215. echo ' 发送过期提醒任务执行完毕 ... ' . PHP_EOL;
  216. return true;
  217. } catch (\Exception $e) {
  218. $errmsg = $e->getMessage() . ', file: ' . $e->getFile() . ', line: ' . $e->getLine();
  219. \Yii::error($errmsg);
  220. \Yii::getLogger()->flush(true);
  221. return false;
  222. }
  223. }
  224. /**
  225. * @name:
  226. * @msg: 更新优惠券主表中的状态
  227. * @param {int} $mall_id
  228. * @return {*}
  229. */
  230. private static function updateMainCoupons(int $mall_id)
  231. {
  232. // $query = AddonsCoupon::find()->where(['mall_id' => $mall_id, 'status' => 1, 'expire_type' => 2])->andWhere(['!=', 'coupon_status', 4]);
  233. $curr_time = time();
  234. $receiving_ids = [];
  235. $finished_receive_ids = [];
  236. $page = 1;
  237. $limit = 500;
  238. do {
  239. $offset = ($page - 1) * $limit;
  240. $page++;
  241. $coupons = AddonsCoupon::find()
  242. ->where(['mall_id' => $mall_id, 'status' => 1, 'expire_type' => 2])
  243. ->andWhere(['!=', 'coupon_status', 4])
  244. ->limit($limit)
  245. ->offset($offset)
  246. ->all();
  247. if (!empty($coupons)) {
  248. foreach ($coupons as $coupon) {
  249. if ($coupon->begin_at <= $curr_time && $coupon->end_at > $curr_time) {
  250. // 优惠券领取中
  251. $receiving_ids[] = $coupon->id;
  252. } elseif ($coupon->end_at <= $curr_time) {
  253. $finished_receive_ids[] = $coupon->id;
  254. }
  255. }
  256. }
  257. } while ($coupons);
  258. if (!empty($receiving_ids)) {
  259. $res = AddonsCoupon::updateAll(['coupon_status' => 2], ['id' => $receiving_ids, 'mall_id' => $mall_id]);
  260. if ($res === false) {
  261. \Yii::error('更新优惠券状态失败:res => ' . var_export($res, true) . ', mall_id => ' . $mall_id . ', receiving_ids => ' . var_export($receiving_ids, true) . ', errmsg => ' . AddonsCoupon::getStaticError());
  262. \Yii::getLogger()->flush(true);
  263. }
  264. }
  265. if (!empty($finished_receive_ids)) {
  266. $res = AddonsCoupon::updateAll(['coupon_status' => 3], ['id' => $finished_receive_ids, 'mall_id' => $mall_id]);
  267. $res_remaind = AddonsCouponRemind::updateAll(['send_status' => 3], ['coupon_id' => $finished_receive_ids, 'mall_id' => $mall_id]);
  268. $res_uc = AddonsCouponUserRelate::updateAll(['coupon_status' => 3], ['coupon_id' => $finished_receive_ids, 'mall_id' => $mall_id]);
  269. if ($res === false || $res_remaind === false || $res_uc === false) {
  270. \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());
  271. \Yii::getLogger()->flush(true);
  272. }
  273. }
  274. }
  275. /**
  276. * @name:
  277. * @msg: 更新会员已经领取的优惠券的状态
  278. * @param {int} $mall_id
  279. * @return {*}
  280. */
  281. private static function updateUserCoupons(int $mall_id)
  282. {
  283. $time = time();
  284. /* $query = AddonsCouponUserRelate::find()
  285. ->where(['mall_id' => $mall_id, 'is_use' => 0, 'status' => 1, 'is_force_failure' => 0, 'coupon_status' => AddonsCouponEnum::STATUS_RECEIVING])
  286. ->andWhere(['or', ['is_giving' => 0], ['is_giving' => 1, 'giving_status' => 1]])
  287. ->andWhere(['<=', 'end_time', $time]);
  288. foreach ($query->each(500) as $user_coupon) {
  289. $user_coupon->coupon_status = AddonsCouponEnum::STATUS_EXPIRED;
  290. $user_coupon->updated_at = $time;
  291. $res = $user_coupon->save();
  292. if (!$res) {
  293. $errmsg = ' 更新优惠券状态失败:res => ' . var_export($res, true) . ', mall_id => ' . $mall_id . ', id => ' . $user_coupon->id;
  294. echo $errmsg . PHP_EOL;
  295. \Yii::error($errmsg);
  296. \Yii::getLogger()->flush(true);
  297. }
  298. } */
  299. $page = 1;
  300. $limit = 500;
  301. do {
  302. $offset = ($page - 1) * $limit;
  303. $page++;
  304. $user_coupons = AddonsCouponUserRelate::find()
  305. ->where(['mall_id' => $mall_id, 'is_use' => 0, 'status' => 1, 'is_force_failure' => 0, 'coupon_status' => AddonsCouponEnum::STATUS_RECEIVING])
  306. ->andWhere(['or', ['is_giving' => 0], ['is_giving' => 1, 'giving_status' => 1]])
  307. ->andWhere(['<=', 'end_time', $time])
  308. ->limit($limit)
  309. ->offset($offset)
  310. ->all();
  311. if (!empty($user_coupons)) {
  312. foreach ($user_coupons as $user_coupon) {
  313. $user_coupon->coupon_status = AddonsCouponEnum::STATUS_EXPIRED;
  314. $user_coupon->updated_at = $time;
  315. $res = $user_coupon->save();
  316. if (!$res) {
  317. $errmsg = ' 更新优惠券状态失败:res => ' . var_export($res, true) . ', mall_id => ' . $mall_id . ', id => ' . $user_coupon->id;
  318. echo $errmsg . PHP_EOL;
  319. \Yii::error($errmsg);
  320. \Yii::getLogger()->flush(true);
  321. }
  322. }
  323. }
  324. } while ($user_coupons);
  325. }
  326. /**
  327. * @name:
  328. * @msg: 统计商品使用优惠券相关数据
  329. * @param {int} $mall_id
  330. * @param {string} $type
  331. * @param {string} $field
  332. * @return {*}
  333. */
  334. private static function statBookNumber(int $mall_id)
  335. {
  336. $stat_datas = $booking_users = $payed_users = $booking_number = [];
  337. $page = 1;
  338. $limit = 500;
  339. do {
  340. $offset = ($page - 1) * $limit;
  341. $page++;
  342. $coupons = AddonsCoupon::find()->where(['mall_id' => $mall_id, 'status' => 1])->limit($limit)->offset($offset)->all();
  343. if (!empty($coupons)) {
  344. $coupon_ids = array_column($coupons, 'id');
  345. if (empty($coupon_ids)) continue;
  346. $coupon_use_log = AddonsCouponUseLog::find()
  347. ->select('order_id,coupon_id,user_coupon_id')
  348. ->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED, 'coupon_id' => $coupon_ids])
  349. ->asArray()
  350. ->indexBy('order_id')
  351. ->all();
  352. $order_ids = array_column($coupon_use_log, 'order_id');
  353. $orders = Order::find()
  354. ->where(['id' => $order_ids, 'status' => StatusEnum::ENABLED])
  355. ->asArray()
  356. ->indexBy('id')
  357. ->all();
  358. if (empty($orders)) continue;
  359. $order_details = OrderDetail::find()
  360. ->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED, 'order_id' => array_column($orders, 'id')])
  361. ->all();
  362. foreach ($order_details as $detail) {
  363. $coupon_id = $coupon_use_log[$detail->order_id]['coupon_id'] ?? 0;
  364. $key_order_number = $coupon_id . '_' . $detail['goods_id'];
  365. // 下单数量
  366. $booking_number[$key_order_number][] = $detail->order_id;
  367. // 支付数量
  368. $pay_status = $orders[$detail->order_id]['pay_status'] ?? OrderStatusEnum::NOT_PAY;
  369. if ($pay_status == OrderStatusEnum::PAY) $payed_number[$key_order_number][] = $detail->order_id;
  370. // 下单总人数
  371. $booking_users[$key_order_number][] = $detail->user_id;
  372. // 购买总人数
  373. if ($pay_status == OrderStatusEnum::PAY) $payed_users[$key_order_number][] = $detail->user_id;
  374. }
  375. }
  376. } while ($coupons);
  377. // var_dump($booking_number, $payed_number, $booking_users, $payed_users);exit;
  378. if (!empty($booking_number)) {
  379. foreach ($booking_number as $key => $order_ids) {
  380. $stat_datas[$key]['booking_number'] = count(array_values(array_unique($order_ids)));
  381. }
  382. }
  383. if (!empty($payed_number)) {
  384. foreach ($payed_number as $key => $order_ids) {
  385. $stat_datas[$key]['payed_number'] = count(array_values(array_unique($order_ids)));
  386. }
  387. }
  388. if (!empty($booking_users)) {
  389. foreach ($booking_users as $key => $users) {
  390. $stat_datas[$key]['booking_user_number'] = count(array_values(array_unique($users)));
  391. }
  392. }
  393. if (!empty($payed_users)) {
  394. foreach ($payed_users as $key => $users) {
  395. $stat_datas[$key]['buyed_user_number'] = count(array_values(array_unique($users)));
  396. }
  397. }
  398. // var_dump($stat_datas);exit;
  399. if (empty($stat_datas)) {
  400. echo ' stat_datas is empty, stat_datas => ' . var_export($stat_datas, true) . PHP_EOL;
  401. return;
  402. }
  403. self::updateCouponGoodStat($stat_datas, $mall_id);
  404. }
  405. /**
  406. * @name:
  407. * @msg: 更新优惠券商品统计表
  408. * @param {array} $data
  409. * @param {int} $mall_id
  410. * @param {*} $field
  411. * @return {*}
  412. */
  413. private static function updateCouponGoodStat(array $data, int $mall_id)
  414. {
  415. if (!empty($data)) {
  416. foreach ($data as $key => $dv) {
  417. $keys = explode('_', $key);
  418. $coupon_good_stat = AddonsCouponGoodStatistics::findOne(['mall_id' => $mall_id, 'coupon_id' => $keys[0], 'good_id' => $keys[1]]);
  419. if (empty($coupon_good_stat)) {
  420. $coupon_good_stat = new AddonsCouponGoodStatistics();
  421. $data = [
  422. 'mall_id' => $mall_id,
  423. 'coupon_id' => $keys[0] ?? 0,
  424. 'good_id' => $keys[1] ?? 0,
  425. 'created_at' => time(),
  426. ];
  427. $coupon_good_stat->attributes = $data;
  428. }
  429. // dd($coupon_good_stat);
  430. foreach ($dv as $dk => $val) {
  431. $coupon_good_stat->$dk = $val;
  432. }
  433. $res = $coupon_good_stat->save();
  434. if (!$res) {
  435. $errmsg = ' 插入/更新已领取数量失败, res => ' . $res . ', data => ' . var_export($data, true);
  436. echo $errmsg . PHP_EOL;
  437. \Yii::error($errmsg);
  438. \Yii::getLogger()->flush(true);
  439. }
  440. }
  441. }
  442. }
  443. /**
  444. * @name:
  445. * @msg: 统计数量
  446. * @param {int} $mall_id
  447. * @param {array} $conditions
  448. * @param {string} $field
  449. * @return {*}
  450. */
  451. private static function statNumber(int $mall_id)
  452. {
  453. echo ' 统计相关数量 ... ' . PHP_EOL;
  454. $user_coupons = [];
  455. /* $used_query = AddonsCouponUserRelate::find()->where(['mall_id' => $mall_id, 'status' => 1]);
  456. foreach ($used_query->batch(500) as $use_coupon) {
  457. $coupon_id = $use_coupon->coupon_id;
  458. $coupon_id == 187 && var_dump($coupon_id);
  459. // 统计已领取数量
  460. if (!isset($user_coupons[$coupon_id]['received_number'])) $user_coupons[$coupon_id]['received_number'] = 0;
  461. $user_coupons[$coupon_id]['received_number'] += 1;
  462. // 统计已使用数量
  463. if (!isset($user_coupons[$coupon_id]['used_number'])) $user_coupons[$coupon_id]['used_number'] = 0;
  464. $use_coupon->is_use == 1 && $user_coupons[$coupon_id]['used_number'] += 1;
  465. // 统计优惠券被转赠的次数
  466. if (!isset($user_coupons[$coupon_id]['giveing_times'])) $user_coupons[$coupon_id]['giveing_times'] = 0;
  467. $use_coupon->is_giving == 1 && $user_coupons[$coupon_id]['giveing_times'] += 1;
  468. // 记录领取会员id
  469. $received_user_ids[$coupon_id][] = $use_coupon->user_id;
  470. // 记录使用会员id
  471. $use_coupon->is_use == 1 && $used_user_ids[$coupon_id][] = $use_coupon->user_id;
  472. // 记录转赠优惠券的会员id
  473. $use_coupon->is_giving == 1 && $giving_user_ids[$coupon_id][] = $use_coupon->user_id;
  474. } */
  475. $page = 1;
  476. $limit = 500;
  477. do {
  478. $offset = ($page - 1) * $limit;
  479. $page++;
  480. $use_coupons = AddonsCouponUserRelate::find()->where(['mall_id' => $mall_id, 'status' => 1])->limit($limit)->offset($offset)->all();
  481. if (!empty($use_coupons)) {
  482. foreach ($use_coupons as $key => $use_coupon) {
  483. $coupon_id = $use_coupon->coupon_id;
  484. // 统计已领取数量
  485. if (!isset($user_coupons[$coupon_id]['received_number'])) $user_coupons[$coupon_id]['received_number'] = 0;
  486. $user_coupons[$coupon_id]['received_number'] += 1;
  487. // 统计已使用数量
  488. if (!isset($user_coupons[$coupon_id]['used_number'])) $user_coupons[$coupon_id]['used_number'] = 0;
  489. $use_coupon->is_use == 1 && $user_coupons[$coupon_id]['used_number'] += 1;
  490. // 统计优惠券被转赠的次数
  491. if (!isset($user_coupons[$coupon_id]['giveing_times'])) $user_coupons[$coupon_id]['giveing_times'] = 0;
  492. $use_coupon->is_giving == 1 && $user_coupons[$coupon_id]['giveing_times'] += 1;
  493. // 记录领取会员id
  494. $received_user_ids[$coupon_id][] = $use_coupon->user_id;
  495. // 记录使用会员id
  496. $use_coupon->is_use == 1 && $used_user_ids[$coupon_id][] = $use_coupon->user_id;
  497. // 记录转赠优惠券的会员id
  498. $use_coupon->is_giving == 1 && $giving_user_ids[$coupon_id][] = $use_coupon->user_id;
  499. }
  500. }
  501. } while ($use_coupons);
  502. // 统计领取会员数量
  503. if (!empty($received_user_ids)) {
  504. foreach ($received_user_ids as $coupon_id => $user_ids) {
  505. $counts = count(array_flip(array_flip($user_ids)));
  506. $user_coupons[$coupon_id]['receive_user_number'] = $counts;
  507. }
  508. }
  509. // 统计用券会员数量
  510. if (!empty($used_user_ids)) {
  511. foreach ($used_user_ids as $coupon_id => $user_ids) {
  512. $counts = count(array_flip(array_flip($user_ids)));
  513. $user_coupons[$coupon_id]['use_user_number'] = $counts;
  514. }
  515. }
  516. // 统计转赠优惠券的会员数量
  517. if (!empty($giving_user_ids)) {
  518. foreach ($giving_user_ids as $coupon_id => $user_ids) {
  519. $counts = count(array_flip(array_flip($user_ids)));
  520. $user_coupons[$coupon_id]['giving_user_number'] = $counts;
  521. }
  522. }
  523. !empty($user_coupons) && self::updateCouponStat($user_coupons, $mall_id);
  524. echo ' 统计相关数量完成 ... ' . PHP_EOL;
  525. }
  526. /**
  527. * @name:
  528. * @msg: 统计订单相关数据
  529. * @param int $mall_id
  530. * @param string $type 统计类型,total_discount:累计优惠总金额;total_number:使用优惠券支付的订单总数量;total_real_payed:使用优惠券后实际支付的总金额;total_payed:使用优惠券支付的订单原价总金额
  531. * @param string $field
  532. * @return {*}
  533. */
  534. private static function statOrder($mall_id)
  535. {
  536. echo ' 统计订单相关数据 ... ' . PHP_EOL;
  537. $stat_datas = [];
  538. $page = 1;
  539. $limit = 500;
  540. do {
  541. $offset = ($page - 1) * $limit;
  542. $page++;
  543. $coupons = AddonsCoupon::find()->where(['mall_id' => $mall_id, 'status' => 1])->limit($limit)->offset($offset)->all();
  544. if (!empty($coupons)) {
  545. $coupon_ids = array_column($coupons, 'id');
  546. $coupon_use_log = AddonsCouponUseLog::find()
  547. ->select('order_id,coupon_id,user_coupon_id')
  548. ->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED, 'coupon_id' => $coupon_ids])
  549. ->asArray()
  550. ->indexBy('user_coupon_id')
  551. ->all();
  552. // var_dump($coupon_use_log);
  553. $order_ids = array_column($coupon_use_log, 'order_id');
  554. if (empty($order_ids)) continue;
  555. $orders = Order::find()
  556. ->where(['id' => $order_ids, 'status' => StatusEnum::ENABLED, 'mall_id' => $mall_id])
  557. ->asArray()
  558. ->indexBy('id')
  559. ->all();
  560. $order_details = OrderDetail::find()
  561. ->where(['order_id' => $order_ids, 'mall_id' => $mall_id, 'status' => StatusEnum::ENABLED])
  562. ->asArray()
  563. ->indexBy('id')
  564. ->all();
  565. $order_detail_ids = array_column($order_details, 'id');
  566. $order_marketings = OrderMarketing::find()
  567. ->where(['order_detail_id' => $order_detail_ids, 'status' => StatusEnum::ENABLED])
  568. ->all();
  569. if (empty($order_marketings)) continue;
  570. foreach ($order_marketings as $marketing) {
  571. if (empty($marketing->user_coupon_id)) continue;
  572. $user_coupon_info = json_decode($marketing->user_coupon_id, true);
  573. foreach ($user_coupon_info as $user_coupon_id => $discount_money) {
  574. $coupon_id = $coupon_use_log[$user_coupon_id]['coupon_id'] ?? 0;
  575. // 累计优惠总金额
  576. if (!isset($stat_datas[$coupon_id]['total_discount_amount'])) $stat_datas[$coupon_id]['total_discount_amount'] = 0;
  577. $stat_datas[$coupon_id]['total_discount_amount'] = bcadd($stat_datas[$coupon_id]['total_discount_amount'], $discount_money, 2);
  578. // 优惠券下单总数量
  579. if (!isset($stat_datas[$coupon_id]['use_order_number'])) $stat_datas[$coupon_id]['use_order_number'] = 0;
  580. $stat_datas[$coupon_id]['use_order_number'] += 1;
  581. // 实际支付总金额
  582. if (!isset($stat_datas[$coupon_id]['total_use_coupon_payed_amount'])) $stat_datas[$coupon_id]['total_use_coupon_payed_amount'] = 0;
  583. $pay_money = $orders[$order_details[$marketing->order_detail_id]['order_id']]['pay_money'] ?? 0;
  584. $stat_datas[$coupon_id]['total_use_coupon_payed_amount'] = bcadd($stat_datas[$coupon_id]['total_use_coupon_payed_amount'], $pay_money, 2);
  585. // 用券总金额:未使用优惠券时订单的实际总金额(是计算了积分抵扣、会员价等等的优惠之后的价格)
  586. if (!isset($stat_datas[$coupon_id]['total_payed_amount'])) $stat_datas[$coupon_id]['total_payed_amount'] = 0;
  587. // $original_goods_price = $orders[$order_details[$marketing->order_detail_id]['order_id']]['original_goods_price'] ?? 0;
  588. // $stat_datas[$coupon_id]['total_payed_amount'] = bcadd($stat_datas[$coupon_id]['total_payed_amount'], $original_goods_price);
  589. $no_coupon_payed_amount = ($orders[$order_details[$marketing->order_detail_id]['order_id']]['pay_money'] ?? 0) + $discount_money;
  590. $stat_datas[$coupon_id]['total_payed_amount'] = bcadd($stat_datas[$coupon_id]['total_payed_amount'], $no_coupon_payed_amount);
  591. }
  592. }
  593. }
  594. } while ($coupons);
  595. // var_dump($stat_datas);
  596. if (!empty($stat_datas)) {
  597. self::updateCouponStat($stat_datas, $mall_id);
  598. }
  599. echo ' 订单相关数据统计完成 ... ' . PHP_EOL;
  600. }
  601. /**
  602. * @name:
  603. * @msg: 更新 jxmall_coupon_statistics 表数据
  604. * @param {array} $data 统计得到的结果
  605. * @param {int} $mall_id
  606. * @param {string} $field 更新字段名
  607. * @return {*}
  608. */
  609. private static function updateCouponStat(array $data, int $mall_id)
  610. {
  611. if (!empty($data)) {
  612. foreach ($data as $coupon_id => $dv) {
  613. $coupon_stat = AddonsCouponStatistics::findOne(['mall_id' => $mall_id, 'coupon_id' => $coupon_id]);
  614. if (empty($coupon_stat)) {
  615. $coupon_stat = new AddonsCouponStatistics();
  616. $data = [
  617. 'mall_id' => $mall_id,
  618. 'coupon_id' => $coupon_id,
  619. 'created_at' => time()
  620. ];
  621. $coupon_stat->attributes = $data;
  622. }
  623. foreach ($dv as $key => $value) {
  624. $coupon_stat->$key = $value;
  625. }
  626. // var_dump($coupon_stat);
  627. $res = $coupon_stat->save();
  628. if (!$res) {
  629. $errmsg = ' 插入/更新已领取数量失败, res => ' . $res . ', data => ' . var_export($data, true);
  630. echo $errmsg . PHP_EOL;
  631. \Yii::error($errmsg);
  632. \Yii::getLogger()->flush(true);
  633. }
  634. }
  635. }
  636. }
  637. /**
  638. * @name:
  639. * @msg: 计算优惠券使用率
  640. * @param {int} $mall_id
  641. * @return {*}
  642. */
  643. private static function calUseProbability(int $mall_id)
  644. {
  645. /* $query = AddonsCouponStatistics::find()->where(['mall_id' => $mall_id]);
  646. foreach ($query->each(500) as $coupon_stat) {
  647. $coupon_stat->use_probability = bcdiv($coupon_stat->used_number, $coupon_stat->received_number, 2);
  648. $res = $coupon_stat->save();
  649. if (!$res) {
  650. $errmsg = ' 计算&更新优惠券使用率出错:res => ' . var_export($res, true) . ', errmsg => ' . $coupon_stat->getErrorMessage() . ', mall_id => ' . $mall_id . ', coupon_id => ' . $coupon_stat->coupon_id;
  651. echo $errmsg . PHP_EOL;
  652. \Yii::error($errmsg);
  653. \Yii::getLogger()->flush(true);
  654. }
  655. } */
  656. $page = 1;
  657. $limit = 500;
  658. do {
  659. $offset = ($page - 1) * $limit;
  660. $page++;
  661. $coupon_stats = AddonsCouponStatistics::find()->where(['mall_id' => $mall_id])->limit($limit)->offset($offset)->all();
  662. if (!empty($coupon_stats)) {
  663. foreach ($coupon_stats as $coupon_stat) {
  664. if (empty($coupon_stat->received_number)) continue;
  665. $coupon_stat->use_probability = bcdiv($coupon_stat->used_number, $coupon_stat->received_number, 2);
  666. $res = $coupon_stat->save();
  667. if (!$res) {
  668. $errmsg = ' 计算&更新优惠券使用率出错:res => ' . var_export($res, true) . ', errmsg => ' . $coupon_stat->getErrorMessage() . ', mall_id => ' . $mall_id . ', coupon_id => ' . $coupon_stat->coupon_id;
  669. echo $errmsg . PHP_EOL;
  670. \Yii::error($errmsg);
  671. \Yii::getLogger()->flush(true);
  672. }
  673. }
  674. }
  675. } while ($coupon_stats);
  676. }
  677. private static function sendWechatMessage(array $params)
  678. {
  679. $wechat = new WechatTemplate($params['mall_id']);
  680. //商城是否开启推送模板消息
  681. if ($wechat->is_logging() === false) {
  682. return;
  683. }
  684. //用户是否开启模板消息
  685. if ($wechat->is_notice($params['user_id']) === false) {
  686. return;
  687. }
  688. //是否公众号用户
  689. if ($wechat->is_platform_wechat($params['user_id']) === false) {
  690. return;
  691. }
  692. $platform = $wechat->getPlatForm();
  693. $h5_domain = \Yii::$app->services->setting->mall->get($params['mall_id'], 'basic.web_url');
  694. if (empty($h5_domain)) {
  695. $h5_domain = \Yii::$app->request->hostInfo;
  696. } else {
  697. $h5_domain = substr($h5_domain, 0, strpos($h5_domain, '?'));
  698. }
  699. $url = 'pages/user/coupon/coupon';
  700. $h5_url = $h5_domain . $url;
  701. $send_data = [
  702. 'first' => $params['first'],
  703. 'remark' => $params['remark'],
  704. ];
  705. if (!empty($params['keywords'])) {
  706. foreach ($params['keywords'] as $key => $val) {
  707. $send_data[$key] = $val;
  708. }
  709. }
  710. $temp_key = WechatTemplate::ORDER_CREATE;
  711. $wechat->sendTemplate($params['user_id'], $temp_key, $h5_url, $send_data, $url);
  712. }
  713. public function actionGivingExpire()
  714. {
  715. $malls = Mall::getEnableMallList();
  716. foreach ($malls as $mall) {
  717. $i = 1;
  718. $page_size = 1000;
  719. while ($list = AddonsCouponShareLog::find()
  720. ->where(['mall_id' => $mall['id']])
  721. ->andWhere(['receive_user_id' => 0])
  722. ->offset(($i - 1) * $page_size)
  723. ->limit($page_size)->all()) {
  724. $coupon_id = array_column($list, 'coupon_id');
  725. $user_coupon_ids = array_column($list, 'user_coupon_id');
  726. $coupon_list = AddonsCoupon::lists([
  727. 'where' => [
  728. ['id' => $coupon_id]
  729. ],
  730. 'index' => 'id'
  731. ]);
  732. $coupon_user_relate_list = AddonsCouponUserRelate::lists([
  733. 'where' => [
  734. ['id' => $user_coupon_ids]
  735. ],
  736. 'index' => 'id'
  737. ],false);
  738. foreach ($list as $item) {
  739. if (!empty($coupon_list[$item['coupon_id']]['giving_expire'])) {
  740. $giving_expire = abs($coupon_list[$item['coupon_id']]['giving_expire']);
  741. $time = time();
  742. $created_at = $item['created_at'];
  743. $res = AddonsCouponShareLog::find()->where(['user_coupon_id'=>$item['user_coupon_id']])
  744. ->andWhere(['<>','receive_user_id',0])->one();
  745. if(empty($res)){
  746. if (($time - $created_at) > ($giving_expire * 24 * 3600)) {
  747. if (isset($coupon_user_relate_list[$item['user_coupon_id']])){
  748. $coupon_user_relate_model = $coupon_user_relate_list[$item['user_coupon_id']];
  749. $coupon_user_relate_model->is_giving = 0;
  750. $coupon_user_relate_model->giving_status = 0;
  751. $res = $coupon_user_relate_model->save();
  752. if($res){
  753. var_dump('user_coupon_id:'.$item['user_coupon_id'].'退回成功');
  754. }
  755. $res = $item->delete();
  756. if($res){
  757. \Yii::error('id:'.$item['id'].'删除分享记录成功');
  758. }
  759. }
  760. }
  761. }
  762. }
  763. }
  764. $i++;
  765. }
  766. }
  767. }
  768. }