Delivery.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2024 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. (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. // $this->sendDeliveryMessage([$completed]);
  76. });
  77. return true;
  78. }
  79. /**
  80. * 设置默认的参数
  81. * @param array $param
  82. * @return array
  83. */
  84. private function buildParam(array $param): array
  85. {
  86. return helper::setQueryDefaultValue($param, [
  87. // 发货方式 (10手动录入 20无需物流)
  88. 'deliveryMethod' => self::DELIVERY_METHOD_MANUAL,
  89. // 物流公司ID
  90. 'expressId' => 0,
  91. // 物流单号
  92. 'expressNo' => '',
  93. // 整单发货
  94. 'isAllPack' => false,
  95. // 发货的商品
  96. 'packGoodsData' => [],
  97. // 同步至微信小程序《发货信息录入》
  98. 'syncMpWeixinShipping' => 1,
  99. ]);
  100. }
  101. /**
  102. * 订单发货事件
  103. * @param $order
  104. * @param array $param 发货参数
  105. * @return bool
  106. */
  107. public function deliveryEvent($order, array $param): bool
  108. {
  109. // 默认参数
  110. $param = helper::setQueryDefaultValue($param, [
  111. // 发货方式 (10手动录入 20无需物流 30电子面单)
  112. 'deliveryMethod' => self::DELIVERY_METHOD_MANUAL,
  113. // 物流公司ID
  114. 'expressId' => 0,
  115. // 物流单号
  116. 'expressNo' => '',
  117. // 为整单发货
  118. 'isAllPack' => false,
  119. // 发货的商品 (整单发货时无需传入)
  120. 'packGoodsData' => [],
  121. // 电子面单内容
  122. 'eorderHtml' => ''
  123. ]);
  124. // 整单发货时获取所有未发货的商品
  125. $param['isAllPack'] && $param['packGoodsData'] = $this->getAllPackGoods($order);
  126. // 实物订单
  127. if ($order['order_type'] == OrderTypeEnum::PHYSICAL) {
  128. // 写入发货单
  129. $this->recordDeliveryOrder($order, $param);
  130. // 判断订单是否已全部发货
  131. $deliveredAll = $this->checkDeliveredAll($order['goods'], $param);
  132. // 更新订单的发货状态
  133. $deliveryStatus = $deliveredAll ? DeliveryStatusEnum::DELIVERED : DeliveryStatusEnum::PART_DELIVERED;
  134. $this->updateDeliveryStatus([$order['order_id']], $deliveryStatus);
  135. }
  136. return true;
  137. }
  138. /**
  139. * 整单发货时获取订单所有未发货的商品
  140. * @param $order
  141. * @return array
  142. */
  143. private function getAllPackGoods($order): array
  144. {
  145. $data = [];
  146. foreach ($order['goods'] as $goods) {
  147. if ($goods['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
  148. $data[] = [
  149. 'orderGoodsId' => $goods['order_goods_id'],
  150. 'deliveryNum' => $goods['total_num'] - $goods['delivery_num'],
  151. ];
  152. }
  153. }
  154. return $data;
  155. }
  156. /**
  157. * 写入发货单
  158. * @param $order
  159. * @param array $param
  160. */
  161. private function recordDeliveryOrder($order, array $param)
  162. {
  163. // 新增发货单记录
  164. $DeliveryModel = new DeliveryModel;
  165. $DeliveryModel->save([
  166. 'order_id' => $order['order_id'],
  167. 'delivery_method' => $param['deliveryMethod'],
  168. 'express_id' => $param['expressId'],
  169. 'express_no' => $param['deliveryMethod'] == self::DELIVERY_METHOD_NONE ? '' : $param['expressNo'],
  170. 'eorder_html' => $param['eorderHtml'] ?? '',
  171. 'store_id' => $this->getStoreId(),
  172. ]);
  173. // 新增发货单商品记录
  174. $this->recordRelivery($order, $param['packGoodsData'], (int)$DeliveryModel['delivery_id']);
  175. // 更新订单商品记录
  176. $this->updateOrderGoods($order, $param['packGoodsData']);
  177. }
  178. /**
  179. * 新增发货单商品记录
  180. * @param $order
  181. * @param array $packGoodsData
  182. * @param int $deliveryId
  183. */
  184. private function recordRelivery($order, array $packGoodsData, int $deliveryId)
  185. {
  186. // 整理发货单记录
  187. $dataset = [];
  188. foreach ($packGoodsData as $item) {
  189. $goods = helper::arraySearch($order['goods'], 'order_goods_id', $item['orderGoodsId']);
  190. !empty($goods) && $dataset[] = [
  191. 'delivery_id' => $deliveryId,
  192. 'order_id' => $order['order_id'],
  193. 'order_goods_id' => $item['orderGoodsId'],
  194. 'goods_id' => $goods['goods_id'],
  195. 'delivery_num' => $item['deliveryNum'],
  196. 'store_id' => $this->getStoreId(),
  197. ];
  198. }
  199. (new DeliveryGoodsModel)->addAll($dataset);
  200. }
  201. /**
  202. * 更新订单商品记录
  203. * @param $order
  204. * @param array $packGoodsData
  205. */
  206. private function updateOrderGoods($order, array $packGoodsData)
  207. {
  208. // 更新订单商品记录
  209. $dataset = [];
  210. foreach ($order['goods'] as $goods) {
  211. // 发货的订单商品数据
  212. $item = helper::arraySearch($packGoodsData, 'orderGoodsId', $goods['order_goods_id']);
  213. if (!$item) {
  214. continue;
  215. }
  216. // 判断订单商品的发货数量是否已完成
  217. $newDeliveryNum = $item['deliveryNum'] + $goods['delivery_num'];
  218. // 记录更新内容
  219. $dataset[] = [
  220. 'where' => ['order_goods_id' => $goods['order_goods_id']],
  221. 'data' => [
  222. 'delivery_num' => $newDeliveryNum,
  223. 'delivery_status' => $newDeliveryNum >= $goods['total_num'] ? DeliveryStatusEnum::DELIVERED
  224. : DeliveryStatusEnum::PART_DELIVERED
  225. ]
  226. ];
  227. }
  228. (new OrderGoodsModel)->updateAll($dataset);
  229. }
  230. /**
  231. * 判断订单是否已全部发货
  232. * @param $orderGoodsList
  233. * @param array $param
  234. * @return bool
  235. */
  236. private function checkDeliveredAll($orderGoodsList, array $param): bool
  237. {
  238. foreach ($orderGoodsList as $goods) {
  239. // 查询商品是否在表单中
  240. $packGoods = helper::arraySearch($param['packGoodsData'], 'orderGoodsId', $goods['order_goods_id']);
  241. if (!empty($packGoods)) {
  242. // 判断订单商品的发货数量是否满足
  243. if (($packGoods['deliveryNum'] + $goods['delivery_num']) < $goods['total_num']) {
  244. return false;
  245. }
  246. } else {
  247. // 判断订单商品的发货状态
  248. if ($goods['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
  249. return false;
  250. }
  251. }
  252. }
  253. return true;
  254. }
  255. /**
  256. * 确认发货后发送消息通知
  257. * @param $orderList
  258. * @return void
  259. */
  260. private function sendDeliveryMessage($orderList): void
  261. {
  262. // 发送消息通知
  263. foreach ($orderList as $item) {
  264. MessageService::send('order.delivery', ['order' => $item], $this->getStoreId());
  265. }
  266. }
  267. /**
  268. * 更新订单发货状态(批量)
  269. * @param array $orderIds
  270. * @param int $deliveryStatus 发货状态
  271. */
  272. private function updateDeliveryStatus(array $orderIds, int $deliveryStatus): void
  273. {
  274. // 整理更新的数据
  275. $data = [];
  276. foreach ($orderIds as $orderId) {
  277. $data[] = [
  278. 'data' => [
  279. 'delivery_status' => $deliveryStatus,
  280. 'delivery_time' => time(),
  281. ],
  282. 'where' => ['order_id' => $orderId]
  283. ];
  284. }
  285. // 批量更新
  286. (new OrderModel)->updateAll($data);
  287. }
  288. /**
  289. * 验证订单是否满足发货条件
  290. * @param $orderList
  291. * @return bool
  292. */
  293. public function verifyDelivery($orderList): bool
  294. {
  295. foreach ($orderList as $order) {
  296. $orderSource = OrderSourceFactory::getFactory($order['order_source']);
  297. if (!$orderSource->checkOrderByDelivery($order)) {
  298. $this->error = $orderSource->getError();
  299. return false;
  300. }
  301. }
  302. return true;
  303. }
  304. }