cqp 1 месяц назад
Родитель
Сommit
bd1987135f
1 измененных файлов с 88 добавлено и 1 удалено
  1. 88 1
      app/Console/Commands/ToDoReminder.php

+ 88 - 1
app/Console/Commands/ToDoReminder.php

@@ -6,6 +6,7 @@ use App\Model\TodoList;
 use App\Model\WxEmployeeOfficial;
 use App\Service\Weixin\WxTemplateMessageService;
 use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
 
@@ -49,7 +50,7 @@ class ToDoReminder extends Command
         }
     }
 
-    public function handleReminders()
+    public function handleReminders1()
     {
         $now = time();
 
@@ -109,6 +110,92 @@ class ToDoReminder extends Command
             });
     }
 
+    public function handleReminders()
+    {
+        $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