vd/app/Domains/Virtual/Services/OrderService.php
2019-03-08 15:47:28 +08:00

372 lines
13 KiB
PHP

<?php
namespace App\Domains\Virtual\Services;
use App\Dicts;
use App\Core\Service;
use App\Models\Virtual\Order;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use App\Exceptions\NotExistException;
use App\Exceptions\NotAllowedException;
use Illuminate\Support\Facades\Validator;
use Dipper\Foundation\Exceptions\HttpException;
use App\Domains\Virtual\Services\CompanyService;
use App\Domains\Virtual\Services\PackageService;
use App\Domains\Virtual\Repositories\OrderRepository;
use App\Domains\Virtual\Jobs\CreateRealVirtualRelation;
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'),
])->withConditions($conditions)->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;
});
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'],
'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'],
'transaction_status' => ['in:0,1,2'],
'extends' => ['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 (!$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'])) {
$attributes['unit_price'] = intval($attributes['unit_price'] * 100);
$product = ProductService::getProduct($attributes['company_id'], $attributes['package_id'], $attributes['unit_price']);
} elseif ($attributes['product_id']) {
$product = app(ProductRepository::class)->find($attributes['product_id']);
}
if (!$product) {
throw new NotExistException('请选择套餐');
}
$attributes['product_id'] = $product->id;
$rule['type'][] = 'required';
$rule['company_id'][] = 'required';
$rule['product_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);
DB::beginTransaction();
if (!$attributes['id']) {
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['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);
}
if ($attributes['selected']) {
try {
$table = $this->tables[$node['type']];
$cards = array_pluck($attributes['selected'], 'cards');
$cards = array_collapse($cards);
$data = [];
foreach ($cards 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'],
'product_id' => $node['product_id'],
'unit_price' => $node['unit_price'],
'created_at' => $node['order_at'],
'updated_at' => date('Y-m-d H:i:s'),
];
}
if (!empty($data)) {
$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($attributes['selected'], 'id'))
->whereIn('sim', array_pluck($value, 'sim'))->update(['virtual_order_id' => $node['id']]);
}
}
$this->orderRepository->forgetCached();
$this->orderCardPartitionRepository->forgetCached();
app(RealOrderCardPartitionRepository::class)->forgetCached();
app(RealOrderRepository::class)->forgetCached();
CreateRealVirtualRelation::dispatch($node->id, array_pluck($attributes['selected'], 'id'));
} catch (\Exception $e) {
DB::rollBack();
throw new HttpException('操作失败');
}
}
DB::commit();
return $node;
}
/**
* 取消订单
*
* @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 !== 3) {
throw new NotExistException('订单未发货,不能修改');
}
$this->orderRepository->setModel($node)->update(['order_status' => 4]);
return $node;
}
/**
* 删除
*
* @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('订单不存在或已删除');
}
}
DB::transaction(function () use ($ids) {
$this->orderRepository->destroy($ids);
$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 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);
}
}