ArticleService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2017~2021 LARAVEL研发中心
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://www.laravel.cn
  8. // +----------------------------------------------------------------------
  9. // | Author: laravel开发员 <laravel.qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace App\Services\Api;
  12. use App\Models\ArticleCateModel;
  13. use App\Models\ArticleModel;
  14. use App\Models\ExamAccessLogModel;
  15. use App\Services\BaseService;
  16. use App\Services\ConfigService;
  17. use App\Services\RedisService;
  18. use PhpOffice\PhpWord\IOFactory;
  19. /**
  20. * 文章-服务类
  21. * @author laravel开发员
  22. * @since 2020/11/11
  23. * @package App\Services\Api
  24. */
  25. class ArticleService extends BaseService
  26. {
  27. // 静态对象
  28. protected static $instance = null;
  29. /**
  30. * 构造函数
  31. * @author laravel开发员
  32. * @since 2020/11/11
  33. */
  34. public function __construct()
  35. {
  36. $this->model = new ArticleModel();
  37. }
  38. /**
  39. * 静态入口
  40. */
  41. public static function make()
  42. {
  43. if (!self::$instance) {
  44. self::$instance = new static();
  45. }
  46. return self::$instance;
  47. }
  48. /**
  49. * 信息
  50. * @param int $num
  51. * @return array|mixed
  52. */
  53. public function getInfoByType($type)
  54. {
  55. $cacheKey = "caches:article:info_{$type}";
  56. $datas = RedisService::get($cacheKey);
  57. if($datas){
  58. return $datas;
  59. }
  60. $datas = $this->model->where(['type'=>$type,'status'=>1,'mark'=>1])
  61. ->select(['id','title','type','content','status'])
  62. ->orderBy('create_time','desc')
  63. ->first();
  64. $datas = $datas? $datas->toArray() : [];
  65. if($datas){
  66. $datas['content'] = preg_replace("/\n+/",'<br/>',$datas['content']);
  67. $datas['content'] = get_format_content($datas['content']);
  68. RedisService::set($cacheKey, $datas, 86400);
  69. }
  70. return $datas;
  71. }
  72. /**
  73. * @param $params
  74. * @param int $pageSize
  75. * @return array
  76. */
  77. public function getDataList($params, $pageSize = 15)
  78. {
  79. $page = isset($params['page'])? $params['page'] : 1;
  80. $cacheKey = "caches:articles:list_{$page}_{$pageSize}:".md5(json_encode($params));
  81. $datas = RedisService::get($cacheKey);
  82. if($datas){
  83. return $datas;
  84. }
  85. $query = $this->getQuery($params);
  86. $list = $query->select(['a.id','a.type','a.title','a.cover','a.view_num','a.show_type','a.create_time','a.description','a.status'])
  87. ->orderBy('a.create_time','desc')
  88. ->paginate($pageSize > 0 ? $pageSize : 9999999);
  89. $list = $list? $list->toArray() :[];
  90. if($list){
  91. foreach($list['data'] as &$item){
  92. $item['create_time'] = $item['create_time']? datetime($item['create_time'],'Y-m-d') : '';
  93. $item['cover'] = $item['cover']? get_image_url($item['cover']) : '';
  94. }
  95. }
  96. $rows = isset($list['data'])? $list['data'] : [];
  97. $datas = [
  98. 'pageSize'=> $pageSize,
  99. 'total'=> isset($list['total'])? $list['total'] : 0,
  100. 'list'=> $rows
  101. ];
  102. if($rows){
  103. RedisService::set($cacheKey, $datas, rand(3600, 7200));
  104. }
  105. return $datas;
  106. }
  107. /**
  108. * 查询
  109. * @param $params
  110. * @return mixed
  111. */
  112. public function getQuery($params)
  113. {
  114. $where = ['a.mark' => 1];
  115. $status = isset($params['status'])? $params['status'] : 0;
  116. $type = isset($params['type'])? $params['type'] : 0;
  117. $cateId = isset($params['cate_id'])? $params['cate_id'] : 0;
  118. if($status>0){
  119. $where['a.status'] = $status;
  120. }
  121. if($type>0){
  122. $where['a.type'] = $type;
  123. }
  124. if($cateId>=0){
  125. $where['a.cate_id'] = $cateId;
  126. }
  127. return $this->model->from('article as a')
  128. ->where($where)
  129. ->where(function ($query) use($params){
  130. $keyword = isset($params['keyword'])? $params['keyword'] : '';
  131. if($keyword){
  132. $query->where('a.title','like',"%{$keyword}%");
  133. }
  134. });
  135. }
  136. /**
  137. * 热门问答(都在问列表)
  138. * @param $userId
  139. * @param false $refresh
  140. * @return array|mixed
  141. */
  142. public function getHotList($userId,$refresh=false)
  143. {
  144. $cacheKey = "caches:articles:hotList_{$userId}";
  145. $datas = RedisService::get($cacheKey);
  146. if($datas && !$refresh){
  147. return $datas;
  148. }
  149. $model = $this->model->where(['type'=>9,'status'=>1,'mark'=>1]);
  150. $model1 = clone $model;
  151. if($model1->where('view_num','>', 0)->count('id')>5){
  152. $model = $model1;
  153. }
  154. $num = ConfigService::make()->getConfigByCode('custom_hot_num', 3);
  155. $num = $num>=2 && $num<= 20? $num : 3;
  156. $datas = $model->select(['id','cover','type','title','description','status'])
  157. ->orderByRaw('RAND()')
  158. ->take($num)
  159. ->get();
  160. $datas = $datas? $datas->toArray() : [];
  161. if($datas){
  162. RedisService::set($cacheKey, $datas, rand(300, 600));
  163. }
  164. return $datas;
  165. }
  166. /**
  167. * 推荐问答
  168. * @param $userId
  169. * @param false $refresh
  170. * @return array|mixed
  171. */
  172. public function getRecommendList($refresh=false)
  173. {
  174. $cacheKey = "caches:articles:recList";
  175. $datas = RedisService::get($cacheKey);
  176. if($datas && !$refresh){
  177. return $datas;
  178. }
  179. $num = ConfigService::make()->getConfigByCode('custom_rec_num', 3);
  180. $num = $num>=2 && $num<= 20? $num : 3;
  181. $datas = $this->model->where(['type'=>9,'status'=>1,'mark'=>1])
  182. ->select(['id','cover','title','type','description','status'])
  183. ->orderBy('view_num','desc')
  184. ->take($num)
  185. ->get();
  186. $datas = $datas? $datas->toArray() : [];
  187. if($datas){
  188. RedisService::set($cacheKey, $datas, rand(300, 600));
  189. }
  190. return $datas;
  191. }
  192. /**
  193. * 获取文章详情
  194. * @param $id
  195. * @return array|mixed
  196. */
  197. public function getInfo($id)
  198. {
  199. $cacheKey = "caches:articles:info_{$id}";
  200. $info = RedisService::get($cacheKey);
  201. if($info){
  202. return $info;
  203. }
  204. $info = $this->model->where(['id'=> $id,'status'=>1,'mark'=>1])
  205. ->select(['id','title','type','cover','show_type','file_url','view_num','author','description','create_time','type','content'])
  206. ->first();
  207. $info = $info? $info->toArray() : [];
  208. if($info){
  209. $info['create_time'] = $info['create_time']? datetime($info['create_time'],'Y-m-d') : '';
  210. $info['cover'] = get_image_url($info['cover']);
  211. $info['file_url'] = get_image_url($info['file_url']);
  212. if($info['show_type']==2){
  213. if($content = $this->convertDocxToHtml($info['file_url'])){
  214. $info['file_content'] = $content;
  215. }else{
  216. $info['file_content'] = $this->error;
  217. }
  218. }
  219. $info['content'] = get_format_content($info['content']);
  220. $this->model->where(['id'=> $id])->increment('view_num',1);
  221. $info['view_num'] += intval($info['view_num']);
  222. RedisService::set($cacheKey, $info, rand(5,10));
  223. }
  224. return $info;
  225. }
  226. /**
  227. * @param $inputFile
  228. * @return false|string
  229. * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  230. */
  231. public function convertDocxToHtml($inputFile)
  232. {
  233. $filePath = get_image_path($inputFile);
  234. $outputFile = str_replace('.docx', '.html', $filePath);
  235. $outputFile = str_replace('.doc', '.html', $outputFile);
  236. $filePath = ATTACHMENT_PATH.get_image_path($filePath);
  237. // 检查文件是否存在
  238. if (!file_exists($filePath)) {
  239. $this->error = '解析文档不存在,请确认已成功上传';
  240. return false;
  241. }
  242. if (file_exists(ATTACHMENT_PATH.$outputFile)) {
  243. return file_get_contents(ATTACHMENT_PATH.$outputFile);
  244. }
  245. try {
  246. // 加载文档
  247. $phpWord = IOFactory::load($filePath);
  248. //保存为HTML
  249. $htmlWriter = IOFactory::createWriter($phpWord, 'HTML');
  250. $htmlWriter->save(ATTACHMENT_PATH.$outputFile);
  251. if (file_exists(ATTACHMENT_PATH.$outputFile)) {
  252. return file_get_contents(ATTACHMENT_PATH.$outputFile);
  253. } else {
  254. $this->error = '解析文档失败';
  255. return false;
  256. }
  257. }catch (\Exception $exception){
  258. $this->error = '解析文档失败:'.$exception->getMessage();
  259. return false;
  260. }
  261. }
  262. /**
  263. * 查找或查看
  264. * @param $params
  265. * @return array|false|mixed
  266. */
  267. public function search($params)
  268. {
  269. $cacheKey = "caches:articles:search_".md5(json_encode($params));
  270. $data = RedisService::get($cacheKey);
  271. if($data){
  272. return $data;
  273. }
  274. $id = isset($params['id'])? $params['id'] : 0;
  275. $keyword = isset($params['keyword'])? $params['keyword'] : '';
  276. if(empty($keyword) && $id<=0){
  277. $this->error = '请选择或输入问题';
  278. return false;
  279. }
  280. $where = ['id'=>0,'type'=>9,'status'=>1,'mark'=>1];
  281. if($id){
  282. $where['id'] = $id;
  283. }else{
  284. unset($where['id']);
  285. }
  286. $data = $this->model->where($where)
  287. ->where(function($query) use($keyword){
  288. if($keyword){
  289. $query->where('title', 'like',"%{$keyword}%")->orWhere('content', 'like',"%{$keyword}%");
  290. }
  291. })
  292. ->select(['id','title','description','content','status'])
  293. ->orderByRaw('RAND()')
  294. ->orderBy('sort','desc')
  295. ->first();
  296. $data = $data? $data->toArray() :[];
  297. if($data){
  298. RedisService::set($cacheKey, $data, rand(3,5));
  299. }
  300. return $data;
  301. }
  302. /**
  303. * 获取分类文章推荐
  304. * @param int $cateId 推荐分类ID
  305. * @param int $type 类别:3-普通文章,4-客服回复
  306. * @return array|mixed
  307. */
  308. public function getCustomRecommend($cateId=0, $type=4)
  309. {
  310. $cacheKey = "caches:articles:list_{$cateId}";
  311. $datas = RedisService::get($cacheKey);
  312. if($datas){
  313. return $datas;
  314. }
  315. $limitNum = ConfigService::make()->getConfigByCode('custom_recommend_num', 6);
  316. $limitNum = $limitNum? $limitNum : 6;
  317. $datas = ArticleCateModel::where(function($query) use($cateId){
  318. if($cateId){
  319. $query->where('cate_id', $cateId);
  320. }
  321. })->where(['type'=>$type,'status'=>1,'mark'=>1])
  322. ->select(['id','cate_id','title','description','sort','type','status'])
  323. ->limit($limitNum)
  324. ->orderBy('sort','desc')
  325. ->orderBy('create_time','desc')
  326. ->get();
  327. $datas = $datas? $datas->toArray() : [];
  328. if($datas){
  329. RedisService::set($cacheKey, $datas, rand(300,600));
  330. }
  331. return $datas;
  332. }
  333. /**
  334. * 获取文章推荐分类
  335. * @param int $type 1-普通文章分类
  336. * @return array|mixed
  337. */
  338. public function getIndexList($type=1)
  339. {
  340. $cacheKey = "caches:articles:list_{$type}";
  341. $datas = RedisService::get($cacheKey);
  342. if($datas){
  343. return $datas;
  344. }
  345. $limitNum = ConfigService::make()->getConfigByCode('index_article_num', 6);
  346. $limitNum = $limitNum? $limitNum : 6;
  347. $datas = $this->model::where(['type'=> $type,'status'=>1,'mark'=>1])
  348. ->orderBy('sort','desc')
  349. ->orderBy('id','desc')
  350. ->limit($limitNum)
  351. ->get();
  352. $datas = $datas? $datas->toArray() : [];
  353. if($datas){
  354. RedisService::set($cacheKey, $datas, rand(300,600));
  355. }
  356. return $datas;
  357. }
  358. /**
  359. * 获取文章推荐分类
  360. * @param int $type 2-对口资料分类,3-专升本资料分类
  361. * @return array|mixed
  362. */
  363. public function getCateList($type=2, $sc=0)
  364. {
  365. $cacheKey = "caches:articles:cateList_{$type}";
  366. $datas = RedisService::get($cacheKey);
  367. // 复习资料访问统计
  368. if (empty($sc)) {
  369. ExamAccessLogModel::saveLog(date('Y-m-d'), $type, 5);
  370. }
  371. if($datas){
  372. return $datas;
  373. }
  374. $datas = ArticleCateModel::where(['type'=> $type,'status'=>1,'mark'=>1])
  375. ->select(['id','icon','name','pid','description','type','sort','status'])
  376. ->orderBy('sort','desc')
  377. ->orderBy('id','asc')
  378. ->get();
  379. $datas = $datas? $datas->toArray() : [];
  380. $list = [];
  381. if($datas){
  382. foreach ($datas as $item){
  383. $pid = isset($item['pid'])? $item['pid'] : 0;
  384. $id = isset($item['id'])? $item['id'] : 0;
  385. if($pid>0){
  386. $list[$pid] = isset($list[$pid])? $list[$pid] : [];
  387. $list[$pid]['children'][] = $item;
  388. }else{
  389. $item['children'] = [];
  390. $list[$id] = $item;
  391. }
  392. }
  393. RedisService::set($cacheKey, $list, rand(300,600));
  394. }
  395. return $list;
  396. }
  397. }