workFlowRule($data, $user, false); if(!$status) return [$status,$msg]; try { DB::beginTransaction(); $model = WorkFlowTemplates::where('id', $data['id'])->first(); $model->code = $data['code'] ?? ''; $model->title = $data['title'] ?? ''; $model->content = $data['content'] ?? ''; $model->save(); DB::commit(); }catch (\Exception $exception){ DB::rollBack(); return [false,$exception->getMessage()]; } return [true, '']; } public function workFlowAdd($data,$user){ list($status,$msg) = $this->workFlowRule($data, $user); if(!$status) return [$status,$msg]; try { DB::beginTransaction(); $model = new WorkFlowTemplates(); $model->code = $data['code'] ?? ''; $model->title = $data['title'] ?? ''; $model->content = $data['content'] ?? ''; $model->crt_id = $user['id']; $model->top_depart_id = $user['top_depart_id']; $model->save(); DB::commit(); }catch (\Exception $exception){ DB::rollBack(); return [false,$exception->getMessage()]; } return [true, '']; } public function workFlowDetail($data, $user){ if($this->isEmpty($data,'id')) return [false,'请选择数据!']; $customer = WorkFlowTemplates::where('del_time',0) ->where('id',$data['id']) ->first(); if(empty($customer)) return [false,'审批流不存在或已被删除']; $customer = $customer->toArray(); $customer['crt_name'] = Employee::where('id',$customer['crt_id'])->value('title'); $customer['crt_time'] = $customer['crt_time'] ? date("Y-m-d H:i:s",$customer['crt_time']): ''; return [true, $customer]; } public function workFlowDel($data, $user){ if($this->isEmpty($data,'id')) return [false,'请选择数据!']; $customer = WorkFlowTemplates::where('del_time',0) ->where('id',$data['id']) ->first(); if(empty($customer)) return [false,'审批流不存在或已被删除']; $customer->del_time = time(); $customer->save(); return [true, '']; } public function workFlowCommon($data,$user, $field = []){ if(empty($field)) $field = WorkFlowTemplates::$field; $model = WorkFlowTemplates::TopClear($user,$data); $model = $model->where('del_time',0) ->select($field) ->orderby('id', 'desc'); if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%'); if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%'); if(! empty($data['id'])) $model->whereIn('id', $data['id']); if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) { $return = $this->changeDateToTimeStampAboutRange($data['crt_time']); $model->where('crt_time','>=',$return[0]); $model->where('crt_time','<=',$return[1]); } return $model; } public function workFlowList($data,$user){ $model = $this->workFlowCommon($data, $user); $list = $this->limit($model,'',$data); $list = $this->fillData($list); return [true, $list]; } public function workFlowRule(&$data, $user, $is_add = true) { if(empty($data['title'])) return [false, '审批流名称不能为空']; if(empty($data['code'])) return [false, '审批流编码不能为空']; if(empty($data['content'])) return [false, '审批流内容不能为空']; // 1. JSON 格式校验与转换 $content = $data['content']; if (is_string($content)) { $content = json_decode($content, true); if (json_last_error() !== JSON_ERROR_NONE) { return [false, '审批流内容格式错误,不是有效的JSON']; } } // 2. 结构完整性校验 if (!isset($content['nodes']) || !is_array($content['nodes'])) { return [false, '审批流配置缺少节点(nodes)信息']; } if (!isset($content['edges']) || !is_array($content['edges'])) { return [false, '审批流配置缺少连线(edges)信息']; } $data['content'] = $content; // 4. 业务唯一性校验 $query = WorkFlowTemplates::where('code', $data['code']) ->where('top_depart_id', $user['top_depart_id']) ->where('del_time', 0); if (!$is_add) { if (empty($data['id'])) return [false, 'ID不能为空']; $query->where('id', '<>', $data['id']); } if ($query->exists()) return [false, '审批流编码已存在']; return [true, '']; } public function fillData($data){ if(empty($data['data'])) return $data; $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($data['data'],'charge_id'), array_column($data['data'],'crt_id')))); foreach ($data['data'] as $key => $value){ $data['data'][$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : ''; $data['data'][$key]['crt_name'] = $emp[$value['crt_id']] ?? ''; } return $data; } /** * 触发审批流实例 * * @param string $templateCode 审批流唯一编码 * @param int $documentId 业务单据ID * @param string $documentType 业务模型类名 * @param array $user 当前操作用户信息 */ public function triggerWorkflow($templateID, $documentId, $documentType, $user) { // 1. 获取对应的审批流模板 $template = WorkFlowTemplates::where('id', $templateID) ->where('top_depart_id', $user['top_depart_id']) ->where('del_time', 0) ->first(); if (!$template) return [false, '审批模板不存在']; // 2. 创建流程主实例 $instance = WorkFlowInstances::create([ 'template_id' => $template->id, 'document_id' => $documentId, 'document_type' => $documentType, 'status' => WorkFlowInstances::status_two, // 激活 'crt_id' => $user['id'], 'crt_time' => time(), 'upd_time' => time(), 'top_depart_id' => $user['top_depart_id'] ]); // 获取前端原始 JSON 中的节点和连线 [cite: 1] $nodes = $template->content['nodes']; $edges = $template->content['edges']; // 3. 准备批量插入的节点数据 $insertNodes = []; foreach ($nodes as $node) { $nodeKey = $node['id']; // 从 edges 中寻找当前节点的前置 (source 为谁) 和 后置 (target 为谁) [cite: 1] // 这里的 source 和 target 是 edges 顶层的字符串 ID $prev = collect($edges)->where('target', $nodeKey)->pluck('source')->first(); $next = collect($edges)->where('source', $nodeKey)->pluck('target')->first(); $tempAssignees = $node['data']['assignees'] ?? []; foreach($tempAssignees as $k => $person) { $tempAssignees[$k]['is_pass'] = 0; // 0-未审,1-通过,2-驳回 $tempAssignees[$k]['mark'] = ''; $tempAssignees[$k]['pass_time'] = 0; } $insertNodes[] = [ 'instance_id' => $instance->id, 'node_key' => $nodeKey, 'label' => $node['label'] ?? '未命名节点', 'prev_node_key' => $prev, // 上级节点 ID 'next_node_key' => $next, // 下级节点 ID // 审批人快照存储 [cite: 1] 'assignees' => json_encode($tempAssignees), // 审批类型:1 会签,2 或签 [cite: 1] 'approval_type' => $node['data']['type'] ?? 2, // 初始状态逻辑:如果没有前置节点,说明是首节点,直接进入“审批中”状态 'status' => $prev ? 0 : 1, 'handled_time' => 0, 'crt_time' => time(), 'upd_time' => time(), 'crt_id' => $user['id'], 'top_depart_id' => $user['top_depart_id'] ]; } // 4. 写入节点实例表 WorkFlowInstancesNodes::insert($insertNodes); return [true, $instance->id]; } /** * 获取当前用户的待办审核列表 */ public function getMyPendingApprovals($data, $user) { $userId = $user['id']; $topDepartId = $user['top_depart_id']; // 1. 构造查询对象 $model = WorkFlowInstancesNodes::where('del_time', 0) ->where('top_depart_id', $topDepartId) ->where('status', 1) ->whereRaw('JSON_CONTAINS(assignees, JSON_OBJECT("id", ?))', [$userId]) ->whereRaw('NOT JSON_CONTAINS(assignees, JSON_OBJECT("id", ?, "is_pass", 1))', [$userId]) ->select('*') ->orderBy('id', 'desc'); $pageData = $this->limit($model, '', $data); if (empty($pageData['data'])) return [true, $pageData]; // 2. 提取当前页的所有流程实例 ID $instanceIds = array_unique(array_column($pageData['data'], 'instance_id')); // 3. 获取流程实例及其关联的模板 $instances = WorkFlowInstances::with(['template']) ->whereIn('id', $instanceIds) ->get() ->keyBy('id'); // --- 处理动态业务单据数据 --- // 4. 按业务类型分组,批量查出单据的 code 和 title $documentGroups = []; foreach ($instances as $ins) { $documentGroups[$ins->document_type][] = $ins->document_id; } $documentDataMap = []; // 存储结构: [type][id] => ['code' => ..., 'title' => ...] foreach ($documentGroups as $type => $ids) { // 这里根据你的 document_type (如 'item') 动态查询 // 假设你的业务表都有 code 和 title 字段 $docs = DB::table($type) ->whereIn('id', array_unique($ids)) ->select('id', 'code', 'title') ->get() ->keyBy('id') ->toArray(); $documentDataMap[$type] = $docs; } // 5. 组装最终返回数据 foreach ($pageData['data'] as $key => $node) { $instance = $instances[$node['instance_id']] ?? null; // 获取关联业务表的具体数据 $docDetail = null; if ($instance) { $docDetail = $documentDataMap[$instance->document_type][$instance->document_id] ?? null; } $pageData['data'][$key] = [ 'node_instance_id' => $node['id'], 'instance_id' => $node['instance_id'], 'node_label' => $node['label'], 'document_id' => $instance->document_id ?? 0, 'document_type' => $instance->document_type ?? '', // --- 新增字段 --- 'document_code' => $docDetail->code ?? '', // 业务单据编号 'document_title' => $docDetail->title ?? '', // 业务单据标题 'workflow_title' => $instance->template->title ?? '', // 审批模板名 'crt_time' => !empty($instance->crt_time->timestamp) ? date('Y-m-d H:i:s', $instance->crt_time->timestamp) : '', 'approval_type' => $node['approval_type'], 'approval_type_title' => WorkFlowInstancesNodes::approval_type[$node['approval_type']] ?? '', ]; } return [true, $pageData]; } public function approval($data, $user) { DB::beginTransaction(); // 开启最外层事务 try { // 1. 执行审批逻辑 list($status, $msg, $result) = $this->approve($data, $user); if (!$status) { DB::rollBack(); return [false, $msg]; } // 2. 如果流程终结(通过或驳回),执行业务结算 if ($result == 1 || $result == 2) { $instance_id = $msg; $this->businessSettle($result, $instance_id); } DB::commit(); return [true, '']; } catch (\Exception $e) { DB::rollBack(); return [false, "审批失败: " . $e->getMessage()]; } } /** * @param int $nodeInstanceId 节点实例ID * @param int $status 结果:2-通过,3-驳回 * @param string $remark 意见 * @param array $user 当前操作人 * * 业务结果说明:0-审批中(继续等待), 1-最终通过(业务单据可改为已完成), 2-已驳回(业务单据可改为已拒绝) */ public function approve($data, $user) { $nodeInstanceId = $data['node_instance_id'] ?? 0; $status = $data['status'] ?? 2; $remark = $data['mark'] ?? ''; $currentNode = WorkFlowInstancesNodes::where('del_time',0) ->where('id', $nodeInstanceId) ->where('top_depart_id', $user['top_depart_id']) ->first(); if (!$currentNode || $currentNode->status != 1) return [false, '环节无效或已处理', 0]; $assignees = is_string($currentNode->assignees) ? json_decode($currentNode->assignees, true) : $currentNode->assignees; $myIndex = collect($assignees)->search(fn($item) => $item['id'] == $user['id']); if ($myIndex === false) return [false, '您不在审批人名单中', 0]; $time = time(); $assignees[$myIndex]['mark'] = $remark; $assignees[$myIndex]['pass_time'] = $time; // --- 情况 A:驳回 --- if ($status == 3) { $currentNode->status = 3; $currentNode->handled_time = $time; $assignees[$myIndex]['is_pass'] = 2; $currentNode->assignees = $assignees; $currentNode->save(); WorkFlowInstances::where('id', $currentNode->instance_id)->update(['status' => WorkFlowInstances::status_four]); DB::commit(); return [true, $currentNode->instance_id, 2]; } // --- 情况 B:通过 --- $assignees[$myIndex]['is_pass'] = 1; $currentNode->assignees = $assignees; $shouldMoveToNext = false; if ($currentNode->approval_type == 2) { $shouldMoveToNext = true; } else { $unPassedCount = collect($assignees)->where('is_pass', '!=', 1)->count(); if ($unPassedCount == 0) { $shouldMoveToNext = true; } } $finalResult = 0; // 默认:还在审批中 if ($shouldMoveToNext) { $currentNode->status = 2; $currentNode->handled_time = $time; $currentNode->save(); if (!empty($currentNode->next_node_key)) { $hasActiveNext = $this->activateNextNode($currentNode->instance_id, $currentNode->next_node_key); if (!$hasActiveNext) { // 如果逻辑上虽然有key但没找到下个节点,也视为终结 WorkFlowInstances::where('id', $currentNode->instance_id)->update(['status' => WorkFlowInstances::status_three]); $finalResult = 1; } } else { // 没有下个节点,流程最终终结 WorkFlowInstances::where('id', $currentNode->instance_id)->update(['status' => WorkFlowInstances::status_three]); $finalResult = 1; // 返回 1,表示业务层面可以执行“生效”动作 } } else { // 会签中,还有人没审 $currentNode->save(); $finalResult = 0; } return [true, $currentNode->instance_id, $finalResult]; } private function activateNextNode($instanceId, $nextNodeKey) { $nextNode = WorkFlowInstancesNodes::where('instance_id', $instanceId) ->where('node_key', $nextNodeKey) ->where('del_time',0) ->first(); if ($nextNode) { $nextNode->status = 1; $nextNode->save(); return true; } return false; } /** * 业务结算逻辑 */ public function businessSettle($result, $instance_id) { $w = WorkFlowInstances::where('id', $instance_id)->first(); if (empty($w)) throw new \Exception("未找到流程实例"); $draft = Draft::where('del_time', 0) ->where('document_type', $w->document_type) ->where('document_id', $w->document_id) ->where('top_depart_id', $w->top_depart_id) ->latest() ->first(); // 审核最终通过 if ($result == 1) { if (!empty($draft)) { if ($draft->opt_type == 1) { // 执行覆盖逻辑 $status = false; $resMsg = ''; // 动态调用 if ($draft->document_type == 'item') { list($status, $resMsg) = (new ItemService())->itemEditSave($draft->content, $draft->opt_user); } elseif ($draft->document_type == 'item_node') { list($status, $resMsg) = (new ItemService())->itemNodeEditSave($draft->content, $draft->opt_user); } elseif ($draft->document_type == 'item_node_mission') { list($status, $resMsg) = (new ItemService())->itemNodeMissionEditSave($draft->content, $draft->opt_user); } // 重要:如果业务 Service 返回失败,抛出异常以触发回滚 if (!$status) { throw new \Exception("业务数据更新失败: " . $resMsg); } }elseif ($draft->opt_type == 2){ DB::table($w->document_type)->where('id', $w->document_id)->update($draft->content); } } } // 无论通过还是驳回,重置业务表状态 DB::table($w->document_type)->where('id', $w->document_id)->update(['approval_state' => 0]); // 清理草稿 if (! empty($draft)) { $draft->del_time = time(); $draft->save(); } } }