Payment.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. <?php
  2. namespace app\api\controller\product;
  3. use app\common\controller\Frontend;
  4. use app\common\service\PaymentFactory;
  5. use think\facade\Db;
  6. use think\facade\Log;
  7. class Payment extends Frontend
  8. {
  9. protected array $noNeedPermission = ['createQrCode', 'queryStatus', 'wechatNotify', 'alipayNotify', 'paymentSuccess'];
  10. protected array $noNeedLogin = ['*'];
  11. /**
  12. * 创建支付二维码
  13. */
  14. public function createQrCode()
  15. {
  16. $userId = $this->auth->id;
  17. $orderId = $this->request->post('order_id', 0);
  18. $payMethod = $this->request->post('pay_method', 'wechat');
  19. if (!$userId || $orderId <= 0) {
  20. $this->error('参数错误');
  21. }
  22. // 验证支付方式
  23. if (!in_array($payMethod, ['wechat', 'alipay'])) {
  24. $this->error('不支持的支付方式');
  25. }
  26. // 查询订单信息
  27. $order = Db::table('ba_order')
  28. ->where('id', $orderId)
  29. ->where('user_id', $userId)
  30. ->where('pay_status', 0) // 待支付
  31. ->find();
  32. if (!$order) {
  33. $this->error('订单不存在或已支付');
  34. }
  35. // 更新订单支付方式
  36. Db::table('ba_order')
  37. ->where('id', $orderId)
  38. ->update([
  39. 'pay_method' => $payMethod,
  40. 'update_time' => date('Y-m-d H:i:s')
  41. ]);
  42. // 检查是否已有支付记录
  43. $paymentRecord = Db::table('ba_payment_record')
  44. ->where('order_id', $orderId)
  45. ->where('status', 0) // 待支付
  46. ->find();
  47. if ($paymentRecord) {
  48. // 检查支付记录是否过期(15分钟)
  49. $createTime = strtotime($paymentRecord['create_time']);
  50. if (time() - $createTime < 900) { // 15分钟
  51. $this->success('', [
  52. 'order_no' => $order['order_no'],
  53. 'pay_amount' => $order['pay_amount'],
  54. 'qr_code_url' => $paymentRecord['qr_code_url'],
  55. 'qr_code_content' => $paymentRecord['qr_code_content'],
  56. 'payment_id' => $paymentRecord['id'],
  57. ]);
  58. }
  59. }
  60. // 查询订单商品信息
  61. $orderItem = Db::table('ba_order_item')
  62. ->where('order_id', $orderId)
  63. ->find();
  64. $subject = $orderItem['item_name'] ?? '商品购买';
  65. $body = $orderItem['spec_name'] ?? '';
  66. // 创建支付服务实例
  67. $paymentService = PaymentFactory::create($payMethod);
  68. // 生成支付二维码
  69. $qrCodeResult = $paymentService->createQrCode(
  70. $order['order_no'],
  71. $order['pay_amount'],
  72. $subject,
  73. $body
  74. );
  75. // 创建支付记录
  76. $payConfig = $paymentService->getPayConfig($payMethod); // 需要添加这个方法到BasePaymentService
  77. $paymentData = [
  78. 'order_id' => $orderId,
  79. 'order_no' => $order['order_no'],
  80. 'pay_config_id' => $payConfig['id'],
  81. 'pay_method' => $payMethod,
  82. 'pay_amount' => $order['pay_amount'],
  83. 'qr_code_url' => $qrCodeResult['qr_code_url'],
  84. 'qr_code_content' => $qrCodeResult['qr_code_content'],
  85. 'status' => 0,
  86. 'create_time' => date('Y-m-d H:i:s'),
  87. 'update_time' => date('Y-m-d H:i:s'),
  88. ];
  89. if ($paymentRecord) {
  90. // 更新现有记录
  91. Db::table('ba_payment_record')
  92. ->where('id', $paymentRecord['id'])
  93. ->update($paymentData);
  94. $paymentId = $paymentRecord['id'];
  95. } else {
  96. // 创建新记录
  97. $paymentId = Db::table('ba_payment_record')->insertGetId($paymentData);
  98. }
  99. $this->success('创建成功', [
  100. 'order_no' => $order['order_no'],
  101. 'pay_amount' => $order['pay_amount'],
  102. 'qr_code_url' => $qrCodeResult['qr_code_url'],
  103. 'qr_code_content' => $qrCodeResult['qr_code_content'],
  104. 'payment_id' => $paymentId,
  105. 'expire_time' => 900, // 15分钟过期
  106. ]);
  107. }
  108. /**
  109. * 查询支付状态
  110. */
  111. public function queryStatus()
  112. {
  113. try {
  114. $orderNo = $this->request->get('order_no', '');
  115. if (empty($orderNo)) {
  116. $this->error('订单号错误');
  117. }
  118. // 查询支付记录
  119. $paymentRecord = Db::table('ba_payment_record')
  120. ->where('order_no', $orderNo)
  121. ->find();
  122. if (!$paymentRecord) {
  123. $this->error('支付记录不存在');
  124. }
  125. // 如果已支付,直接返回
  126. if ($paymentRecord['status'] == 1) {
  127. $this->success('', [
  128. 'pay_status' => 1,
  129. 'pay_time' => $paymentRecord['pay_time'],
  130. 'trade_no' => $paymentRecord['trade_no'],
  131. 'message' => '支付成功',
  132. ]);
  133. }
  134. // 检查是否过期
  135. $createTime = strtotime($paymentRecord['create_time']);
  136. if (time() - $createTime > 900) { // 15分钟
  137. // 更新为已关闭
  138. Db::table('ba_payment_record')
  139. ->where('id', $paymentRecord['id'])
  140. ->update([
  141. 'status' => 3,
  142. 'update_time' => date('Y-m-d H:i:s'),
  143. ]);
  144. $this->success('', [
  145. 'pay_status' => 3,
  146. 'message' => '支付已超时',
  147. ]);
  148. }
  149. // 查询支付配置
  150. $payConfig = Db::table('ba_pay_config')
  151. ->where('id', $paymentRecord['pay_config_id'])
  152. ->find();
  153. if (!$payConfig) {
  154. $this->error('支付配置不存在');
  155. }
  156. // 查询支付平台
  157. $paymentService = PaymentFactory::create($paymentRecord['pay_method'], $payConfig['id']);
  158. $queryResult = $paymentService->queryOrder($orderNo);
  159. // 如果支付成功,处理支付成功逻辑
  160. if ($queryResult['pay_status'] == 1) {
  161. $this->processPaymentSuccess(
  162. $orderNo,
  163. $queryResult['trade_no'],
  164. $queryResult['pay_amount'],
  165. $queryResult['pay_time']
  166. );
  167. }
  168. $this->success('', [
  169. 'pay_status' => $queryResult['pay_status'],
  170. 'pay_time' => $queryResult['pay_time'] ?? null,
  171. 'trade_no' => $queryResult['trade_no'] ?? null,
  172. 'message' => $this->getStatusMessage($queryResult['pay_status']),
  173. ]);
  174. } catch (\Throwable $e) {
  175. \think\facade\Log::error('查询支付状态失败: ' . $e->getMessage());
  176. $this->error('查询支付状态失败: ' . $e->getMessage());
  177. }
  178. }
  179. /**
  180. * 微信支付回调
  181. */
  182. public function wechatNotify()
  183. {
  184. try {
  185. // 获取原始输入
  186. $input = file_get_contents('php://input');
  187. if (empty($input)) {
  188. throw new \Exception('回调数据为空');
  189. }
  190. Log::info('微信支付回调数据: ' . $input);
  191. // 解析XML
  192. $data = $this->xmlToArray($input);
  193. $orderNo = $data['out_trade_no'] ?? '';
  194. if (empty($orderNo)) {
  195. throw new \Exception('未找到订单号');
  196. }
  197. // 使用事务和锁处理回调
  198. Db::startTrans();
  199. try {
  200. // 使用行锁检查支付记录
  201. $paymentRecord = Db::table('ba_payment_record')
  202. ->where('order_no', $orderNo)
  203. ->lock(true)
  204. ->find();
  205. if (!$paymentRecord) {
  206. throw new \Exception('支付记录不存在: ' . $orderNo);
  207. }
  208. // 如果已处理,直接返回成功响应(幂等性)
  209. if ($paymentRecord['status'] == 1) {
  210. Db::rollback();
  211. Log::info('微信支付回调幂等性检查:订单已处理,直接返回成功', [
  212. 'order_no' => $orderNo,
  213. 'payment_record_id' => $paymentRecord['id']
  214. ]);
  215. $response = [
  216. 'return_code' => 'SUCCESS',
  217. 'return_msg' => 'OK',
  218. ];
  219. echo $this->arrayToXml($response);
  220. return;
  221. }
  222. // 查询支付配置
  223. $payConfig = Db::table('ba_pay_config')
  224. ->where('id', $paymentRecord['pay_config_id'])
  225. ->find();
  226. if (!$payConfig) {
  227. throw new \Exception('支付配置不存在');
  228. }
  229. // 处理回调
  230. $paymentService = PaymentFactory::create('wechat', $payConfig['id']);
  231. $result = $paymentService->handleNotify($input);
  232. // 处理支付成功
  233. $this->processPaymentSuccess(
  234. $result['order_no'],
  235. $result['trade_no'],
  236. $result['pay_amount'],
  237. $result['pay_time']
  238. );
  239. Db::commit();
  240. // 返回成功响应给微信
  241. $response = [
  242. 'return_code' => 'SUCCESS',
  243. 'return_msg' => 'OK',
  244. ];
  245. echo $this->arrayToXml($response);
  246. } catch (\Throwable $e) {
  247. Db::rollback();
  248. throw $e;
  249. }
  250. } catch (\Throwable $e) {
  251. Log::error('微信支付回调处理失败: ' . $e->getMessage());
  252. $response = [
  253. 'return_code' => 'FAIL',
  254. 'return_msg' => $e->getMessage(),
  255. ];
  256. echo $this->arrayToXml($response);
  257. }
  258. }
  259. /**
  260. * 支付宝支付回调
  261. */
  262. public function alipayNotify()
  263. {
  264. try {
  265. $input = $_POST;
  266. if (empty($input)) {
  267. throw new \Exception('回调数据为空');
  268. }
  269. // 记录回调日志
  270. \think\facade\Log::info('支付宝支付回调数据: ' . json_encode($input));
  271. $orderNo = $input['out_trade_no'] ?? '';
  272. if (empty($orderNo)) {
  273. throw new \Exception('未找到订单号');
  274. }
  275. // 幂等性检查:检查订单是否已处理
  276. $processedOrder = Db::table('ba_payment_record')
  277. ->where('order_no', $orderNo)
  278. ->where('status', 1) // 已支付
  279. ->find();
  280. if ($processedOrder) {
  281. // 如果已处理,直接返回成功响应
  282. \think\facade\Log::info('支付宝支付回调幂等性检查:订单已处理,直接返回成功', [
  283. 'order_no' => $orderNo,
  284. 'payment_record_id' => $processedOrder['id']
  285. ]);
  286. echo 'success';
  287. return;
  288. }
  289. // 查询支付记录
  290. $paymentRecord = Db::table('ba_payment_record')
  291. ->where('order_no', $orderNo)
  292. ->find();
  293. if (!$paymentRecord) {
  294. throw new \Exception('支付记录不存在: ' . $orderNo);
  295. }
  296. // 查询支付配置
  297. $payConfig = Db::table('ba_pay_config')
  298. ->where('id', $paymentRecord['pay_config_id'])
  299. ->find();
  300. if (!$payConfig) {
  301. throw new \Exception('支付配置不存在');
  302. }
  303. // 处理回调
  304. $paymentService = PaymentFactory::create('alipay', $payConfig['id']);
  305. $result = $paymentService->handleNotify($input);
  306. // 处理支付成功
  307. $this->processPaymentSuccess(
  308. $result['order_no'],
  309. $result['trade_no'],
  310. $result['pay_amount'],
  311. $result['pay_time']
  312. );
  313. echo 'success';
  314. } catch (\Throwable $e) {
  315. \think\facade\Log::error('支付宝支付回调处理失败: ' . $e->getMessage());
  316. echo 'fail';
  317. }
  318. }
  319. /**
  320. * 处理支付成功
  321. */
  322. private function processPaymentSuccess($orderNo, $tradeNo, $payAmount, $payTime)
  323. {
  324. // 使用数据库事务和锁来确保幂等性
  325. Db::startTrans();
  326. try {
  327. // 1. 使用行锁检查订单是否已处理
  328. $order = Db::table('ba_order')
  329. ->where('order_no', $orderNo)
  330. ->lock(true) // 行锁
  331. ->find();
  332. if (!$order) {
  333. Db::rollback();
  334. Log::error('订单不存在: ' . $orderNo);
  335. throw new \Exception('订单不存在');
  336. }
  337. // 2. 如果订单已支付,直接返回(幂等性)
  338. if ($order['pay_status'] == 1) {
  339. Db::rollback();
  340. Log::info('订单已支付,跳过处理(幂等性)', ['order_no' => $orderNo]);
  341. return;
  342. }
  343. // 3. 更新支付记录
  344. Db::table('ba_payment_record')
  345. ->where('order_no', $orderNo)
  346. ->where('status', 0) // 只更新待支付的记录
  347. ->update([
  348. 'status' => 1,
  349. 'trade_no' => $tradeNo,
  350. 'pay_time' => $payTime,
  351. 'notify_time' => date('Y-m-d H:i:s'),
  352. 'update_time' => date('Y-m-d H:i:s'),
  353. ]);
  354. // 4. 更新订单状态
  355. Db::table('ba_order')
  356. ->where('order_no', $orderNo)
  357. ->where('pay_status', 0) // 只更新未支付的订单
  358. ->update([
  359. 'pay_status' => 1,
  360. 'pay_time' => $payTime,
  361. 'order_status' => 1,
  362. 'update_time' => date('Y-m-d H:i:s'),
  363. ]);
  364. // 5. 获取更新后的订单
  365. $order = Db::table('ba_order')
  366. ->where('order_no', $orderNo)
  367. ->find();
  368. // 6. 获取订单商品
  369. $orderItems = Db::table('ba_order_item')
  370. ->where('order_id', $order['id'])
  371. ->select();
  372. // 7. 创建用户产品服务实例
  373. $userProductService = new \app\common\service\UserProductService();
  374. $companyPackageService = new \app\common\service\CompanyPackageService();
  375. foreach ($orderItems as $item) {
  376. // 8. 使用事务和锁检查是否已存在使用记录
  377. $existingUsage = Db::table('ba_user_product_usage')
  378. ->where('order_item_id', $item['id'])
  379. ->where('user_id', $order['user_id'])
  380. ->lock(true)
  381. ->find();
  382. if ($existingUsage) {
  383. Log::info('订单商品已处理,跳过', [
  384. 'order_item_id' => $item['id'],
  385. 'order_id' => $order['id']
  386. ]);
  387. continue;
  388. }
  389. // 9. 获取套餐信息
  390. $package = Db::table('ba_package_price')
  391. ->where('id', $item['package_price_id'])
  392. ->find();
  393. if (!$package) {
  394. throw new \Exception('套餐不存在: ' . $item['package_price_id']);
  395. }
  396. Log::info('处理订单商品', [
  397. 'order_item_id' => $item['id'],
  398. 'product_id' => $item['product_id'],
  399. 'package_id' => $item['package_price_id'],
  400. 'package_type' => $package['type'],
  401. 'card_type' => $package['card_type'],
  402. 'quantity' => $item['quantity']
  403. ]);
  404. if ($package['type'] == 1) {
  405. // 个人套餐:立即创建使用记录
  406. $userProductService->createUserProductRecord($order['user_id'], $item, $package);
  407. } else {
  408. // 企业套餐:创建席位记录
  409. // 首先验证企业购买
  410. $companyInfo = $companyPackageService->getUserCompany($order['user_id']);
  411. if (!$companyInfo) {
  412. throw new \Exception('企业套餐购买失败:用户不属于任何企业');
  413. }
  414. // 创建企业套餐席位记录
  415. $companyPackageService->processCompanyPackagePurchase(
  416. $order['id'],
  417. $companyInfo['id'],
  418. $package['id'],
  419. $item['quantity'],
  420. $package['card_type'],
  421. $package['unit_value'],
  422. $package['points_value']
  423. );
  424. // 企业套餐购买后,自动分配一个席位给购买者
  425. $companyPackageService->autoAllocateToBuyer(
  426. $order['user_id'],
  427. $companyInfo['id'],
  428. $package['id'],
  429. $order['id']
  430. );
  431. // 注意:这里不再重复创建用户产品记录!
  432. // 因为 autoAllocateToBuyer 中已经会创建
  433. // 只有次卡/积分卡需要在购买时也创建使用记录
  434. // if ($package['card_type'] == 1 || $package['card_type'] == 2) {
  435. // // 使用事务安全的方式创建
  436. // $this->createUserProductRecordSafe($order['user_id'], $item, $package);
  437. // }
  438. }
  439. // 10. 扣减库存
  440. $this->deductStockAfterPayment($order['id']);
  441. }
  442. // 11. 处理分销佣金
  443. if ($order['is_commission'] == 0 && $order['distributor_id'] && $order['commission_amount'] > 0) {
  444. $this->processCommission($order);
  445. }
  446. Db::commit();
  447. Log::info('支付成功处理完成', ['order_no' => $orderNo]);
  448. } catch (\Throwable $e) {
  449. Db::rollback();
  450. Log::error('处理支付成功失败: ' . $e->getMessage());
  451. throw $e;
  452. }
  453. }
  454. /**
  455. * 安全创建用户产品记录(防止重复)
  456. */
  457. private function createUserProductRecordSafe($userId, $orderItem, $package)
  458. {
  459. try {
  460. // 检查是否已存在
  461. $existing = Db::table('ba_user_product_usage')
  462. ->where('order_item_id', $orderItem['id'])
  463. ->where('user_id', $userId)
  464. ->find();
  465. if ($existing) {
  466. Log::info('用户产品记录已存在,跳过创建', [
  467. 'order_item_id' => $orderItem['id'],
  468. 'user_id' => $userId
  469. ]);
  470. return $existing['id'];
  471. }
  472. // 创建用户产品服务实例
  473. $userProductService = new \app\common\service\UserProductService();
  474. return $userProductService->createUserProductRecord($userId, $orderItem, $package);
  475. } catch (\Throwable $e) {
  476. Log::error('创建用户产品记录失败: ' . $e->getMessage());
  477. // 不抛出异常,避免影响主流程
  478. return false;
  479. }
  480. }
  481. /**
  482. * 支付成功后扣减库存
  483. */
  484. private function deductStockAfterPayment($orderId)
  485. {
  486. try {
  487. // 查询订单商品
  488. $orderItem = Db::table('ba_order_item')
  489. ->where('order_id', $orderId)
  490. ->find();
  491. if (!$orderItem) {
  492. return;
  493. }
  494. // 扣减套餐库存
  495. if ($orderItem['package_price_id']) {
  496. $result = Db::table('ba_package_price')
  497. ->where('id', $orderItem['package_price_id'])
  498. ->where('stock', '>=', $orderItem['quantity']) // 再次检查库存
  499. ->dec('stock', $orderItem['quantity'])
  500. ->inc('sold_count', $orderItem['quantity'])
  501. ->update();
  502. if (!$result) {
  503. // 库存不足,需要处理异常(退款或补货)
  504. \think\facade\Log::error('支付成功后库存不足,订单ID:' . $orderId);
  505. // 这里可以触发退款逻辑或人工处理
  506. }
  507. }
  508. // 如果有产品库存,也可以在这里扣减
  509. if ($orderItem['product_id']) {
  510. // 如果产品表有库存字段,在这里扣减
  511. // Db::table('ba_product')...
  512. }
  513. } catch (\Throwable $e) {
  514. \think\facade\Log::error('扣减库存失败: ' . $e->getMessage());
  515. }
  516. }
  517. /**
  518. * 处理分销佣金
  519. */
  520. private function processCommission($order)
  521. {
  522. try {
  523. // 调用佣金服务
  524. $commissionService = new \app\common\service\CommissionService();
  525. $result = $commissionService->updateDistributionRecord($order['id'], 2, $order['pay_time']);
  526. if ($result) {
  527. // 2. 更新分销人的佣金统计
  528. $commissionService->updateUserCommission($order['distributor_id']);
  529. // 3. 标记订单佣金已处理
  530. Db::table('ba_order')
  531. ->where('id', $order['id'])
  532. ->update([
  533. 'is_commission' => 1,
  534. 'update_time' => date('Y-m-d H:i:s')
  535. ]);
  536. }
  537. } catch (\Throwable $e) {
  538. \think\facade\Log::error('处理分销佣金失败: ' . $e->getMessage());
  539. }
  540. }
  541. /**
  542. * 确认订单库存
  543. */
  544. private function confirmOrderStock($orderId)
  545. {
  546. try {
  547. // 查询订单商品
  548. $orderItems = Db::table('ba_order_item')
  549. ->where('order_id', $orderId)
  550. ->select();
  551. foreach ($orderItems as $item) {
  552. if ($item['package_price_id']) {
  553. // 可以在这里记录库存确认,或者更新相关状态
  554. // 由于创建订单时已经扣减库存,这里主要做确认处理
  555. }
  556. }
  557. } catch (\Throwable $e) {
  558. \think\facade\Log::error('确认订单库存失败: ' . $e->getMessage());
  559. }
  560. }
  561. /**
  562. * 发送支付成功通知
  563. */
  564. private function sendPaymentSuccessNotify($order)
  565. {
  566. // 这里可以实现发送微信模板消息、短信、站内信等通知
  567. // 根据业务需求实现
  568. \think\facade\Log::info('支付成功通知: 订单号=' . $order['order_no'] . ', 用户ID=' . $order['user_id']);
  569. }
  570. /**
  571. * 获取状态消息
  572. */
  573. private function getStatusMessage($status)
  574. {
  575. $messages = [
  576. 0 => '等待支付',
  577. 1 => '支付成功',
  578. 2 => '支付失败',
  579. 3 => '支付已关闭',
  580. ];
  581. return $messages[$status] ?? '未知状态';
  582. }
  583. /**
  584. * XML转数组
  585. */
  586. private function xmlToArray($xml)
  587. {
  588. if (empty($xml)) {
  589. return [];
  590. }
  591. try {
  592. // 使用DOMDocument方式,兼容PHP 8.0+
  593. $dom = new \DOMDocument();
  594. if (version_compare(PHP_VERSION, '8.0.0', '>=')) {
  595. // PHP 8.0+ 直接加载
  596. $dom->loadXML($xml, LIBXML_NOENT | LIBXML_NOCDATA);
  597. } else {
  598. // PHP 8.0以下需要禁用实体加载器
  599. $previous = libxml_disable_entity_loader(true);
  600. $dom->loadXML($xml, LIBXML_NOENT | LIBXML_NOCDATA);
  601. libxml_disable_entity_loader($previous);
  602. }
  603. // 简单的转换方法
  604. $data = [];
  605. foreach ($dom->documentElement->childNodes as $node) {
  606. if ($node->nodeType == XML_ELEMENT_NODE) {
  607. $data[$node->nodeName] = $node->nodeValue;
  608. }
  609. }
  610. return $data;
  611. } catch (\Exception $e) {
  612. \think\facade\Log::error('XML解析失败: ' . $e->getMessage());
  613. return [];
  614. }
  615. }
  616. /**
  617. * 数组转XML
  618. */
  619. private function arrayToXml($arr)
  620. {
  621. $xml = '<xml>';
  622. foreach ($arr as $key => $val) {
  623. if (is_numeric($val)) {
  624. $xml .= "<{$key}>{$val}</{$key}>";
  625. } else {
  626. $xml .= "<{$key}><![CDATA[{$val}]]></{$key}>";
  627. }
  628. }
  629. $xml .= '</xml>';
  630. return $xml;
  631. }
  632. }