feat 初步融合工作流

This commit is contained in:
ganzizi 2023-04-12 16:25:26 +08:00
parent 9ee8d9b828
commit 7181652f01
10 changed files with 1295 additions and 5 deletions

View File

@ -0,0 +1,604 @@
<?php
namespace App\Http\Controllers\Api;
use Cache;
use Request;
use Carbon\Carbon;
use App\Models\User;
use App\Module\Base;
use App\Module\Ihttp;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Models\WorkflowProcMsg;
/**
* @apiDefine workflow
*
* 工作流
*/
class WorkflowController extends AbstractController
{
private $flow_url = '';
public function __construct()
{
// $this->flow_url = 'http://dootask-workflow-'.env('APP_ID');
$this->flow_url = 'http://192.168.100.219:8700';
}
/**
* @api {post} api/workflow/procdef/all 01. 查询流程定义
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName procdef__all
*
* @apiQuery {String} name 流程名称
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function procdef__all()
{
//{"name": "请假"}
$data['name'] = Request::input('name');
$procdef = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/procdef/findAll', json_encode($data));
$procdef = json_decode($procdef['data'] ?? '', true);
if (!$procdef || $procdef['status'] != 200) {
return Base::retError($procdef['message'] ?? '查询流程失败');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($procdef['data']));
}
/**
* @api {get} api/workflow/procdef/del 02. 删除流程定义
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName procdef__del
*
* @apiQuery {String} id 流程ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function procdef__del()
{
$data['id'] = Request::input('id');
$procdef = Ihttp::ihttp_get($this->flow_url.'/api/v1/workflow/procdef/delById?'.http_build_query($data));
$procdef = json_decode($procdef['data'] ?? '', true);
if (!$procdef || $procdef['status'] != 200) {
return Base::retError($procdef['message'] ?? '删除流程失败');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($procdef['data']));
}
/**
* @api {post} api/workflow/process/start 03. 启动流程
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName process__start
*
* @apiQuery {String} proc_name 流程名称
* @apiQuery {Number} userid 用户ID
* @apiQuery {Number} department_id 部门ID
* @apiQuery {Array} [var] 启动流程类型信息(格式:[{type,startTime,endTime,description}]
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function process__start()
{
//{"procName":"请假222","userID":"2", "departmentId":1, "var":{"type":"事假","startTime":"12", "endTime":"1", "description":"请假描述"}}
$data['userid'] = Request::input('userid');
$data['department_id'] = intval(Request::input('department_id'));
$data['proc_name'] = Request::input('proc_name');
//
$var = json_decode(Request::input('var'), true);
$data['var'] = $var;
$process = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/process/start', json_encode(Base::arrayKeyToCamel($data)));
$process = json_decode($process['data'] ?? '', true);
if (!$process || $process['status'] != 200) {
return Base::retError($process['message'] ?? '流程启动失败');
}
//
$process = Base::arrayKeyToUnderline($process['data']);
$process = $this->getProcessById($process['id']); //获取最新的流程信息
if ($process['candidate']) {
$userid = explode(',', $process['candidate']);
$toUser = User::whereIn('userid', $userid)->get();
$botUser = User::botGetOrCreate('approval-alert');
if (empty($botUser)) {
return Base::retError('审批机器人不存在');
}
foreach ($toUser as $val) {
if ($val->bot) {
continue;
}
$dialog = WebSocketDialog::checkUserDialog($botUser, $val->userid);
if (empty($dialog)) {
continue;
}
$this->workflowMsg('workflow_reviewer', $dialog, $botUser, $val, $process, 'start');
}
// 抄送人
$notifier = $this->handleProcessNode($process);
if ($notifier) {
foreach ($notifier as $val) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $val['target_id']);
$this->workflowMsg('workflow_notifier', $dialog, $botUser, $process, $process);
}
}
}
return Base::retSuccess('success', $process);
}
/**
* @api {post} api/workflow/task/complete 04. 审批
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName task__complete
*
* @apiQuery {Number} task_id 流程ID
* @apiQuery {String} pass 标题 [true-通过false-拒绝]
* @apiQuery {Number} userid 用户ID
* @apiQuery {String} comment 评论
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function task__complete()
{
//{"taskID":51,"pass":"true","userID":"2","comment": "评论备注51"}
$data['task_id'] = intval(Request::input('task_id'));
$data['pass'] = Request::input('pass');
$data['userid'] = Request::input('userid');
$data['comment'] = Request::input('comment');
info(Base::arrayKeyToCamel($data));
$task = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/task/complete', json_encode(Base::arrayKeyToCamel($data)));
info($task);
$task = json_decode($task['data'] ?? '', true);
if (!$task || $task['status'] != 200) {
return Base::retError($task['message'] ?? '审批失败');
}
//
$task = Base::arrayKeyToUnderline($task['data']);
$pass = $data['pass'] == 'true' ? 'pass' : 'refuse';
$process = $this->getProcessById($task['proc_inst_id']);
info($process);
$botUser = User::botGetOrCreate('approval-alert');
if (empty($botUser)) {
return Base::retError('审批机器人不存在');
}
// 在流程信息关联的用户中查找
$toUser = WorkflowProcMsg::where('proc_inst_id', $process['id'])->get()->toArray();
info($toUser);
foreach ($toUser as $val) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $val['userid']);
if (empty($dialog)) {
continue;
}
info('11111111');
$this->workflowMsg('workflow_reviewer', $dialog, $botUser, $val, $process, $pass);
}
// 发起人
if($process['is_finished'] == true) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $process['start_user_id']);
$this->workflowMsg('workflow_submitter', $dialog, $botUser, ['userid' => $data['userid']], $process, $pass);
}
// 抄送人
$notifier = $this->handleProcessNode($process, $task['step']);
if ($notifier && $pass == 'pass') {
foreach ($notifier as $val) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $val['target_id']);
$this->workflowMsg('workflow_notifier', $dialog, $botUser, $process, $process);
}
}
return Base::retSuccess('success', $task);
}
/**
* @api {post} api/workflow/task/withdraw 05. 撤回
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName task__withdraw
*
* @apiQuery {Number} task_id 流程ID
* @apiQuery {String} userid 用户ID
* @apiQuery {Number} proc_inst_id 流程实例ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function task__withdraw()
{
//{"taskID":2,"userID":"2","procInstID":1}
$data['task_id'] = intval(Request::input('task_id'));
$data['userid'] = Request::input('userid');
$data['proc_inst_id'] = intval(Request::input('proc_inst_id'));
info(Base::arrayKeyToCamel($data));
$task = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/task/withdraw', json_encode(Base::arrayKeyToCamel($data)));
info($task);
$task = json_decode($task['data'] ?? '', true);
if (!$task || $task['status'] != 200) {
return Base::retError($task['message'] ?? '撤回失败');
}
//
$process = $this->getProcessById($data['proc_inst_id']);
$botUser = User::botGetOrCreate('approval-alert');
if (empty($botUser)) {
return Base::retError('审批机器人不存在');
}
// 在流程信息关联的用户中查找
$toUser = WorkflowProcMsg::where('proc_inst_id', $process['id'])->get()->toArray();
info($toUser);
foreach ($toUser as $val) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $val['userid']);
if (empty($dialog)) {
continue;
}
info('11111111');
//发送撤回提醒
$this->workflowMsg('workflow_reviewer', $dialog, $botUser, $val, $process, 'withdraw');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($task['data']));
}
/**
* @api {post} api/workflow/process/findTask 06. 查询需要我审批的流程
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName process__findTask
*
* @apiQuery {Number} userID 用户ID
* @apiQuery {String} groups 用户组
* @apiQuery {String} departments 部门
* @apiQuery {String} company 公司
* @apiQuery {String} procName 流程名称
* @apiQuery {Number} pageIndex 页码
* @apiQuery {Number} pageSize 每页条数
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function process__findTask()
{
//{"userID":"2","groups":["人事"],"departments":["技术中心"],"company":"A公司","procName": "请假","pageIndex":1,"pageSize":10}
$data['userID'] = Request::input('userID');
// $data['groups'] = Request::input('groups');
// $data['departments'] = Request::input('departments');
// $data['company'] = Request::input('company');
// $data['procName'] = Request::input('procName');
$data['pageIndex'] = Request::input('pageIndex');
$data['pageSize'] = Request::input('pageSize');
info($data);
$workflow = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/process/findTask', json_encode($data));
info($workflow);
$workflow = json_decode($workflow['data'] ?? '', true);
if (!$workflow || $workflow['status'] != 200) {
return Base::retError($workflow['message'] ?? '查询失败');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow['data']));
}
/**
* @api {post} api/workflow/identitylink/findParticipant 07. 查询流程审批人与评论
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName identitylink__findParticipant
*
* @apiQuery {Number} procInstID 流程ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function identitylink__findParticipant()
{
$data['procInstID'] = Request::input('procInstID');
$get = Ihttp::ihttp_get($this->flow_url.'/api/v1/workflow/identitylink/findParticipant?procInstID=' . $data['procInstID']);
info($get);
$workflow = json_decode($get['data'] ?? '', true);
if (!$workflow || $get['msg'] != 200) {
return Base::retError($workflow['message'] ?? 'fail');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow));
}
/**
* @api {post} api/workflow/procHistory/findTask 08. 查询我审批的流程(已结束)
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName procHistory__findTask
*
* @apiQuery {Number} user_id 用户ID
* @apiQuery {String} company 公司
* @apiQuery {Number} pageIndex 页码
* @apiQuery {Number} pageSize 每页条数
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function procHistory__findTask()
{
//{"userID":"2","company":"A公司","pageIndex":1,"pageSize":2}
$data['userID'] = Request::input('userID');
$data['company'] = Request::input('company');
$data['pageIndex'] = Request::input('pageIndex');
$data['pageSize'] = Request::input('pageSize');
$workflow = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/procHistory/findTask', json_encode($data));
info($workflow);
$workflow = json_decode($workflow['data'] ?? '', true);
if (!$workflow || $workflow['status'] != 200) {
return Base::retError($workflow['message'] ?? 'fail');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow['data']));
}
/**
* @api {post} api/workflow/procHistory/startByMyself 09. 查询我发起的流程(已结束)
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName procHistory__startByMyself
*
* @apiQuery {Number} user_id 用户ID
* @apiQuery {String} company 公司
* @apiQuery {Number} pageIndex 页码
* @apiQuery {Number} pageSize 每页条数
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function procHistory__startByMyself()
{
//{"userID":"2","company":"A公司","pageIndex":1,"pageSize":1}
$data['userID'] = Request::input('userID');
$data['company'] = Request::input('company');
$data['pageIndex'] = Request::input('pageIndex');
$data['pageSize'] = Request::input('pageSize');
$workflow = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/procHistory/startByMyself', json_encode($data));
info($workflow);
$workflow = json_decode($workflow['data'] ?? '', true);
if (!$workflow || $workflow['status'] != 200) {
return Base::retError($workflow['message'] ?? 'fail');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow['data']));
}
/**
* @api {post} api/workflow/process/startByMyself 10. 查询我发起的流程(审批中)
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName process__startByMyself
*
* @apiQuery {Number} user_id 用户ID
* @apiQuery {String} company 公司
* @apiQuery {Number} pageIndex 页码
* @apiQuery {Number} pageSize 每页条数
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function process__startByMyself()
{
//{"userID":"2","company":"A公司","pageIndex":1,"pageSize":1}
$data['userID'] = Request::input('userID');
$data['company'] = Request::input('company');
$data['pageIndex'] = Request::input('pageIndex');
$data['pageSize'] = Request::input('pageSize');
$workflow = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/process/startByMyself', json_encode($data));
info($workflow);
$workflow = json_decode($workflow['data'] ?? '', true);
if (!$workflow || $workflow['status'] != 200) {
return Base::retError($workflow['message'] ?? 'fail');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow['data']));
}
/**
* @api {post} api/workflow/process/FindProcNotify 11. 查询抄送我的流程(审批中)
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName process__FindProcNotify
*
* @apiQuery {Number} user_id 用户ID
* @apiQuery {String} company 公司
* @apiQuery {Array} groups 抄送组
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function process__FindProcNotify()
{
//{"userID":"2","company":"A公司","groups":["人事","产品经理"]}
$data['userID'] = Request::input('userID');
$data['company'] = Request::input('company');
$data['groups'] = Request::input('groups');
$workflow = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/process/FindProcNotify', json_encode($data));
info($workflow);
$workflow = json_decode($workflow['data'] ?? '', true);
if (!$workflow || $workflow['status'] != 200) {
return Base::retError($workflow['message'] ?? 'fail');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow['data']));
}
/**
* @api {post} api/workflow/procHistory/FindProcNotify 12. 查询抄送我的流程(已结束)
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName procHistory__FindProcNotify
*
* @apiQuery {Number} user_id 用户ID
* @apiQuery {String} company 公司
* @apiQuery {Array} groups 抄送组
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function procHistory__FindProcNotify()
{
//{"userID":"2","company":"A公司","groups":["人事","产品经理"]}
$data['userID'] = Request::input('userID');
$data['company'] = Request::input('company');
$data['groups'] = Request::input('groups');
$workflow = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/procHistory/FindProcNotify', json_encode($data));
info($workflow);
$workflow = json_decode($workflow['data'] ?? '', true);
if (!$workflow || $workflow['status'] != 200) {
return Base::retError($workflow['message'] ?? 'fail');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($workflow['data']));
}
/**
* @api {get} api/workflow/process/findById 13. 根据流程ID查询流程所有
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName process__findById
*
* @apiQuery {Number} id 流程ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function process__findById()
{
//{"id":"2"}
$data['id'] = intval(Request::input('id'));
$workflow = $this->getProcessById($data['id']);
return Base::retSuccess('success', $workflow);
}
// 审批机器人消息-审核人
public function workflowMsg($type, $dialog, $botUser, $toUser, $process, $action = null)
{
$data = [
'nickname' => User::userid2nickname($type == 'workflow_submitter' ? $toUser['userid'] : $process['start_user_id']),
'proc_def_name' => $process['proc_def_name'],
'department' => $process['department'],
'type' => $process['var']['type'],
'start_time' => $process['var']['start_time'],
'end_time' => $process['var']['end_time'],
];
info($action);
$text = view('push.bot', ['type' => $type, 'action' => $action, 'data' => (object)$data])->render();
$text = preg_replace("/^\x20+/", "", $text);
$text = preg_replace("/\n\x20+/", "\n", $text);
$msg_action = null;
if ($action == 'withdraw' || $action == 'pass' || $action == 'refuse') {
info(222222222222);
// 如果任务没有完成,则不需要更新消息
info($process['is_finished'] == true);
if ($process['is_finished'] != true) {
return true;
}
info('===========22222222========');
// 任务完成,给发起人发送消息
if($type == 'workflow_submitter' && $action != 'withdraw'){
info('==================任务完成,给发起人发送消息========');
return WebSocketDialogMsg::sendMsg($msg_action, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
}
// 查找最后一条消息msg_id
$msg_action = 'update-'.$toUser['msg_id'];
info($msg_action);
}
info(33333333333333);
$msg = WebSocketDialogMsg::sendMsg($msg_action, $dialog->id, 'text', ['text' => $text], $botUser->userid, false, false, true);
// 保存关联信息
if ($action == 'start') {
info(666666666666);
// 创建关联信息
$proc_msg = new WorkflowProcMsg();
$proc_msg->proc_inst_id = $process['id'];
$proc_msg->msg_id = $msg['data']->id;
$proc_msg->userid = $toUser->userid;
$proc_msg->save();
}
return true;
}
// 根据ID获取流程
public function getProcessById($id)
{
$data['id'] = intval($id);
$process = Ihttp::ihttp_get($this->flow_url."/api/v1/workflow/process/findById?".http_build_query($data));
$process = json_decode($process['data'] ?? '', true);
if (!$process || $process['status'] != 200) {
return Base::retError($process['message'] ?? 'fail');
}
return Base::arrayKeyToUnderline($process['data']);
}
// 处理流程节点返回是否有抄送人
public function handleProcessNode($process, $step = 0)
{
// 获取流程节点
$process_node = $process['node_infos'];
//判断下一步是否有抄送人
$step = $step + 1;
$next_node = $process_node[$step] ?? [];
info($next_node);
if ($next_node) {
if ($next_node['type'] == 'notifier'){
return $next_node['node_user_list'] ?? [];
}
}
return [];
}
// 根据ID查询流程实例的参与者审批中
public function getUserProcessParticipantById($id)
{
$data['id'] = intval($id);
$user = Ihttp::ihttp_get($this->flow_url."/api/v1/user/identitylink/findParticipant?".http_build_query($data));
$user = json_decode($user['data'] ?? '', true);
if (!$user || $user['status'] != 200) {
return Base::retError($user['message'] ?? 'fail');
}
return $user;
}
}

View File

@ -0,0 +1,510 @@
<?php
/**
* 生成 /public/docs/swagger.json
*/
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
$path = dirname(__FILE__). '/';
$files = controllerFiles($path);
//
$tags = [];
$paths = [];
foreach ($files as $fillPath) {
$content = file_get_contents($fillPath);
preg_match("/\/\*\*(\s+\*)+\s*@apiDefine\s+(.*?)\n(\s+\*)+(.*?)([\s\S]*?)\*\//i", $content, $matchs);
if ($matchs) {
$name = trim($matchs[2]);
$description = trim($matchs[5]);
$tags[$name] = $description;
}
preg_match_all("/\/\*\*(\s+\*)+\s*@api\s\{(get|post|put|delete|head|options|patch|propfind|proppatch|mkcol|copy|move|lock|unlock)\}([\s\S]*?)\*\//i", $content, $matchs);
foreach ($matchs[2] as $key => $text) {
$original = $matchs[0][$key];
//
preg_match("/\*\s*\@api\s+\{(get|post|put|delete|head|options|patch|propfind|proppatch|mkcol|copy|move|lock|unlock)\}\s+(.*?)\s+(.*?)\n/i", $original, $matchApi);
preg_match("/\*\s*\@apiDescription\s+(.*?)\n/i", $original, $matchDesc);
preg_match("/\*\s*\@apiGroup\s+(.*?)\n/i", $original, $matchGroup);
if (empty($matchApi) || empty($matchGroup)) {
continue;
}
$tagText = $tags[$matchGroup[1]] ?? $matchGroup[1];
//
preg_match_all("/\*\s*\@(apiHeader|apiBody|apiParam|apiQuery)\s+\{(.*?)\}(.*?)\n([\s\S]*?)(?=(\*\s*@|\*\s*\/))/i", $original, $matchParam);
$paramArray = [];
foreach ($matchParam[1] as $index => $value) {
$paramItem = [];
$in = '';
if ($matchParam[1][$index] == 'apiHeader') {
$in = 'header';
} elseif ($matchParam[1][$index] == 'apiQuery') {
$in = 'query';
} elseif ($matchParam[1][$index] == 'apiBody') {
$in = 'formData';
} elseif ($matchParam[1][$index] == 'apiParam') {
$schema = $matchParam[4][$index];
$schema = preg_replace("/(\\n|^)\s*\*/", '', $schema);
$schemaData = json5_decode($schema);
if ($schemaData) {
$paramItem = [
"name" => "root",
"in" => "body",
"schema" => [
"\$schema" => "http://json-schema.org/draft-04/schema#",
]
];
$isArr = __isArr($schemaData);
$schemaData = $isArr ? $schemaData[1] : $schemaData;
$properties = __propertieData($schemaData);
if ($isArr) {
$paramItem["schema"]["type"] = "array";
$paramItem["schema"]["items"] = [
"type" => "object",
"properties" => $properties
];
} else {
$paramItem["schema"]["type"] = "object";
$paramItem["schema"]["properties"] = $properties;
}
} else {
$in = 'query';
}
} else {
continue;
}
if ($in) {
preg_match("/^\s*(\[*(.*?)\]*)\s+(.*?)$/i", $matchParam[3][$index], $matchParam3);
if ($matchParam3) {
$type = strtolower($matchParam[2][$index]);
$name = $matchParam3[2] ?: $matchParam3[3];
$desc = trim($matchParam3[3]);
$matchParam4 = $matchParam[4][$index];
$matchParam4 = preg_replace("/(\\n|^)\s*\*/", "\n", $matchParam4);
if ($matchParam4) {
$matchParam4 = preg_replace("/(\\n|^)\s*-/", "$1", trim($matchParam4));
$desc = trim(sprintf("%s\n%s", $desc, $matchParam4));
}
$paramArray[] = [
"name" => $name,
"in" => $in,
"required" => !str_starts_with($matchParam3[1], '['),
"description" => $desc,
"type" => $type,
];
}
}
if ($paramItem) {
$paramArray[] = $paramItem;
}
}
//
preg_match_all("/\*\s*\@apiSuccess\s+\{(.*?)\}(.*?)\n([\s\S]*?)(?=(\*\s*@|\*\s*\/))/i", $original, $matchSuccess);
$successArray = [];
$requiredArray = [];
foreach ($matchSuccess[1] as $index => $value) {
preg_match("/^\s*(\[*(.*?)\]*)\s+(.*?)$/i", $matchSuccess[2][$index], $matchSuccess2);
$name = $matchSuccess2[2] ?: $matchSuccess2[3];
if (!str_starts_with($matchSuccess2[1], '[')) {
$requiredArray[] = $name;
}
$type = strtolower($matchSuccess[1][$index]);
$successArray[$name] = [
'type' => strtolower($matchSuccess[1][$index]),
'description' => $matchSuccess2[3]
];
if (in_array($type, ['object', 'array', 'json'])) {
$exampleSuccess = $matchSuccess[3];
preg_match("/\*\s*\@apiSuccessExample\s+\{(.*?)\}\s*{$name}:*([\s\S]*?)(?=(\*\s*@|\*\s*\/))/i", $original, $matchExample);
if ($matchExample) {
$exampleSuccess = [$matchExample[2]];
}
$schemaData = [];
if (array_filter($exampleSuccess, function ($item) use (&$schemaData) {
$item = preg_replace("/(\\n|^)\s*\*/", '', $item);
$itemData = json5_decode($item);
if ($itemData) {
$schemaData = $itemData;
return true;
}
return false;
})) {
$isArr = __isArr($schemaData);
$schemaData = $isArr ? $schemaData[1] : $schemaData;
$successArray[$name]["properties"] = __propertieData($schemaData);
}
}
}
$responsesSchema = [
"\$schema" => "http://json-schema.org/draft-04/schema#",
'type' => 'object',
'properties' => $successArray,
'required' => $requiredArray,
];
//
$paths[$matchApi[2]][$matchApi[1]] = [
'tags' => [$tagText],
'consumes' => strtolower($matchApi[1]) == 'post' ? ['multipart/form-data'] : [],
'summary' => $matchApi[3],
'description' => $matchDesc ? trim($matchDesc[1]) : '',
'parameters' => $paramArray,
'responses' => [
'200' => [
'description' => 'successful operation',
'schema' => $responsesSchema
]
]
];
}
}
//
$tagArray = [];
foreach ($tags as $tag) {
$tagArray[] = [
'name' => $tag,
'description' => $tag,
];
}
//
$title = "Api";
$version = "0.0.1";
$packagePath = dirname($path, 4) . '/package.json';
if (file_exists($packagePath)) {
$packageArray = json_decode(file_get_contents(dirname($path, 4) . '/package.json'), true);
if ($packageArray) {
$title = $packageArray['name'];
$version = $packageArray['version'];
}
}
//
$content = [
'swagger' => '2.0',
'info' => [
'title' => $title,
'version' => $version,
],
'basePath' => '/',
'tags' => $tagArray,
'schemes' => ['http'],
'paths' => $paths
];
$filePath = dirname($path, 4) . '/public/docs';
mkdir($filePath, 0777, true);
file_put_contents($filePath . '/swagger.json', __array2json($content, true));
echo "Success \n";
/** ************************************************************** */
/** ************************************************************** */
/** ************************************************************** */
function __propertieData($jsonArray)
{
list($jsonArray) = json5_value_note($jsonArray);
$properties = [];
foreach ($jsonArray as $k => $v) {
$properties[$k] = [
"type" => "string",
];
list($value, $note) = json5_value_note($v);
if ($note) {
$properties[$k]["description"] = $note;
}
if (__isArr($value)) {
$properties[$k]["type"] = "array";
$properties[$k]["items"] = __propertieItem($value);
} elseif (__isJson($value)) {
$properties[$k]["type"] = "object";
$properties[$k]["properties"] = __propertieItem($value);
} elseif (floatval($value) == $value) {
$properties[$k]["type"] = str_contains("$value", ".") ? "integer" : "number";
}
}
return $properties;
}
function __propertieItem($data)
{
if (__isArr($data)) {
return [
'type' => 'object',
'properties' => $data ? __propertieData($data[0]) : json_decode('{}'),
];
} elseif (__isJson($data)) {
return $data ? __propertieData($data) : json_decode('{}');
}
return json_decode('{}');
}
function __array2json($array, $options = 0)
{
if (!is_array($array)) {
return $array;
}
if ($options === true) {
$options = JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
}
try {
return json_encode($array, $options);
} catch (Exception $e) {
return '';
}
}
function __isArr($data)
{
return is_array($data) && str_starts_with(__array2json($data), '[');
}
function __isJson($data)
{
return is_array($data) && str_starts_with(__array2json($data), '{');
}
/** ************************************************************** */
/** ************************************************************** */
/** ************************************************************** */
// JSON5 for PHP
// [LICENSE] MIT
// [URL] https://github.com/kujirahand/JSON5-PHP
function json5_value_note($v)
{
if (is_array($v) && $v['type'] === '__note__') {
return [$v['value'], $v['note']];
}
return [$v, null];
}
function json5_decode($json5, $assoc = true)
{
return json5_value($json5, $assoc);
}
function json5_value(&$json5, $assoc)
{
$json5 = trim($json5);
json5_comment($json5); // skip comment
// check 1char
$c = substr($json5, 0, 1);
// Object
if ($c == '{') {
return json5_note($json5, json5_object($json5, $assoc));
}
// Array
if ($c == '[') {
return json5_note($json5, json5_array($json5, $assoc));
}
// String
if ($c == '"' || $c == "'") {
return json5_note($json5, json5_string($json5));
}
// null / true / false / Infinity
if (strncasecmp($json5, "null", 4) == 0) {
$json5 = substr($json5, 4);
return json5_note($json5, null);
}
if (strncasecmp($json5, "true", 4) == 0) {
$json5 = substr($json5, 4);
return json5_note($json5, true);
}
if (strncasecmp($json5, "false", 5) == 0) {
$json5 = substr($json5, 5);
return json5_note($json5, false);
}
if (strncasecmp($json5, "infinity", 8) == 0) {
$json5 = substr($json5, 8);
return json5_note($json5, INF);
}
// hex
if (preg_match('/^(0x[a-zA-Z0-9]+)/', $json5, $m)) {
$num = $m[1];
$json5 = substr($json5, strlen($num));
return json5_note($json5, intval($num, 16));
}
// number
if (preg_match('/^((\+|\-)?\d*\.?\d*[eE]?(\+|\-)?\d*)/', $json5, $m)) {
$num = $m[1];
$json5 = substr($json5, strlen($num));
return json5_note($json5, floatval($num));
}
// other char ... maybe error
$json5 = substr($json5, 1);
return json5_note($json5, null);
}
function json5_note($original, $value)
{
$array = explode("\n", $original);
preg_match("/\/\/(.*?)(\\n|$)/i", $array[0], $match);
if ($match) {
return [
'type' => '__note__',
'value' => $value,
'note' => trim($match[1]),
];
}
return $value;
}
function json5_comment(&$json5)
{
while ($json5 != '') {
$json5 = ltrim($json5);
$c2 = substr($json5, 0, 2);
// block comment
if ($c2 == '/*') {
json5_token($json5, '*/');
continue;
}
// line comment
if ($c2 == '//') {
json5_token($json5, "\n");
continue;
}
break;
}
}
function json5_string(&$json5)
{
// check flag
$flag = substr($json5, 0, 1);
$json5 = substr($json5, 1);
$str = "";
while ($json5 != "") {
// check
$c = mb_substr($json5, 0, 1);
$json5 = substr($json5, strlen($c));
// Is terminator?
if ($c == $flag) break;
// escape
if ($c == "\\") {
if (str_starts_with($json5, "\r\n")) {
$json5 = substr($json5, 2);
$str .= "\r\n";
continue;
}
if (str_starts_with($json5, "\n")) {
$json5 = substr($json5, 1);
$str .= "\n";
continue;
}
if (substr($json5, 0, 1) == $flag) {
$json5 = substr($json5, 1);
$str .= "\\" . $flag;
continue;
}
}
$str .= $c;
}
$str = json_decode('"' . $str . '"'); // extract scape chars...
return $str;
}
function json5_array(&$json5, $assoc)
{
// skip '['
$json5 = substr($json5, 1);
$res = array();
while ($json5 != '') {
json5_comment($json5);
// Array terminator?
if (strncmp($json5, ']', 1) == 0) {
$json5 = substr($json5, 1);
break;
}
// get value
$res[] = json5_value($json5, $assoc);
// skip comma
$json5 = ltrim($json5);
if (str_starts_with($json5, ',')) {
$json5 = substr($json5, 1);
}
}
return $res;
}
function json5_object(&$json5, $assoc)
{
// skip '{'
$json5 = substr($json5, 1);
if ($assoc) {
$res = array();
} else {
$res = new \stdClass();
}
while ($json5 != '') {
json5_comment($json5);
// Object terminator?
if (strncmp($json5, '}', 1) == 0) {
$json5 = substr($json5, 1);
break;
}
// get KEY
$c = substr($json5, 0, 1);
if ($c == '"' || $c == "'") {
$key = json5_string($json5);
json5_token($json5, ':');
} else {
$key = trim(json5_token($json5, ":"));
}
// get VALUE
$value = json5_value($json5, $assoc);
if ($assoc) {
$res[$key] = $value;
} else {
$res->$key = $value;
}
// skip Comma
$json5 = ltrim($json5);
if (strncmp($json5, ',', 1) == 0) {
$json5 = substr($json5, 1);
}
}
return $res;
}
function json5_token(&$str, $spl)
{
$i = strpos($str, $spl);
if ($i === FALSE) {
$result = $str;
$str = "";
return $result;
}
$result = substr($str, 0, $i);
$str = substr($str, $i + strlen($spl));
return $result;
}
/**
* @param $path
* @return array
*/
function controllerFiles($path)
{
//判断目录是否为空
if (!file_exists($path)) {
return [];
}
$files = scandir($path);
$fileItem = [];
foreach ($files as $v) {
$newPath = $path . DIRECTORY_SEPARATOR . $v;
if (is_dir($newPath) && $v != '.' && $v != '..') {
$fileItem = array_merge($fileItem, controllerFiles($newPath));
} else if (is_file($newPath) && str_ends_with($newPath, 'Controller.php')) {
$fileItem[] = $newPath;
}
}
return $fileItem;
}

View File

@ -605,6 +605,9 @@ class User extends AbstractModel
case 'anon-msg':
$update['nickname'] = '匿名消息';
break;
case 'approval-alert':
$update['nickname'] = '审批';
break;
case 'bot-manager':
$update['nickname'] = '机器人管理';
break;

View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
/**
* App\Models\WorkflowProcMsg
*
* @property int $id
* @property string|null $type 信息类型candidate-候选人、participant-参与人、notifier-抄送人)
* @property int|null $proc_inst_id 流程实例ID
* @property int|null $userid 会员ID
* @property int|null $msg_id 消息ID
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg query()
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereMsgId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereProcInstId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereType($value)
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WorkflowProcMsg whereUserid($value)
* @mixin \Eloquent
*/
class WorkflowProcMsg extends AbstractModel
{
}

View File

@ -2985,4 +2985,43 @@ class Base
}
return false;
}
/**
* 多维数组字母转下划线格式
* @param $array
* @return array
*/
public static function arrayKeyToUnderline($array)
{
$newArray = [];
foreach ($array as $key => $value) {
//如果是数组,递归调用
if (is_array($value)) {
$value = self::arrayKeyToUnderline($value);
}
$newKey = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $key));
$newArray[$newKey] = $value;
}
return $newArray;
}
/**
* 多维数组字母转驼峰格式
*
* @param [type] $array
* @return array
*/
public static function arrayKeyToCamel($array)
{
$newArray = [];
foreach ($array as $key => $value) {
//如果是数组,递归调用
if (is_array($value)) {
$value = self::arrayKeyToCamel($value);
}
$newKey = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
$newArray[$newKey] = $value;
}
return $newArray;
}
}

1
cmd
View File

@ -393,6 +393,7 @@ if [ $# -gt 0 ]; then
elif [[ "$1" == "doc" ]]; then
shift 1
run_exec php "php app/Http/Controllers/Api/apidoc.php"
run_exec php "php app/Http/Controllers/Api/apiswagger.php"
docker run -it --rm -v ${cur_path}:/home/node/apidoc kuaifan/apidoc -i app/Http/Controllers/Api -o public/docs
elif [[ "$1" == "debug" ]]; then
shift 1

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWorkflowProcMsgs extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('workflow_proc_msgs')) {
Schema::create('workflow_proc_msgs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('proc_inst_id')->nullable()->default(0)->comment('流程实例ID');
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
$table->bigInteger('msg_id')->nullable()->default(0)->comment('消息ID');
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('workflow_proc_msgs', function (Blueprint $table) {
Schema::dropIfExists('workflow_proc_msgs');
});
}
}

View File

@ -148,6 +148,24 @@ services:
ipv4_address: "${APP_IPPR}.10"
restart: unless-stopped
workflow:
container_name: "dootask-workflow-${APP_ID}"
image: "weifashi/go-workflow:1.0.0"
ports:
- "8800:80"
environment:
TZ: "Asia/Shanghai"
MYSQL_HOST: "${DB_HOST}"
MYSQL_PORT: "${DB_PORT}"
MYSQL_DBNAME: "${DB_DATABASE}"
MYSQL_USERNAME: "${DB_USERNAME}"
MYSQL_PASSWORD: "${DB_PASSWORD}"
MYSQL_Prefix: "${DB_PREFIX}workflow_"
networks:
extnetwork:
ipv4_address: "${APP_IPPR}.11"
restart: always
networks:
extnetwork:
name: "dootask-networks-${APP_ID}"

View File

@ -108,6 +108,49 @@
<span style="color:#84c56a">version</span>: 系统版本
@elseif ($type === 'notice')
{{$notice}}
@elseif ($type === 'workflow_reviewer')
<b>{{$data->nickname}}提交的「{{$data->proc_def_name}}」待你审批</b>
申请人:<span style="color:#84c56a">{{$data->nickname}} {{$data->department}}</span>
<b>审批事由</b>
假期类型:<span style="color:#84c56a">{{$data->type}}</span>
开始时间:<span style="color:#84c56a">{{$data->start_time}}</span>
结束时间:<span style="color:#84c56a">{{$data->end_time}}</span>
@if ($action === 'pass')
<span style="color:#84c56a">已同意</span>
@elseif ($action === 'refuse')
<span style="color:#84c56a">已拒绝</span>
@elseif ($action === 'withdraw')
<span style="color:#84c56a">已撤销</span>
@else
<span style="color:#84c56a">同意</span>
<span style="color:#84c56a">拒绝</span>
@endif
@elseif ($type === 'workflow_notifier')
<b>抄送{{$data->nickname}}提交的「{{$data->proc_def_name}}」记录</b>
申请人:<span style="color:#84c56a">{{$data->nickname}} {{$data->department}}</span>
<b>审批事由</b>
假期类型:<span style="color:#84c56a">{{$data->type}}</span>
开始时间:<span style="color:#84c56a">{{$data->start_time}}</span>
结束时间:<span style="color:#84c56a">{{$data->end_time}}</span>
<span style="color:#84c56a">查看详情</span>
@elseif ($type === 'workflow_submitter')
@if ($action === 'pass')
<b>您发起的「{{$data->proc_def_name}}」已通过</b>
@else
<b>您发起的「{{$data->proc_def_name}}」被{{$data->nickname}}拒绝</b>
@endif
<b>审批事由</b>
假期类型:<span style="color:#84c56a">{{$data->type}}</span>
开始时间:<span style="color:#84c56a">{{$data->start_time}}</span>
结束时间:<span style="color:#84c56a">{{$data->end_time}}</span>
<span style="color:#84c56a">查看详情</span>
@else
你好,我是你的机器人助理,你可以发送 <span style="color:#84c56a">/help</span> 查看帮助菜单。
@endif

View File

@ -1,14 +1,15 @@
<?php
use App\Http\Controllers\Api\DialogController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\IndexController;
use App\Http\Controllers\Api\FileController;
use App\Http\Controllers\Api\ProjectController;
use App\Http\Controllers\Api\UsersController;
use App\Http\Controllers\Api\DialogController;
use App\Http\Controllers\Api\PublicController;
use App\Http\Controllers\Api\ReportController;
use App\Http\Controllers\Api\SystemController;
use App\Http\Controllers\Api\UsersController;
use App\Http\Controllers\IndexController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\ProjectController;
use App\Http\Controllers\Api\WorkflowController;
/*
|--------------------------------------------------------------------------
@ -47,6 +48,9 @@ Route::prefix('api')->middleware(['webapi'])->group(function () {
// 公开接口
Route::any('public/{method}', PublicController::class);
Route::any('public/{method}/{action}', PublicController::class);
// 审批
Route::any('workflow/{method}', WorkflowController::class);
Route::any('workflow/{method}/{action}', WorkflowController::class);
});
/**