orderRepository = $orderRepository; $this->orderCardPartitionRepository = $orderCardPartitionRepository; } /** * 订单列表 * * @param array $conditions * @return mixed */ public function paginate(array $conditions = []) { $limit = $conditions['limit'] ?? 35; $res = $this->orderRepository->withConditions($conditions)->applyConditions()->orderBy('order_at', 'desc')->paginate($limit); $orderShipments = $this->orderCardPartitionRepository->select([ 'order_id', DB::raw('SUM(counts) as counts'), DB::raw('SUM(CASE WHEN refunded_at IS NULL THEN 0 ELSE 1 END) as refunds') ])->withConditions([ 'order_id' => $res->pluck('id')->toArray(), ])->groupBy('order_id')->get()->keyBy('order_id'); $res->map(function ($item) use ($orderShipments) { $item->company = CompanyService::load($item->company_id); $item->package = PackageService::load($item->package_id); $item->unit_price = sprintf('%.02f', $item->unit_price/100); $item->total_price = sprintf('%.02f', $item->total_price/100); $item->custom_price = sprintf('%.02f', $item->custom_price/100); $item->shipments = $orderShipments[$item->id]['counts'] ?? 0; $item->refunds = $orderShipments[$item->id]['refunds'] ?? 0; }); return $res; } /** * 订单计数 * * @param array $conditions * @return mixed */ public function count(array $conditions = []) { $select = [ DB::raw('COUNT(*) as total_count'), DB::raw('SUM(custom_price) as total_price'), DB::raw('SUM(CASE WHEN transaction_status=1 THEN custom_price ELSE 0 END) as transacted_price'), ]; $res = $this->orderRepository->select($select)->withConditions($conditions)->applyConditions()->first()->toArray(); $res['total_price'] = $res['total_price'] ?? 0; $res['total_price'] = sprintf('%.02f', $res['total_price']/100); $res['transacted_price'] = $res['transacted_price'] ?? 0; $res['transacted_price'] = sprintf('%.02f', $res['transacted_price']/100); unset($res['company']); unset($res['package']); return $res; } /** * 下单 * * @param array $attributes * @return Order */ public function store(array $attributes = []) { $rule = [ 'type' => ['in:0,1,2,3'], 'sign' => ['in:1,2'], // 转销售 1,改企业 2 'company_id' => ['exists:virtual_companies,id'], 'product_id' => [], 'counts' => [], 'pay_channel' => [Rule::in(array_collapse(app(Dicts::class)->get('pay_channel')))], 'order_status' => ['in:0,1,2,3,4,5'], 'transaction_status' => ['in:0,1,2'], 'extends' => ['array'], 'selected' => ['array'], ]; $message = [ 'company_id.required' => '请输入企业ID', 'company_id.exists' => '企业不存在或已删除', 'product_id.required' => '请选择套餐', 'counts.required' => '请输入订购数量', 'pay_channel.required' => '请选择支付方式', 'pay_channel.in' => '支付方式不合法', 'contacts.required' => '请选择收货地址', 'mobile.required' => '请选择收货地址', 'address.required' => '请选择收货地址', ]; if (isset($attributes['unit_price'])) { $attributes['unit_price'] = intval($attributes['unit_price'] * 100); } if (!$attributes['id']) { $attributes['sn'] = $attributes['sn'] ?: $this->generateSn(); $attributes['transaction_no'] = $attributes['transaction_no'] ?: $this->generateTransactionNo($attributes['pay_channel']); if ($attributes['company_id'] && $attributes['package_id'] && isset($attributes['unit_price'])) { $product = ProductService::getProduct($attributes['type'], $attributes['company_id'], $attributes['package_id'], $attributes['unit_price']); } elseif ($attributes['product_id']) { $product = app(ProductRepository::class)->find($attributes['product_id']); $attributes['unit_price'] = $product['price']; } if (!$product) { throw new NotExistException('请选择套餐'); } $rule['type'][] = 'required'; $rule['company_id'][] = 'required'; $rule['counts'][] = 'required'; $rule['pay_channel'][] = 'required'; if (!$attributes['source']) { $rule['contacts'][] = 'required'; $rule['mobile'][] = 'required'; $rule['address'][] = 'required'; } } Validator::validate($attributes, $rule, $message); if (isset($attributes['selected']) && empty('selected')) { throw new InvalidArgumentException('请选择卡'); } DB::beginTransaction(); try { // 改企业的卡新增一批虚拟卡,并替换原有订单里的卡 if (isset($attributes['sign']) && $attributes['sign'] == 2) { $simArray = implode(',', array_pluck($attributes['selected'], 'sim')); DB::statement("select change_cards('{{$simArray}}'::INT8[]);"); } // 转销售和改企业的都立即激活卡 if (isset($attributes['sign']) && in_array($attributes['sign'], [1, 2])) { $array = array_map(function ($item) use ($attributes) { return ['sim' => $item['sim'], 'virtual_activated_at' => $attributes['order_at']]; }, $attributes['selected']); Card::upsert($array, 'sim', ['virtual_activated_at']); $attributes['type'] = 0; $originType = $attributes['type']; // 销售订单,如果单卡使用多次,仅第一次为销售,其他改续费。 $extras = []; $selected = $attributes['selected']; $attributes['counts'] = count($selected); foreach ($selected as &$card) { if ($card['counts'] > 1) { array_push($extras, [ 'sim' => $card['sim'], 'counts' => $card['counts'] - 1, ]); $card['counts'] = 1; } } $attributes['selected'] = $selected; } if ($attributes['id']) { if (!$node = $this->orderRepository->find($attributes['id'])) { throw new NotExistException('订单不存在或已删除'); } if (!empty($attributes['extends']) && is_array($attributes['extends'])) { $attributes['extends'] = array_merge($node->extends ?: [], $attributes['extends']); } $this->orderRepository->setModel($node)->update($attributes); } else { $maxId = Order::withTrashed()->max('id'); $attributes['id'] = ++$maxId; if ($product->company_id != $attributes['company_id']) { throw new NotAllowedException('非法操作'); } $attributes['price'] = $product->price; $attributes['total_price'] = $attributes['unit_price'] * $attributes['counts']; $attributes['custom_price'] = $attributes['unit_price'] * $attributes['counts']; $attributes['order_at'] = $attributes['order_at'] ?? date('Y-m-d H:i:s'); $attributes['package_id'] = $attributes['package_id'] ?? $product->package_id; $node = $this->orderRepository->create($attributes); } if ($attributes['selected']) { try { $this->upsertOrderCards($attributes['selected'], $node); $this->orderRepository->forgetCached(); $this->orderCardPartitionRepository->forgetCached(); app(RealOrderCardPartitionRepository::class)->forgetCached(); } catch (\Exception $e) { DB::rollBack(); throw new HttpException('操作失败'); } } if (!empty($extras)) { $extraOrderData = $attributes; $extraOrderData['type'] = $originType; $extraOrderData['counts'] = array_sum(array_pluck($extras, 'counts')); $extraOrderData['total_price'] = $attributes['unit_price'] * $extraOrderData['counts']; $extraOrderData['custom_price'] = $attributes['unit_price'] * $extraOrderData['counts']; $extraOrder = $this->orderRepository->create($attributes); $this->upsertOrderCards($extras, $extraOrder); } } catch (\Exception $e) { DB::rollBack(); throw $e; } DB::commit(); return $node; } protected function upsertOrderCards($array, $node) { $table = $this->tables[$node['type']]; $data = []; foreach ($array as $card) { $data[] = [ 'sim' => $card['sim'], 'counts' => $card['counts'], 'type' => $node['type'], 'order_id' => $node['id'], 'company_id' => $node['company_id'], 'package_id' => $node['package_id'], 'unit_price' => $node['unit_price'], 'created_at' => $node['order_at'], 'updated_at' => date('Y-m-d H:i:s'), ]; } if (empty($data)) { return; } $array = array_chunk($data, 10000); foreach ($array as $value) { DB::table($table)->upsert($value, ['sim', 'order_id', 'deleted_at']); $simArray = implode(',', array_pluck($value, 'sim')); DB::statement("select fix_timelines('{{$simArray}}'::INT8[]);"); RealOrderCardPartition::whereIn('order_id', array_pluck($array, 'order_id'))->whereIn('sim', array_pluck($value, 'sim'))->update(['virtual_order_id' => $node['id']]); } } /** * 以卡续费/续费包/加油包 * * @param array $attributes * @return Order */ public function storeAdded(array $attributes = []) { $rule = [ 'type' => ['in:1,2,3'], 'pay_channel' => ['required'], 'cards' => ['array'], 'cards.*.sim' => ['required'], 'cards.*.counts' => ['required'], 'company_id' => ['required'] ]; $message = [ 'pay_channel.required' => '请选择支付方式', 'company_id.required' => '企业不能为空', 'cards.*.sim.required' => '卡号为必填项', 'cards.*.sim.counts' => '数量为必填项', ]; if ($attributes['type'] !== 1) { $rule['product_id'] = ['required']; $message['product_id.required'] = '请选择套餐'; } Validator::validate($attributes, $rule, $message); if (empty($attributes['cards'])) { throw new InvalidArgumentException('请至少选择一张卡'); } $simArray = array_map('intval', array_pluck($attributes['cards'], 'sim')); $conditions = [ 'type' => [0, 1], 'sim' => $simArray, ]; $res = $this->orderCardPartitionRepository->selectRaw('distinct on (sim) *') ->withConditions($conditions)->orderBy('sim')->orderBy('created_at', 'desc')->get(); $res->map(function ($item) { $item->groupKey = $item->company_id . '_' . $item->package_id; }); $errors = []; foreach ($res as $card) { if ($card->company_id !== $attributes['company_id']) { $errors[] = $card->sim; } } if (!empty($errors)) { $message = sprintf('卡(%s)不属于您的企业', implode(',', $errors)); throw new InvalidArgumentException($message); } $errors = array_diff($simArray, $res->pluck('sim')->toArray()); if (!empty($errors)) { $message = sprintf('卡(%s)未找到销售订单', implode(',', $errors)); throw new InvalidArgumentException($message); } $maxId = Order::withTrashed()->max('id'); $orders = []; $orderCards = []; $order_at = date('Y-m-d H:i:s'); if ($attributes['type'] === 1) { foreach ($res->groupBy('groupKey') as $key => $value) { $orderId = ++$maxId; $realCards = array_filter($attributes['cards'], function ($item) use ($value) { return !in_array($item->sim, array_pluck($value, 'sim')); }); $counts = array_sum(array_pluck($realCards, 'counts')); $orders[$key] = [ 'id' => $orderId, 'sn' => $this->generateSn(), 'source' => 0, 'type' => $attributes['type'], 'company_id' => $attributes['company_id'], 'package_id' => $value[0]['package_id'], 'transaction_no' => $this->generateTransactionNo($attributes['pay_channel']), 'pay_channel' => $attributes['pay_channel'], 'unit_price' => $value[0]['unit_price'], 'counts' => $counts, 'total_price' => $value[0]['unit_price'] * $counts, 'custom_price' => $value[0]['unit_price'] * $counts, 'order_at' => $order_at, 'order_status' => 3, 'transaction_status' => 1, 'created_at' => $order_at, 'updated_at' => $order_at, ]; $cardCounts = array_pluck($attributes['cards'], 'counts', 'sim'); foreach ($value as $card) { $orderCards[] = [ 'type' => $attributes['type'], 'sim' => $card->sim, 'order_id' => $orderId, 'company_id' => $attributes['company_id'], 'package_id' => $card['package_id'], 'counts' => $cardCounts[$card->sim], 'unit_price' => $card->unit_price, 'created_at' => $order_at, 'updated_at' => $order_at, ]; } }; } if ($attributes['type'] !== 1) { if (!$product = app(ProductRepository::class)->find($attributes['product_id'])) { throw new NotExistException('定价未找到或已删除'); } if ($product->company_id !== $attributes['company_id']) { throw new InvalidArgumentException('定价不属于该企业'); } $orderId = $maxId + 1; $orders[] = [ 'id' => $orderId, 'sn' => $this->generateSn(), 'source' => 0, 'type' => $attributes['type'], 'company_id' => $attributes['company_id'], 'package_id' => $product['package_id'], 'transaction_no' => $this->generateTransactionNo($attributes['pay_channel']), 'pay_channel' => $attributes['pay_channel'], 'unit_price' => $product['price'], 'counts' => $counts, 'total_price' => $product['price'] * $counts, 'custom_price' => $product['price'] * $counts, 'order_at' => $order_at, 'order_status' => 3, 'transaction_status' => 0, 'created_at' => $order_at, 'updated_at' => $order_at, ]; $cardCounts = array_pluck($attributes['cards'], 'counts', 'sim'); foreach ($res as $card) { $orderCards[] = [ 'type' => $attributes['type'], 'sim' => $card->sim, 'order_id' => $orderId, 'company_id' => $attributes['company_id'], 'package_id' => $product['package_id'], 'counts' => $cardCounts[$card->sim], 'unit_price' => $product['price'], 'transaction_status' => 0, 'created_at' => $order_at, 'updated_at' => $order_at, ]; }; } DB::beginTransaction(); try { Order::upsert($orders, 'id'); $table = $this->tables[$attributes['type']]; DB::table($table)->upsert($orderCards, ['sim', 'order_id', 'deleted_at']); } catch (\Exception $e) { DB::rollback(); throw $e; } DB::commit(); app(OrderRepository::class)->forgetCached(); app(OrderCardPartitionRepository::class)->forgetCached(); return true; } /** * 取消订单 * * @return bool */ public function cancel($id) { if (!$node = $this->orderRepository->find($id)) { throw new NotExistException('订单不存在或已删除'); } if ($node->order_status !== 0) { throw new NotExistException('订单已出库,不能取消'); } if ($node->transaction_status !== 0) { throw new NotExistException('订单已付款,不能取消'); } $this->orderRepository->setModel($node)->update(['order_status' => 1]); return $node; } /** * 确认收货 * * @return bool */ public function received($id) { if (!$node = $this->orderRepository->find($id)) { throw new NotExistException('订单不存在或已删除'); } if ($node->order_status !== 4) { throw new NotExistException('订单未发货,不能修改'); } $this->orderRepository->setModel($node)->update(['order_status' => 5]); return $node; } /** * 重置 * * @return bool */ public function reset($ids) { DB::transaction(function () use ($ids) { foreach ($ids as $id) { $id = intval($id); if (!$node = $this->orderRepository->find($id)) { throw new NotExistException('订单不存在或已删除'); } if ($node->type === 0) { $sql = 'SELECT COUNT(*) as counts FROM virtual_order_cards_partition WHERE type != 0 AND sim In ( SELECT sim FROM virtual_order_cards_partition WHERE order_id = ? )'; $counts = DB::select($sql, [$id])[0]->counts; if ($counts) { throw new NotAllowedException('订单中的卡已存在于其他类型订单中,不能进行重置'); } // 转销售重置 $sql = 'UPDATE virtual_order_cards_partition SET sim=original_sim,original_sim=0 WHERE original_sim IN ( SELECT DISTINCT SIM FROM virtual_order_cards_partition WHERE type=0 AND order_id = ? )'; DB::statement($sql, [$id]); } } $this->orderCardPartitionRepository->whereIn('order_id', $ids)->delete(); app(RealOrderCardPartitionRepository::class)->whereIn('virtual_order_id', $ids)->update(['virtual_order_id' => 0]); }); app(RealOrderCardPartitionRepository::class)->forgetCached(); app(RealOrderRepository::class)->forgetCached(); return true; } /** * 删除 * * @return bool */ public function destroy($ids) { $ids = is_array($ids) ? $ids : [$ids]; foreach ($ids as $id) { if (!$node = $this->orderRepository->find($id)) { throw new NotExistException('订单不存在或已删除'); } } $this->orderRepository->destroy($ids); return true; } /** * 生成订单编号 * * @return void */ public function generateSn() { return date('YmdHis') .sprintf('%04d', explode('.', microtime(true))[1]) . sprintf('%02d', rand(0, 99)); } /** * 生成流水号 * * 4200000252201903085372480404 微信 * 2019030722001407831022090620 支付宝 * * @return void */ public function generateTransactionNo($payChannel) { switch ($payChannel) { case 'wx': case 'wx_pub': case 'wx_pub_qr': case 'wx_pub_scan': case 'wx_wap': case 'wx_lite': $transactionNo = '4200000' . sprintf('%03d', rand(0, 999)) . date('YmdHis') .sprintf('%04d', explode('.', microtime(true))[1]); break; case 'alipay': case 'alipay_wap': case 'alipay_qr': case 'alipay_scan': case 'alipay_pc_direct': $transactionNo = date('YmdHis') . sprintf('%04d', explode('.', microtime(true))[1]) . '1' . sprintf('%9d', rand(0, 999999999)); break; case 'bank': $transactionNo = date('YmdHis') . sprintf('%04d', explode('.', microtime(true))[1]) . '2' . sprintf('%9d', rand(0, 999999999)); break; case 'account': $transactionNo = date('YmdHis') . sprintf('%04d', explode('.', microtime(true))[1]) . '3' . sprintf('%9d', rand(0, 999999999)); break; case 'tmall': $transactionNo = date('YmdHis') . sprintf('%04d', explode('.', microtime(true))[1]) . '4' . sprintf('%9d', rand(0, 999999999)); break; default: $transactionNo = date('YmdHis') . sprintf('%04d', explode('.', microtime(true))[1]) . '0' . sprintf('%9d', rand(0, 999999999)); break; } return intval($transactionNo); } /** * 订单卡查询 * * @param array $conditions * @return void */ public function cards(array $conditions = []) { $conditions['limit'] = $conditions['limit'] ?? 20; $cards = $this->orderCardPartitionRepository->withRefunded()->select(['sim', 'counts', 'refunded_at']) ->withConditions($conditions)->orderBy('sim') ->paginate($conditions['limit']); return $cards; } }