DingService.php 39 KB

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