Initial project files: BESCMS full source

This commit is contained in:
bes
2026-08-13 23:37:46 +08:00
parent fa6cddb540
commit 2e26f85723
2166 changed files with 396625 additions and 0 deletions
+701
View File
@@ -0,0 +1,701 @@
<?php namespace Phpcmf\Model\Weixin;
class Weixin extends \Phpcmf\Model
{
// 发送通知
public function send_notice($value, $siteid, $error) {
$content = [];
if (isset($value['config']['weixin']['tpl_content']) && is_array($value['config']['weixin']['tpl_content'])) {
$content = $value['config']['weixin']['tpl_content'];
if (!$content) {
$error[] = $debug = '自定义通知内容参数tpl_content不存在';
CI_DEBUG && log_message('debug', '通知任务('.$value['name'].')微信执行失败:'.$debug);
}
} else {
$rt = \Phpcmf\Service::L('notice')->_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.'<br>'.$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;
}
}