cqp 2 месяцев назад
Родитель
Сommit
1c437bd8a3

+ 60 - 18
app/Console/Commands/ToDoReminder.php

@@ -3,8 +3,11 @@
 namespace App\Console\Commands;
 
 use App\Model\TodoList;
+use App\Model\WxEmployeeOfficial;
+use App\Service\Weixin\WxTemplateMessageService;
 use Illuminate\Console\Command;
 use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 
 class ToDoReminder extends Command
 {
@@ -46,32 +49,71 @@ class ToDoReminder extends Command
     {
         $now = time();
 
+        $wxService = new WxTemplateMessageService();
         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) {
-                DB::transaction(function () use ($data, $now) {
-                    foreach ($data as $todo) {
-                        // 单次提醒
-                        if ($todo->remind_interval == 0) {
-                            if (empty($todo->last_remind_time)) {
-                                $this->sendReminder($todo);
-                                $todo->last_remind_time = $now;
-                                $todo->save();
-                            }
-                            continue;
-                        }
+            ->chunkById(10, function ($data) use ($now, $wxService) {
+                // 查找关联员工 openid
+                $wxInfo = WxEmployeeOfficial::where('employee_id', array_column($data,'crt_id'))
+                    ->where('type', WxEmployeeOfficial::login_type_two)
+                    ->where('del_time', 0)
+                    ->pluck('open_id','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 (empty($todo->last_remind_time) || ($now - $todo->last_remind_time) >= $todo->remind_interval) {
-                            $this->sendReminder($todo);
-                            $todo->last_remind_time = $now;
-                            $todo->save();
+                    if (! $shouldRemind) continue;
+
+                    // 先发送微信消息(接口可能失败)
+                    try {
+                        list($status, $msg) = $this->sendReminder($todo, $wxService, $wxInfo);
+                        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) {
+                        $todo->last_remind_time = $now;
+                        $todo->status = TodoList::status_one;
+                        $todo->save();
+                    });
+                }
             });
     }
+
+    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/todo/detail?id=' . $todo->id, // 小程序路径或URL
+        ]);
+        return [$status, $msg];
+    }
+
 }

+ 13 - 0
app/Http/Controllers/Api/OrderController.php

@@ -154,6 +154,19 @@ class OrderController extends BaseController
         }
     }
 
+    public function toDoFinished(Request $request)
+    {
+        $service = new OrderService();
+        $user = $request->userData;
+        list($status,$data) = $service->toDoFinished($request->all(),$user);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+
     public function toDoEdit(Request $request)
     {
         $service = new OrderService();

+ 15 - 1
app/Service/EmployeeService.php

@@ -17,6 +17,7 @@ use App\Model\RoleMenuButton;
 use App\Model\SysMenu;
 use App\Model\SysMenuButton;
 use App\Model\Team;
+use App\Model\WxEmployeeOfficial;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Hash;
 use Mockery\Exception;
@@ -264,9 +265,11 @@ class EmployeeService extends Service
         if (empty($data['data'])) return $data;
 
         //获取部门 角色
-        list($status, $return) = $this->getEmployee(array_column($data['data'],'id'));
+        $employee_id = array_column($data['data'],'id');
+        list($status, $return) = $this->getEmployee($employee_id);
         if($status) list($role, $role2, $depart_title, $depart_id) = $return;
 
+        $wx_map = $this->getWxBind($employee_id);
         foreach ($data['data'] as $key => $value){
             $data['data'][$key]['role'] = $role2[$value['id']] ?? [];
             $data['data'][$key]['role_name'] = $role[$value['id']] ?? '';
@@ -274,12 +277,23 @@ class EmployeeService extends Service
             $data['data'][$key]['depart_title'] = $depart_title[$value['id']] ?? '';
             $data['data'][$key]['is_wx_admin_title'] = Employee::IS_WX_ADMIN_PC[$value['is_wx_admin']] ?? "";
             $data['data'][$key]['is_admin_title'] = Employee::IS_ADMIN_PC[$value['is_admin']] ?? "";
+            $data['data'][$key]['is_wx_title'] = $wx_map[$value['id']] ? '是' : '否';
             $data['data'][$key]['crt_time'] = $value['crt_time'] ? date("Y-m-d",$value['crt_time']) : "";
         }
 
         return $data;
     }
 
+    private function getWxBind($employee_id = []){
+        $appid = config("wx_msg.f_appid");
+
+        return WxEmployeeOfficial::whereIn('employee_id', $employee_id)
+            ->where('type', WxEmployeeOfficial::login_type_two)
+            ->where('appid', $appid)
+            ->pluck('id', 'employee_id')
+            ->toArray();
+    }
+
     public function getEmployee($employee_ids){
         if(empty($employee_ids)) return [false, ''];
         if(! is_array($employee_ids)) $employee_ids = [$employee_ids];

+ 128 - 4
app/Service/OrderService.php

@@ -10,7 +10,10 @@ use App\Model\ReminderDetails;
 use App\Model\ReminderRecord;
 use App\Model\ReminderRecordDetails;
 use App\Model\TodoList;
+use App\Model\WxEmployeeOfficial;
+use App\Service\Weixin\WxTemplateMessageService;
 use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 
 class OrderService extends Service
 {
@@ -562,13 +565,98 @@ class OrderService extends Service
 
     public function reminderSendWx($data, $user){
         if(empty($data['id'])) return [false, 'ID不能为空'];
+        $appid = config("wx_msg.f_appid");
 
-        $customer = Reminder::where('del_time',0)
+        $reminders = Reminder::where('del_time',0)
             ->whereIn('id',$data['id'])
             ->get()->toArray();
-        if(empty($customer)) return [false,'催单数据不存在或已被删除'];
+        if(empty($reminders)) return [false,'催单数据不存在或已被删除'];
+        if(count($reminders) >= 10) return [false, '批量操作一次最多10条'];
 
-        return [true, ''];
+        $details = ReminderDetails::from('reminder_details as a')
+            ->leftjoin('wx_employee_official as b','b.employee_id','a.customer_supply_id')
+            ->where('a.del_time',0)
+            ->where('b.type',WxEmployeeOfficial::login_type_one)
+            ->where('b.appid',$appid)
+            ->whereIn('a.reminder_id', array_column($reminders,'id'))
+            ->select('a.reminder_id','a.customer_supply_id','a.open_id')
+            ->get()->toArray();
+        if (empty($details)) return [false, '未找到任何可发送的微信消息的供应商'];
+
+        // 按 reminder_id 分组
+        $detailMap = [];
+        foreach ($details as $d) {
+            if (! empty($d['open_id'])) {
+                $detailMap[$d['reminder_id']][] = [
+                    'open_id' => $d['open_id'],
+                    'employee_id' => $d['customer_supply_id'],
+                ];
+            }
+        }
+
+        $wxService = new WxTemplateMessageService();
+
+        $total = 0;
+        $success = 0;
+        $fail = 0;
+        $failList = [];
+
+        // ③ 循环发送每条催单的每个人
+        foreach ($reminders as $reminder) {
+            $reminderId = $reminder['id'];
+            $d_array = $detailMap[$reminderId] ?? [];
+            if (empty($d_array)) continue;
+
+            foreach ($d_array as $value) {
+                $openid = $value['open_id'];
+                $employee_id = $value['employee_id'];
+                $params = [
+                    'order_number'   => $reminder['order_number'] ?? '',
+                    'product_detail' => $reminder['product_detail'] ?? '',
+                    'order_no'        => $reminder['order_no'] ?? '',
+                ];
+
+                // URL 可自定义跳转地址
+                $options = [
+                    'url' => 'https://xlkj.qingyaokeji.com/#/wxDetailGet?openid='.$openid.'&employee_id='.$employee_id,
+                    'openid' => $openid,
+                ];
+
+                $total++;
+
+                try {
+                    // 调用微信发送
+                    [$res, $msg] = $wxService->sendTemplateMessage('supply_reminder', $params, $options);
+
+                    if ($res) {
+                        $success++;
+                    } else {
+                        $fail++;
+                        $failList[] = [
+                            'reminder_id' => $reminderId,
+                            'openid' => $openid,
+                            'error' => $msg,
+                        ];
+                    }
+                } catch (\Exception $e) {
+                    $fail++;
+                    $failList[] = [
+                        'reminder_id' => $reminderId,
+                        'openid' => $openid,
+                        'error' => $e->getMessage(),
+                    ];
+                }
+            }
+        }
+
+        // ④ 结果统计返回
+        $resultMsg = "共需发送 {$total} 条模板消息,成功 {$success} 条,失败 {$fail} 条。";
+        if ($fail > 0) {
+            $resultMsg .= ' 失败详情见日志。';
+            Log::error('微信模板消息发送失败详情', $failList);
+        }
+
+        return [true, $resultMsg];
     }
     //催单管理---------------------------------------------
 
@@ -684,7 +772,10 @@ class OrderService extends Service
             ->orderby('id', 'desc');
 
         if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
-        if(! empty($data['id'])) $model->whereIn('id', $data['id']);
+        if(! empty($data['id'])) {
+            if(! is_array($data['id'])) $data['id'] = [$data['id']];
+            $model->whereIn('id', $data['id']);
+        }
         if(isset($data['status'])) $model->where('status', $data['status']);
         if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
             $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
@@ -742,6 +833,39 @@ class OrderService extends Service
         return [true, $data];
     }
 
+    public function toDoFinished($data, $user)
+    {
+        if (empty($data['id'])) return [false, 'ID不能为空'];
+
+        // 确保 id 是数组
+        $ids = is_array($data['id']) ? $data['id'] : [$data['id']];
+        if (empty($ids)) return [false, 'ID不能为空'];
+
+        // 查询待办事项
+        $orders = TodoList::whereIn('id', $ids)
+            ->where('del_time', 0)
+            ->where('status', '<', TodoList::status_two)
+            ->get();
+
+        if ($orders->isEmpty()) return [false, '未找到可操作的待办事项'];
+
+        try {
+            DB::beginTransaction();
+
+            // 批量更新
+            TodoList::whereIn('id', $orders->pluck('id'))
+                ->update(['status' => TodoList::status_two]);
+
+            DB::commit();
+        } catch (\Exception $e) {
+            DB::rollBack();
+            return [false, $e->getMessage()];
+        }
+
+        $count = count($orders);
+        return [true, "成功完成 {$count} 条待办事项"];
+    }
+
     public function fillToDoData($data, $user, $search){
         if(empty($data['data'])) return $data;
 

+ 1 - 1
app/Service/Weixin/WxTemplateMessageService.php

@@ -42,7 +42,7 @@ class WxTemplateMessageService extends WeixinService
                 $payload['url'] = $options['url'] ?? '';
             } elseif ($jumpType === 'mp') {
                 $payload['miniprogram'] = [
-                    'appid' => config('wx_msg.default_appid'),
+                    'appid' => config('wx_msg.appid'),
                     'pagepath' => $options['pagepath'] ?? '',
                 ];
             }

+ 4 - 0
config/header/64.php

@@ -34,4 +34,8 @@ return [
         'key' =>'is_wx_admin_title',
         'value' => '是否允许登录微信小程序',
     ],
+    [
+        'key' =>'is_wx_title',
+        'value' => '是否绑定微信公众号',
+    ],
 ];

+ 2 - 1
config/wx_msg.php

@@ -22,8 +22,9 @@ return [
         ],
     ],
 
-    'appid' => '', // 小程序 appid
+    'appid' => 'wx4148f0d9f05fb3fe', // 小程序 appid
     'appSecret' => '', // 小程序 密码
     'f_appid' => 'wxc98d589b271423bc', // 服务号 appid
     'f_appSecret' => 'aea299509ab589df54f975765ee69905', // 服务号 密码
+    'todo_url' => 'pages/Todo/index/index?id=',//客户待办列表页
 ];

+ 1 - 0
routes/api.php

@@ -145,6 +145,7 @@ Route::group(['middleware'=> ['checkLogin']],function ($route){
     $route->any('toDoAdd', 'Api\OrderController@toDoAdd');
     $route->any('toDoDel', 'Api\OrderController@toDoDel');
     $route->any('toDoDetail', 'Api\OrderController@toDoDetail');
+    $route->any('toDoFinished', 'Api\OrderController@toDoFinished');
 
     //获取默认表头
     $route->any('getTableHead','Api\TableHeadController@tableHeadGet');