[关闭]
@Chiang 2019-09-21T16:35:36.000000Z 字数 10659 阅读 1070

钉钉审批

SCM

参考文档


审批流程

1.钉钉后台设置审批模板 form_component_values[1]
审批模板

2.钉钉后台设置审批人流程 process_code[2]
审批人流程

3.系统调用钉钉开放平台审批流接口的业务流程

Created with Raphaël 2.1.2MysqlMysqlSCMSCMDingDingoriginator_user_id, dept_id发起审批实例(process_code, form_component_values)返回审批实例ID审批实例ID获取审批实例详情审批变更数据推送获取更新实例详情

4.注册业务事件回调接口

Created with Raphaël 2.1.2SCMSCMDingDing注册事件回调接口向接口地址推送'check_url'事件(POST)返回加密字符串"success"注册成功

基础配置

1.安装 EasyDingTalk 包

  1. $ composer require mingyoung/dingtalk:^2.0

2.基本参数设置

  1. use EasyDingTalk\Application;
  2. $config = [
  3. /*
  4. |-----------------------------------------------------------
  5. | 【必填】企业 corpId
  6. |-----------------------------------------------------------
  7. */
  8. 'corp_id' => 'xxxxxxxxxxx',
  9. /*
  10. |-----------------------------------------------------------
  11. | 【必填】应用 AppKey
  12. |-----------------------------------------------------------
  13. */
  14. 'app_key' => 'xxxxxxxxxxx',
  15. /*
  16. |-----------------------------------------------------------
  17. | 【必填】应用 AppSecret
  18. |-----------------------------------------------------------
  19. */
  20. 'app_secret' => 'xxxxxxxxxxx',
  21. /*
  22. |-----------------------------------------------------------
  23. | 【选填】加解密
  24. |-----------------------------------------------------------
  25. | 此处的 `token` 和 `aes_key` 用于事件通知的加解密
  26. | 如果你用到事件回调功能,需要配置该两项
  27. */
  28. 'token' => 'xxxxxxxxxxx',
  29. 'aes_key' => 'xxxxxxxxxxx',
  30. /*
  31. |-----------------------------------------------------------
  32. | 【选填】后台免登配置信息
  33. |-----------------------------------------------------------
  34. | 如果你用到应用管理后台免登功能,需要配置该项
  35. */
  36. 'sso_secret' => 'xxxxxxxxxxx',
  37. /*
  38. |-----------------------------------------------------------
  39. | 【选填】第三方网站 OAuth 授权
  40. |-----------------------------------------------------------
  41. | 如果你用到扫码登录、钉钉内免登和密码登录第三方网站,需要配置该项
  42. */
  43. 'oauth' => [
  44. /*
  45. |-------------------------------------------
  46. | `app-01` 为你自定义的名称,不要重复即可
  47. |-------------------------------------------
  48. | 数组内需要配置 `client_id`, `client_secret`, `scope` 和 `redirect` 四项
  49. |
  50. | `client_id` 为钉钉登录应用的 `appId`
  51. | `client_secret` 为钉钉登录应用的 `appSecret`
  52. | `scope`:
  53. | - 扫码登录第三方网站和密码登录第三方网站填写 `snsapi_login`
  54. | - 钉钉内免登第三方网站填写 `snsapi_auth`
  55. | `redirect` 为回调地址
  56. */
  57. 'app-01' => [
  58. 'client_id' => 'xxxxxxxxxxx',
  59. 'client_secret' => 'xxxxxxxxxxx',
  60. 'scope' => 'snsapi_login',
  61. 'redirect' => 'https://easydingtalk.org/callback',
  62. ],
  63. /*
  64. |-------------------------------------------
  65. | 可配置多个 OAuth 应用,数组内内容同上
  66. |-------------------------------------------
  67. */
  68. 'app-02' => [
  69. // ...
  70. ]
  71. ]
  72. ];
  73. $app = new Application($config);

SCM中实现方式

发起审批实例

  1. /**
  2. * 提交审批, 获取审批实例ID,保存
  3. * @param $process_code 审批流的唯一码,process_code就在审批流编辑的页面URL中
  4. * @param $originator_user_id 审批实例发起人的userid
  5. * @param $dept_id 发起人所在的部门,如果发起人属于根部门,传-1
  6. * @param $form_component_values 审批流表单参数
  7. * @return mixed
  8. */
  9. static function sendInstance($process_code, $originator_user_id, $dept_id, $form_component_values)
  10. {
  11. $params = [];
  12. $params['process_code'] = $process_code;
  13. $params['originator_user_id'] = $originator_user_id;
  14. $params['dept_id'] = $dept_id;
  15. $params['form_component_values'] = $form_component_values;
  16. //发起审批,这里会返回实例ID
  17. $app = new Application(config('ding.config'));
  18. $data = $app->process->create($params);
  19. if ($data['errcode'] == 0) {
  20. return $data['process_instance_id'];
  21. } else {
  22. hawk($data['errmsg']);
  23. }
  24. }

获取审批实例详情

  1. /**
  2. * 根据实例ID, 获取审批流程
  3. * @param $id
  4. * @return mixed
  5. */
  6. static function getInstance($id)
  7. {
  8. // $id = '198d5cbd-918a-465c-81ab-512e33d5a0c6'; //实例ID
  9. $app = new Application(config('ding.config'));
  10. $data = $app->process->get($id);
  11. return $data;
  12. }

审批流数据推送(业务事件回调)

1.注册业务事件回调

  1. /**
  2. * 注册地址
  3. * http://api.scm.test.kuaigang.net/api/ding/register
  4. */
  5. public function registerInstance()
  6. {
  7. $params = [
  8. 'call_back_tag' => ['bpms_task_change', 'bpms_instance_change'],//这里是审批的标签
  9. 'url' => 'http://api.scm.test.kuaigang.net/api/ding/push',//这里是自己定义的回调推送地址
  10. ];
  11. $app = new Application(config('ding.config'));
  12. $app->callback->register($params);//注册
  13. // $app->callback->update($params);//修改
  14. // $app->callback->delete();//删除
  15. // $app->callback->failed();//获取回调失败结果
  16. $list = $app->callback->list();//列表
  17. var_dump($list);die;
  18. }

2.查询事件回调地址方法

  • 这里需要注意的是由于我们注册业务事件回调的时候添加了两个审批标签所以这里会回调两次具体业务需要判断是哪一个审批标签的然后执行回写操作
  1. /**
  2. * 审批推送
  3. * 事件推送自动更新审批列表
  4. * 同时修改关联的主表状态
  5. * 判断是否作废
  6. * @return mixed
  7. */
  8. public function pushInstance()
  9. {
  10. // 获取 server 实例
  11. $app = new Application(config('ding.config'));
  12. $server = $app->server;
  13. // return $server->serve();//这里是验证回调地址返回success加密json
  14. $server->push(function ($payload) {
  15. // 此处 $payload 为钉钉推送事件解密后的内容,为数组形式
  16. /**
  17. * 比如测试回调 URL 事件,$payload 内容为:
  18. *
  19. * $payload = [
  20. * "EventType" => "check_url",
  21. * ]
  22. */
  23. $type = $payload['EventType'];
  24. Log::info(date('Y-m-d H:i:s').$type);
  25. // ...
  26. // 可根据内容处理你对应的业务逻辑
  27. //审批实例ID
  28. $process_instance_id = $payload['processInstanceId'];
  29. //审批实例详情
  30. $process_instance_details = $this->getInstance($process_instance_id);
  31. try {
  32. //1.这里写入ding_approval_list表中
  33. $instance_obj = DB::table(config('alias.ding_approval_list'))->where(['process_instance_id' => $process_instance_id])->update(['instance_details' => json_encode($process_instance_details)]);
  34. $bill_num = DB::table(config('alias.ding_approval_list'))->where([['process_instance_id', $process_instance_id],['status', 1]])->value('bill_num');
  35. $bill_num_type = substr($bill_num, 0, 4);
  36. //2.根据具体单据号 回写具体主表数据,各个模块写接口
  37. if ($bill_num_type == 'XSJH') {
  38. $sales_plan = new SalesPlan();
  39. $sales_plan->backInstance($process_instance_details, $bill_num);
  40. //判断审批状态,审批不通过执行回写接口,调用审批作废接口...
  41. if ($process_instance_details['process_instance']['result'] == 'refuse') {
  42. $data['bill_num'] = $bill_num;
  43. $sales_plan->cancelInstance($data, 4);//审核不通过
  44. }
  45. }
  46. //开票额度控制
  47. if ($bill_num_type == 'KPED' && $type == 'bpms_instance_change' && $payload['type'] == 'finish' ) {
  48. Ding::send( json_encode($payload) );
  49. (new InvoiceLimitRecord())->checkCallBack($payload, $bill_num);
  50. }
  51. //todo...
  52. // switch ($bill_num_type) {
  53. // case 'XSJH':
  54. // $sales_plan = new SalesPlan();
  55. // $sales_plan->backInstance($process_instance_details, $bill_num);
  56. //
  57. // //判断审批状态,审批不通过执行回写接口,调用审批作废接口...
  58. // if ($process_instance_details['process_instance']['result'] == 'refuse') {
  59. // $data['bill_num'] = $bill_num;
  60. // $sales_plan->cancelInstance($data, 4);//审核不通过
  61. // }
  62. //
  63. // break;
  64. //
  65. // //todo ....
  66. // }
  67. } catch (QueryException $e) {
  68. Log::info(date('Y-m-d H:i:s').'-钉钉回调error-'.$e->getMessage());
  69. }
  70. });
  71. return $server->serve();
  72. }

3.回调事件列表

如果注册回调事件时包含审批事件“bpms_task_change”,“bpms_instance_change”,当审批事件发生后,钉钉服务器会向回调url推送事件。

具体业务逻辑实现

  1. /**
  2. * 审批
  3. * 1.审批列表查询数据
  4. * 2.有数据状态置0
  5. * 3.没有添加数据,钉钉提交审批,获取审批详情,保存到审批列表
  6. * 4.回写当前审批状态
  7. * @param $input
  8. */
  9. public function approve($input)
  10. {
  11. $user = obj2array($this->auth->getUser());
  12. $this->validate($input,'approve');
  13. //1.准备参数
  14. $bill_num = $input['bill_num'];
  15. // $process_code = ''; // 审批流的唯一码,process_code就在审批流编辑的页面URL中
  16. // $originator_user_id = $user['originator_user_id']; // 审批实例发起人的userid
  17. // $dept_id = $user['dept_id']; // 发起人所在的部门,如果发起人属于根部门,传-1
  18. $process_code = config('ding.process_code'); // 审批流的唯一码,process_code就在审批流编辑的页面URL中
  19. $originator_user_id = '084462401933296495'; // 审批实例发起人的userid
  20. $dept_id = '3162029'; // 发起人所在的部门,如果发起人属于根部门,传-1
  21. $form_component_values = [ // 审批流表单参数
  22. ['name' => '标题', 'value' => '222test销售计划标题'],
  23. ['name' => '描述', 'value' => '222test销售计划内容'],
  24. ['name' => '品名', 'value' => '222test销售计划内容'],
  25. ['name' => '材质', 'value' => '222test销售计划内容'],
  26. ['name' => '规格', 'value' => '222test销售计划内容'],
  27. ['name' => '产地', 'value' => '222test销售计划内容'],
  28. ['name' => '运输方式', 'value' => '222test销售计划内容'],
  29. ['name' => '发货方式', 'value' => '222test销售计划内容'],
  30. ['name' => '类型', 'value' => '222test销售计划内容'],
  31. ];
  32. //2.钉钉提交审批,获取审批实例ID
  33. $process_instance_id = Ding::sendInstance($process_code, $originator_user_id, $dept_id, $form_component_values);
  34. //3.获取审批详情
  35. $process_instance_details = Ding::getInstance($process_instance_id);
  36. //4.存入审批列表
  37. $time = date('Y-m-d H:i:s');
  38. $data = [];
  39. $data['bill_num'] = $bill_num;
  40. $data['process_instance_id'] = $process_instance_id;
  41. $data['instance_details'] = json_encode($process_instance_details);
  42. $data['status'] = 1;
  43. $data['instance_user_id'] = $user['user_id'];
  44. $data['created_at'] = $time;
  45. $data['updated_at'] = $time;
  46. DB::beginTransaction();
  47. try {
  48. DB::table(config('alias.ding_approval_list'))->insert($data);
  49. //5.回写当前审批状态
  50. $this->backInstance($process_instance_details, $bill_num);
  51. DB::commit();
  52. } catch (QueryException $e) {
  53. DB::rollBack();
  54. hawk($e->getMessage());
  55. }
  56. return 1;
  57. }
  58. /**
  59. * 审批流程
  60. * 根据 bill_num查询审批列表数据
  61. * @param $input
  62. * @return mixed
  63. */
  64. public function processApprove($input)
  65. {
  66. $this->validate($input,'approve');
  67. $bill_num = $input['bill_num'];
  68. $instance_details = DB::table(config('alias.ding_approval_list'))->where([['bill_num', $bill_num], ['status', 1]])->value('instance_details');
  69. if (!$instance_details) {
  70. hawk(config('error.restful.449'));
  71. }
  72. return $instance_details;
  73. }
  74. /**
  75. * 审批作废
  76. * 审批状态 1-未审 2-在审 3-已审 4-弃审 5-作废
  77. * 主表审批状态改为作废,审批不通过
  78. * 审批表里状态改为 => status=0
  79. * @param $input
  80. * @param int $status
  81. * @return int
  82. */
  83. public function cancelApprove($input, $status = 5)
  84. {
  85. $bill_num = $input['bill_num'];
  86. //根据主表查出原有明细表数据数组
  87. $details_id_tf_arr = DB::table($this->table_details)->where([['bill_num_sales_plan', $input['bill_num']], ['status', 1]])->pluck('id')->toArray();
  88. //筛选出明细数据(判断数据是否可以编辑,weight_exec>0不可编辑,货齐不可编辑)
  89. if ($this->tfEditCancelApprove($details_id_tf_arr, $input['bill_num'])) {hawk(config('error.restful.450'));}
  90. $data = [];
  91. $data['audit_type'] = $status;
  92. DB::beginTransaction();
  93. try {
  94. //修改主表状态
  95. DB::table($this->table)->where(['bill_num' => $bill_num])->update($data);
  96. //修改审批表里的状态
  97. DB::table(config('alias.ding_approval_list'))->where(['bill_num' => $bill_num])->update(['status' => 0]);
  98. //todo 如果有引用,作废数据回写引用
  99. DB::commit();
  100. } catch (QueryException $e) {
  101. DB::rollBack();
  102. hawk($e->getMessage());
  103. }
  104. return 1;
  105. }
  106. /**
  107. * 审批回写主表接口
  108. * audit_type 审批状态
  109. * audit_userid 审核人
  110. * audit_date 审批时间
  111. * audit_remark 审核备注
  112. * @param $param
  113. * @param $bill_num
  114. */
  115. public function backInstance($param, $bill_num)
  116. {
  117. $data = [];
  118. $audit_type = 2;//审批状态 1-未审 2-在审 3-已审 4-弃审 5-作废
  119. $result = $param['process_instance']['result']; //审批结果,分为 agree 和 refuse
  120. if ($result == 'agree') {
  121. $audit_type = 3;
  122. } elseif ($result == 'refuse') {
  123. $audit_type = 4;
  124. } else {
  125. $audit_type = 2;
  126. }
  127. // switch ($result) {
  128. // case 'agree':
  129. // $audit_type = 3;
  130. // break;
  131. // case 'refuse':
  132. // $audit_type = 4;
  133. // break;
  134. // default:
  135. // $audit_type = 2;
  136. // break;
  137. // }
  138. $data['audit_type'] = $audit_type;
  139. $operation_records = end($param['process_instance']['operation_records']);
  140. //todo 这里是钉钉user_id,需要查询关联本地ID
  141. $ding_user_id = $operation_records['userid'];
  142. $audit_userid = DB::table(config('alias.admin'))->where(['ding_user_id' => $ding_user_id])->value('id');
  143. // $data['audit_userid'] = $audit_userid;
  144. $data['audit_userid'] = 1;
  145. $data['audit_date'] = $operation_records['date'];
  146. $data['audit_remark'] = isset($operation_records['remark'])?$operation_records['remark']:'';
  147. DB::table($this->table)->where('bill_num', $bill_num)->update($data);
  148. }

表结构设计

  1. CREATE TABLE `ding_approval_list` (
  2. `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
  3. `bill_num` varchar(64) NOT NULL DEFAULT '' COMMENT '单据号',
  4. `process_instance_id` varchar(64) NOT NULL DEFAULT '' COMMENT '审批实例id',
  5. `instance_details` text NOT NULL COMMENT '审批实例详情',
  6. `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(0: 删除,1:正常)',
  7. `instance_user_id` int(11) NOT NULL DEFAULT '0' COMMENT '提交审核人',
  8. `created_at` timestamp NULL DEFAULT NULL,
  9. `updated_at` timestamp NULL DEFAULT NULL,
  10. PRIMARY KEY (`id`)
  11. ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COMMENT='钉钉审批表';

[1] 审批流表单参数
这里的数组结构name,必须和钉钉后台设置的审批模板一一对应
$form_component_values = [
['name' => '标题', 'value' => '222test销售计划标题'],
['name' => '描述', 'value' => '222test销售计划内容'],
['name' => '品名', 'value' => '222test销售计划内容'],
['name' => '材质', 'value' => '222test销售计划内容'],
['name' => '规格', 'value' => '222test销售计划内容'],
['name' => '产地', 'value' => '222test销售计划内容'],
];
[2] processCode,表示审批模板的唯一编码,在审批模板编辑页的url中查看
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注