_get_tpl_content($siteid, $value['name'], 'weixin', $value['data']); if (!$rt['code']) { $error[] = $rt['msg']; CI_DEBUG && log_message('debug', '通知任务('.$value['name'].')执行失败:'.$rt['msg']); } else { $xml = \Phpcmf\Service::L('notice')->_xml_array($rt['msg']); if (!$xml || !isset($xml['xml']) || !$xml['xml']) { $error[] = $debug = 'xml解析失败,检查文件格式是否正确:'.$value['name'].'.html'; CI_DEBUG && log_message('debug', '通知任务('.$value['name'].')微信执行失败:'.$debug.'
'.$rt['msg']); } else { $content = $xml['xml']; } } } if ($content) { $rt = \Phpcmf\Service::M('member')->weixin_template($value['data']['uid'], $content['id'], $content['param'], $content['url']); if (!$rt['code']) { $error[] = $debug = '微信消息执行错误:'.$rt['msg']; CI_DEBUG && log_message('debug', '通知任务('.$value['name'].')微信执行失败:'.$debug); } else { // 成功 unset($value['config']['weixin']); } } return [$error, $value]; } // 获取微信表名 public function wxtable($name) { return weixin_wxtable($name); } // 获取配置信息 public function get_config($name) { $data = $this->db->table(weixin_wxtable('weixin'))->where('name', $name)->get()->getRowArray(); if ($data) { $data = dr_string2array($data['value']); return $data; } else { $this->db->table(weixin_wxtable('weixin'))->insert([ 'name' => $name, 'value' => '', ]); return []; } } // 存储配置信息 public function save_config($name, $post) { $rt = $this->db->table(weixin_wxtable('weixin'))->replace([ 'name' => $name, 'value' => dr_array2string($post), ]); if ($rt->connID->error) { return dr_return_data(0, $this->table.': '.$rt->connID->error); } return dr_return_data(1); } // 上传图文消息内的图片获取URL public function get_file_media_id($access_token, $type, $filename) { // 查询本地库 $rt = $this->table(weixin_wxtable('media_id'))->where('file', md5($filename))->getRow(); if ($rt) { return dr_return_data(1, 'ok', $rt); } $id = dr_get_file($filename, true); if (!$id) { return dr_return_data(0, '文件不存在'); } $file = WRITEPATH.'temp/sync_'.md5($id).'.'.substr(strrchr($id, '.'), 1);; $c = dr_catcher_data($id); if (!$c) { return dr_return_data(0, '无法获取文件内容:'.$id); } $rt = file_put_contents($file, $c); if (!$rt) { return dr_return_data(0, '本地文件写入失败:'.$id); } elseif (!is_file($file)) { return dr_return_data(0, '本地文件不存在'); } $rt = ihttp_request( 'https://api.weixin.qq.com/cgi-bin/material/add_material?access_token='.$access_token, [ 'type' => 'image', 'media' => '@' . $file ] ); if ($rt['code']) { // 更新到媒体表 $save = [ 'file' => md5($filename), 'url' => $rt['data']['url'], 'media_id' => $rt['data']['media_id'], ]; $this->table(weixin_wxtable('media_id'))->insert($save); } return $rt; } // 同步素材到服务器 public function sync_content($access_token, $t, $return = 0) { if (!$t['media_id'] || $return) { // 图文素材 $url = 'https://api.weixin.qq.com/cgi-bin/draft/add?access_token='.$access_token; $param = [ 'articles' => [ ] ]; $value = dr_string2array($t['content']); for ($i = 1; $i<=9; $i++) { if (!$value['title_'.$i]) { break; } if (!$value['thumb_'.$i]) { return dr_return_data(0, '素材['.$t['title'].'] 第'.$i.'个内容缺少封面图片'); } $cache = []; $content = htmlspecialchars_decode($value['content_'.$i]); if (preg_match_all("/(src)=([\"|']?)([^ \"'>]+\.(gif|jpg|jpeg|png))\\2/i", $content, $imgs)) { foreach ($imgs[3] as $image) { if (!isset($cache[$image])) { $tmp = WRITEPATH.'temp/sync_'.md5($image).'.'.substr(strrchr($image, '.'), 1);; if (strpos($image, '/') === 0 && is_file(WEBPATH.$image)) { $file = file_get_contents(WEBPATH.$image); } else { $file = dr_catcher_data($image); } if ($file && file_put_contents($tmp, $file)) { $cache[$image] = 1; $rt = ihttp_request( 'https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token='.$access_token, [ 'media' => '@' . $tmp ] ); // 更新到内容 $rt['code'] && $content = str_replace($image, $rt['data']['url'], $content); } } } } $rt = $this->get_file_media_id($access_token, 'thumb', $value['thumb_'.$i]); if (!$rt['code']) { $rt['msg'] = $value['thumb_'.$i].' - 附件上传失败:'.$rt['msg']; return $rt; } $thumb_id = $rt['data']['media_id']; $param['articles'][] = [ "title" => $value['title_'.$i], "thumb_media_id" => $thumb_id, "author" => $value['author_'.$i], "digest" => dr_strcut($value['description_'.$i], 50), "content" => $content, "show_cover_pic" => 1, "content_source_url" => html_entity_decode($value['url_'.$i]) ]; } if (!count($param['articles'])) { return dr_return_data(0, '素材['.$t['title'].'] 资源信息为空无法同步'); } $rt = wx_post_https_json_data($url, $param); if (!$rt['code']) { return $rt; } $media_id = $rt['data']['media_id']; if ($return) { return dr_return_data(1, $media_id); } } else { $media_id = $t['media_id']; } if (!$t['publish_id']) { // 发布内容 $rt = wx_post_https_json_data( 'https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token='.$access_token, [ 'media_id' => $media_id ] ); if (!$rt['code']) { $this->table(weixin_wxtable('content'))->update($t['id'], [ 'media_id' => $media_id ]); return $rt; } // 存储 $this->table(weixin_wxtable('content'))->update($t['id'], [ 'media_id' => $media_id, 'publish_id' => $rt['data']['publish_id'], ]); } else { // 查询结果 $rt = wx_post_https_json_data( 'https://api.weixin.qq.com/cgi-bin/freepublish/get?access_token='.$access_token, [ 'publish_id' => $t['publish_id'] ] ); if (!$rt['code']) { return $rt; } $this->table(weixin_wxtable('content'))->update($t['id'], [ 'status' => $rt['data']['publish_status'], 'article_id' => $rt['data']['article_id'] ]); } return dr_return_data(1, 'ok'); } // 高级群发 public function sendall($groupid, $data) { $rt = weixin_get_access_token(); if (!$rt['code']) { return $rt; } // 将内容转成为草稿箱 $rs = $this->sync_content($rt['msg'], $data, 1); if (!$rs['code']) { return $rs; } $param = [ 'msgtype' => 'mpnews', 'mpnews' => [ 'media_id' => $rs['msg'] ], ]; if ($groupid) { $param['filter'] = [ 'is_to_all' => false, 'tag_id' => $groupid, ]; } else { $param['filter'] = [ 'is_to_all' => true, ]; } $rt = wx_post_https_json_data( 'https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token='.$rt['msg'], $param ); if (!$rt['code']) { return $rt; } return dr_return_data(1, 'ok'); } // 客服群发 文本内容 public function send_for_text($groupid, $data) { $param = [ 'msgtype' => 'text', 'text' => [ 'content' => $data ], ]; if ($groupid) { if (strlen($groupid) > 5) { // 指定粉丝 $user = [ [ 'openid' => $groupid, ] ]; } else { // 粉丝组 $user = $this->table(weixin_wxtable('user'))->where('subscribe>0 and subscribe_time > '.(SYS_TIME - 3600*48))->where('`groupids` like "%\"'.$groupid.'\"%"')->getAll(); } } else { // 全部粉丝 $user = $this->table(weixin_wxtable('user'))->where('subscribe>0 and subscribe_time > '.(SYS_TIME - 3600*48))->getAll(); } if (!$user) { return dr_return_data(0, '48小时内互动的粉丝不存在'); } return $this->_save_send_data($user, $param); } // 客服群发 内容模块 public function send_for_module($dir, $ids) { $data = $this->table_site($dir)->where_in('id', $ids)->getAll(9); if (!$data) { return dr_return_data(0, '模块内容不存在'); } $param = [ 'msgtype' => 'news', 'news' => [ 'articles' => [] ], ]; foreach ($data as $t) { $param['news']['articles'][] = [ "title" => $t['title'], "description" => $t['description'], "picurl" => dr_get_file($t['thumb'], true), "url" => dr_url_prefix($t['url'], $dir) ]; } $user = $this->table(weixin_wxtable('user'))->where('subscribe>0 and subscribe_time > '.(SYS_TIME - 3600*48))->getAll(); if (!$user) { return dr_return_data(0, '粉丝信息不存在'); } return $this->_save_send_data($user, $param); } // 客服群发 内容素材 public function send_for_content($groupid, $data) { $param = [ 'msgtype' => 'mpnewsarticle', 'mpnewsarticle' => [ 'article_id' => $data['article_id'] ], ]; if ($groupid) { if (strlen($groupid) > 5) { // 指定粉丝 $user = [ [ 'openid' => $groupid, ] ]; } else { // 粉丝组 $user = $this->table(weixin_wxtable('user'))->where('groupid', $groupid)->where('subscribe>0 and subscribe_time > '.(SYS_TIME - 3600*48))->getAll(); } } else { // 全部粉丝 $user = $this->table(weixin_wxtable('user'))->where('subscribe>0 and subscribe_time > '.(SYS_TIME - 3600*48))->getAll(); } if (!$user) { return dr_return_data(0, '48小时内互动的粉丝不存在'); } elseif (!$param) { return dr_return_data(0, '未知的tid类别信息:'.$data['tid']); } return $this->_save_send_data($user, $param); } // 存储客服消息发送记录表 private function _save_send_data($user, $param) { $id = (int)substr(SYS_TIME - rand(0, 9999), 0, 10); foreach ($user as $t) { $rt = $this->table(weixin_wxtable('send'))->insert([ 'cid' => $id, 'openid' => $t['openid'], 'status' => 0, 'content' => dr_array2string($param) ]); if (!$rt['code']) { return $rt; } } return dr_return_data($id, 'ok'); } // 存储客户端消息 public function save_message($data) { $rt = $this->table(weixin_wxtable('message'))->insert([ 'tid' => $data['MsgType'], 'openid' => $data['FromUserName'], 'nickname' => '', 'headimgurl' => '', 'status' => 0, 'content' => dr_array2string($data), 'inputtime' => $data['CreateTime'], ]); if (!$rt['code']) { log_message('error', '微信消息存储失败:'.$rt['msg']); } } // 更新统计数据 public function update_count_fans() { return $this->table(weixin_wxtable('count_fans'))->order_by('id desc')->getAll(10, 'date'); $seven_days = array( date('Ymd', strtotime('-1 days')), date('Ymd', strtotime('-2 days')), date('Ymd', strtotime('-3 days')), date('Ymd', strtotime('-4 days')), date('Ymd', strtotime('-5 days')), date('Ymd', strtotime('-6 days')), date('Ymd', strtotime('-7 days')), ); // 本周数据 $week_stat_fans = $this->table(weixin_wxtable('count_fans'))->where_in('date', $seven_days)->getAll(0, 'date'); $stat_update_yes = false; foreach ($seven_days as $sevens) { if (empty($week_stat_fans[$sevens]) || $week_stat_fans[$sevens]['cumulate'] <=0) { $stat_update_yes = true; break; } } if (empty($stat_update_yes)) { return $week_stat_fans; } $rt = weixin_get_access_token(); if ($rt['code']) { $access_token = $rt['msg']; $url = 'https://api.weixin.qq.com/datacube/getusersummary?access_token='.$access_token; $rt = wx_post_https_json_data($url, [ "begin_date" => date('Y-m-d', strtotime('-7 days')), "end_date" => date('Y-m-d', strtotime('-1 days')), ]); if ($rt['code']) { $summary = $rt['data']; } $url = 'https://api.weixin.qq.com/datacube/getusercumulate?access_token='.$access_token; $rt = wx_post_https_json_data($url, [ "begin_date" => date('Y-m-d', strtotime('-7 days')), "end_date" => date('Y-m-d', strtotime('-1 days')), ]); if ($rt['code']) { $cumulate = $rt['data']; } $result = []; if (!empty($summary['list'])) { foreach ($summary['list'] as $row) { $key = str_replace('-', '', $row['ref_date']); $result[$key]['new'] = intval($result[$key]['new']) + $row['new_user']; $result[$key]['cancel'] = intval($result[$key]['cancel']) + $row['cancel_user']; } } if (!empty($cumulate['list'])) { foreach ($cumulate['list'] as $row) { $key = str_replace('-', '', $row['ref_date']); $result[$key]['cumulate'] = $row['cumulate_user']; } } foreach ($result as $i => $t) { $this->table(weixin_wxtable('count_fans'))->replace([ 'new' => (int)$t['new'], 'cancel' => (int)$t['cancel'], 'cumulate' => (int)$t['cumulate'], 'date' => $i, ]); } return $result; } return $week_stat_fans; } /** * 发送微信通知模板 * * $uid 会员id * $id 微信模板id * $data 通知内容 * $url 详细地址 * $color top颜色 */ public function send_template($uid, $id, $data, $url = '', $color = '') { $rt = weixin_get_access_token(); if (!$rt['code']) { return dr_return_data(0, $rt['msg']); } if (is_array($uid)) { $error = []; foreach ($uid as $u) { $oauth = $this->db->table('member_oauth')->where('uid', $u)->where('oauth', 'wechat')->get()->getRowArray(); if (!$oauth) { $error[] = 'uid('.$u.')未绑定账号'; continue; } $rs = wx_post_https_json_data('https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='.$rt['msg'], [ 'touser' => $oauth['oid'], 'template_id' => $id, 'url' => $url, 'topcolor' => $color, 'data' => $data, ]); if (!$rs['code']) { $error[] = $rs['msg']; } } if (dr_count($error) == dr_count($uid)) { return dr_return_data(0, implode(';', $error)); } else { return dr_return_data(1, '发送成功'); } } else { $oauth = $this->db->table('member_oauth')->where('uid', $uid)->where('oauth', 'wechat')->get()->getRowArray(); if (!$oauth) { return dr_return_data(0, 'uid('.$uid.')未绑定账号'); } return wx_post_https_json_data('https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='.$rt['msg'], [ 'touser' => $oauth['oid'], 'template_id' => $id, 'url' => $url, 'topcolor' => $color, 'data' => $data, ]); } } /** * 发送微信订阅通知 * * $uid 会员id * $id 所需下发的订阅模板id * $data 通知内容 * $url 详细地址 */ public function send_biz($uid, $id, $data, $url = '') { $oauth = $this->db->table('member_oauth')->where('uid', $uid)->where('oauth', 'wechat')->get()->getRowArray(); if (!$oauth) { return dr_return_data(0, 'uid('.$uid.')未绑定账号'); } $rt = weixin_get_access_token(); if (!$rt['code']) { return dr_return_data(0, $rt['msg']); } return wx_post_https_json_data('https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token='.$rt['msg'], [ 'touser' => $oauth['oid'], 'template_id' => $id, 'page' => $url, 'data' => $data, ]); } // 缓存 public function cache($siteid = SITE_ID) { $table = \Phpcmf\Service::M()->dbprefix('app_weixin'); if (!\Phpcmf\Service::M()->is_table_exists($table)) { \Phpcmf\Service::M('table')->create_table( $table, [ 'id' => 'int(10) NOT NULL AUTO_INCREMENT COMMENT \'id\'', 'name' => 'varchar(200) NOT NULL COMMENT \'公众号名称\'', ], [ 'PRIMARY KEY (`id`)', ], '多公众号表' ); } $mr = \Phpcmf\Service::M()->table('app_weixin')->get(1); if (!$mr) { \Phpcmf\Service::M()->table('app_weixin')->replace([ 'id' => 1, 'name' => '默认公众号' ]); } $alls = $this->table('app_weixin')->getAll(); $more_cache = []; foreach ($alls as $more) { $more_cache[$more['id']] = $more['name']; ///////////////////////// $data = $this->table(weixin_wxtable('weixin', $more['id']))->getAll(); $cache = []; if ($data) { foreach ($data as $t) { $value = dr_string2array($t['value']); $cache[$t['name']] = $value; } } $kws = []; $data = $this->table(weixin_wxtable('reply', $more['id']))->getAll(); if ($data) { foreach ($data as $t) { $arr = explode(',', $t['keyword']); if ($arr) { foreach ($arr as $kw) { $kws[$kw] = $t['id']; } } } uksort($kws, function ($a, $b) { return strlen($b) - strlen($a); }); } \Phpcmf\Service::L('cache')->set_file('weixin-kws'.($more['id'] > 1 ? '_more_'.$more['id'] : ''), $kws); $table = $this->dbprefix(weixin_wxtable('template', $more['id'])); if (!\Phpcmf\Service::M()->is_table_exists($table)) { \Phpcmf\Service::M('table')->create_table( $table, [ 'id' => 'int(10) NOT NULL AUTO_INCREMENT COMMENT \'id\'', 'title' => 'varchar(200) NOT NULL COMMENT \'主题\'', 'groupid' => 'int(10) NOT NULL COMMENT \'发送标签组\'', 'counts' => 'int(10) NOT NULL COMMENT \'发送数\'', 'content' => 'text NOT NULL COMMENT \'消息内容\'', 'template' => 'varchar(200) NOT NULL COMMENT \'模板ID\'', 'url' => 'varchar(200) NOT NULL COMMENT \'url\'', 'color' => 'varchar(200) NOT NULL COMMENT \'主题颜色\'', 'inputtime' => 'int(10) NOT NULL COMMENT \'录入时间\'', ], [ 'PRIMARY KEY (`id`)', 'KEY `inputtime` (`inputtime`)', ], '微信模板消息记录表' ); } $table = $this->dbprefix(weixin_wxtable('user', $more['id'])); \Phpcmf\Service::M()->query('UPDATE `'.$table.'` SET `subscribe`=`subscribe_time` where `subscribe`=1'); $table = $this->dbprefix(weixin_wxtable('content', $more['id'])); $t = \Phpcmf\Service::M('table'); $m = \Phpcmf\Service::M(); if (!$m->is_field_exists($table, 'nums')) { $t->add_field($table, 'nums', 'tinyint(2)', 'DEFAULT \'0\'', ''); } if (!$m->is_field_exists($table, 'status')) { $t->add_field($table, 'status', 'tinyint(2)', 'DEFAULT \'0\'', ''); } if (!$m->is_field_exists($table, 'publish_id')) { $t->add_field($table, 'publish_id', 'varchar(255)', 'DEFAULT \'\'', ''); } if (!$m->is_field_exists($table, 'article_id')) { $t->add_field($table, 'article_id', 'varchar(255)', 'DEFAULT \'\'', ''); } \Phpcmf\Service::L('cache')->set_file('weixin'.($more['id'] > 1 ? '_more_'.$more['id'] : ''), $cache); /// //////////////////////// } \Phpcmf\Service::L('cache')->set_file('app_weixin', count($more_cache) == 1 ? '' : $more_cache); return; } }