'待审批', 'approve' => '已通过', 'reject' => '已拒绝', 'revoked' => '已撤回', 'voided' => '已作废', ]; $prefix = $isCC ? '【抄送】' : '【审批】'; $text = $map[$action] ?? $action; // 业务标题/内容 $approvalType = array_flip(\app\common\service\SysDictService::pluckKeyValueByCode('approval_type')); $flowTxt = $approvalType[$instance->flow_type] ?? (string)$instance->flow_type; $title = $prefix.'审批通知'; $content = "{$prefix}你有一条审批 {$text}:类型 {$flowTxt},业务标识 {$instance->biz_id}"; switch ($instance->flow_type) { case 'stock_orders': // 备货单 $stockOrder = \app\common\model\StockOrders::find($instance->biz_id); if ($stockOrder) { $title = $prefix."备货单:{$stockOrder->order_no}"; $flowTxt = $approvalType[$instance->flow_type] ?? $instance->flow_type; $content = "{$prefix}你有一条审批 {$text}:类型 {$flowTxt},业务单号 {$stockOrder->order_no}"; } break; case 'stock_orders_nullify': // 备货单作废 $stockOrder = \app\common\model\StockOrders::find($instance->biz_id); if ($stockOrder) { $title = $prefix."备货单作废:{$stockOrder->order_no}"; $flowTxt = $approvalType[$instance->flow_type] ?? '备货单作废'; $content = "{$prefix}你有一条审批 {$text}:类型 {$flowTxt},业务单号 {$stockOrder->order_no}"; // 把作废原因附带展示更清晰(也可依赖 $extra['note']) $fd = json_decode($instance->form_data ?? '{}', true) ?: []; if (!empty($fd['_nullify_reason'])) { $content .= ';原因:'.(function_exists('mb_strimwidth') ? mb_strimwidth($fd['_nullify_reason'], 0, 120, '...', 'UTF-8') : $fd['_nullify_reason']); } } break; case 'purchase_orders': // 采购单 $purchaseOrder = \app\common\model\PurchaseOrders::find($instance->biz_id); if ($purchaseOrder) { $title = $prefix."采购单:{$purchaseOrder->order_no}"; $flowTxt = $approvalType[$instance->flow_type] ?? $instance->flow_type; $content = "{$prefix}你有一条审批 {$text}:类型 {$flowTxt},业务单号 {$purchaseOrder->order_no}"; } break; case 'purchase_order': // 采购单 break; } // 附加说明(提交说明/申诉/讨论摘要) $append = $extra['appeal'] ?? ($extra['note'] ?? null); if ($append) { if (function_exists('mb_strimwidth')) { $append = mb_strimwidth($append, 0, 120, '...', 'UTF-8'); } $content .= ";说明:{$append}"; } noticePush([ 'title' => $title, 'content' => $content, 'recipient' => $userIds, 'email' => 1, ]); } private function notifyInitiator(object $instance, string $result): void { $this->sendNotification([(int)$instance->initiator_id], $instance, $result, false); } /** WebSocket:把一组用户的“待办数量”推送给前端(旧版) */ private function pushTodoChanged22(array $userIds): void { $userIds = array_values(array_unique(array_map('intval', $userIds))); if (empty($userIds)) return; // 统计这些用户当前的待办数量(以 approval_records.status = 'pending' 为准) $rows = Db::table('approval_records') ->whereIn('approver_id', $userIds) ->where('status', 'pending') ->groupBy('approver_id') ->select('approver_id', Db::raw('COUNT(*) as cnt')) ->get(); // 默认都置 0,再填充真实值 $payload = array_fill_keys($userIds, 0); foreach ($rows as $r) { $payload[(int)$r->approver_id] = (int)$r->cnt; } try { foreach ($payload as $uid => $cnt) { $data = [ 'event' => 'approval.todo.counts', 'user_id' => $uid, 'num' => $cnt, ]; \Webman\RedisQueue\Redis::send('ws-msg', $data); } } catch (\Throwable $e) { Log::error('ws todo push error: '.$e->getMessage()); } } /** WebSocket:把一组用户的“待办数量”推送给前端(统一口径) */ private function pushTodoChanged(array $userIds): void { $userIds = array_values(array_unique(array_map('intval', $userIds))); if (empty($userIds)) return; // 只统计:实例进行中 + 当前轮次 + 当前步骤 的 pending $rows = Db::table('approval_records as ar') ->join('approval_instances as ai', 'ai.id', '=', 'ar.instance_id') ->whereIn('ar.approver_id', $userIds) ->where('ar.status', 'pending') ->where('ai.status', 'processing') ->whereRaw('ar.round = ai.round') ->whereRaw('ar.step_index = ai.current_step') ->groupBy('ar.approver_id') ->select('ar.approver_id', Db::raw('COUNT(*) as cnt')) ->get(); // 默认置 0,再填充真实值 $payload = array_fill_keys($userIds, 0); foreach ($rows as $r) { $payload[(int)$r->approver_id] = (int)$r->cnt; } try { foreach ($payload as $uid => $cnt) { (new \app\common\utils\WsEvents())->todoCounts($uid, $cnt); } } catch (\Throwable $e) { Log::error('ws todo push error: '.$e->getMessage()); } } }