Cron.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <?php
  2. /**
  3. * Niushop商城系统 - 团队十年电商经验汇集巨献!
  4. * =========================================================
  5. * Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
  6. * ----------------------------------------------
  7. * 官方网址: https://www.niushop.com
  8. * =========================================================
  9. */
  10. namespace app\model\system;
  11. use app\model\BaseModel;
  12. use think\facade\Cache;
  13. use think\facade\Log;
  14. use think\facade\Queue;
  15. /**
  16. * 计划任务管理
  17. * @author Administrator
  18. *
  19. */
  20. class Cron extends BaseModel
  21. {
  22. public $time_diff = 60;//默认半个小时检测一次
  23. /**
  24. * 添加计划任务
  25. * @param int $type 任务类型 1.固定任务 2.循环任务
  26. * @param int $period 执行周期
  27. * @param string $name 任务名称
  28. * @param string $event 执行事件
  29. * @param int $execute_time 待执行时间
  30. * @param int $relate_id 关联id
  31. * @param int $period_type 周期类型
  32. */
  33. public function addCron($type, $period, $name, $event, $execute_time, $relate_id, $period_type = 0)
  34. {
  35. $data = [
  36. 'type' => $type,
  37. 'period' => $period,
  38. 'period_type' => $period_type,
  39. 'name' => $name,
  40. 'event' => $event,
  41. 'execute_time' => $execute_time,
  42. 'relate_id' => $relate_id,
  43. 'create_time' => time()
  44. ];
  45. $res = model('cron')->add($data);
  46. return $this->success($res);
  47. }
  48. /**
  49. * 删除计划任务
  50. * @param $condition
  51. * @return array
  52. */
  53. public function deleteCron($condition)
  54. {
  55. $res = model('cron')->delete($condition);
  56. return $this->success($res);
  57. }
  58. /**
  59. * 执行任务
  60. */
  61. public function execute()
  62. {
  63. $system_config_model = new SystemConfig();
  64. $config = $system_config_model->getSystemConfig()[ 'data' ] ?? [];
  65. $is_open_queue = $config[ 'is_open_queue' ] ?? 0;
  66. $query_execute_time = $is_open_queue == 1 ? time() + 60 : time();
  67. $list = model('cron')->getList([ [ 'execute_time', '<=', $query_execute_time ] ]);
  68. $now_time = time();
  69. if (!empty($list)) {
  70. foreach ($list as $k => $v) {
  71. $event_res = checkQueue($v, function($params) {
  72. //加入消息队列
  73. $job_handler_classname = 'Cronexecute';
  74. try {
  75. if ($params[ 'execute_time' ] <= time()) {
  76. Queue::push($job_handler_classname, $params);
  77. } else {
  78. Queue::later($params[ 'execute_time' ] - time(), $job_handler_classname, $params);
  79. }
  80. } catch (\Exception $e) {
  81. $res = $this->error($e->getMessage(), $e->getMessage());
  82. }
  83. return $res ?? $this->success();
  84. }, function($params) {
  85. try {
  86. $res = event($params[ 'event' ], [ 'relate_id' => $params[ 'relate_id' ] ]);
  87. } catch (\Exception $e) {
  88. $res = $this->error($e->getMessage(), $e->getMessage());
  89. }
  90. $data_log = [
  91. 'name' => $params[ 'name' ],
  92. 'event' => $params[ 'event' ],
  93. 'relate_id' => $params[ 'relate_id' ],
  94. 'message' => json_encode($res)
  95. ];
  96. $this->addCronLog($data_log);
  97. return $res;
  98. });
  99. //定义最新的执行时间或错误
  100. $event_code = $event_res[ 'code' ] ?? 0;
  101. if ($event_code < 0) {
  102. Log::write($event_res);
  103. continue;
  104. }
  105. //循环任务
  106. if ($v[ 'type' ] == 2) {
  107. $period = $v[ 'period' ] == 0 ? 1 : $v[ 'period' ];
  108. switch ( $v[ 'period_type' ] ) {
  109. case 0://分
  110. $execute_time = $now_time + $period * 60;
  111. break;
  112. case 1://天
  113. $execute_time = strtotime('+' . $period . 'day', $v[ 'execute_time' ]);
  114. break;
  115. case 2://周
  116. $execute_time = strtotime('+' . $period . 'week', $v[ 'execute_time' ]);
  117. break;
  118. case 3://月
  119. $execute_time = strtotime('+' . $period . 'month', $v[ 'execute_time' ]);
  120. break;
  121. }
  122. model('cron')->update([ 'execute_time' => $execute_time ], [ [ 'id', '=', $v[ 'id' ] ] ]);
  123. } else {
  124. model('cron')->delete([ [ 'id', '=', $v[ 'id' ] ] ]);
  125. }
  126. }
  127. }
  128. $this->setCron();
  129. // $list = model('cron')->getList([['execute_time', '<=', time()]]);
  130. // if (!empty($list)) {
  131. // foreach ($list as $k => $v) {
  132. // try {
  133. // $res = event($v['event'], ['relate_id' => $v['relate_id']]);
  134. // } catch (\Exception $e) {
  135. // $res = $this->error($e->getMessage());
  136. // }
  137. //
  138. // $data_log = [
  139. // 'name' => $v['name'],
  140. // 'event' => $v['event'],
  141. // 'execute_time' => time(),
  142. // 'relate_id' => $v['relate_id'],
  143. // 'message' => json_encode($res)
  144. // ];
  145. // //model('cron_log')->add($data_log);
  146. // //循环任务
  147. // if ($v['type'] == 2) {
  148. // $period = $v['period'] == 0 ? 1 : $v['period'];
  149. // switch ($v['period_type']) {
  150. // case 0://分
  151. //
  152. // $execute_time = $v['execute_time'] + $period * 60;
  153. // break;
  154. // case 1://天
  155. //
  156. // $execute_time = strtotime('+' . $period . 'day', $v['execute_time']);
  157. // break;
  158. // case 2://周
  159. //
  160. // $execute_time = strtotime('+' . $period . 'week', $v['execute_time']);
  161. // break;
  162. // case 3://月
  163. //
  164. // $execute_time = strtotime('+' . $period . 'month', $v['execute_time']);
  165. // break;
  166. // }
  167. // model('cron')->update(['execute_time' => $execute_time], [['id', '=', $v['id']]]);
  168. //
  169. // } else {
  170. // model('cron')->delete([['id', '=', $v['id']]]);
  171. // }
  172. // }
  173. // }
  174. }
  175. /**
  176. * 添加自动任务日志
  177. * @param $data
  178. * @return array
  179. */
  180. public function addCronLog($data)
  181. {
  182. // 日常不需要添加,调试使用
  183. // $data[ 'execute_time' ] = time();
  184. // model('cron_log')->add($data);
  185. return $this->success();
  186. }
  187. /**
  188. * 检测自动任务标识缓存是否已过期
  189. */
  190. public function checkCron()
  191. {
  192. $diff = $this->time_diff;
  193. $now_time = time();
  194. $cron_cache = Cache::get('cron_cache');
  195. if (empty($cron_cache)) {
  196. //todo 不存在缓存标识,并不视为任务停止
  197. //创建缓存标识,当前时间填充
  198. Cache::set('cron_cache', [ 'time' => $now_time, 'error' => '' ]);
  199. } else {
  200. $time = $cron_cache[ 'time' ];
  201. $error = $cron_cache[ 'error' ] ?? '';
  202. $attempts = $cron_cache[ 'attempts' ] ?? 0;//尝试次数
  203. if (!empty($error) || ( $now_time - $time ) > $diff) {
  204. $message = '自动任务已停止';
  205. if (!empty($error)) {
  206. $message .= ',停止原因:' . $error;
  207. } else {
  208. $system_config_model = new \app\model\system\SystemConfig();
  209. $config = $system_config_model->getSystemConfig()[ 'data' ] ?? [];
  210. $is_open_queue = $config[ 'is_open_queue' ] ?? 0;
  211. if (!$is_open_queue) {//如果不是消息队列的话,可以尝试异步调用一下
  212. if ($attempts < 1) {
  213. Cache::set('cron_cache', [ 'time' => $now_time, 'error' => '', 'attempts' => 1 ]);
  214. $url = url('cron/task/execute');
  215. http($url, 1);
  216. return $this->success();
  217. }
  218. } else {
  219. //消息队列无法启动,应该在前端引导跳转到官方的手册
  220. }
  221. }
  222. //判断任务是 消息队列自动任务,还是默认睡眠sleep自动任务
  223. return $this->error([], $message);
  224. }
  225. }
  226. return $this->success();
  227. }
  228. /**
  229. * 设置自动任务
  230. * @param $params
  231. */
  232. public function setCron($params = [])
  233. {
  234. $cron_cache = Cache::get('cron_cache');
  235. if (empty($cron_cache)) {
  236. $cron_cache = [];
  237. }
  238. // $code = $params['code'] ?? 0;
  239. // if($code < 0){
  240. // $error = $params['message'] ?? '位置的错误';
  241. // $cron_cache['error'] = $error;
  242. // }
  243. $cron_cache[ 'time' ] = time();
  244. $cron_cache[ 'attempts' ] = 0;
  245. Cache::set('cron_cache', $cron_cache);
  246. return $this->success();
  247. }
  248. }