Delivery.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2025 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\store\service\order;
  13. use think\facade\Db;
  14. use app\store\model\Order as OrderModel;
  15. use app\store\model\Express as ExpressModel;
  16. use app\store\model\OrderGoods as OrderGoodsModel;
  17. use app\store\model\order\Delivery as DeliveryModel;
  18. use app\store\model\order\DeliveryGoods as DeliveryGoodsModel;
  19. use app\common\service\BaseService;
  20. use app\common\service\Message as MessageService;
  21. use app\common\service\order\Shipping as ShippingService;
  22. use app\common\service\order\source\Factory as OrderSourceFactory;
  23. use cores\exception\BaseException;
  24. use app\common\enum\order\{OrderType as OrderTypeEnum, DeliveryStatus as DeliveryStatusEnum};
  25. use app\common\library\helper;
  26. use app\common\library\FileLocal;
  27. use app\common\library\phpoffice\ReadExecl;
  28. /**
  29. * 服务层:订单发货事件
  30. * Class Delivery
  31. * @package app\store\service\order
  32. */
  33. class Delivery extends BaseService
  34. {
  35. // 发货方式: 手动录入
  36. const DELIVERY_METHOD_MANUAL = 10;
  37. // 发货方式: 无需物流
  38. const DELIVERY_METHOD_NONE = 20;
  39. /**
  40. * 手动发货
  41. * @param int $orderId
  42. * @param array $param
  43. * @return bool
  44. */
  45. public function delivery(int $orderId, array $param): bool
  46. {
  47. // 设置默认的参数
  48. $param = $this->buildParam($param);
  49. // 获取订单详情
  50. $detail = OrderModel::detail($orderId);
  51. // 验证订单是否满足发货条件
  52. if (!$this->verifyDelivery([$detail])) {
  53. return false;
  54. }
  55. Db::transaction(function () use ($detail, $param, $orderId) {
  56. // 订单发货事件
  57. $this->deliveryEvent($detail, $param);
  58. // 获取已发货的订单
  59. $completed = OrderModel::detail($orderId, ['goods', 'trade']);
  60. // 发货信息同步微信平台
  61. $syncStatus = (new ShippingService)->syncMpWeixinShipping($completed, [
  62. // 同步至微信小程序《发货信息管理》
  63. 'syncMpWeixinShipping' => $param['syncMpWeixinShipping'],
  64. // 物流模式:1物流配送 3虚拟商品 4用户自提
  65. 'logisticsType' => [
  66. self::DELIVERY_METHOD_MANUAL => ShippingService::DELIVERY_EXPRESS,
  67. self::DELIVERY_METHOD_NONE => ShippingService::DELIVERY_VIRTUAL
  68. ][$param['deliveryMethod']],
  69. // 物流公司ID
  70. 'expressId' => $param['expressId'],
  71. // 物流单号
  72. 'expressNo' => $param['expressNo'],
  73. ]);
  74. // 记录是否已同步微信小程序发货信息管理
  75. OrderModel::updateSyncWeixinShipping($orderId, $syncStatus);
  76. // 发送消息通知 [未实现]
  77. // $this->sendDeliveryMessage([$completed]);
  78. });
  79. return true;
  80. }
  81. /**
  82. * 设置默认的参数
  83. * @param array $param
  84. * @return array
  85. */
  86. private function buildParam(array $param): array
  87. {
  88. return helper::setQueryDefaultValue($param, [
  89. // 发货方式 (10手动录入 20无需物流)
  90. 'deliveryMethod' => self::DELIVERY_METHOD_MANUAL,
  91. // 物流公司ID
  92. 'expressId' => 0,
  93. // 物流单号
  94. 'expressNo' => '',
  95. // 整单发货
  96. 'isAllPack' => false,
  97. // 发货的商品
  98. 'packGoodsData' => [],
  99. // 同步至微信小程序《发货信息管理》
  100. 'syncMpWeixinShipping' => 1,
  101. ]);
  102. }
  103. /**
  104. * 订单发货事件
  105. * @param $order
  106. * @param array $param 发货参数
  107. * @return bool
  108. */
  109. public function deliveryEvent($order, array $param): bool
  110. {
  111. // 默认参数
  112. $param = helper::setQueryDefaultValue($param, [
  113. // 发货方式 (10手动录入 20无需物流 30电子面单)
  114. 'deliveryMethod' => self::DELIVERY_METHOD_MANUAL,
  115. // 物流公司ID
  116. 'expressId' => 0,
  117. // 物流单号
  118. 'expressNo' => '',
  119. // 为整单发货
  120. 'isAllPack' => false,
  121. // 发货的商品 (整单发货时无需传入)
  122. 'packGoodsData' => [],
  123. // 电子面单内容
  124. 'eorderHtml' => ''
  125. ]);
  126. // 整单发货时获取所有未发货的商品
  127. $param['isAllPack'] && $param['packGoodsData'] = $this->getAllPackGoods($order);
  128. // 实物订单
  129. if ($order['order_type'] == OrderTypeEnum::PHYSICAL) {
  130. // 写入发货单
  131. $this->recordDeliveryOrder($order, $param);
  132. // 判断订单是否已全部发货
  133. $deliveredAll = $this->checkDeliveredAll($order['goods'], $param);
  134. // 更新订单的发货状态
  135. $deliveryStatus = $deliveredAll ? DeliveryStatusEnum::DELIVERED : DeliveryStatusEnum::PART_DELIVERED;
  136. $this->updateDeliveryStatus([$order['order_id']], $deliveryStatus);
  137. }
  138. return true;
  139. }
  140. /**
  141. * 整单发货时获取订单所有未发货的商品
  142. * @param $order
  143. * @return array
  144. */
  145. private function getAllPackGoods($order): array
  146. {
  147. $data = [];
  148. foreach ($order['goods'] as $goods) {
  149. if ($goods['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
  150. $data[] = [
  151. 'orderGoodsId' => $goods['order_goods_id'],
  152. 'deliveryNum' => $goods['total_num'] - $goods['delivery_num'],
  153. ];
  154. }
  155. }
  156. return $data;
  157. }
  158. /**
  159. * 写入发货单
  160. * @param $order
  161. * @param array $param
  162. */
  163. private function recordDeliveryOrder($order, array $param)
  164. {
  165. // 新增发货单记录
  166. $DeliveryModel = new DeliveryModel;
  167. $DeliveryModel->save([
  168. 'order_id' => $order['order_id'],
  169. 'delivery_method' => $param['deliveryMethod'],
  170. 'express_id' => $param['expressId'],
  171. 'express_no' => $param['deliveryMethod'] == self::DELIVERY_METHOD_NONE ? '' : $param['expressNo'],
  172. 'eorder_html' => $param['eorderHtml'] ?? '',
  173. 'store_id' => $this->getStoreId(),
  174. ]);
  175. // 新增发货单商品记录
  176. $this->recordRelivery($order, $param['packGoodsData'], (int)$DeliveryModel['delivery_id']);
  177. // 更新订单商品记录
  178. $this->updateOrderGoods($order, $param['packGoodsData']);
  179. }
  180. /**
  181. * 新增发货单商品记录
  182. * @param $order
  183. * @param array $packGoodsData
  184. * @param int $deliveryId
  185. */
  186. private function recordRelivery($order, array $packGoodsData, int $deliveryId)
  187. {
  188. // 整理发货单记录
  189. $dataset = [];
  190. foreach ($packGoodsData as $item) {
  191. $goods = helper::arraySearch($order['goods'], 'order_goods_id', $item['orderGoodsId']);
  192. !empty($goods) && $dataset[] = [
  193. 'delivery_id' => $deliveryId,
  194. 'order_id' => $order['order_id'],
  195. 'order_goods_id' => $item['orderGoodsId'],
  196. 'goods_id' => $goods['goods_id'],
  197. 'delivery_num' => $item['deliveryNum'],
  198. 'store_id' => $this->getStoreId(),
  199. ];
  200. }
  201. (new DeliveryGoodsModel)->addAll($dataset);
  202. }
  203. /**
  204. * 更新订单商品记录
  205. * @param $order
  206. * @param array $packGoodsData
  207. */
  208. private function updateOrderGoods($order, array $packGoodsData)
  209. {
  210. // 更新订单商品记录
  211. $dataset = [];
  212. foreach ($order['goods'] as $goods) {
  213. // 发货的订单商品数据
  214. $item = helper::arraySearch($packGoodsData, 'orderGoodsId', $goods['order_goods_id']);
  215. if (!$item) {
  216. continue;
  217. }
  218. // 判断订单商品的发货数量是否已完成
  219. $newDeliveryNum = $item['deliveryNum'] + $goods['delivery_num'];
  220. // 记录更新内容
  221. $dataset[] = [
  222. 'where' => ['order_goods_id' => $goods['order_goods_id']],
  223. 'data' => [
  224. 'delivery_num' => $newDeliveryNum,
  225. 'delivery_status' => $newDeliveryNum >= $goods['total_num'] ? DeliveryStatusEnum::DELIVERED
  226. : DeliveryStatusEnum::PART_DELIVERED
  227. ]
  228. ];
  229. }
  230. (new OrderGoodsModel)->updateAll($dataset);
  231. }
  232. /**
  233. * 判断订单是否已全部发货
  234. * @param $orderGoodsList
  235. * @param array $param
  236. * @return bool
  237. */
  238. private function checkDeliveredAll($orderGoodsList, array $param): bool
  239. {
  240. foreach ($orderGoodsList as $goods) {
  241. // 查询商品是否在表单中
  242. $packGoods = helper::arraySearch($param['packGoodsData'], 'orderGoodsId', $goods['order_goods_id']);
  243. if (!empty($packGoods)) {
  244. // 判断订单商品的发货数量是否满足
  245. if (($packGoods['deliveryNum'] + $goods['delivery_num']) < $goods['total_num']) {
  246. return false;
  247. }
  248. } else {
  249. // 判断订单商品的发货状态
  250. if ($goods['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
  251. return false;
  252. }
  253. }
  254. }
  255. return true;
  256. }
  257. /**
  258. * 确认发货后发送消息通知
  259. * @param $orderList
  260. * @return void
  261. */
  262. private function sendDeliveryMessage($orderList): void
  263. {
  264. // 发送消息通知
  265. foreach ($orderList as $item) {
  266. MessageService::send('order.delivery', ['order' => $item], $this->getStoreId());
  267. }
  268. }
  269. /**
  270. * 更新订单发货状态(批量)
  271. * @param array $orderIds
  272. * @param int $deliveryStatus 发货状态
  273. */
  274. private function updateDeliveryStatus(array $orderIds, int $deliveryStatus): void
  275. {
  276. // 整理更新的数据
  277. $data = [];
  278. foreach ($orderIds as $orderId) {
  279. $data[] = [
  280. 'data' => [
  281. 'delivery_status' => $deliveryStatus,
  282. 'delivery_time' => time(),
  283. ],
  284. 'where' => ['order_id' => $orderId]
  285. ];
  286. }
  287. // 批量更新
  288. (new OrderModel)->updateAll($data);
  289. }
  290. /**
  291. * 验证订单是否满足发货条件
  292. * @param $orderList
  293. * @return bool
  294. */
  295. public function verifyDelivery($orderList): bool
  296. {
  297. foreach ($orderList as $order) {
  298. $orderSource = OrderSourceFactory::getFactory($order['order_source']);
  299. if (!$orderSource->checkOrderByDelivery($order)) {
  300. $this->error = $orderSource->getError();
  301. return false;
  302. }
  303. }
  304. return true;
  305. }
  306. }