CustomService.php 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489
  1. <?php
  2. namespace app\admin\service;
  3. use app\admin\controller\send\message\Mini;
  4. use app\admin\library\ShortUrl;
  5. use app\common\constants\BigData;
  6. use app\common\constants\Common;
  7. use app\common\constants\Custom;
  8. use app\common\constants\ErrorCode;
  9. use app\common\constants\Menu;
  10. use app\common\constants\Message;
  11. use app\common\constants\OfficialAccount;
  12. use app\common\library\WeChatObject;
  13. use app\common\model\Activity;
  14. use app\common\model\AdminConfig;
  15. use app\common\model\User;
  16. use app\main\constants\ActivityConstants;
  17. use app\main\service\AdminService;
  18. use app\main\service\HigeMessageService;
  19. use app\main\service\MiniProgramService;
  20. use app\main\service\UrlService;
  21. use EasyWeChat\Kernel\Messages\Image;
  22. use EasyWeChat\Kernel\Messages\MiniProgramPage;
  23. use EasyWeChat\Kernel\Messages\Music;
  24. use EasyWeChat\Kernel\Messages\News;
  25. use EasyWeChat\Kernel\Messages\NewsItem;
  26. use EasyWeChat\Kernel\Messages\Text;
  27. use think\Config;
  28. use think\Log;
  29. use think\Request;
  30. class CustomService
  31. {
  32. /**
  33. * @var \app\admin\model\Custom
  34. */
  35. private $_customModel;
  36. /**
  37. * @var \app\admin\model\CustomMedia
  38. */
  39. private $_customMediaModel;
  40. /**
  41. * @var \app\admin\model\CustomMediaPush
  42. */
  43. private $_customMediaPushModel;
  44. /**
  45. * @var \app\admin\model\CustomUrl
  46. */
  47. private $_customUrlModel;
  48. /**
  49. * @var \app\common\model\AdminConfig
  50. */
  51. private $_adminConfigModel;
  52. /**
  53. * @var \app\common\model\Subscription
  54. */
  55. private $_subscriptionModel;
  56. /**
  57. * @var \app\common\model\Book
  58. */
  59. private $_bookModel;
  60. /**
  61. * @var \app\common\model\Referral
  62. */
  63. private $_referralModel;
  64. /**
  65. * @var CustomService
  66. */
  67. private static $instance = NULL;
  68. /**
  69. * @return CustomService 单例调用
  70. */
  71. public static function instance()
  72. {
  73. if (self::$instance == NULL) {
  74. self::$instance = new self();
  75. }
  76. return self::$instance;
  77. }
  78. /**
  79. * TODO 此方法应改为私有方法
  80. */
  81. public function __construct()
  82. {
  83. $this->_customModel = model('Custom');
  84. $this->_customMediaModel = model('CustomMedia');
  85. $this->_customMediaPushModel = model('CustomMediaPush');
  86. $this->_customUrlModel = model('CustomUrl');
  87. $this->_adminConfigModel = model('AdminConfig');
  88. $this->_subscriptionModel = model('Subscription');
  89. $this->_bookModel = model('Book');
  90. $this->_referralModel = model('Referral');
  91. }
  92. /**
  93. * 创建客服消息素材
  94. * @param int $adminId 渠道商id
  95. * @param string $msgType 客服消息类型
  96. * @param string $msgContent 文字链消息内容
  97. * @return int|string
  98. * @throws \Exception
  99. */
  100. public function createMedia($adminId, $msgType, $msgContent)
  101. {
  102. $media = [
  103. 'created_admin_id' => $adminId,
  104. 'message_type' => $msgType,
  105. 'createtime' => time(),
  106. 'updatetime' => time(),
  107. ];
  108. if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  109. if (!Common::isJson($msgContent)) {
  110. throw new \Exception('图文参数不是json格式');
  111. }
  112. //检测是否有null项 有的话清理掉
  113. if (strrpos($msgContent, 'null') !== false) {
  114. $data = json_decode($msgContent, true);
  115. $data = array_filter($data);
  116. $msgContent = json_encode($data);
  117. }
  118. $media['message_json'] = $msgContent;
  119. } elseif ($msgType == Message::MESSAGE_TYPE_LINK) {
  120. if (!Common::isJson($msgContent)) {
  121. throw new \Exception('文字链参数不是json格式');
  122. }
  123. $media['message_text'] = $msgContent;
  124. } else {
  125. throw new \Exception('message_type不正确:' . $msgType);
  126. }
  127. $id = $this->_customMediaModel->insertGetId($media);
  128. return $id;
  129. }
  130. /**
  131. * 创建客服消息推送记录
  132. * @param $params
  133. * @param int $msgType 客服消息类型
  134. * @param int $officialType 公众号类型
  135. * @param array $officialAccountIds 公众号id列表
  136. * @param string $msgContent 消息体
  137. * @return int|string
  138. * @throws \Exception
  139. */
  140. public function createCustomMediaPush($params, $msgType, $officialType, $officialAccountIds, $msgContent, $other = [])
  141. {
  142. $user_json_default = json_encode(["all" => "0", "sex" => -1, "tag" => -1, "consume" => -1, "kandian" => -1, "subscribe_time" => -1], JSON_UNESCAPED_UNICODE);
  143. $model = [
  144. 'custom_media_id' => $params['custom_media_id'] ? $params['custom_media_id'] : 0,
  145. 'message_type' => $msgType,
  146. 'official_account_type' => $officialType,
  147. 'sendtime' => strtotime($params['sendtime']),
  148. 'user_json' => $params['user_json'] ?? $user_json_default,
  149. 'created_admin_id' => $params['created_admin_id'],
  150. 'created_from' => $params['created_from'],
  151. 'createtime' => time(),
  152. 'updatetime' => time(),
  153. 'select_type' => $other['select_type'] ?? 0,
  154. 'group_id' => $other['group_id'] ?? '',
  155. ];
  156. if ($officialType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE) {
  157. $model['official_account_service_ids'] = implode(',', $officialAccountIds);
  158. } elseif ($officialType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SUBSCRIPTION) {
  159. $model['official_account_subscribe_ids'] = implode(',', $officialAccountIds);
  160. } else {
  161. throw new \Exception('公众号类型不正确:' . $officialType);
  162. }
  163. if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  164. if (!Common::isJson($msgContent)) {
  165. throw new \Exception('图文参数不是json格式');
  166. }
  167. $model['message_json'] = $msgContent;
  168. } elseif ($msgType == Message::MESSAGE_TYPE_LINK) {
  169. if (!Common::isJson($msgContent)) {
  170. throw new \Exception('文字链参数不是json格式');
  171. }
  172. $model['message_text'] = $msgContent;
  173. } else {
  174. throw new \Exception('message_type不正确:' . $msgType);
  175. }
  176. $id = $this->_customMediaPushModel->insertGetId($model);
  177. return $id;
  178. }
  179. /**
  180. * 删除客服消息 push
  181. * @param $pushId
  182. * @return bool
  183. * @throws \Exception
  184. */
  185. public function delCustomMediaPush($pushId)
  186. {
  187. $result = $this->_customMediaPushModel->save(['status' => Custom::CUSTOM_MEDIA_PUSH_STATUS_DELETE],
  188. ['id' => $pushId]);
  189. if ($result === false) {
  190. throw new \Exception('删除客服消息失败,push_id:' . $pushId);
  191. }
  192. $result = $this->_customModel->save(['statue' => Custom::CUSTOM_STATUE_DELETE],
  193. ['custom_media_push_id' => $pushId]);
  194. if ($result === false) {
  195. throw new \Exception('删除客服消息失败!!,push_id:' . $pushId);
  196. }
  197. return true;
  198. }
  199. /**
  200. * 更新客服消息 push
  201. * @param $pushId
  202. * @param $sendtime
  203. * @return bool
  204. * @throws \Exception
  205. */
  206. public function editCustomMediaPush($pushId, $params)
  207. {
  208. $mediaData = $this->_customMediaPushModel->where('id', $pushId)->find();
  209. //更新
  210. if ($mediaData) {
  211. $sendTime = strtotime($params['sendtime']);
  212. if ($params['select_type'] == 1 && $params['group_id']) {
  213. //分组 拉取公众号ID
  214. $groupIds = trim($params['group_id'], ',');
  215. $channelRows = model("VipGroupInfo")->where('group_id', 'in', $groupIds)->field("channel_id")->select();
  216. $channelIds = array_column($channelRows, 'channel_id');
  217. } else {
  218. $channelIds = $params['service_id'];
  219. }
  220. //删除之前的数据
  221. $this->_customMediaPushModel->where('id', $pushId)->delete();
  222. $this->_customModel->where('custom_media_push_id', $pushId)->delete();
  223. $this->_customUrlModel->where('custom_media_push_id', $pushId)->delete();
  224. $result = $this->multiSendMessageService($mediaData['custom_media_id'], $params['sendtime'], $channelIds, $mediaData['created_admin_id'], ['group_id' => $params['group_id'], 'select_type' => $params['select_type']]);
  225. if ($result['error'] == ErrorCode::SUCCESS) {
  226. return true;
  227. } else {
  228. return false;
  229. }
  230. }
  231. return true;
  232. }
  233. /**
  234. * 创建客服消息推送
  235. * @param array $params
  236. * @param int $msgType 消息类型
  237. * @param string $msgContent 图文客服消息内容
  238. * @return array
  239. * @throws \Exception
  240. */
  241. public function createCustom($params, $msgType, $msgContent)
  242. {
  243. $sendTime = strtotime($params['sendtime']);
  244. $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);
  245. $custom = [
  246. 'admin_id' => $params['admin_id'],
  247. 'created_admin_id' => $params['created_admin_id'],
  248. 'custom_media_push_id' => $params['custom_media_push_id'],
  249. 'title' => $params['title'],
  250. 'message_type' => $msgType,
  251. 'user_json' => $params['user_json'] ?? $user_json_default,
  252. 'official_account_type' => $params['official_account_type'],
  253. 'official_account_id' => $params['official_account_id'],
  254. 'sendtime' => $sendTime,
  255. 'created_from' => $params['created_from'],
  256. 'createtime' => time(),
  257. 'updatetime' => time(),
  258. 'message_json' => json_encode([]),
  259. ];
  260. //替换占位符
  261. $msgContent = UrlService::instance()->replaceReferralHost($params['admin_id'], $msgContent)->data;
  262. if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  263. $aMessageJson = json_decode($msgContent, true);
  264. } else {
  265. $custom['message_json'] = json_encode([]);
  266. $aMessageJson = json_decode($msgContent, true);
  267. }
  268. # 这里的bookIds 可能为空
  269. $bookIds = array_column($aMessageJson, 'book_id');
  270. $customId = $this->_customModel->allowField(true)->insertGetId($custom);
  271. if ($customId) {
  272. $pointInfo = $this->_buildPointInfo($msgType, $aMessageJson, $sendTime, $customId, $params['admin_id']);
  273. foreach ($pointInfo as $key=>&$item) {
  274. $item = UrlService::instance()->replaceReferralHost($params['admin_id'], $item)->data;
  275. }
  276. $updateData = [];
  277. $updateData = array_merge($updateData, $pointInfo);
  278. $this->_customModel->update($updateData, ['id' => $customId]);
  279. } else {
  280. Log::error('customId 未插入');
  281. Log::error($custom);
  282. }
  283. return [$customId, $bookIds, $aMessageJson];
  284. }
  285. #region 构造链接
  286. /**
  287. * 构造url
  288. * @param int $channelId 渠道商id
  289. * @param array $params 参数
  290. * @return string url
  291. * @throws \Exception
  292. */
  293. public function buildUrl($channelId, $params)
  294. {
  295. $type = $params['type'];
  296. $url = '';
  297. switch ($type) {
  298. case Message::MESSAGE_LINK_TYPE_BOOK:
  299. //$url = $this->_buildBookUrl($channelId, $params);
  300. $url = $this->_buildBookUrl_v2($channelId, $params);
  301. break;
  302. case Message::MESSAGE_LINK_TYPE_ACTIVITY:
  303. $url = $this->_buildActivityUrl($channelId, $params['activity_id']);
  304. break;
  305. case Message::MESSAGE_LINK_TYPE_RECENT:
  306. $url = $this->_buildRecentUrl($channelId);
  307. break;
  308. case Message::MESSAGE_LINK_TYPE_MENU:
  309. $url = $this->_buildMenuUrl($channelId, $params['menu_id']);
  310. break;
  311. case Message::MESSAGE_LINK_TYPE_NOURL:
  312. $url = '';
  313. break;
  314. case Message::MESSAGE_LINK_TYPE_URL:
  315. $url = $params['url'];
  316. break;
  317. case Message::MESSAGE_LINK_TYPE_CUSTOMIZE_ACTIVITY:
  318. $url = $this->_buildVipActivityUrl($channelId, $params['activity_id']);
  319. break;
  320. case Message::MESSAGE_LINK_TYPE_SIGN:
  321. $url = $params['url'];
  322. break;
  323. //如果是常用链接类型,替换当前的渠道域名信息
  324. case Message::MESSAGE_LINK_TYPE_DAILY_LINK:
  325. $dailyLink = CommonService::instance()->getTmpDailyLink();
  326. if(isset($dailyLink[$params['url']]['link'])){
  327. $url = getCurrentDomain($channelId,$dailyLink[$params['url']]['link'],[],$dailyLink[$params['url']]['isUseMenuDomain']);
  328. }else{
  329. //$url = $params['url'];
  330. $url = getCurrentDomain($channelId,$params['url'],[]);
  331. }
  332. break;
  333. default:
  334. break;
  335. }
  336. return $url;
  337. }
  338. /**
  339. * 获取书籍对应的推广链接
  340. * @param $channelId
  341. * @param $params
  342. * @return string
  343. * @throws \Exception
  344. */
  345. private function _buildBookUrl($channelId, $params)
  346. {
  347. try {
  348. $redisService = new RedisService();
  349. $firstChapter = $this->_bookModel->getChapterLimit($params['book_id'], 0, 0);
  350. if (empty($firstChapter)) {
  351. throw new \Exception('获取小说第一章失败,bookId:' . $params['book_id']);
  352. }
  353. $referral = [
  354. 'type' => 1,
  355. 'book_id' => $params['book_id'],
  356. 'chapter_id' => $firstChapter['id'],
  357. 'chapter_idx' => 1,
  358. 'chapter_name' => $firstChapter['name'],
  359. 'cost' => $params['cost'],
  360. 'name' => $params['channel_name'],
  361. 'wx_type' => $params['wx_type'],
  362. 'push' => $params['push'] ?? '0',
  363. 'guide_chapter_idx' => $params['guide_chapter_idx'],
  364. 'admin_id' => $channelId,
  365. 'createtime' => time(),
  366. ];
  367. $refId = $this->_referralModel->allowField(true)->insertGetId($referral);
  368. $sourceUrl = '/index/book/chapter?book_id=' . $referral['book_id'] . '&sid=' . $referral['chapter_id'] . '&referral_id=' . $refId;
  369. $sourceUrl = getCurrentDomain($channelId, $sourceUrl);
  370. $updateData['source_url'] = $sourceUrl;
  371. $shortUrl = new ShortUrl();
  372. //生成腾讯短链
  373. $updateData['short_url_qq'] = $shortUrl->tencent($channelId, $sourceUrl);
  374. //生成sina短链
  375. $updateData['short_url_weibo'] = $shortUrl->sina($sourceUrl);
  376. $this->_referralModel->save($updateData, ['id' => $refId]);
  377. return $sourceUrl;
  378. } catch (\Exception $e) {
  379. throw $e;
  380. }
  381. }
  382. /**
  383. * 根据参数,生成阅读页链接
  384. * @param $channelId
  385. * @param $params
  386. * @return string
  387. * @throws \Exception
  388. */
  389. private function _buildBookUrl_v2($channelId, $params)
  390. {
  391. try {
  392. $url = '';
  393. if ($params['type'] == Message::MESSAGE_LINK_TYPE_BOOK) {
  394. $sourceUrl = '/index/book/chapter?book_id=' . $params['book_id'];
  395. $url = getCurrentDomain($channelId, $sourceUrl);
  396. }
  397. return $url;
  398. } catch (\Exception $e) {
  399. throw $e;
  400. }
  401. }
  402. /**
  403. * 获取活动id
  404. * @param int $channelId 渠道商对应的appId
  405. * @param int $activityId 活动id
  406. * @return string
  407. */
  408. private function _buildActivityUrl($channelId, $activityId)
  409. {
  410. $activityObj = Activity::get(['id' => $activityId]);
  411. if ($activityObj->type == ActivityConstants::ACTIVITY_TYPE_CUSTOM) {
  412. $config_id = $activityObj->config_id;
  413. $activity_config = Config::get('site.activity_config');
  414. $url = getCurrentDomain($channelId, '/s/' . $activityId . '/r/' . $activity_config['config'][$config_id]['resource_id']);
  415. } else if ($activityObj->type == ActivityConstants::ACTIVITY_TYPE_GIVE) {
  416. $config_id = $activityObj->config_id;
  417. $activity_config = Config::get('site.activity_give_config');
  418. $url = getCurrentDomain($channelId, '/s/' . $activityId . '/r/' . $activity_config['config'][$config_id]['resource_id']);
  419. } else {
  420. $url = getCurrentDomain($channelId, '/s/' . $activityId);
  421. }
  422. return $url;
  423. }
  424. /**
  425. * 获取VIP活动对应渠道的id
  426. * @param int $channelId 渠道商对应的id
  427. * @param int $activityId 活动id
  428. * @return string
  429. */
  430. public function _buildVipActivityUrl($channelId, $activityId)
  431. {
  432. $url = '';
  433. $activityObj = model("Activity")->where('id', 'eq', $activityId)->find();
  434. if ($activityObj->type == ActivityConstants::ACTIVITY_TYPE_CUSTOM) {
  435. $config_id = $activityObj->config_id;
  436. $activity_config = Config::get('site.activity_config');
  437. //拉取绑定关系
  438. $row = model("VipActivityRelation")
  439. ->field("activity.id, activity.admin_id")
  440. ->join("activity", "activity.id = vip_activity_relation.slave_activity_id")
  441. ->where('vip_activity_relation.master_activity_id', 'eq', $activityId)
  442. ->where('vip_activity_relation.status', 'eq', 'normal')
  443. ->where('activity.admin_id', 'eq', $channelId)
  444. ->where('activity.status', 'eq', '1')
  445. ->where('activity.endtime', '>', time())
  446. ->find();
  447. if ($row) {
  448. $url = getCurrentDomain($channelId, '/s/' . $row['id'] . '/r/' . $activity_config['config'][$config_id]['resource_id']);
  449. } else {
  450. Log::info("活动不存在,channel_id: {$channelId}, activity_id: {$activityId}");
  451. }
  452. }
  453. return $url;
  454. }
  455. /**
  456. * 获取最近阅读的链接
  457. * @param int $channelId
  458. * @return string
  459. */
  460. private function _buildRecentUrl($channelId)
  461. {
  462. $recentUrl = Message::getRecentFullPath($channelId);
  463. return $recentUrl;
  464. }
  465. /**
  466. * 获取目录绝对路径
  467. * @param $channelId
  468. * @param $menuId
  469. * @return string
  470. */
  471. private function _buildMenuUrl($channelId, $menuId)
  472. {
  473. $menuPath = Menu::$allLinks[$menuId]['menu_url'];
  474. $menuFullPath = getCurrentDomain($channelId, $menuPath);
  475. return $menuFullPath;
  476. }
  477. #endregion
  478. /**
  479. * 构造url的打点信息
  480. * @param $msgType
  481. * @param $aMessageJson
  482. * @param $sendTime
  483. * @param $customId
  484. * @return array
  485. * @throws \Exception
  486. */
  487. private function _buildPointInfo($msgType, $aMessageJson, $sendTime, $customId, $channelId = 0)
  488. {
  489. $customMsgJson = [];
  490. $customUpdate = [];
  491. $shortUrlService = new ShortUrlService();
  492. if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  493. $mark = BigData::BIG_DATA_MARK_IMAGE_TEXT;
  494. foreach ($aMessageJson as $idx => $msgJson) {
  495. $url = $msgJson['url'] ?? '';
  496. $url = $this->_urlBuildExt($url, $mark, $sendTime, $customId, $idx + 1);
  497. $shortUrl = $shortUrlService->create($url, $channelId);
  498. $description = ' ';
  499. if (isset($msgJson['description']) && !empty($msgJson['description'])) {
  500. $description = $msgJson['description'];
  501. }
  502. $customMsgInfo = [
  503. 'type' => (isset($msgJson['mini_type']) && $msgJson['mini_type'] == 4) ? 11 : $msgJson['type'],
  504. 'title' => $msgJson['title'],
  505. 'url' => $shortUrl,
  506. 'image' => $msgJson['image'],
  507. 'description' => $description,
  508. ];
  509. if ($customMsgInfo['type'] == 10 || $customMsgInfo['type'] == 11 ){//小程序类型
  510. $mini_content = array(
  511. 'appid'=>$msgJson['appid']??'',
  512. 'page'=>$msgJson['page']??'',
  513. 'media_id'=>$msgJson['media_id']??'',
  514. 'title'=>$msgJson['title'],
  515. );
  516. $customMsgInfo['mini_content'] = \GuzzleHttp\json_encode($mini_content,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
  517. }
  518. if (isset($msgJson['media_id'])){
  519. $customMsgInfo['media_id'] = $msgJson['media_id'];
  520. }
  521. $customMsgJson[] = $customMsgInfo;
  522. }
  523. $customUpdate['message_json'] = json_encode($customMsgJson, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
  524. } elseif ($msgType == Message::MESSAGE_TYPE_LINK) {
  525. $mark = BigData::BIG_DATA_MARK_LINK;
  526. $customMsgLink = [];
  527. foreach ($aMessageJson as $idx => $msgJson) {
  528. if (!array_key_exists('type', $msgJson) || $msgJson['type'] == Message::MESSAGE_LINK_TYPE_NOURL) {
  529. $customMsgLink[] = $msgJson['title'];
  530. } else {
  531. if($msgJson['type'] == Message::MESSAGE_LINK_TYPE_SIGN){
  532. $customMsgLink[] = sprintf('<a href="%s">%s</a>', $msgJson['url'], $msgJson['title']);
  533. }else if($msgJson['type'] == Message::MESSAGE_LINK_TYPE_MINI){
  534. $customMsgLink[] = $msgJson['url'];
  535. }else{
  536. $url = $msgJson['url'];
  537. $url = $this->_urlBuildExt($url, $mark, $sendTime, $customId, $idx + 1);
  538. $shortUrl = $shortUrlService->create($url, $channelId);
  539. $customMsgLink[] = sprintf('<a href="%s">%s</a>', $shortUrl, $msgJson['title']);
  540. }
  541. }
  542. }
  543. $customUpdate['message_text'] = implode("\n\n", $customMsgLink);
  544. }
  545. return $customUpdate;
  546. }
  547. /**
  548. * 推广链接js打点相关参数
  549. * @param $url
  550. * @param $mark
  551. * @param int $sendTime
  552. * @param int $customId
  553. * @param int $idx
  554. * @return string
  555. */
  556. private function _urlBuildExt($url, $mark, $sendTime, $customId, $idx)
  557. {
  558. $url = trim($url);
  559. if(!strpos($url, '{$ophost}')){
  560. // 如果 url中没有 域名占位符,不加入ext参数
  561. $url = $this->_rejectUrlExt($url);
  562. return $url;
  563. }else{
  564. $ext = [
  565. 'mark' => $mark,
  566. 'push_time' => $sendTime,
  567. 'push_id' => $customId,
  568. 'push_idx' => $idx,
  569. ];
  570. $strExt = json_encode($ext);
  571. if (strpos($url, '?') === false) {
  572. $url .= '?ext=' . $strExt;
  573. } else {
  574. $url .= '&ext=' . $strExt;
  575. }
  576. }
  577. return $url;
  578. }
  579. private function _rejectUrlExt($url)
  580. {
  581. $url_data = parse_url($url);
  582. $query = isset($url_data['query']) ? $url_data['query'] : '';
  583. parse_str($query, $q_arr);
  584. if(!empty($q_arr['ext'])){
  585. unset($q_arr['ext']);
  586. }
  587. $url_data['query'] = http_build_query($q_arr);
  588. $url = $this->_unparse_url($url_data);
  589. return $url;
  590. }
  591. private function _unparse_url($parsed_url) {
  592. $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
  593. $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
  594. $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
  595. $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
  596. $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
  597. $pass = ($user || $pass) ? "$pass@" : '';
  598. $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
  599. $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
  600. $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
  601. return "$scheme$user$pass$host$port$path$query$fragment";
  602. }
  603. /**
  604. * 创建统计信息
  605. * @param $bookIds
  606. * @param $customId
  607. * @param $customMediaPushId
  608. * @param $msgType
  609. * @param $officialAccountId
  610. * @param $officialAccountType
  611. * @param $aMessageJson
  612. * @param $sendtime
  613. */
  614. public function createCustomUrl(
  615. $bookIds,
  616. $customId,
  617. $customMediaPushId,
  618. $msgType,
  619. $officialAccountId,
  620. $officialAccountType,
  621. $aMessageJson,
  622. $sendtime
  623. )
  624. {
  625. $aCustomUrl = [];
  626. $bookInfos = Message::getBooksInfo($bookIds);
  627. $officialAccountName = '';
  628. if ($officialAccountType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE) {
  629. $configData = $this->_adminConfigModel->find($officialAccountId);
  630. if (!empty($configData)) {
  631. $officialAccountName = $configData->json['authorizer_info']['nick_name'];
  632. }
  633. } elseif ($officialAccountType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SUBSCRIPTION) {
  634. $subObj = $this->_subscriptionModel->getSubscriptionById($officialAccountId);
  635. if (!empty($subObj)) {
  636. $officialAccountName = $subObj['name'];
  637. }
  638. }
  639. foreach ($aMessageJson as $idx => $msgInfo) {
  640. $bookId = $msgInfo['book_id'] ?? 0;
  641. $bookId = intval($bookId);
  642. $aCustomUrl[] = [
  643. 'custom_id' => $customId,
  644. 'idx' => $idx + 1,
  645. 'custom_media_push_id' => $customMediaPushId,
  646. 'title' => $msgInfo['title'],
  647. 'message_type' => $msgType,
  648. 'url' => $msgInfo['url'] ?? '',
  649. 'type' => $msgInfo['type'],
  650. 'official_account_id' => $officialAccountId,
  651. 'official_account_type' => $officialAccountType,
  652. 'official_account_name' => $officialAccountName,
  653. 'push_type' => $msgInfo['push'] ?? null,
  654. 'book_id' => $bookId,
  655. 'book_name' => $bookInfos[$bookId]['name'] ?? '',
  656. 'book_realname' => $bookInfos[$bookId]['realname'] ?? '',
  657. 'sendtime' => strtotime($sendtime),
  658. ];
  659. }
  660. $this->_customUrlModel->saveAll($aCustomUrl);
  661. }
  662. /**
  663. * 图文客服消息素材库 处理群发消息中的url
  664. * @param $channel_id
  665. * @param $list
  666. * @return string
  667. * @throws \Exception
  668. */
  669. public function processUrlForChannelId($channel_id, $list)
  670. {
  671. if ($list) {
  672. $list = json_decode($list, true);
  673. $customService = new CustomService();
  674. foreach ($list as $index => $item) {
  675. $list[$index]['url'] = UrlService::instance()->replaceReferralHost($channel_id, $customService->buildUrl($channel_id, $item))->data;
  676. if (empty($list[$index]['url'])) {
  677. Log::info("url 不存在");
  678. }
  679. }
  680. } else {
  681. $list = [];
  682. }
  683. return json_encode($list);
  684. }
  685. /**
  686. * @param $admin_id
  687. * @param $sub_ids
  688. * @param $open_id
  689. * @param string $data
  690. * @param int $ids
  691. * @param int $msg_type
  692. */
  693. public function sendMessageSubscribeToUser($admin_id, $sub_ids, $open_id, $data = '', $ids = 0, $msg_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
  694. {
  695. if (is_array($admin_id)) {
  696. $admin_id = array_pop($admin_id);
  697. }
  698. if (!$admin_id) {
  699. return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'admin_id参数错误');
  700. }
  701. if (!$sub_ids) {
  702. return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'sub_id参数错误');
  703. }
  704. if (!$open_id) {
  705. return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'open_id参数错误');
  706. }
  707. try {
  708. if ($ids) {
  709. $aCustomMedia = $this->_customMediaModel->find($ids);
  710. $msg_type = $aCustomMedia['message_type'];
  711. if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  712. $data = $aCustomMedia['message_json'];
  713. } else {
  714. $data = $aCustomMedia['message_text'];
  715. }
  716. }
  717. if (!$data) {
  718. return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, '素材参数无效');
  719. }
  720. //生成素材消息
  721. $data = \GuzzleHttp\json_decode($data, true);
  722. if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  723. #region easywechat 生成图文消息
  724. $newsArr = [];
  725. foreach ($data as $item) {
  726. $tmpArr = [];
  727. $tmpArr['title'] = $item['title'];
  728. $tmpArr['image'] = $item['image'] ?? ''; #文字链消息,没有image字段
  729. $tmpArr['url'] = self::instance()->buildUrl($admin_id, $item);
  730. $tmpObj = new NewsItem($tmpArr);
  731. array_push($newsArr, $tmpObj);
  732. }
  733. $news = new News($newsArr);
  734. #endregion
  735. } else {
  736. #region easywechat 生成文字消息
  737. $content = '';
  738. foreach ($data as $item) {
  739. $url = self::instance()->buildUrl($admin_id, $item);
  740. if ($url) {
  741. $content .= "<a href=\"" . $url . "\">";
  742. $content .= $item['title'];
  743. $content .= "</a>";
  744. } else {
  745. $content .= $item['title'];
  746. }
  747. $content .= "\n\n";
  748. }
  749. $news = new Text($content);
  750. #endregion
  751. }
  752. $success_num = 0;
  753. foreach ($sub_ids as $sub_id) {
  754. $wechat = new WeChatObject();
  755. $officialAccount = $wechat->getSubscriptionOfficialAccount($sub_id);
  756. if (!$officialAccount) {
  757. return LogService::toError(ErrorCode::DB_ERROR_SELECT, '无效的服务商信息');
  758. }
  759. $result = $officialAccount->customer_service
  760. ->message($news)
  761. ->to($open_id)
  762. ->send();
  763. if ($result['errcode'] == 0) {
  764. $success_num++;
  765. LogService::toSuccess(ErrorCode::SUCCESS, '发送成功');
  766. } else {
  767. LogService::alert("推送消息发送失败,内容:" . json_encode($result));
  768. }
  769. }
  770. if ($success_num) {
  771. return LogService::toSuccess(ErrorCode::SUCCESS, '发送完成');
  772. } else {
  773. return LogService::toSuccess(ErrorCode::SUCCESS, '发送失败');
  774. }
  775. } catch (\Exception $e) {
  776. LogService::exception($e);
  777. return LogService::toError(ErrorCode::EXCEPTION, '未知错误');
  778. }
  779. }
  780. /**
  781. * 测试粉丝
  782. * @param array $admin_ids 渠道id
  783. * @param int $user_id 用户id
  784. * @param string $data 素材json字符串
  785. * @param string $ids 素材id
  786. * @param string $msg_type 消息类型
  787. * @return array|\think\response\Json
  788. */
  789. public function sendMessageServiceToUser(array $admin_ids, $user_id, $data = '', $ids = 0, $msg_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
  790. {
  791. if (!$admin_ids || !is_array($admin_ids)) {
  792. return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'admin_id参数错误');
  793. }
  794. if (!$user_id) {
  795. return LogService::toError(ErrorCode::PARAMS_ERROR_INVALID, 'user_id参数错误');
  796. }
  797. $success_num = 0;
  798. try {
  799. if ($ids) {
  800. $aCustomMedia = $this->_customMediaModel->find($ids);
  801. $msg_type = $aCustomMedia['message_type'];
  802. if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  803. $data = $aCustomMedia['message_json'];
  804. } else {
  805. $data = $aCustomMedia['message_text'];
  806. }
  807. }
  808. if (!$data) {
  809. return LogService::toSuccess(ErrorCode::SUCCESS, '素材参数无效');
  810. }
  811. foreach ($admin_ids as $admin_id) {
  812. // refresh_token adminConfig表获取
  813. if (preg_match("/\d+/i", $user_id, $matches)) {
  814. if (!empty($matches)) {
  815. if (empty($matches)) {
  816. //没有匹配到数字。错误
  817. return LogService::toSuccess(ErrorCode::SUCCESS, '无法匹配到此用户');
  818. } else {
  819. $user_id = intval($matches[0]);
  820. }
  821. }
  822. } else {
  823. //没有匹配到数组,错误
  824. return LogService::toSuccess(ErrorCode::SUCCESS, '无法匹配到此用户');
  825. }
  826. $mUser = new User();
  827. $mAdminConfig = new AdminConfig();
  828. $user = $mUser->setConnect($user_id)->where('id', $user_id)->find();
  829. if (!$user) {
  830. return LogService::toSuccess(ErrorCode::SUCCESS, '无效的用户信息');
  831. }
  832. if ($user->channel_id != $admin_id) {
  833. continue;
  834. }
  835. $openid = $user->openid;
  836. $Config = $mAdminConfig->getAdminInfoAll($admin_id);
  837. $wechat = new WeChatObject($Config);
  838. $officialAccount = $wechat->getOfficialAccount();
  839. if (!$officialAccount) {
  840. return LogService::toSuccess(ErrorCode::SUCCESS, '无效的服务商信息');
  841. }
  842. $dataContent = str_replace(['$user_nickname'], [$user['nickname']], $data);
  843. $dataContent = \GuzzleHttp\json_decode($dataContent, true);
  844. $news = [];
  845. if ($msg_type == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  846. #region easywechat 生成图文消息
  847. $newsArr = [];
  848. foreach ($dataContent as $item) {
  849. if ($item['type'] == 10){
  850. switch ($item['mini_type']){
  851. case 3:
  852. $tempArgs = [];
  853. $tempArgs['appid'] = $item['appid'] ?? config('site.mini_appid') ;
  854. $tempArgs['title'] = $item['title'];
  855. $tempArgs['pagepath'] = $item['page'];
  856. $r = HigeMessageService::instance()->image2media($admin_id,$item['image']);
  857. $tempArgs['thumb_media_id'] = $r['media_id'];
  858. $miniObj = new MiniProgramPage($tempArgs);
  859. $result = $officialAccount->customer_service
  860. ->message($miniObj)
  861. ->to($openid)
  862. ->send();
  863. break;
  864. case 4:
  865. $re = MiniProgramService::instance()->getMiniContent($admin_id,$item);
  866. $miniImage = new Image(\GuzzleHttp\json_decode($re,true)['media_id']);
  867. $result = $officialAccount->customer_service
  868. ->message($miniImage)
  869. ->to($openid)
  870. ->send();
  871. break;
  872. default:
  873. break;
  874. }
  875. }else{
  876. $tmpArr = [];
  877. $tmpArr['title'] = $item['title'];
  878. $tmpArr['description'] = $item['description'] ?: ' ';
  879. $tmpArr['image'] = $item['image'] ?? ''; #文字链消息,没有image字段
  880. $tmpArr['url'] = self::instance()->buildUrl($admin_id, $item);
  881. $tmpObj = new NewsItem($tmpArr);
  882. }
  883. if (isset($tmpObj)){
  884. array_push($newsArr, $tmpObj);
  885. }
  886. }
  887. if (!empty($newsArr)){
  888. $news = new News($newsArr);
  889. }
  890. #endregion
  891. } else {
  892. #region easywechat 生成文字消息
  893. $str_list = [];
  894. foreach ($dataContent as $item) {
  895. $url = '';
  896. if ($item['type'] == Message::MESSAGE_LINK_TYPE_BOOK) {
  897. $sourceUrl = '/index/book/chapter?book_id='.$item['book_id'];
  898. $url = getCurrentDomain($admin_id, $sourceUrl);
  899. }elseif($item['type'] == Message::MESSAGE_LINK_TYPE_MINI){
  900. $tempArgs = [];
  901. $tempArgs['type'] = 1;
  902. $tempArgs['page'] = trim($item['page']);
  903. $tempArgs['content'] = $item['title'];
  904. $miniStr = MiniProgramService::instance()->getMiniProgram($tempArgs,$admin_id);
  905. $str_list[] = $miniStr;
  906. continue;
  907. }else {
  908. $url = self::instance()->buildUrl($admin_id, $item);
  909. }
  910. if ($url) {
  911. $str = '';
  912. $str .= "<a href=\"" . $url . "\">";
  913. $str .= $item['title'];
  914. $str .= "</a>";
  915. $str_list[] = $str;
  916. } else {
  917. $str_list[] = $item['title'];
  918. }
  919. }
  920. $content = implode("\n\n", $str_list);
  921. $news = new Text($content);
  922. #endregion
  923. }
  924. if (!empty($news)){
  925. $result = $officialAccount->customer_service
  926. ->message($news)
  927. ->to($openid)
  928. ->send();
  929. }
  930. if ($result['errcode'] == 0) {
  931. $success_num++;
  932. LogService::toSuccess(ErrorCode::SUCCESS, '发送成功');
  933. } else {
  934. LogService::alert("推送消息发送失败,内容:" . json_encode($result));
  935. }
  936. }
  937. if ($success_num) {
  938. return LogService::toSuccess(ErrorCode::SUCCESS, '发送完成');
  939. } else {
  940. return LogService::toSuccess(ErrorCode::SUCCESS, '发送失败');
  941. }
  942. } catch (\Exception $e) {
  943. LogService::exception($e);
  944. Log::error($e);
  945. return LogService::toError(ErrorCode::EXCEPTION, '未知错误');
  946. }
  947. }
  948. /**
  949. * 服务号群发消息
  950. * @param int $mediaId 素材ID
  951. * @param string $sendTime 发送时间
  952. * @param array $channelIds 服务号id
  953. * @param int $adminId 后台账号id
  954. * @return array
  955. */
  956. public function multiSendMessageService($mediaId, $sendTime, $channelIds, $adminId, $other = [])
  957. {
  958. try {
  959. # 读取 media 信息
  960. $media = $this->_customMediaModel::get(['id' => $mediaId])->toArray();
  961. $msgType = $media['message_type'];
  962. if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  963. $msgContent = $media['message_json'];
  964. } else {
  965. $msgContent = $media['message_text'];
  966. }
  967. $params = Request::instance()->post("row/a");
  968. return self::instance()->createPushService($sendTime, $channelIds, $adminId, $msgType, $msgContent, $mediaId, false, '', $params['user_json'], Custom::CUSTOM_CREATED_FROM_GROUP_SEND, $other);
  969. } catch (\Exception $e) {
  970. LogService::exception($e);
  971. return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
  972. }
  973. }
  974. /**
  975. * 服务号群发消息
  976. * @param int $mediaId 素材ID
  977. * @param string $sendTime 发送时间
  978. * @param array $subIds 订阅号id
  979. * @param int $channelId 服务号id
  980. * @param int $adminId 后台账号id
  981. * @return array
  982. */
  983. public function multiSendMessageSub($mediaId, $sendTime, $subIds, $channelId, $adminId)
  984. {
  985. try {
  986. //获取素材信息
  987. $media = $this->_customMediaModel::get(['id' => $mediaId])->toArray();
  988. $msgType = $media['message_type'];
  989. if ($msgType == Message::MESSAGE_TYPE_IMAGE_TEXT) {
  990. $msgContent = $media['message_json'];
  991. } else {
  992. $msgContent = $media['message_text'];
  993. }
  994. return self::instance()->createPushSub($sendTime, $subIds, $channelId, $adminId, $msgType, $msgContent, $mediaId, false, Custom::CUSTOM_CREATED_FROM_GROUP_SEND);
  995. } catch (\Exception $e) {
  996. LogService::exception($e);
  997. return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
  998. }
  999. }
  1000. /**
  1001. * 推送订阅号
  1002. * @param string $sendTime 发送时间
  1003. * @param array $subIds 订阅号id
  1004. * @param int $channelId 服务号id
  1005. * @param int $adminId 后台登录账号id
  1006. * @param int $msgType 消息类型
  1007. * @param string $msgContent 消息体
  1008. * @param int $mediaId 素材id
  1009. * @param bool $toSave 是否保存为素材
  1010. * @param string $created_from 是否保存为素材
  1011. * @return array
  1012. */
  1013. public function createPushSub($sendTime, $subIds, $channelId, $adminId, $msgType, $msgContent, $mediaId = 0, $toSave = false, $created_from = Custom::CUSTOM_CREATED_FROM_ADD)
  1014. {
  1015. try {
  1016. if ($toSave) {
  1017. $mediaId = self::instance()->createMedia($adminId, $msgType, $msgContent);
  1018. }
  1019. $officialType = OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SUBSCRIPTION;
  1020. $mediaPushData = [
  1021. 'custom_media_id' => $mediaId,
  1022. 'sendtime' => $sendTime,
  1023. 'created_admin_id' => $adminId,
  1024. 'created_from' => $created_from,
  1025. ];
  1026. $customMediaPushId = self::instance()->createCustomMediaPush($mediaPushData, $msgType, $officialType, $subIds, $msgContent);
  1027. //endregion
  1028. $msgContent = self::instance()->processUrlForChannelId($channelId, $msgContent);
  1029. // custom 表中记录 N 条消息 (选择了N个订阅号)
  1030. foreach ($subIds as $subId) {
  1031. // 服务号
  1032. $data = [
  1033. 'title' => '',
  1034. 'sendtime' => $sendTime,
  1035. 'admin_id' => $channelId,
  1036. 'created_admin_id' => $adminId,
  1037. 'custom_media_push_id' => $customMediaPushId,
  1038. 'official_account_type' => $officialType, // 公众号类型:0=服务号,1=订阅号
  1039. 'official_account_id' => $subId, // 公众号id
  1040. 'created_from' => $created_from,
  1041. ];
  1042. list($customId, $bookIds, $aMessageJson) = CustomService::instance()->createCustom($data, $msgType, $msgContent);
  1043. #保存客服消息统计信息
  1044. CustomService::instance()->createCustomUrl($bookIds, $customId, $customMediaPushId, $msgType,
  1045. $channelId, $officialType, $aMessageJson, $sendTime);
  1046. }
  1047. return ['error' => ErrorCode::SUCCESS, 'msg' => 'ok'];
  1048. } catch (\Exception $e) {
  1049. LogService::exception($e);
  1050. return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
  1051. }
  1052. }
  1053. /**
  1054. * 推送服务号
  1055. * @param string $sendTime 发送时间
  1056. * @param array $channelIds 服务号id
  1057. * @param int $adminId 后台登录账号id
  1058. * @param int $msgType 消息类型
  1059. * @param string $msgContent 消息体
  1060. * @param int $mediaId 素材id
  1061. * @param bool $toSave 是否保存为素材
  1062. * @param string $title 消息标题
  1063. * @param string $created_from 消息标题
  1064. * @return array
  1065. */
  1066. public function createPushService($sendTime, $channelIds, $adminId, $msgType, $msgContent, $mediaId = 0, $toSave = false, $title = '', $userJson = null, $created_from = Custom::CUSTOM_CREATED_FROM_ADD, $other = [])
  1067. {
  1068. try {
  1069. if ($toSave) {
  1070. $mediaId = self::instance()->createMedia($adminId, $msgType, $msgContent);
  1071. }
  1072. # 服务号 OR 订阅号
  1073. $officialType = OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE;
  1074. $mediaPushData = [
  1075. 'custom_media_id' => $mediaId,
  1076. 'sendtime' => $sendTime,
  1077. 'created_admin_id' => $adminId,
  1078. 'created_from' => $created_from,
  1079. 'user_json' => $userJson,
  1080. ];
  1081. $channelIds = array_unique($channelIds);
  1082. $customMediaPushId = self::instance()->createCustomMediaPush($mediaPushData, $msgType, $officialType, $channelIds, $msgContent, $other);
  1083. $ignoreArr = [];
  1084. foreach ($channelIds as $channel_id) {
  1085. $msgContent = self::instance()->processUrlForChannelId($channel_id, $msgContent);
  1086. $jsonData = json_decode($msgContent, true);
  1087. $del = [];
  1088. foreach ($jsonData as $key=>$item) {
  1089. if ($item['type'] == Message::MESSAGE_LINK_TYPE_CUSTOMIZE_ACTIVITY && empty($item['url'])) {
  1090. //url为空 则跳过 不创建客服消息
  1091. $del[] = $key;
  1092. }
  1093. //如果type为url常用链接类型(type=10)&&选择的链接类型为Vip充值和小额充值,需要判断当前渠道是否有vip充值和小额充值权限
  1094. else if($item['type'] == Message::MESSAGE_LINK_TYPE_DAILY_LINK ){
  1095. //常用链接
  1096. $dailyLink = CommonService::instance()->getTmpDailyLink();
  1097. $adminConfig = AdminService::instance()->getAdminConfigModel()->getAdminInfoAll($channel_id);
  1098. //vip充值
  1099. if(strpos($item['url'],$dailyLink['vip']['link'])){
  1100. if(($adminConfig['vip_state'] ?? false) && ($adminConfig['vip_goods_id'] ?? null)){
  1101. //vip自定义商品
  1102. if(($adminconfig['vip_goods_id_custom'] ?? false)){
  1103. $item['url'] = $item['url']."&vip_custom_money=1";
  1104. }
  1105. }else{
  1106. $del[] = $key;
  1107. }
  1108. }
  1109. }//小程序
  1110. else if($item['type'] == Message::MESSAGE_LINK_TYPE_MINI){
  1111. $temMini = [];
  1112. $temMini['type'] = $item['mini_type'] ?? 1;
  1113. if ($temMini['type'] == 1){//文本
  1114. $temMini['page'] = $item['page'];
  1115. $temMini['content'] = $item['title'];
  1116. $miniContent = MiniProgramService::instance()->getMiniProgram($temMini, $adminId);
  1117. $jsonData[$key]['url'] = $miniContent;
  1118. }else{//图文
  1119. $con = MiniProgramService::instance()->getMiniContent($adminId,$item);
  1120. $conArr = \GuzzleHttp\json_decode($con,true);
  1121. $jsonData[$key] = array_merge($item,$conArr);
  1122. }
  1123. }
  1124. }
  1125. $lastContent = [];
  1126. if ($del) {
  1127. foreach ($jsonData as $index=>$row) {
  1128. if (!in_array($index, $del)) {
  1129. $lastContent[] = $row;
  1130. }
  1131. }
  1132. } else {
  1133. $lastContent = $jsonData;
  1134. }
  1135. if (empty($lastContent)) {
  1136. $ignoreArr[] = $channel_id;
  1137. continue;
  1138. }
  1139. $msgContent = json_encode($lastContent, JSON_UNESCAPED_UNICODE);
  1140. $data = [
  1141. 'title' => $title,
  1142. 'sendtime' => $sendTime,
  1143. 'admin_id' => $channel_id,
  1144. 'created_admin_id' => $adminId,
  1145. 'custom_media_push_id' => $customMediaPushId,
  1146. 'official_account_type' => $officialType, # 公众号类型:0=服务号,1=订阅号
  1147. 'official_account_id' => $channel_id, # 公众号id
  1148. 'created_from' => $created_from, # 公众号id
  1149. 'user_json' => $userJson ? json_encode(json_encode(json_decode($userJson, true),JSON_UNESCAPED_UNICODE), true) : null,
  1150. ];
  1151. list($customId, $bookIds, $aMessageJson) = CustomService::instance()->createCustom($data, $msgType, $msgContent);
  1152. #保存客服消息统计信息
  1153. CustomService::instance()->createCustomUrl($bookIds, $customId, $customMediaPushId, $msgType,
  1154. $channel_id, $officialType, $aMessageJson, $sendTime);
  1155. }
  1156. if ($ignoreArr && count($ignoreArr) == count($channelIds)) {
  1157. //消息为空 删除push
  1158. $this->_customMediaPushModel->where('id', 'eq', $customMediaPushId)->delete();
  1159. Log::info("所有公众号消息为空, 删除push");
  1160. return ['error' => ErrorCode::EXCEPTION, 'msg' => "您选择的公众号中存在未创建此活动的账户,请核对后重试"];
  1161. }
  1162. return ['error' => ErrorCode::SUCCESS, 'msg' => 'ok'];
  1163. } catch (\Exception $e) {
  1164. //LogService::exception($e);
  1165. Log::error($e->getMessage());
  1166. return ['error' => ErrorCode::EXCEPTION, 'msg' => $e->getMessage()];
  1167. }
  1168. }
  1169. /**
  1170. * 获取服务号的名字
  1171. * @param $channelId
  1172. * @return string
  1173. * @throws \think\db\exception\DataNotFoundException
  1174. * @throws \think\db\exception\ModelNotFoundException
  1175. * @throws \think\exception\DbException
  1176. */
  1177. public function getServiceNameByChannelId($channelId)
  1178. {
  1179. $adminConfig = $this->_adminConfigModel->where(['admin_id' => $channelId])->find();
  1180. if ($adminConfig) {
  1181. return $adminConfig->json['authorizer_info']['nick_name'];
  1182. } else {
  1183. return '';
  1184. }
  1185. }
  1186. /**
  1187. * 获取 custom_media_push 对象
  1188. * @param $push_id custom_media_push ID
  1189. * @return array
  1190. */
  1191. public function getCustomMessageByPushId($push_id)
  1192. {
  1193. $customPushData = $this->_customMediaPushModel->getCustomInfoById($push_id);
  1194. return $customPushData;
  1195. }
  1196. /**
  1197. * 更改custom数据
  1198. * @param $customData 数据
  1199. * @param $customId custom 表 ID
  1200. * @param $channelId 渠道ID
  1201. */
  1202. public function updateCustomMessage($customData, $customId, $channelId)
  1203. {
  1204. $msgType = $customData['message_type'];
  1205. if (!$msgType) {
  1206. $message_arr = json_decode($customData['message_json'], true, 1024);
  1207. } else {
  1208. $message_arr = json_decode($customData['message_text'], true, 1024);
  1209. }
  1210. foreach ($message_arr as $key => $item) {
  1211. if ($item['type'] == Message::MESSAGE_LINK_TYPE_MINI){//处理小程序类型
  1212. $temMini = [];
  1213. $temMini['type'] = $item['mini_type'] ?? 1;
  1214. if ($temMini['type'] == 1){//文本
  1215. $temMini['page'] = $item['page'];
  1216. $temMini['content'] = $item['title'];
  1217. $miniContent = MiniProgramService::instance()->getMiniProgram($temMini, $channelId);
  1218. $message_arr[$key]['url'] = $miniContent;
  1219. }else{//图文
  1220. $con = MiniProgramService::instance()->getMiniContent($channelId,$item);
  1221. $conArr = \GuzzleHttp\json_decode($con,true);
  1222. $message_arr[$key] = array_merge($item,$conArr);
  1223. }
  1224. }else{
  1225. $url = self::instance()->buildUrl($channelId, $item);
  1226. //转换占位符
  1227. $message_arr[$key]['url'] = UrlService::instance()->replaceReferralHost($channelId, $url)->data;
  1228. }
  1229. }
  1230. $pointInfo = $this->_buildPointInfo($msgType, $message_arr, $customData['sendtime'], $customId, $channelId);
  1231. foreach ($pointInfo as $key=>&$item) {
  1232. $item = UrlService::instance()->replaceReferralHost($channelId, $item)->data;
  1233. }
  1234. $customData = array_merge($customData, $pointInfo);
  1235. $result = $this->_customModel->allowField(true)->save($customData, ['id' => $customId]);
  1236. return $result;
  1237. }
  1238. /**
  1239. * 获取发送成功|失败的订阅号数据
  1240. * @param $pushId custom_media_push 表ID
  1241. * @param int $statue 1 => 发送成功 0=> 发送失败
  1242. * @return array
  1243. * @throws \think\Exception
  1244. * @throws \think\db\exception\DataNotFoundException
  1245. * @throws \think\db\exception\ModelNotFoundException
  1246. * @throws \think\exception\DbException
  1247. */
  1248. public function getOfficialSubList($pushId, $statue = Custom::CUSTOM_STATUE_HIDDEN)
  1249. {
  1250. $push_data = $this->_customMediaPushModel->get(['id' => $pushId])->toArray();
  1251. $data = $this->_customModel
  1252. ->alias('t')
  1253. ->join('subscription t1', 't.official_account_id = t1.id', 'left')
  1254. ->where('t.custom_media_push_id', '=', $pushId)
  1255. ->where('t.statue', '=', $statue)
  1256. ->where('t.official_account_id', 'in', $push_data['official_account_subscribe_ids'])
  1257. ->field('t1.name')
  1258. ->select();
  1259. $list = [];
  1260. if ($data) {
  1261. foreach ($data as $key => $item) {
  1262. $list[] = $item['name'];
  1263. }
  1264. }
  1265. return $list;
  1266. }
  1267. /**
  1268. * 获取发送成功|失败的订阅号数据
  1269. * @param $pushId custom_media_push 表ID
  1270. * @param int $statue 1 => 发送成功 0=> 发送失败
  1271. * @return array
  1272. * @throws \think\Exception
  1273. * @throws \think\db\exception\DataNotFoundException
  1274. * @throws \think\db\exception\ModelNotFoundException
  1275. * @throws \think\exception\DbException
  1276. */
  1277. public function getOfficialServiceList($pushId, $statue = Custom::CUSTOM_STATUE_HIDDEN)
  1278. {
  1279. $push_data = $this->_customMediaPushModel->get(['id' => $pushId])->toArray();
  1280. $data = $this->_customModel
  1281. ->alias('t')
  1282. ->join('admin_config t1', 't.official_account_id = t1.admin_id', 'left')
  1283. ->where('t.custom_media_push_id', '=', $pushId)
  1284. ->where('t.statue', '=', $statue)
  1285. ->where('t.official_account_id', 'in', $push_data['official_account_service_ids'])
  1286. ->field('t1.json')
  1287. ->select();
  1288. $list = [];
  1289. if ($data) {
  1290. foreach ($data as $key => $item) {
  1291. $json = json_decode($item->json, true);
  1292. $list[] = $json['authorizer_info']['nick_name'];
  1293. }
  1294. }
  1295. return $list;
  1296. }
  1297. /**
  1298. * @param $officialType
  1299. * @param $pushId
  1300. * @param $statue
  1301. * @return array
  1302. * @throws \think\Exception
  1303. * @throws \think\db\exception\DataNotFoundException
  1304. * @throws \think\db\exception\ModelNotFoundException
  1305. * @throws \think\exception\DbException
  1306. */
  1307. public function getOfficialPushStatusList($officialType, $pushId, $statue)
  1308. {
  1309. if ($officialType == OfficialAccount::OFFICIAL_ACCOUNT_TYPE_SERVICE) {
  1310. return self::instance()->getOfficialServiceList($pushId, $statue);
  1311. } else {
  1312. return self::instance()->getOfficialSubList($pushId, $statue);
  1313. }
  1314. }
  1315. public function getCustomMediaDetail($id, $image_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
  1316. {
  1317. $obj = $this->_customMediaModel->get(['id' => $id]);
  1318. if($image_type == Message::MESSAGE_TYPE_IMAGE_TEXT){
  1319. $message_json = json_decode($obj->message_json, true, 512);
  1320. }else{
  1321. $message_json = json_decode($obj->message_text, true, 512);
  1322. }
  1323. foreach($message_json as $index=>$item){
  1324. switch ($message_json[$index]['type']) {
  1325. case Message::MESSAGE_LINK_TYPE_MENU:
  1326. $message_json[$index]['book_name'] = Menu::$allLinks[$message_json[$index]['menu_id']]['title'];
  1327. break;
  1328. case Message::MESSAGE_LINK_TYPE_ACTIVITY:
  1329. $activity = model('Activity')
  1330. ->where('id', $message_json[$index]['activity_id'])->find();
  1331. $message_json[$index]['book_name'] = $activity['name'];
  1332. break;
  1333. case Message::MESSAGE_LINK_TYPE_CUSTOMIZE_ACTIVITY:
  1334. $activity = model('Activity')
  1335. ->where('id', $message_json[$index]['activity_id'])->find();
  1336. $message_json[$index]['book_name'] = $activity['name'];
  1337. break;
  1338. }
  1339. }
  1340. return array("total" => count($message_json), "rows" => $message_json);
  1341. }
  1342. public function getCustomMediaPushDetail($id, $image_type = Message::MESSAGE_TYPE_IMAGE_TEXT)
  1343. {
  1344. $obj = $this->_customMediaPushModel->get(['id' => $id]);
  1345. if($image_type == Message::MESSAGE_TYPE_IMAGE_TEXT){
  1346. $message_json = json_decode($obj->message_json, true, 512);
  1347. }else{
  1348. $message_json = json_decode($obj->message_text, true, 512);
  1349. }
  1350. foreach($message_json as $index=>$item){
  1351. switch ($message_json[$index]['type']) {
  1352. case Message::MESSAGE_LINK_TYPE_MENU:
  1353. $message_json[$index]['name'] = Menu::$allLinks[$message_json[$index]['menu_id']]['title'];
  1354. break;
  1355. case Message::MESSAGE_LINK_TYPE_ACTIVITY:
  1356. $activity = model('Activity')
  1357. ->where('id', $message_json[$index]['activity_id'])->find();
  1358. $message_json[$index]['name'] = $activity['name'];
  1359. break;
  1360. }
  1361. }
  1362. return array("total" => count($message_json), "rows" => $message_json);
  1363. }
  1364. }