AttachmentService.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. <?php
  2. namespace addons\Wechat\services;
  3. use common\helpers\ArrayHelper;
  4. use Yii;
  5. use yii\data\Pagination;
  6. use common\helpers\StringHelper;
  7. use common\helpers\Url;
  8. use common\enums\StatusEnum;
  9. use common\components\Service;
  10. use EasyWeChat\Kernel\Messages\Article;
  11. use addons\Wechat\common\models\Attachment;
  12. use addons\Wechat\common\models\AttachmentNews;
  13. use addons\Wechat\common\enums\AttachmentLinkTypeEnum;
  14. use addons\Wechat\common\enums\AttachmentTypeEnum;
  15. use addons\Wechat\common\enums\AttachmentModelEnum;
  16. /**
  17. * Class AttachmentService
  18. * @package addons\Wechat\services
  19. * @author jianyan74 <751393839@qq.com>
  20. */
  21. class AttachmentService extends Service
  22. {
  23. /**
  24. * 预览方法
  25. *
  26. * @var array
  27. */
  28. protected $previewActions = [
  29. AttachmentTypeEnum::TEXT => 'previewText',
  30. AttachmentTypeEnum::NEWS => 'previewNews',
  31. AttachmentTypeEnum::VOICE => 'previewVoice',
  32. AttachmentTypeEnum::IMAGE => 'previewImage',
  33. AttachmentTypeEnum::VIDEO => 'previewVideo',
  34. AttachmentTypeEnum::CARD => 'previewCard',
  35. ];
  36. /**
  37. * 基于名称的预览方法
  38. *
  39. * @var array
  40. */
  41. protected $previewActionsByName = [
  42. AttachmentTypeEnum::TEXT => 'previewTextByName',
  43. AttachmentTypeEnum::NEWS => 'previewNewsByName',
  44. AttachmentTypeEnum::VOICE => 'previewVoiceByName',
  45. AttachmentTypeEnum::IMAGE => 'previewImageByName',
  46. AttachmentTypeEnum::VIDEO => 'previewVideoByName',
  47. AttachmentTypeEnum::CARD => 'previewCardByName',
  48. ];
  49. /**
  50. * 获取资源数据
  51. *
  52. * @param string $media_type
  53. * @param string $year
  54. * @param string $month
  55. * @param string $keyword
  56. * @return array
  57. */
  58. public function getListPage($media_type = '', $year = '', $month = '', $keyword = '')
  59. {
  60. $data = Attachment::find()
  61. ->where(['status' => StatusEnum::ENABLED, 'media_type' => $media_type])
  62. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  63. ->andFilterWhere(['year' => $year])
  64. ->andFilterWhere(['month' => $month])
  65. ->andFilterWhere(['like', 'file_name', $keyword]);
  66. $pages = new Pagination(['totalCount' => $data->count(), 'pageSize' => 10, 'validatePage' => false]);
  67. $models = $data->offset($pages->offset)
  68. ->orderBy('id desc')
  69. ->limit($pages->limit)
  70. ->asArray()
  71. ->all();
  72. $list = [];
  73. foreach ($models as $model) {
  74. $listTmp = [];
  75. $listTmp['key'] = $model['media_id'];
  76. $listTmp['title'] = $model['file_name'];
  77. $listTmp['type'] = $model['media_type'];
  78. $listTmp['imgUrl'] = '';
  79. if ($media_type == AttachmentTypeEnum::IMAGE) {
  80. $listTmp['imgUrl'] = Url::to(['analysis/image', 'attach' => $model['media_url']]);
  81. }
  82. $list[] = $listTmp;
  83. unset($listTmp);
  84. }
  85. return $list;
  86. }
  87. /**
  88. * 资源预览
  89. *
  90. * @param $attach_id
  91. * @param $sendType
  92. * @param $content
  93. * @throws \EasyWeChat\Kernel\Exceptions\HttpException
  94. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  95. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  96. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  97. * @throws \Psr\SimpleCache\InvalidArgumentException
  98. * @throws \yii\web\UnprocessableEntityHttpException
  99. */
  100. public function preview($attach_id, $sendType, $content)
  101. {
  102. $attachment = $this->findById($attach_id);
  103. // 1:微信号预览 2:openid预览
  104. $method = $sendType == 1 ? $this->previewActionsByName[$attachment['media_type']] : $this->previewActions[$attachment['media_type']];
  105. $result = Yii::$app->wechat->app->broadcasting->$method($attachment['media_id'], $content);
  106. Yii::$app->services->base->getWechatError($result);
  107. }
  108. /**
  109. * 同步
  110. *
  111. * @param string $type 素材的类型,图片(image)、视频(video)、语音 (voice)、图文(news)
  112. * @param int $offset 从全部素材的该偏移位置开始返回,可选,默认 0,0 表示从第一个素材 返回
  113. * @param int $count 返回素材的数量,可选,默认 20, 取值在 1 到 20 之间
  114. * @return bool|array
  115. * @throws \EasyWeChat\Kernel\Exceptions\HttpException
  116. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  117. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  118. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  119. * @throws \Psr\SimpleCache\InvalidArgumentException
  120. * @throws \yii\db\Exception
  121. * @throws \yii\web\UnprocessableEntityHttpException
  122. */
  123. public function sync($type, $offset, $count)
  124. {
  125. $app = Yii::$app->wechat->app;
  126. if ($type == AttachmentTypeEnum::NEWS) {
  127. $lists = Yii::$app->wechat->app->draft->batchGet($offset, $count);
  128. } else {
  129. $lists = Yii::$app->wechat->app->material->list($type, $offset, $count);
  130. }
  131. // 解析微信接口是否报错.报错则抛出错误信息
  132. Yii::$app->services->base->getWechatError($lists);
  133. if (empty($lists)) {
  134. return true;
  135. }
  136. $total = $lists['total_count'];
  137. // 素材列表
  138. $list = $lists['item'];
  139. $addMaterial = [];
  140. // 系统内的素材
  141. $systemMaterial = $this->getListByMediaIds(array_column($list, 'media_id'));
  142. $defaultData = array_column($systemMaterial, 'media_id');
  143. switch ($type) {
  144. // ** 图文 **//
  145. case AttachmentTypeEnum::NEWS :
  146. foreach ($list as $vo) {
  147. if (!in_array($vo['media_id'], $defaultData)) {
  148. $attachment = new Attachment();
  149. $attachment->media_id = $vo['media_id'];
  150. $attachment->media_type = $type;
  151. $attachment->is_temporary = AttachmentModelEnum::MODEL_PERM;
  152. $attachment->created_at = $vo['update_time'];
  153. $attachment->save();
  154. // 插入文章
  155. foreach ($vo['content']['news_item'] as $key => $news_item) {
  156. $news = new AttachmentNews();
  157. $news->attributes = $news_item;
  158. $news->media_url = $news_item['url'];
  159. $news->content = str_replace("data-src", "src", $news->content);
  160. $news->attachment_id = $attachment->id;
  161. $news->sort = $key;
  162. $news->save();
  163. }
  164. }
  165. }
  166. break;
  167. //** 图片/语音/视频 **//
  168. default :
  169. $merchant_id = Yii::$app->services->merchant->getNotNullId();
  170. foreach ($list as $vo) {
  171. if (!in_array($vo['media_id'], $defaultData)) {
  172. // 判断是否是视频
  173. if (AttachmentTypeEnum::VIDEO == $type) {
  174. $detail = $app->material->get($vo['media_id']);
  175. $mediaUrl = $detail['down_url'];
  176. $mediaDescription = $detail['description'];
  177. } else {
  178. $mediaUrl = $vo['url'] ?? '';
  179. $mediaDescription = '';
  180. }
  181. $addMaterial[] = [
  182. $merchant_id,
  183. $vo['name'],
  184. $vo['media_id'],
  185. $mediaUrl,
  186. $mediaDescription,
  187. $type,
  188. AttachmentModelEnum::MODEL_PERM,
  189. $vo['update_time'],
  190. time()
  191. ];
  192. }
  193. }
  194. if (!empty($addMaterial)) {
  195. // 批量插入数据
  196. $field = [
  197. 'merchant_id',
  198. 'file_name',
  199. 'media_id',
  200. 'media_url',
  201. 'description',
  202. 'media_type',
  203. 'is_temporary',
  204. 'created_at',
  205. 'updated_at'
  206. ];
  207. Yii::$app->db->createCommand()->batchInsert(Attachment::tableName(), $field, $addMaterial)->execute();
  208. }
  209. break;
  210. }
  211. if ($total - ($offset + $count) > 0) {
  212. return [
  213. 'offset' => ($offset + $count),
  214. 'count' => $count
  215. ];
  216. }
  217. return true;
  218. }
  219. /**
  220. * @param Attachment $model
  221. * @param $list
  222. * @throws \EasyWeChat\Kernel\Exceptions\HttpException
  223. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  224. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  225. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  226. * @throws \Psr\SimpleCache\InvalidArgumentException
  227. * @throws \yii\web\UnprocessableEntityHttpException
  228. */
  229. public function editNews(Attachment $model, $list, $isNewRecord)
  230. {
  231. $wechatArticleList = [];
  232. // 获取图片的链接地址
  233. $localImageUrl = Url::to(['analysis/image']) . "?attach=";
  234. foreach ($list as $key => &$item) {
  235. $item['content'] = StringHelper::replace($localImageUrl, '', trim($item['content']));
  236. $item['thumb_url'] = StringHelper::replace($localImageUrl, '', trim($item['thumb_url']));
  237. // 上传资源
  238. list($media_id, $item['thumb_url']) = $this->isUploadWechatByImage($item['thumb_url']);
  239. !empty($media_id) && $item['thumb_media_id'] = $media_id;;
  240. // 循环上传文章图片到微信
  241. preg_match_all('/<img[^>]*src\s*=\s*([\'"]?)([^\'" >]*)\1/isu', $item['content'], $match);
  242. foreach ($match[2] as $src) {
  243. // 判断是否已经上传到微信了
  244. if (strpos(urldecode($src), AttachmentLinkTypeEnum::URL) === false) {
  245. $result = Yii::$app->wechat->app->material->uploadArticleImage(StringHelper::getLocalFilePath($src));
  246. // 替换图片上传
  247. $item['content'] = StringHelper::replace($src, $result['url'], $item['content']);
  248. }
  249. }
  250. $item['content'] = htmlspecialchars_decode($item['content']);
  251. // 默认微信返回值
  252. $wechatArticleList[] = $item;
  253. }
  254. // 上传到微信
  255. $news_item = $this->isUploadWechatByNews($model, $wechatArticleList, $isNewRecord);
  256. // 插入文章到表
  257. foreach ($list as $k => $vo) {
  258. $news = Yii::$app->wechatService->attachmentNews->findModel($vo['id'] ?? null);
  259. $news->attributes = $vo;
  260. $news->attachment_id = $model->id;
  261. // 判断是否微信 否则直接拿取图文链接
  262. if ($model->link_type == AttachmentLinkTypeEnum::WECHAT) {
  263. $isNewRecord && $news->media_url = $news_item[$k]['url'] ?? '';
  264. } else {
  265. $news->media_url = $vo['content_source_url'];
  266. }
  267. $news->sort = $k;
  268. !$news->save() && $this->error($news);
  269. }
  270. }
  271. /**
  272. * 上传更新图文到微信服务器
  273. *
  274. * @param Attachment $model
  275. * @param $wechatArticleList
  276. * @throws \EasyWeChat\Kernel\Exceptions\HttpException
  277. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  278. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  279. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  280. * @throws \Psr\SimpleCache\InvalidArgumentException
  281. * @throws \yii\web\UnprocessableEntityHttpException
  282. */
  283. protected function isUploadWechatByNews(Attachment $model, $wechatArticleList, $isNewRecord)
  284. {
  285. // 如果是微信图文则上传更新到微信服务器
  286. if ($model->link_type == AttachmentLinkTypeEnum::WECHAT) {
  287. if (!$isNewRecord) {
  288. // 更新图文
  289. foreach ($wechatArticleList as $k => $value) {
  290. $res = Yii::$app->wechat->app->draft->update($model['media_id'], $k, $value);
  291. Yii::$app->services->base->getWechatError($res);
  292. }
  293. } else {
  294. // 上传图文信息
  295. $res = Yii::$app->wechat->app->draft->add(['articles' => $wechatArticleList]);
  296. Yii::$app->services->base->getWechatError($res);
  297. $model->media_id = $res['media_id'];
  298. !$model->save() && $this->error($model);
  299. $getNews = Yii::$app->wechat->app->draft->get($res['media_id']);
  300. return $getNews['news_item'];
  301. }
  302. }
  303. return [];
  304. }
  305. /**
  306. * 上传封面到微信服务器
  307. *
  308. * @param $thumb_url
  309. * @return array
  310. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  311. */
  312. protected function isUploadWechatByImage($thumb_url)
  313. {
  314. if (strpos(urldecode($thumb_url), AttachmentLinkTypeEnum::URL) === false) {
  315. $model = new Attachment();
  316. $model->local_url = $thumb_url;
  317. // 上传到微信
  318. $material = Yii::$app->wechat->app->material->uploadImage(StringHelper::getLocalFilePath($thumb_url));
  319. $model->media_type = AttachmentTypeEnum::IMAGE;
  320. $model->media_id = $material['media_id'];
  321. $model->media_url = $material['url'];
  322. !$model->save() && $this->error($model);
  323. return [$model->media_id, $model->media_url];
  324. }
  325. return ['', $thumb_url];
  326. }
  327. /**
  328. * 创建资源
  329. *
  330. * @param Attachment $model
  331. * @throws \EasyWeChat\Kernel\Exceptions\HttpException
  332. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  333. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  334. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  335. * @throws \Psr\SimpleCache\InvalidArgumentException
  336. * @throws \yii\web\UnprocessableEntityHttpException
  337. */
  338. public function saveCreate(Attachment $model)
  339. {
  340. $defaultType = $model->media_type;
  341. $localFilePath = StringHelper::getLocalFilePath($model->local_url, $defaultType . 's');
  342. switch ($defaultType) {
  343. case AttachmentTypeEnum::VIDEO :
  344. $result = Yii::$app->wechat->app->material->uploadVideo($localFilePath, $model->file_name, $model->description);
  345. $detail = Yii::$app->wechat->app->material->get($result['media_id']);
  346. $model->media_url = $detail['down_url'];
  347. break;
  348. case AttachmentTypeEnum::IMAGE :
  349. $result = Yii::$app->wechat->app->material->uploadImage($localFilePath);
  350. $model->media_url = $result['url'] ?? '';
  351. $model->file_name = array_slice(explode('/', $model->local_url), -1, 1)[0];
  352. break;
  353. case AttachmentTypeEnum::VOICE :
  354. $result = Yii::$app->wechat->app->material->uploadVoice($localFilePath);
  355. $model->file_name = array_slice(explode('/', $model->local_url), -1, 1)[0];
  356. break;
  357. }
  358. Yii::$app->services->base->getWechatError($result);
  359. $model->media_id = $result['media_id'];
  360. !$model->save() && $this->error($model);
  361. return $model;
  362. }
  363. /**
  364. * 返回素材列表
  365. *
  366. * @param $type
  367. * @return array|\yii\db\ActiveRecord[]
  368. */
  369. public function getListByType($type)
  370. {
  371. return Attachment::find()
  372. ->where(['media_type' => $type])
  373. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  374. ->with('news')
  375. ->orderBy('id desc')
  376. ->asArray()
  377. ->all();
  378. }
  379. /**
  380. * @param $mediaId
  381. * @return array|null|\yii\db\ActiveRecord
  382. */
  383. public function findByMediaId($mediaId)
  384. {
  385. return Attachment::find()
  386. ->where(['media_id' => $mediaId])
  387. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  388. ->asArray()
  389. ->one();
  390. }
  391. /**
  392. * @param $mediaIds
  393. * @return array|\yii\db\ActiveRecord[]
  394. */
  395. public function getListByMediaIds($mediaIds)
  396. {
  397. return Attachment::find()
  398. ->where(['in', 'media_id', $mediaIds])
  399. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  400. ->select('media_id')
  401. ->asArray()
  402. ->all();
  403. }
  404. /**
  405. * @param $id
  406. * @return array|null|\yii\db\ActiveRecord
  407. */
  408. public function findById($id)
  409. {
  410. return Attachment::find()
  411. ->where(['id' => $id])
  412. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  413. ->asArray()
  414. ->one();
  415. }
  416. }
粤ICP备19079148号