OrderService.php 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  1. <?php
  2. namespace app\common\Service;
  3. use app\common\Enum\GoodsEnum;
  4. use app\common\model\Order;
  5. use app\common\model\OrderGoods;
  6. use app\common\model\Address;
  7. use app\common\model\UserCoupon;
  8. use app\common\model\Goods;
  9. use app\common\model\Sku;
  10. use app\common\model\Freight;
  11. use think\Db;
  12. use think\Exception;
  13. use app\common\model\OrderAddress;
  14. use app\common\Enum\OrderEnum;
  15. use app\common\exception\BusinessException;
  16. use app\common\Service\Order\OrderActionService;
  17. use app\common\Enum\OrderActionEnum;
  18. use app\common\Service\SkuSpec;
  19. use app\common\Service\ParentOrderService;
  20. use app\common\Service\DiscountService;
  21. /**
  22. * 订单服务类
  23. * 封装订单创建相关逻辑
  24. *
  25. * 新增功能:
  26. * - createSingleGoodsOrders(): 创建单商品订单,一个商品一个订单
  27. *
  28. * 使用示例:
  29. *
  30. * // 创建单商品订单(一个商品一个订单)
  31. * $goodsList = [
  32. * ['goods_id' => 1, 'goods_sku_id' => 10, 'nums' => 2],
  33. * ['goods_id' => 2, 'goods_sku_id' => 20, 'nums' => 1],
  34. * ['goods_id' => 3, 'goods_sku_id' => 30, 'nums' => 3]
  35. * ];
  36. * $orders = OrderService::createSingleGoodsOrders($addressId, $userId, $goodsList, $userCouponId, $remark);
  37. * // 返回3个订单对象的数组,每个商品对应一个订单
  38. *
  39. * // 创建传统订单(多商品合并为一个订单)
  40. * $order = OrderService::createOrder($addressId, $userId, $goodsList, $userCouponId, $remark);
  41. * // 返回1个包含所有商品的订单对象
  42. */
  43. class OrderService
  44. {
  45. /**
  46. * 订单金额
  47. * @var array
  48. */
  49. protected static $orderPrice = [
  50. 'amount' => 0, //订单应付金额
  51. 'goods_price' => 0, //商品总费用
  52. 'goods_num' => 0, //商品数量
  53. 'discount_fee' => 0, //优惠金额
  54. 'coupon_discount_fee' => 0, //优惠券金额
  55. 'promo_discount_fee' => 0, //营销金额
  56. 'total_discount_amount' => 0, //总优惠
  57. 'order_amount' => 0, //订单金额
  58. 'pay_amount' => 0, //实际付款金额
  59. 'pay_original_amount' => 0, //原始支付金额
  60. 'pay_remain_amount' => 0, //剩余支付金额
  61. 'express_fee' => 0, //配送费用
  62. 'activity_discount_amount' => 0, //活动折扣金额(新增)
  63. 'goods_original_price' => 0, //商品原价总额(新增)
  64. 'order_snapshot' => null, //订单快照,记录折扣等详细信息
  65. ];
  66. /**
  67. * 初始化订单价格
  68. * @return array
  69. */
  70. protected static function initOrderPrice()
  71. {
  72. return [
  73. 'amount' => 0, //订单应付金额
  74. 'goods_price' => 0, //商品总费用
  75. 'goods_num' => 0, //商品数量
  76. 'discount_fee' => 0, //优惠金额
  77. 'coupon_discount_fee' => 0, //优惠券金额
  78. 'promo_discount_fee' => 0, //营销金额
  79. 'total_discount_amount' => 0, //总优惠
  80. 'order_amount' => 0, //订单金额
  81. 'pay_amount' => 0, //实际付款金额
  82. 'pay_original_amount' => 0, //原始支付金额
  83. 'pay_remain_amount' => 0, //剩余支付金额
  84. 'express_fee' => 0, //配送费用
  85. 'activity_discount_amount' => 0, //活动折扣金额(新增)
  86. 'goods_original_price' => 0, //商品原价总额(新增)
  87. 'order_snapshot' => null, //订单快照,记录折扣等详细信息
  88. ];
  89. }
  90. /**
  91. * 根据商品列表计算订单明细
  92. * @param array $orderInfo 订单基础信息
  93. * @param array $goods_list 商品列表
  94. * @param int $user_id 用户ID
  95. * @param int $area_id 地区ID
  96. * @param int $user_coupon_id 优惠券ID
  97. * @return array
  98. * @throws
  99. */
  100. protected static function computeGoods(&$orderInfo, $goodsList, $userId, $areaId, $userCouponId = 0)
  101. {
  102. // 初始化订单价格
  103. $orderPrice = self::initOrderPrice();
  104. foreach ($orderPrice as $key => $value) {
  105. $orderInfo[$key] = $value;
  106. }
  107. $orderItem = [];
  108. $shippingTemp = [];
  109. $userCoupon = null;
  110. $processedGoodsList = []; // 处理后的商品列表,避免与参数$goodsList冲突
  111. // 校验优惠券
  112. if ($userCouponId) {
  113. $userCouponModel = new UserCoupon();
  114. $userCoupon = $userCouponModel->checkUserOrUse($userCouponId, $userId);
  115. $orderInfo['user_coupon_id'] = $userCouponId;
  116. }
  117. // 提取所有商品ID和SKU ID,进行批量查询
  118. $goodsIds = array_column($goodsList, 'goods_id');
  119. $skuIds = [];
  120. foreach ($goodsList as $index => $item) {
  121. if (isset($item['goods_sku_id']) && $item['goods_sku_id'] > 0) {
  122. $skuIds[] = $item['goods_sku_id'];
  123. }
  124. }
  125. // 批量查询商品信息
  126. $goodsData = [];
  127. if (!empty($goodsIds)) {
  128. $goodsModel = new Goods();
  129. $goodsCollection = $goodsModel->with(['brand'])
  130. ->where('id', 'in', $goodsIds)
  131. ->where('status', GoodsEnum::STATUS_ON_SALE)
  132. ->select();
  133. foreach ($goodsCollection as $goods) {
  134. $goodsData[$goods->id] = $goods;
  135. }
  136. }
  137. // 批量查询SKU信息
  138. $skuData = [];
  139. $multiSpecSkuIds = []; // 用于存储多规格商品的SKU ID
  140. if (!empty($skuIds)) {
  141. $skuModel = new Sku();
  142. $skuCollection = $skuModel->where('id', 'in', $skuIds)->select();
  143. foreach ($skuCollection as $sku) {
  144. $skuData[$sku->id] = $sku;
  145. // 过滤出有规格值的SKU ID(spec_value_ids不为空)
  146. if (!empty($sku->spec_value_ids)) {
  147. $multiSpecSkuIds[] = $sku->id;
  148. }
  149. }
  150. }
  151. // 批量查询规格属性字符串(只查询多规格商品的SKU)
  152. $skuAttrData = [];
  153. if (!empty($multiSpecSkuIds)) {
  154. $skuAttrData = SkuSpec::getSkuAttrs($multiSpecSkuIds);
  155. }
  156. // 批量查询商品的折扣信息
  157. $goodsDiscountData = [];
  158. $orderSnapshotData = []; // 用于收集订单快照信息
  159. if (!empty($goodsIds)) {
  160. $goodsDiscountData = DiscountService::getBatchGoodsDiscountInfo($goodsIds);
  161. }
  162. // echo "<pre>";
  163. // print_r($goodsDiscountData);
  164. // echo "</pre>";
  165. // exit;
  166. // 验证并构建商品数据
  167. foreach ($goodsList as $item) {
  168. $goods_id = $item['goods_id'];
  169. $goods_sku_id = $item['goods_sku_id']; // 现在所有商品都应该有SKU ID
  170. $nums = $item['nums'];
  171. if ($nums <= 0) {
  172. throw new BusinessException("商品数量必须大于0");
  173. }
  174. // 检查商品是否存在
  175. if (!isset($goodsData[$goods_id])) {
  176. throw new BusinessException("商品已下架");
  177. }
  178. $goods = $goodsData[$goods_id];
  179. // 所有商品都必须有SKU(包括单规格商品的默认SKU)
  180. if (empty($skuData) || !isset($skuData[$goods_sku_id])) {
  181. throw new BusinessException("商品规格不存在");
  182. }
  183. $sku = $skuData[$goods_sku_id];
  184. // 验证SKU是否属于该商品
  185. if ($sku->goods_id != $goods_id) {
  186. throw new BusinessException("商品规格不匹配");
  187. }
  188. // 获取规格属性字符串(单规格商品的sku_attr为空)
  189. $sku_attr = $skuAttrData[$goods_sku_id] ?? '';
  190. // 构建商品对象,模拟购物车数据结构
  191. $goodsItem = (object)[
  192. 'goods_id' => $goods_id,
  193. 'goods_sku_id' => $goods_sku_id,
  194. 'nums' => $nums,
  195. 'goods' => $goods,
  196. 'sku' => $sku,
  197. 'sku_attr' => $sku_attr
  198. ];
  199. $processedGoodsList[] = $goodsItem;
  200. }
  201. // 计算商品价格和运费(统一使用SKU进行计算)
  202. foreach ($processedGoodsList as $item) {
  203. $goodsItemData = [];
  204. if (empty($item->goods) || empty($item->sku)) {
  205. throw new BusinessException("商品已下架");
  206. }
  207. // 库存验证(统一使用SKU库存)
  208. if ($item->sku->stocks < $item->nums) {
  209. throw new BusinessException("商品库存不足,请重新修改数量");
  210. }
  211. // 获取商品折扣信息
  212. $discountInfo = null;
  213. $actualPrice = $item->sku->price; // 默认使用原价
  214. $originalPrice = $item->sku->price;
  215. if (isset($goodsDiscountData[$item->goods_id])) {
  216. // 查找对应SKU的折扣信息
  217. foreach ($goodsDiscountData[$item->goods_id] as $discount) {
  218. if ($discount['sku_id'] == $item->sku->id) {
  219. $discountInfo = $discount;
  220. $actualPrice = $discount['discount_price']; // 使用折扣价格
  221. break;
  222. }
  223. }
  224. }
  225. // 计算金额
  226. $amount = bcmul($actualPrice, $item->nums, 2); // 使用实际价格计算
  227. $originalAmount = bcmul($originalPrice, $item->nums, 2); // 原价总额
  228. // 统一使用SKU数据进行计算
  229. $goodsItemData['image'] = !empty($item->sku->image) ? $item->sku->image : $item->goods->image;
  230. $goodsItemData['price'] = $actualPrice; // 使用实际价格(折扣价或原价)
  231. $goodsItemData['original_price'] = $originalPrice; // 原价
  232. $goodsItemData['sku_sn'] = $item->sku->sku_sn;
  233. $goodsItemData['discount_info'] = $discountInfo; // 折扣信息
  234. $goodsItemData['amount'] = $amount;
  235. $goodsItemData['original_amount'] = $originalAmount;
  236. // 订单应付金额
  237. $orderInfo['amount'] = bcadd($orderInfo['amount'], $amount, 2);
  238. // 商品总价
  239. $orderInfo['goods_price'] = bcadd($orderInfo['goods_original_price'], $originalAmount, 2);;
  240. // 商品原价总额
  241. $orderInfo['goods_original_price'] = bcadd($orderInfo['goods_original_price'], $originalAmount, 2);
  242. // 商品数量累计
  243. $orderInfo['goods_num'] += $item->nums;
  244. // 活动折扣金额累计
  245. if ($discountInfo) {
  246. $discountAmount = bcsub($originalAmount, $amount, 2);
  247. // 确保折扣金额不为负数,如果计算结果为负数则设为0
  248. $discountAmount = bccomp($discountAmount, '0', 2) >= 0 ? $discountAmount : '0';
  249. $orderInfo['activity_discount_amount'] = bcadd($orderInfo['activity_discount_amount'], $discountAmount, 2);
  250. // 收集折扣信息到订单快照
  251. $orderSnapshotData['discount_activities'][] = [
  252. 'goods_id' => $item->goods_id,
  253. 'goods_title' => $item->goods->title,
  254. 'sku_id' => $item->sku->id,
  255. 'sku_attr' => $item->sku_attr,
  256. 'activity_id' => $discountInfo['activity_id'],
  257. 'activity_name' => $discountInfo['activity_name'],
  258. 'original_price' => $originalPrice,
  259. 'discount_price' => $actualPrice,
  260. 'discount' => $discountInfo['discount'],
  261. 'discount_amount' => $discountAmount,
  262. 'quantity' => $item->nums,
  263. 'total_discount_amount' => $discountAmount,
  264. ];
  265. }
  266. $freight_id = $item->goods->express_template_id;
  267. // 计算邮费【合并运费模板】
  268. if (!isset($shippingTemp[$freight_id])) {
  269. $shippingTemp[$freight_id] = [
  270. 'nums' => $item->nums,
  271. 'weight' => $item->sku->weight,
  272. 'amount' => $amount
  273. ];
  274. } else {
  275. $shippingTemp[$freight_id] = [
  276. 'nums' => bcadd($shippingTemp[$freight_id]['nums'], $item->nums, 2),
  277. 'weight' => bcadd($shippingTemp[$freight_id]['weight'], $item->sku->weight, 2),
  278. 'amount' => bcadd($shippingTemp[$freight_id]['amount'], $amount, 2)
  279. ];
  280. }
  281. // 创建订单商品数据 (基于正确的表结构)
  282. $orderItemData = [
  283. 'user_id' => $userId,
  284. 'order_sn' => '', // 将在订单创建后补充
  285. 'goods_sn' => $item->goods->goods_sn ?: '', // 商品货号
  286. 'sku_sn' => $item->sku->sku_sn ?: '', // SKU编号
  287. 'goods_type' => $item->goods->type ?: 0, // 商品类型
  288. 'goods_id' => $item->goods->id,
  289. 'goods_sku_attr' => $item->sku_attr,
  290. 'goods_spec_value_ids' => $item->sku->spec_value_ids,
  291. 'goods_sku_id' => $item->sku->id,
  292. 'goods_title' => $item->goods->title,
  293. 'goods_market_price' => $item->sku->market_price ?: 0, // 市场价
  294. 'goods_original_price' => $originalPrice, // 商品原价
  295. 'goods_price' => $amount, // 实付金额(折扣价)
  296. 'goods_image' => $goodsItemData['image'],
  297. 'goods_weight' => $item->sku->weight ?: 0,
  298. 'nums' => $item->nums,
  299. 'sale_status' => 0, // 销售状态:0=待申请
  300. 'comment_status' => 0, // 评论状态:0=未评论
  301. 'status' => 1, // 状态
  302. 'supplier_id' => $item->goods->supplier_id ?: 0,
  303. 'inspection_type_id' => $item->goods->inspection_type_id ?: 0,
  304. ];
  305. // 如果有折扣信息,记录活动相关信息
  306. // if ($discountInfo) {
  307. // $orderItemData['activity_id'] = $discountInfo['activity_id'] ?? 0;
  308. // $orderItemData['activity_name'] = $discountInfo['activity_name'] ?? '';
  309. // $orderItemData['discount_amount'] = bcsub($originalAmount, $amount, 2);
  310. // }
  311. $orderItem[] = $orderItemData;
  312. }
  313. // 按运费模板计算
  314. foreach ($shippingTemp as $key => $item) {
  315. $shippingfee = Freight::calculate($key, $areaId, $item['nums'], $item['weight'], $item['amount']);
  316. $orderInfo['express_fee'] = bcadd($orderInfo['express_fee'], $shippingfee, 2);
  317. }
  318. // 订单金额(商品价格+运费)
  319. $orderInfo['order_amount'] = bcadd($orderInfo['goods_price'], $orderInfo['express_fee'], 2);
  320. // 订单应付金额(暂时等于订单金额,后续会减去优惠)
  321. $orderInfo['amount'] = $orderInfo['order_amount'];
  322. // if (!empty($userCoupon)) {
  323. // // 校验优惠券
  324. // $goods_ids = array_column($orderItem, 'goods_id');
  325. // $category_ids = array_column($orderItem, 'category_id');
  326. // $brand_ids = array_column($orderItem, 'brand_id');
  327. // $couponModel = new Coupon();
  328. // $coupon = $couponModel->getCoupon($userCoupon['coupon_id'])
  329. // ->checkCoupon()
  330. // ->checkOpen()
  331. // ->checkUseTime($userCoupon['createtime'])
  332. // ->checkConditionGoods($goods_ids, $userId, $category_ids, $brand_ids);
  333. // // 计算折扣金额,判断是使用不含运费,还是含运费的金额
  334. // $amount = !isset($config['shippingfeecoupon']) || $config['shippingfeecoupon'] == 0 ? $orderInfo['goods_price'] : $orderInfo['order_amount'];
  335. // list($new_money, $coupon_money) = $coupon->doBuy($amount);
  336. // // 判断优惠金额是否超出总价,超出则直接设定优惠金额为总价
  337. // $orderInfo['coupon_discount_fee'] = $coupon_money > $amount ? $amount : $coupon_money;
  338. // $orderInfo['discount_fee'] = $orderInfo['coupon_discount_fee'];
  339. // }
  340. // 汇总所有折扣金额
  341. $orderInfo['discount_fee'] = bcadd($orderInfo['activity_discount_amount'], $orderInfo['coupon_discount_fee'], 2);
  342. $orderInfo['promo_discount_fee'] = $orderInfo['activity_discount_amount']; // 营销金额 = 活动折扣金额
  343. $orderInfo['total_discount_amount'] = $orderInfo['discount_fee']; // 总优惠金额
  344. // 计算最终应付金额【订单金额减去折扣】
  345. $orderInfo['amount'] = max(0, bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2));
  346. $orderInfo['pay_amount'] = $orderInfo['amount']; // 实际付款金额等于应付金额
  347. // 保存订单快照信息
  348. if (!empty($orderSnapshotData)) {
  349. $orderSnapshotData['order_summary'] = [
  350. 'total_goods_original_price' => $orderInfo['goods_original_price'],
  351. 'total_goods_price' => $orderInfo['goods_price'],
  352. 'total_activity_discount' => $orderInfo['activity_discount_amount'],
  353. 'total_coupon_discount' => $orderInfo['coupon_discount_fee'],
  354. 'total_discount' => $orderInfo['discount_fee'],
  355. 'express_fee' => $orderInfo['express_fee'],
  356. 'final_amount' => $orderInfo['amount'],
  357. 'snapshot_time' => time(),
  358. ];
  359. $orderInfo['order_snapshot'] = json_encode($orderSnapshotData, JSON_UNESCAPED_UNICODE);
  360. }
  361. return [
  362. $orderItem,
  363. $processedGoodsList,
  364. $userCoupon
  365. ];
  366. }
  367. /**
  368. * 统一的创建订单方法
  369. * @param int $address_id 地址ID
  370. * @param int $user_id 用户ID
  371. * @param array $goods_list 标准化的商品列表
  372. * @param int $user_coupon_id 优惠券ID
  373. * @param string $memo 备注
  374. * @param array $cart_ids 购物车ID数组(如果是购物车模式需要清空)
  375. * @return Order
  376. * @throws BusinessException
  377. */
  378. public static function createOrder($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '',$supplierId = 0)
  379. {
  380. $address = Address::get($addressId);
  381. if (!$address || $address['user_id'] != $userId) {
  382. throw new BusinessException("地址未找到");
  383. }
  384. if (empty($goodsList)) {
  385. throw new BusinessException("商品列表不能为空");
  386. }
  387. $orderTimeout = ShopConfigService::getConfigs('shop.order.order_timeout',false) ?? 3600;
  388. $orderSn = date("Ymdhis") . sprintf("%08d", $userId) . mt_rand(1000, 9999);
  389. // 订单主表信息 (基于新表结构)
  390. $orderInfo = array_merge(self::initOrderPrice(), [
  391. 'type' => 1, // 1:普通订单
  392. 'order_sn' => $orderSn,
  393. 'user_id' => $userId,
  394. 'expire_time' => time() + $orderTimeout, // 过期时间
  395. 'order_status' => OrderEnum::STATUS_CREATE, // 待付款
  396. 'invoice_status' => 0, // 发票开具状态
  397. 'remark' => $remark, // 用户备注
  398. 'user_coupon_id' => $userCouponId ?: null,
  399. 'ip' => request()->ip(), // IP地址
  400. 'status' => 'normal',
  401. ]);
  402. $orderInfo['platform'] = request()->header('platform', 'H5');
  403. // 通过商品列表计算订单明细
  404. list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods($orderInfo, $goodsList, $userId, $address->area_id, $userCouponId);
  405. // 如果有活动折扣,记录活动信息
  406. if ($orderInfo['activity_discount_amount'] > 0) {
  407. $currentActivity = DiscountService::getCurrentActivity();
  408. if ($currentActivity) {
  409. $orderInfo['activity_type'] = 'discount';
  410. $orderInfo['activity_id'] = $currentActivity['id'];
  411. }
  412. }
  413. $orderInfo['pay_amount'] = max(0, bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2));
  414. $orderInfo['pay_original_amount'] = $orderInfo['pay_amount'];
  415. $orderInfo['pay_remain_amount'] = $orderInfo['pay_amount'];
  416. // echo "<pre>";
  417. // print_r($orderInfo);
  418. // echo "</pre>";
  419. // exit;
  420. // 创建订单
  421. $order = self::createOrderWithTransaction($orderInfo, $orderItem, $calculatedGoodsList, $userCoupon, $address);
  422. return $order;
  423. }
  424. /**
  425. * 创建单商品订单 - 一个商品一个订单
  426. * @param int $addressId 地址ID
  427. * @param int $userId 用户ID
  428. * @param array $goodsList 商品列表(支持多商品但会拆分为多个订单)
  429. * @param int $userCouponId 优惠券ID(仅用于第一个订单)
  430. * @param string $remark 备注
  431. * @return array 返回创建的订单数组
  432. * @throws BusinessException
  433. */
  434. public static function createSingleGoodsOrders($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '')
  435. {
  436. // 验证地址
  437. $address = Address::get($addressId);
  438. if (!$address || $address['user_id'] != $userId) {
  439. throw new BusinessException("地址未找到");
  440. }
  441. if (empty($goodsList)) {
  442. throw new BusinessException("商品列表不能为空");
  443. }
  444. // 验证商品列表格式
  445. self::validateGoodsList($goodsList);
  446. $orderTimeout = ShopConfigService::getConfigs('shop.order.order_timeout', false) ?? 3600;
  447. $platform = request()->header('platform', 'H5');
  448. $ip = request()->ip();
  449. $orders = [];
  450. $firstOrder = true;
  451. // 为每个商品创建单独的订单
  452. foreach ($goodsList as $goodsItem) {
  453. // 构建单商品列表
  454. $singleGoodsList = [$goodsItem];
  455. // 生成订单号
  456. $orderSn = date("Ymdhis") . sprintf("%08d", $userId) . mt_rand(1000, 9999);
  457. // 订单主表信息
  458. $orderInfo = array_merge(self::initOrderPrice(), [
  459. 'type' => 1, // 1:普通订单
  460. 'order_sn' => $orderSn,
  461. 'user_id' => $userId,
  462. 'expire_time' => time() + $orderTimeout, // 过期时间
  463. 'order_status' => OrderEnum::STATUS_CREATE, // 待付款
  464. 'invoice_status' => 0, // 发票开具状态
  465. 'remark' => $remark, // 用户备注
  466. 'user_coupon_id' => $firstOrder && $userCouponId > 0 ? $userCouponId : null, // 优惠券仅用于第一个订单
  467. 'ip' => $ip,
  468. 'status' => 'normal',
  469. 'platform' => $platform,
  470. ]);
  471. try {
  472. // 通过单商品列表计算订单明细
  473. list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods(
  474. $orderInfo,
  475. $singleGoodsList,
  476. $userId,
  477. $address->area_id,
  478. $firstOrder && $userCouponId > 0 ? $userCouponId : 0
  479. );
  480. // 如果有活动折扣,记录活动信息
  481. if ($orderInfo['activity_discount_amount'] > 0) {
  482. $currentActivity = DiscountService::getCurrentActivity();
  483. if ($currentActivity) {
  484. $orderInfo['activity_type'] = 'discount';
  485. $orderInfo['activity_id'] = $currentActivity['id'];
  486. }
  487. }
  488. $orderInfo['pay_amount'] = max(0, bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2));
  489. $orderInfo['pay_original_amount'] = $orderInfo['pay_amount'];
  490. $orderInfo['pay_remain_amount'] = $orderInfo['pay_amount'];
  491. // 创建订单
  492. $order = self::createOrderWithTransaction($orderInfo, $orderItem, $calculatedGoodsList, $userCoupon, $address);
  493. $orders[] = $order;
  494. // 第一个订单创建完成后,后续订单不再使用优惠券
  495. $firstOrder = false;
  496. } catch (Exception $e) {
  497. // 如果某个订单创建失败,记录错误并继续创建其他订单
  498. \think\Log::error("创建单商品订单失败: " . $e->getMessage() . ", 商品ID: " . $goodsItem['goods_id']);
  499. throw new BusinessException("创建订单失败: " . $e->getMessage());
  500. }
  501. }
  502. if (empty($orders)) {
  503. throw new BusinessException("所有订单创建失败");
  504. }
  505. return $orders;
  506. }
  507. /**
  508. * 在事务中创建订单
  509. * @param array $orderInfo 订单信息
  510. * @param array $orderItem 订单商品列表
  511. * @param array $goodsList 商品列表
  512. * @param object $userCoupon 优惠券
  513. * @param object $address 地址信息
  514. * @return Order
  515. * @throws BusinessException
  516. */
  517. protected static function createOrderWithTransaction($orderInfo, $orderItem, $goodsList, $userCoupon, $address)
  518. {
  519. $order = null;
  520. Db::startTrans();
  521. try {
  522. // 创建订单
  523. $order = Order::create($orderInfo, true);
  524. // 为每个订单商品添加订单ID和订单号
  525. foreach ($orderItem as &$item) {
  526. $item['order_id'] = $order->id;
  527. $item['order_sn'] = $order->order_sn;
  528. // 移除临时字段
  529. unset($item['category_id'], $item['brand_id']);
  530. }
  531. unset($item);
  532. // 创建订单地址信息
  533. $orderAddressData = [
  534. 'order_id' => $order->id,
  535. 'user_id' => $orderInfo['user_id'],
  536. 'consignee' => $address->receiver,
  537. 'mobile' => $address->mobile,
  538. 'province_name' => $address->province->name ?? '',
  539. 'city_name' => $address->city->name ?? '',
  540. 'district_name' => $address->area->name ?? '',
  541. 'address' => $address->address,
  542. 'province_id' => $address->province_id,
  543. 'city_id' => $address->city_id,
  544. 'district_id' => $address->area_id,
  545. ];
  546. OrderAddress::create($orderAddressData);
  547. // 减库存
  548. foreach ($goodsList as $index => $item) {
  549. if ($item->sku) {
  550. $item->sku->setDec('stocks', $item->nums);
  551. }
  552. $item->goods->setDec("stocks", $item->nums);
  553. }
  554. // 计算单个商品折扣后的价格 (基于新字段名)
  555. $saleamount = bcsub($order['amount'], $order['express_fee'], 2);
  556. $saleratio = $order['goods_price'] > 0 ? bcdiv($saleamount, $order['goods_price'], 10) : 1;
  557. $saleremains = $saleamount;
  558. foreach ($orderItem as $index => &$item) {
  559. if (!isset($orderItem[$index + 1])) {
  560. $saleprice = $saleremains;
  561. } else {
  562. $saleprice = $order['discount_fee'] == 0 ? bcmul($item['goods_original_price'], $item['nums'], 2) : bcmul(bcmul($item['goods_original_price'], $item['nums'], 2), $saleratio, 2);
  563. }
  564. $saleremains = bcsub($saleremains, $saleprice, 2);
  565. $item['goods_price'] = $saleprice;
  566. }
  567. unset($item);
  568. // 批量创建订单商品数据
  569. if (!empty($orderItem)) {
  570. (new OrderGoods())->saveAll($orderItem);
  571. }
  572. // 修改地址使用次数
  573. if ($address) {
  574. $address->setInc('used_nums');
  575. }
  576. // 优惠券已使用
  577. if (!empty($userCoupon)) {
  578. $userCoupon->save(['is_used' => 2]);
  579. }
  580. // 提交事务
  581. Db::commit();
  582. } catch (Exception $e) {
  583. Db::rollback();
  584. throw new BusinessException($e->getMessage());
  585. }
  586. // 记录操作
  587. OrderActionService::recordUserAction(
  588. $orderInfo['order_sn'],
  589. OrderActionEnum::ACTION_CREATE,
  590. $orderInfo['user_id'],
  591. '创建订单',
  592. $orderInfo['user_id']
  593. );
  594. // 订单应付金额为0时直接结算
  595. if ($order['amount'] == 0) {
  596. // Order::settle($order->order_sn, 0);
  597. // $order = Order::get($order->id);
  598. return $order;
  599. }
  600. return $order;
  601. }
  602. /**
  603. * 验证商品规格参数
  604. * @param array $goods_list 商品列表
  605. * @throws BusinessException
  606. */
  607. public static function validateGoodsList($goods_list)
  608. {
  609. if (empty($goods_list) || !is_array($goods_list)) {
  610. throw new BusinessException("商品列表不能为空");
  611. }
  612. foreach ($goods_list as $item) {
  613. if (!isset($item['goods_id']) || !is_numeric($item['goods_id']) || $item['goods_id'] <= 0) {
  614. throw new BusinessException("商品ID无效");
  615. }
  616. if (!isset($item['nums']) || !is_numeric($item['nums']) || $item['nums'] <= 0) {
  617. throw new BusinessException("商品数量必须大于0");
  618. }
  619. if (isset($item['goods_sku_id']) && !is_numeric($item['goods_sku_id'])) {
  620. throw new BusinessException("商品规格ID无效");
  621. }
  622. }
  623. }
  624. /**
  625. * 创建父子订单 - 每个商品一个子订单,统一支付父订单
  626. * @param int $addressId 地址ID
  627. * @param int $userId 用户ID
  628. * @param array $goodsList 商品列表
  629. * @param int $userCouponId 优惠券ID
  630. * @param string $remark 备注
  631. * @return array 返回父订单和子订单信息
  632. * @throws BusinessException
  633. */
  634. public static function createParentChildOrders($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '')
  635. {
  636. return ParentOrderService::createParentChildOrders($addressId, $userId, $goodsList, $userCouponId, $remark);
  637. }
  638. /**
  639. * 统一的订单计算方法(用于预览订单)
  640. * @param array $goods_list 标准化的商品列表
  641. * @param int $user_id 用户ID
  642. * @param int $area_id 地区ID
  643. * @param int $user_coupon_id 优惠券ID
  644. * @return array
  645. * @throws BusinessException
  646. */
  647. public static function calculateOrder($goodsList, $userId, $areaId = 0, $userCouponId = 0)
  648. {
  649. if (empty($goodsList)) {
  650. throw new BusinessException("商品列表不能为空");
  651. }
  652. // 验证商品列表格式
  653. self::validateGoodsList($goodsList);
  654. // 订单基础信息(用于计算,不包含订单号)
  655. $orderInfo = self::initOrderPrice();
  656. // 计算商品明细
  657. list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods($orderInfo, $goodsList, $userId, $areaId, $userCouponId);
  658. return [
  659. 'orderItem' => $orderItem,
  660. 'goodsList' => $calculatedGoodsList,
  661. 'orderInfo' => $orderInfo,
  662. 'userCoupon' => $userCoupon
  663. ];
  664. }
  665. /**
  666. * 订单列表
  667. *
  668. * @param $param
  669. * @return \think\Paginator
  670. */
  671. public static function getOrderList($userId = 0, $param =[],$status = [],$supplierId = 0)
  672. {
  673. $pageSize = 10;
  674. if (!empty($param['pageSize'])) {
  675. $pageSize = $param['pageSize'];
  676. }
  677. $orderModel = new Order();
  678. return $orderModel->with(['orderGoods'])
  679. ->where(function ($query) use ($param,$userId,$status) {
  680. if (!empty($userId)) {
  681. $query->where('user_id', $userId);
  682. }
  683. if (!empty($status)) {
  684. $query->whereIn('order_status', $status );
  685. }
  686. if (isset($param['keywords']) && $param['keywords'] != '') {
  687. $query->where('order_sn', 'in', function ($query) use ($param) {
  688. return $query->name('shop_order_goods')->where('order_sn|goods_title', 'like', '%' . $param['q'] . '%')->field('order_sn');
  689. });
  690. }
  691. if (!empty($supplierId)) {
  692. $query->where('order_sn', 'in', function ($query) use ($supplierId) {
  693. return $query->name('shop_order_goods')->where('supplier_id', $supplierId)->field('order_sn');
  694. });
  695. }
  696. })
  697. ->order('createtime desc')
  698. ->paginate($pageSize, false, ['query' => request()->get()]);
  699. }
  700. /**
  701. *
  702. * @ 订单信息
  703. * @param $orderId
  704. * @param $userId
  705. * @return array|false|\PDOStatement|string|Model
  706. */
  707. public static function getDetail($orderId = 0, $userId = 0)
  708. {
  709. $orderModel = new Order();
  710. return $orderModel->with(['orderGoods'])
  711. ->where('id', $orderId)
  712. ->where(function($query) use ($userId){
  713. if($userId > 0){
  714. $query->where('user_id', $userId);
  715. }
  716. })
  717. ->find();
  718. }
  719. public static function getDetailByOrderSn($orderSn)
  720. {
  721. $orderModel = new Order();
  722. return $orderModel->with(['orderGoods'])
  723. ->where('order_sn', $orderSn)
  724. ->find();
  725. }
  726. // 查询地址信息
  727. public static function getAddressInfo($orderId)
  728. {
  729. return OrderAddress::where('order_id', $orderId)->find();
  730. }
  731. /**
  732. * 判断订单是否失效
  733. * @param $order_sn
  734. * @return bool
  735. */
  736. public static function isExpired($orderSn)
  737. {
  738. $orderInfo = self::getByOrderSn($orderSn);
  739. //订单过期
  740. if (!$orderInfo['orderstate'] && !$orderInfo['paystate'] && time() > $orderInfo['expiretime']) {
  741. // 启动事务
  742. Db::startTrans();
  743. try {
  744. $orderInfo->save(['orderstate' => 2]);
  745. //库存恢复
  746. OrderGoods::setGoodsStocksInc($orderInfo->order_sn);
  747. // //恢复优惠券
  748. // UserCoupon::resetUserCoupon($orderInfo->user_coupon_id, $orderInfo->order_sn);
  749. // 提交事务
  750. Db::commit();
  751. } catch (\Exception $e) {
  752. // 回滚事务
  753. Db::rollback();
  754. }
  755. return true;
  756. }
  757. return false;
  758. }
  759. public static function getByOrderSn($orderSn)
  760. {
  761. return Order::where('order_sn', $orderSn)->find();
  762. }
  763. public static function getByOrderId($orderId)
  764. {
  765. return Order::where('id', $orderId)->find();
  766. }
  767. // 获取状态订单统计
  768. public static function getOrderStatusCount($userId = 0)
  769. {
  770. $info = [];
  771. $info['unpay'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_CREATE)->count();
  772. $info['unsend'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_INSPECTION_PASS)->count();
  773. $info['unrec'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_SHIP)->count();
  774. $info['uneva'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_CONFIRM)->count();
  775. $info['inspect'] = Order::where('user_id', $userId)
  776. ->whereIn('order_status',[
  777. OrderEnum::STATUS_PAY,
  778. OrderEnum::STATUS_INSPECTION,
  779. OrderEnum::STATUS_INSPECTION_FAIL])
  780. ->count();
  781. return $info;
  782. }
  783. // 更新订单状态
  784. public static function updateOrderStatus($orderId = 0, $userId = 0, $status = 0)
  785. {
  786. $order = self::getDetail($orderId, $userId);
  787. if (!$order) {
  788. throw new BusinessException('订单不存在!');
  789. }
  790. // 验证状态
  791. // if (!OrderEnum::isValidOrderStatus($status)) {
  792. // throw new BusinessException('状态不合法!');
  793. // }
  794. // 要处理每个状态对应的时间字段在枚举类中
  795. $timeField = OrderEnum::STATUS_TIME_MAP[$status];
  796. $updateData = [
  797. 'order_status' => $status,
  798. $timeField => time()
  799. ];
  800. Order::where('id', $orderId)->update($updateData);
  801. return $order;
  802. }
  803. /**
  804. * 获取供应商订单列表 - 关联查询方式
  805. * @param int $supplierId 供应商ID
  806. * @param int $userId 用户ID(可选)
  807. * @param array $param 查询参数
  808. * @param array $status 订单状态筛选
  809. * @return \think\Paginator
  810. */
  811. public static function getSupplierOrderList($supplierId, $userId = 0, $param = [], $status = [])
  812. {
  813. $pageSize = $param['page_size'] ?? 10;
  814. return Order::alias('o')
  815. ->join('shop_order_goods og', 'o.order_sn = og.order_sn', 'inner')
  816. ->with(['orderGoods' => function($query) use ($supplierId) {
  817. $query->where('supplier_id', $supplierId);
  818. }])
  819. ->where('og.supplier_id', $supplierId)
  820. ->where(function ($query) use ($param, $userId, $status) {
  821. if (!empty($userId)) {
  822. $query->where('o.user_id', $userId);
  823. }
  824. if (!empty($status)) {
  825. $query->whereIn('o.order_status', $status);
  826. }
  827. if (isset($param['keywords']) && $param['keywords'] != '') {
  828. $query->where(function($subQuery) use ($param) {
  829. $subQuery->where('o.order_sn', 'like', '%' . $param['keywords'] . '%')
  830. ->whereOr('og.goods_title', 'like', '%' . $param['keywords'] . '%');
  831. });
  832. }
  833. })
  834. ->field('o.*')
  835. ->group('o.id')
  836. ->order('o.createtime desc')
  837. ->paginate($pageSize, false, ['query' => request()->get()]);
  838. }
  839. /**
  840. * 获取供应商订单详情 - 支持订单ID和订单号两种方式
  841. * @param mixed $orderParam 订单ID或订单号
  842. * @param int $supplierId 供应商ID
  843. * @param int $userId 用户ID(可选,用于权限验证)
  844. * @return array|null
  845. */
  846. public static function getSupplierOrderDetail($orderParam, $supplierId, $userId = 0)
  847. {
  848. // 根据参数类型判断是订单ID还是订单号
  849. if (is_numeric($orderParam)) {
  850. // 数字类型,当作订单ID处理
  851. $query = Order::where('id', $orderParam);
  852. } else {
  853. // 字符串类型,当作订单号处理
  854. $query = Order::where('order_sn', $orderParam);
  855. }
  856. // 如果指定了用户ID,添加用户权限验证
  857. if (!empty($userId)) {
  858. $query->where('user_id', $userId);
  859. }
  860. $order = $query->find();
  861. if (!$order) {
  862. return null;
  863. }
  864. // 获取该供应商在此订单中的商品
  865. $orderGoods = OrderGoods::where('order_sn', $order->order_sn)
  866. ->where('supplier_id', $supplierId)
  867. ->select();
  868. // 如果该供应商在此订单中没有商品,返回null
  869. if (empty($orderGoods)) {
  870. return null;
  871. }
  872. $order->order_goods = $orderGoods;
  873. return $order;
  874. }
  875. /**
  876. * 获取供应商订单统计
  877. * @param int $supplierId 供应商ID
  878. * @param int $userId 用户ID(可选)
  879. * @return array
  880. */
  881. public static function getSupplierOrderStatusCount($supplierId, $userId = 0)
  882. {
  883. $baseQuery = function($status) use ($supplierId, $userId) {
  884. $query = Order::alias('o')
  885. ->join('shop_order_goods og', 'o.order_sn = og.order_sn', 'inner')
  886. ->where('og.supplier_id', $supplierId)
  887. ->where('o.order_status', $status);
  888. if (!empty($userId)) {
  889. $query->where('o.user_id', $userId);
  890. }
  891. return $query->count('DISTINCT o.id');
  892. };
  893. return [
  894. 'unpay' => $baseQuery(OrderEnum::STATUS_CREATE), // 待付款
  895. 'unsend' => $baseQuery(OrderEnum::STATUS_PAY), // 待发货
  896. 'unrec' => $baseQuery(OrderEnum::STATUS_SHIP), // 待收货
  897. 'uneva' => $baseQuery(OrderEnum::STATUS_CONFIRM), // 待评价
  898. ];
  899. }
  900. /**
  901. * 验证订单和订单商品是否存在
  902. * @param int $orderId 订单ID
  903. * @param int $orderGoodsId 订单商品ID
  904. * @return array|false 返回订单和订单商品信息,不存在返回false
  905. */
  906. public static function validateOrderAndOrderGoods($orderId, $orderGoodsId)
  907. {
  908. // 查询订单
  909. $order = Order::where('id', $orderId)->find();
  910. if (!$order) {
  911. return false;
  912. }
  913. // 查询订单商品
  914. $orderGoods = OrderGoods::where('id', $orderGoodsId)
  915. ->where('order_id', $orderId)
  916. ->find();
  917. if (!$orderGoods) {
  918. return false;
  919. }
  920. return [
  921. 'order' => $order,
  922. 'order_goods' => $orderGoods
  923. ];
  924. }
  925. /**
  926. * 更新订单商品的验收状态
  927. * @param int $orderGoodsId 订单商品ID
  928. * @param int $inspectStatus 验收状态 0:待验收 1:验收通过 2:验收不通过
  929. * @return bool
  930. */
  931. public static function updateOrderGoodsInspectStatus($orderGoodsId, $inspectStatus,$inspectUid = 0)
  932. {
  933. return OrderGoods::where('id', $orderGoodsId)->update([
  934. 'inspect_status' => $inspectStatus,
  935. 'inspect_time' => time(),
  936. 'inspect_id' => $inspectUid
  937. ]);
  938. }
  939. /**
  940. * 检查订单中所有商品的验收状态
  941. * @param int $orderId 订单ID
  942. * @return array 返回验收状态统计
  943. */
  944. public static function checkOrderInspectStatus($orderId)
  945. {
  946. $orderGoods = OrderGoods::where('order_id', $orderId)->select();
  947. $statusCount = [
  948. 'total' => count($orderGoods), // 总商品数
  949. 'pending' => 0, // 待验收
  950. 'passed' => 0, // 验收通过
  951. 'failed' => 0 // 验收不通过
  952. ];
  953. foreach ($orderGoods as $goods) {
  954. switch ($goods['inspect_status']) {
  955. case 0:
  956. $statusCount['pending']++;
  957. break;
  958. case 1:
  959. $statusCount['passed']++;
  960. break;
  961. case 2:
  962. $statusCount['failed']++;
  963. break;
  964. }
  965. }
  966. return $statusCount;
  967. }
  968. /**
  969. * 根据验收状态决定是否更新订单状态
  970. * @param int $orderId 订单ID
  971. * @param int $userId 用户ID
  972. * @return bool
  973. */
  974. public static function updateOrderStatusByInspectResult($orderId, $userId = 0)
  975. {
  976. $inspectStatus = self::checkOrderInspectStatus($orderId);
  977. // 如果所有商品都验收完成(没有待验收的商品)
  978. if ($inspectStatus['pending'] == 0) {
  979. // 如果所有商品都验收通过
  980. if ($inspectStatus['failed'] == 0 && $inspectStatus['passed'] > 0) {
  981. // 更新订单状态为验收通过
  982. return self::updateOrderStatus($orderId, $userId, OrderEnum::STATUS_INSPECTION_PASS);
  983. }
  984. // 如果所有商品都验收不通过
  985. elseif ($inspectStatus['failed'] > 0 && $inspectStatus['passed'] == 0) {
  986. // 更新订单状态为验收失败
  987. return self::updateOrderStatus($orderId, $userId, OrderEnum::STATUS_INSPECTION_FAIL);
  988. }
  989. // 如果部分商品验收通过,部分不通过
  990. elseif ($inspectStatus['failed'] > 0 && $inspectStatus['passed'] > 0) {
  991. // 混合状态,由业务人员手动处理,不自动更新订单状态
  992. return true;
  993. }
  994. }
  995. return true;
  996. }
  997. /**
  998. * 更新订单商品的发货状态
  999. * @param int $orderGoodsId 订单商品ID
  1000. * @param string $expressName 快递公司
  1001. * @param string $expressNo 快递单号
  1002. * @param array $expressImage 快递图片
  1003. * @return bool
  1004. */
  1005. public static function updateOrderGoodsDeliveryStatus($orderGoodsId, $expressName, $expressNo, $expressImage = [],$orderExpressId = 0)
  1006. {
  1007. $updateData = [
  1008. 'express_name' => $expressName,
  1009. 'express_no' => $expressNo,
  1010. 'express_image' => is_array($expressImage) ? json_encode($expressImage) : $expressImage,
  1011. 'delivery_status' => 1, // 已发货
  1012. 'updatetime' => time(),
  1013. 'order_express_id' =>$orderExpressId
  1014. ];
  1015. return OrderGoods::where('id', $orderGoodsId)->update($updateData);
  1016. }
  1017. /**
  1018. * 检查订单中所有商品的发货状态
  1019. * @param int $orderId 订单ID
  1020. * @return array 返回发货状态统计
  1021. */
  1022. public static function checkOrderDeliveryStatus($orderId)
  1023. {
  1024. $orderGoods = OrderGoods::where('order_id', $orderId)->select();
  1025. $statusCount = [
  1026. 'total' => count($orderGoods), // 总商品数
  1027. 'pending' => 0, // 待发货
  1028. 'delivered' => 0, // 已发货
  1029. ];
  1030. foreach ($orderGoods as $goods) {
  1031. if ($goods['delivery_status'] == 1) {
  1032. $statusCount['delivered']++;
  1033. } else {
  1034. $statusCount['pending']++;
  1035. }
  1036. }
  1037. return $statusCount;
  1038. }
  1039. /**
  1040. * 根据发货状态决定是否更新订单状态
  1041. * @param int $orderId 订单ID
  1042. * @param int $userId 用户ID
  1043. * @return bool
  1044. */
  1045. public static function updateOrderStatusByDeliveryResult($orderId, $userId = 0)
  1046. {
  1047. $deliveryStatus = self::checkOrderDeliveryStatus($orderId);
  1048. // 如果所有商品都已发货(没有待发货的商品)
  1049. if ($deliveryStatus['pending'] == 0 && $deliveryStatus['delivered'] > 0) {
  1050. // 更新订单状态为已发货
  1051. return self::updateOrderStatus($orderId, $userId, OrderEnum::STATUS_SHIP);
  1052. }
  1053. return true;
  1054. }
  1055. /**
  1056. * 验证订单商品是否可以发货
  1057. * @param int $orderId 订单ID
  1058. * @param int $orderGoodsId 订单商品ID
  1059. * @return array|false 返回订单和订单商品信息,验证失败返回false
  1060. */
  1061. public static function validateOrderGoodsForDelivery($orderId, $orderGoodsId)
  1062. {
  1063. // 验证订单和订单商品是否存在
  1064. $orderData = self::validateOrderAndOrderGoods($orderId, $orderGoodsId);
  1065. if (!$orderData) {
  1066. return false;
  1067. }
  1068. $orderGoods = $orderData['order_goods'];
  1069. // 检查订单商品的状态是都验货通过才可以发货
  1070. // if ($orderGoods['inspect_status'] !== 1) {
  1071. // return false;
  1072. // }
  1073. if (empty($orderGoods['inspect_time'])) {
  1074. return false;
  1075. }
  1076. // 检查该订单商品是否已经发货
  1077. if ($orderGoods['delivery_status'] == 1) {
  1078. return false;
  1079. }
  1080. return $orderData;
  1081. }
  1082. }