DingService.php 33 KB

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