vd/app/Domains/Virtual/Services/OrderService.php
2019-03-18 11:55:39 +08:00

446 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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 App\Models\Virtual\OrderCardPartition;
use Dipper\Foundation\Exceptions\HttpException;
use Illuminate\Database\Query\Grammars\Grammar;
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;
use App\Exceptions\InvalidArgumentException;
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'],
'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'],
'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 (isset($attributes['sign']) && $attributes['sign'] == 2) {
if (empty($attributes['selected'])) {
throw new InvalidArgumentException('请选择卡');
}
// 改企业的卡新增一批虚拟卡,并替换原有订单里的卡
$simArray = implode(',', array_pluck($attributes['selected'], 'sim'));
try {
DB::statement("select change_cards('{{$simArray}}'::INT8[]);");
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
}
// 转销售和改企业的都立即激活卡
if (isset($attributes['sign']) && in_array($attributes['sign'], [1, 2])) {
$params = array_pluck($attributes['selected'], 'sim');
$sql = 'UPDATE cards SET virtual_activated_at = ? WHERE sim IN (%s)';
$sql = sprintf($sql, app(Grammar::class)->parameterize($params));
array_unshift($params, $attributes['order_at']);
DB::update($sql, $params);
$attributes['type'] = 0;
}
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 = $attributes['selected'];
$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'], 'order_id'))
->whereIn('sim', array_pluck($value, 'sim'))->update(['virtual_order_id' => $node['id']]);
}
}
$this->orderRepository->forgetCached();
$this->orderCardPartitionRepository->forgetCached();
app(RealOrderCardPartitionRepository::class)->forgetCached();
// 销售订单创建企业套餐关联
if ($node['type'] === 0) {
CreateRealVirtualRelation::dispatch($node->id, array_pluck($attributes['selected'], 'order_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 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 = '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->select(['sim'])->withConditions($conditions)->orderBy('sim')->paginate($conditions['limit']);
return $cards;
}
}