handleReminders(); }catch (\Exception $exception){ Log::error("发送待办提醒异常", ['msg' => $exception->getMessage()]); } } public function handleReminders() { $now = time(); TodoList::where('status', '<', TodoList::status_two) ->where('del_time', 0) ->where('remind_start', '<=', $now) ->orderBy('remind_start') ->chunkById(50, function ($list) { foreach ($list as $todo) { dispatch(new TodoRemindJob($todo->id))->onQueue(TodoList::$job); } }); } //弃用----------------------------------------------------------- public function handleReminders1() { $now = time(); $wxService = new WxTemplateMessageService(); $appid = config("wx_msg.f_appid"); TodoList::where('status', '<', TodoList::status_two) ->where('del_time', 0) ->where('remind_start', '<=', $now) ->select(TodoList::$field) ->orderBy('id') ->chunkById(10, function ($data) use ($now, $wxService, $appid) { $crtIds = $data->pluck('crt_id')->toArray(); // 查找关联员工 openid $wxInfo = WxEmployeeOfficial::where('employee_id', $crtIds) ->where('type', WxEmployeeOfficial::login_type_two) ->where('appid', $appid) ->pluck('openid','employee_id') ->toArray(); foreach ($data as $todo) { $shouldRemind = false; if ($todo->remind_interval == 0 && empty($todo->last_remind_time)) { $shouldRemind = true; } elseif ($todo->remind_interval > 0 && (empty($todo->last_remind_time) || ($now - $todo->last_remind_time) >= $todo->remind_interval) ) { $shouldRemind = true; } if (! $shouldRemind){ echo $todo->id."|"; continue; } // 先发送微信消息(接口可能失败) $message = ""; try { list($status, $msg) = $this->sendReminder($todo, $wxService, $wxInfo); $message = $msg; if(! $status) { Log::error("发送待办提醒失败 todo_id: {$todo->id}", ['msg' => $msg]); } } catch (\Throwable $e) { Log::error("发送待办提醒失败 todo_id: {$todo->id}", ['msg' => $e->getMessage()]); } // 再更新数据库(单条事务保护即可) DB::transaction(function () use ($todo, $now,$message) { $todo->last_remind_time = $now; $todo->status = TodoList::status_one; $todo->last_time_result = $message; $todo->save(); }); } }); } public function handleReminders2() { $now = time(); $appid = config("wx_msg.f_appid"); $wxSrv = new WxTemplateMessageService(); // 推荐按提醒时间来排序 TodoList::where('status', '<', TodoList::status_two) ->where('del_time', 0) ->where('remind_start', '<=', $now) ->orderBy('remind_start') ->chunkById(20, function ($list) use ($now, $appid, $wxSrv) { // 批量拿创建人 ID $crtIds = array_unique($list->pluck('crt_id')->toArray()); // 批量查询 openid(统一用字符串 key) $wxInfo = WxEmployeeOfficial::where('employee_id', $crtIds) ->where('type', WxEmployeeOfficial::login_type_two) ->where('appid', $appid) ->pluck('openid','employee_id') ->toArray(); // 处理每一条 foreach ($list as $todo) { // 分布式锁(避免并发重复提醒) $lockKey = "todo_remind_lock:" . $todo->id; if (! Cache::lock($lockKey, 5)->get()) { continue; // 已有别的任务在处理本记录 } try { // 1. 判断是否应提醒 if (! $this->shouldRemind($todo, $now)) { continue; } // 2. 发送消息 list($ok, $msg) = $this->sendReminder($todo, $wxSrv, $wxInfo); if (! $ok) { Log::warning("发送待办提醒失败 todo_id={$todo->id}", ['msg' => $msg]); } $this->updateTodoStatus($todo, $now, $msg); } catch (\Throwable $e) { Log::error("提醒异常 todo_id={$todo->id}", ['msg' => $e->getMessage()]); } finally { Cache::lock($lockKey)->release(); } } }); } protected function shouldRemind($todo, $now) { // 首次提醒 if ($todo->remind_interval == 0 && empty($todo->last_remind_time)) { return true; } // 间隔提醒 if ($todo->remind_interval > 0) { if (empty($todo->last_remind_time)) return true; return ($now - $todo->last_remind_time) >= $todo->remind_interval; } return false; } protected function updateTodoStatus($todo, $now, $msg) { // 防止 msg 是数组,写入数据库报错 $msg = is_scalar($msg) ? $msg : json_encode($msg, JSON_UNESCAPED_UNICODE); // 乐观锁,防重复写 TodoList::where('id', $todo->id) ->where('last_remind_time', $todo->last_remind_time ?? 0) ->update([ 'last_remind_time' => $now, 'status' => TodoList::status_one, 'last_time_result' => $msg, ]); } protected function sendReminder($todo, $wxService, $wxInfo) { // 查找关联员工 openid $openid = $wxInfo[$todo->crt_id] ?? ""; if (! $openid) return [false, '无openid']; // 模板参数 $params = [ 'title' => $todo->title ?? '待办提醒', 'time' => date('Y-m-d H:i:00', $todo->remind_start), ]; // 发送消息 list($status, $msg) = $wxService->sendTemplateMessage('to_do', $params, [ 'openid' => $openid, 'pagepath' => 'pages/todoManage/add?type=4&id=' . $todo->id, // 小程序路径或URL ]); return [$status, $msg]; } }