1, 'goods_sku_id' => 10, 'nums' => 2], * ['goods_id' => 2, 'goods_sku_id' => 20, 'nums' => 1], * ['goods_id' => 3, 'goods_sku_id' => 30, 'nums' => 3] * ]; * $orders = OrderService::createSingleGoodsOrders($addressId, $userId, $goodsList, $userCouponId, $remark); * // 返回3个订单对象的数组,每个商品对应一个订单 * * // 创建传统订单(多商品合并为一个订单) * $order = OrderService::createOrder($addressId, $userId, $goodsList, $userCouponId, $remark); * // 返回1个包含所有商品的订单对象 */ class OrderService { /** * 订单金额 * @var array */ protected static $orderPrice = [ 'amount' => 0, //订单应付金额 'goods_price' => 0, //商品总费用 'goods_num' => 0, //商品数量 'discount_fee' => 0, //优惠金额 'coupon_discount_fee' => 0, //优惠券金额 'promo_discount_fee' => 0, //营销金额 'total_discount_amount' => 0, //总优惠 'order_amount' => 0, //订单金额 'pay_amount' => 0, //实际付款金额 'pay_original_amount' => 0, //原始支付金额 'pay_remain_amount' => 0, //剩余支付金额 'express_fee' => 0, //配送费用 'activity_discount_amount' => 0, //活动折扣金额(新增) 'goods_original_price' => 0, //商品原价总额(新增) 'order_snapshot' => null, //订单快照,记录折扣等详细信息 ]; /** * 初始化订单价格 * @return array */ protected static function initOrderPrice() { return [ 'amount' => 0, //订单应付金额 'goods_price' => 0, //商品总费用 'goods_num' => 0, //商品数量 'discount_fee' => 0, //优惠金额 'coupon_discount_fee' => 0, //优惠券金额 'promo_discount_fee' => 0, //营销金额 'total_discount_amount' => 0, //总优惠 'order_amount' => 0, //订单金额 'pay_amount' => 0, //实际付款金额 'pay_original_amount' => 0, //原始支付金额 'pay_remain_amount' => 0, //剩余支付金额 'express_fee' => 0, //配送费用 'activity_discount_amount' => 0, //活动折扣金额(新增) 'goods_original_price' => 0, //商品原价总额(新增) 'order_snapshot' => null, //订单快照,记录折扣等详细信息 ]; } /** * 根据商品列表计算订单明细 * @param array $orderInfo 订单基础信息 * @param array $goods_list 商品列表 * @param int $user_id 用户ID * @param int $area_id 地区ID * @param int $user_coupon_id 优惠券ID * @return array * @throws */ protected static function computeGoods(&$orderInfo, $goodsList, $userId, $areaId, $userCouponId = 0) { // 初始化订单价格 $orderPrice = self::initOrderPrice(); foreach ($orderPrice as $key => $value) { $orderInfo[$key] = $value; } $orderItem = []; $shippingTemp = []; $userCoupon = null; $processedGoodsList = []; // 处理后的商品列表,避免与参数$goodsList冲突 // 校验优惠券 if ($userCouponId) { $userCouponModel = new UserCoupon(); $userCoupon = $userCouponModel->checkUserOrUse($userCouponId, $userId); $orderInfo['user_coupon_id'] = $userCouponId; } // 提取所有商品ID和SKU ID,进行批量查询 $goodsIds = array_column($goodsList, 'goods_id'); $skuIds = []; foreach ($goodsList as $index => $item) { if (isset($item['goods_sku_id']) && $item['goods_sku_id'] > 0) { $skuIds[] = $item['goods_sku_id']; } } // 批量查询商品信息 $goodsData = []; if (!empty($goodsIds)) { $goodsModel = new Goods(); $goodsCollection = $goodsModel->with(['brand']) ->where('id', 'in', $goodsIds) ->where('status', GoodsEnum::STATUS_ON_SALE) ->select(); foreach ($goodsCollection as $goods) { $goodsData[$goods->id] = $goods; } } // 批量查询SKU信息 $skuData = []; $multiSpecSkuIds = []; // 用于存储多规格商品的SKU ID if (!empty($skuIds)) { $skuModel = new Sku(); $skuCollection = $skuModel->where('id', 'in', $skuIds)->select(); foreach ($skuCollection as $sku) { $skuData[$sku->id] = $sku; // 过滤出有规格值的SKU ID(spec_value_ids不为空) if (!empty($sku->spec_value_ids)) { $multiSpecSkuIds[] = $sku->id; } } } // 批量查询规格属性字符串(只查询多规格商品的SKU) $skuAttrData = []; if (!empty($multiSpecSkuIds)) { $skuAttrData = SkuSpec::getSkuAttrs($multiSpecSkuIds); } // 批量查询商品的折扣信息 $goodsDiscountData = []; $orderSnapshotData = []; // 用于收集订单快照信息 if (!empty($goodsIds)) { $goodsDiscountData = DiscountService::getBatchGoodsDiscountInfo($goodsIds); } // echo "
";
// print_r($goodsDiscountData);
// echo "";
// exit;
// 验证并构建商品数据
foreach ($goodsList as $item) {
$goods_id = $item['goods_id'];
$goods_sku_id = $item['goods_sku_id']; // 现在所有商品都应该有SKU ID
$nums = $item['nums'];
if ($nums <= 0) {
throw new BusinessException("商品数量必须大于0");
}
// 检查商品是否存在
if (!isset($goodsData[$goods_id])) {
throw new BusinessException("商品已下架");
}
$goods = $goodsData[$goods_id];
// 所有商品都必须有SKU(包括单规格商品的默认SKU)
if (empty($skuData) || !isset($skuData[$goods_sku_id])) {
throw new BusinessException("商品规格不存在");
}
$sku = $skuData[$goods_sku_id];
// 验证SKU是否属于该商品
if ($sku->goods_id != $goods_id) {
throw new BusinessException("商品规格不匹配");
}
// 获取规格属性字符串(单规格商品的sku_attr为空)
$sku_attr = $skuAttrData[$goods_sku_id] ?? '';
// 构建商品对象,模拟购物车数据结构
$goodsItem = (object)[
'goods_id' => $goods_id,
'goods_sku_id' => $goods_sku_id,
'nums' => $nums,
'goods' => $goods,
'sku' => $sku,
'sku_attr' => $sku_attr
];
$processedGoodsList[] = $goodsItem;
}
// 计算商品价格和运费(统一使用SKU进行计算)
foreach ($processedGoodsList as $item) {
$goodsItemData = [];
if (empty($item->goods) || empty($item->sku)) {
throw new BusinessException("商品已下架");
}
// 库存验证(统一使用SKU库存)
if ($item->sku->stocks < $item->nums) {
throw new BusinessException("商品库存不足,请重新修改数量");
}
// 获取商品折扣信息
$discountInfo = null;
$actualPrice = $item->sku->price; // 默认使用原价
$originalPrice = $item->sku->price;
if (isset($goodsDiscountData[$item->goods_id])) {
// 查找对应SKU的折扣信息
foreach ($goodsDiscountData[$item->goods_id] as $discount) {
if ($discount['sku_id'] == $item->sku->id) {
$discountInfo = $discount;
$actualPrice = $discount['discount_price']; // 使用折扣价格
break;
}
}
}
// 计算金额
$amount = bcmul($actualPrice, $item->nums, 2); // 使用实际价格计算
$originalAmount = bcmul($originalPrice, $item->nums, 2); // 原价总额
// 统一使用SKU数据进行计算
$goodsItemData['image'] = !empty($item->sku->image) ? $item->sku->image : $item->goods->image;
$goodsItemData['price'] = $actualPrice; // 使用实际价格(折扣价或原价)
$goodsItemData['original_price'] = $originalPrice; // 原价
$goodsItemData['sku_sn'] = $item->sku->sku_sn;
$goodsItemData['discount_info'] = $discountInfo; // 折扣信息
$goodsItemData['amount'] = $amount;
$goodsItemData['original_amount'] = $originalAmount;
// 订单应付金额
$orderInfo['amount'] = bcadd($orderInfo['amount'], $amount, 2);
// 商品总价
$orderInfo['goods_price'] = bcadd($orderInfo['goods_original_price'], $originalAmount, 2);;
// 商品原价总额
$orderInfo['goods_original_price'] = bcadd($orderInfo['goods_original_price'], $originalAmount, 2);
// 商品数量累计
$orderInfo['goods_num'] += $item->nums;
// 活动折扣金额累计
if ($discountInfo) {
$discountAmount = bcsub($originalAmount, $amount, 2);
$orderInfo['activity_discount_amount'] = bcadd($orderInfo['activity_discount_amount'], $discountAmount, 2);
// 收集折扣信息到订单快照
$orderSnapshotData['discount_activities'][] = [
'goods_id' => $item->goods_id,
'goods_title' => $item->goods->title,
'sku_id' => $item->sku->id,
'sku_attr' => $item->sku_attr,
'activity_id' => $discountInfo['activity_id'],
'activity_name' => $discountInfo['activity_name'],
'original_price' => $originalPrice,
'discount_price' => $actualPrice,
'discount' => $discountInfo['discount'],
'discount_amount' => $discountAmount,
'quantity' => $item->nums,
'total_discount_amount' => $discountAmount,
];
}
$freight_id = $item->goods->express_template_id;
// 计算邮费【合并运费模板】
if (!isset($shippingTemp[$freight_id])) {
$shippingTemp[$freight_id] = [
'nums' => $item->nums,
'weight' => $item->sku->weight,
'amount' => $amount
];
} else {
$shippingTemp[$freight_id] = [
'nums' => bcadd($shippingTemp[$freight_id]['nums'], $item->nums, 2),
'weight' => bcadd($shippingTemp[$freight_id]['weight'], $item->sku->weight, 2),
'amount' => bcadd($shippingTemp[$freight_id]['amount'], $amount, 2)
];
}
// 创建订单商品数据 (基于正确的表结构)
$orderItemData = [
'user_id' => $userId,
'order_sn' => '', // 将在订单创建后补充
'goods_sn' => $item->goods->goods_sn ?: '', // 商品货号
'sku_sn' => $item->sku->sku_sn ?: '', // SKU编号
'goods_type' => $item->goods->type ?: 0, // 商品类型
'goods_id' => $item->goods->id,
'goods_sku_attr' => $item->sku_attr,
'goods_spec_value_ids' => $item->sku->spec_value_ids,
'goods_sku_id' => $item->sku->id,
'goods_title' => $item->goods->title,
'goods_market_price' => $item->sku->market_price ?: 0, // 市场价
'goods_original_price' => $originalPrice, // 商品原价
'goods_price' => $amount, // 实付金额(折扣价)
'goods_image' => $goodsItemData['image'],
'goods_weight' => $item->sku->weight ?: 0,
'nums' => $item->nums,
'sale_status' => 0, // 销售状态:0=待申请
'comment_status' => 0, // 评论状态:0=未评论
'status' => 1, // 状态
'supplier_id' => $item->goods->supplier_id ?: 0,
'inspection_type_id' => $item->goods->inspection_type_id ?: 0,
];
// 如果有折扣信息,记录活动相关信息
// if ($discountInfo) {
// $orderItemData['activity_id'] = $discountInfo['activity_id'] ?? 0;
// $orderItemData['activity_name'] = $discountInfo['activity_name'] ?? '';
// $orderItemData['discount_amount'] = bcsub($originalAmount, $amount, 2);
// }
$orderItem[] = $orderItemData;
}
// 按运费模板计算
foreach ($shippingTemp as $key => $item) {
$shippingfee = Freight::calculate($key, $areaId, $item['nums'], $item['weight'], $item['amount']);
$orderInfo['express_fee'] = bcadd($orderInfo['express_fee'], $shippingfee, 2);
}
// 订单金额(商品价格+运费)
$orderInfo['order_amount'] = bcadd($orderInfo['goods_price'], $orderInfo['express_fee'], 2);
// 订单应付金额(暂时等于订单金额,后续会减去优惠)
$orderInfo['amount'] = $orderInfo['order_amount'];
// if (!empty($userCoupon)) {
// // 校验优惠券
// $goods_ids = array_column($orderItem, 'goods_id');
// $category_ids = array_column($orderItem, 'category_id');
// $brand_ids = array_column($orderItem, 'brand_id');
// $couponModel = new Coupon();
// $coupon = $couponModel->getCoupon($userCoupon['coupon_id'])
// ->checkCoupon()
// ->checkOpen()
// ->checkUseTime($userCoupon['createtime'])
// ->checkConditionGoods($goods_ids, $userId, $category_ids, $brand_ids);
// // 计算折扣金额,判断是使用不含运费,还是含运费的金额
// $amount = !isset($config['shippingfeecoupon']) || $config['shippingfeecoupon'] == 0 ? $orderInfo['goods_price'] : $orderInfo['order_amount'];
// list($new_money, $coupon_money) = $coupon->doBuy($amount);
// // 判断优惠金额是否超出总价,超出则直接设定优惠金额为总价
// $orderInfo['coupon_discount_fee'] = $coupon_money > $amount ? $amount : $coupon_money;
// $orderInfo['discount_fee'] = $orderInfo['coupon_discount_fee'];
// }
// 汇总所有折扣金额
$orderInfo['discount_fee'] = bcadd($orderInfo['activity_discount_amount'], $orderInfo['coupon_discount_fee'], 2);
$orderInfo['promo_discount_fee'] = $orderInfo['activity_discount_amount']; // 营销金额 = 活动折扣金额
$orderInfo['total_discount_amount'] = $orderInfo['discount_fee']; // 总优惠金额
// 计算最终应付金额【订单金额减去折扣】
$orderInfo['amount'] = max(0, bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2));
$orderInfo['pay_amount'] = $orderInfo['amount']; // 实际付款金额等于应付金额
// 保存订单快照信息
if (!empty($orderSnapshotData)) {
$orderSnapshotData['order_summary'] = [
'total_goods_original_price' => $orderInfo['goods_original_price'],
'total_goods_price' => $orderInfo['goods_price'],
'total_activity_discount' => $orderInfo['activity_discount_amount'],
'total_coupon_discount' => $orderInfo['coupon_discount_fee'],
'total_discount' => $orderInfo['discount_fee'],
'express_fee' => $orderInfo['express_fee'],
'final_amount' => $orderInfo['amount'],
'snapshot_time' => time(),
];
$orderInfo['order_snapshot'] = json_encode($orderSnapshotData, JSON_UNESCAPED_UNICODE);
}
return [
$orderItem,
$processedGoodsList,
$userCoupon
];
}
/**
* 统一的创建订单方法
* @param int $address_id 地址ID
* @param int $user_id 用户ID
* @param array $goods_list 标准化的商品列表
* @param int $user_coupon_id 优惠券ID
* @param string $memo 备注
* @param array $cart_ids 购物车ID数组(如果是购物车模式需要清空)
* @return Order
* @throws BusinessException
*/
public static function createOrder($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '',$supplierId = 0)
{
$address = Address::get($addressId);
if (!$address || $address['user_id'] != $userId) {
throw new BusinessException("地址未找到");
}
if (empty($goodsList)) {
throw new BusinessException("商品列表不能为空");
}
$orderTimeout = ShopConfigService::getConfigs('shop.order.order_timeout',false) ?? 3600;
$orderSn = date("Ymdhis") . sprintf("%08d", $userId) . mt_rand(1000, 9999);
// 订单主表信息 (基于新表结构)
$orderInfo = array_merge(self::initOrderPrice(), [
'type' => 1, // 1:普通订单
'order_sn' => $orderSn,
'user_id' => $userId,
'expire_time' => time() + $orderTimeout, // 过期时间
'order_status' => OrderEnum::STATUS_CREATE, // 待付款
'invoice_status' => 0, // 发票开具状态
'remark' => $remark, // 用户备注
'user_coupon_id' => $userCouponId ?: null,
'ip' => request()->ip(), // IP地址
'status' => 'normal',
]);
$orderInfo['platform'] = request()->header('platform', 'H5');
// 通过商品列表计算订单明细
list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods($orderInfo, $goodsList, $userId, $address->area_id, $userCouponId);
// 如果有活动折扣,记录活动信息
if ($orderInfo['activity_discount_amount'] > 0) {
$currentActivity = DiscountService::getCurrentActivity();
if ($currentActivity) {
$orderInfo['activity_type'] = 'discount';
$orderInfo['activity_id'] = $currentActivity['id'];
}
}
$orderInfo['pay_amount'] = bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2);
$orderInfo['pay_original_amount'] = $orderInfo['pay_amount'];
$orderInfo['pay_remain_amount'] = $orderInfo['pay_amount'];
// echo "";
// print_r($orderInfo);
// echo "";
// exit;
// 创建订单
$order = self::createOrderWithTransaction($orderInfo, $orderItem, $calculatedGoodsList, $userCoupon, $address);
return $order;
}
/**
* 创建单商品订单 - 一个商品一个订单
* @param int $addressId 地址ID
* @param int $userId 用户ID
* @param array $goodsList 商品列表(支持多商品但会拆分为多个订单)
* @param int $userCouponId 优惠券ID(仅用于第一个订单)
* @param string $remark 备注
* @return array 返回创建的订单数组
* @throws BusinessException
*/
public static function createSingleGoodsOrders($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '')
{
// 验证地址
$address = Address::get($addressId);
if (!$address || $address['user_id'] != $userId) {
throw new BusinessException("地址未找到");
}
if (empty($goodsList)) {
throw new BusinessException("商品列表不能为空");
}
// 验证商品列表格式
self::validateGoodsList($goodsList);
$orderTimeout = ShopConfigService::getConfigs('shop.order.order_timeout', false) ?? 3600;
$platform = request()->header('platform', 'H5');
$ip = request()->ip();
$orders = [];
$firstOrder = true;
// 为每个商品创建单独的订单
foreach ($goodsList as $goodsItem) {
// 构建单商品列表
$singleGoodsList = [$goodsItem];
// 生成订单号
$orderSn = date("Ymdhis") . sprintf("%08d", $userId) . mt_rand(1000, 9999);
// 订单主表信息
$orderInfo = array_merge(self::initOrderPrice(), [
'type' => 1, // 1:普通订单
'order_sn' => $orderSn,
'user_id' => $userId,
'expire_time' => time() + $orderTimeout, // 过期时间
'order_status' => OrderEnum::STATUS_CREATE, // 待付款
'invoice_status' => 0, // 发票开具状态
'remark' => $remark, // 用户备注
'user_coupon_id' => $firstOrder && $userCouponId > 0 ? $userCouponId : null, // 优惠券仅用于第一个订单
'ip' => $ip,
'status' => 'normal',
'platform' => $platform,
]);
try {
// 通过单商品列表计算订单明细
list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods(
$orderInfo,
$singleGoodsList,
$userId,
$address->area_id,
$firstOrder && $userCouponId > 0 ? $userCouponId : 0
);
// 如果有活动折扣,记录活动信息
if ($orderInfo['activity_discount_amount'] > 0) {
$currentActivity = DiscountService::getCurrentActivity();
if ($currentActivity) {
$orderInfo['activity_type'] = 'discount';
$orderInfo['activity_id'] = $currentActivity['id'];
}
}
$orderInfo['pay_amount'] = bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2);
$orderInfo['pay_original_amount'] = $orderInfo['pay_amount'];
$orderInfo['pay_remain_amount'] = $orderInfo['pay_amount'];
// 创建订单
$order = self::createOrderWithTransaction($orderInfo, $orderItem, $calculatedGoodsList, $userCoupon, $address);
$orders[] = $order;
// 第一个订单创建完成后,后续订单不再使用优惠券
$firstOrder = false;
} catch (Exception $e) {
// 如果某个订单创建失败,记录错误并继续创建其他订单
\think\Log::error("创建单商品订单失败: " . $e->getMessage() . ", 商品ID: " . $goodsItem['goods_id']);
throw new BusinessException("创建订单失败: " . $e->getMessage());
}
}
if (empty($orders)) {
throw new BusinessException("所有订单创建失败");
}
return $orders;
}
/**
* 在事务中创建订单
* @param array $orderInfo 订单信息
* @param array $orderItem 订单商品列表
* @param array $goodsList 商品列表
* @param object $userCoupon 优惠券
* @param object $address 地址信息
* @return Order
* @throws BusinessException
*/
protected static function createOrderWithTransaction($orderInfo, $orderItem, $goodsList, $userCoupon, $address)
{
$order = null;
Db::startTrans();
try {
// 创建订单
$order = Order::create($orderInfo, true);
// 为每个订单商品添加订单ID和订单号
foreach ($orderItem as &$item) {
$item['order_id'] = $order->id;
$item['order_sn'] = $order->order_sn;
// 移除临时字段
unset($item['category_id'], $item['brand_id']);
}
unset($item);
// 创建订单地址信息
$orderAddressData = [
'order_id' => $order->id,
'user_id' => $orderInfo['user_id'],
'consignee' => $address->receiver,
'mobile' => $address->mobile,
'province_name' => $address->province->name ?? '',
'city_name' => $address->city->name ?? '',
'district_name' => $address->area->name ?? '',
'address' => $address->address,
'province_id' => $address->province_id,
'city_id' => $address->city_id,
'district_id' => $address->area_id,
];
OrderAddress::create($orderAddressData);
// 减库存
foreach ($goodsList as $index => $item) {
if ($item->sku) {
$item->sku->setDec('stocks', $item->nums);
}
$item->goods->setDec("stocks", $item->nums);
}
// 计算单个商品折扣后的价格 (基于新字段名)
$saleamount = bcsub($order['amount'], $order['express_fee'], 2);
$saleratio = $order['goods_price'] > 0 ? bcdiv($saleamount, $order['goods_price'], 10) : 1;
$saleremains = $saleamount;
foreach ($orderItem as $index => &$item) {
if (!isset($orderItem[$index + 1])) {
$saleprice = $saleremains;
} else {
$saleprice = $order['discount_fee'] == 0 ? bcmul($item['goods_original_price'], $item['nums'], 2) : bcmul(bcmul($item['goods_original_price'], $item['nums'], 2), $saleratio, 2);
}
$saleremains = bcsub($saleremains, $saleprice, 2);
$item['goods_price'] = $saleprice;
}
unset($item);
// 批量创建订单商品数据
if (!empty($orderItem)) {
(new OrderGoods())->saveAll($orderItem);
}
// 修改地址使用次数
if ($address) {
$address->setInc('used_nums');
}
// 优惠券已使用
if (!empty($userCoupon)) {
$userCoupon->save(['is_used' => 2]);
}
// 提交事务
Db::commit();
} catch (Exception $e) {
Db::rollback();
throw new BusinessException($e->getMessage());
}
// 记录操作
OrderActionService::recordUserAction(
$orderInfo['order_sn'],
OrderActionEnum::ACTION_CREATE,
$orderInfo['user_id'],
'创建订单',
$orderInfo['user_id']
);
// 订单应付金额为0时直接结算
if ($order['amount'] == 0) {
// Order::settle($order->order_sn, 0);
// $order = Order::get($order->id);
return $order;
}
return $order;
}
/**
* 验证商品规格参数
* @param array $goods_list 商品列表
* @throws BusinessException
*/
public static function validateGoodsList($goods_list)
{
if (empty($goods_list) || !is_array($goods_list)) {
throw new BusinessException("商品列表不能为空");
}
foreach ($goods_list as $item) {
if (!isset($item['goods_id']) || !is_numeric($item['goods_id']) || $item['goods_id'] <= 0) {
throw new BusinessException("商品ID无效");
}
if (!isset($item['nums']) || !is_numeric($item['nums']) || $item['nums'] <= 0) {
throw new BusinessException("商品数量必须大于0");
}
if (isset($item['goods_sku_id']) && !is_numeric($item['goods_sku_id'])) {
throw new BusinessException("商品规格ID无效");
}
}
}
/**
* 创建父子订单 - 每个商品一个子订单,统一支付父订单
* @param int $addressId 地址ID
* @param int $userId 用户ID
* @param array $goodsList 商品列表
* @param int $userCouponId 优惠券ID
* @param string $remark 备注
* @return array 返回父订单和子订单信息
* @throws BusinessException
*/
public static function createParentChildOrders($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '')
{
return ParentOrderService::createParentChildOrders($addressId, $userId, $goodsList, $userCouponId, $remark);
}
/**
* 统一的订单计算方法(用于预览订单)
* @param array $goods_list 标准化的商品列表
* @param int $user_id 用户ID
* @param int $area_id 地区ID
* @param int $user_coupon_id 优惠券ID
* @return array
* @throws BusinessException
*/
public static function calculateOrder($goodsList, $userId, $areaId = 0, $userCouponId = 0)
{
if (empty($goodsList)) {
throw new BusinessException("商品列表不能为空");
}
// 验证商品列表格式
self::validateGoodsList($goodsList);
// 订单基础信息(用于计算,不包含订单号)
$orderInfo = self::initOrderPrice();
// 计算商品明细
list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods($orderInfo, $goodsList, $userId, $areaId, $userCouponId);
return [
'orderItem' => $orderItem,
'goodsList' => $calculatedGoodsList,
'orderInfo' => $orderInfo,
'userCoupon' => $userCoupon
];
}
/**
* 订单列表
*
* @param $param
* @return \think\Paginator
*/
public static function getOrderList($userId = 0, $param =[],$status = [],$supplierId = 0)
{
$pageSize = 10;
if (!empty($param['pageSize'])) {
$pageSize = $param['pageSize'];
}
$orderModel = new Order();
return $orderModel->with(['orderGoods'])
->where(function ($query) use ($param,$userId,$status) {
if (!empty($userId)) {
$query->where('user_id', $userId);
}
if (!empty($status)) {
$query->whereIn('order_status', $status );
}
if (isset($param['keywords']) && $param['keywords'] != '') {
$query->where('order_sn', 'in', function ($query) use ($param) {
return $query->name('shop_order_goods')->where('order_sn|goods_title', 'like', '%' . $param['q'] . '%')->field('order_sn');
});
}
if (!empty($supplierId)) {
$query->where('order_sn', 'in', function ($query) use ($supplierId) {
return $query->name('shop_order_goods')->where('supplier_id', $supplierId)->field('order_sn');
});
}
})
->order('createtime desc')
->paginate($pageSize, false, ['query' => request()->get()]);
}
/**
*
* @ 订单信息
* @param $orderId
* @param $userId
* @return array|false|\PDOStatement|string|Model
*/
public static function getDetail($orderId = 0, $userId = 0)
{
$orderModel = new Order();
return $orderModel->with(['orderGoods'])
->where('id', $orderId)
->where(function($query) use ($userId){
if($userId > 0){
$query->where('user_id', $userId);
}
})
->find();
}
public static function getDetailByOrderSn($orderSn)
{
$orderModel = new Order();
return $orderModel->with(['orderGoods'])
->where('order_sn', $orderSn)
->find();
}
// 查询地址信息
public static function getAddressInfo($orderId)
{
return OrderAddress::where('order_id', $orderId)->find();
}
/**
* 判断订单是否失效
* @param $order_sn
* @return bool
*/
public static function isExpired($orderSn)
{
$orderInfo = self::getByOrderSn($orderSn);
//订单过期
if (!$orderInfo['orderstate'] && !$orderInfo['paystate'] && time() > $orderInfo['expiretime']) {
// 启动事务
Db::startTrans();
try {
$orderInfo->save(['orderstate' => 2]);
//库存恢复
OrderGoods::setGoodsStocksInc($orderInfo->order_sn);
// //恢复优惠券
// UserCoupon::resetUserCoupon($orderInfo->user_coupon_id, $orderInfo->order_sn);
// 提交事务
Db::commit();
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
}
return true;
}
return false;
}
public static function getByOrderSn($orderSn)
{
return Order::where('order_sn', $orderSn)->find();
}
public static function getByOrderId($orderId)
{
return Order::where('id', $orderId)->find();
}
// 获取状态订单统计
public static function getOrderStatusCount($userId = 0)
{
$info = [];
$info['unpay'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_CREATE)->count();
$info['unsend'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_INSPECTION_PASS)->count();
$info['unrec'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_SHIP)->count();
$info['uneva'] = Order::where('user_id', $userId)->where('order_status',OrderEnum::STATUS_CONFIRM)->count();
$info['inspect'] = Order::where('user_id', $userId)
->whereIn('order_status',[
OrderEnum::STATUS_PAY,
OrderEnum::STATUS_INSPECTION,
OrderEnum::STATUS_INSPECTION_FAIL])
->count();
return $info;
}
// 更新订单状态
public static function updateOrderStatus($orderId = 0, $userId = 0, $status = 0)
{
$order = self::getDetail($orderId, $userId);
if (!$order) {
throw new BusinessException('订单不存在!');
}
// 验证状态
// if (!OrderEnum::isValidOrderStatus($status)) {
// throw new BusinessException('状态不合法!');
// }
// 要处理每个状态对应的时间字段在枚举类中
$timeField = OrderEnum::STATUS_TIME_MAP[$status];
$updateData = [
'order_status' => $status,
$timeField => time()
];
Order::where('id', $orderId)->update($updateData);
return $order;
}
/**
* 获取供应商订单列表 - 关联查询方式
* @param int $supplierId 供应商ID
* @param int $userId 用户ID(可选)
* @param array $param 查询参数
* @param array $status 订单状态筛选
* @return \think\Paginator
*/
public static function getSupplierOrderList($supplierId, $userId = 0, $param = [], $status = [])
{
$pageSize = $param['page_size'] ?? 10;
return Order::alias('o')
->join('shop_order_goods og', 'o.order_sn = og.order_sn', 'inner')
->with(['orderGoods' => function($query) use ($supplierId) {
$query->where('supplier_id', $supplierId);
}])
->where('og.supplier_id', $supplierId)
->where(function ($query) use ($param, $userId, $status) {
if (!empty($userId)) {
$query->where('o.user_id', $userId);
}
if (!empty($status)) {
$query->whereIn('o.order_status', $status);
}
if (isset($param['keywords']) && $param['keywords'] != '') {
$query->where(function($subQuery) use ($param) {
$subQuery->where('o.order_sn', 'like', '%' . $param['keywords'] . '%')
->whereOr('og.goods_title', 'like', '%' . $param['keywords'] . '%');
});
}
})
->field('o.*')
->group('o.id')
->order('o.createtime desc')
->paginate($pageSize, false, ['query' => request()->get()]);
}
/**
* 获取供应商订单详情 - 支持订单ID和订单号两种方式
* @param mixed $orderParam 订单ID或订单号
* @param int $supplierId 供应商ID
* @param int $userId 用户ID(可选,用于权限验证)
* @return array|null
*/
public static function getSupplierOrderDetail($orderParam, $supplierId, $userId = 0)
{
// 根据参数类型判断是订单ID还是订单号
if (is_numeric($orderParam)) {
// 数字类型,当作订单ID处理
$query = Order::where('id', $orderParam);
} else {
// 字符串类型,当作订单号处理
$query = Order::where('order_sn', $orderParam);
}
// 如果指定了用户ID,添加用户权限验证
if (!empty($userId)) {
$query->where('user_id', $userId);
}
$order = $query->find();
if (!$order) {
return null;
}
// 获取该供应商在此订单中的商品
$orderGoods = OrderGoods::where('order_sn', $order->order_sn)
->where('supplier_id', $supplierId)
->select();
// 如果该供应商在此订单中没有商品,返回null
if (empty($orderGoods)) {
return null;
}
$order->order_goods = $orderGoods;
return $order;
}
/**
* 获取供应商订单统计
* @param int $supplierId 供应商ID
* @param int $userId 用户ID(可选)
* @return array
*/
public static function getSupplierOrderStatusCount($supplierId, $userId = 0)
{
$baseQuery = function($status) use ($supplierId, $userId) {
$query = Order::alias('o')
->join('shop_order_goods og', 'o.order_sn = og.order_sn', 'inner')
->where('og.supplier_id', $supplierId)
->where('o.order_status', $status);
if (!empty($userId)) {
$query->where('o.user_id', $userId);
}
return $query->count('DISTINCT o.id');
};
return [
'unpay' => $baseQuery(OrderEnum::STATUS_CREATE), // 待付款
'unsend' => $baseQuery(OrderEnum::STATUS_PAY), // 待发货
'unrec' => $baseQuery(OrderEnum::STATUS_SHIP), // 待收货
'uneva' => $baseQuery(OrderEnum::STATUS_CONFIRM), // 待评价
];
}
/**
* 验证订单和订单商品是否存在
* @param int $orderId 订单ID
* @param int $orderGoodsId 订单商品ID
* @return array|false 返回订单和订单商品信息,不存在返回false
*/
public static function validateOrderAndOrderGoods($orderId, $orderGoodsId)
{
// 查询订单
$order = Order::where('id', $orderId)->find();
if (!$order) {
return false;
}
// 查询订单商品
$orderGoods = OrderGoods::where('id', $orderGoodsId)
->where('order_id', $orderId)
->find();
if (!$orderGoods) {
return false;
}
return [
'order' => $order,
'order_goods' => $orderGoods
];
}
/**
* 更新订单商品的验收状态
* @param int $orderGoodsId 订单商品ID
* @param int $inspectStatus 验收状态 0:待验收 1:验收通过 2:验收不通过
* @return bool
*/
public static function updateOrderGoodsInspectStatus($orderGoodsId, $inspectStatus,$inspectUid = 0)
{
return OrderGoods::where('id', $orderGoodsId)->update([
'inspect_status' => $inspectStatus,
'inspect_time' => time(),
'inspect_id' => $inspectUid
]);
}
/**
* 检查订单中所有商品的验收状态
* @param int $orderId 订单ID
* @return array 返回验收状态统计
*/
public static function checkOrderInspectStatus($orderId)
{
$orderGoods = OrderGoods::where('order_id', $orderId)->select();
$statusCount = [
'total' => count($orderGoods), // 总商品数
'pending' => 0, // 待验收
'passed' => 0, // 验收通过
'failed' => 0 // 验收不通过
];
foreach ($orderGoods as $goods) {
switch ($goods['inspect_status']) {
case 0:
$statusCount['pending']++;
break;
case 1:
$statusCount['passed']++;
break;
case 2:
$statusCount['failed']++;
break;
}
}
return $statusCount;
}
/**
* 根据验收状态决定是否更新订单状态
* @param int $orderId 订单ID
* @param int $userId 用户ID
* @return bool
*/
public static function updateOrderStatusByInspectResult($orderId, $userId = 0)
{
$inspectStatus = self::checkOrderInspectStatus($orderId);
// 如果所有商品都验收完成(没有待验收的商品)
if ($inspectStatus['pending'] == 0) {
// 如果所有商品都验收通过
if ($inspectStatus['failed'] == 0 && $inspectStatus['passed'] > 0) {
// 更新订单状态为验收通过
return self::updateOrderStatus($orderId, $userId, OrderEnum::STATUS_INSPECTION_PASS);
}
// 如果所有商品都验收不通过
elseif ($inspectStatus['failed'] > 0 && $inspectStatus['passed'] == 0) {
// 更新订单状态为验收失败
return self::updateOrderStatus($orderId, $userId, OrderEnum::STATUS_INSPECTION_FAIL);
}
// 如果部分商品验收通过,部分不通过
elseif ($inspectStatus['failed'] > 0 && $inspectStatus['passed'] > 0) {
// 混合状态,由业务人员手动处理,不自动更新订单状态
return true;
}
}
return true;
}
/**
* 更新订单商品的发货状态
* @param int $orderGoodsId 订单商品ID
* @param string $expressName 快递公司
* @param string $expressNo 快递单号
* @param array $expressImage 快递图片
* @return bool
*/
public static function updateOrderGoodsDeliveryStatus($orderGoodsId, $expressName, $expressNo, $expressImage = [],$orderExpressId = 0)
{
$updateData = [
'express_name' => $expressName,
'express_no' => $expressNo,
'express_image' => is_array($expressImage) ? json_encode($expressImage) : $expressImage,
'delivery_status' => 1, // 已发货
'updatetime' => time(),
'order_express_id' =>$orderExpressId
];
return OrderGoods::where('id', $orderGoodsId)->update($updateData);
}
/**
* 检查订单中所有商品的发货状态
* @param int $orderId 订单ID
* @return array 返回发货状态统计
*/
public static function checkOrderDeliveryStatus($orderId)
{
$orderGoods = OrderGoods::where('order_id', $orderId)->select();
$statusCount = [
'total' => count($orderGoods), // 总商品数
'pending' => 0, // 待发货
'delivered' => 0, // 已发货
];
foreach ($orderGoods as $goods) {
if ($goods['delivery_status'] == 1) {
$statusCount['delivered']++;
} else {
$statusCount['pending']++;
}
}
return $statusCount;
}
/**
* 根据发货状态决定是否更新订单状态
* @param int $orderId 订单ID
* @param int $userId 用户ID
* @return bool
*/
public static function updateOrderStatusByDeliveryResult($orderId, $userId = 0)
{
$deliveryStatus = self::checkOrderDeliveryStatus($orderId);
// 如果所有商品都已发货(没有待发货的商品)
if ($deliveryStatus['pending'] == 0 && $deliveryStatus['delivered'] > 0) {
// 更新订单状态为已发货
return self::updateOrderStatus($orderId, $userId, OrderEnum::STATUS_SHIP);
}
return true;
}
/**
* 验证订单商品是否可以发货
* @param int $orderId 订单ID
* @param int $orderGoodsId 订单商品ID
* @return array|false 返回订单和订单商品信息,验证失败返回false
*/
public static function validateOrderGoodsForDelivery($orderId, $orderGoodsId)
{
// 验证订单和订单商品是否存在
$orderData = self::validateOrderAndOrderGoods($orderId, $orderGoodsId);
if (!$orderData) {
return false;
}
$orderGoods = $orderData['order_goods'];
// 检查订单商品的状态是都验货通过才可以发货
// if ($orderGoods['inspect_status'] !== 1) {
// return false;
// }
if (empty($orderGoods['inspect_time'])) {
return false;
}
// 检查该订单商品是否已经发货
if ($orderGoods['delivery_status'] == 1) {
return false;
}
return $orderData;
}
}