_customModel = model('Custom');
$this->_customMediaModel = model('CustomMedia');
$this->_customMediaPushModel = model('CustomMediaPush');
$this->_customUrlModel = model('CustomUrl');
$this->_adminConfigModel = model('AdminConfig');
$this->_subscriptionModel = model('Subscription');
$this->_bookModel = model('Book');
$this->_referralModel = model('Referral');
}
/**
* 创建客服消息素材
* @param int $adminId 渠道商id
* @param string $msgType 客服消息类型
* @param string $msgContent 文字链消息内容
* @return int|string
* @throws \Exception
*/
public function createMedia($adminId, $msgType, $msgContent)
{
$media = [
'created_admin_id' => $adminId,
'message_type' => $msgType,
'createtime' => time(),
'updatetime' => time(),
];
if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
if (!Common::isJson($msgContent)) {
throw new \Exception('图文参数不是json格式');
}
//检测是否有null项 有的话清理掉
if (strrpos($msgContent, 'null') !== false) {
$data = json_decode($msgContent, true);
$data = array_filter($data);
$msgContent = json_encode($data);
}
$media['message_json'] = $msgContent;
} elseif ($msgType == Message::MESSAGE_TYPE_LINK) {
if (!Common::isJson($msgContent)) {
throw new \Exception('文字链参数不是json格式');
}
$media['message_text'] = $msgContent;
} else {
throw new \Exception('message_type不正确:' . $msgType);
}
$id = $this->_customMediaModel->insertGetId($media);
return $id;
}
/**
* 创建客服消息推送记录
* @param $params
* @param int $msgType 客服消息类型
* @param int $officialType 公众号类型
* @param array $officialAccountIds 公众号id列表
* @param string $msgContent 消息体
* @return int|string
* @throws \Exception
*/
public function createCustomMediaPush($params, $msgType, $officialType, $officialAccountIds, $msgContent, $other = [])
{
$user_json_default = json_encode(["all" => "0", "sex" => -1, "tag" => -1, "consume" => -1, "kandian" => -1, "subscribe_time" => -1], JSON_UNESCAPED_UNICODE);
$model = [
'custom_media_id' => $params['custom_media_id'] ? $params['custom_media_id'] : 0,
'message_type' => $msgType,
'official_account_type' => $officialType,
'sendtime' => strtotime($params['sendtime']),
'user_json' => $params['user_json'] ?? $user_json_default,
'created_admin_id' => $params['created_admin_id'],
'created_from' => $params['created_from'],
'createtime' => time(),
'updatetime' => time(),
'select_type' => $other['select_type'] ?? 0,
'group_id' => $other['group_id'] ?? '',
];
if ($officialType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE) {
$model['official_account_service_ids'] = implode(',', $officialAccountIds);
} elseif ($officialType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SUBSCRIPTION) {
$model['official_account_subscribe_ids'] = implode(',', $officialAccountIds);
} else {
throw new \Exception('公众号类型不正确:' . $officialType);
}
if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
if (!Common::isJson($msgContent)) {
throw new \Exception('图文参数不是json格式');
}
$model['message_json'] = $msgContent;
} elseif ($msgType == Message::MESSAGE_TYPE_LINK) {
if (!Common::isJson($msgContent)) {
throw new \Exception('文字链参数不是json格式');
}
$model['message_text'] = $msgContent;
} else {
throw new \Exception('message_type不正确:' . $msgType);
}
$id = $this->_customMediaPushModel->insertGetId($model);
return $id;
}
/**
* 删除客服消息 push
* @param $pushId
* @return bool
* @throws \Exception
*/
public function delCustomMediaPush($pushId)
{
$result = $this->_customMediaPushModel->save(['status' => Custom::CUSTOM_MEDIA_PUSH_STATUS_DELETE],
['id' => $pushId]);
if ($result === false) {
throw new \Exception('删除客服消息失败,push_id:' . $pushId);
}
$result = $this->_customModel->save(['statue' => Custom::CUSTOM_STATUE_DELETE],
['custom_media_push_id' => $pushId]);
if ($result === false) {
throw new \Exception('删除客服消息失败!!,push_id:' . $pushId);
}
return true;
}
/**
* 更新客服消息 push
* @param $pushId
* @param $sendtime
* @return bool
* @throws \Exception
*/
public function editCustomMediaPush($pushId, $params)
{
$mediaData = $this->_customMediaPushModel->where('id', $pushId)->find();
//更新
if ($mediaData) {
$sendTime = strtotime($params['sendtime']);
if ($params['select_type'] == 1 && $params['group_id']) {
//分组 拉取公众号ID
$groupIds = trim($params['group_id'], ',');
$channelRows = model("VipGroupInfo")->where('group_id', 'in', $groupIds)->field("channel_id")->select();
$channelIds = array_column($channelRows, 'channel_id');
} else {
$channelIds = $params['service_id'];
}
//删除之前的数据
$this->_customMediaPushModel->where('id', $pushId)->delete();
$this->_customModel->where('custom_media_push_id', $pushId)->delete();
$this->_customUrlModel->where('custom_media_push_id', $pushId)->delete();
$result = $this->multiSendMessageService($mediaData['custom_media_id'], $params['sendtime'], $channelIds, $mediaData['created_admin_id'], ['group_id' => $params['group_id'], 'select_type' => $params['select_type']]);
if ($result['error'] == ErrorCode::SUCCESS) {
return true;
} else {
return false;
}
}
return true;
}
/**
* 创建客服消息推送
* @param array $params
* @param int $msgType 消息类型
* @param string $msgContent 图文客服消息内容
* @return array
* @throws \Exception
*/
public function createCustom($params, $msgType, $msgContent)
{
$sendTime = strtotime($params['sendtime']);
$user_json_default = json_encode(json_encode(["all" => "0", "sex" => -1, "tag" => -1, "consume" => -1, "kandian" => -1, "subscribe_time" => -1], JSON_UNESCAPED_UNICODE), JSON_UNESCAPED_UNICODE);
$custom = [
'admin_id' => $params['admin_id'],
'created_admin_id' => $params['created_admin_id'],
'custom_media_push_id' => $params['custom_media_push_id'],
'title' => $params['title'],
'message_type' => $msgType,
'user_json' => $params['user_json'] ?? $user_json_default,
'official_account_type' => $params['official_account_type'],
'official_account_id' => $params['official_account_id'],
'sendtime' => $sendTime,
'created_from' => $params['created_from'],
'createtime' => time(),
'updatetime' => time(),
'message_json' => json_encode([]),
];
//替换占位符
$msgContent = UrlService::instance()->replaceReferralHost($params['admin_id'], $msgContent)->data;
if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
$aMessageJson = json_decode($msgContent, true);
} else {
$custom['message_json'] = json_encode([]);
$aMessageJson = json_decode($msgContent, true);
}
# 这里的bookIds 可能为空
$bookIds = array_column($aMessageJson, 'book_id');
$customId = $this->_customModel->allowField(true)->insertGetId($custom);
if ($customId) {
$pointInfo = $this->_buildPointInfo($msgType, $aMessageJson, $sendTime, $customId, $params['admin_id']);
foreach ($pointInfo as $key=>&$item) {
$item = UrlService::instance()->replaceReferralHost($params['admin_id'], $item)->data;
}
$updateData = [];
$updateData = array_merge($updateData, $pointInfo);
$this->_customModel->update($updateData, ['id' => $customId]);
} else {
Log::error('customId 未插入');
Log::error($custom);
}
return [$customId, $bookIds, $aMessageJson];
}
#region 构造链接
/**
* 构造url
* @param int $channelId 渠道商id
* @param array $params 参数
* @return string url
* @throws \Exception
*/
public function buildUrl($channelId, $params)
{
$type = $params['type'];
$url = '';
switch ($type) {
case Message::MESSAGE_LINK_TYPE_BOOK:
//$url = $this->_buildBookUrl($channelId, $params);
$url = $this->_buildBookUrl_v2($channelId, $params);
break;
case Message::MESSAGE_LINK_TYPE_ACTIVITY:
$url = $this->_buildActivityUrl($channelId, $params['activity_id']);
break;
case Message::MESSAGE_LINK_TYPE_RECENT:
$url = $this->_buildRecentUrl($channelId);
break;
case Message::MESSAGE_LINK_TYPE_MENU:
$url = $this->_buildMenuUrl($channelId, $params['menu_id']);
break;
case Message::MESSAGE_LINK_TYPE_NOURL:
$url = '';
break;
case Message::MESSAGE_LINK_TYPE_URL:
$url = $params['url'];
break;
case Message::MESSAGE_LINK_TYPE_CUSTOMIZE_ACTIVITY:
$url = $this->_buildVipActivityUrl($channelId, $params['activity_id']);
break;
case Message::MESSAGE_LINK_TYPE_SIGN:
$url = $params['url'];
break;
//如果是常用链接类型,替换当前的渠道域名信息
case Message::MESSAGE_LINK_TYPE_DAILY_LINK:
$dailyLink = CommonService::instance()->getTmpDailyLink();
if(isset($dailyLink[$params['url']]['link'])){
$url = getCurrentDomain($channelId,$dailyLink[$params['url']]['link'],[],$dailyLink[$params['url']]['isUseMenuDomain']);
}else{
//$url = $params['url'];
$url = getCurrentDomain($channelId,$params['url'],[]);
}
break;
default:
break;
}
return $url;
}
/**
* 获取书籍对应的推广链接
* @param $channelId
* @param $params
* @return string
* @throws \Exception
*/
private function _buildBookUrl($channelId, $params)
{
try {
$redisService = new RedisService();
$firstChapter = $this->_bookModel->getChapterLimit($params['book_id'], 0, 0);
if (empty($firstChapter)) {
throw new \Exception('获取小说第一章失败,bookId:' . $params['book_id']);
}
$referral = [
'type' => 1,
'book_id' => $params['book_id'],
'chapter_id' => $firstChapter['id'],
'chapter_idx' => 1,
'chapter_name' => $firstChapter['name'],
'cost' => $params['cost'],
'name' => $params['channel_name'],
'wx_type' => $params['wx_type'],
'push' => $params['push'] ?? '0',
'guide_chapter_idx' => $params['guide_chapter_idx'],
'admin_id' => $channelId,
'createtime' => time(),
];
$refId = $this->_referralModel->allowField(true)->insertGetId($referral);
$sourceUrl = '/index/book/chapter?book_id=' . $referral['book_id'] . '&sid=' . $referral['chapter_id'] . '&referral_id=' . $refId;
$sourceUrl = getCurrentDomain($channelId, $sourceUrl);
$updateData['source_url'] = $sourceUrl;
$shortUrl = new ShortUrl();
//生成腾讯短链
$updateData['short_url_qq'] = $shortUrl->tencent($channelId, $sourceUrl);
//生成sina短链
$updateData['short_url_weibo'] = $shortUrl->sina($sourceUrl);
$this->_referralModel->save($updateData, ['id' => $refId]);
return $sourceUrl;
} catch (\Exception $e) {
throw $e;
}
}
/**
* 根据参数,生成阅读页链接
* @param $channelId
* @param $params
* @return string
* @throws \Exception
*/
private function _buildBookUrl_v2($channelId, $params)
{
try {
$url = '';
if ($params['type'] == Message::MESSAGE_LINK_TYPE_BOOK) {
$sourceUrl = '/index/book/chapter?book_id=' . $params['book_id'];
$url = getCurrentDomain($channelId, $sourceUrl);
}
return $url;
} catch (\Exception $e) {
throw $e;
}
}
/**
* 获取活动id
* @param int $channelId 渠道商对应的appId
* @param int $activityId 活动id
* @return string
*/
private function _buildActivityUrl($channelId, $activityId)
{
$activityObj = Activity::get(['id' => $activityId]);
if ($activityObj->type == ActivityConstants::ACTIVITY_TYPE_CUSTOM) {
$config_id = $activityObj->config_id;
$activity_config = Config::get('site.activity_config');
$url = getCurrentDomain($channelId, '/s/' . $activityId . '/r/' . $activity_config['config'][$config_id]['resource_id']);
} else if ($activityObj->type == ActivityConstants::ACTIVITY_TYPE_GIVE) {
$config_id = $activityObj->config_id;
$activity_config = Config::get('site.activity_give_config');
$url = getCurrentDomain($channelId, '/s/' . $activityId . '/r/' . $activity_config['config'][$config_id]['resource_id']);
} else {
$url = getCurrentDomain($channelId, '/s/' . $activityId);
}
return $url;
}
/**
* 获取VIP活动对应渠道的id
* @param int $channelId 渠道商对应的id
* @param int $activityId 活动id
* @return string
*/
public function _buildVipActivityUrl($channelId, $activityId)
{
$url = '';
$activityObj = model("Activity")->where('id', 'eq', $activityId)->find();
if ($activityObj->type == ActivityConstants::ACTIVITY_TYPE_CUSTOM) {
$config_id = $activityObj->config_id;
$activity_config = Config::get('site.activity_config');
//拉取绑定关系
$row = model("VipActivityRelation")
->field("activity.id, activity.admin_id")
->join("activity", "activity.id = vip_activity_relation.slave_activity_id")
->where('vip_activity_relation.master_activity_id', 'eq', $activityId)
->where('vip_activity_relation.status', 'eq', 'normal')
->where('activity.admin_id', 'eq', $channelId)
->where('activity.status', 'eq', '1')
->where('activity.endtime', '>', time())
->find();
if ($row) {
$url = getCurrentDomain($channelId, '/s/' . $row['id'] . '/r/' . $activity_config['config'][$config_id]['resource_id']);
} else {
Log::info("活动不存在,channel_id: {$channelId}, activity_id: {$activityId}");
}
}
return $url;
}
/**
* 获取最近阅读的链接
* @param int $channelId
* @return string
*/
private function _buildRecentUrl($channelId)
{
$recentUrl = Message::getRecentFullPath($channelId);
return $recentUrl;
}
/**
* 获取目录绝对路径
* @param $channelId
* @param $menuId
* @return string
*/
private function _buildMenuUrl($channelId, $menuId)
{
$menuPath = Menu::$allLinks[$menuId]['menu_url'];
$menuFullPath = getCurrentDomain($channelId, $menuPath);
return $menuFullPath;
}
#endregion
/**
* 构造url的打点信息
* @param $msgType
* @param $aMessageJson
* @param $sendTime
* @param $customId
* @return array
* @throws \Exception
*/
private function _buildPointInfo($msgType, $aMessageJson, $sendTime, $customId, $channelId = 0)
{
$customMsgJson = [];
$customUpdate = [];
$shortUrlService = new ShortUrlService();
if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
$mark = BigData::BIG_DATA_MARK_IMAGE_TEXT;
foreach ($aMessageJson as $idx => $msgJson) {
$url = $msgJson['url'] ?? '';
$url = $this->_urlBuildExt($url, $mark, $sendTime, $customId, $idx + 1);
$shortUrl = $shortUrlService->create($url, $channelId);
$description = ' ';
if (isset($msgJson['description']) && !empty($msgJson['description'])) {
$description = $msgJson['description'];
}
$customMsgInfo = [
'type' => (isset($msgJson['mini_type']) && $msgJson['mini_type'] == 4) ? 11 : $msgJson['type'],
'title' => $msgJson['title'],
'url' => $shortUrl,
'image' => $msgJson['image'],
'description' => $description,
];
if ($customMsgInfo['type'] == 10 || $customMsgInfo['type'] == 11 ){//小程序类型
$mini_content = array(
'appid'=>$msgJson['appid']??'',
'page'=>$msgJson['page']??'',
'media_id'=>$msgJson['media_id']??'',
'title'=>$msgJson['title'],
);
$customMsgInfo['mini_content'] = \GuzzleHttp\json_encode($mini_content,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
}
if (isset($msgJson['media_id'])){
$customMsgInfo['media_id'] = $msgJson['media_id'];
}
$customMsgJson[] = $customMsgInfo;
}
$customUpdate['message_json'] = json_encode($customMsgJson, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
} elseif ($msgType == Message::MESSAGE_TYPE_LINK) {
$mark = BigData::BIG_DATA_MARK_LINK;
$customMsgLink = [];
foreach ($aMessageJson as $idx => $msgJson) {
if (!array_key_exists('type', $msgJson) || $msgJson['type'] == Message::MESSAGE_LINK_TYPE_NOURL) {
$customMsgLink[] = $msgJson['title'];
} else {
if($msgJson['type'] == Message::MESSAGE_LINK_TYPE_SIGN){
$customMsgLink[] = sprintf('%s', $msgJson['url'], $msgJson['title']);
}else if($msgJson['type'] == Message::MESSAGE_LINK_TYPE_MINI){
$customMsgLink[] = $msgJson['url'];
}else{
$url = $msgJson['url'];
$url = $this->_urlBuildExt($url, $mark, $sendTime, $customId, $idx + 1);
$shortUrl = $shortUrlService->create($url, $channelId);
$customMsgLink[] = sprintf('%s', $shortUrl, $msgJson['title']);
}
}
}
$customUpdate['message_text'] = implode("\n\n", $customMsgLink);
}
return $customUpdate;
}
/**
* 推广链接js打点相关参数
* @param $url
* @param $mark
* @param int $sendTime
* @param int $customId
* @param int $idx
* @return string
*/
private function _urlBuildExt($url, $mark, $sendTime, $customId, $idx)
{
$url = trim($url);
if(!strpos($url, '{$ophost}')){
// 如果 url中没有 域名占位符,不加入ext参数
$url = $this->_rejectUrlExt($url);
return $url;
}else{
$ext = [
'mark' => $mark,
'push_time' => $sendTime,
'push_id' => $customId,
'push_idx' => $idx,
];
$strExt = json_encode($ext);
if (strpos($url, '?') === false) {
$url .= '?ext=' . $strExt;
} else {
$url .= '&ext=' . $strExt;
}
}
return $url;
}
private function _rejectUrlExt($url)
{
$url_data = parse_url($url);
$query = isset($url_data['query']) ? $url_data['query'] : '';
parse_str($query, $q_arr);
if(!empty($q_arr['ext'])){
unset($q_arr['ext']);
}
$url_data['query'] = http_build_query($q_arr);
$url = $this->_unparse_url($url_data);
return $url;
}
private function _unparse_url($parsed_url) {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
/**
* 创建统计信息
* @param $bookIds
* @param $customId
* @param $customMediaPushId
* @param $msgType
* @param $officialAccountId
* @param $officialAccountType
* @param $aMessageJson
* @param $sendtime
*/
public function createCustomUrl(
$bookIds,
$customId,
$customMediaPushId,
$msgType,
$officialAccountId,
$officialAccountType,
$aMessageJson,
$sendtime
)
{
$aCustomUrl = [];
$bookInfos = Message::getBooksInfo($bookIds);
$officialAccountName = '';
if ($officialAccountType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE) {
$configData = $this->_adminConfigModel->find($officialAccountId);
if (!empty($configData)) {
$officialAccountName = $configData->json['authorizer_info']['nick_name'];
}
} elseif ($officialAccountType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SUBSCRIPTION) {
$subObj = $this->_subscriptionModel->getSubscriptionById($officialAccountId);
if (!empty($subObj)) {
$officialAccountName = $subObj['name'];
}
}
foreach ($aMessageJson as $idx => $msgInfo) {
$bookId = $msgInfo['book_id'] ?? 0;
$bookId = intval($bookId);
$aCustomUrl[] = [
'custom_id' => $customId,
'idx' => $idx + 1,
'custom_media_push_id' => $customMediaPushId,
'title' => $msgInfo['title'],
'message_type' => $msgType,
'url' => $msgInfo['url'] ?? '',
'type' => $msgInfo['type'],
'official_account_id' => $officialAccountId,
'official_account_type' => $officialAccountType,
'official_account_name' => $officialAccountName,
'push_type' => $msgInfo['push'] ?? null,
'book_id' => $bookId,
'book_name' => $bookInfos[$bookId]['name'] ?? '',
'book_realname' => $bookInfos[$bookId]['realname'] ?? '',
'sendtime' => strtotime($sendtime),
];
}
$this->_customUrlModel->saveAll($aCustomUrl);
}
/**
* 图文客服消息素材库 处理群发消息中的url
* @param $channel_id
* @param $list
* @return string
* @throws \Exception
*/
public function processUrlForChannelId($channel_id, $list)
{
if ($list) {
$list = json_decode($list, true);
$customService = new CustomService();
foreach ($list as $index => $item) {
$list[$index]['url'] = UrlService::instance()->replaceReferralHost($channel_id, $customService->buildUrl($channel_id, $item))->data;
if (empty($list[$index]['url'])) {
Log::info("url 不存在");
}
}
} else {
$list = [];
}
return json_encode($list);
}
/**
* @param $admin_id
* @param $sub_ids
* @param $open_id
* @param string $data
* @param int $ids
* @param int $msg_type
*/
public function sendMessageSubscribeToUser($admin_id, $sub_ids, $open_id, $data = '', $ids = 0, $msg_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
{
if (is_array($admin_id)) {
$admin_id = array_pop($admin_id);
}
if (!$admin_id) {
return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'admin_id参数错误');
}
if (!$sub_ids) {
return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'sub_id参数错误');
}
if (!$open_id) {
return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'open_id参数错误');
}
try {
if ($ids) {
$aCustomMedia = $this->_customMediaModel->find($ids);
$msg_type = $aCustomMedia['message_type'];
if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
$data = $aCustomMedia['message_json'];
} else {
$data = $aCustomMedia['message_text'];
}
}
if (!$data) {
return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, '素材参数无效');
}
//生成素材消息
$data = \GuzzleHttp\json_decode($data, true);
if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
#region easywechat 生成图文消息
$newsArr = [];
foreach ($data as $item) {
$tmpArr = [];
$tmpArr['title'] = $item['title'];
$tmpArr['image'] = $item['image'] ?? ''; #文字链消息,没有image字段
$tmpArr['url'] = self::instance()->buildUrl($admin_id, $item);
$tmpObj = new NewsItem($tmpArr);
array_push($newsArr, $tmpObj);
}
$news = new News($newsArr);
#endregion
} else {
#region easywechat 生成文字消息
$content = '';
foreach ($data as $item) {
$url = self::instance()->buildUrl($admin_id, $item);
if ($url) {
$content .= "";
$content .= $item['title'];
$content .= "";
} else {
$content .= $item['title'];
}
$content .= "\n\n";
}
$news = new Text($content);
#endregion
}
$success_num = 0;
foreach ($sub_ids as $sub_id) {
$wechat = new WeChatObject();
$officialAccount = $wechat->getSubscriptionOfficialAccount($sub_id);
if (!$officialAccount) {
return LogService::toError(ErrorCode::DB_ERROR_SELECT, '无效的服务商信息');
}
$result = $officialAccount->customer_service
->message($news)
->to($open_id)
->send();
if ($result['errcode'] == 0) {
$success_num++;
LogService::toSuccess(ErrorCode::SUCCESS, '发送成功');
} else {
LogService::alert("推送消息发送失败,内容:" . json_encode($result));
}
}
if ($success_num) {
return LogService::toSuccess(ErrorCode::SUCCESS, '发送完成');
} else {
return LogService::toSuccess(ErrorCode::SUCCESS, '发送失败');
}
} catch (\Exception $e) {
LogService::exception($e);
return LogService::toError(ErrorCode::EXCEPTION, '未知错误');
}
}
/**
* 测试粉丝
* @param array $admin_ids 渠道id
* @param int $user_id 用户id
* @param string $data 素材json字符串
* @param string $ids 素材id
* @param string $msg_type 消息类型
* @return array|\think\response\Json
*/
public function sendMessageServiceToUser(array $admin_ids, $user_id, $data = '', $ids = 0, $msg_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
{
if (!$admin_ids || !is_array($admin_ids)) {
return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'admin_id参数错误');
}
if (!$user_id) {
return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'user_id参数错误');
}
$success_num = 0;
try {
if ($ids) {
$aCustomMedia = $this->_customMediaModel->find($ids);
$msg_type = $aCustomMedia['message_type'];
if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
$data = $aCustomMedia['message_json'];
} else {
$data = $aCustomMedia['message_text'];
}
}
if (!$data) {
return LogService::toSuccess(ErrorCode::SUCCESS, '素材参数无效');
}
foreach ($admin_ids as $admin_id) {
// refresh_token adminConfig表获取
if (preg_match("/\d+/i", $user_id, $matches)) {
if (!empty($matches)) {
if (empty($matches)) {
//没有匹配到数字。错误
return LogService::toSuccess(ErrorCode::SUCCESS, '无法匹配到此用户');
} else {
$user_id = intval($matches[0]);
}
}
} else {
//没有匹配到数组,错误
return LogService::toSuccess(ErrorCode::SUCCESS, '无法匹配到此用户');
}
$mUser = new User();
$mAdminConfig = new AdminConfig();
$user = $mUser->setConnect($user_id)->where('id', $user_id)->find();
if (!$user) {
return LogService::toSuccess(ErrorCode::SUCCESS, '无效的用户信息');
}
if ($user->channel_id != $admin_id) {
continue;
}
$openid = $user->openid;
$Config = $mAdminConfig->getAdminInfoAll($admin_id);
$wechat = new WeChatObject($Config);
$officialAccount = $wechat->getOfficialAccount();
if (!$officialAccount) {
return LogService::toSuccess(ErrorCode::SUCCESS, '无效的服务商信息');
}
$dataContent = str_replace(['$user_nickname'], [$user['nickname']], $data);
$dataContent = \GuzzleHttp\json_decode($dataContent, true);
$news = [];
if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
#region easywechat 生成图文消息
$newsArr = [];
foreach ($dataContent as $item) {
if ($item['type'] == 10){
switch ($item['mini_type']){
case 3:
$tempArgs = [];
$tempArgs['appid'] = $item['appid'] ?? config('site.mini_appid') ;
$tempArgs['title'] = $item['title'];
$tempArgs['pagepath'] = $item['page'];
$r = HigeMessageService::instance()->image2media($admin_id,$item['image']);
$tempArgs['thumb_media_id'] = $r['media_id'];
$miniObj = new MiniProgramPage($tempArgs);
$result = $officialAccount->customer_service
->message($miniObj)
->to($openid)
->send();
break;
case 4:
$re = MiniProgramService::instance()->getMiniContent($admin_id,$item);
$miniImage = new Image(\GuzzleHttp\json_decode($re,true)['media_id']);
$result = $officialAccount->customer_service
->message($miniImage)
->to($openid)
->send();
break;
default:
break;
}
}else{
$tmpArr = [];
$tmpArr['title'] = $item['title'];
$tmpArr['description'] = $item['description'] ?: ' ';
$tmpArr['image'] = $item['image'] ?? ''; #文字链消息,没有image字段
$tmpArr['url'] = self::instance()->buildUrl($admin_id, $item);
$tmpObj = new NewsItem($tmpArr);
}
if (isset($tmpObj)){
array_push($newsArr, $tmpObj);
}
}
if (!empty($newsArr)){
$news = new News($newsArr);
}
#endregion
} else {
#region easywechat 生成文字消息
$str_list = [];
foreach ($dataContent as $item) {
$url = '';
if ($item['type'] == Message::MESSAGE_LINK_TYPE_BOOK) {
$sourceUrl = '/index/book/chapter?book_id='.$item['book_id'];
$url = getCurrentDomain($admin_id, $sourceUrl);
}elseif($item['type'] == Message::MESSAGE_LINK_TYPE_MINI){
$tempArgs = [];
$tempArgs['type'] = 1;
$tempArgs['page'] = trim($item['page']);
$tempArgs['content'] = $item['title'];
$miniStr = MiniProgramService::instance()->getMiniProgram($tempArgs,$admin_id);
$str_list[] = $miniStr;
continue;
}else {
$url = self::instance()->buildUrl($admin_id, $item);
}
if ($url) {
$str = '';
$str .= "";
$str .= $item['title'];
$str .= "";
$str_list[] = $str;
} else {
$str_list[] = $item['title'];
}
}
$content = implode("\n\n", $str_list);
$news = new Text($content);
#endregion
}
if (!empty($news)){
$result = $officialAccount->customer_service
->message($news)
->to($openid)
->send();
}
if ($result['errcode'] == 0) {
$success_num++;
LogService::toSuccess(ErrorCode::SUCCESS, '发送成功');
} else {
LogService::alert("推送消息发送失败,内容:" . json_encode($result));
}
}
if ($success_num) {
return LogService::toSuccess(ErrorCode::SUCCESS, '发送完成');
} else {
return LogService::toSuccess(ErrorCode::SUCCESS, '发送失败');
}
} catch (\Exception $e) {
LogService::exception($e);
Log::error($e);
return LogService::toError(ErrorCode::EXCEPTION, '未知错误');
}
}
/**
* 服务号群发消息
* @param int $mediaId 素材ID
* @param string $sendTime 发送时间
* @param array $channelIds 服务号id
* @param int $adminId 后台账号id
* @return array
*/
public function multiSendMessageService($mediaId, $sendTime, $channelIds, $adminId, $other = [])
{
try {
# 读取 media 信息
$media = $this->_customMediaModel::get(['id' => $mediaId])->toArray();
$msgType = $media['message_type'];
if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
$msgContent = $media['message_json'];
} else {
$msgContent = $media['message_text'];
}
$params = Request::instance()->post("row/a");
return self::instance()->createPushService($sendTime, $channelIds, $adminId, $msgType, $msgContent, $mediaId, false, '', $params['user_json'], Custom::CUSTOM_CREATED_FROM_GROUP_SEND, $other);
} catch (\Exception $e) {
LogService::exception($e);
return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
}
}
/**
* 服务号群发消息
* @param int $mediaId 素材ID
* @param string $sendTime 发送时间
* @param array $subIds 订阅号id
* @param int $channelId 服务号id
* @param int $adminId 后台账号id
* @return array
*/
public function multiSendMessageSub($mediaId, $sendTime, $subIds, $channelId, $adminId)
{
try {
//获取素材信息
$media = $this->_customMediaModel::get(['id' => $mediaId])->toArray();
$msgType = $media['message_type'];
if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
$msgContent = $media['message_json'];
} else {
$msgContent = $media['message_text'];
}
return self::instance()->createPushSub($sendTime, $subIds, $channelId, $adminId, $msgType, $msgContent, $mediaId, false, Custom::CUSTOM_CREATED_FROM_GROUP_SEND);
} catch (\Exception $e) {
LogService::exception($e);
return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
}
}
/**
* 推送订阅号
* @param string $sendTime 发送时间
* @param array $subIds 订阅号id
* @param int $channelId 服务号id
* @param int $adminId 后台登录账号id
* @param int $msgType 消息类型
* @param string $msgContent 消息体
* @param int $mediaId 素材id
* @param bool $toSave 是否保存为素材
* @param string $created_from 是否保存为素材
* @return array
*/
public function createPushSub($sendTime, $subIds, $channelId, $adminId, $msgType, $msgContent, $mediaId = 0, $toSave = false, $created_from = Custom::CUSTOM_CREATED_FROM_ADD)
{
try {
if ($toSave) {
$mediaId = self::instance()->createMedia($adminId, $msgType, $msgContent);
}
$officialType = OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SUBSCRIPTION;
$mediaPushData = [
'custom_media_id' => $mediaId,
'sendtime' => $sendTime,
'created_admin_id' => $adminId,
'created_from' => $created_from,
];
$customMediaPushId = self::instance()->createCustomMediaPush($mediaPushData, $msgType, $officialType, $subIds, $msgContent);
//endregion
$msgContent = self::instance()->processUrlForChannelId($channelId, $msgContent);
// custom 表中记录 N 条消息 (选择了N个订阅号)
foreach ($subIds as $subId) {
// 服务号
$data = [
'title' => '',
'sendtime' => $sendTime,
'admin_id' => $channelId,
'created_admin_id' => $adminId,
'custom_media_push_id' => $customMediaPushId,
'official_account_type' => $officialType, // 公众号类型:0=服务号,1=订阅号
'official_account_id' => $subId, // 公众号id
'created_from' => $created_from,
];
list($customId, $bookIds, $aMessageJson) = CustomService::instance()->createCustom($data, $msgType, $msgContent);
#保存客服消息统计信息
CustomService::instance()->createCustomUrl($bookIds, $customId, $customMediaPushId, $msgType,
$channelId, $officialType, $aMessageJson, $sendTime);
}
return ['error' => ErrorCode::SUCCESS, 'msg' => 'ok'];
} catch (\Exception $e) {
LogService::exception($e);
return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
}
}
/**
* 推送服务号
* @param string $sendTime 发送时间
* @param array $channelIds 服务号id
* @param int $adminId 后台登录账号id
* @param int $msgType 消息类型
* @param string $msgContent 消息体
* @param int $mediaId 素材id
* @param bool $toSave 是否保存为素材
* @param string $title 消息标题
* @param string $created_from 消息标题
* @return array
*/
public function createPushService($sendTime, $channelIds, $adminId, $msgType, $msgContent, $mediaId = 0, $toSave = false, $title = '', $userJson = null, $created_from = Custom::CUSTOM_CREATED_FROM_ADD, $other = [])
{
try {
if ($toSave) {
$mediaId = self::instance()->createMedia($adminId, $msgType, $msgContent);
}
# 服务号 OR 订阅号
$officialType = OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE;
$mediaPushData = [
'custom_media_id' => $mediaId,
'sendtime' => $sendTime,
'created_admin_id' => $adminId,
'created_from' => $created_from,
'user_json' => $userJson,
];
$channelIds = array_unique($channelIds);
$customMediaPushId = self::instance()->createCustomMediaPush($mediaPushData, $msgType, $officialType, $channelIds, $msgContent, $other);
$ignoreArr = [];
foreach ($channelIds as $channel_id) {
$msgContent = self::instance()->processUrlForChannelId($channel_id, $msgContent);
$jsonData = json_decode($msgContent, true);
$del = [];
foreach ($jsonData as $key=>$item) {
if ($item['type'] == Message::MESSAGE_LINK_TYPE_CUSTOMIZE_ACTIVITY && empty($item['url'])) {
//url为空 则跳过 不创建客服消息
$del[] = $key;
}
//如果type为url常用链接类型(type=10)&&选择的链接类型为Vip充值和小额充值,需要判断当前渠道是否有vip充值和小额充值权限
else if($item['type'] == Message::MESSAGE_LINK_TYPE_DAILY_LINK ){
//常用链接
$dailyLink = CommonService::instance()->getTmpDailyLink();
$adminConfig = AdminService::instance()->getAdminConfigModel()->getAdminInfoAll($channel_id);
//vip充值
if(strpos($item['url'],$dailyLink['vip']['link'])){
if(($adminConfig['vip_state'] ?? false) && ($adminConfig['vip_goods_id'] ?? null)){
//vip自定义商品
if(($adminconfig['vip_goods_id_custom'] ?? false)){
$item['url'] = $item['url']."&vip_custom_money=1";
}
}else{
$del[] = $key;
}
}
}//小程序
else if($item['type'] == Message::MESSAGE_LINK_TYPE_MINI){
$temMini = [];
$temMini['type'] = $item['mini_type'] ?? 1;
if ($temMini['type'] == 1){//文本
$temMini['page'] = $item['page'];
$temMini['content'] = $item['title'];
$miniContent = MiniProgramService::instance()->getMiniProgram($temMini, $adminId);
$jsonData[$key]['url'] = $miniContent;
}else{//图文
$con = MiniProgramService::instance()->getMiniContent($adminId,$item);
$conArr = \GuzzleHttp\json_decode($con,true);
$jsonData[$key] = array_merge($item,$conArr);
}
}
}
$lastContent = [];
if ($del) {
foreach ($jsonData as $index=>$row) {
if (!in_array($index, $del)) {
$lastContent[] = $row;
}
}
} else {
$lastContent = $jsonData;
}
if (empty($lastContent)) {
$ignoreArr[] = $channel_id;
continue;
}
$msgContent = json_encode($lastContent, JSON_UNESCAPED_UNICODE);
$data = [
'title' => $title,
'sendtime' => $sendTime,
'admin_id' => $channel_id,
'created_admin_id' => $adminId,
'custom_media_push_id' => $customMediaPushId,
'official_account_type' => $officialType, # 公众号类型:0=服务号,1=订阅号
'official_account_id' => $channel_id, # 公众号id
'created_from' => $created_from, # 公众号id
'user_json' => $userJson ? json_encode(json_encode(json_decode($userJson, true),JSON_UNESCAPED_UNICODE), true) : null,
];
list($customId, $bookIds, $aMessageJson) = CustomService::instance()->createCustom($data, $msgType, $msgContent);
#保存客服消息统计信息
CustomService::instance()->createCustomUrl($bookIds, $customId, $customMediaPushId, $msgType,
$channel_id, $officialType, $aMessageJson, $sendTime);
}
if ($ignoreArr && count($ignoreArr) == count($channelIds)) {
//消息为空 删除push
$this->_customMediaPushModel->where('id', 'eq', $customMediaPushId)->delete();
Log::info("所有公众号消息为空, 删除push");
return ['error' => ErrorCode::EXCEPTION, 'msg' => "您选择的公众号中存在未创建此活动的账户,请核对后重试"];
}
return ['error' => ErrorCode::SUCCESS, 'msg' => 'ok'];
} catch (\Exception $e) {
//LogService::exception($e);
Log::error($e->getMessage());
return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
}
}
/**
* 获取服务号的名字
* @param $channelId
* @return string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getServiceNameByChannelId($channelId)
{
$adminConfig = $this->_adminConfigModel->where(['admin_id' => $channelId])->find();
if ($adminConfig) {
return $adminConfig->json['authorizer_info']['nick_name'];
} else {
return '';
}
}
/**
* 获取 custom_media_push 对象
* @param $push_id custom_media_push ID
* @return array
*/
public function getCustomMessageByPushId($push_id)
{
$customPushData = $this->_customMediaPushModel->getCustomInfoById($push_id);
return $customPushData;
}
/**
* 更改custom数据
* @param $customData 数据
* @param $customId custom 表 ID
* @param $channelId 渠道ID
*/
public function updateCustomMessage($customData, $customId, $channelId)
{
$msgType = $customData['message_type'];
if (!$msgType) {
$message_arr = json_decode($customData['message_json'], true, 1024);
} else {
$message_arr = json_decode($customData['message_text'], true, 1024);
}
foreach ($message_arr as $key => $item) {
if ($item['type'] == Message::MESSAGE_LINK_TYPE_MINI){//处理小程序类型
$temMini = [];
$temMini['type'] = $item['mini_type'] ?? 1;
if ($temMini['type'] == 1){//文本
$temMini['page'] = $item['page'];
$temMini['content'] = $item['title'];
$miniContent = MiniProgramService::instance()->getMiniProgram($temMini, $channelId);
$message_arr[$key]['url'] = $miniContent;
}else{//图文
$con = MiniProgramService::instance()->getMiniContent($channelId,$item);
$conArr = \GuzzleHttp\json_decode($con,true);
$message_arr[$key] = array_merge($item,$conArr);
}
}else{
$url = self::instance()->buildUrl($channelId, $item);
//转换占位符
$message_arr[$key]['url'] = UrlService::instance()->replaceReferralHost($channelId, $url)->data;
}
}
$pointInfo = $this->_buildPointInfo($msgType, $message_arr, $customData['sendtime'], $customId, $channelId);
foreach ($pointInfo as $key=>&$item) {
$item = UrlService::instance()->replaceReferralHost($channelId, $item)->data;
}
$customData = array_merge($customData, $pointInfo);
$result = $this->_customModel->allowField(true)->save($customData, ['id' => $customId]);
return $result;
}
/**
* 获取发送成功|失败的订阅号数据
* @param $pushId custom_media_push 表ID
* @param int $statue 1 => 发送成功 0=> 发送失败
* @return array
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getOfficialSubList($pushId, $statue = Custom::CUSTOM_STATUE_HIDDEN)
{
$push_data = $this->_customMediaPushModel->get(['id' => $pushId])->toArray();
$data = $this->_customModel
->alias('t')
->join('subscription t1', 't.official_account_id = t1.id', 'left')
->where('t.custom_media_push_id', '=', $pushId)
->where('t.statue', '=', $statue)
->where('t.official_account_id', 'in', $push_data['official_account_subscribe_ids'])
->field('t1.name')
->select();
$list = [];
if ($data) {
foreach ($data as $key => $item) {
$list[] = $item['name'];
}
}
return $list;
}
/**
* 获取发送成功|失败的订阅号数据
* @param $pushId custom_media_push 表ID
* @param int $statue 1 => 发送成功 0=> 发送失败
* @return array
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getOfficialServiceList($pushId, $statue = Custom::CUSTOM_STATUE_HIDDEN)
{
$push_data = $this->_customMediaPushModel->get(['id' => $pushId])->toArray();
$data = $this->_customModel
->alias('t')
->join('admin_config t1', 't.official_account_id = t1.admin_id', 'left')
->where('t.custom_media_push_id', '=', $pushId)
->where('t.statue', '=', $statue)
->where('t.official_account_id', 'in', $push_data['official_account_service_ids'])
->field('t1.json')
->select();
$list = [];
if ($data) {
foreach ($data as $key => $item) {
$json = json_decode($item->json, true);
$list[] = $json['authorizer_info']['nick_name'];
}
}
return $list;
}
/**
* @param $officialType
* @param $pushId
* @param $statue
* @return array
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getOfficialPushStatusList($officialType, $pushId, $statue)
{
if ($officialType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE) {
return self::instance()->getOfficialServiceList($pushId, $statue);
} else {
return self::instance()->getOfficialSubList($pushId, $statue);
}
}
public function getCustomMediaDetail($id, $image_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
{
$obj = $this->_customMediaModel->get(['id' => $id]);
if($image_type == Message::MESSAGE_TYPE_IMAGE_TEXT){
$message_json = json_decode($obj->message_json, true, 512);
}else{
$message_json = json_decode($obj->message_text, true, 512);
}
foreach($message_json as $index=>$item){
switch ($message_json[$index]['type']) {
case Message::MESSAGE_LINK_TYPE_MENU:
$message_json[$index]['book_name'] = Menu::$allLinks[$message_json[$index]['menu_id']]['title'];
break;
case Message::MESSAGE_LINK_TYPE_ACTIVITY:
$activity = model('Activity')
->where('id', $message_json[$index]['activity_id'])->find();
$message_json[$index]['book_name'] = $activity['name'];
break;
case Message::MESSAGE_LINK_TYPE_CUSTOMIZE_ACTIVITY:
$activity = model('Activity')
->where('id', $message_json[$index]['activity_id'])->find();
$message_json[$index]['book_name'] = $activity['name'];
break;
}
}
return array("total" => count($message_json), "rows" => $message_json);
}
public function getCustomMediaPushDetail($id, $image_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
{
$obj = $this->_customMediaPushModel->get(['id' => $id]);
if($image_type == Message::MESSAGE_TYPE_IMAGE_TEXT){
$message_json = json_decode($obj->message_json, true, 512);
}else{
$message_json = json_decode($obj->message_text, true, 512);
}
foreach($message_json as $index=>$item){
switch ($message_json[$index]['type']) {
case Message::MESSAGE_LINK_TYPE_MENU:
$message_json[$index]['name'] = Menu::$allLinks[$message_json[$index]['menu_id']]['title'];
break;
case Message::MESSAGE_LINK_TYPE_ACTIVITY:
$activity = model('Activity')
->where('id', $message_json[$index]['activity_id'])->find();
$message_json[$index]['name'] = $activity['name'];
break;
}
}
return array("total" => count($message_json), "rows" => $message_json);
}
}