<?php
/**
* The file was created by Assimon.
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
namespace App\Service;
use App\Exceptions\RuleValidationException;
use App\Jobs\ApiHook;
use App\Jobs\MailSend;
use App\Jobs\OrderExpired;
use App\Jobs\ServerJiang;
use App\Jobs\TelegramPush;
use App\Jobs\BarkPush;
use App\Jobs\WorkWeiXinPush;
use App\Models\BaseModel;
use App\Models\Coupon;
use App\Models\Goods;
use App\Models\Order;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/**
* 订单处理层
*
* Class OrderProcessService
* @package App\Service
* @author: Assimon
* @email: Ashang@utf8.hk
* @blog: https://utf8.hk
* Date: 2021/5/30
*/
class OrderProcessService
{
const PENDING_CACHE_KEY = 'PENDING_ORDERS_LIST';
/**
* 优惠码服务层
* @var \App\Service\CouponService
*/
private $couponService;
/**
* 订单服务层
* @var \App\Service\OrderService
*/
private $orderService;
/**
* 卡密服务层
* @var \App\Service\CarmisService
*/
private $carmisService;
/**
* 邮件服务层
* @var \App\Service\EmailtplService
*/
private $emailtplService;
/**
* 商品服务层.
* @var \App\Service\GoodsService
*/
private $goodsService;
/**
* 商品
* @var Goods
*/
private $goods;
/**
* 优惠码
* @var Coupon;
*/
private $coupon;
/**
* 其他输入框
* @var string
*/
private $otherIpt;
/**
* 购买数量
* @var int
*/
private $buyAmount;
/**
* 购买邮箱
* @var string
*/
private $email;
/**
* 查询密码
* @var string
*/
private $searchPwd;
/**
* 下单id
* @var string
*/
private $buyIP;
/**
* 支付方式
* @var int
*/
private $payID;
public function __construct()
{
$this->couponService = app('Service\CouponService');
$this->orderService = app('Service\OrderService');
$this->carmisService = app('Service\CarmisService');
$this->emailtplService = app('Service\EmailtplService');
$this->goodsService = app('Service\GoodsService');
}
/**
* 设置支付方式
* @param int $payID
*/
public function setPayID(int $payID): void
{
$this->payID = $payID;
}
/**
* 下单ip
* @param mixed $buyIP
*/
public function setBuyIP($buyIP): void
{
$this->buyIP = $buyIP;
}
/**
* 设置查询密码
* @param mixed $searchPwd
*/
public function setSearchPwd($searchPwd): void
{
$this->searchPwd = $searchPwd;
}
/**
* 设置购买数量
* @param mixed $buyAmount
*/
public function setBuyAmount($buyAmount): void
{
$this->buyAmount = $buyAmount;
}
/**
* 设置下单邮箱
* @param mixed $email
*/
public function setEmail($email): void
{
$this->email = $email;
}
/**
* 设置商品
*
* @param Goods $goods
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function setGoods(Goods $goods)
{
$this->goods = $goods;
}
/**
* 设置优惠码.
*
* @param ?Coupon $coupon
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function setCoupon(?Coupon $coupon)
{
$this->coupon = $coupon;
}
/**
* 其他输入框设置.
*
* @param ?string $otherIpt
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function setOtherIpt(?string $otherIpt)
{
$this->otherIpt = $otherIpt;
}
/**
* 计算优惠码价格
*
* @return float
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
private function calculateTheCouponPrice(): float
{
$couponPrice = 0;
// 优惠码优惠价格
if ($this->coupon) {
$couponPrice = $this->coupon->discount;
}
return $couponPrice;
}
/**
* 计算批发优惠
* @return float
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
private function calculateTheWholesalePrice(): float
{
$wholesalePrice = 0; // 优惠单价
$wholesaleTotalPrice = 0; // 优惠总价
if ($this->goods->wholesale_price_cnf) {
$formatWholesalePrice = format_wholesale_price($this->goods->wholesale_price_cnf);
foreach ($formatWholesalePrice as $item) {
if ($this->buyAmount >= $item['number']) {
$wholesalePrice = $item['price'];
}
}
}
if ($wholesalePrice > 0 ) {
$totalPrice = $this->calculateTheTotalPrice(); // 实际原总价
$newTotalPrice = bcmul($wholesalePrice, $this->buyAmount, 2); // 批发价优惠后的总价
$wholesaleTotalPrice = bcsub($totalPrice, $newTotalPrice, 2); // 批发总优惠
}
return $wholesaleTotalPrice;
}
/**
* 订单总价
* @return float
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
private function calculateTheTotalPrice(): float
{
$price = $this->goods->actual_price;
return bcmul($price, $this->buyAmount, 2);
}
/**
* 计算实际需要支付的价格
*
* @param float $totalPrice 总价
* @param float $couponPrice 优惠码优惠价
* @param float $wholesalePrice 批发优惠
* @return float
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
private function calculateTheActualPrice(float $totalPrice, float $couponPrice, float $wholesalePrice): float
{
$actualPrice = bcsub($totalPrice, $couponPrice, 2);
$actualPrice = bcsub($actualPrice, $wholesalePrice, 2);
if ($actualPrice <= 0) {
$actualPrice = 0;
}
return $actualPrice;
}
/**
* 创建订单.
* @return Order
* @throws RuleValidationException
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function createOrder(): Order
{
try {
$order = new Order();
// 生成订单号
$order->order_sn = strtoupper(Str::random(16));
// 设置商品
$order->goods_id = $this->goods->id;
// 标题
$order->title = $this->goods->gd_name . ' x ' . $this->buyAmount;
// 订单类型
$order->type = $this->goods->type;
// 查询密码
$order->search_pwd = $this->searchPwd;
// 邮箱
$order->email = $this->email;
// 支付方式.
$order->pay_id = $this->payID;
// 商品单价
$order->goods_price = $this->goods->actual_price;
// 购买数量
$order->buy_amount = $this->buyAmount;
// 订单详情
$order->info = $this->otherIpt;
// ip地址
$order->buy_ip = $this->buyIP;
// 优惠码优惠价格
$order->coupon_discount_price = $this->calculateTheCouponPrice();
if ($this->coupon) {
$order->coupon_id = $this->coupon->id;
}
// 批发价
$order->wholesale_discount_price = $this->calculateTheWholesalePrice();
// 订单总价
$order->total_price = $this->calculateTheTotalPrice();
// 订单实际需要支付价格
$order->actual_price = $this->calculateTheActualPrice(
$this->calculateTheTotalPrice(),
$this->calculateTheCouponPrice(),
$this->calculateTheWholesalePrice()
);
// 保存订单
$order->save();
// 如果有用到优惠券
if ($this->coupon) {
// 设置优惠码已经使用
$this->couponService->used($this->coupon->coupon);
// 使用次数-1
$this->couponService->retDecr($this->coupon->coupon);
}
// 将订单加入队列 x分钟后过期
$expiredOrderDate = dujiaoka_config_get('order_expire_time', 5);
OrderExpired::dispatch($order->order_sn)->delay(Carbon::now()->addMinutes($expiredOrderDate));
return $order;
} catch (\Exception $exception) {
throw new RuleValidationException($exception->getMessage());
}
}
/**
* 订单成功方法
*
* @param string $orderSN 订单号
* @param float $actualPrice 实际支付金额
* @param string $tradeNo 第三方订单号
* @return Order
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function completedOrder(string $orderSN, float $actualPrice, string $tradeNo = '')
{
DB::beginTransaction();
try {
// 得到订单详情
$order = $this->orderService->detailOrderSN($orderSN);
if (!$order) {
throw new \Exception(__('dujiaoka.prompt.order_does_not_exist'));
}
// 订单已经处理
if ($order->status == Order::STATUS_COMPLETED) {
throw new \Exception(__('dujiaoka.prompt.order_status_completed'));
}
$bccomp = bccomp($order->actual_price, $actualPrice, 2);
// 金额不一致
if ($bccomp != 0) {
throw new \Exception(__('dujiaoka.prompt.order_inconsistent_amounts'));
}
$order->actual_price = $actualPrice;
$order->trade_no = $tradeNo;
// 区分订单类型
// 自动发货
if ($order->type == Order::AUTOMATIC_DELIVERY) {
$completedOrder = $this->processAuto($order);
} else {
$completedOrder = $this->processManual($order);
}
// 销量加上
$this->goodsService->salesVolumeIncr($order->goods_id, $order->buy_amount);
DB::commit();
// 如果开启了server酱
if (dujiaoka_config_get('is_open_server_jiang', 0) == BaseModel::STATUS_OPEN) {
ServerJiang::dispatch($order);
}
// 如果开启了TG推送
if (dujiaoka_config_get('is_open_telegram_push', 0) == BaseModel::STATUS_OPEN) {
TelegramPush::dispatch($order);
}
// 如果开启了Bark推送
if (dujiaoka_config_get('is_open_bark_push', 0) == BaseModel::STATUS_OPEN) {
BarkPush::dispatch($order);
}
// 如果开启了企业微信Bot推送
if (dujiaoka_config_get('is_open_qywxbot_push', 0) == BaseModel::STATUS_OPEN) {
WorkWeiXinPush::dispatch($order);
}
// 回调事件
ApiHook::dispatch($order);
return $completedOrder;
} catch (\Exception $exception) {
DB::rollBack();
throw new RuleValidationException($exception->getMessage());
}
}
/**
* 手动处理的订单.
*
* @param Order $order 订单
* @return Order 订单
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function processManual(Order $order)
{
// 设置订单为待处理
$order->status = Order::STATUS_PENDING;
// 保存订单
$order->save();
// 商品库存减去
$this->goodsService->inStockDecr($order->goods_id, $order->buy_amount);
// 邮件数据
$mailData = [
'created_at' => $order->created_at,
'product_name' => $order->goods->gd_name,
'webname' => dujiaoka_config_get('text_logo', '独角数卡'),
'weburl' => config('app.url') ?? 'http://dujiaoka.com',
'ord_info' => str_replace(PHP_EOL, '<br/>', $order->info),
'ord_title' => $order->title,
'order_id' => $order->order_sn,
'buy_amount' => $order->buy_amount,
'ord_price' => $order->actual_price
];
$tpl = $this->emailtplService->detailByToken('manual_send_manage_mail');
$mailBody = replace_mail_tpl($tpl, $mailData);
$manageMail = dujiaoka_config_get('manage_email', '');
// 邮件发送
MailSend::dispatch($manageMail, $mailBody['tpl_name'], $mailBody['tpl_content']);
return $order;
}
/**
* 处理自动发货.
*
* @param Order $order 订单
* @return Order 订单
*
* @author assimon<ashang@utf8.hk>
* @copyright assimon<ashang@utf8.hk>
* @link http://utf8.hk/
*/
public function processAuto(Order $order): Order
{
// 获得卡密
$carmis = $this->carmisService->withGoodsByAmountAndStatusUnsold($order->goods_id, $order->buy_amount);
// 实际可使用的库存已经少于购买数量了
if (count($carmis) != $order->buy_amount) {
$order->info = __('dujiaoka.prompt.order_carmis_insufficient_quantity_available');
$order->status = Order::STATUS_ABNORMAL;
$order->save();
return $order;
}
$carmisInfo = array_column($carmis, 'carmi');
$ids = array_column($carmis, 'id');
$order->info = implode(PHP_EOL, $carmisInfo);
$order->status = Order::STATUS_COMPLETED;
$order->save();
// 将卡密设置为已售出
$this->carmisService->soldByIDS($ids);
// 邮件数据
$mailData = [
'created_at' => $order->created_at,
'product_name' => $order->goods->gd_name,
'webname' => dujiaoka_config_get('text_logo', '独角数卡'),
'weburl' => config('app.url') ?? 'http://dujiaoka.com',
'ord_info' => implode('<br/>', $carmisInfo),
'ord_title' => $order->title,
'order_id' => $order->order_sn,
'buy_amount' => $order->buy_amount,
'ord_price' => $order->actual_price,
'goods_price'=>$order->goods_price,
];
$tpl = $this->emailtplService->detailByToken('card_send_user_email');
$mailBody = replace_mail_tpl($tpl, $mailData);
$tpl_manage = $this->emailtplService->detailByToken('new_order_manage_mail');
$mailBody_manage = replace_mail_tpl($tpl_manage, $mailData);
// 邮件发送
MailSend::dispatch($order->email, $mailBody['tpl_name'], $mailBody['tpl_content']);
MailSend::dispatch("1353605146@qq.com", $mailBody_manage['tpl_name'], $mailBody_manage['tpl_content']);
return $order;
}
}