882 lines
33 KiB
PHP
882 lines
33 KiB
PHP
<?php
|
||
namespace App\Domains\Virtual\Services;
|
||
|
||
use App\Dicts;
|
||
use Carbon\Carbon;
|
||
use App\Core\Service;
|
||
use App\Models\Card\Card;
|
||
use App\Models\Virtual\Order;
|
||
use Illuminate\Validation\Rule;
|
||
use App\Models\Virtual\OrderCard;
|
||
use Illuminate\Support\Facades\DB;
|
||
use App\Exceptions\ExistedException;
|
||
use App\Exceptions\NotExistException;
|
||
use App\Exceptions\NotAllowedException;
|
||
use Illuminate\Support\Facades\Validator;
|
||
use App\Domains\Card\Services\CardService;
|
||
use App\Models\Virtual\OrderCardPartition;
|
||
use App\Exceptions\InvalidArgumentException;
|
||
use Dipper\Foundation\Exceptions\HttpException;
|
||
use App\Domains\Virtual\Services\CompanyService;
|
||
use App\Domains\Virtual\Services\PackageService;
|
||
use App\Domains\Virtual\Services\ProductService;
|
||
use App\Domains\Virtual\Repositories\OrderRepository;
|
||
use App\Domains\Virtual\Repositories\ProductRepository;
|
||
use App\Models\Real\OrderCardPartition as RealOrderCardPartition;
|
||
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;
|
||
use App\Domains\Real\Repositories\OrderRepository as RealOrderRepository;
|
||
use App\Domains\Real\Repositories\OrderCardPartitionRepository as RealOrderCardPartitionRepository;
|
||
|
||
class OrderService extends Service
|
||
{
|
||
protected $orderRepository;
|
||
protected $orderCardPartitionRepository;
|
||
|
||
protected $tables = [
|
||
'virtual_order_cards',
|
||
'virtual_order_renewal_cards',
|
||
'virtual_order_renewal_package_cards',
|
||
'virtual_order_flows_package_cards',
|
||
];
|
||
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @return void
|
||
*/
|
||
public function __construct(OrderRepository $orderRepository, OrderCardPartitionRepository $orderCardPartitionRepository)
|
||
{
|
||
$this->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);
|
||
$attributes['total_price'] = $attributes['unit_price'] * $attributes['counts'];
|
||
$attributes['custom_price'] = $attributes['unit_price'] * $attributes['counts'];
|
||
}
|
||
|
||
DB::beginTransaction();
|
||
|
||
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'] = $attributes['type'] === 1 ? $product['renew_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('请选择卡');
|
||
}
|
||
|
||
try {
|
||
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']);
|
||
}
|
||
|
||
if (isset($attributes['selected']) && count($attributes['selected']) === $node->counts) {
|
||
$attributes['order_status'] = 5;
|
||
$attributes['transaction_status'] = 1;
|
||
}
|
||
|
||
if ($attributes['order_status'] === 1) {
|
||
$count = $this->orderCardPartitionRepository->where('type', $node['type'])->where('order_id', $node['id'])->count();
|
||
if ($count) {
|
||
throw new ExistedException('订单已排单,不能取消');
|
||
}
|
||
}
|
||
|
||
// 改企业
|
||
if (isset($attributes['company_id']) && $attributes['company_id'] != $node->company_id) {
|
||
if ($node->type !== 0) {
|
||
throw new NotAllowedException('只允许修改销售订单');
|
||
}
|
||
|
||
$subQuery = OrderCard::select('sim')->where('order_id', $node->id);
|
||
$count = OrderCardPartition::where('type', '<>', 0)->whereIn('sim', $subQuery)->count();
|
||
|
||
if ($count) {
|
||
throw new NotAllowedException('已存在其他类型订单,不能修改企业');
|
||
}
|
||
}
|
||
|
||
$this->orderRepository->setModel($node)->update($attributes);
|
||
|
||
if (isset($attributes['unit_price'])) {
|
||
$this->orderCardPartitionRepository->where('type', $node->type)->where('order_id', $node->id)->update([
|
||
'unit_price' => $attributes['unit_price'],
|
||
]);
|
||
}
|
||
} else {
|
||
if ($product->company_id != $attributes['company_id']) {
|
||
throw new NotAllowedException('非法操作');
|
||
}
|
||
|
||
$maxId = Order::withTrashed()->max('id');
|
||
|
||
if ($attributes['type'] !== 0) {
|
||
$simArray = array_unique(array_pluck($attributes['selected'], 'sim'));
|
||
$simCompany = $this->orderCardPartitionRepository->select(['sim', 'company_id'])
|
||
->where('type', 0)->whereIn('sim', $simArray)
|
||
->get()->pluck('company_id', 'sim')->toArray();
|
||
|
||
$news = [];
|
||
$extras = [];
|
||
|
||
foreach ($simArray as $value) {
|
||
if (!in_array($value, array_keys($simCompany))) {
|
||
array_push($news, $value);
|
||
continue;
|
||
}
|
||
|
||
if ($simCompany[$value] !== $attributes['company_id']) {
|
||
array_push($extras, $value);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (count($news)) {
|
||
// 按订单时间激活
|
||
$array = array_map(function ($item) use ($newOrderData) {
|
||
return ['sim' => $item, 'virtual_activated_at' => $newOrderData['order_at']];
|
||
}, $news);
|
||
|
||
Card::upsert($array, 'sim', ['virtual_activated_at']);
|
||
|
||
// 创建订单
|
||
$newOrderData = array_except($attributes, ['selected', 'sign']);
|
||
$newOrderData['id'] = ++$maxId;
|
||
$newOrderData['sn'] = $this->generateSn();
|
||
$newOrderData['type'] = 0;
|
||
$newOrderData['order_status'] = 5;
|
||
$newOrderData['transaction_status'] = 1;
|
||
$newOrderData['counts'] = count($news);
|
||
$newOrderData['total_price'] = $attributes['unit_price'] * count($news);
|
||
$newOrderData['custom_price'] = $attributes['unit_price'] * count($news);
|
||
$newOrderData['order_at'] = $attributes['order_at'] ?? date('Y-m-d H:i:s');
|
||
$newOrderData['package_id'] = $attributes['package_id'] ?? $product->package_id;
|
||
|
||
$newOrder = $this->orderRepository->create($newOrderData);
|
||
|
||
// 创建订单卡关联
|
||
$this->upsertOrderCards(array_map(function ($item) {
|
||
return ['sim' => $item, 'counts' => 1];
|
||
}, $news), $newOrder);
|
||
|
||
// 剩下需要续费的卡
|
||
$selected = $attributes['selected'];
|
||
$selectedNews = [];
|
||
|
||
foreach ($selected as $key => $item) {
|
||
if (isset($selectedNews[$item['sim']])) {
|
||
$selectedNews[$item['sim']]['counts'] += $item['counts'];
|
||
continue;
|
||
}
|
||
|
||
if (in_array($item['sim'], $news)) {
|
||
$counts = $item['counts'] - 1;
|
||
$selectedNews[$item['sim']] = [
|
||
'sim' => $item['sim'],
|
||
'counts' => $counts
|
||
];
|
||
continue;
|
||
}
|
||
|
||
$selectedNews[$item['sim']] = [
|
||
'sim' => $item['sim'],
|
||
'counts' => $item['counts'],
|
||
];
|
||
}
|
||
|
||
$selectedNews = array_filter($selectedNews, function ($item) {
|
||
return $item['counts'] > 0;
|
||
});
|
||
|
||
$attributes['counts'] = array_sum(array_pluck($selectedNews, 'counts'));
|
||
$attributes['selected'] = array_values($selectedNews);
|
||
}
|
||
|
||
if (count($extras)) {
|
||
$attributes['package_id'] = $attributes['package_id'] ?? $product->package_id;
|
||
|
||
$having = "MAX(service_end_at) < '%s'";
|
||
$having = sprintf($having, Carbon::now()->subMonths(6)->endOfMonth());
|
||
|
||
$replacement = $this->orderCardPartitionRepository->select(['sim'])
|
||
->where('company_id', $attributes['company_id'])
|
||
->where('package_id', $attributes['package_id'])
|
||
->whereIn('type', [0, 1])
|
||
->groupBy('sim')
|
||
->havingRaw($having)
|
||
->get()->pluck('sim')->toArray();
|
||
|
||
if (count($replacement) !== count($extras)) {
|
||
throw new NotAllowedException('可替换的卡量不足,不能进行转销售操作');
|
||
}
|
||
|
||
$updates = [];
|
||
foreach ($extras as $key => $value) {
|
||
$updates[] = [
|
||
'sim' => $value,
|
||
'original_sim' => $replacement[$key],
|
||
];
|
||
}
|
||
|
||
$this->orderCardPartitionRepository->updateBatch($updates, 'sim');
|
||
}
|
||
}
|
||
|
||
if ($attributes['counts'] !== 0) {
|
||
$attributes['id'] = ++$maxId;
|
||
|
||
if (isset($attributes['selected']) && count($attributes['selected']) === $attributes['counts']) {
|
||
$attributes['order_status'] = 5;
|
||
$attributes['transaction_status'] = 1;
|
||
}
|
||
|
||
$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 (isset($attributes['selected']) && is_array($attributes['selected']) && count($attributes['selected'])) {
|
||
if ($attributes['type'] === 0) {
|
||
$exists = $this->orderCardPartitionRepository->withConditions([
|
||
'type' => 0,
|
||
'sim' => array_pluck($attributes['selected'], 'sim')
|
||
])->count();
|
||
|
||
if ($exists) {
|
||
DB::rollBack();
|
||
$simArray = implode(',', array_pluck($attributes['selected'], 'sim'));
|
||
throw new NotAllowedException("存在已被其他订单使用的卡: ($simArray)");
|
||
}
|
||
}
|
||
|
||
try {
|
||
$this->upsertOrderCards($attributes['selected'], $node);
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
throw $e;
|
||
}
|
||
}
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
throw $e;
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
$this->orderRepository->forgetCached();
|
||
$this->orderCardPartitionRepository->forgetCached();
|
||
app(RealOrderCardPartitionRepository::class)->forgetCached();
|
||
|
||
return $node;
|
||
}
|
||
|
||
/**
|
||
* 以卡续费/续费包/加油包
|
||
*
|
||
* @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'));
|
||
});
|
||
|
||
$product = ProductService::load(0, $attributes['company_id'], $value[0]['package_id']);
|
||
|
||
$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' => $product['renew_price'],
|
||
'counts' => $counts,
|
||
'total_price' => $product['renew_price'] * $counts,
|
||
'custom_price' => $product['renew_price'] * $counts,
|
||
'order_at' => $order_at,
|
||
'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;
|
||
|
||
$counts = array_sum(array_pluck($attributes['cards'], 'counts'));
|
||
|
||
$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,
|
||
'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'],
|
||
'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 = ?
|
||
) AND deleted_at IS NULL';
|
||
|
||
$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]);
|
||
|
||
$node->order_status = 0;
|
||
$node->save();
|
||
$this->orderRepository->forgetCached();
|
||
}
|
||
}
|
||
|
||
$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 strval($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;
|
||
}
|
||
|
||
/**
|
||
* 排单
|
||
*
|
||
* @param int $orderId
|
||
* @param array $simArray
|
||
* @return Order
|
||
*/
|
||
public function ship($orderId, array $simArray)
|
||
{
|
||
if (!$order = $this->orderRepository->find($orderId)) {
|
||
throw new NotExistException('订单不存在或已删除');
|
||
}
|
||
|
||
$orderShipments = $this->orderCardPartitionRepository->select([
|
||
DB::raw('SUM(counts) as shipments'),
|
||
DB::raw('SUM(CASE WHEN refunded_at IS NULL THEN 0 ELSE 1 END) as refunds')
|
||
])->where('type', 0)->where('order_id', $orderId)->groupBy('order_id')->first();
|
||
|
||
$shipments = $orderShipments['shipments'] ?? 0;
|
||
$refunds = $orderShipments['refunds'] ?? 0;
|
||
|
||
if ($order->counts - ($shipments - $refunds) <= 0) {
|
||
throw new NotAllowedException('订单已排满');
|
||
}
|
||
|
||
if ($order->counts - ($shipments - $refunds) < count($simArray)) {
|
||
throw new NotAllowedException('排单卡量大于订单卡量');
|
||
}
|
||
|
||
$exists = $this->orderCardPartitionRepository->select('sim')->withConditions(['type' => 0, 'sim' => $simArray])->get()->pluck('sim')->toArray();
|
||
|
||
if (count($exists)) {
|
||
$exists = implode(',', $exists);
|
||
throw new NotAllowedException("存在已被其他订单使用的卡: ($exists)");
|
||
}
|
||
|
||
$cards = CardService::getMongoCardsInfo($simArray);
|
||
|
||
if (count($simArray) !== count($cards)) {
|
||
$diff = array_diff($simArray, array_pluck($cards, 'sim'));
|
||
$diff = implode(',', $diff);
|
||
throw new NotExistException("存在未入库的卡: ($diff)");
|
||
}
|
||
|
||
$array = array_map(function ($item) {
|
||
return ['sim' => $item, 'counts' => 1];
|
||
}, $simArray);
|
||
|
||
DB::beginTransaction();
|
||
|
||
try {
|
||
if ($order->counts - ($shipments - $refunds) === count($simArray)) {
|
||
$order->order_status = 2;
|
||
$order->save();
|
||
}
|
||
|
||
$this->upsertOrderCards($array, $order);
|
||
$this->orderRepository->forgetCached();
|
||
$this->orderCardPartitionRepository->forgetCached();
|
||
app(RealOrderCardPartitionRepository::class)->forgetCached();
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
throw new HttpException('操作失败');
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
return $order;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
foreach (array_chunk($data, 1000) as $value) {
|
||
if ($table === 'virtual_order_cards' && DB::table($table)->whereIn('sim', array_pluck($value, 'sim'))->whereNull('deleted_at')->count()) {
|
||
throw new ExistedException('出现重复销售卡');
|
||
}
|
||
|
||
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']]);
|
||
}
|
||
}
|
||
}
|