MaterialService.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. namespace business\common\services;
  3. use app\common\exceptions\AppException;
  4. use business\common\models\Material;
  5. use EasyWeChat\Factory;
  6. class MaterialService
  7. {
  8. static $current;
  9. protected $agent_app;
  10. protected $nickname;
  11. protected $business_id;
  12. protected $filterName = false;
  13. const MEDIA_UPLOAD = "cgi-bin/media/upload";
  14. private function __construct($business_id)
  15. {
  16. $business_id or $business_id = SettingService::getBusinessId();
  17. if (!$business_id) {
  18. throw new AppException('企业ID参数缺失!');
  19. }
  20. $this->business_id = $business_id;
  21. }
  22. /**
  23. * @param $business_id
  24. * @param $app
  25. * @return static
  26. * @throws AppException
  27. */
  28. public static function current($business_id = 0, $app = null)
  29. {
  30. if (static::$current) {
  31. return static::$current;
  32. }
  33. static::$current = new static($business_id);
  34. if ($app) {
  35. static::$current->setApp($app);
  36. } else {
  37. $config = SettingService::getQyWxSetting($business_id);
  38. static::$current->setApp(Factory::work([
  39. 'corp_id' => $config['corpid'],
  40. 'agent_id' => $config['agentid'],
  41. 'secret' => $config['contact_secret'],
  42. ]));
  43. }
  44. return static::$current;
  45. }
  46. public function setApp($app)
  47. {
  48. $this->agent_app = $app;
  49. }
  50. // 设置文本昵称
  51. public function setNickname($nickname)
  52. {
  53. $this->nickname = $nickname;
  54. }
  55. public function filterName()
  56. {
  57. $this->filterName = true;
  58. }
  59. /**
  60. * Upload Image.
  61. *
  62. * @return mixed
  63. */
  64. public function uploadImage(string $path)
  65. {
  66. return $this->upload('image', $path);
  67. }
  68. /**
  69. * Upload Voice.
  70. *
  71. * @return mixed
  72. */
  73. public function uploadVoice(string $path)
  74. {
  75. return $this->upload('voice', $path);
  76. }
  77. /**
  78. * Upload Video.
  79. *
  80. * @return mixed
  81. */
  82. public function uploadVideo(string $path)
  83. {
  84. return $this->upload('video', $path);
  85. }
  86. /**
  87. * Upload File.
  88. *
  89. * @return mixed
  90. */
  91. public function uploadFile(string $path)
  92. {
  93. return $this->upload('file', $path);
  94. }
  95. /**
  96. * 组装请求
  97. * @param string $type
  98. * @param string $url
  99. * @return mixed
  100. */
  101. private function upload(string $type, string $url)
  102. {
  103. $query = ['type' => $type];
  104. $multipart[0] = [
  105. 'name' => "media",
  106. 'filename' => self::baseName($url),
  107. 'contents' => fopen($url, 'r'),
  108. ];
  109. return $this->httpUpload($query, $multipart);
  110. }
  111. /**
  112. * 发送请求上传临时素材给企业微信
  113. * @param string $query
  114. * @param array $multipart
  115. * @return mixed
  116. */
  117. private function httpUpload(array $query, array $multipart)
  118. {
  119. $res = $this->agent_app->media->request(self::MEDIA_UPLOAD, 'POST', ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 30, 'timeout' => 30, 'read_timeout' => 30]);
  120. return $res;
  121. }
  122. /**
  123. * 返回文件名
  124. * @param $filename
  125. * @return string
  126. */
  127. public static function baseName($filename) : string
  128. {
  129. return preg_replace('/^.+[\\\\\\/]/', '', $filename);
  130. }
  131. public function handleContent($content_list)
  132. {
  133. foreach ($content_list as &$v) {
  134. switch ($v['type']) {
  135. case 'text':
  136. // PC后台编辑素材内容时换行有以下三种形式
  137. $str = str_replace('<div><br></div>', "\n", html_entity_decode($v['news']));
  138. $str = str_replace('<br>',"\n",$str);
  139. $v['news'] = strip_tags(str_replace('<div>',"\n",$str));
  140. if ($this->filterName){
  141. $v['news'] = str_replace('客户昵称', '', $v['news']);
  142. }
  143. // 传入客户昵称或群聊昵称则替换
  144. if ($this->nickname) {
  145. $v['news'] = str_replace('客户昵称', $this->nickname, $v['news']);
  146. }
  147. break;
  148. case 'image':
  149. $v['media_id'] = $this->getMediaId($v['type'], $v['link_img']);
  150. break;
  151. case 'link':
  152. break;
  153. case 'applet':
  154. $v['media_id'] = $this->getMediaId('image', $v['link_img']);
  155. if ($v['page']){
  156. if ($start = strpos($v['page'],'?') !== false){
  157. $v['page'] = substr_replace($v['page'],'.html?',$start,1);
  158. }else{
  159. $v['page'] .= '.html';
  160. }
  161. }
  162. break;
  163. case 'video':
  164. $v['media_id'] = $this->getMediaId($v['type'], $v['link_video']);
  165. break;
  166. case 'file':
  167. $v['media_id'] = $this->getMediaId($v['type'], $v['link_file']);
  168. break;
  169. }
  170. }
  171. return $content_list;
  172. }
  173. public function getMediaId($type, $url)
  174. {
  175. $file_name = self::baseName($url);
  176. $model = Material::builder($this->business_id)
  177. ->where([
  178. 'type' => $type,
  179. 'file_name' => $file_name
  180. ])
  181. ->first();
  182. if ($model and (time() - $model->updated_at < 60 * 60 * 24 * 3)) {
  183. return $model->media_id;
  184. }
  185. switch ($type) {
  186. case 'image':
  187. $res = $this->uploadImage($url);
  188. break;
  189. case 'video':
  190. $res = $this->uploadVideo($url);
  191. break;
  192. case 'file':
  193. $res = $this->uploadFile($url);
  194. break;
  195. }
  196. if (!is_array($res)) {
  197. $res = json_decode($res, true);
  198. }
  199. if ($res['errcode'] === 0) {
  200. if (!$model) {
  201. $model = new Material();
  202. }
  203. $data = [
  204. 'uniacid' => \YunShop::app()->uniacid,
  205. 'business_id' => $this->business_id,
  206. 'type' => $type,
  207. 'file_name' => $file_name,
  208. 'file_url' => $url,
  209. 'media_id' => $res['media_id'],
  210. 'updated_at' => time()
  211. ];
  212. $model->fill($data);
  213. if ($model->save()) {
  214. return $res['media_id'];
  215. } else {
  216. \Log::debug('media_id保存失败', ['uniacid' => $data['uniacid'], 'business_id' => $data['business_id'], 'type' => $type, 'file_name' => $file_name, 'media_id' => $res['media_id']]);
  217. throw new \Exception('media_id保存失败');
  218. }
  219. } else {
  220. \Log::debug('素材上传失败,' . $res['errmsg'], ['uniacid' => \YunShop::app()->uniacid, 'business_id' => $this->business_id, 'type' => $type, 'file_name' => $file_name]);
  221. throw new \Exception('media_id获取失败');
  222. }
  223. }
  224. }