Finish mzl_dev
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace app\common\service;
|
||||
|
||||
use support\Db;
|
||||
|
||||
class MenuBadgeService
|
||||
{
|
||||
/**
|
||||
* 构造某个节点向上的角标链路(根->叶),每一级返回“完整url + 角标”
|
||||
* @param int $menuId 叶子或中间节点ID(directory/menu均可)
|
||||
* @param int $leafCount 该节点需要向上累加的消息数(正数可表示新增)
|
||||
* @param bool $includeHidden 是否包含隐藏/禁用菜单(默认 false:过滤 status != 1)
|
||||
* @return array<int, array{id:int,name:string,type:string,url:string,badge:int}>
|
||||
*/
|
||||
public static function buildBadgeChain(
|
||||
int $menuId,
|
||||
int $leafCount,
|
||||
bool $includeHidden = false,
|
||||
bool $forceIncludeSelf = true
|
||||
): array {
|
||||
// 取到从“叶->根”的节点列表
|
||||
$nodes = self::getAncestorNodesIterative($menuId, $includeHidden, $forceIncludeSelf);
|
||||
if (empty($nodes)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 调整为“根->叶”
|
||||
$nodes = array_reverse($nodes);
|
||||
$segments = [];
|
||||
$result = [];
|
||||
$leafId = $menuId;
|
||||
|
||||
foreach ($nodes as $n) {
|
||||
$isLeaf = ((int)$n['id'] === $leafId);
|
||||
|
||||
// 中间层的 permission 不展示(但若有内部url段,仍需累计到segments)
|
||||
if (($n['type'] ?? '') === 'permission' && !$isLeaf) {
|
||||
// 若这是 internal 且有 url 段,仍拼到段里,便于子级正确拼接
|
||||
$openType = strtolower((string)($n['open_type'] ?? 'internal'));
|
||||
if ($openType === 'internal') {
|
||||
$seg = trim((string)($n['url'] ?? ''), '/');
|
||||
if ($seg !== '') {
|
||||
$segments[] = $seg;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算完整URL(internal 按段拼接;external/iframe 直接用 link/url)
|
||||
$fullUrl = self::composeFullUrl($segments, $n);
|
||||
|
||||
// 叶子(传入ID)必须返回;其他节点照常
|
||||
if ($isLeaf || ($n['type'] ?? '') !== 'permission') {
|
||||
$result[] = [
|
||||
'id' => (int)$n['id'],
|
||||
'name' => (string)$n['name'],
|
||||
'type' => (string)$n['type'],
|
||||
'url' => $fullUrl,
|
||||
'badge' => (int)$leafCount,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量聚合多个叶子节点的角标,把同一路径处的角标相加(父级=所有子级之和)
|
||||
* @param array<int, array{menu_id:int,count:int}> $items
|
||||
* @param bool $includeHidden
|
||||
* @return array<int, array{url:string,badge:int}>
|
||||
*/
|
||||
public static function aggregateChains(array $items, bool $includeHidden = false): array
|
||||
{
|
||||
$acc = []; // key = url
|
||||
|
||||
foreach ($items as $it) {
|
||||
$menuId = (int)($it['menu_id'] ?? 0);
|
||||
$leafCount = (int)($it['count'] ?? 0);
|
||||
if ($menuId <= 0 || $leafCount === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$chain = self::buildBadgeChain($menuId, $leafCount, $includeHidden);
|
||||
foreach ($chain as $node) {
|
||||
$key = $node['url'];
|
||||
if (!isset($acc[$key])) {
|
||||
$acc[$key] = ['url' => $node['url'], 'badge' => 0];
|
||||
}
|
||||
$acc[$key]['badge'] += $node['badge'];
|
||||
}
|
||||
}
|
||||
|
||||
// 父级在前(更短的路径在前),便于前端逐级渲染
|
||||
usort($acc, function ($a, $b) {
|
||||
return substr_count($a['url'], '/') <=> substr_count($b['url'], '/');
|
||||
});
|
||||
|
||||
return array_values($acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代法:从叶子一路查到根(MySQL版本无关)
|
||||
* $forceIncludeSelf=true 时,无论叶子 status 是否为1,都会把叶子纳入;
|
||||
* 但祖先节点仍按 $includeHidden 规则过滤。
|
||||
*/
|
||||
private static function getAncestorNodesIterative(
|
||||
int $id,
|
||||
bool $includeHidden = false,
|
||||
bool $forceIncludeSelf = true
|
||||
): array {
|
||||
$cols = ['id', 'parent_id', 'name', 'type', 'url', 'open_type', 'link', 'is_hide', 'status'];
|
||||
$nodes = [];
|
||||
$seen = [];
|
||||
$cur = $id;
|
||||
$maxHop = 32; // 防环
|
||||
|
||||
$isFirst = true;
|
||||
for ($i = 0; $i < $maxHop && $cur > 0; $i++) {
|
||||
/** @var object|null $row */
|
||||
$row = Db::table('resources')->select($cols)->where('id', $cur)->first();
|
||||
if (!$row) break;
|
||||
|
||||
$rowArr = (array)$row;
|
||||
|
||||
if (!$includeHidden) {
|
||||
// 叶子节点:若 $forceIncludeSelf=true 则无视 status 过滤;祖先照常过滤
|
||||
if (!$isFirst && (int)$rowArr['status'] !== 1) {
|
||||
break;
|
||||
}
|
||||
if ($isFirst && !$forceIncludeSelf && (int)$rowArr['status'] !== 1) {
|
||||
// 如果明确不强制包含叶子,叶子也按规则过滤
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes[] = $rowArr;
|
||||
|
||||
if (in_array($cur, $seen, true)) {
|
||||
break; // 防环
|
||||
}
|
||||
$seen[] = $cur;
|
||||
|
||||
$cur = (int)$rowArr['parent_id'];
|
||||
$isFirst = false;
|
||||
}
|
||||
|
||||
return $nodes; // 叶 -> 根
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装“完整路由”
|
||||
* - internal:把父级段 + 自身 url 段拼成形如 /a/b/c
|
||||
* - external/iframe:直接用 link(若无则退回自身 url)
|
||||
* - 忽略空段(避免出现重复斜杠)
|
||||
*/
|
||||
private static function composeFullUrl(array &$segments, array $node): string
|
||||
{
|
||||
$openType = strtolower((string)($node['open_type'] ?? 'internal'));
|
||||
|
||||
// 外链或 iframe:直接返回 link/url,不再参与分段拼接
|
||||
if ($openType !== 'internal') {
|
||||
$target = trim((string)($node['link'] ?? ''), " \t\r\n");
|
||||
if ($target === '') {
|
||||
$target = (string)($node['url'] ?? '');
|
||||
}
|
||||
return $target;
|
||||
}
|
||||
|
||||
// internal:把自身 url 作为一个“段”拼到前面来的 segments
|
||||
$seg = trim((string)($node['url'] ?? ''), '/');
|
||||
if ($seg !== '') {
|
||||
$segments[] = $seg;
|
||||
}
|
||||
|
||||
return '/' . implode('/', $segments);
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ class MessageNotificationsService
|
||||
];
|
||||
|
||||
// 执行推送
|
||||
(new \app\common\utils\WsEvents())->pushWsNotice($data, $userId);
|
||||
(new \app\common\utils\WsEvents())->pushWebsocket('notify.receive', $userId, $data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,16 @@ class WsEvents
|
||||
}
|
||||
}
|
||||
|
||||
protected function eventToMethod(string $event): string
|
||||
{
|
||||
$parts = explode('.', $event);
|
||||
$method = array_shift($parts);
|
||||
foreach ($parts as $part) {
|
||||
$method .= ucfirst($part);
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
|
||||
// ================= 系统事件 =================
|
||||
/**
|
||||
@@ -112,6 +122,14 @@ class WsEvents
|
||||
'system.auth',
|
||||
['uid' => $user->uid]
|
||||
));
|
||||
|
||||
// 初始化推送
|
||||
$wdata = [
|
||||
'is_first' => true,
|
||||
'event' => 'approval.todo.counts',
|
||||
'user_id' => $user->uid
|
||||
];
|
||||
\Webman\RedisQueue\Redis::send('ws-msg', $wdata);
|
||||
} catch (\Throwable $th) {
|
||||
flg('[x] [TOKEN认证] TOKEN认证失败:'.$th->getMessage(), false);
|
||||
Gateway::sendToClient($client_id, WsResponse::error(
|
||||
@@ -235,22 +253,20 @@ class WsEvents
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送消息通知
|
||||
*/
|
||||
public function pushWsNotice(array $wsData, int $userId)
|
||||
{
|
||||
Gateway::sendToUid($userId, WsResponse::success('notify.receive', $wsData));
|
||||
}
|
||||
|
||||
// ================= 辅助方法 =================
|
||||
protected function eventToMethod(string $event): string
|
||||
/*
|
||||
approval.todo.counts 审批待办数量
|
||||
|
||||
*/
|
||||
|
||||
// ================= 主动推送 =================
|
||||
|
||||
public function pushWebsocket(string $event, int|string $uid, array $data)
|
||||
{
|
||||
$parts = explode('.', $event);
|
||||
$method = array_shift($parts);
|
||||
foreach ($parts as $part) {
|
||||
$method .= ucfirst($part);
|
||||
}
|
||||
return $method;
|
||||
return match (gettype($uid)) {
|
||||
'integer' => Gateway::sendToUid($uid, WsResponse::success($event, $data)),
|
||||
'string' => Gateway::sendToClient($uid, WsResponse::success($event, $data)),
|
||||
default => throw new \InvalidArgumentException('UID must be integer or string'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,8 @@ class ApprovalFlowService
|
||||
|
||||
$this->notifyCCs($firstStep, $instance, 'pending');
|
||||
|
||||
$this->pushTodoChanged($approverIds);
|
||||
|
||||
return ['instance_id' => $instanceId];
|
||||
});
|
||||
}
|
||||
@@ -152,12 +154,18 @@ class ApprovalFlowService
|
||||
$outcome = $this->getStepOutcome($instanceId, $round, $idx, $type); // 'pending' | 'approve' | 'reject'
|
||||
|
||||
if ($outcome === 'pending') {
|
||||
$this->pushTodoChanged([$approverId]);
|
||||
return ['status' => 'step_pending', 'msg' => '审批待定'];
|
||||
}
|
||||
|
||||
$otherPendingUids = $this->getPendingApproverIds($instanceId, $round, $idx);
|
||||
|
||||
// 3) 有结论:关闭本步其余 pending
|
||||
$this->closeOtherPending($instanceId, $round, $idx, [$approverId]);
|
||||
|
||||
// 这些人 pending→skipped,待办数减少
|
||||
$this->pushTodoChanged($otherPendingUids);
|
||||
|
||||
// 4) 拒绝 => 结束
|
||||
if ($outcome === 'reject') {
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
@@ -167,6 +175,7 @@ class ApprovalFlowService
|
||||
]);
|
||||
$this->onFlowFinalized($instance, 'rejected');
|
||||
$this->sendNotification([$instance->initiator_id], $instance, 'reject', false);
|
||||
$this->pushTodoChanged([$approverId]);
|
||||
return ['status' => 'completed', 'msg' => '拒绝'];
|
||||
}
|
||||
|
||||
@@ -185,7 +194,7 @@ class ApprovalFlowService
|
||||
]);
|
||||
$this->onFlowFinalized($instance, $finalStatus);
|
||||
$this->sendNotification([$instance->initiator_id], $instance, $notifyAction, false);
|
||||
|
||||
$this->pushTodoChanged([$approverId]);
|
||||
return ['status' => 'completed', 'msg' => ($finalStatus === 'voided' ? '已作废' : '同意')];
|
||||
}
|
||||
|
||||
@@ -207,6 +216,7 @@ class ApprovalFlowService
|
||||
}
|
||||
$this->sendNotification($nextApprovers, $instance, 'pending', false);
|
||||
$this->notifyCCs($nextStep, $instance, 'pending');
|
||||
$this->pushTodoChanged(array_merge([$approverId], $nextApprovers));
|
||||
return ['status' => 'next_step', 'msg' => '进行下一步:'.$nextStep['name'] ?? ('Step '.$nextIndex)];
|
||||
});
|
||||
}
|
||||
@@ -282,12 +292,18 @@ class ApprovalFlowService
|
||||
|
||||
$this->onFlowFinalized($instance, 'revoked');
|
||||
|
||||
$pendingUids = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('status', 'pending')
|
||||
->pluck('approver_id')->toArray();
|
||||
|
||||
Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('status', 'pending')
|
||||
->update(['status' => 'skipped', 'approved_at' => $now, 'updated_at' => $now]);
|
||||
|
||||
$this->sendNotification([$initiatorId], $instance, 'revoked', false);
|
||||
$this->pushTodoChanged($pendingUids);
|
||||
return ['status' => 'completed', 'result' => 'revoked'];
|
||||
});
|
||||
}
|
||||
@@ -378,6 +394,13 @@ class ApprovalFlowService
|
||||
|
||||
$this->onFlowFinalized((object)$instance, $final);
|
||||
|
||||
$pendingUids = Db::table('approval_records')
|
||||
->where('instance_id', $instance->id)
|
||||
->where('round', (int)$instance->round)
|
||||
->where('step_index', $instance->current_step)
|
||||
->where('status', 'pending')
|
||||
->pluck('approver_id')->toArray();
|
||||
|
||||
Db::table('approval_records')
|
||||
->where('instance_id', $instance->id)
|
||||
->where('round', (int)$instance->round)
|
||||
@@ -386,6 +409,8 @@ class ApprovalFlowService
|
||||
->update(['status' => 'timeout', 'approved_at' => $now, 'updated_at' => $now]);
|
||||
|
||||
$this->notifyInitiator($instance, $finalAction);
|
||||
|
||||
$this->pushTodoChanged($pendingUids);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -586,7 +611,7 @@ class ApprovalFlowService
|
||||
{
|
||||
$records = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('round', $round) // ★
|
||||
->where('round', $round) //
|
||||
->where('step_index', $stepIndex)
|
||||
->get();
|
||||
|
||||
@@ -620,7 +645,7 @@ class ApprovalFlowService
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$q = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('round', $round) // ★
|
||||
->where('round', $round) //
|
||||
->where('step_index', $stepIndex)
|
||||
->where('status', 'pending');
|
||||
|
||||
@@ -637,7 +662,7 @@ class ApprovalFlowService
|
||||
|
||||
$exists = Db::table('approval_records')
|
||||
->where('instance_id', (int)$instance->id)
|
||||
->where('round', $round) // ★ 限当前轮
|
||||
->where('round', $round) // 限当前轮
|
||||
->where('step_index', $stepIndex)
|
||||
->where('approver_id', $approverId)
|
||||
->where('status', 'pending')
|
||||
@@ -649,7 +674,7 @@ class ApprovalFlowService
|
||||
'instance_id' => (int)$instance->id,
|
||||
'approver_id' => $approverId,
|
||||
'step_index' => $stepIndex,
|
||||
'round' => $round, // ★
|
||||
'round' => $round, //
|
||||
'action' => null,
|
||||
'status' => 'pending',
|
||||
'comment' => null,
|
||||
@@ -995,7 +1020,7 @@ class ApprovalFlowService
|
||||
$finishedAt = null;
|
||||
$durationSec = null;
|
||||
|
||||
// ★ 每一步的“审批意见”(只展示有内容的 comment)
|
||||
// 每一步的“审批意见”(只展示有内容的 comment)
|
||||
$opinions = []; // [{approver_id, approver_name?, action, status, comment, time}...]
|
||||
|
||||
if ($hasRecs) {
|
||||
@@ -1082,7 +1107,7 @@ class ApprovalFlowService
|
||||
'started_at' => $startedAt,
|
||||
'finished_at' => $finishedAt,
|
||||
'duration_seconds' => $durationSec,
|
||||
'opinions' => $opinions, // ★ 这就是每个审批意见
|
||||
'opinions' => $opinions, // 这就是每个审批意见
|
||||
];
|
||||
|
||||
// 把申请人信息并到第一步(可选)
|
||||
@@ -1262,13 +1287,18 @@ class ApprovalFlowService
|
||||
$deadlineAt = date('Y-m-d H:i:s', time() + $flow->timeout_hours * 3600);
|
||||
}
|
||||
|
||||
$oldPendingUids = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('status', 'pending')
|
||||
->pluck('approver_id')->toArray();
|
||||
|
||||
// 清理可能遗留的 pending
|
||||
Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('status', 'pending')
|
||||
->update(['status'=>'skipped','approved_at'=>$now,'updated_at'=>$now]);
|
||||
|
||||
// ★ 轮次 +1
|
||||
// 轮次 +1
|
||||
$newRound = ((int)$instance->round) + 1;
|
||||
|
||||
// 重新开启实例
|
||||
@@ -1303,6 +1333,7 @@ class ApprovalFlowService
|
||||
$extra = $appealText !== '' ? ['appeal' => $appealText] : [];
|
||||
$this->sendNotification($approvers, $instanceLite, 'pending', false, $extra);
|
||||
$this->notifyCCs($firstStep, $instanceLite, 'pending');
|
||||
$this->pushTodoChanged(array_merge($oldPendingUids, $approvers));
|
||||
|
||||
return [
|
||||
'status' => 'resubmitted',
|
||||
@@ -1595,13 +1626,14 @@ class ApprovalFlowService
|
||||
'created_at' => $now,
|
||||
]);
|
||||
|
||||
$this->pushTodoChanged([$fromUserId, $toUserId]);
|
||||
return ['status' => 'ok', 'record_id' => $effectiveRecordId];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** ★ 新增:发起“作废审批” */
|
||||
/** 新增:发起“作废审批” */
|
||||
public function startNullify(
|
||||
string $voidFlowType, // 例如 'stock_orders_nullify'
|
||||
string $bizType, // 原业务类型,如 'stock_orders'
|
||||
@@ -1686,7 +1718,7 @@ class ApprovalFlowService
|
||||
'initiator_id' => $initiatorId,
|
||||
'status' => 'processing',
|
||||
'current_step' => $firstIndex,
|
||||
'round' => 1, // ★
|
||||
'round' => 1, //
|
||||
'deadline_at' => $deadlineAt,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
@@ -1701,7 +1733,7 @@ class ApprovalFlowService
|
||||
'initiator_id' => $initiatorId,
|
||||
'current_step' => $firstIndex,
|
||||
'status' => 'processing',
|
||||
'round' => 1, // ★
|
||||
'round' => 1, //
|
||||
];
|
||||
|
||||
// 6) 生成首步待办 + 通知(带上作废原因)
|
||||
@@ -1715,7 +1747,7 @@ class ApprovalFlowService
|
||||
}
|
||||
$this->sendNotification($approverIds, $instance, 'pending', false, $extra);
|
||||
$this->notifyCCs($firstStep, $instance, 'pending');
|
||||
|
||||
$this->pushTodoChanged($approverIds);
|
||||
return ['instance_id' => $instanceId];
|
||||
});
|
||||
}
|
||||
@@ -1810,4 +1842,111 @@ class ApprovalFlowService
|
||||
return $final;
|
||||
}
|
||||
|
||||
/** 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());
|
||||
}
|
||||
}
|
||||
|
||||
/** 取“某实例、某轮次、某步骤”里当前 pending 的审批人(用于推送受影响的用户) */
|
||||
private function getPendingApproverIds(int $instanceId, int $round, int $stepIndex): array
|
||||
{
|
||||
$ids = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('round', $round)
|
||||
->where('step_index', $stepIndex)
|
||||
->where('status', 'pending')
|
||||
->pluck('approver_id')->toArray();
|
||||
|
||||
return array_values(array_unique(array_map('intval', $ids)));
|
||||
}
|
||||
|
||||
|
||||
/** 登录/首屏初始化:获取当前用户的待办总数(仅统计当前步骤、进行中的实例) */
|
||||
public function getInitTodoCount(int $userId): int
|
||||
{
|
||||
$userId = (int)$userId;
|
||||
if ($userId <= 0) {
|
||||
throw new BusinessException('无效的用户ID');
|
||||
}
|
||||
|
||||
$row = Db::table('approval_records as ar')
|
||||
->join('approval_instances as ai', 'ai.id', '=', 'ar.instance_id')
|
||||
->where('ar.approver_id', $userId)
|
||||
->where('ar.status', 'pending')
|
||||
->where('ai.status', 'processing')
|
||||
// 只统计实例“当前轮次、当前步骤”的待办,避免历史残留干扰
|
||||
->whereRaw('ar.round = ai.round')
|
||||
->whereRaw('ar.step_index = ai.current_step')
|
||||
->selectRaw('COUNT(*) as c')
|
||||
->first();
|
||||
|
||||
return (int)($row->c ?? 0);
|
||||
}
|
||||
|
||||
/** WebSocket:把一组用户的“待办数量”推送给前端(与 getInitTodoCount 统一口径) */
|
||||
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) {
|
||||
\Webman\RedisQueue\Redis::send('ws-msg', [
|
||||
'event' => 'approval.todo.counts',
|
||||
'user_id' => $uid,
|
||||
'num' => $cnt,
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('ws todo push error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -102,14 +102,17 @@ class IndexController
|
||||
|
||||
public function test(Request $request)
|
||||
{
|
||||
$result = $this->tocurl('https://magicbridgeauto.com');
|
||||
if (preg_match('/magicbridgeauto/', $result['body'])) {
|
||||
echo "未被篡改";
|
||||
flg("【magicbridgeauto】未被篡改");
|
||||
} else {
|
||||
echo "已被篡改";
|
||||
flg("【magicbridgeauto】已被篡改");
|
||||
}
|
||||
// $chain = \app\common\service\MenuBadgeService::buildBadgeChain(menuId: 226, leafCount: rand(1, 10));
|
||||
// (new \app\common\utils\WsEvents())->pushWebsocket('approval.todo.counts', 74, $chain);
|
||||
|
||||
$data = [
|
||||
'event' => 'approval.todo.counts',
|
||||
'user_id' => 74,
|
||||
'num' => rand(1, 10),
|
||||
];
|
||||
\Webman\RedisQueue\Redis::send('ws-msg', $data);
|
||||
|
||||
return json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use app\common\utils\FeishuMessage;
|
||||
use support\exception\BusinessException;
|
||||
use support\Log;
|
||||
|
||||
class InquiryToCrmUpdate implements Consumer
|
||||
class InquiryToCrmUpdate_bak implements Consumer
|
||||
{
|
||||
// 要消费的队列名
|
||||
public $queue = 'inquiry-to-crm-update';
|
||||
@@ -11,7 +11,7 @@ use app\common\utils\FeishuMessage;
|
||||
use support\exception\BusinessException;
|
||||
use support\Log;
|
||||
|
||||
class InquiryToCrm implements Consumer
|
||||
class InquiryToCrm_bak implements Consumer
|
||||
{
|
||||
// 要消费的队列名
|
||||
public $queue = 'inquiry-to-crm';
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace app\queue\redis;
|
||||
|
||||
use app\common\model\UserSystems;
|
||||
use Webman\RedisQueue\Consumer;
|
||||
|
||||
class WsMsg implements Consumer
|
||||
{
|
||||
// 要消费的队列名
|
||||
public $queue = 'ws-msg';
|
||||
|
||||
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
|
||||
public $connection = 'default';
|
||||
|
||||
// 消费
|
||||
public function consume($data)
|
||||
{
|
||||
try {
|
||||
$ly = [
|
||||
1 => 186,
|
||||
3 => 226,
|
||||
];
|
||||
|
||||
if (isset($data['is_first']) && $data['is_first'] === true) {
|
||||
$num = (new \app\common\utils\approval\service\ApprovalFlowService())->getInitTodoCount($data['user_id']);
|
||||
} else {
|
||||
$num = $data['num'];
|
||||
}
|
||||
|
||||
if ($data['event'] === 'approval.todo.counts') {
|
||||
$systemIds = UserSystems::where('user_id', $data['user_id'])->pluck('system_id')->toArray();
|
||||
if (count($systemIds) > 0) {
|
||||
foreach ($systemIds as $systemId) {
|
||||
if (isset($ly[$systemId])) {
|
||||
$chain = \app\common\service\MenuBadgeService::buildBadgeChain(menuId: $ly[$systemId], leafCount: $num);
|
||||
(new \app\common\utils\WsEvents())->pushWebsocket('approval.todo.counts', $data['user_id'], $chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
// 消费失败回调
|
||||
/*
|
||||
$package = [
|
||||
'id' => 1357277951, // 消息ID
|
||||
'time' => 1709170510, // 消息时间
|
||||
'delay' => 0, // 延迟时间
|
||||
'attempts' => 2, // 消费次数
|
||||
'queue' => 'send-mail', // 队列名
|
||||
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
|
||||
'max_attempts' => 5, // 最大重试次数
|
||||
'error' => '错误信息' // 错误信息
|
||||
]
|
||||
*/
|
||||
public function onConsumeFailure(\Throwable $e, $package)
|
||||
{
|
||||
echo "consume failure\n";
|
||||
echo $e->getMessage() . "\n";
|
||||
// 无需反序列化
|
||||
var_export($package);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user