ArticleController_bak.php 44 KB

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