order_goods; $attr_ids = array_column($order_goods, 'goods_attr_id'); // 获取SKU $sku = GoodsAttr::getSkuByAttrIDs($attr_ids, 'sku_id, mall_goods_id, chain_goods_id, attr_id'); if (empty($sku) || (count($sku) != count($attr_ids))) { throw new Exception('下单商品SKU有误,请重新下单'); } // 重组sku 添加数量 $sku = array_map(function ($item) use ($order_goods) { $goods = ArrayHelper::first($order_goods, function ($goods) use ($item) { return $goods['goods_attr_id'] == $item['attr_id']; }); $item['goods_attr_name'] = $goods['goods_attr_name']; $item['goods_name'] = $goods['goods_name']; $item['num'] = $goods['num']; return $item; }, $sku); // 获取用户收货地址信息 if (empty($extra['address'])) { $address = UserAddress::getOne([['id' => $form['address_id']], ['user_id' => $form['user_id']]], true); $extra['address'] = $address; } $group_order_goods = $form->group_order_goods; // 检测起购 $this->checkBuyStartQty($sku); // 未选择地址不进行计算运费 if (!empty($address)) { // 地址编码 $district = Region::getDistrictByRegion($address['province'], $address['city'], $address['district'], $address['town']); if (empty($district)) { throw new Exception('地址信息有误,请联系管理员处理'); } $spuInfoList = array_map(function ($item) { return [ 'quantity' => $item['num'], 'spuId' => $item['chain_goods_id'], ]; }, $sku); $params = [ 'spuInfoList' => $spuInfoList, 'cityId' => $district->parent_code, ]; // 检测可售 if ($is_new_record) { $this->checkGoodsLimitByRegionCode($sku, $district->region_code); } // 库存监测 if ($is_new_record) { $this->checkGoodsLimitBySkuStock($sku, $district->region_code); } // 运费 $freight = $this->api->calculate($params); $form->marketing['deduct']['shipping_fee'] = [ 'name' => '运费', 'money' => $freight, ]; foreach ($group_order_goods as $k => &$v) { if ($v['mch_order_info']) { // 标识 $v['mch_order_info']['order_source'] = StarChainEnum::PLUGIN_SIGN; // 邮费 $v['mch_order_info']['shipping_type'] = ShippingTypeEnum::LOGISTICS; // 默认物流配送 $v['mch_order_info']['shipping_money'] = $freight; // 收货地址 $v['mch_order_info']['province'] = $address['province_id']; $v['mch_order_info']['city'] = $address['city_id']; $v['mch_order_info']['area'] = $address['district_id']; $v['mch_order_info']['town'] = $address['town_id']; $v['mch_order_info']['region_name'] = $address['province'] .' '. $address['city'] .' '. $address['district'].' '.$address['town']; $v['mch_order_info']['address'] = $address['detail']; $v['mch_order_info']['receiver_name'] = $address['name']; $v['mch_order_info']['mobile'] = $address['mobile']; } } // 计算总额 $form['total_pay_money'] = bcadd($form['total_pay_money'], $freight, 2); } $form->group_order_goods = $group_order_goods; return $form; } /** * 创建订单 * * @param integer $order_id * @param string $order_no * @return void */ public static function createOrder($order_id/* , $order_no */) { /** * @var MallOrder|null */ $order = MallOrder::getOrder($order_id); if (!$order->isChainOrder()) return false; if (!$order->pay_status == StatusEnum::ENABLED) return false; /** * @var array|null */ $details = $order->detail; if (empty($details)) return false; // 已经创建订单 不执行 // if ($order->hasChainOrder()) return false; // 下单商品 $attr_ids = array_column($details, 'goods_attr_id'); // 获取SKU $sku = GoodsAttr::getSkuByAttrIDs($attr_ids, 'sku_id, mall_goods_id, chain_goods_id, attr_id'); if (empty($sku) || (count($sku) != count($attr_ids))) throw new Exception('下单商品SKU有误,请重新下单'); // 重组sku 添加数量 $sku = array_map(function ($item) use ($details) { $detail = ArrayHelper::first($details, function ($goods) use ($item) { return $goods['goods_attr_id'] == $item['attr_id']; }); $item['num'] = $detail['num']; $item['mall_order_id'] = $detail['order_id']; $item['order_detail_id'] = $detail['id']; return $item; }, $sku); // 下单数据 $goods_list = array_map(function ($item) { return [ 'goodsQty' => $item['num'], 'skuId' => $item['sku_id'], 'spuId' => $item['chain_goods_id'], 'mall_order_id' => $item['mall_order_id'], 'order_detail_id' => $item['order_detail_id'], ]; }, $sku); if (empty($goods_list)) throw new Exception('下单数据为空'); // 地址信息 $region = explode(' ', $order->region_name); $region = Region::getAddress($region); // 地址编码 $area_code = Region::getAreaCode($region); if ($area_code === false) throw new Exception('该地址无法下单'); $dto = [ 'orderSource' => 1, 'goodsList' => $goods_list, // 下单商品 'shipArea' => trim(implode(',', $region), ','), 'shipAreaCode' => trim(implode(',', array_reverse(explode(',', $area_code)))), 'shipName' => $order->receiver_name, 'shipAddress' => $order->address, 'shipMobile' => $order->mobile, 'outOrderSn' => $order->order_no, 'buyerMemo' => $order->remark, ]; /** * @var Order|null $order_model */ $order_model = null; // 添加 Yii::$app->db->transaction(function () use ($order, $dto, &$order_model) { $order_model = Order::createOrder($order, $dto); }); // 创建订单 try { $dto['goodsList'] = array_map(function ($item) { return [ 'goodsQty' => $item['goodsQty'], 'skuId' => $item['skuId'], ]; }, $dto['goodsList']); $order_data = (new self(['mall_id' => $order->mall_id]))->api->submitOrder($dto); } catch (Exception $e) { $order_model->orderError($e->getMessage()); throw new Exception($e->getMessage()); } // 修改状态 Yii::$app->db->transaction(function () use ($order_data, $order_model, $order) { $order->changeOrderSource(); if ($order_model->orderSuccess($order_data['orderList']) === false) throw new \Exception('修改状态失败'); }); } /** * 起购检测 * * @param array $spu_id * @return void */ protected function checkBuyStartQty(array $skus) { $data_list = $this->api->listSkuBySpuIds(array_unique(array_column($skus, 'chain_goods_id'))); foreach ($skus as $sku) { // 供应链端商品信息 $chain_goods = ArrayHelper::first($data_list, function ($item) use ($sku) { return $sku['chain_goods_id'] == $item['spuId']; }); // 供应链sku $chain_sku = ArrayHelper::first($chain_goods['data'], function ($item) use ($sku) { return $item['skuId'] == $sku['sku_id']; }); if ($chain_sku['buyStartQty'] > $sku['num']) { throw new Exception('「' . $sku['goods_attr_name'] . '」最低起购数量为' . $chain_sku['buyStartQty']); } } } /** * 商品区域限制检测 * * @param array $skus * @param string $region_code * @return true * @throws Exception */ protected function checkGoodsLimitByRegionCode(array $skus, string $region_code) { $spu_ids = array_unique(array_column($skus, 'chain_goods_id')); $goods = Goods::getInfoBySpuIds($spu_ids, 'name, is_limit_area, limit_area_type, limit_area'); try { foreach ($goods as $item) { $limit_area = Json::decode($item->limit_area) ?? []; // 未开启限制 if ($item->is_limit_area == 0) return true; // 限制类型 if ($item->limit_area_type == 1 && in_array($region_code, $limit_area)) { throw new Exception('商品: 「' . $item->name . '」, 对区域: 「' . $region_code . '」, 已开启区域限制1, 请选择其他商品'); } else if (!in_array($region_code, $limit_area)) { throw new Exception('商品: 「' . $item->name . '」, 对区域: 「' . $region_code . '」, 已开启区域限制0, 请选择其他商品'); } } } catch (Exception $e) { // 如果区域编码不是6位,进行处理 if (strlen($region_code) != 6) { // 区域编码处理 $region_code = substr($region_code, 0, 6); $this->checkGoodsLimitByRegionCode($skus, $region_code); } throw new Exception($e->getMessage()); } } /** * 库存限制坚持 * * @param array $skus * @return void * @throws Exception */ protected function checkGoodsLimitBySkuStock(array $skus, string $region_code) { $data = array_map(function ($item) { return [ 'skuId' => $item['sku_id'], 'skuNum' => $item['num'], ]; }, $skus); $data = $this->api->checkSkuStock($data, $region_code); foreach ($data as $item) { if (!($item['sailState'] && $item['stockState'])) { throw new Exception($item['msg'] ?? sprintf('「%s」库存限制购买', $item['skuId'])); } } } }