where(['status' => 0, 'push_type' => 1])->select()->toArray(); if($event_list) { try { Db::startTrans(); foreach ($event_list as $k => $event) { Db::name('tb_push_event')->where(['id' => $event['id']])->update(['status' => 1, 'start_time' => date('Y-m-d H:i:s')]); $res = self::getPullInfoUser($event); if($res) { foreach ($res as $rk => &$rv) { if(empty($rv['open_id'])) { unset($res[$rk]); } } $user_list = uniqueMultidimensionalArray($res); // 需要接收推送的用户 准备发送 foreach ($user_list as $k => $user) { $data = [ 'thing2' => $event['keyword1'], 'amount3' => $event['keyword2'], 'time6' => $event['keyword3'], ]; $user_push_list_res_id = Db::name('tb_user_push_event')->insertGetId([ 'push_event_id' => $event['id'], 'user_id' => $user['user_id'], 'create_time' => date("Y-m-d H:i:s"), 'content' => json_encode($data), ]); if($user_push_list_res_id) { $app = Factory::officialAccount([ 'app_id' => config('cons.app_id'), 'secret' => config('cons.secret'), 'response_type' => 'array', ]); // 正式推送 $push = $app->template_message->send([ 'touser' => $user['open_id'], 'template_id' => $event['template_id'], 'data' => $data ]); Log::info('用户-' . $user['user_id'] . ' 推送模版ID:'. $event['template_id'] .' 内容:' . json_encode($data, 256) . ' 推送结果:' . json_encode($push)); if($push['errcode'] == 0) { Db::name('tb_user_push_event')->where(['id' => $user_push_list_res_id])->update(['update_time' => date("Y-m-d H:i:s"), 'status' => 1]); // 送券 if(isset($event['coupon'])) { $coupon = json_decode($event['coupon'], true); self::sendUserCoupon($event['shop_id'], $user['user_id'], $coupon['id'], $coupon['num']); } }else { Db::name('tb_user_push_event')->where(['id' => $user_push_list_res_id])->update(['update_time' => date("Y-m-d H:i:s"), 'status' => -1, 'error' => json_encode($push)]); } } sleep(1); } Db::name('tb_push_event')->where(['id' => $event['id']])->update(['status' => 2, 'send_time' => date('Y-m-d H:i:s')]); } } Db::commit(); }catch (\Exception $e) { Log::info('执行出错--' . $e->getMessage()); Db::rollback(); } } Log::info('没有推送任务可执行'); return true; } // 推送短信消息 public static function send_sms_msg() { // 查询短信任务 $event_list = Db::name('tb_push_event')->where(['status' => 0, 'push_type' => 2])->select()->toArray(); if($event_list) { try { Db::startTrans(); foreach ($event_list as $k => $event) { Db::name('tb_push_event')->where(['id' => $event['id']])->update(['status' => 1, 'start_time' => date('Y-m-d H:i:s')]); $res = self::getPullInfoUser($event); if($res) { foreach ($res as $rk => &$rv) { if(empty($rv['phone'])) { unset($res[$rk]); } } $user_list = uniqueMultidimensionalArray($res); // 需要接收推送的用户 准备发送 foreach ($user_list as $k => $user) { // 查询商家短信余额 $sms_shop = Db::name('tb_shop_sms_money')->where(['shop_id' => $event['shop_id']])->find(); if (!$sms_shop) { Db::name('tb_shop_sms_money')->insertGetId([ 'shop_id' => $event['shop_id'], 'create_time' => date("Y-m-d H:i:s"), ]); log::info('商户' . $event['shop_id'] . '不存在或余额不足,停止发送'); continue 2; } else { // 一条短信的价格 $one_sms_price = config('cons.sms_price'); if($sms_shop['money'] < $one_sms_price) { log::info('商户' . $event['shop_id'] . '余额不足,停止发送'); continue 2; }else { $user_push_list_res_id = Db::name('tb_user_push_event')->insertGetId([ 'push_event_id' => $event['id'], 'user_id' => $user['user_id'], 'create_time' => date("Y-m-d H:i:s"), 'content' => json_encode($user), ]); if ($user_push_list_res_id) { // 发送短信 $ret = AlibabaSms::main([ 'templateCode' => $event['sms_code'], 'templateParam' => json_encode($event['sms_content']), 'phoneNumbers' => $user['phone'], 'signName' => $event['sms_sign'], ]); Log::info('用户-' . $user['user_id'] . ' 短信发送ID:' . $user['template_id'] . ' 内容:' . json_encode($event['sms_content'], 256) . ' 推送结果:' . $ret); if ($ret) { Db::name('tb_user_push_event')->where(['id' => $user_push_list_res_id])->update(['update_time' => date("Y-m-d H:i:s"), 'status' => 1]); // 加入明细 $tb_shop_sms_money = Db::name('tb_shop_sms_money_detail')->insertGetId([ 'shop_id' => $event['shop_id'], 'type' => 2, 'money' => $one_sms_price, 'create_time' => date("Y-m-d H:i:s"), ]); if($tb_shop_sms_money) { // 扣掉余额 Db::name('tb_shop_sms_money')->where([ 'shop_id' => $event['shop_id'], ])->dec('money', $one_sms_price)->update(); } // 送券 if(isset($event['coupon'])) { $coupon = json_decode($event['coupon'], true); self::sendUserCoupon($event['shop_id'], $user['user_id'], $coupon['id'], $coupon['num']); } } else { Db::name('tb_user_push_event')->where(['id' => $user_push_list_res_id])->update(['update_time' => date("Y-m-d H:i:s"), 'status' => -1, 'error' => json_encode($push)]); } } sleep(1); } } } Db::name('tb_push_event')->where(['id' => $event['id']])->update(['status' => 2, 'send_time' => date('Y-m-d H:i:s')]); } } Db::commit(); }catch (\Exception $e) { Log::info('执行出错--' . $e->getMessage()); Db::rollback(); } } Log::info('没有推送任务可执行'); return true; } /** * 找到可以接收订阅消息的用户 * @param $type类型 1按次数发送 2自定义 3按标签 * @return array */ public static function getPullInfoUser($event): array { $user_arr = []; switch ($event['type']) { // 按可接收次数发送(模版推送)/全部绑定手机用户(短信推送) case 1; $user_arr = self::getPullInfoUserToNumber($event); break; // 自定义 case 2; $user_arr = self::getPullInfoUserToCustom($event); break; // 按标签 case 3; $user_arr = self::getPullInfoUserToLabel($event); break; } return $user_arr; } // 按照可接收次数获取用户信息 public static function getPullInfoUserToNumber($event) { $user_num = []; if($event['push_type'] == 1 && $event['can_send_times'] > 0) { $user_num = Db::name('tb_user_push_event_num')->alias('upen')->where('upen.num', '>=', $event['can_send_times']) ->join('tb_user_info u', 'upen.user_id = u.id') ->field('upen.user_id as user_id, upen.open_id as open_id') ->select() ->toArray(); } if($event['push_type'] == 2) { $user_num = Db::name('tb_user_info')->where('phone', '<>', '') ->field('id as user_id, wechat_ac_open_id as open_id, phone') ->select() ->toArray(); } return $user_num; } // 按照自定义获取用户信息 public static function getPullInfoUserToCustom($event) { $data_arr = []; // 按照性别 if(isset($event['gender']) && is_string($event['gender'])) { $gender_data_arr = []; $gender_arr = explode(',', $event['gender']); foreach ($gender_arr as $k => $gender) { // 未知 if($gender == 0) { $gender_data_res = Db::name('tb_user_info')->whereNotIn('sex', [0,1]) ->field('id as user_id, wechat_ac_open_id as open_id, phone') ->select() ->toArray(); } // 男 if($gender == 1) { $gender_data_res = Db::name('tb_user_info')->where('sex', 1) ->field('id as user_id, wechat_ac_open_id as open_id, phone') ->select() ->toArray(); } // 女 if($gender == 2) { $gender_data_res = Db::name('tb_user_info')->where('sex', 0) ->field('id as user_id, wechat_ac_open_id as open_id, phone') ->select() ->toArray(); } if($gender_data_arr) { $gender_data_arr = array_merge($gender_data_arr, $gender_data_res); }else { $gender_data_arr = $gender_data_res; } } if($gender_data_arr) { $data_arr = array_merge($data_arr, $gender_data_arr); } } // 按照下单次数 if(isset($event['order_history']) && is_string($event['order_history'])) { $order_history_arr = explode(',', $event['order_history']); $order_history_data_arr = []; foreach ($order_history_arr as $k => $order_history) { // 从未下单 if($order_history == 0) { $order_history_data_res = Db::name('tb_user_info') ->alias('u') // 用户表别名u ->leftJoin('tb_order_info o', 'u.id = o.user_id') ->where('o.user_id', 'null') ->distinct() ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->select() ->toArray(); } // 下过1单 if($order_history == 1) { $order_history_data_res = Db::name('tb_user_info') ->alias('u') // 关联订单表,使用内连接确保只包含有订单的用户 ->join('tb_order_info o', 'u.id = o.user_id') // 按用户ID分组 ->group('u.id') // 筛选出订单数量为1的用户 ->having('COUNT(o.id) = 1') // 选择需要的用户字段,同时查询订单数量(可选) ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->select() ->toArray(); } // 下过2-5单 if($order_history == 2) { $order_history_data_res = Db::name('tb_user_info') ->alias('u') // 关联订单表,使用内连接确保只包含有订单的用户 ->join('tb_order_info o', 'u.id = o.user_id') // 按用户ID分组 ->group('u.id') // 筛选出订单数量为1的用户 ->having('COUNT(o.id) BETWEEN 2 AND 5') ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->select() ->toArray(); } // 下过5单起 if($order_history == 3) { $order_history_data_res = Db::name('tb_user_info') ->alias('u') // 关联订单表,使用内连接确保只包含有订单的用户 ->join('tb_order_info o', 'u.id = o.user_id') // 按用户ID分组 ->group('u.id') // 筛选出订单数量为1的用户 ->having('COUNT(o.id) >= 5') ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->select() ->toArray(); } if($order_history_data_arr) { $order_history_data_arr = array_merge($order_history_data_arr, $order_history_data_res); ; }else { $order_history_data_arr = $order_history_data_res; } } if($order_history_data_arr) { $data_arr = array_merge($data_arr, $gender_data_arr); } } // 按照下单时间 if(isset($event['order_times']) && is_string($event['order_times'])) { $order_times_arr = explode(',', $event['order_times']); $order_times_data_arr = []; foreach ($order_times_arr as $k => $order_times) { // 今天下过 if($order_times == 0) { $order_times_data_res = Db::name('tb_user_info') ->alias('u') // 用户表别名u ->join('tb_order_info o', 'u.id = o.user_id') ->where('o.create_time', '>=', date('Y-m-d 00:00:00')) ->where('o.create_time', '<=', date('Y-m-d 23:59:59')) ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->distinct() ->select() ->toArray(); } // 昨天下过 if($order_times == 1) { $order_times_data_res = Db::name('tb_user_info') ->alias('u') // 用户表别名u ->join('tb_order_info o', 'u.id = o.user_id') ->where('o.create_time', '>=', date('Y-m-d 00:00:00', strtotime('-1 day'))) ->where('o.create_time', '<=', date('Y-m-d 23:59:59', strtotime('-1 day'))) ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->distinct() ->select() ->toArray(); } // 2周内没下过 if($order_times == 2) { $twoWeeksAgo = date('Y-m-d H:i:s', strtotime('-14 days')); $order_times_data_res = Db::name('tb_user_info') ->alias('u') // 用户表别名u // 左连接订单表,关联条件包含近两周内的订单 ->leftJoin('tb_order_info o', 'u.id = o.user_id') ->where('o.create_time', '>=', $twoWeeksAgo) ->where('o.id', 'null') ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->distinct() ->select() ->toArray(); } // 半个月-1个月没下过 if($order_times == 3) { $startTime = date('Y-m-d H:i:s', strtotime('-30 days')); $endTime = date('Y-m-d H:i:s', strtotime('-15 days')); $order_times_data_res = Db::name('tb_user_info') ->alias('u') // 用户表别名u // 左连接订单表,关联条件包含近两周内的订单 ->join('tb_order_info o', 'u.id = o.user_id') // 按用户分组 ->group('u.id') // 筛选最后下单时间在30天前到15天前之间的用户 ->having('MAX(o.create_time) >="'. $startTime .'" AND MAX(o.create_time) <= "' . $endTime . '"') ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->select() ->toArray(); } // 1个月以上没下过 if($order_times == 4) { $oneMonthAgo = date('Y-m-d H:i:s', strtotime('-30 days')); $order_times_data_res = Db::name('tb_user_info') ->alias('u') // 用户表别名u // 左连接订单表,关联条件包含近两周内的订单 ->join('tb_order_info o', 'u.id = o.user_id') // 按用户分组 ->group('u.id') // 筛选最后下单时间在1个月前及更早 ->having('MAX(o.create_time) <= "' . $oneMonthAgo . '"') // ->field('u.id as user_id, u.wechat_ac_open_id as open_id, MAX(o.create_time) as last_order_time') ->field('u.id as user_id, u.wechat_ac_open_id as open_id, u.phone as phone') ->select() ->toArray(); } if($order_times_data_arr) { $order_times_data_arr = array_merge($order_times_data_arr, $order_times_data_res); }else { $order_times_data_arr = $order_times_data_res; } } if($order_times_data_arr) { $data_arr = array_merge($data_arr, $order_times_data_arr); } // 按照会员 if(isset($event['vip']) && is_string($event['vip'])) { $vip_data_arr = []; $vip_arr = explode(',', $event['vip']); foreach ($vip_arr as $k => $vip) { // 非会员 if($vip == 0) { $vip_data_res = Db::name('tb_user_info')->whereNotIn('is_vip', 0) ->field('id as user_id, wechat_ac_open_id as open_id,phone') ->select() ->toArray(); } // 会员 if($vip == 1) { if($event['push_type'] == 1) { $vip_data_res = Db::name('tb_user_info')->where('is_vip', 1) ->field('id as user_id, wechat_ac_open_id as open_id,phone') ->select() ->toArray(); } // 推送类型为短信推送 if($event['push_type'] == 2) { // 全部会员 if($event['vip_lavel'] == 0) { $vip_data_res = Db::name('tb_user_info')->where('is_vip', 1) ->field('id as user_id, wechat_ac_open_id as open_id,phone') ->select() ->toArray(); } // 仅限等级 if($event['vip_lavel'] == 1) { $vip_data_res = Db::name('tb_user_info')->where(['is_vip' => 1, 'member_level_id' => $event['vip_lavel_id']]) ->field('id as user_id, wechat_ac_open_id as open_id,phone') ->select() ->toArray(); } // 以上等级 if($event['vip_lavel'] == 2) { // 找到会员详情 $vip_config_info = Db::name('tb_member_level_config')->where(['id' => $event['vip_lavel_id']])->find(); if($vip_config_info) { $vip_config_info = Db::name('tb_member_level_config')->where('experience_value', '>', $vip_config_info['experience_value'])->column('id'); if($vip_config_info) { $vip_data_res = Db::name('tb_user_info')->where(['is_vip' => 1])->whereIn('member_level_id', $vip_config_info) ->field('id as user_id, wechat_ac_open_id as open_id,phone') ->select() ->toArray(); } } } } } if($vip_data_arr) { $vip_data_arr = array_merge($vip_data_arr, $vip_data_res); }else { $vip_data_arr = $vip_data_res; } } if($vip_data_arr) { $data_arr = array_merge($data_arr, $vip_data_arr); } } // 按照手机号 if(isset($event['telphone']) && is_string($event['telphone'])) { $telphone_data_arr = []; $telphone_arr = explode(',', $event['telphone']); foreach ($telphone_arr as $k => $telphone) { // 非绑定 if($telphone == 0) { $telphone_data_res = Db::name('tb_user_info')->where(function ($query) { $query->where('phone', 'null') // 为 NULL ->whereOr('phone', ''); // 为空字符串 }) ->field('id as user_id, wechat_ac_open_id as open_id') ->select() ->toArray(); } // 已绑定 if($telphone == 1) { $telphone_data_res = Db::name('tb_user_info')->where('phone', '<>', '') ->field('id as user_id, wechat_ac_open_id as open_id, phone') ->select() ->toArray(); } if($telphone_data_arr) { $telphone_data_arr = array_merge($telphone_data_arr, $telphone_data_res); }else { $telphone_data_arr = $telphone_data_res; } } if($telphone_data_arr) { $data_arr = array_merge($data_arr, $telphone_data_arr); } } } return $data_arr; } // 按照标签获取用户信息 public static function getPullInfoUserToLabel($event) { $data_arr = []; $label = Db::name('tb_user_label')->whereIn('label_id', explode(',', $event['user_label'])) ->distinct() ->column('user_id'); if($label) { $data_arr = Db::name('tb_user_info')->whereIn('id', $label) ->field('id as user_id, wechat_ac_open_id as open_id, phone') ->select() ->toArray(); } return $data_arr; } // 给用户送送优惠券 public static function sendUserCoupon($shop_id, int | array $user_id, $coupon_id, $number = 1) { if ($number === null || $number <= 0) { Log::info('推送用户送券发放数量不能小于0'); return false; } $url = 'http://192.168.1.31/market/admin/coupon/grant'; $res = http_post($url, [ 'userId' => $user_id, 'couponId' => $coupon_id, 'num' => $number, 'shopId' => $shop_id, ]); Log::info('发券:' . $res); $res = json_decode($res, true); if($res['code'] == 200) { Log::info('userId:' . $user_id . ' couponId:' . $coupon_id . ' shopId:' . $shop_id . ' 发券成功'); }else { Log::info('userId:' . $user_id . ' couponId:' . $coupon_id . ' shopId:' . $shop_id . ' 发券失败' . ' 信息:' . $res['msg']); } return true; } }