ArticleLogic.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Administrator
  5. * Date: 2019/4/18 0018
  6. * Time: 16:27
  7. */
  8. namespace App\Http\Logics\Admin;
  9. use App\Http\Models\Article;
  10. use App\Http\Models\ArticleSiteKeep;
  11. use App\Http\Models\SiteInfo;
  12. use App\Jobs\TranArticle;
  13. use App\Libs\Ssh;
  14. use App\Libs\Translate;
  15. use DirkGroenen\Pinterest\Pinterest;
  16. use Illuminate\Database\Eloquent\Collection;
  17. use Illuminate\Support\Facades\Auth;
  18. use Illuminate\Support\Facades\Log;
  19. use GuzzleHttp\Client;
  20. use GuzzleHttp\Pool;
  21. use GuzzleHttp\Psr7\Response;
  22. use Lightit\LinkedinShare\Facades\LinkedinShare;
  23. class ArticleLogic
  24. {
  25. public function amountRule($words)
  26. {
  27. return round(($words / 300) * 10, 2);
  28. }
  29. public function autoTranslate(Collection $articles)
  30. {
  31. $translate = new Translate;
  32. $successNum = 0;
  33. foreach ($articles as $item) {
  34. $content = html_entity_decode(strip_tags($item->content));
  35. $result = json_decode($translate->request($content), true);
  36. if (empty($result['translation'][0])) {
  37. Log::info(var_export($result, 1));
  38. continue;
  39. }
  40. $item->translate_content = $result['translation'][0];
  41. $item->save();
  42. $successNum++;
  43. }
  44. return response()->json(['message' => $successNum . '篇翻译完成']);
  45. }
  46. public function save($validated, $id)
  47. {
  48. $validated['status'] = Article::STATUS_PENDING; //默认待翻译
  49. if (!empty($validated['translator_id'])) {
  50. if ($validated['translator_id'] == Article::INSIDER) { //如果是内部人员yingyingli
  51. $validated['status'] = Article::STATUS_PASSED; //已通过
  52. } elseif ($validated['translator_id'] == Article::IS_EXPIRED) {
  53. $validated['expired_at'] = date('Y-m-d H:i:s', strtotime('+20 year'));
  54. $validated['status'] = Article::STATUS_TRANSLATING; //翻译中
  55. } else {
  56. $validated['expired_at'] = date('Y-m-d H:i:s', strtotime('+1 day'));
  57. $validated['status'] = Article::STATUS_TRANSLATING; //翻译中
  58. }
  59. }
  60. if ($id > 0) { //编辑
  61. $article = Article::query()->where(['id' => $id])->first();
  62. if (!$article) return response()->json(['message' => '数据不存在']);
  63. $article->update($validated);
  64. } else { //添加
  65. // $client = new Client;
  66. // $response = $client->post('http://translate.api.yinqingli.net/translate/translate', [
  67. // 'form_params' => [
  68. // 'q' => $validated['content'],
  69. // 'source' => 'zh-CN',
  70. // 'target' => 'en'
  71. // ],
  72. // 'timeout' => 0
  73. // ]);
  74. // $result = json_decode($response->getBody()->getContents(), true);
  75. //
  76. // $validated['translate_content'] = $result['data']['translations'][0]['translatedText'] ?? '';
  77. // $validated['translate_words'] = !empty($validated['translate_content']) ? word_count($validated['translate_content']) : 0;
  78. $translate = new Translate;
  79. $result = json_decode($translate->request($validated['title']), true);
  80. if (!empty($result['translation'][0])) {
  81. $validated['translate_title'] = $result['translation'][0];
  82. $validated['init_tran_title'] = $validated['translate_title'];
  83. }
  84. $validated['creator_id'] = Auth::id();
  85. $article = Article::query()->create($validated);
  86. TranArticle::dispatch($article->id, $validated['content'])->onQueue('translate');
  87. }
  88. ArticleSiteKeep::query()->where(['user_id' => Auth::id()])->update(['site_id' => $article->id ?? 0]);
  89. return response()->json(['message' => '操作成功']);
  90. }
  91. protected function sendImage($targetBaseDir, $fullSrc)
  92. {
  93. $path = substr($fullSrc, 1);
  94. $originTruePath = public_path($path);
  95. if (file_exists($originTruePath)) {
  96. $targetTruePath = $targetBaseDir . $path;
  97. Ssh::mkdir(dirname($targetTruePath));
  98. try {
  99. Ssh::send($originTruePath, $targetTruePath);
  100. } catch (\Throwable $throwable) {
  101. Log::alert(var_export($throwable->getMessage(), 1));
  102. }
  103. }
  104. }
  105. public function sync($validated, $site, $server)
  106. {
  107. $articles = Article::query()->whereIn('id', $validated['article_ids'])->get();
  108. $siteInfo = SiteInfo::query()->select(['is_upload_thumb'])->where(['site_id' => $site->id])->first();
  109. if (!empty($siteInfo->is_upload_thumb)) {
  110. $failList = [];
  111. foreach ($articles as $ac) {
  112. if (empty($ac->thumb)) {
  113. $failList[] = $ac->cn_title;
  114. }
  115. }
  116. if ($failList) {
  117. return response()->json(['message' => '站点设置缩略图 不能为空' . implode("|", $failList)], 400);
  118. }
  119. }
  120. try {
  121. Ssh::factory($server->server_ip, $server->server_user_name, $server->server_passwd);
  122. } catch (\Throwable $throwable) {
  123. Log::alert(var_export($throwable->getMessage(), 1));
  124. return response()->json(['message' => '用户名密码验证失败'], 400);
  125. }
  126. $targetBaseDir = $site->code_dir ? sprintf('%s/wwwroot/', $site->code_dir)
  127. : sprintf('/repo/%s/wwwroot/', $site->domain);
  128. Log::info($targetBaseDir);
  129. if (!Ssh::dir_exists($targetBaseDir)) {
  130. return response()->json(['message' => '站点服务器目录不存在'], 400);
  131. }
  132. $requestData = [];
  133. $articles->each(function ($article, $inx) use ($validated, $targetBaseDir, &$requestData) {
  134. //<img src="http://build.cn/storage/uploads/image/2019/05/29/26cf238774423b482241baf35e39b774.jpg" >
  135. if (strpos($article->translate_content, '<img') !== false) {
  136. preg_match_all('/<img.*?src="(.*?)".*?>/i', $article->translate_content, $matches);
  137. $imgList = [];
  138. array_walk($matches[1], function ($val) use (&$imgList) {
  139. $imgList[] = $val;
  140. });
  141. array_walk($imgList, function ($fullSrc) use ($targetBaseDir) {
  142. $this->sendImage($targetBaseDir, $fullSrc);
  143. });
  144. }
  145. //http://build.cn/storage/201905/jpg/VnhVz2B4eigmihZo6AKb7MMz8F5TM9EF9A1sswVe.jpeg
  146. if ($article->thumb) {
  147. $this->sendImage($targetBaseDir, $article->thumb);
  148. }
  149. $publishTime = strtotime('+' . ($validated['release_interval'] * $inx) . ' day', strtotime($validated['release_at']));
  150. $requestData[$inx]['form_params'] = [
  151. 'lang' => 'en',
  152. // 'thumb' => preg_replace(sprintf('/%s/i', $url), '', $article->thumb),
  153. 'thumb' => $article->thumb,
  154. 'parent_id' => $validated['remote_content_id'],
  155. 'tpl_id' => $validated['template_id'],
  156. 'title' => htmlspecialchars($article->translate_title),
  157. 'uri' => $article->translate_title2,
  158. 'is_freeze_url' => 1,
  159. // 'content' => preg_replace_callback(sprintf('/(src=.)%s/i', $url), function ($matches) {
  160. // return $matches[1];
  161. // }, $article->content),
  162. // 'content' => preg_replace_callback(sprintf('/(src=.)%s/i', $url), function ($matches) {
  163. // return $matches[1];
  164. // }, $article->content),
  165. 'content' => $article->translate_content,
  166. 'is_enabled' => 1,
  167. 'publish_time' => $publishTime
  168. ];
  169. /** @var \App\Http\Models\Article $article * */
  170. $article->update([
  171. 'sync_at' => date('Y-m-d H:i:s'),
  172. 'publish_at' => date('Y-m-d H:i:s', $publishTime)
  173. ]);
  174. });
  175. $this->concurrentRequest($requestData, $site->api_url, $articles);
  176. // Article::query()->whereIn('id', $validated['article_ids'])->update(['sync_at' => date('Y-m-d H:i:s')]);
  177. return response()->json(['message' => '操作成功']);
  178. }
  179. protected function concurrentRequest($requestData, $apiUrl, Collection $articles)
  180. {
  181. $client = new Client([
  182. 'verify' => false,
  183. 'timeout' => 10
  184. ]); //并发请求链接地址
  185. $requests = function () use ($client, $requestData, $apiUrl) {
  186. foreach ($requestData as $item) {
  187. if (empty($item))
  188. continue;
  189. yield new \GuzzleHttp\Psr7\Request(
  190. 'POST',
  191. ($apiUrl . 'content/sync'),
  192. ['Content-type' => 'application/x-www-form-urlencoded'],
  193. http_build_query($item['form_params'])
  194. );
  195. }
  196. };
  197. $results = [];
  198. $pool = new Pool($client, $requests(), [
  199. 'concurrency' => 5, //同时并发抓取几个
  200. 'fulfilled' => function (Response $response, $index) use (&$results) {
  201. $content = $response->getBody()->getContents();
  202. $content = trim($content, "\xEF\xBB\xBF");
  203. $ret = json_decode($content, true);
  204. if (!empty($ret['id'])) {
  205. $results[$index] = $ret['id'];
  206. }
  207. },
  208. 'rejected' => function (\Throwable $throwable, $index) {
  209. Log::error(var_export($throwable->getMessage(), 1));
  210. },
  211. ]);
  212. $promise = $pool->promise();
  213. $promise->wait();
  214. foreach ($articles as $key => $article) {
  215. foreach ($results as $inx => $val) {
  216. if ($key == $inx) {
  217. $article->update(['remote_content_id' => $val]);
  218. break;
  219. }
  220. }
  221. }
  222. }
  223. public function pin()
  224. {
  225. $pin = new Pinterest(config('services.pinterest.client_id'), config('services.pinterest.client_secret'));
  226. $pin->auth->setOAuthToken('AsityC1Rl6-kSqfwD98akYNaj5gxFatL7qHFJjNF8JCvpODAwAu6ADAAAUegRfKs6higoeEAAAAA');
  227. try {
  228. $pin->pins->create([
  229. "note" => "Test board from API",
  230. "image_url" => "http://admin.yinqingli.com/storage/201906/jpg/1Q987Ryl6mdPFhJljhy5O1EhPol9M79dSVFbGnod.jpeg",
  231. "board" => "hinaqin/api-demo"
  232. ]);
  233. } catch (\Throwable $throwable) {
  234. Log::warning(var_export($throwable->getMessage(), 1));
  235. return false;
  236. }
  237. return true;
  238. }
  239. public function linkedIn($content)
  240. {
  241. $text = str_replace('<br/>', PHP_EOL, html_entity_decode(strip_tags($content, '<br/>')));
  242. // $text = $content;
  243. $token = 'AQUOiNxK2Fn0fEwbqCzeo26xT-CFp_DO57GJ1Hqf9XoD1zezGa1knRuENJyu7RtJTjQhSS3UZ_winzcHsxHxm_nWE0pJUclmN21BMsTmiYnqA_i2mjo8l26ZjZ0Y0Qn2umh0aGqt5XGVw9_YY5HkHmy_5JUjGQb7SMpdEO1mobEyb9CK0L5-jPcLAfmwLDRrap3KeeOIdu3i8EgPKzJG8dNqmcsbHuA1Gnq3WX1kXtBtKmnd1KzPxGbQSK9-V_txzz3VhdsRs6CFqoZrWdZjcxngkN26etQg6cG58Il1f7W7PVS8SKjpyC6rzIAEN9ee00dp3EvyfckcJDZTtYbojr-HUnmw_Q';
  244. try {
  245. LinkedinShare::shareTemp($token, 'http://admin.yinqingli.com/storage/201906/jpg/1Q987Ryl6mdPFhJljhy5O1EhPol9M79dSVFbGnod.jpeg', $text, 'accessToken');
  246. } catch (\Throwable $throwable) {
  247. Log::warning(var_export($throwable->getMessage(), 1));
  248. return false;
  249. }
  250. return true;
  251. }
  252. }