WorkFlowService.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <?php
  2. namespace App\Service;
  3. use App\Model\Employee;
  4. use App\Model\WorkFlowInstances;
  5. use App\Model\WorkFlowInstancesNodes;
  6. use App\Model\WorkFlowTemplates;
  7. use Illuminate\Support\Facades\DB;
  8. class WorkFlowService extends Service
  9. {
  10. public function workFlowEdit($data,$user){
  11. list($status,$msg) = $this->workFlowRule($data, $user, false);
  12. if(!$status) return [$status,$msg];
  13. try {
  14. DB::beginTransaction();
  15. $model = WorkFlowTemplates::where('id', $data['id'])->first();
  16. $model->code = $data['code'] ?? '';
  17. $model->title = $data['title'] ?? '';
  18. $model->content = $data['content'] ?? '';
  19. $model->save();
  20. DB::commit();
  21. }catch (\Exception $exception){
  22. DB::rollBack();
  23. return [false,$exception->getMessage()];
  24. }
  25. return [true, ''];
  26. }
  27. public function workFlowAdd($data,$user){
  28. list($status,$msg) = $this->workFlowRule($data, $user);
  29. if(!$status) return [$status,$msg];
  30. try {
  31. DB::beginTransaction();
  32. $model = new WorkFlowTemplates();
  33. $model->code = $data['code'] ?? '';
  34. $model->title = $data['title'] ?? '';
  35. $model->content = $data['content'] ?? '';
  36. $model->crt_id = $user['id'];
  37. $model->top_depart_id = $user['top_depart_id'];
  38. $model->save();
  39. DB::commit();
  40. }catch (\Exception $exception){
  41. DB::rollBack();
  42. return [false,$exception->getMessage()];
  43. }
  44. return [true, ''];
  45. }
  46. public function workFlowDetail($data, $user){
  47. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  48. $customer = WorkFlowTemplates::where('del_time',0)
  49. ->where('id',$data['id'])
  50. ->first();
  51. if(empty($customer)) return [false,'审批流不存在或已被删除'];
  52. $customer = $customer->toArray();
  53. $customer['crt_name'] = Employee::where('id',$customer['crt_id'])->value('title');
  54. $customer['crt_time'] = $customer['crt_time'] ? date("Y-m-d H:i:s",$customer['crt_time']): '';
  55. return [true, $customer];
  56. }
  57. public function workFlowDel($data, $user){
  58. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  59. $customer = WorkFlowTemplates::where('del_time',0)
  60. ->where('id',$data['id'])
  61. ->first();
  62. if(empty($customer)) return [false,'审批流不存在或已被删除'];
  63. $customer->del_time = time();
  64. $customer->save();
  65. return [true, ''];
  66. }
  67. public function workFlowCommon($data,$user, $field = []){
  68. if(empty($field)) $field = WorkFlowTemplates::$field;
  69. $model = WorkFlowTemplates::TopClear($user,$data);
  70. $model = $model->where('del_time',0)
  71. ->select($field)
  72. ->orderby('id', 'desc');
  73. if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
  74. if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%');
  75. if(! empty($data['id'])) $model->whereIn('id', $data['id']);
  76. if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
  77. $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
  78. $model->where('crt_time','>=',$return[0]);
  79. $model->where('crt_time','<=',$return[1]);
  80. }
  81. return $model;
  82. }
  83. public function workFlowList($data,$user){
  84. $model = $this->workFlowCommon($data, $user);
  85. $list = $this->limit($model,'',$data);
  86. $list = $this->fillData($list);
  87. return [true, $list];
  88. }
  89. public function workFlowRule(&$data, $user, $is_add = true) {
  90. if(empty($data['title'])) return [false, '审批流名称不能为空'];
  91. if(empty($data['code'])) return [false, '审批流编码不能为空'];
  92. if(empty($data['content'])) return [false, '审批流内容不能为空'];
  93. // 1. JSON 格式校验与转换
  94. $content = $data['content'];
  95. if (is_string($content)) {
  96. $content = json_decode($content, true);
  97. if (json_last_error() !== JSON_ERROR_NONE) {
  98. return [false, '审批流内容格式错误,不是有效的JSON'];
  99. }
  100. }
  101. // 2. 结构完整性校验
  102. if (!isset($content['nodes']) || !is_array($content['nodes'])) {
  103. return [false, '审批流配置缺少节点(nodes)信息'];
  104. }
  105. if (!isset($content['edges']) || !is_array($content['edges'])) {
  106. return [false, '审批流配置缺少连线(edges)信息'];
  107. }
  108. // 4. 业务唯一性校验
  109. $query = WorkFlowTemplates::where('code', $data['code'])
  110. ->where('top_depart_id', $user['top_depart_id'])
  111. ->where('del_time', 0);
  112. if (!$is_add) {
  113. if (empty($data['id'])) return [false, 'ID不能为空'];
  114. $query->where('id', '<>', $data['id']);
  115. }
  116. if ($query->exists()) return [false, '审批流编码已存在'];
  117. return [true, ''];
  118. }
  119. public function fillData($data){
  120. if(empty($data['data'])) return $data;
  121. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($data['data'],'charge_id'), array_column($data['data'],'crt_id'))));
  122. foreach ($data['data'] as $key => $value){
  123. $data['data'][$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  124. $data['data'][$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  125. }
  126. return $data;
  127. }
  128. /**
  129. * 触发审批流实例
  130. * * @param string $templateCode 审批流唯一编码
  131. * @param int $documentId 业务单据ID
  132. * @param string $documentType 业务模型类名
  133. * @param array $user 当前操作用户信息
  134. */
  135. public function triggerWorkflow($templateCode, $documentId, $documentType, $user)
  136. {
  137. // 1. 获取对应的审批流模板
  138. $template = WorkFlowTemplates::where('code', $templateCode)
  139. ->where('top_depart_id', $user['top_depart_id'])
  140. ->where('del_time', 0)
  141. ->first();
  142. if (!$template) return [false, '审批模板不存在'];
  143. // 2. 创建流程主实例
  144. $instance = WorkFlowInstances::create([
  145. 'template_id' => $template->id,
  146. 'document_id' => $documentId,
  147. 'document_type' => $documentType,
  148. 'status' => 'processing',
  149. 'crt_id' => $user['id'],
  150. 'crt_time' => time(),
  151. 'upd_time' => time(),
  152. 'top_depart_id' => $user['top_depart_id']
  153. ]);
  154. // 获取前端原始 JSON 中的节点和连线 [cite: 1]
  155. $nodes = $template->content['nodes'];
  156. $edges = $template->content['edges'];
  157. // 3. 准备批量插入的节点数据
  158. $insertNodes = [];
  159. foreach ($nodes as $node) {
  160. $nodeKey = $node['id'];
  161. // 从 edges 中寻找当前节点的前置 (source 为谁) 和 后置 (target 为谁) [cite: 1]
  162. // 这里的 source 和 target 是 edges 顶层的字符串 ID
  163. $prev = collect($edges)->where('target', $nodeKey)->pluck('source')->first();
  164. $next = collect($edges)->where('source', $nodeKey)->pluck('target')->first();
  165. $insertNodes[] = [
  166. 'instance_id' => $instance->id,
  167. 'node_key' => $nodeKey,
  168. 'label' => $node['label'] ?? '未命名节点',
  169. 'prev_node_key' => $prev, // 上级节点 ID
  170. 'next_node_key' => $next, // 下级节点 ID
  171. // 审批人快照存储 [cite: 1]
  172. 'assignees' => json_encode($node['data']['assignees'] ?? []),
  173. // 审批类型:1 会签,2 或签 [cite: 1]
  174. 'approval_type' => $node['data']['type'] ?? 2,
  175. // 初始状态逻辑:如果没有前置节点,说明是首节点,直接进入“审批中”状态
  176. 'status' => $prev ? 0 : 1,
  177. 'handled_time' => 0,
  178. 'crt_time' => time(),
  179. 'upd_time' => time(),
  180. 'crt_id' => $user['id'],
  181. 'top_depart_id' => $user['top_depart_id']
  182. ];
  183. }
  184. // 4. 写入节点实例表
  185. WorkFlowInstancesNodes::insert($insertNodes);
  186. return [true, $instance->id];
  187. }
  188. /**
  189. * 获取当前用户的待办审核列表
  190. */
  191. public function getMyPendingApprovals($userId, $topDepartId)
  192. {
  193. // 1. 在节点实例表中查询
  194. // 条件:公司隔离 + 状态为审批中(1) + 审批人JSON中包含当前用户ID
  195. $pendingNodes = WorkFlowInstancesNodes::where('top_depart_id', $topDepartId)
  196. ->where('status', 1) // 1 = 审批中
  197. ->whereRaw('JSON_CONTAINS(assignees, JSON_OBJECT("id", ?))', [$userId])
  198. ->get();
  199. if ($pendingNodes->isEmpty()) {
  200. return [];
  201. }
  202. // 2. 获取关联的实例信息和业务单据信息
  203. // 建议在 Model 中定义好 belongsTo 关系
  204. $instanceIds = $pendingNodes->pluck('instance_id')->unique();
  205. $instances = WorkFlowInstances::with(['template']) // 关联查出模板名称等信息
  206. ->whereIn('id', $instanceIds)
  207. ->get()
  208. ->keyBy('id');
  209. // 3. 组装返回数据
  210. $result = $pendingNodes->map(function ($node) use ($instances) {
  211. $instance = $instances[$node->instance_id] ?? null;
  212. return [
  213. 'node_instance_id' => $node->id, // 节点实例ID(审批接口要用)
  214. 'instance_id' => $node->instance_id, // 流程实例ID
  215. 'node_label' => $node->label, // 当前环节名称(如:财务审批)
  216. 'document_id' => $instance->document_id ?? 0, // 业务单据ID
  217. 'document_type' => $instance->document_type ?? '', // 业务模型
  218. 'flow_title' => $instance->template->title ?? '未知流程',
  219. 'apply_time' => $instance->crt_time ?? 0, // 发起时间
  220. 'approval_type' => $node->approval_type, // 1会签/2或签
  221. ];
  222. });
  223. return $result;
  224. }
  225. }