1016 lines
38 KiB
PHP
1016 lines
38 KiB
PHP
<?php
|
||
|
||
namespace app\common\library\order;
|
||
use think\Model;
|
||
use app\common\model\goods\Handle;
|
||
use app\common\exception\Exception;
|
||
use app\common\model\order\Item as OrderItem;
|
||
use think\Db;
|
||
use app\common\model\order\Order as orderModel;
|
||
class OrderCreate
|
||
{
|
||
|
||
/**
|
||
* 类型,create:创建订单,calc:计算费用
|
||
*/
|
||
protected $calc_type = 'create';
|
||
|
||
protected $order_type = 'goods';
|
||
|
||
protected $goodsList = [];
|
||
|
||
protected $user = null;
|
||
|
||
protected $msg = null;
|
||
|
||
/**
|
||
* 订单相关费用
|
||
*/
|
||
protected $orderData = [
|
||
'goods_total_price_marking' => '0', // 商品原始总价
|
||
'goods_total_price' => '0', // 商品总价
|
||
'coupon_discount_fee' => '0', // 优惠券优惠金额
|
||
'discount_price' => 0, // 当前订单,总优惠金额(优惠券 + 活动优惠)
|
||
'coupon' => null, // 所使用的优惠券
|
||
'coupon_goods_ids' => [],
|
||
'dispatch_price' => '0', // 运费总价
|
||
'score_amount' => 0, // 订单总积分
|
||
'order_total_price' => 0, // 订单总费用
|
||
'real_price' => 0, // 应支付总金额 (减去优惠之后的)
|
||
"vip_discount_price"=>0,//会员优惠
|
||
"score_fee"=>0,//支付积分
|
||
'goods_count'=>0,
|
||
'user_id'=>0,
|
||
'live_id'=>'',
|
||
'live_goods_action_id'=>''
|
||
];
|
||
|
||
|
||
|
||
protected $scoreGoodsModel = null;
|
||
|
||
public function __construct($params = [])
|
||
{
|
||
|
||
$this->user = user_info();
|
||
|
||
if($this->user){
|
||
$this->orderData['user_id'] = $this->user->id;
|
||
}
|
||
|
||
if($params){
|
||
$this->calcParams($params);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 创建订单
|
||
* @param $result
|
||
* @return mixed
|
||
*/
|
||
public function createOrder($result)
|
||
{
|
||
|
||
$order = $this->createHandle($result);
|
||
|
||
return $order;
|
||
}
|
||
|
||
/**
|
||
* 创建订单
|
||
* @param $result
|
||
* @return mixed
|
||
*/
|
||
public function createHandle($orderCalculate=[],$orderSource='')
|
||
{
|
||
//订单来源
|
||
if(!$orderSource){
|
||
$orderSource = \app\common\library\Platform::getPlatform();
|
||
}
|
||
|
||
|
||
// 如果需要支付积分,扣除积分
|
||
if ($this->order_type=='score' && $orderCalculate['score_amount']) {
|
||
//扣积分
|
||
$user = \app\common\model\User::getUserInfo($this->user->id);
|
||
|
||
if(!$user || $user['score'] < $orderCalculate['score_amount']){
|
||
//抛出异常 积分不足
|
||
new \app\common\exception\Exception('当前积分余额不足');
|
||
}
|
||
|
||
\app\common\model\User::score((-1 * $orderCalculate['score_amount']),$this->user->id,"积分兑换商品");
|
||
}
|
||
|
||
|
||
$order = Db::transaction(function () use ($orderCalculate,$orderSource) {
|
||
|
||
$orderData = [];
|
||
$orderData['order_no'] = orderModel::getSn($this->user->id ?? 0);
|
||
$orderData['user_id'] = $this->user->id ?? 0;
|
||
$orderData['need_post'] = 0;
|
||
|
||
$orderData['order_type'] = $this->order_type;
|
||
$orderData['goods_total_price_marking'] = $this->orderData['goods_total_price_marking'];
|
||
$orderData['goods_total_price'] = $this->orderData['goods_total_price'];
|
||
$orderData['dispatch_price'] = $this->orderData['dispatch_price'];
|
||
$orderData['order_total_price'] = $this->orderData['order_total_price'];
|
||
$orderData['real_price'] = $this->orderData['real_price'];
|
||
$orderData['discount_price'] = $this->orderData['discount_price'];
|
||
$orderData['goods_count'] = $orderCalculate['goods_count'];
|
||
$orderData['vip_discount_price'] = $this->orderData['vip_discount_price'];
|
||
$orderData['coupon_discount_fee'] = $this->orderData['coupon_discount_fee'] ?? 0;
|
||
$orderData['coupon_user_id'] = isset($orderCalculate['coupon']) && $orderCalculate['coupon'] ? $orderCalculate['coupon']['id'] : 0;
|
||
$orderData['coupon_id'] = isset($orderCalculate['coupon']) && $orderCalculate['coupon']->coupon->id ? $orderCalculate['coupon']->coupon->id : 0;
|
||
|
||
$orderData['status'] = \app\common\constant\order\Status::STATUS_UNPAID;
|
||
$orderData['buyer_remark'] = $this->remark ?? '';
|
||
$orderData['source'] = $orderSource;
|
||
$orderData['uniacid'] = UNIACID;
|
||
$orderData['createtime'] = time();
|
||
$orderData['live_id'] = $this->orderData['live_id'];
|
||
|
||
$orderData['score_amount'] = $orderCalculate['score_amount'];
|
||
$orderData['score_fee'] = $orderCalculate['score_amount'];
|
||
$orderData['is_virtual_pay'] = isset($orderCalculate['is_virtual_pay']) ? intval($orderCalculate['is_virtual_pay']) : 0;
|
||
|
||
if ($orderData['is_virtual_pay'] && $orderData['real_price'] < 1) {
|
||
$orderData['real_price'] = 1;
|
||
}
|
||
|
||
$order = orderModel::insertGetId($orderData);
|
||
|
||
// 将优惠券使用掉
|
||
if (isset($orderCalculate['coupon']) && $orderCalculate['coupon']) {
|
||
$orderCalculate['coupon']->use_order_id = $order;
|
||
$orderCalculate['coupon']->use_time = time();
|
||
$orderCalculate['coupon']->allowField(true)->save();
|
||
}
|
||
|
||
// 添加 订单 item(使用原始goodsList,不乘汇率)
|
||
foreach ($this->goodsList as $key => $buyinfo) {
|
||
|
||
$item = [];
|
||
$item['item_id'] = $buyinfo['item_id'];
|
||
$item['item_name'] = $buyinfo['item_name'];
|
||
$item['count'] = $buyinfo['count'];
|
||
$item['total_price_marking'] = $buyinfo['total_price_marking'];
|
||
$item['total_price'] = $buyinfo['total_price'];
|
||
|
||
$item['goods_type'] = $buyinfo['goodsType'];
|
||
$item['unit_price'] = $buyinfo['unit_price'];
|
||
$item['discount_price'] = $buyinfo['discount_price'];
|
||
$item['real_price'] = $buyinfo['real_price'];
|
||
if ($orderData['is_virtual_pay'] && $item['real_price'] < 1) {
|
||
$item['real_price'] = 1;
|
||
}
|
||
$item['snapshoot'] = is_array($buyinfo['snapshoot']) ? json_encode($buyinfo['snapshoot']) : $buyinfo['snapshoot'];
|
||
$item['vip_discount_price'] = $buyinfo['vip_discount_price'];
|
||
$item['coupon_discount_fee'] = $buyinfo['coupon_discount_fee'];
|
||
$item['createtime'] = $orderData['createtime'];
|
||
$item['send_status'] = $orderData['status'];
|
||
$item['order_no'] = $orderData['order_no'];
|
||
$item['user_id'] = $orderData['user_id'];
|
||
$item['score_amount'] = $orderData['score_amount'];
|
||
|
||
if($this->order_type == 'vipcard' || $this->order_type == 'activity' || $this->order_type == 'live_gift'){
|
||
$item['extend_data'] = $buyinfo['extend_data'];
|
||
}
|
||
$item['uniacid'] = UNIACID;
|
||
|
||
|
||
OrderItem::insert($item);
|
||
|
||
if($item['goods_type'] == 'activity'){
|
||
$ticketInfo = json_decode($item['snapshoot'],true);
|
||
|
||
if($ticketInfo['form_user_id'] != $item['user_id']){
|
||
throw new \app\common\exception\Exception('报名信息错误,请返回重试');
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// 保存订单地址(实物商品)
|
||
if ($this->order_type == 'physical' && isset($this->orderData['address_id']) && $this->orderData['address_id']) {
|
||
$this->saveOrderAddress($order, $orderData);
|
||
}
|
||
|
||
|
||
if($orderSource == 'channels' && \app\common\library\Platform::getSystemType() == 'single' && \app\admin\library\project\App::isInstall('channels')){
|
||
$hookParams = [
|
||
'params'=>$orderCalculate['channels_params'],
|
||
'order'=>$orderData
|
||
];
|
||
\think\Hook::listen('channels_order_push',$hookParams);
|
||
}
|
||
|
||
// 重新获取订单
|
||
$order = orderModel::where('id', $order)->find();
|
||
\app\common\model\order\Log::set($orderData['order_no'],"订单创建完成,等待支付");
|
||
\think\Hook::listen('agent',$orderData['order_no']);
|
||
|
||
//同步订单号到直播商品
|
||
if($this->orderData['live_goods_action_id']){
|
||
$liveGoodsAction = [
|
||
'live_goods_action_id'=>$this->orderData['live_goods_action_id'],
|
||
'order_no'=>$orderData['order_no'],
|
||
'user_id'=>$orderData['user_id']
|
||
];
|
||
\think\Hook::listen('live_goods_order',$liveGoodsAction);
|
||
}
|
||
|
||
return $order;
|
||
});
|
||
|
||
|
||
|
||
return $order;
|
||
}
|
||
|
||
/**
|
||
* 获取请求参数,初始化,并设置默认值
|
||
*
|
||
* @param array $params
|
||
* @return array
|
||
*/
|
||
public function calcParams($params)
|
||
{
|
||
$this->order_type = $params['order_type'] ?? 'goods';
|
||
|
||
|
||
if($this->order_type == 'score'){
|
||
$this->scoreGoodsModel = new \app\api\model\app\score\Goods;
|
||
}
|
||
|
||
$this->goodsList = $params['goods_list'] ?? [];
|
||
|
||
|
||
$this->coupon_id = $params['coupon_id'] ?? 0;
|
||
$this->remark = $params['buyer_remark'] ?? '';
|
||
|
||
$this->money = (isset($params['money']) && $params['money'] > 0) ? $params['money'] : 0;
|
||
|
||
$this->orderData['live_goods_action_id'] = '';
|
||
|
||
if(isset($params['live_id'])){
|
||
$this->orderData['live_id'] = $params['live_id'];
|
||
}
|
||
|
||
if(isset($params['live_goods_action_id'])){
|
||
$this->orderData['live_goods_action_id'] = $params['live_goods_action_id'];
|
||
}
|
||
|
||
if(isset($params['address_id'])){
|
||
$this->orderData['address_id'] = $params['address_id'];
|
||
}
|
||
|
||
// 获取商品信息
|
||
$this->goodsListInit();
|
||
}
|
||
|
||
|
||
/**
|
||
* 格式化订单商品
|
||
* @return void
|
||
*/
|
||
public function goodsListInit()
|
||
{
|
||
foreach ($this->goodsList as $key => &$buyinfo) {
|
||
|
||
|
||
$buyinfo['extend'] = isset($buyinfo['extend']) ? $buyinfo['extend'] : '';
|
||
|
||
$detail = Handle::getGoodsDetail($buyinfo['goodsId'],$buyinfo['goodsType'],$buyinfo['count'],$buyinfo['extend']);
|
||
if (!$detail || $detail['status'] != 1) {
|
||
throw new Exception('商品不存在或已下架');
|
||
}
|
||
|
||
if($buyinfo['goodsType'] == 'vipcard'){
|
||
$buyinfo['goodsId'] = explode("_",$buyinfo['goodsId'])[0];
|
||
}
|
||
|
||
if($buyinfo['goodsType'] == 'physical'){
|
||
$buyinfo['goodsId'] = explode("_",$buyinfo['goodsId'])[0];
|
||
}
|
||
|
||
|
||
if($this->order_type == 'score'){
|
||
$detail['score_goods'] = $this->scoreGoodsModel->where([
|
||
'goods_id'=>$buyinfo['goodsId']
|
||
])->find();
|
||
}
|
||
|
||
$buyinfo['coupon_discount_fee'] = 0;
|
||
$buyinfo['discount_price'] = 0;
|
||
|
||
|
||
$buyinfo['detail'] = $detail;
|
||
// $this->goodsList[] = $buyinfo;
|
||
// $buyinfo['goods'] = $buyinfo;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 是否计算促销
|
||
*
|
||
* @return bool
|
||
*/
|
||
private function isCalcPromos()
|
||
{
|
||
if ($this->order_type == 'score') {
|
||
return false; // 积分商品不能参与促销
|
||
} else {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算订单
|
||
*
|
||
* @return void
|
||
*/
|
||
public function calc($calc_type = 'calc')
|
||
{
|
||
$this->calc_type = $calc_type;
|
||
|
||
// 检查系统必要条件
|
||
// check_env(['bcmath', 'queue']);
|
||
|
||
// 检查是否可下单
|
||
$this->calcCheck();
|
||
|
||
// 计算订单各种费用
|
||
$this->calcAmount();
|
||
|
||
// 计算优惠券费用
|
||
if ($this->coupon_id) {
|
||
$this->calcCoupon();
|
||
}
|
||
|
||
foreach ($this->goodsList as $key => $buyinfo) {
|
||
//单个商品的优惠金额
|
||
$this->goodsList[$key]['real_price'] = $this->goodsList[$key]['total_price'] - $this->goodsList[$key]['discount_price'];
|
||
}
|
||
|
||
// 订单应付金额
|
||
$this->orderData['order_total_price'] = bcadd($this->orderData['goods_total_price'], $this->orderData['dispatch_price'], 2);
|
||
|
||
$this->orderData['discount_price'] = bcadd($this->orderData['coupon_discount_fee'], $this->orderData['vip_discount_price'], 2);
|
||
$this->orderData['real_price'] = bcsub($this->orderData['order_total_price'], $this->orderData['discount_price'], 2);
|
||
$this->orderData['real_price'] = $this->orderData['real_price'] < 0 ? 0 : $this->orderData['real_price'];
|
||
|
||
return $this->calcReturn();
|
||
}
|
||
|
||
/**
|
||
* 返回订单数据
|
||
* @return []|array
|
||
*/
|
||
public function calcReturn() {
|
||
$isVirtualPay = $this->isUseVirtualPay();
|
||
$result = [
|
||
'goods_total_price_marking' => $this->orderData['goods_total_price_marking'],
|
||
'vip_discount_price'=>$this->orderData['vip_discount_price'],
|
||
'goods_total_price' => $this->orderData['goods_total_price'],
|
||
'dispatch_price' => $this->orderData['dispatch_price'],
|
||
'order_total_price' => $this->orderData['order_total_price'],
|
||
'real_price' => $this->orderData['real_price'],
|
||
'discount_price' => $this->orderData['discount_price'],
|
||
'coupon_discount_fee' => $this->orderData['coupon_discount_fee']
|
||
];
|
||
|
||
|
||
// 处理小数点保留两位小数
|
||
foreach ($result as $key => $amount) {
|
||
$result[$key] = number_format($amount, 2, '.', '');
|
||
}
|
||
|
||
$orderGoodsList = $this->goodsList;
|
||
if ($isVirtualPay) {
|
||
$result = $this->convertVirtualPayResultPrices($result);
|
||
$orderGoodsList = $this->convertVirtualPayGoodsPrices($orderGoodsList);
|
||
}
|
||
|
||
// 合并不需要处理小数点的
|
||
$result = array_merge($result, [
|
||
'score_amount' => $this->orderData['score_amount'],
|
||
'goods_count' => $this->orderData['goods_count'],
|
||
'is_virtual_pay' => $isVirtualPay,
|
||
'orderGoodsList' => $orderGoodsList
|
||
]);
|
||
|
||
// 如果是下单,合并 优惠券, 收货地址
|
||
if ($this->calc_type == 'create') {
|
||
$result = array_merge($result, [
|
||
"coupon" => $this->orderData['coupon']
|
||
]);
|
||
} else {
|
||
$result = array_merge($result, ['msg' => $this->msg]);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 转换订单金额字段为代币展示金额。
|
||
* @param array $result
|
||
* @return array
|
||
*/
|
||
protected function convertVirtualPayResultPrices($result)
|
||
{
|
||
return \app\common\library\pay\VirtualPayService::convertPriceFields($result, [
|
||
'goods_total_price_marking',
|
||
'vip_discount_price',
|
||
'goods_total_price',
|
||
'dispatch_price',
|
||
'order_total_price',
|
||
'real_price',
|
||
'discount_price',
|
||
'coupon_discount_fee'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 转换订单商品金额字段为代币展示金额。
|
||
* @param array $goodsList
|
||
* @return array
|
||
*/
|
||
protected function convertVirtualPayGoodsPrices($goodsList)
|
||
{
|
||
foreach ($goodsList as &$buyinfo) {
|
||
$buyinfo = \app\common\library\pay\VirtualPayService::convertPriceFields($buyinfo, [
|
||
'coupon_discount_fee',
|
||
'discount_price',
|
||
'real_price',
|
||
'total_price_marking',
|
||
'total_price',
|
||
'unit_price',
|
||
'vip_discount_price'
|
||
]);
|
||
|
||
if (isset($buyinfo['detail'])) {
|
||
$detail = $buyinfo['detail'];
|
||
if (!is_array($detail)) {
|
||
$detail = $detail instanceof \think\Model ? $detail->toArray() : (array)$detail;
|
||
}
|
||
$detail = \app\common\library\pay\VirtualPayService::convertPriceFields($detail, [
|
||
'price',
|
||
'price_marking',
|
||
'pay_price',
|
||
'min_price'
|
||
]);
|
||
$buyinfo['detail'] = $detail;
|
||
$buyinfo['snapshoot'] = json_encode($detail);
|
||
}
|
||
}
|
||
unset($buyinfo);
|
||
|
||
return $goodsList;
|
||
}
|
||
|
||
/**
|
||
* 根据订单内商品实际类型判断是否使用微信虚拟支付。
|
||
* @return int
|
||
*/
|
||
protected function isUseVirtualPay()
|
||
{
|
||
foreach ($this->goodsList as $buyinfo) {
|
||
$goodsType = isset($buyinfo['detail']['type']) ? $buyinfo['detail']['type'] : ($buyinfo['goodsType'] ?? '');
|
||
if (!$goodsType) {
|
||
continue;
|
||
}
|
||
|
||
if (\app\common\library\pay\VirtualPayService::isUseVirtualPay($goodsType)) {
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/**
|
||
* 计算优惠券的优惠
|
||
*
|
||
* @return void
|
||
*/
|
||
public function calcCoupon()
|
||
{
|
||
// 获取优惠券
|
||
$this->getCoupon($this->coupon_id);
|
||
|
||
|
||
if ($this->orderData['coupon']) {
|
||
|
||
$this->orderData['coupon']->coupon_money = $this->orderData['coupon']->coupon_money > $this->orderData['goods_total_price'] ? $this->orderData['goods_total_price'] : $this->orderData['coupon']->coupon_money;
|
||
|
||
$this->orderData['coupon_discount_fee'] = $this->orderData['coupon']->coupon_money; // 获取优惠券时计算的金额
|
||
|
||
$this->orderData['coupon_goods_ids'] = $this->orderData['coupon']->goods_ids; // 获取优惠券时参与的商品
|
||
|
||
// 将当前优惠券优惠金额平分到使用优惠券的商品上
|
||
foreach ($this->goodsList as &$buyInfo) {
|
||
|
||
if (in_array($buyInfo['goodsId'], $this->orderData['coupon_goods_ids'])) {
|
||
|
||
$avgDiscountPrice = bcdiv($this->orderData['coupon_discount_fee'], $this->orderData['goods_total_price'], 6);
|
||
|
||
$buyInfo['coupon_discount_fee'] = bcmul($buyInfo['total_price'], $avgDiscountPrice, 6);
|
||
$buyInfo['coupon_discount_fee'] = round($buyInfo['coupon_discount_fee'], 2);
|
||
$buyInfo['discount_price'] += $buyInfo['coupon_discount_fee'];
|
||
}
|
||
}
|
||
|
||
} else {
|
||
$this->exception('优惠券不可用');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算订单各种费用
|
||
*
|
||
* @return array
|
||
*/
|
||
public function calcAmount()
|
||
{
|
||
// 计算商品金额
|
||
foreach ($this->goodsList as $key => $buyinfo) {
|
||
$detail = $buyinfo['detail'];
|
||
$this->orderData['goods_count'] += $buyinfo['count'];
|
||
|
||
// 当前商品原始总价
|
||
$current_goods_total_price_marking = ($detail['price_marking'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price_marking'] += $current_goods_total_price_marking;
|
||
|
||
// 当前商品现在总价
|
||
|
||
// 当前商品所需积分
|
||
$current_score_amount = 0;
|
||
//会员折扣金额
|
||
$current_vip_discount_price = 0;
|
||
$current_goods_total_price = 0;
|
||
|
||
switch ($this->order_type){
|
||
case 'score':
|
||
// 积分商城规格
|
||
$current_score_amount = ($detail['score_goods']['score'] * $buyinfo['count']);
|
||
$this->orderData['score_amount'] += $current_score_amount;
|
||
$current_goods_total_price = 0;
|
||
break;
|
||
case 'activity':
|
||
// 线下活动
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
$this->goodsList[$key]['extend_data'] = \fast\Random::numeric(14);
|
||
break;
|
||
case 'exercises':
|
||
// 题库练习
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
break;
|
||
|
||
case 'live_gift':
|
||
// 直播礼物
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
$this->goodsList[$key]['extend_data'] = $buyinfo['extend']['course_id'].'_'.$buyinfo['extend']['message_topic'];
|
||
break;
|
||
case 'goods':
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
//获取付费VIP折扣
|
||
if($this->user){
|
||
$vipUserGoodsDiscount = \app\common\model\app\vip\CardPrivilege::getPrivilegeGoodsDiscount($this->user->id,$detail['id']);
|
||
|
||
if($vipUserGoodsDiscount){
|
||
//会员折扣金额
|
||
$current_vip_discount_price += number_format(($current_goods_total_price * (10 - $vipUserGoodsDiscount) / 10), 2, '.', '');;
|
||
}
|
||
}
|
||
break;
|
||
case 'physical':
|
||
//购买实物商品的订单 规格格式为商品价格_商品数量
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
$this->goodsList[$key]['extend_data'] = $detail['price'].'_'.$buyinfo['count'];
|
||
|
||
// 检查库存是否满足购买数量
|
||
$goodsId = $detail['id'] ?? 0;
|
||
$specType = $detail['spec_type'] ?? 'single';
|
||
$buyCount = $buyinfo['count'] ?? 1;
|
||
|
||
$skuPriceQuery = \app\admin\model\app\physical\SkuPrice::where('goods_id', $goodsId)
|
||
->where('uniacid', UNIACID);
|
||
|
||
if ($specType == 'single') {
|
||
$skuPrice = $skuPriceQuery->order('id', 'asc')->find();
|
||
} else {
|
||
$skuText = $detail['sku_text'] ?? '';
|
||
$skuPrice = $skuPriceQuery->where('goods_sku_text', $skuText)->find();
|
||
}
|
||
|
||
if (!$skuPrice) {
|
||
throw new Exception('商品规格不存在');
|
||
}
|
||
|
||
if ($skuPrice['stock'] < $buyCount) {
|
||
throw new Exception('商品库存不足');
|
||
}
|
||
|
||
// 检查限购
|
||
$goods = \app\admin\model\app\physical\Goods::where('id', $goodsId)
|
||
->where('uniacid', UNIACID)
|
||
->find();
|
||
|
||
if ($goods) {
|
||
$singleLimit = $goods['single_limit'] ?? 0;
|
||
$lifetimeLimit = $goods['lifetime_limit'] ?? 0;
|
||
|
||
// 检查单次限购
|
||
if ($singleLimit > 0 && $buyCount > $singleLimit) {
|
||
throw new Exception('单次限购' . $singleLimit . '件');
|
||
}
|
||
|
||
// 检查终身限购
|
||
if ($lifetimeLimit > 0 && $this->user) {
|
||
$purchasedCount = \app\common\model\order\Item::where('user_id', $this->user->id)
|
||
->where('item_id', $goodsId)
|
||
->where('goods_type', 'physical')
|
||
->where('refund_status', '<>', 3)
|
||
->sum('count');
|
||
|
||
if (($purchasedCount + $buyCount) > $lifetimeLimit) {
|
||
throw new Exception('终身限购' . $lifetimeLimit . '件,已购买' . $purchasedCount . '件');
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case 'composite':
|
||
if (!\app\admin\library\project\App::isInstall('composite')) {
|
||
throw new \app\common\exception\Exception('组合商品未安装');
|
||
}
|
||
// 组合商品:extend_data 存储规格ID和规格价格的组合
|
||
$this->goodsList[$key]['extend_data'] = $detail['spec_id'].'_'.$detail['price'];
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
|
||
// 组合原价(划线价)
|
||
if (isset($detail['price_marking'])) {
|
||
$current_goods_total_price_marking = ($detail['price_marking'] * $buyinfo['count']);
|
||
}
|
||
break;
|
||
|
||
case 'vipcard':
|
||
//购买会员卡的订单 规格格式为会员卡价格_又下时间
|
||
$this->goodsList[$key]['extend_data'] = $detail['price'].'_'.$detail['time'];
|
||
|
||
//判断会员卡配置 换购,状态
|
||
$cardConfig = \app\common\model\app\Config::getConfig('vip');
|
||
|
||
if($cardConfig['status'] != 1){
|
||
throw new Exception('会员卡购买通道暂时关闭');
|
||
}
|
||
//付费会员卡逻辑
|
||
$userVipInfo = \app\common\model\app\vip\CardUser::getUserVipInfo($this->user->id);
|
||
//判断会员卡换购状态
|
||
if($cardConfig['exchange'] != 1){
|
||
if($userVipInfo && $userVipInfo['card_id'] != $detail['id']){
|
||
throw new Exception('暂不支持会员卡换购');
|
||
}
|
||
}
|
||
//限购
|
||
if($detail['limit'] != 0){
|
||
$orderCount = OrderItem::with(['order'=>function($query){
|
||
$query->where([
|
||
'status'=>\app\common\constant\order\Status::STATUS_SUCCESS
|
||
]);
|
||
}])->where([
|
||
'extend_data'=>$this->goodsList[$key]['extend_data'],
|
||
'item_id'=>$detail['id'],
|
||
'user_id'=>$this->user->id
|
||
])->count();
|
||
|
||
if($orderCount >= $detail['limit']){
|
||
throw new Exception("该会员卡规格限购{$detail['limit']}次,当前已购{$orderCount}次");
|
||
}
|
||
}
|
||
|
||
$current_goods_total_price = ($detail['price'] * $buyinfo['count']);
|
||
$this->orderData['goods_total_price'] += $current_goods_total_price;
|
||
break;
|
||
}
|
||
|
||
|
||
|
||
//会员折扣金额
|
||
$this->goodsList[$key]['vip_discount_price'] = $current_vip_discount_price;
|
||
$this->goodsList[$key]['discount_price'] += $current_vip_discount_price;
|
||
|
||
$this->orderData['vip_discount_price'] += $current_vip_discount_price;
|
||
|
||
//商品划线价格
|
||
$this->goodsList[$key]['total_price_marking'] = $current_goods_total_price_marking;
|
||
//商品总价
|
||
$this->goodsList[$key]['total_price'] = $current_goods_total_price;
|
||
//商品单价
|
||
$this->goodsList[$key]['unit_price'] = $detail['price'];
|
||
$this->goodsList[$key]['item_id'] = $detail['id'];
|
||
$this->goodsList[$key]['item_name'] = $detail['name'];
|
||
|
||
//积分支付数
|
||
$this->goodsList[$key]['score_amount'] = $current_score_amount;
|
||
|
||
$this->goodsList[$key]['snapshoot'] = json_encode($detail);
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* 下单前检测,商品状态,秒杀,拼团活动状态,必要的选择项(比如下单收货地址),收货地址等
|
||
*
|
||
* @param array $params,请求参数
|
||
* @return array
|
||
*/
|
||
public function calcCheck()
|
||
{
|
||
if (!count($this->goodsList)) {
|
||
$this->exception('请选择要购买的商品');
|
||
}
|
||
|
||
if ($this->order_type == 'score' && count($this->goodsList) > 1) {
|
||
$this->exception('积分商品必须单独购买');
|
||
}
|
||
|
||
// 购买课程前表单校验
|
||
if (\app\admin\library\project\App::isInstall('form')) {
|
||
foreach ($this->goodsList as $goodsItem) {
|
||
$goodsId = $goodsItem['goodsId'] ?? 0;
|
||
if (!$goodsId) continue;
|
||
|
||
// 通过 content_id 匹配,不按 content_type(课程类商品 order 的 goodsType 是 course,
|
||
// 而 bind 表存储的实际类型如 column/video 等,直接匹配会查不到)
|
||
$bindForms = \app\admin\model\app\form\Bind::alias('bind')
|
||
->join('app_form f', 'bind.form_id = f.id')
|
||
->where('bind.content_id', $goodsId)
|
||
->where('f.bind_type', 'before_buy')
|
||
->where('f.status', 1)
|
||
->field('bind.form_id, f.name')
|
||
->select();
|
||
|
||
if (!empty($bindForms)) {
|
||
foreach ($bindForms as $bindForm) {
|
||
$submitted = \app\admin\model\app\form\Log::where([
|
||
'form_id' => $bindForm['form_id'],
|
||
'user_id' => $this->orderData['user_id']
|
||
])->count();
|
||
|
||
if (!$submitted) {
|
||
$this->exception("请先完成购买前表单:{$bindForm['name']}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检测用户优惠券是否可用
|
||
*
|
||
* @param array|object $coupon
|
||
* @param string $type
|
||
* @return array|object
|
||
*/
|
||
private function checkCoupon($coupon)
|
||
{
|
||
$can_use = true;
|
||
$cannot_use_msg = '';
|
||
// (积分或者活动 并且 优惠券不支持优惠叠加)
|
||
|
||
if($this->order_type == 'score'){
|
||
$can_use = false;
|
||
$cannot_use_msg = '积分商品不可使用优惠券';
|
||
return compact('can_use', 'cannot_use_msg');
|
||
}
|
||
|
||
if ($this->orderData['vip_discount_price'] > 0 && $coupon->coupon->double_discount_status == 2) {
|
||
$can_use = false;
|
||
$cannot_use_msg = '该优惠券不可与会员叠加使用';
|
||
return compact('can_use', 'cannot_use_msg');
|
||
}
|
||
|
||
if($coupon->coupon->status == 3){
|
||
$can_use = false;
|
||
$cannot_use_msg = '该优惠券已作废';
|
||
return compact('can_use', 'cannot_use_msg');
|
||
}
|
||
|
||
$goods_total_price = 0;
|
||
|
||
$goodsIds = []; // 符合优惠券规则的 商品 ids
|
||
|
||
|
||
$goods_bind_ids = json_encode($coupon->goods_bind_ids);
|
||
$goods_bind_ids = str_replace("column","course",$goods_bind_ids);
|
||
$goods_bind_ids = str_replace("video","course",$goods_bind_ids);
|
||
$goods_bind_ids = str_replace("article","course",$goods_bind_ids);
|
||
$goods_bind_ids = str_replace("audio","course",$goods_bind_ids);
|
||
$goods_bind_ids = str_replace("live","course",$goods_bind_ids);
|
||
$goods_bind_ids = json_decode($goods_bind_ids,true);
|
||
|
||
if ($coupon->use_scope == 'all_use') {
|
||
// 计算商品总金额
|
||
foreach ($this->goodsList as $buyInfo) {
|
||
$buyInfo['goods_total_price'] = $buyInfo['detail']['price'] * $buyInfo['count'];
|
||
$goods_total_price = bcadd($goods_total_price, $buyInfo['total_price'], 2);
|
||
$goodsIds[] = $buyInfo['goodsId'];
|
||
}
|
||
} elseif ($coupon->use_scope == 'goods') {
|
||
// 计算指定商品的总金额是否满足
|
||
foreach ($this->goodsList as $buyInfo) {
|
||
|
||
if($buyInfo['goodsType'] == 'activity'){
|
||
$buyInfo['goodsId'] = $buyInfo['detail']['id'];
|
||
}
|
||
|
||
if (in_array($buyInfo['goodsId']."_".$buyInfo['goodsType'], $goods_bind_ids)) {
|
||
$goods_total_price = bcadd($goods_total_price, $buyInfo['total_price'], 2);
|
||
$goodsIds[] = $buyInfo['goodsId'];
|
||
}
|
||
}
|
||
|
||
} elseif ($coupon->use_scope == 'disabled_goods') {
|
||
// 计算非指定的商品的总金额是否满足
|
||
foreach ($this->goodsList as $buyInfo) {
|
||
|
||
if($buyInfo['goodsType'] == 'activity'){
|
||
$buyInfo['goodsId'] = $buyInfo['detail']['id'];
|
||
}
|
||
|
||
if (!in_array($buyInfo['goodsId']."_".$buyInfo['goodsType'], $goods_bind_ids)) {
|
||
$goods_total_price = bcadd($goods_total_price, $buyInfo['total_price'], 2);
|
||
$goodsIds[] = $buyInfo['goodsId'];
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if($goods_total_price == 0){
|
||
$can_use = false;
|
||
$cannot_use_msg = '优惠券不支持该商品';
|
||
return compact('can_use', 'cannot_use_msg');
|
||
}
|
||
|
||
if (floatval($coupon->enough) <= $goods_total_price) {
|
||
$coupon->coupon_money = $coupon->amount;
|
||
$coupon->goods_total_price = $goods_total_price; // 参与优惠券的商品的总金额
|
||
$coupon->goods_ids = $goodsIds;
|
||
|
||
if ($coupon->type == 'discount') {
|
||
$scale = bcmul($coupon->coupon->discount, 0.1, 3);
|
||
|
||
$scale = $scale > 1 ? 1 : ($scale < 0 ? 0 : $scale);
|
||
$coupon_money = bcmul(bcsub(1, $scale, 3), $goods_total_price, 2);
|
||
// 优惠金额最大为 coupon->max_amount;
|
||
|
||
$coupon['coupon_money'] = floatval($coupon->max_amount) && $coupon_money > $coupon->max_amount ? $coupon->max_amount : $coupon_money;
|
||
}
|
||
|
||
$this->orderData['coupon'] = $coupon;
|
||
|
||
} else {
|
||
$can_use = false;
|
||
if ($goods_total_price > 0) {
|
||
$cannot_use_msg = '商品金额不满足优惠券门槛';
|
||
} else {
|
||
$cannot_use_msg = '优惠券不支持该商品';
|
||
}
|
||
}
|
||
|
||
return compact('can_use', 'cannot_use_msg');
|
||
}
|
||
|
||
/**
|
||
* 获取用户的优惠券列表,可用和不可用的做区分
|
||
* @return array
|
||
*/
|
||
public function getCoupons($calc_type = 'coupons')
|
||
{
|
||
|
||
|
||
$user = user_info();
|
||
|
||
$this->calc_type = $calc_type;
|
||
// 检查是否可下单
|
||
$this->calcCheck();
|
||
|
||
// 计算订单各种费用
|
||
$this->calcAmount();
|
||
|
||
|
||
// 用户可用优惠券列表
|
||
$coupons = \app\admin\model\app\coupon\CouponUser::with('coupon')->where('user_id', $user->id)->canUse()->select();
|
||
|
||
$cannot_use = [];
|
||
$can_use = [];
|
||
foreach ($coupons as $key => $coupon) {
|
||
$result = $this->checkCoupon($coupon);
|
||
if (isset($result['can_use']) && $result['can_use']) {
|
||
$can_use[] = $coupon;
|
||
} else {
|
||
$coupon->cannot_use_msg = $result['cannot_use_msg'];
|
||
$cannot_use[] = $coupon;
|
||
}
|
||
}
|
||
|
||
return compact('can_use', 'cannot_use');
|
||
}
|
||
|
||
public function getCoupon($coupon_id)
|
||
{
|
||
$user = user_info();
|
||
|
||
// 选用的优惠券
|
||
$coupon = \app\admin\model\app\coupon\CouponUser::with('coupon')->where('user_id', $user->id)->canUse()->find($coupon_id);
|
||
|
||
if ($coupon) {
|
||
$result = $this->checkCoupon($coupon);
|
||
|
||
|
||
return (isset($result['can_use']) && $result['can_use']) ? $coupon : null;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 判断并处理异常
|
||
*
|
||
* @param string $msg 处理结果
|
||
* @param boolean $force 是否强制抛出异常
|
||
* @return boolean
|
||
*/
|
||
private function exception($msg, $force = false)
|
||
{
|
||
if ($this->calc_type == 'create' || $force) {
|
||
// 如果是创建订单,或者强制抛出异常
|
||
throw new \app\common\exception\Exception($msg);
|
||
} else {
|
||
// 预下单,记录第一条错误信息
|
||
if (!$this->msg) {
|
||
$this->msg = $msg;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 保存订单地址
|
||
*
|
||
* @param int $orderId 订单ID
|
||
* @param array $orderData 订单数据
|
||
* @return void
|
||
*/
|
||
private function saveOrderAddress($orderId, $orderData)
|
||
{
|
||
$addressId = $this->orderData['address_id'];
|
||
|
||
// 获取用户地址
|
||
$address = \think\Db::name('user_address')
|
||
->where('id', $addressId)
|
||
->where('user_id', $this->user->id)
|
||
->where('uniacid', UNIACID)
|
||
->find();
|
||
|
||
if (!$address) {
|
||
throw new \app\common\exception\Exception('收货地址不存在');
|
||
}
|
||
|
||
// 转换为数组
|
||
$address = is_object($address) ? $address->toArray() : $address;
|
||
|
||
// 保存订单地址
|
||
$orderAddressData = [
|
||
'order_id' => $orderId,
|
||
'user_id' => $this->user->id,
|
||
'consignee' => $address['name'],
|
||
'mobile' => $address['mobile'],
|
||
'province_name' => $address['province'],
|
||
'city_name' => $address['city'],
|
||
'district_name' => $address['district'],
|
||
'address' => $address['address'],
|
||
'province_id' => $address['province_id'] ?? 0,
|
||
'city_id' => $address['city_id'] ?? 0,
|
||
'district_id' => $address['district_id'] ?? 0,
|
||
'uniacid' => UNIACID,
|
||
'createtime' => time(),
|
||
'updatetime' => time()
|
||
];
|
||
|
||
\think\Db::name('app_physical_order_address')->insert($orderAddressData);
|
||
}
|
||
|
||
}
|