DingService.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <?php
  2. namespace App\Service;
  3. use App\Jobs\ProcessDataJob;
  4. use App\Model\DDEmployee;
  5. use App\Model\Record;
  6. use App\Model\U8State;
  7. use Illuminate\Support\Facades\DB;
  8. use Illuminate\Support\Facades\Redis;
  9. class DingService extends Service
  10. {
  11. public function getAccessToken($type)
  12. {
  13. $key = DDEmployee::type_redis[$type];
  14. if(empty($key)) return [false, 'token名称未设置'];
  15. $token = Redis::get($key);
  16. if(! empty($token)) return [true, ['access_token' => $token]];
  17. $config = config("dingtalk.{$type}");
  18. if(empty($config)) return [false, '钉钉配置信息不存在'];
  19. $appKey = $config['app_key'] ?? null;
  20. $appSecret = $config['app_secret'] ?? null;
  21. if (!$appKey) return [false, 'AppKey 配置不完整'];
  22. if (!$appSecret) return [false, 'AppSecret 配置不完整'];
  23. $url = "https://api.dingtalk.com/v1.0/oauth2/accessToken";
  24. $resp = $this->curlOpen1($url, [
  25. 'request' => 'post',
  26. 'header' => ['Content-Type: application/json'],
  27. 'json' => [
  28. "appKey" => $appKey,
  29. "appSecret" => $appSecret
  30. ]
  31. ]);
  32. $res = json_decode($resp, true);
  33. $accessToken = $res['accessToken'] ?? "";
  34. $expires_in = $res['expires_in'] ?? 0;
  35. if(empty($accessToken)) return [false, 'AccessToken获取失败'];
  36. Redis::setex($key, $expires_in, $accessToken);
  37. return [true, ['access_token' => $accessToken]];
  38. }
  39. /**
  40. * 根据前端传来的免登 code 获取用户信息
  41. * @param string $code 前端 dd.getAuthCode 获取的 code
  42. * @return array [bool, data] bool 表示成功与否,data 成功返回用户信息,失败返回错误信息
  43. */
  44. public function getUserByCode($data, $LoginType)
  45. {
  46. if(empty($LoginType)) return [false, 'login_type类型不能为空'];
  47. if(! isset(DDEmployee::type[$LoginType])) return [false, 'login_type类型不存在或错误'];
  48. $code = $data['code'] ?? "";
  49. if (empty($code)) return [false, '钉钉授权code不能为空'];
  50. // 1. 获取 access_token
  51. [$success, $tokenData] = $this->getAccessToken($LoginType);
  52. if (! $success) return [false, $tokenData]; // tokenData 是错误信息
  53. $accessToken = $tokenData['access_token'];
  54. // 2. 用 code 换取用户信息(v2 接口)
  55. $url = "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token={$accessToken}";
  56. $resp = $this->curlOpen1($url, [
  57. 'request' => 'post',
  58. 'header' => [
  59. "Content-Type: application/json",
  60. ],
  61. 'json' => [
  62. "code" => $code
  63. ]
  64. ]);
  65. $res = json_decode($resp, true);
  66. if (!isset($res['errcode'])) {
  67. return [false, '接口返回异常: ' . $resp];
  68. }
  69. if ($res['errcode'] !== 0) {
  70. return [false, '获取用户信息失败: ' . $res['errmsg']];
  71. }
  72. if(! empty($res['result'])){
  73. $result = $res['result'];
  74. DDEmployee::updateOrCreate(
  75. ['userid' => $result['userid'], 'login_type' => $LoginType],
  76. ['name' => $result['name'], 'userid' => $result['userid'], 'login_type' => $LoginType, 'u_userid' => $result['unionid']]
  77. );
  78. }
  79. return [true, $res];
  80. }
  81. private function getManDetail($user, $accessToken){
  82. // 3. 根据 userid 获取详细用户信息(包括部门)
  83. $urlDetail = "https://oapi.dingtalk.com/topapi/v2/user/get?access_token={$accessToken}";
  84. $respDetail = $this->curlOpen1($urlDetail, [
  85. 'request' => 'post',
  86. 'header' => ["Content-Type: application/json"],
  87. 'json' => ["userid" => $user['userid']]
  88. ]);
  89. $detail = json_decode($respDetail, true);
  90. if (!isset($detail['errcode'])) return [false, '获取用户详情接口异常: ' . $respDetail];
  91. if ($detail['errcode'] !== 0) return [false, '获取用户详情失败: ' . $detail['errmsg']];
  92. if (empty($detail['result'])) return [false, '获取用户详情失败,结果为空'];
  93. // 返回完整用户信息
  94. return [true, $detail['result']];
  95. }
  96. public function createProcessInstance($data, $user)
  97. {
  98. if(empty($data['type'])) return [false, '单据类型不能为空'];
  99. $type = $data['type'];
  100. if(empty($data['order_number'])) return [false,'单号不能为空'];
  101. [$success, $msg] = $this->checkCreateProcessInstance($data, $user);
  102. if(! $success) return [false, $msg];
  103. //获取模板id
  104. $code = $this->getModelCode($type, $user);
  105. //获取模板数据
  106. [$success, $formData] = $this->getFormData($data, $user);
  107. if(! $success) return [false, $formData];
  108. // 1. 获取 access_token
  109. [$success, $tokenData] = $this->getAccessToken($user['login_type']);
  110. if (!$success) return [false, $tokenData];
  111. $accessToken = $tokenData['access_token'];
  112. $userId = $user['userid'];
  113. [$success, $userDetail] = $this->getManDetail($user, $accessToken);
  114. if(!$success) return [false, $userDetail];
  115. //创建审批
  116. [$success, $msg] = $this->createFlow($accessToken, $code, $userId, $userDetail, $formData);
  117. if(! $success) return [false, $msg];
  118. //记录信息
  119. $this->recordDatabase($data, $user, $msg);
  120. return [true, ''];
  121. }
  122. private function recordDatabase($data, $user, $process_instance_id){
  123. $type = $data['type'];
  124. Record::insert([
  125. 'type' => $type,
  126. 'login_type' => $user['login_type'],
  127. 'database' => $user['zt_database'],
  128. 'userid' => $user['userid'],
  129. 'order_number'=> $data['order_number'],
  130. 'crt_time' => time(),
  131. 'process_instance_id' => $process_instance_id,
  132. 'state' => Record::state_zero
  133. ]);
  134. //保证
  135. U8State::updateOrCreate(
  136. ['order_number' => $data['order_number'], 'login_type' => $user['login_type'], 'type' => $type],
  137. ['state' => U8State::state_zero, 'userid' => $user['userid'],]
  138. );
  139. return [true, ''];
  140. }
  141. private function checkCreateProcessInstance($data, $user){
  142. list($status,$msg) = $this->limitingSendRequestBackgExpire($data['order_number'].$data['type'].$user['login_type']);
  143. if(! $status) return [false,$msg];
  144. $type = $data['type'];
  145. $login_type = $user['login_type'];
  146. // // 1. 获取该单号最新的一条记录
  147. // $lastRecord = Record::where('del_time', 0)
  148. // ->where('type', $type)
  149. // ->where('login_type', $login_type)
  150. // ->where('order_number', $data['order_number'])
  151. // ->latest('id') // 按 ID 倒序,取最新
  152. // ->first(['state']); // 只取状态字段
  153. //
  154. // // 2. 逻辑判断
  155. // if ($lastRecord) {
  156. // $text_o = Record::type_name[$type];
  157. // // 如果状态是 0 (待审核) 或 1 (审核通过),拦截
  158. // if (in_array($lastRecord->state, [Record::state_zero, Record::state_one])) {
  159. // $text = Record::state_name[$lastRecord->state];
  160. // return [false, $text_o . ' (' . $data['order_number'] . ') ' . $text . ',操作失败'];
  161. // }
  162. // }
  163. // 1. 获取该单号最新的一条记录
  164. $lastRecord = U8State::where('del_time', 0)
  165. ->where('type', $type)
  166. ->where('login_type', $login_type)
  167. ->where('order_number', $data['order_number'])
  168. ->first(['state']); // 只取状态字段
  169. // 2. 逻辑判断
  170. if ($lastRecord) {
  171. $text_o = U8State::type_name[$type];
  172. // 如果状态是 0 (待审核) 或 1 (审核通过),拦截
  173. if (in_array($lastRecord->state, [U8State::state_zero, U8State::state_one])) {
  174. $text = U8State::state_name[$lastRecord->state];
  175. return [false, $text_o . ' (' . $data['order_number'] . ') 处于' . $text . ',操作失败'];
  176. }
  177. }
  178. return [true, ''];
  179. }
  180. private function createFlow($accessToken, $code, $userId, $userDetail, $formData){
  181. // 2. 请求 URL
  182. $url = "https://oapi.dingtalk.com/topapi/processinstance/create?access_token={$accessToken}";
  183. // 3. 请求体
  184. $payload = [
  185. "process_code" => $code, // 审批模板编码
  186. "originator_user_id" => $userId, // 发起人 userId
  187. "dept_id" => $userDetail['dept_id_list'][0], // 发起人部门 ID
  188. "form_component_values" => $formData, // 表单数据
  189. ];
  190. // 4. 发送请求
  191. $resp = $this->curlOpen1($url, [
  192. 'request' => 'post',
  193. 'header' => [
  194. "Content-Type: application/json",
  195. ],
  196. 'json' => $payload
  197. ]);
  198. $res = json_decode($resp, true);
  199. if (!isset($res['errcode'])) {
  200. return [false, "接口返回异常: " . $resp];
  201. }
  202. if ($res['errcode'] !== 0) {
  203. return [false, "创建审批实例失败: " . $res['errmsg']];
  204. }
  205. return [true, $res['process_instance_id']];
  206. }
  207. //待审核
  208. public function getTodoProcessList($data, $user)
  209. {
  210. // 获取 AccessToken
  211. [$success, $tokenData] = $this->getAccessToken($user['login_type']);
  212. if (!$success) return [false, $tokenData];
  213. $accessToken = $tokenData['access_token'];
  214. // 新版接口分页参数(pageNumber 从 1 开始)
  215. $pageNumber = $data['page_index'] ?? 1;
  216. $pageSize = $data['page_size'] ?? 20;
  217. // 1. 请求 URL
  218. $url = "https://api.dingtalk.com/v1.0/workflow/processCentres/todoTasks?userId={$user['userid']}&pageNumber={$pageNumber}&pageSize={$pageSize}";
  219. // 2. 发送请求
  220. $resp = $this->curlOpen1($url, [
  221. 'request' => 'get', // 这个接口是 GET 请求
  222. 'header' => [
  223. "Content-Type: application/json",
  224. "x-acs-dingtalk-access-token: {$accessToken}" // 新版标准的 Header 鉴权
  225. ]
  226. ]);
  227. $res = json_decode($resp, true);
  228. // 3. 错误处理
  229. if (isset($res['code']) && $res['code'] !== 'success') return [false, "获取新版待办列表失败: " . ($res['message'] ?? '未知错误')];
  230. // 4. 提取实例 ID
  231. $result = $res['result'];
  232. $hasMore = $result['hasMore'];
  233. $tasks = $result['list'];
  234. $instanceIds = array_column($tasks, 'processInstanceId');
  235. if (empty($instanceIds)) {
  236. return [true, [
  237. 'list' => [],
  238. 'has_more' => $hasMore,
  239. ]];
  240. }
  241. $map = array_column($tasks,'taskId','processInstanceId');
  242. // 5. 关联本地
  243. $record = Record::where('del_time',0)
  244. ->whereIn('process_instance_id', $instanceIds)
  245. ->where('login_type', $user['login_type'])
  246. ->select('type', 'order_number', 'process_instance_id')
  247. ->get()
  248. ->toArray();
  249. foreach ($record as $key => $item) {
  250. $record[$key]['type_title'] = U8State::type_name[$item['type']];
  251. $record[$key]['task_id'] = $map[$item['process_instance_id']];
  252. }
  253. return [true, [
  254. 'list' => $record,
  255. 'has_more' => $hasMore
  256. ]];
  257. }
  258. //审核
  259. public function executeProcess($data, $user)
  260. {
  261. if(empty($data['result']) || ! in_array($data['result'], ['agree','refuse'])) return [false, 'result错误或不存在'];
  262. if(empty($data['task_id'])) return [false, 'task_id错误或不存在'];
  263. if(empty($data['process_instance_id'])) return [false, 'process_instance_id错误或不存在'];
  264. // 1. 获取 AccessToken
  265. [$success, $tokenData] = $this->getAccessToken($user['login_type']);
  266. if (!$success) return [false, $tokenData];
  267. $accessToken = $tokenData['access_token'];
  268. // 2. 接口 URL (新版 v1.0 路径)
  269. $url = "https://api.dingtalk.com/v1.0/workflow/processInstances/execute";
  270. // 3. 构建请求体
  271. // 注意:taskId 必须从待办任务接口中获取,不能随意填写
  272. $payload = [
  273. "processInstanceId" => $data['process_instance_id'], // 审批实例 ID
  274. "actionerUserId" => $user['userid'], // 操作人钉钉 ID
  275. "result" => $data['result'],
  276. "remark" => $data['remark'] ?? '同意。', // 审批意见
  277. "taskId" => $data['task_id'],
  278. ];
  279. // 如果有图片或附件,则加入 file 字段
  280. if (!empty($data['photos']) || !empty($data['attachments'])) {
  281. $payload['file'] = [
  282. "photos" => $data['photos'] ?? [],
  283. "attachments" => $data['attachments'] ?? []
  284. ];
  285. }
  286. // 4. 发送请求
  287. $resp = $this->curlOpen1($url, [
  288. 'request' => 'post',
  289. 'header' => [
  290. "Content-Type: application/json",
  291. "x-acs-dingtalk-access-token: {$accessToken}"
  292. ],
  293. 'json' => $payload
  294. ]);
  295. $res = json_decode($resp, true);
  296. // 5. 结果处理
  297. // 新版接口成功时通常返回空对象或 {"result": true}
  298. if (isset($res['code']) && $res['code'] !== 'success') {
  299. return [false, "审批操作失败: " . ($res['message'] ?? '未知错误')];
  300. }
  301. try {
  302. DB::beginTransaction();
  303. // 成功后续业务逻辑
  304. if(isset($res['success']) && $res['success'] == true) $this->updateDatabase($data, $user);
  305. DB::commit();
  306. }catch (\Throwable $exception){
  307. DB::rollBack();
  308. return [false, $exception->getMessage()];
  309. }
  310. return [true, ""];
  311. }
  312. private function updateDatabase($data, $user){
  313. $record = Record::where('login_type', $user['login_type'])
  314. ->where('process_instance_id')
  315. ->first();
  316. if(empty($record)) return [false, 'record记录不存在'];
  317. $record = $record->toArray();
  318. if($data['result'] == 'agree'){
  319. //通过
  320. Record::where('login_type', $user['login_type'])
  321. ->where('process_instance_id')
  322. ->update(['del_time' => 2]);
  323. ProcessDataJob::dispatch($record)->onQueue(Record::$job);
  324. }else{
  325. //驳回
  326. Record::where('login_type', $user['login_type'])
  327. ->where('process_instance_id')
  328. ->update(['del_time' => 1]);
  329. U8State::updateOrCreate(
  330. ['order_number' => $record['order_number'], 'login_type' => $user['login_type'], 'type' => $record['type']],
  331. ['state' => U8State::state_two]
  332. );
  333. }
  334. return [true, ''];
  335. }
  336. public function executeApproval($data, $user)
  337. {
  338. [$success, $tokenData] = $this->getAccessToken($user['login_type']);
  339. if (!$success) return [false, $tokenData];
  340. $accessToken = $tokenData['access_token'];
  341. $processInstanceId = $data['process_instance_id'] ?? 0;
  342. $action = $data['action'] ?? 'agree';
  343. $remark = $data['remark'] ?? '同意';
  344. // 1. 请求 URL
  345. $url = "https://oapi.dingtalk.com/topapi/process/instance/execute?access_token={$accessToken}";
  346. // 2. 构造请求体
  347. $payload = [
  348. "process_instance_id" => $processInstanceId,
  349. "remark" => $remark,
  350. "result" => $action, // 'agree' 或 'refuse'
  351. "task_id" => $this->getTaskIdByInstanceId($accessToken, $processInstanceId, $user['userid']), // 获取当前人的任务ID
  352. ];
  353. // 3. 发送请求
  354. $resp = $this->curlOpen1($url, [
  355. 'request' => 'post',
  356. 'header' => ["Content-Type: application/json"],
  357. 'json' => $payload
  358. ]);
  359. $res = json_decode($resp, true);
  360. if (!isset($res['errcode']) || $res['errcode'] !== 0) {
  361. return [false, "审批操作失败: " . ($res['errmsg'] ?? '未知错误')];
  362. }
  363. return [true, ""];
  364. }
  365. private function getTaskIdByInstanceId($accessToken, $instanceId, $userId)
  366. {
  367. $url = "https://oapi.dingtalk.com/topapi/processinstance/get?access_token={$accessToken}";
  368. $resp = $this->curlOpen1($url, [
  369. 'request' => 'post',
  370. 'header' => ["Content-Type: application/json"],
  371. 'json' => ["process_instance_id" => $instanceId]
  372. ]);
  373. $res = json_decode($resp, true);
  374. $tasks = $res['process_instance']['tasks'] ?? [];
  375. foreach ($tasks as $task) {
  376. // 状态为 RUNNING (进行中) 且 处理人是当前用户
  377. if ($task['task_status'] === 'RUNNING' && $task['userid'] === $userId) {
  378. return $task['task_id'];
  379. }
  380. }
  381. return null;
  382. }
  383. private function getModelCode($type, $user){
  384. $login_type = $user['login_type'];
  385. if($login_type == DDEmployee::type_one){
  386. //浙江
  387. if($type == 1){
  388. // 采购单
  389. $code = "PROC-B31B268E-6B7E-417D-A103-8765D8AE9090";
  390. }elseif ($type == 2){
  391. // 请购单
  392. $code = "PROC-C2F6D31A-979F-4038-B2A7-F2295A71702B";
  393. }elseif ($type == 3){
  394. // 采购入库单
  395. $code = "PROC-6DFEB04C-0E53-4430-B6F8-ED887E78EA9E";
  396. }elseif ($type == 4){
  397. // 存货
  398. $code = "PROC-9934D26D-D25D-408B-8B0E-FBD88EAB510D";
  399. }elseif ($type == 5){
  400. // 供应商
  401. $code = "PROC-30C00FA9-BD09-4139-92AD-220579109E07";
  402. }
  403. }elseif ($login_type == DDEmployee::type_two){
  404. //杭州
  405. if($type == 1){
  406. // 采购单
  407. $code = "PROC-DC4D5B5E-EF49-4230-A0A8-E6CCA55336DC";
  408. }elseif ($type == 2){
  409. // 请购单
  410. $code = "PROC-E44BE6B5-4530-42E1-A1A1-707708E45841";
  411. }elseif ($type == 3){
  412. // 采购入库单
  413. $code = "PROC-E166769C-4FEA-4D49-BD3C-F9183BD366F2";
  414. }elseif ($type == 4){
  415. // 存货
  416. $code = "PROC-229F1263-4180-4B50-A92B-749E699AB16E";
  417. }elseif ($type == 5){
  418. // 供应商
  419. $code = "PROC-D7304810-1767-4FA3-A62C-E791B5801715";
  420. }
  421. }
  422. return $code ?? "";
  423. }
  424. private function getFormData($data, $user){
  425. //cs
  426. // $formData = [ [ "name" => "订单日期", "value" => "2025-09-23" ], [ "name" => "订单编号", "value" => "PO20250923001" ], [ "name" => "业务类型", "value" => "标准采购" ], [ "name" => "供应商", "value" => "XX供应商有限公司" ], [ "name" => "制单人", "value" => "陈庆鹏" ], [ "name" => "表体", "value" => json_encode([ [ [ "name" => "存货名称", "value" => "打印机" ], [ "name" => "数量", "value" => "2" ], [ "name" => "主计量单位", "value" => "台" ], [ "name" => "原币价税合计", "value" => "3000" ] ], [ [ "name" => "存货名称", "value" => "显示器" ], [ "name" => "数量", "value" => "5" ], [ "name" => "主计量单位", "value" => "个" ], [ "name" => "原币价税合计", "value" => "5000" ] ] ], JSON_UNESCAPED_UNICODE) ] ];
  427. // return [true, $formData];
  428. //cs
  429. $service = new U8ServerService($user);
  430. $error = $service->getError();
  431. if(! empty($error)) return [false, $error];
  432. [$success, $order] = $service->getOrderDetails($data, $user);
  433. if(! $success) return [false, $order];
  434. $type = $data['type'];
  435. if($type == 1){
  436. // 采购单
  437. $formData = $this->typeOne($order);
  438. }elseif ($type == 2){
  439. // 采购请购单
  440. $formData = $this->typeTwo($order);
  441. }elseif($type == 3){
  442. // 采购入库单
  443. $formData = $this->typeThree($order);
  444. }elseif($type == 4){
  445. // 存货
  446. $formData = $this->typeFour($order);
  447. }elseif($type == 5){
  448. // 供应商
  449. $formData = $this->typeFive($order);
  450. }
  451. if(empty($formData)) return [false, '审批参数不能为空'];
  452. return [true, $formData];
  453. }
  454. private function typeOne($userOrder){
  455. if (empty($userOrder)) return [];
  456. $formData = [
  457. [
  458. "name" => "订单日期",
  459. "value" => date('Y-m-d', strtotime($userOrder['order_date'] ?? ''))
  460. ],
  461. [
  462. "name" => "订单编号",
  463. "value" => $userOrder['order_number'] ?? ''
  464. ],
  465. [
  466. "name" => "业务类型",
  467. "value" => $userOrder['business_type'] ?? ''
  468. ],
  469. [
  470. "name" => "供应商",
  471. "value" => $userOrder['supplier_title'] ?? ''
  472. ],
  473. [
  474. "name" => "制单人",
  475. "value" => $userOrder['crt_name'] ?? ''
  476. ],
  477. [
  478. "name" => "表体", // 对应 TableField 的 label
  479. "value" => json_encode(
  480. array_map(function($item){
  481. return [
  482. [
  483. "name" => "存货名称",
  484. "value" => $item['product_title'] ?? ''
  485. ],
  486. [
  487. "name" => "数量",
  488. "value" => $item['quantity'] ?? 0
  489. ],
  490. [
  491. "name" => "主计量", // 修改这里,对应模板字段
  492. "value" => $item['unit_title'] ?? ''
  493. ],
  494. [
  495. "name" => "原币价税合计",
  496. "value" => $item['amount'] ?? 0
  497. ]
  498. ];
  499. }, $userOrder['detail'] ?? []),
  500. JSON_UNESCAPED_UNICODE
  501. )
  502. ]
  503. ];
  504. return $formData;
  505. }
  506. private function typeTwo($userOrder){
  507. if (empty($userOrder)) return [];
  508. $formData = [
  509. [
  510. "name" => "单据号",
  511. "value" => $userOrder['order_number'] ?? ''
  512. ],
  513. [
  514. "name" => "日期",
  515. "value" => date('Y-m-d', strtotime($userOrder['order_date'] ?? ''))
  516. ],
  517. [
  518. "name" => "业务类型",
  519. "value" => $userOrder['business_type'] ?? ''
  520. ],
  521. [
  522. "name" => "请购人",
  523. "value" => $userOrder['purchase_name'] ?? ''
  524. ],
  525. [
  526. "name" => "制单人",
  527. "value" => $userOrder['crt_name'] ?? ''
  528. ],
  529. [
  530. "name" => "表体", // 对应 TableField 的 label
  531. "value" => json_encode(
  532. array_map(function($item){
  533. return [
  534. [
  535. "name" => "存货名称",
  536. "value" => $item['product_title'] ?? ''
  537. ],
  538. [
  539. "name" => "数量",
  540. "value" => $item['quantity'] ?? 0
  541. ],
  542. [
  543. "name" => "主计量", // 修改这里,对应模板字段
  544. "value" => $item['unit_title'] ?? ''
  545. ],
  546. [
  547. "name" => "要求到货日期",
  548. "value" => $item['need_arrived_date'] ?? ''
  549. ]
  550. ];
  551. }, $userOrder['detail'] ?? []),
  552. JSON_UNESCAPED_UNICODE
  553. )
  554. ]
  555. ];
  556. return $formData;
  557. }
  558. private function typeThree($userOrder){
  559. if (empty($userOrder)) return [];
  560. $formData = [
  561. [
  562. "name" => "入库单号",
  563. "value" => $userOrder['order_number'] ?? ''
  564. ],
  565. [
  566. "name" => "入库日期",
  567. "value" => date('Y-m-d', strtotime($userOrder['order_date'] ?? ''))
  568. ],
  569. [
  570. "name" => "仓库",
  571. "value" => $userOrder['warehouse_name'] ?? ''
  572. ],
  573. [
  574. "name" => "入库类别",
  575. "value" => $userOrder['rd_style_name'] ?? ''
  576. ],
  577. [
  578. "name" => "供货单位",
  579. "value" => $userOrder['supplier_title'] ?? ''
  580. ],
  581. [
  582. "name" => "制单人",
  583. "value" => $userOrder['crt_name'] ?? ''
  584. ],
  585. [
  586. "name" => "表体", // 对应 TableField 的 label
  587. "value" => json_encode(
  588. array_map(function($item){
  589. return [
  590. [
  591. "name" => "存货名称",
  592. "value" => $item['product_title'] ?? ''
  593. ],
  594. [
  595. "name" => "数量",
  596. "value" => $item['quantity'] ?? 0,
  597. ],
  598. [
  599. "name" => "主计量", // 修改这里,对应模板字段
  600. "value" => $item['unit_title'] ?? ''
  601. ],
  602. [
  603. "name" => "原币价税合计",
  604. "value" => $item['tax_amount'] ?? 0
  605. ]
  606. ];
  607. }, $userOrder['detail'] ?? []),
  608. JSON_UNESCAPED_UNICODE
  609. )
  610. ]
  611. ];
  612. return $formData;
  613. }
  614. private function typeFour($userOrder){
  615. if (empty($userOrder)) return [];
  616. $formData = [
  617. [
  618. "name" => "存货编码",
  619. "value" => $userOrder['code'] ?? ''
  620. ],
  621. [
  622. "name" => "存货名称",
  623. "value" => $userOrder['title'] ?? ''
  624. ],
  625. [
  626. "name" => "规格型号",
  627. "value" => $userOrder['size'] ?? ''
  628. ],
  629. [
  630. "name" => "计量单位组名",
  631. "value" => $userOrder['unit_group_code_title'] ?? ''
  632. ],
  633. [
  634. "name" => "主计量单位名",
  635. "value" => $userOrder['unit_code_title'] ?? ''
  636. ],
  637. [
  638. "name" => "生产企业",
  639. "value" => $userOrder['vendor_code_title'] ?? ''
  640. ],
  641. [
  642. "name" => "制单人",
  643. "value" => $userOrder['crt_name'] ?? ''
  644. ],
  645. ];
  646. return $formData;
  647. }
  648. private function typeFive($userOrder){
  649. if (empty($userOrder)) return [];
  650. $formData = [
  651. [
  652. "name" => "供应商编码",
  653. "value" => $userOrder['code'] ?? ''
  654. ],
  655. [
  656. "name" => "供应商名称",
  657. "value" => $userOrder['title'] ?? ''
  658. ],
  659. [
  660. "name" => "供应商简称",
  661. "value" => $userOrder['easy_title'] ?? ''
  662. ],
  663. [
  664. "name" => "制单人",
  665. "value" => $userOrder['crt_name'] ?? ''
  666. ],
  667. ];
  668. return $formData;
  669. }
  670. public function getTemplateFields($data)
  671. {
  672. $processCode = $data['code'] ?? "";
  673. if (empty($processCode)) {
  674. return [false, '模板编号 process_code 不能为空'];
  675. }
  676. [$ok, $tokenData] = $this->getAccessToken();
  677. if (! $ok) return [false, $tokenData];
  678. $accessToken = $tokenData['access_token'];
  679. // 注意这里是 GET,并且 processCode 是 query 参数
  680. $url = "https://api.dingtalk.com/v1.0/workflow/forms/schemas/processCodes?processCode={$processCode}";
  681. $resp = $this->curlOpen1($url, [
  682. 'request' => 'get',
  683. 'header' => [
  684. "Content-Type: application/json",
  685. "x-acs-dingtalk-access-token: {$accessToken}"
  686. ],
  687. ]);
  688. $res = json_decode($resp, true);
  689. if (isset($res['schemas'])) {
  690. return [true, $res['schemas']];
  691. } else {
  692. return [false, $res];
  693. }
  694. }
  695. protected function curlOpen1($url, $config = [])
  696. {
  697. $default = [
  698. 'post' => false,
  699. 'request' => 'get',
  700. 'header' => [],
  701. 'json' => null,
  702. 'timeout' => 30
  703. ];
  704. $arr = array_merge($default, $config);
  705. $ch = curl_init();
  706. curl_setopt($ch, CURLOPT_URL, $url);
  707. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  708. curl_setopt($ch, CURLOPT_TIMEOUT, $arr['timeout']);
  709. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  710. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  711. if (!empty($arr['header'])) {
  712. curl_setopt($ch, CURLOPT_HTTPHEADER, $arr['header']);
  713. }
  714. if ($arr['post'] || $arr['request'] !== 'get') {
  715. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($arr['request']));
  716. if ($arr['json'] !== null) {
  717. curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($arr['json'], JSON_UNESCAPED_UNICODE));
  718. }
  719. }
  720. $result = curl_exec($ch);
  721. if ($result === false) {
  722. $err = curl_error($ch);
  723. curl_close($ch);
  724. return json_encode(['error' => $err]);
  725. }
  726. curl_close($ch);
  727. return $result;
  728. }
  729. }