ArticleController_bak04-24.php 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Administrator
  5. * Date: 2019/4/17 0017
  6. * Time: 16:27
  7. */
  8. namespace App\Http\Controllers\Admin;
  9. use App\Exports\BasicExport;
  10. use App\Http\Controllers\Controller;
  11. use App\Http\Logics\Admin\ArticleLogic;
  12. use App\Http\Models\Article;
  13. use App\Http\Models\ArticleLinkNotice;
  14. use App\Http\Models\ArticleNote;
  15. use App\Http\Models\ArticleSiteKeep;
  16. use App\Http\Models\ArticleSocial;
  17. use App\Http\Models\Role;
  18. use App\Http\Models\Site;
  19. use App\Http\Models\SiteEnterprise;
  20. use App\Http\Models\SiteInfo;
  21. use App\Http\Models\User;
  22. use App\Http\Requests\Article\ArticleRequest;
  23. use App\Http\Requests\Article\ArticleTranslateRequest;
  24. use App\Http\Traits\HasSites;
  25. use Illuminate\Database\Eloquent\Builder;
  26. use Illuminate\Http\Request;
  27. use Illuminate\Support\Facades\Auth;
  28. use GuzzleHttp\Client;
  29. use GuzzleHttp\Pool;
  30. use GuzzleHttp\Psr7\Response;
  31. use App\Http\Requests\Article\ArticleSyncRequest;
  32. use Illuminate\Support\Facades\DB;
  33. use Illuminate\Support\Facades\Log;
  34. use Illuminate\Contracts\View\Factory;
  35. use Illuminate\Http\JsonResponse;
  36. use Illuminate\View\View;
  37. /**
  38. * 软文控制器
  39. * Class ArticleController
  40. * @package App\Http\Controllers\Admin
  41. */
  42. class ArticleController extends Controller
  43. {
  44. use HasSites;
  45. protected $logic;
  46. const STATUS = [
  47. 1 => '待翻译',
  48. 2 => '翻译中',
  49. 3 => '待审核',
  50. 4 => '审核不通过',
  51. 5 => '审核通过',
  52. ];
  53. public function __construct(ArticleLogic $logic)
  54. {
  55. $this->logic = $logic;
  56. }
  57. /**
  58. * 首页
  59. * @param Request $request
  60. * @return Factory|JsonResponse|View
  61. */
  62. public function index(Request $request)
  63. {
  64. if (!$request->ajax()) {
  65. return view('admin/article/index');
  66. }
  67. $userSite = $this->hasUserOneSite();
  68. $idaa = $userSite->id;
  69. $siteTitle = $request->input('site_title');
  70. $beApplicable = $request->input('be_applicable');
  71. if (isset($beApplicable)) {
  72. $condition[] = ['be_applicable', '=', $beApplicable];
  73. $condition[] = ['site_id', '=', $idaa];
  74. }
  75. $status = $request->input('status');
  76. if (!empty($status) && $status != 7) {
  77. $condition[] = ['status', '=', $status];
  78. }
  79. $beApplicable = $request->input('beApplicable');
  80. if (isset($beApplicable)) {
  81. $condition[] = ['be_applicable', '=', $beApplicable];
  82. $condition[] = ['site_id', '=', $idaa];
  83. }
  84. $builder = Article::query()->scopes(['user'])->whereHas('site', function (Builder $builder) use ($siteTitle, $status) {
  85. if ($siteTitle) {
  86. $builder->where('cn_title', 'like', '%' . $siteTitle . '%');
  87. }
  88. if ($status == 7) {
  89. $builder->where('status', $status);
  90. }
  91. })->with(['translator']);
  92. if ($siteId = $request->input('siteId')) {
  93. $condition[] = ['site_id', '=', $siteId];
  94. }
  95. if ($keyword = $request->input('keyword')) {
  96. $condition[] = ['title', 'like', '%' . $keyword . '%'];
  97. }
  98. $articles = $builder->where($condition ?? [])->orderByDesc('id')
  99. ->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  100. $items = $articles->items();
  101. array_walk($items, function ($item) {
  102. $item->status_title = $item->status_with_css;
  103. $item->site_title = $item->site->cn_title ?? '';
  104. $item->translator_name = $item->translator->username ?? '';
  105. $item->be_applicable = $item->check_status_with_css;
  106. if (!empty($item->publish_at)) {
  107. $item->publish_at = date('Y-m-d', strtotime($item->publish_at));
  108. }
  109. });
  110. return response()->json([
  111. 'rows' => $items,
  112. 'total' => $articles->total()
  113. ]);
  114. }
  115. /**
  116. * 项目管理详情中的软文列表
  117. * @param Request $request
  118. * @param $id
  119. * @return Factory|JsonResponse|View
  120. */
  121. public function siteArticles(Request $request, $id)
  122. {
  123. if (!$request->ajax()) {
  124. $start = date('Y-m-01 00:00:00');
  125. $end = date('Y-m-t 23:59:59');
  126. $siteInFo = SiteInfo::query()->where('site_id', $id)->first();
  127. $count = Article::query()->where('site_id', $id)->where('be_applicable', 1)->count();
  128. $site = Site::query()->where('id', $id)->first();
  129. $pendingUpgrade = Article::query()->where('site_id', $id)->where('be_applicable', 1)->whereNull('publish_at')->count() ?? 0;
  130. $thisMonthUpgrade = Article::query()->where('site_id', $id)->whereBetween('publish_at', [$start, $end])->count() ?? 0;
  131. return view('admin/article/site_article', [
  132. 'siteId' => $id,
  133. 'siteInFo' => $siteInFo,
  134. 'count' => $count,
  135. 'pendingUpgrade' => $pendingUpgrade,
  136. 'thisMonthUpgrade' => $thisMonthUpgrade,
  137. 'site' => $site,
  138. ]);
  139. }
  140. $builder = Article::query()->with(['site', 'translator']);
  141. $sortName = $request->input('sortName');
  142. $sortOrder = $request->input('sortOrder');
  143. if (!empty($sortName) && !empty($sortOrder)) {
  144. $builder->orderBy($sortName, $sortOrder);
  145. }
  146. $condition[] = ['site_id', '=', $id];
  147. $condition[] = ['be_applicable', '=', 1];
  148. if ($status = $request->input('status')) {
  149. $condition[] = ['status', '=', $status];
  150. }
  151. if ($keyword = $request->input('keyword')) {
  152. $condition[] = ['title', 'like', '%' . $keyword . '%'];
  153. }
  154. $articles = $builder->where($condition ?? [])->orderByDesc('id')
  155. ->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  156. $items = $articles->items();
  157. array_walk($items, function ($item) {
  158. $item->status_title = $item->status_with_css;
  159. $item->site_title = $item->site->cn_title ?? '';
  160. $item->translator_name = $item->translator->username ?? '';
  161. });
  162. return response()->json([
  163. 'rows' => $items,
  164. 'total' => $articles->total()
  165. ]);
  166. }
  167. /**
  168. * 保存软文每月更新数
  169. * @param Request $request
  170. * @return JsonResponse
  171. */
  172. public function articlesUpdatesPerMonth(Request $request)
  173. {
  174. $siteId = $request->input('siteId');
  175. $articlesUpdatesPerMonth = $request->input('articlesUpdatesPerMonth') ?? 4;
  176. $renewalQuantity = $request->input('article_renewal_quantity') ?? 0;
  177. Site::query()->where('id', $siteId)->update(['articles_updates_per_month' => $articlesUpdatesPerMonth, 'article_renewal_quantity' => $renewalQuantity]);
  178. return response()->json(['message' => '操作成功']);
  179. }
  180. /**
  181. * 文章详情
  182. * @param Request $request
  183. * @param $id
  184. * @return Factory|View
  185. */
  186. public function detail(Request $request, $id)
  187. {
  188. $article = Article::query()->find($id);
  189. if ($article) {
  190. $site = Site::query()->select(['id', 'cn_title', 'en_title', 'domain'])->where(['id' => $article->site_id])->first();
  191. $siteEnterprise = SiteEnterprise::query()->select(['brand'])->where(['site_id' => $article->site_id])->first();
  192. }
  193. return view('admin/article/detail', [
  194. 'data' => $article,
  195. 'referer' => $request->input('referer'),
  196. 'site' => $site ?? null,
  197. 'siteEnterprise' => $siteEnterprise ?? null
  198. ]);
  199. }
  200. /**
  201. * 文章添加保存
  202. * @param ArticleRequest $request
  203. * @param $id
  204. * @return Factory|JsonResponse|View
  205. */
  206. public function save(ArticleRequest $request, $id)
  207. {
  208. if (!$request->ajax()) {
  209. if ($id > 0) {
  210. $article = Article::query()->where(['id' => $id])->first();
  211. } else { //添加 [选中默认的站点]
  212. if ($siteId = $request->input('siteId')) {
  213. $siteKeep = (object)['site_id' => $siteId];
  214. } else {
  215. $siteKeep = ArticleSiteKeep::query()->where(['user_id' => Auth::id()])->first();
  216. }
  217. }
  218. $insider = User::query()->select(['id', 'username', 'nickname'])->find(Article::INSIDER);
  219. $translators = Role::getUsers(Role::TYPE_ARTICLE_PART);
  220. if ($insider) {
  221. $translators->prepend($insider);
  222. }
  223. return view('admin/article/save', [
  224. 'data' => $article ?? null,
  225. 'sites' => $this->userSites(),
  226. 'translators' => $translators,
  227. 'siteKeep' => $siteKeep ?? null
  228. ]);
  229. }
  230. $validated = $request->validated();
  231. if ($validated['translator_id'] != Article::INSIDER && empty($validated['content'])) {
  232. return response()->json(['message' => '正文不能为空'], 400);
  233. }
  234. if (!empty($validated['translate_content'])) {
  235. $validated['translate_content'] = del_domain_from_src($validated['translate_content']);
  236. }
  237. if (empty($validated['thumb'])) {
  238. $siteInfo = SiteInfo::query()->select(['is_upload_thumb'])->where(['site_id' => $validated['site_id']])->first();
  239. if (!empty($siteInfo->is_upload_thumb)) {
  240. return response()->json(['message' => '缩略图站点设置必填 不能为空'], 400);
  241. }
  242. }
  243. return $this->logic->save($validated, $id);
  244. }
  245. /**
  246. * 文章同步
  247. * @param ArticleSyncRequest $request
  248. * @param $id
  249. * @return Factory|JsonResponse|View
  250. */
  251. public function sync(ArticleSyncRequest $request, $id)
  252. {
  253. $site = Site::query()->where(['id' => $id])->first();
  254. if (!$site) return response()->json(['message' => '站点信息不存在'], 400);
  255. if (!$request->ajax()) {
  256. $taskUrls = [sprintf('%scontent/getOptsByJson', $site->api_url), sprintf('%scontent/getTplsByJson', $site->api_url)];
  257. $client = new Client([
  258. 'verify' => false,
  259. 'timeout' => 100,
  260. ]); //并发请求链接地址
  261. $requests = function () use ($client, $taskUrls) {
  262. foreach ($taskUrls as $item) {
  263. if (empty($item))
  264. continue;
  265. yield new \GuzzleHttp\Psr7\Request('GET', $item);
  266. }
  267. };
  268. $rejected = false; // 默认没有被拒绝
  269. $result = [];
  270. $pool = new Pool($client, $requests(), [
  271. 'concurrency' => 2, //同时并发抓取几个
  272. 'fulfilled' => function (Response $response, $index) use (&$result) {
  273. $temp = $response->getBody()->getContents();
  274. $temp = trim($temp, "\xEF\xBB\xBF");
  275. $result[$index] = json_decode($temp, true);
  276. },
  277. 'rejected' => function (\Throwable $throwable, $index) use (&$rejected, &$site) {
  278. Log::error(var_export($throwable->getMessage(), 1));
  279. // Log::error(sprintf('%scontent/getOptsByJson', $site->api_url));
  280. // Log::error(sprintf('%scontent/getTplsByJson', $site->api_url));
  281. $rejected = true; //接口错误
  282. },
  283. ]);
  284. $promise = $pool->promise();
  285. $promise->wait();
  286. if ($rejected) { //如果接口错误提示
  287. return view('admin/errors/tips', [
  288. 'tips' => 'api接口异常(请检查项目概况-基本信息-api地址是否填写正确',
  289. 'closeBtn' => 1
  290. ]);
  291. }
  292. return view('admin/article/sync', [
  293. // 'remoteContent' => array_values($content),
  294. 'remoteContent' => array_values($result[0]),
  295. // 'removeTemplate' => $template,
  296. 'removeTemplate' => $result[1],
  297. 'articles' => Article::query()->whereIn('id', $request->input('articleIds'))->select(['id', 'title'])->get(),
  298. 'siteId' => $id
  299. ]);
  300. }
  301. $server = $site->server;
  302. if (empty($server)) return response()->json(['message' => '站点服务器信息不存在'], 400);
  303. return $this->logic->sync($request->validated(), $site, $server);
  304. }
  305. /**
  306. * 文章认领
  307. * @param $id
  308. * @return JsonResponse
  309. */
  310. public function claim($id)
  311. {
  312. $article = Article::query()->where(['id' => $id])->first();
  313. if (!$article) {
  314. return response()->json(['message' => '文章信息不存在'], 400);
  315. }
  316. if ($article->translator_id) {
  317. return response()->json(['message' => '此文章已被认领'], 400);
  318. }
  319. if ($article->status != 1) {
  320. return response()->json(['message' => '此文章不属于待翻译状态'], 400);
  321. }
  322. if (Auth::id() != Article::IS_EXPIRED) {
  323. if (Article::query()->where(['translator_id' => Auth::id()])
  324. ->whereIn('status', [Article::STATUS_TRANSLATING, Article::STATUS_FAILED])->count() >= 10) {
  325. return response()->json(['message' => '一个用户最多只有10个翻译文章'], 400);
  326. }
  327. }
  328. $article->status = Article::STATUS_TRANSLATING;
  329. $article->translator_id = Auth::id();
  330. $article->expired_at = date('Y-m-d H:i:s', strtotime('+1 day'));
  331. $article->save();
  332. return response()->json(['message' => '操作成功']);
  333. }
  334. /**
  335. * 我要翻译
  336. * @param ArticleTranslateRequest $request
  337. * @param $id
  338. * @return Factory|JsonResponse|View
  339. */
  340. public function translate(ArticleTranslateRequest $request, $id)
  341. {
  342. if (!$request->ajax()) {
  343. $article = Article::query()->where(['id' => $id])->first();
  344. return view('admin/article/hall_translate', [
  345. 'data' => $article,
  346. 'site' => Site::query()->select(['cn_title', 'en_title', 'domain'])->find($article->site_id),
  347. 'siteEnterprise' => SiteEnterprise::query()->select(['brand'])->find($article->site_id)
  348. ]);
  349. }
  350. $article = Article::query()->where(['id' => $id])->first();
  351. if (!$article) return response()->json(['message' => '文章信息不存在'], 400);
  352. $validated = $request->validated();
  353. if ($request->input('admin')) {
  354. if (empty($validated['thumb'])) {
  355. $siteInfo = SiteInfo::query()->select(['is_upload_thumb'])->where(['site_id' => $article->site_id])->first();
  356. if (!empty($siteInfo->is_upload_thumb)) {
  357. return response()->json(['message' => '缩略图站点设置必填 不能为空'], 400);
  358. }
  359. }
  360. if (!$validated['translate_title2']) {
  361. $str = preg_replace("/[^a-zA-Z0-9\s]/", "", $validated['translate_title']);
  362. $validated['translate_title2'] = strtolower(preg_replace("/\s+/", "-", $str));
  363. }
  364. } else {
  365. unset($validated['translate_title2'], $validated['thumb']);
  366. }
  367. $validated['translate_words'] = word_count($validated['translate_title']) + word_count($validated['translate_content']);
  368. $validated['translate_content'] = del_domain_from_src($validated['translate_content']);
  369. $validated['translate_submit_at'] = date('Y-m-d H:i:s');
  370. $article->update($validated);
  371. return response()->json(['message' => '操作成功']);
  372. }
  373. /**
  374. * 批量审核通过
  375. * @param Request $request
  376. * @return JsonResponse
  377. */
  378. public function batchPass(Request $request)
  379. {
  380. $user = Auth::user();
  381. $passScore = $request->input('passScore') ?? 0;
  382. $ids = $request->input('ids');
  383. Article::query()->whereIn('id', $ids)->where(['status' => Article::STATUS_AUDITING])->update([
  384. 'status' => Article::STATUS_PASSED,
  385. 'audit_at' => date('Y-m-d H:i:s'),
  386. 'auditor_id' => $user->id,
  387. 'auditor_name' => $user->nickname,
  388. 'pass_score' => $passScore
  389. ]);
  390. return response()->json(['message' => '操作成功']);
  391. }
  392. /**
  393. * 批量审核不通过
  394. * @param Request $request
  395. * @return JsonResponse
  396. */
  397. public function batchNoPass(Request $request)
  398. {
  399. $ids = $request->input('ids');
  400. Article::query()->whereIn('id', $ids)->where(['status' => Article::STATUS_AUDITING])->update([
  401. 'status' => Article::STATUS_FAILED,
  402. 'fail_reason' => $request->input('fail_reason')
  403. ]);
  404. return response()->json(['message' => '操作成功']);
  405. }
  406. /**
  407. * 释放任务
  408. * @param $id
  409. * @return JsonResponse
  410. */
  411. public function release($id)
  412. {
  413. $article = Article::query()->where(['id' => $id])->first();
  414. if (!$article) return response()->json(['message' => '文章信息不存在'], 400);
  415. if (!in_array($article->status, [Article::STATUS_TRANSLATING, Article::STATUS_FAILED])) {
  416. return response()->json(['message' => '此文章不属于翻译中或审核失败状态'], 400);
  417. }
  418. $article->update([
  419. 'status' => 1,// 待翻译
  420. 'translator_id' => 0,
  421. 'translate_title' => $article->init_tran_title,
  422. 'translate_words' => word_count($article->init_tran_content) + word_count($article->init_tran_title),
  423. 'translate_content' => $article->init_tran_content,
  424. 'expired_at' => null
  425. ]);
  426. return response()->json(['message' => '操作成功']);
  427. }
  428. //软文注意事项
  429. public function notice(Request $request)
  430. {
  431. if (!$request->ajax()) {
  432. $data = ArticleLinkNotice::query()->where(['type' => 'article'])->first();
  433. return view('admin/article/notice', [
  434. 'data' => $data ?? ''
  435. ]);
  436. }
  437. ArticleLinkNotice::query()->where(['type' => 'article'])->update([
  438. 'content' => $request->input('content')
  439. ]);
  440. return response()->json(['message' => '操作成功']);
  441. }
  442. /**
  443. * 任务大厅
  444. * @param Request $request
  445. * @return Factory|JsonResponse|View
  446. */
  447. public function hall(Request $request)
  448. {
  449. if (!$request->ajax()) {
  450. Article::query()->whereIn('status', [Article::STATUS_TRANSLATING])
  451. ->where([['expired_at', '<', date('Y-m-d H:i:s')]])->update([
  452. 'translator_id' => 0,
  453. 'expired_at' => null,
  454. 'status' => Article::STATUS_PENDING
  455. ]); //检测是否有翻译中且已过期的 将其释放任务
  456. // $haveNotSettle = Article::query()->where(['translator_id' => Auth::id()])->where('status', 5)->get();
  457. // $settleHistory = Article::query()->where(['translator_id' => Auth::id()])->where('status', 6)->get()->groupBy('audit_at');
  458. return view('admin/article/hall', [
  459. 'notice' => ArticleLinkNotice::query()->where(['type' => 'article'])->first(),
  460. // 'settleHistory' => $settleHistory,
  461. // 'haveNotSettle'=>$haveNotSettle
  462. ]);
  463. }
  464. $builder = Article::query()->where('be_applicable', 1)->with(['site', 'translator']);
  465. if ($status = $request->input('status') ?? 1) {
  466. $condition[] = ['status', '=', $status];
  467. }
  468. if ($status != Article::STATUS_PENDING) { //任务大厅可以看到未分配的文章
  469. $condition[] = ['translator_id', '=', Auth::id()];
  470. }
  471. if ($keyword = $request->input('keyword')) {
  472. $builder->where(function (Builder $query) use ($keyword) {
  473. $query->where('title', 'like', '%' . $keyword . '%');
  474. $query->orWhere('id', 'like', $keyword);
  475. });
  476. }
  477. $articles = $builder->where($condition ?? [])->orderByDesc('id')
  478. ->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  479. $items = $articles->items();
  480. array_walk($items, function ($item) {
  481. $item->status_title = $item->status_with_css;
  482. $item->site_title = $item->site->cn_title ?? '';
  483. $item->translator_name = $item->translator->username ?? '';
  484. });
  485. return response()->json([
  486. 'rows' => $items,
  487. 'total' => $articles->total()
  488. ]);
  489. }
  490. //任务大厅详情
  491. public function hallDetail($articleId)
  492. {
  493. $article = Article::query()->where(['id' => $articleId])->first();
  494. if (!$article) {
  495. return abort(404, '数据不存在');
  496. }
  497. return view('admin/article/hall_detail', [
  498. 'data' => $article,
  499. 'site' => Site::query()->select(['cn_title', 'en_title', 'domain'])->find($article->site_id),
  500. 'siteEnterprise' => SiteEnterprise::query()->select(['brand'])->find($article->site_id),
  501. 'siteInfo' => SiteInfo::query()->selectRaw('article_pt_remark')->where(['site_id' => $article->site_id])->first()
  502. ]);
  503. }
  504. //批量删除
  505. public function batchDelete(Request $request)
  506. {
  507. $ids = $request->input('ids');
  508. Article::destroy($ids);
  509. return response()->json(['message' => '操作成功']);
  510. }
  511. //批量更新发布时间
  512. public function batchPublished(Request $request)
  513. {
  514. $ids = $request->input('ids');
  515. Article::query()->whereIn('id', $ids)->update([
  516. 'publish_at' => date('Y-m-d H:i:s')
  517. ]);
  518. return response()->json(['message' => '操作成功']);
  519. }
  520. //批量更新状态
  521. public function batchUpdate(Request $request)
  522. {
  523. $ids = $request->input('ids');
  524. Article::query()->whereIn('id', $ids)->update([
  525. 'status' => 1,
  526. ]);
  527. return response()->json(['message' => '操作成功']);
  528. }
  529. //推送消息至app
  530. public function pushMessage(Request $request)
  531. {
  532. $siteId = $request->input('siteId');
  533. DB::table('articles_notice')->insert(['site_id' => $siteId]);
  534. return response()->json(['message' => '操作成功']);
  535. }
  536. /**
  537. * 任务结算列表
  538. * @param Request $request
  539. * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse|\Illuminate\View\View
  540. */
  541. public function calc(Request $request)
  542. {
  543. if (!$request->ajax()) {
  544. return view('admin/article/calc');
  545. }
  546. $builder = Article::query()->where(['status' => Article::STATUS_PASSED]);
  547. if ($startAt = $request->input('start_at')) {
  548. $builder->whereDate('created_at', '>', $startAt);
  549. }
  550. if ($endAt = $request->input('end_at')) {
  551. $builder->whereDate('created_at', '<', $endAt);
  552. }
  553. // 注意 group_concat长度限制
  554. $data = $builder->whereHas('translator', function (Builder $query) use ($request) {
  555. if ($username = $request->input('username')) {
  556. $query->where('username', 'like', '%' . $username . '%');
  557. }
  558. })->selectRaw('SUM(translate_words) AS totalWords,translator_id,GROUP_CONCAT(id) as article_ids,GROUP_CONCAT(id) as article_ids')
  559. ->groupBy('translator_id')->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  560. $items = $data->items();
  561. array_walk($items, function ($item) {
  562. $item->article_ids = explode(',', $item->article_ids);
  563. $scoreList = Article::query()->selectRaw('id,pass_score')->whereIn('id', $item->article_ids)
  564. ->whereNotNull('pass_score')->get();
  565. $totalScore = 0;
  566. foreach ($scoreList as $score) {
  567. $totalScore += $score->pass_score;
  568. }
  569. $item->avgScore = $scoreList->count() < 1 ? 0 : ($totalScore / $scoreList->count());
  570. $item->amount = $this->logic->amountRule($item->totalWords);
  571. $item->username = $item->translator->username ?? '';
  572. unset($item->translator);
  573. });
  574. return response()->json([
  575. 'rows' => $items,
  576. 'total' => $data->total()
  577. ]);
  578. }
  579. // 结算历史
  580. public function calcHistory(Request $request)
  581. {
  582. if (!$request->ajax()) {
  583. return view('/admin/article/calc_history');
  584. }
  585. $condition = [['status', '=', 6]];
  586. if ($start_at = $request->input('start_at')) {
  587. $condition[] = ['audit_at', '>=', $start_at];
  588. }
  589. if ($end_at = $request->input('end_at')) {
  590. $condition[] = ['audit_at', '<=', $end_at];
  591. }
  592. if ($keyword = $request->input('keyword')) {
  593. $condition[] = ['title', 'like', '%' . $keyword . '%'];
  594. }
  595. $data = Article::query()->with(['translator'])->where($condition)->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  596. $items = $data->items();
  597. $totalAmount = 0;
  598. array_walk($items, function ($item) use (&$totalAmount) {
  599. $item->amount = $item->settle_amount;
  600. $item->username = $item->translator->username ?? '';
  601. $totalAmount = bcadd($totalAmount, $item->settle_amount, 2);
  602. });
  603. return response()->json([
  604. 'rows' => $items,
  605. 'total' => $data->total(),
  606. 'totalAmount' => $totalAmount
  607. ]);
  608. }
  609. /***
  610. * 有道翻译自动翻译
  611. * @param Request $request
  612. * @return \Illuminate\Http\JsonResponse
  613. */
  614. public function autoTranslate(Request $request)
  615. {
  616. $ids = $request->input('ids');
  617. $articles = Article::query()->where(['status' => 1])
  618. ->whereIn('id', $ids)->get();
  619. return $this->logic->autoTranslate($articles);
  620. }
  621. /**
  622. * 文章结算
  623. * @param Request $request
  624. * @return \Illuminate\Http\JsonResponse
  625. */
  626. public function settle(Request $request)
  627. {
  628. $records = Article::query()->whereIn('id', $request->input('ids'))->get();
  629. $settleAt = date('Y-m-d H:i:s');
  630. foreach ($records as $record) {
  631. $record->update([
  632. 'status' => Article::STATUS_CALC, //已结算
  633. 'settle_amount' => $this->logic->amountRule($record->translate_words),
  634. 'settle_at' => $settleAt
  635. ]);
  636. }
  637. return response()->json(['message' => '操作成功']);
  638. }
  639. //好像没用了
  640. public function syncSocial(Request $request)
  641. {
  642. $article = Article::query()->whereIn('id', $request->input('ids'))->first();
  643. $date = date('Y-m-d H:i:s');
  644. $update = ['article_id' => $article->id];
  645. if ($this->logic->linkedIn($article->translate_content)) {
  646. $update['linked_sync_at'] = $date;
  647. }
  648. ArticleSocial::query()->updateOrCreate(['article_id' => $article->id], $update);
  649. return response()->json(['message' => '操作成功']);
  650. }
  651. //项目管理中下的软文管理 保存
  652. public function saveInfo(Request $request)
  653. {
  654. $article_custom_audit = $request->input('article_custom_audit') == 'true' ? 1 : 0;
  655. $is_upload_thumb = $request->input('is_upload_thumb') == 'true' ? 1 : 0;
  656. $article_remark = $request->input('article_remark');
  657. $article_pt_remark = $request->input('article_pt_remark');
  658. $article_need_num = $request->input('article_need_num');
  659. $outside_update_platform = $request->input('outside_update_platform');
  660. $siteId = $request->input('siteId');
  661. if (!$siteId) {
  662. return response()->json(['message' => '参数错误'], 400);
  663. }
  664. $data = [
  665. 'site_id' => $siteId,
  666. 'is_upload_thumb' => $is_upload_thumb,
  667. 'article_custom_audit' => $article_custom_audit,
  668. 'article_remark' => $article_remark,
  669. 'outside_update_platform' => $outside_update_platform,
  670. 'article_pt_remark' => $article_pt_remark,
  671. 'article_need_num' => $article_need_num,
  672. ];
  673. $info = SiteInfo::query()->where(['site_id' => $siteId])->first();
  674. if ($info) {
  675. $info->update($data);
  676. } else {
  677. SiteInfo::query()->create($data);
  678. }
  679. return response()->json(['message' => '操作成功']);
  680. }
  681. //软文任务审核
  682. public function auditWord(Request $request)
  683. {
  684. if (!$request->ajax()) {
  685. return view('admin/article/audit_word');
  686. }
  687. $builder = Article::query()->where(['status' => Article::STATUS_PASSED]);
  688. if ($startAt = $request->input('start_at')) {
  689. $builder->whereDate('created_at', '>', $startAt);
  690. }
  691. if ($endAt = $request->input('end_at')) {
  692. $builder->whereDate('created_at', '<', $endAt);
  693. }
  694. // 注意 group_concat长度限制
  695. $data = $builder->whereHas('auditor', function (Builder $query) use ($request) {
  696. if ($username = $request->input('username')) {
  697. $query->where('username', 'like', '%' . $username . '%');
  698. }
  699. })->selectRaw('SUM(translate_words) AS totalWords')
  700. ->groupBy('auditor_id')->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  701. $items = $data->items();
  702. return response()->json([
  703. 'rows' => $items,
  704. 'total' => $data->total()
  705. ]);
  706. }
  707. //近三月趋势图
  708. public function trend()
  709. {
  710. $auditData = $queryData = $xAxis = [];
  711. for ($i = 1; $i <= 3; $i++) {
  712. $xAxis[] = date('Y年m月', strtotime('first day of - ' . $i . 'month'));
  713. $beginAt = date('Y-m-01 00:00:00', strtotime('first day of - ' . $i . 'month'));
  714. $endAt = date('Y-m-t 23:59:59', strtotime('first day of - ' . $i . 'month'));
  715. $queryData[] = Article::query()->where([['created_at', '>=', $beginAt], ['created_at', '<=', $endAt]])->count();
  716. $auditData[] = Article::query()->where([['audit_at', '>=', $beginAt], ['audit_at', '<=', $endAt]])->count();
  717. }
  718. $data = [
  719. 'xAxis' => $xAxis,
  720. 'queryData' => $queryData,
  721. 'auditData' => $auditData,
  722. ];
  723. return view('admin.article.trend', [
  724. 'line' => $data
  725. ]);
  726. }
  727. //特殊软文 有撰写人员的
  728. public function spec(Request $request)
  729. {
  730. if (!$request->ajax()) {
  731. return view('admin/article/spec');
  732. }
  733. $records = Site::query()->withCount('articles')->with(['users'])->where(['spec_article' => 1])
  734. ->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  735. $result = [];
  736. foreach ($records as $record) {
  737. $result[] = [
  738. 'cn_title' => $record->cn_title,
  739. 'domain' => $record->domain,
  740. 'status_title' => Site::STATUS_MAP[$record->status] ?? '',
  741. 'article_goal' => $record->article_goal,
  742. 'articles_count' => $record->articles_count,
  743. 'editor' => implode(',', $record->users->where('role_id', Role::TYPE_PROPAGANDA)->pluck('nickname')->toArray())
  744. ];
  745. }
  746. return response()->json([
  747. 'rows' => $result,
  748. 'total' => $records->total()
  749. ]);
  750. }
  751. //软文备注
  752. public function siteArticlesNote(Request $request, $siteId)
  753. {
  754. if (!$request->ajax()) {
  755. $note = ArticleNote::query()->where(['site_id' => $siteId])->first();
  756. return view('admin.article.note', [
  757. 'siteId' => $siteId,
  758. 'data' => $note,
  759. ]);
  760. }
  761. $content = $request->input('content');
  762. $note = ArticleNote::query()->where(['site_id' => $siteId])->first();
  763. if ($note) {
  764. $note->update(['content' => $content]);
  765. } else {
  766. ArticleNote::query()->create([
  767. 'site_id' => $siteId,
  768. 'content' => $content,
  769. ]);
  770. }
  771. return response()->json(['message' => '操作成功']);
  772. }
  773. //软文查找
  774. public function siteArticlesFind(Request $request, $id)
  775. {
  776. if (!$request->ajax()) {
  777. $siteInFo = SiteInfo::query()->where('site_id', $id)->first();
  778. $mobile = DB::table('site_mobile')->where('site_id', $id)->value('mobile') ?? '';
  779. return view('admin/article/find', [
  780. 'siteId' => $id,
  781. 'siteInFo' => $siteInFo,
  782. 'mobile' => $mobile
  783. ]);
  784. }
  785. $builder = Article::query()->with(['translator']);
  786. $condition[] = ['site_id', '=', $id];
  787. $beApplicable = $request->input('beApplicable');
  788. if (isset($beApplicable)) {
  789. $condition[] = ['be_applicable', '=', $beApplicable];
  790. }
  791. $status = $request->input('status');
  792. if (isset($status)) {
  793. $condition[] = ['status', '=', $status];
  794. }
  795. $title = $request->input('title');
  796. if (!empty($title)) {
  797. $condition[] = ['title', 'like', $title . '%'];
  798. }
  799. $userList = User::query()->where('status', 1)->pluck('nickname', 'id') ?? [];
  800. $articles = $builder->where($condition ?? [])->orderByDesc('id')
  801. ->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  802. $items = $articles->items();
  803. array_walk($items, function ($item) use ($userList) {
  804. $item->translator_name = $item->translator->username ?? '';
  805. $item->check = $item->check_status_with_css;
  806. $item->status_title = $item->status_with_css;
  807. $item->reviewer = $userList[$item->reviewer_id] ?? '';
  808. });
  809. return response()->json([
  810. 'rows' => $items,
  811. 'total' => $articles->total()
  812. ]);
  813. }
  814. //批量适用
  815. public function batchBeApplicable(Request $request, $siteId)
  816. {
  817. $ids = $request->input('ids');
  818. $type = $request->input('type');
  819. Article::query()->where('site_id', $siteId)
  820. ->whereIn('id', $ids)
  821. ->update(['be_applicable' => $type, 'reviewer_id' => Auth::id()]);
  822. return response()->json(['message' => '操作成功']);
  823. }
  824. //待定
  825. public function toBeDetermined(Request $request, $siteId)
  826. {
  827. $id = $request->input('id');
  828. $type = $request->input('type');
  829. Article::query()->where('site_id', $siteId)
  830. ->where('id', $id)
  831. ->update(['be_applicable' => $type, 'reviewer_id' => Auth::id()]);
  832. return response()->json(['message' => '操作成功']);
  833. }
  834. public function notApplicableWherefore(Request $request, $siteId)
  835. {
  836. $id = $request->input('id');
  837. $notBeApplicableInfo = $request->input('notBeApplicableInfo');
  838. Article::query()->where('site_id', $siteId)
  839. ->where('id', $id)
  840. ->update(['not_be_applicable_info' => $notBeApplicableInfo, 'reviewer_id' => Auth::id()]);
  841. return response()->json(['message' => '操作成功']);
  842. }
  843. //任务大厅结算历史
  844. public function hallCalcHistory(Request $request)
  845. {
  846. if (!$request->ajax()) {
  847. return view('admin/article/hall_calc_history');
  848. }
  849. $condition = [['translator_id', '=', Auth::id()], ['status', '=', 6]];
  850. if ($start_at = $request->input('start_at')) {
  851. $condition[] = ['audit_at', '>=', $start_at];
  852. }
  853. if ($end_at = $request->input('end_at')) {
  854. $condition[] = ['audit_at', '<=', $end_at];
  855. }
  856. if ($keyword = $request->input('keyword')) {
  857. $condition[] = ['title', 'like', '%' . $keyword . '%'];
  858. }
  859. $data = Article::query()->with(['translator'])->where($condition)->paginate($request->input('pageSize') ?? TABLE_PAGE_SIZE);
  860. $items = $data->items();
  861. $totalAmount = 0;
  862. array_walk($items, function ($item) use (&$totalAmount) {
  863. $item->amount = $item->settle_amount;
  864. $item->username = $item->translator->username ?? '';
  865. });
  866. return response()->json([
  867. 'rows' => $items,
  868. 'total' => $data->total(),
  869. 'totalAmount' => $totalAmount
  870. ]);
  871. }
  872. //项目详情-软文概述
  873. public function siteArticlesOverview(Request $request, $id)
  874. {
  875. if (!$request->ajax()) {
  876. list($siteInfo, $data, $result, $scope) = $this->softArticleSummaryStatistics($id);
  877. return view('admin/article/articles_overview', [
  878. 'data' => $data,
  879. 'siteId' => $siteInfo->id,
  880. 'result' => $result,
  881. 'scope' => $scope
  882. ]);
  883. }
  884. }
  885. //软文概况
  886. public function articleOverview(Request $request)
  887. {
  888. if (!$request->ajax()) {
  889. $userSite = $this->hasUserOneSite();
  890. if (!$userSite) {
  891. return view('admin/errors/tips', [
  892. 'tips' => '站点信息不存在'
  893. ]);
  894. }
  895. list($siteInfo, $data, $result, $scope) = $this->softArticleSummaryStatistics($userSite->id);
  896. return view('admin/article/article_overview', [
  897. 'data' => $data,
  898. 'siteId' => $siteInfo->id,
  899. 'result' => $result,
  900. 'scope' => $scope
  901. ]);
  902. }
  903. }
  904. public function softArticleSummaryStatistics($siteId)
  905. {
  906. $siteIno = Site::query()->where('id', $siteId)->first();
  907. $published = Article::query()->where('site_id', $siteIno->id)->whereNotNull('publish_at')->count() ?? 0;
  908. $unpublished = Article::query()->where('site_id', $siteIno->id)->whereNull('publish_at')->count() ?? 0;
  909. $thisMonth = Article::query()
  910. ->whereBetween('publish_at', [date('Y-m-01 00:00:00'), date('Y-m-t 23:59:59')])
  911. ->where('site_id', $siteIno->id)->whereNotNull('publish_at')->count() ?? 0;
  912. $pending = Article::query()->where('site_id', $siteIno->id)
  913. ->where('be_applicable', 0)
  914. ->whereNull('publish_at')->count() ?? 0;
  915. $needToPublish = $siteIno->articles_updates_per_month ?? 0 - $published;
  916. if ($needToPublish < 0) {
  917. $needToPublish = 0;
  918. }
  919. $data = [
  920. 'xAxis' => $list = [
  921. '合同数', '实际需发布软文数', '软文扣除数', '已发布数', '未发数', '本月需发数', '本月已发数', '待审核数'
  922. ],
  923. 'yAxis' => $list1 = [
  924. $siteIno->article_goal, $siteIno->article_goal - $siteIno->article_difference, $siteIno->article_difference, $published, $unpublished, $needToPublish, $thisMonth, $pending
  925. ]
  926. ];
  927. $info = DB::table('site_articles_info')->where('site_id', $siteIno->id)->first();
  928. $authUser = auth()->user();
  929. if ($authUser->is_super || in_array($authUser->role_id, [Role::TYPE_MANAGE_LEADER])) {
  930. $scope = true;
  931. } else {
  932. $scope = false;
  933. }
  934. $result = [
  935. 'optimizeDataScopeList1' => json_decode($info->content_one ?? '', true) ?? [],
  936. 'optimizeDataScopeList2' => json_decode($info->content_two ?? '', true) ?? [],
  937. 'memo' => json_decode($info->memo ?? '', true) ?? [],
  938. ];
  939. return [$siteIno, $data, $result, $scope];
  940. }
  941. //保存软文概述
  942. public function saveSoftArticleOverview(Request $request, $siteId)
  943. {
  944. $dataList1 = $request->input('dataList1') ?? [];
  945. $dataList2 = $request->input('dataList2') ?? [];
  946. $insertData['site_id'] = $siteId;
  947. if (!empty($dataList1)) {
  948. $insertData['content_one'] = \GuzzleHttp\json_encode($dataList1);
  949. }
  950. if (!empty($dataList2)) {
  951. $insertData['content_two'] = \GuzzleHttp\json_encode($dataList2);
  952. }
  953. DB::table('site_articles_info')->where('site_id', $siteId)->delete();
  954. DB::table('site_articles_info')->insert($insertData);
  955. return response()->json(['message' => '操作成功']);
  956. }
  957. //中文文章
  958. public function chineseArticle(Request $request)
  959. {
  960. $userSite = $this->hasUserOneSite();
  961. if (!$userSite) {
  962. return view('admin/errors/tips', [
  963. 'tips' => '站点信息不存在'
  964. ]);
  965. }
  966. $id = $userSite->id;
  967. $siteIno = Site::query()->with(['users'])->where('id', $id)->first();
  968. if (!$request->ajax()) {
  969. return view('admin/article/chinese_article', [
  970. 'siteId' => $siteIno->id,
  971. ]);
  972. }
  973. }
  974. //英文文章
  975. public function englishArticle(Request $request)
  976. {
  977. $userSite = $this->hasUserOneSite();
  978. if (!$userSite) {
  979. return view('admin/errors/tips', [
  980. 'tips' => '站点信息不存在'
  981. ]);
  982. }
  983. $id = $userSite->id;
  984. $siteIno = Site::query()->with(['users'])->where('id', $id)->first();
  985. if (!$request->ajax()) {
  986. return view('admin/article/english_article', [
  987. 'siteId' => $siteIno->id,
  988. ]);
  989. }
  990. }
  991. //导出
  992. public function export(Request $request, $siteId)
  993. {
  994. $article = Article::query();
  995. $ids = $request->input('ids');
  996. if ($ids) {
  997. $id = explode(',', $ids);
  998. $article->whereIn('id', $id);
  999. }
  1000. $list = $article->where('site_id', $siteId)
  1001. ->orderBy('id', 'desc')
  1002. ->select('id', 'status', 'publish_at', 'title', 'content', 'translate_title', 'translate_content', 'created_at', 'be_applicable')->get();
  1003. $dataList = [];
  1004. foreach ($list as $item) {
  1005. $beApplicable = '待定';
  1006. if ($item->be_applicable == 1) {
  1007. $beApplicable = '适用';
  1008. }
  1009. if ($item->be_applicable == 2) {
  1010. $beApplicable = '不适用';
  1011. }
  1012. $data = [
  1013. 'id' => $item->id,
  1014. 'be_applicable' => $beApplicable,
  1015. 'status' => self::STATUS[$item->status],
  1016. 'created_at' => $item->created_at,
  1017. 'publish_at' => $item->publish_at,
  1018. 'title' => $item->title,
  1019. 'content' => str_replace('&nbsp;', '', strip_tags($item->content)),
  1020. 'translate_title' => $item->translate_title,
  1021. 'translate_content' => str_replace('&nbsp;', '', strip_tags($item->translate_content)),
  1022. ];
  1023. $dataList[] = $data;
  1024. }
  1025. array_unshift($dataList, ['编号', '客户审核状态', '翻译状态', '创建时间', '发布时间', '中文标题', '中文内容', '英文标题', '英文内容']);
  1026. return (new BasicExport($dataList))->download(sprintf('软文列表%s.xls', date('YmdHis')));
  1027. }
  1028. public function pushMobileMessage(Request $request)
  1029. {
  1030. $mobile = $request->input('mobile');
  1031. $type = $request->input('type');
  1032. if (empty($mobile)) {
  1033. return response()->json(['message' => '请填写手机号'], 400);
  1034. }
  1035. if (!preg_match("/^1[3456789]\d{9}$/", $mobile)) {
  1036. return response()->json(['message' => '手机号输入有误'], 400);
  1037. }
  1038. $siteId = $request->input('siteId');
  1039. $info = DB::table('site_mobile')->where('site_id', $siteId)->first();
  1040. if (empty($info)) {
  1041. DB::table('site_mobile')->insert(['site_id' => $siteId, 'mobile' => $mobile]);
  1042. }
  1043. DB::table('site_mobile')->where('site_id', $siteId)->update(['mobile' => $mobile]);
  1044. //1软文审核 2月报推送
  1045. if ($type == 1) {
  1046. $tpl = 'SMS_217408472';
  1047. } else {
  1048. $tpl = 'SMS_217438615';
  1049. }
  1050. try {
  1051. $url = 'http://translate.api.yinqingli.net/openapi/Msg/Msg';
  1052. $data = [
  1053. 'mobile' => $mobile,
  1054. 'tpl' => $tpl,
  1055. ];
  1056. $client = new Client();
  1057. $response = $client->post($url, [
  1058. 'form_params' => $data,
  1059. ]);
  1060. $result = $response->getBody()->getContents();
  1061. return response()->json(['message' => '操作成功']);
  1062. } catch (\Throwable $exception) {
  1063. return response()->json(['message' => 'error'], 400);
  1064. }
  1065. }
  1066. }