Files
oil/server-tp6/app/api/service/order/Checkout.php
T
vipnub fafd3e2c16 1、合作商余额查询
2、分油账户司机可用额度
2026-10-20 23:21:10 +08:00

980 lines
36 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
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\api\service\order;
use app\api\model\Order as OrderModel;
use app\api\model\User as UserModel;
use app\api\model\Merchant as MerchantModel;
use app\api\model\Goods as GoodsModel;
use app\api\model\Setting as SettingModel;
use app\api\model\UserCoupon as UserCouponModel;
use app\api\service\User as UserService;
use app\api\service\Payment as PaymentService;
use app\api\service\user\Grade as UserGradeService;
use app\api\service\coupon\GoodsDeduct as GoodsDeductService;
use app\api\service\points\GoodsDeduct as PointsDeductService;
use app\api\service\order\source\checkout\Factory as CheckoutFactory;
use app\common\enum\Setting as SettingEnum;
use app\common\enum\order\PayType as OrderPayTypeEnum;
use app\common\enum\order\OrderStatus as OrderStatusEnum;
use app\common\enum\order\OrderSource as OrderSourceEnum;
use app\common\enum\order\DeliveryType as DeliveryTypeEnum;
use app\common\lock\RedisLock;
use app\common\service\BaseService;
use app\common\service\delivery\Express as ExpressService;
use app\common\service\goods\source\Factory as StockFactory;
use app\common\library\helper;
use app\common\service\Order as OrderService;
use app\store\model\Company as CompanyModel;
use cores\exception\BaseException;
use think\facade\Db;
use app\common\service\SybOil;
use app\common\model\OrderGoods as OrderGoodsModel;
/**
* 订单结算台服务类
* Class Checkout
* @package app\api\service\order
*/
class Checkout extends BaseService
{
/* $model OrderModel 订单模型 */
public $model;
/* @var UserModel $user 当前用户信息 */
private $user;
// 订单结算商品列表
private $goodsList = [];
/**
* 订单结算api参数
* @var array
*/
private $param = [
'delivery' => null, // 配送方式
'couponId' => 0, // 用户的优惠券ID
'isUsePoints' => 0, // 是否使用积分抵扣
'remark' => '', // 买家留言
'payType' => OrderPayTypeEnum::WECHAT, // 支付方式
];
/**
* 订单结算的规则
* @var array
*/
private $checkoutRule = [
'isUserGrade' => true, // 会员等级折扣
'isCoupon' => true, // 优惠券抵扣
'isUsePoints' => true, // 是否使用积分抵扣
];
/**
* 订单来源
* @var array
*/
private $orderSource = [
'source' => OrderSourceEnum::MAIN,
'source_id' => 0,
];
/**
* 订单结算数据
* @var array
*/
private $orderData = [];
/**
* 构造函数
* Checkout constructor.
* @throws BaseException
*/
public function __construct()
{
parent::__construct();
$this->user = UserService::getCurrentLoginUser(true);
$this->model = new OrderModel;
$this->storeId = $this->getStoreId();
}
/**
* 设置结算台请求的参数
* @param $param
* @return array
*/
public function setParam($param): array
{
$this->param = array_merge($this->param, $param);
return $this->getParam();
}
/**
* 获取结算台请求的参数
* @return array
*/
public function getParam(): array
{
return $this->param;
}
/**
* 订单结算的规则
* @param $data
* @return $this
*/
public function setCheckoutRule($data): Checkout
{
$this->checkoutRule = array_merge($this->checkoutRule, $data);
return $this;
}
/**
* 设置订单来源(普通订单)
* @param $data
* @return $this
*/
public function setOrderSource($data): Checkout
{
$this->orderSource = array_merge($this->orderSource, $data);
return $this;
}
/**
* 订单确认-结算台
* @param $goodsList
* @return array
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function onCheckout($goodsList): array
{
// 订单确认-立即购买
$this->goodsList = $goodsList;
return $this->checkout();
}
/**
* 订单结算台
* @return array
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function checkout(): array
{
// 整理订单数据
$this->orderData = $this->getOrderData();
// 验证商品状态, 是否允许购买
$this->validateGoodsList();
// 订单商品总数量
$orderTotalNum = (int)helper::getArrayColumnSum($this->goodsList, 'total_num');
// 设置订单商品会员折扣价
$this->setOrderGoodsGradeMoney();
// 设置订单商品总金额(不含优惠折扣)
$this->setOrderTotalPrice();
// 当前用户可用的优惠券列表
$couponList = $this->getUserCouponList((float)$this->orderData['orderTotalPrice']);
// 计算优惠券抵扣
$this->setOrderCouponMoney($couponList, (int)$this->param['couponId']);
// 计算可用积分抵扣
$this->setOrderPoints();
// 计算订单商品的实际付款金额
$this->setOrderGoodsPayPrice();
// 设置默认配送方式
if (!$this->param['delivery']) {
$deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
$this->param['delivery'] = current($deliveryType);
}
// 处理配送方式
if ($this->param['delivery'] == DeliveryTypeEnum::EXPRESS) {
$this->setOrderExpress();
}
// 计算订单最终金额
$this->setOrderPayPrice();
// 计算订单积分赠送数量
$this->setOrderPointsBonus();
// 返回订单数据
return array_merge([
'goodsList' => $this->goodsList, // 商品信息
'orderTotalNum' => $orderTotalNum, // 商品总数量
'couponList' => array_values($couponList), // 优惠券列表
'hasError' => $this->hasError(),
'errorMsg' => $this->getError(),
], $this->orderData);
}
/**
* 计算订单可用积分抵扣
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function setOrderPoints(): void
{
// 设置默认的商品积分抵扣信息
$this->setDefaultGoodsPoints();
// 积分设置
$setting = SettingModel::getItem('points');
// 条件:后台开启下单使用积分抵扣
if (!$setting['is_shopping_discount'] || !$this->checkoutRule['isUsePoints']) {
return;
}
// 条件:订单金额满足[?]元
if (helper::bccomp($setting['discount']['full_order_price'], $this->orderData['orderTotalPrice']) === 1) {
return;
}
// 计算订单商品最多可抵扣的积分数量
$this->setOrderGoodsMaxPointsNum();
// 订单最多可抵扣的积分总数量
$maxPointsNumCount = (int)helper::getArrayColumnSum($this->goodsList, 'max_points_num');
// 实际可抵扣的积分数量
$actualPointsNum = min($maxPointsNumCount, $this->user['points']);
if ($actualPointsNum < 1) {
return;
}
// 计算订单商品实际抵扣的积分数量和金额
$GoodsDeduct = new PointsDeductService($this->goodsList);
$GoodsDeduct->setGoodsPoints($maxPointsNumCount, $actualPointsNum);
// 积分抵扣总金额
$orderPointsMoney = helper::getArrayColumnSum($this->goodsList, 'points_money');
$this->orderData['pointsMoney'] = helper::number2($orderPointsMoney);
// 积分抵扣总数量
$this->orderData['pointsNum'] = $actualPointsNum;
// 允许积分抵扣
$this->orderData['isAllowPoints'] = true;
}
/**
* 计算订单商品最多可抵扣的积分数量
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function setOrderGoodsMaxPointsNum(): void
{
// 积分设置
$setting = SettingModel::getItem('points');
foreach ($this->goodsList as &$goods) {
// 商品不允许积分抵扣
if (!$goods['is_points_discount']) continue;
// 积分抵扣比例
$deductionRatio = helper::bcdiv($setting['discount']['max_money_ratio'], 100);
// 最多可抵扣的金额
$totalPayPrice = helper::bcsub($goods['total_price'], $goods['coupon_money']);
$maxPointsMoney = helper::bcmul($totalPayPrice, $deductionRatio);
// 最多可抵扣的积分数量
$goods['max_points_num'] = helper::bcdiv($maxPointsMoney, $setting['discount']['discount_ratio'], 0);
}
}
/**
* 设置默认的商品积分抵扣信息
* @return void
*/
private function setDefaultGoodsPoints(): void
{
foreach ($this->goodsList as &$goods) {
// 最多可抵扣的积分数量
$goods['max_points_num'] = 0;
// 实际抵扣的积分数量
$goods['pointsNum'] = 0;
// 实际抵扣的金额
$goods['points_money'] = 0.00;
}
}
/**
* 整理订单数据(结算台初始化)
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function getOrderData(): array
{
// 整理当前订单商品类型
$types = helper::getArrayColumn($this->goodsList, 'oilCardType');
// 系统支持的配送方式 (后台设置)
$deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
return [
// 当前配送类型
'delivery' => $this->param['delivery'] > 0 ? $this->param['delivery'] : $deliveryType[0],
// 默认地址
'address' => $this->user['address_default'],
// 是否存在收货地址
'existAddress' => $this->user['address_id'] > 0,
// 配送费用
'expressPrice' => 0.00,
// 当前用户收货城市是否存在配送规则中
'isIntraRegion' => true,
// 是否允许使用积分抵扣
'isAllowPoints' => false,
// 是否使用积分抵扣
'isUsePoints' => $this->param['isUsePoints'],
// 积分抵扣金额
'pointsMoney' => 0.00,
// 赠送的积分数量
'pointsBonus' => 0,
// 支付方式
'payType' => $this->param['payType'],
// 订单类型
'order_type' => $types[0] ?? '',
// 系统设置 TODO: 废弃
'setting' => $this->getSetting(),
];
}
/**
* 获取订单页面中使用到的系统设置
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSetting(): array
{
// 系统支持的配送方式 (后台设置)
$deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
// 积分设置
$pointsSetting = SettingModel::getItem(SettingEnum::POINTS);
return [
'deliveryType' => $deliveryType, // 支持的配送方式
'points_name' => $pointsSetting['points_name'], // 积分名称
'points_describe' => $pointsSetting['describe'], // 积分说明
];
}
// 获取订单结算时的个人信息
public function getPersonal(): array
{
return [
'user_id' => $this->user['user_id'],
'balance' => $this->user['balance'],
'points' => $this->user['points'],
'address_id' => $this->user['address_id'],
// 实名信息(分油实名制,下单前必须补全):结算页据此拦截并跳去补全
'real_name' => (string)$this->user['real_name'],
'id_card' => (string)$this->user['id_card'],
'plate_no' => (string)$this->user['plate_no'],
'isRealnameReady' => UserModel::isRealnameReady($this->user),
'hasOilGoods' => $this->hasOilGoods(),
'needRealname' => $this->hasOilGoods() && !UserModel::isRealnameReady($this->user),
];
}
/**
* 本次结算是否包含油券商品(oilCardType = zsy_ticket)
* 油券要走企油通分油,必须有姓名/身份证号/车牌号
*/
private function hasOilGoods(): bool
{
foreach ($this->goodsList as $goods) {
if (($goods['oilCardType'] ?? '') === 'zsy_ticket') {
return true;
}
}
return false;
}
/**
* 当前用户可用的优惠券列表
* @param float $orderTotalPrice 总金额
* @return array|mixed
* @throws \think\db\exception\DbException
*/
private function getUserCouponList(float $orderTotalPrice)
{
// 是否开启优惠券折扣
if (!$this->checkoutRule['isCoupon']) {
return [];
}
// 整理当前订单所有商品ID集
$orderGoodsIds = helper::getArrayColumn($this->goodsList, 'goods_id');
// 当前用户可用的优惠券列表
$couponList = UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice);
// 判断当前优惠券是否满足订单使用条件 (优惠券适用范围)
return UserCouponModel::couponListApplyRange($couponList, $orderGoodsIds);
}
/**
* 验证订单商品的状态
* @return void
*/
private function validateGoodsList(): void
{
$Checkout = CheckoutFactory::getFactory(
$this->user,
$this->goodsList,
$this->orderSource['source'],
$this->orderData
);
$status = $Checkout->validateGoodsList();
$status == false && $this->setError($Checkout->getError());
}
/**
* 设置订单的商品总金额(不含优惠折扣)
*/
private function setOrderTotalPrice()
{
// 订单商品的总金额(不含优惠券折扣)
$this->orderData['orderTotalPrice'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_price'));
}
/**
* 设置订单的实际支付金额(含配送费)
*/
private function setOrderPayPrice()
{
// 订单金额(含优惠折扣)
$this->orderData['orderPrice'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_pay_price'));
// 订单实付款金额(订单金额 + 运费)
$this->orderData['orderPayPrice'] = helper::number2(helper::bcadd($this->orderData['orderPrice'], $this->orderData['expressPrice']));
}
/**
* 计算订单积分赠送数量
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function setOrderPointsBonus(): void
{
// 初始化商品积分赠送数量
foreach ($this->goodsList as &$goods) {
$goods['points_bonus'] = 0;
}
// 积分设置
$setting = SettingModel::getItem('points');
// 条件:后台开启开启购物送积分
if (!$setting['is_shopping_gift']) {
return;
}
// 设置商品积分赠送数量
foreach ($this->goodsList as &$goods) {
// 积分赠送比例
$ratio = helper::bcdiv($setting['gift_ratio'], 100);
// 计算抵扣积分数量
$goods['points_bonus'] = !$goods['is_points_gift'] ? 0 : helper::bcmul($goods['total_pay_price'], $ratio, 0);
}
// 订单积分赠送数量
$this->orderData['pointsBonus'] = (int)helper::getArrayColumnSum($this->goodsList, 'points_bonus');
}
/**
* 计算订单商品的实际付款金额
* @return void
*/
private function setOrderGoodsPayPrice(): void
{
// 商品总价 - 优惠抵扣
foreach ($this->goodsList as &$goods) {
// 减去优惠券抵扣金额
$value = helper::bcsub($goods['total_price'], $goods['coupon_money']);
// 减去积分抵扣金额
if ($this->orderData['isAllowPoints'] && $this->orderData['isUsePoints']) {
$value = helper::bcsub($value, $goods['points_money']);
}
$goods['total_pay_price'] = helper::number2($value);
}
}
/**
* 设置订单商品会员折扣价
* @return void
* @throws BaseException
*/
private function setOrderGoodsGradeMoney(): void
{
// 设置默认数据
helper::setDataAttribute($this->goodsList, [
// 标记参与会员折扣
'is_user_grade' => false,
// 会员等级抵扣的金额
'grade_ratio' => 0,
// 会员折扣的商品单价
'grade_goods_price' => 0.00,
// 会员折扣的总额差
'grade_total_money' => 0.00,
], true);
// 是否开启会员等级折扣
if (!$this->checkoutRule['isUserGrade']) {
return;
}
// 获取当前登录用户的会员等级信息
$gradeInfo = UserGradeService::getCurrentGradeInfo();
// 判断商品是否参与会员折扣
if (empty($gradeInfo)) {
return;
}
// 计算抵扣金额
foreach ($this->goodsList as &$goods) {
// 判断商品是否参与会员折扣
if (!$goods['is_enable_grade']) {
continue;
}
// 折扣比例
$discountRatio = $gradeInfo['equity']['discount'];
// 商品单独设置了会员折扣
if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$this->user['grade_id']])) {
$discountRatio = $goods['alone_grade_equity'][$gradeInfo['grade_id']];
}
if (empty($discountRatio)) {
continue;
}
// 会员折扣后的商品总金额
$gradeTotalPrice = UserGradeService::getDiscountPrice($goods['total_price'], $discountRatio);
helper::setDataAttribute($goods, [
'is_user_grade' => true,
'grade_ratio' => $discountRatio,
'grade_goods_price' => UserGradeService::getDiscountPrice($goods['goods_price'], $discountRatio),
'grade_total_money' => helper::bcsub($goods['total_price'], $gradeTotalPrice),
'total_price' => $gradeTotalPrice,
], false);
}
}
/**
* 设置订单优惠券抵扣信息
* @param array $couponList 当前用户可用的优惠券列表
* @param int $userCouponId 当前选择的优惠券ID
* @return void
* @throws BaseException
*/
private function setOrderCouponMoney(array $couponList, int $userCouponId): void
{
// 默认选择优惠券
if (empty($userCouponId)) {
foreach ($couponList as $item) {
if (!empty($item['is_apply'])) { // 如果 is_apply 为 true 或非空
$userCouponId = $item['user_coupon_id'] ?? 0;
break; // 找到第一个就退出
}
}
}
// 设置默认数据:订单信息
helper::setDataAttribute($this->orderData, [
'couponId' => 0, // 用户的优惠券ID
'couponMoney' => 0, // 优惠券抵扣金额
], false);
// 设置默认数据:订单商品列表
helper::setDataAttribute($this->goodsList, [
'coupon_money' => 0, // 优惠券抵扣金额
], true);
// 验证选择的优惠券ID是否合法
if (!$this->verifyOrderCouponId($userCouponId, $couponList)) {
return;
}
// 获取优惠券信息
$couponInfo = $this->getCouponInfo($userCouponId, $couponList);
// 计算订单商品优惠券抵扣金额
$goodsListTemp = helper::getArrayColumns($this->goodsList, ['goods_id', 'goods_sku_id', 'total_price']);
$CouponMoney = new GoodsDeductService;
$rangeGoodsList = $CouponMoney->setGoodsList($goodsListTemp)
->setCouponInfo($couponInfo)
->setGoodsCouponMoney()
->getRangeGoodsList();
// 分配订单商品优惠券抵扣金额
foreach ($this->goodsList as &$goods) {
$goodsKey = "{$goods['goods_id']}-{$goods['goods_sku_id']}";
if (isset($rangeGoodsList[$goodsKey])) {
$goods['coupon_money'] = helper::bcdiv($rangeGoodsList[$goodsKey]['coupon_money'], 100);
}
}
// 记录订单优惠券信息
$this->orderData['couponId'] = $userCouponId;
$this->orderData['couponMoney'] = helper::number2(helper::bcdiv($CouponMoney->getActualReducedMoney(), 100));
}
/**
* 验证用户选择的优惠券ID是否合法
* @param int $userCouponId
* @param $couponList
* @return bool
* @throws BaseException
*/
private function verifyOrderCouponId(int $userCouponId, $couponList): bool
{
// 是否开启优惠券折扣
if (!$this->checkoutRule['isCoupon']) {
return false;
}
// 如果没有可用的优惠券,直接返回
if ($userCouponId <= 0 || empty($couponList)) {
return false;
}
// 判断优惠券是否存在
$couponInfo = $this->getCouponInfo($userCouponId, $couponList);
if (!$couponInfo) {
throwError('未找到优惠券信息');
}
// 判断优惠券适用范围是否合法
if (!$couponInfo['is_apply']) {
throwError($couponInfo['not_apply_info']);
}
return true;
}
/**
* 查找指定的优惠券信息
* @param int $userCouponId 优惠券ID
* @param array $couponList 优惠券列表
* @return false|mixed
*/
private function getCouponInfo(int $userCouponId, array $couponList)
{
return helper::getArrayItemByColumn($couponList, 'user_coupon_id', $userCouponId);
}
/**
* 订单配送-快递配送
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function setOrderExpress(): void
{
// 设置默认数据:配送费用
helper::setDataAttribute($this->goodsList, [
'expressPrice' => 0,
], true);
// 当前用户收货城市id
$cityId = $this->user['address_default'] ? (int)$this->user['address_default']['city_id'] : 0;
// 初始化配送服务类
$ExpressService = new ExpressService($cityId, $this->goodsList);
// 验证商品是否在配送范围
$isIntraRegion = $ExpressService->isIntraRegion();
if ($cityId > 0 && $isIntraRegion == false) {
$notInRuleGoodsName = $ExpressService->getNotInRuleGoodsName();
$this->setError("很抱歉,您的收货地址不在商品 [{$notInRuleGoodsName}] 的配送范围内");
}
// 订单总运费金额
$this->orderData['isIntraRegion'] = $isIntraRegion;
$this->orderData['expressPrice'] = $ExpressService->getDeliveryFee();
}
/**
* 创建新订单
* @param array $order 订单信息
* @return bool
*/
public function createOrder(array $order): bool
{
// 表单验证
if (!$this->validateOrderForm($order)) {
return false;
}
// 创建新的订单
$status = $this->model->transaction(function () use ($order) {
// 创建订单事件
if ($this->createOrderEvent($order)) {
// 余额支付标记订单已支付
if ($order['payType'] == OrderPayTypeEnum::BALANCE && !$this->model->onPaymentByBalance($this->model['order_no'])) {
throwError($this->error);
}else{
return true;
}
} else {
// 抛出异常以触发事务回滚
throwError($this->error);
}
});
return $status;
}
/**
* 创建订单事件
* @param $order
* @return bool
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function createOrderEvent($order): bool
{
// 新增订单记录
$status = $this->add($order, $this->param['remark']);
/*if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
// 记录收货地址
$this->saveOrderAddress($order['address']);
}*/
// 保存订单商品信息
$goodsStatus = $this->saveOrderGoods($order);
// 更新商品库存 (针对下单减库存的商品)
$this->updateGoodsStockNum($order);
// 设置优惠券使用状态
$order['couponId'] > 0 && UserCouponModel::setIsUse((int)$order['couponId']);
// 积分抵扣情况下扣除用户积分
if ($order['isAllowPoints'] && $order['isUsePoints'] && $order['pointsNum'] > 0) {
$describe = "用户消费:{$this->model['order_no']}";
UserModel::setIncPoints($this->user['user_id'], -$order['pointsNum'], $describe);
}
// 获取订单详情
// $detail = OrderModel::getUserOrderDetail((int)$this->model['order_id']);
return $status && $goodsStatus;
}
/**
* 构建支付请求的参数
* @return array
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function onOrderPayment(): array
{
return PaymentService::orderPayment($this->model, $this->param['payType']);
}
/**
* 表单验证 (订单提交)
* @param array $order 订单信息
* @return bool
*/
private function validateOrderForm(array $order): bool
{
/*if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
if (empty($order['address'])) {
$this->error = '您还没有选择配送地址';
return false;
}
}*/
// 余额支付时判断用户余额是否足够
/*if ($order['payType'] != OrderPayTypeEnum::BALANCE) {
$this->error = '目前仅支持余额支付';
return false;
}*/
// 余额支付时判断用户余额是否足够
if ($order['payType'] == OrderPayTypeEnum::BALANCE) {
if ($this->user['balance'] < $order['orderPayPrice']) {
$this->error = '您的余额不足,无法使用余额支付';
return false;
}
}
// 油券商品要走企油通分油(实名制:上游做姓名+身份证号二要素认证),
// 下单前必须补全实名信息。结算页已拦截,这里是服务端兜底。
if ($this->hasOilGoods() && !UserModel::isRealnameReady($this->user)) {
$this->error = '请先完善姓名、身份证号和车牌号后再下单';
return false;
}
return true;
}
/**
* 当前订单是否存在和使用积分抵扣
* @param $order
* @return bool
*/
private function isExistPointsDeduction($order): bool
{
return $order['isAllowPoints'] && $order['isUsePoints'];
}
/**
* 新增订单记录
* @param $order
* @param string $remark
* @return bool|false
*/
private function add($order, string $remark = ''): bool
{
// 当前订单是否存在和使用积分抵扣
$isExistPointsDeduction = $this->isExistPointsDeduction($order);
// 订单数据
$data = [
'user_id' => $this->user['user_id'],
'order_no' => $this->model->orderNo(),
'order_type' => $order['order_type'],
'total_price' => $order['orderTotalPrice'],
'order_price' => $order['orderPrice'],
'coupon_id' => $order['couponId'],
'coupon_money' => $order['couponMoney'],
'points_money' => $isExistPointsDeduction ? $order['pointsMoney'] : 0.00,
'points_num' => $isExistPointsDeduction ? $order['pointsNum'] : 0,
'pay_price' => $order['orderPayPrice'],
'delivery_type' => $order['delivery'],
'pay_type' => $order['payType'],
'buyer_remark' => trim($remark),
'order_source' => $this->orderSource['source'],
'order_source_id' => $this->orderSource['source_id'],
'points_bonus' => $order['pointsBonus'],
'order_status' => OrderStatusEnum::NORMAL,
'platform' => getPlatform(),
'store_id' => $this->storeId,
];
if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
$data['express_price'] = $order['expressPrice'];
}
// 计算返佣
if($this->user['referrer_id'] > 0) {
// 分销设置
$setting = SettingModel::getItem('promotion');
// 查询一级返佣
$spread = MerchantModel::detail($this->user['referrer_id']);
$data['spread_uid'] = $spread['merchant_id'];
$data['one_brokerage_ratio'] = round($setting['one_brokerage_ratio']/100,2);
$data['one_brokerage'] = round($data['pay_price']*$data['one_brokerage_ratio'],2);
// 查询二级返佣
if($spread['parent_merchant_id'] > 0) {
$spreadTwo = MerchantModel::detail($spread['parent_merchant_id']);
$data['spread_two_uid'] = $spreadTwo['merchant_id'];
$data['two_brokerage_ratio'] = round($setting['two_brokerage_ratio']/100,2);
$data['two_brokerage'] = round($data['pay_price']*$data['two_brokerage_ratio'],2);
}
}
// 保存订单记录
return $this->model->save($data);
}
public static function createOrderNo(): string
{
// 获取当前时间戳到毫秒(13位)
$time = (int)(microtime(true) * 1000); // 返回形如 1716544103986
// 拼接一个 4 位随机数
$rand = mt_rand(1000, 9999);
return (string)($time . $rand); // 拼接成纯数字字符串
}
/**
* 保存订单商品信息
* @param $order
* @return bool
*/
private function saveOrderGoods($order): bool
{
// 当前订单是否存在和使用积分抵扣
$isExistPointsDeduction = $this->isExistPointsDeduction($order);
// 订单商品列表
$goodsList = [];
// 查询一级返佣
$spread = MerchantModel::detail($this->user['referrer_id']);
$spread_uid = $spread ? $spread['merchant_id'] : 0;
$spread_two_uid = 0;
// 查询二级返佣
if($spread && $spread['parent_merchant_id'] > 0) {
$spreadTwo = MerchantModel::detail($spread['parent_merchant_id']);
$spread_two_uid = $spreadTwo ? $spreadTwo['merchant_id'] : 0;
}
// 分销设置
$setting = SettingModel::getItem('promotion');
// 一级分销
$one_brokerage_ratio = round($setting['one_brokerage_ratio']/100,2);
// 二级分销
$two_brokerage_ratio = round($setting['two_brokerage_ratio']/100,2);
foreach ($order['goodsList'] as $goods) {
/* @var GoodsModel $goods */
$item = [
'oilCardNo' => 'OIL' . self::createOrderNo(),
'user_id' => $this->user['user_id'],
'spread_uid' => $spread_uid,
'spread_two_uid' => $spread_two_uid,
'one_brokerage_ratio' => $one_brokerage_ratio,
'one_brokerage' => ($goods['total_price'] > 0 && $one_brokerage_ratio > 0) ? round($goods['total_price'] * $one_brokerage_ratio, 2) : 0,
'two_brokerage_ratio' => $two_brokerage_ratio,
'two_brokerage' => ($goods['total_price'] > 0 && $two_brokerage_ratio > 0) ? round($goods['total_price'] * $two_brokerage_ratio,2) : 0,
'province' => $goods['province'],
'store_id' => $this->storeId,
'goods_id' => $goods['goods_id'],
'goods_name' => $goods['goods_name'],
'goods_no' => $goods['goods_no'] ?: '',
'image_id' => $this->getGoodsImageId($goods),
'deduct_stock_type' => $goods['deduct_stock_type'],
'spec_type' => $goods['spec_type'],
'goods_sku_id' => $goods['skuInfo']['goods_sku_id'],
'goods_props' => $goods['skuInfo']['goods_props'] ?: '',
'content' => $goods['content'] ?? '',
'goods_sku_no' => $goods['skuInfo']['goods_sku_no'] ?: '',
'goods_price' => $goods['skuInfo']['goods_price'],
'line_price' => $goods['skuInfo']['line_price'],
'goods_weight' => $goods['skuInfo']['goods_weight'],
'is_user_grade' => (int)$goods['is_user_grade'],
'grade_ratio' => $goods['grade_ratio'],
'grade_goods_price' => $goods['grade_goods_price'],
'grade_total_money' => $goods['grade_total_money'],
'coupon_money' => $goods['coupon_money'],
'points_money' => $isExistPointsDeduction ? $goods['points_money'] : 0.00,
'points_num' => $isExistPointsDeduction ? $goods['points_num'] : 0,
'points_bonus' => $goods['points_bonus'],
'total_num' => $goods['total_num'],
'total_price' => $goods['total_price'],
'total_pay_price' => $goods['total_pay_price'],
'oilCardType' => $goods['oilCardType'],
'expiration_time' => strtotime("+15 days")
];
// 记录订单商品来源id
$item['goods_source_id'] = isset($goods['goods_source_id']) ? $goods['goods_source_id'] : 0;
$goodsList[] = $item;
}
$this->model->goods()->saveAll($goodsList) !== false;
return true;
}
/**
* 获取订单商品的封面图(优先sku封面图)
* @param $goods
* @return int
*/
private function getGoodsImageId($goods): int
{
return $goods['skuInfo']['image_id'] ?: (int)current($goods['goods_images'])['file_id'];
}
/**
* 更新商品库存 (针对下单减库存的商品)
* @param $order
* @return void
*/
private function updateGoodsStockNum($order): void
{
StockFactory::getFactory($this->model['order_source'])->updateGoodsStock($order['goodsList']);
}
/**
* 记录收货地址
* @param $address
* @return void
*/
private function saveOrderAddress($address): void
{
$this->model->address()->save([
'user_id' => $this->user['user_id'],
'store_id' => $this->storeId,
'name' => $address['name'],
'phone' => $address['phone'],
'province_id' => $address['province_id'],
'city_id' => $address['city_id'],
'region_id' => $address['region_id'],
'detail' => $address['detail'],
]);
}
}