caipan_shop_admin/app/repository/OrderRepository.php

1797 lines
65 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace app\repository;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\AccountAddress;
use app\model\AccountCoupon;
use app\model\AccountDataLog;
use app\model\Express;
use app\model\Order;
use app\model\OrderActivity;
use app\model\OrderAfterSale;
use app\model\OrderGroupList;
use app\model\OrderSku;
use app\model\PaymentLog;
use app\model\ShoppingCart;
use app\model\Sku;
use app\model\Spu;
use app\model\SpuActivity;
use app\service\Kd100;
use app\service\Math;
use app\service\Repository;
use app\service\wx\WechatPay;
use app\traits\order\ActivityTrait;
use app\traits\order\AfterSaleTrait;
use app\traits\order\ExpressLogTrait;
use app\traits\order\ExpressTrait;
use app\traits\order\ShoppingCartTrait;
use app\traits\spu\SkuTrait;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Config as CConfig;
use think\facade\Db;
use think\facade\Log;
use think\Model;
use function EasyWeChat\Kernel\Support\generate_sign;
/**
* 订单域 相关操作
*
* Class OrderRepository
* @package app\repository
* @method self getInstance(Model $model = null) static
*/
class OrderRepository extends Repository
{
use ShoppingCartTrait;
use AfterSaleTrait;
use ExpressTrait;
use ExpressLogTrait;
use ActivityTrait;
use SkuTrait;
/** 订单状态 **/
public const STATUS_ORDER_PLACED = Order::STATUS_ORDER_PLACED;//已下单 (已付款待发货)
public const STATUS_MAKEING = Order::STATUS_MAKEING;//制作中
public const STATUS_SHIPPED = Order::STATUS_SHIPPED;//已发货
public const STATUS_ARRIVED = Order::STATUS_ARRIVED;//已送达
public const STATUS_CANCEL = Order::STATUS_CANCEL;//已取消
public const IS_EVALUATE_YES = 1; // is_evaluate 已评价(订单中的已评价指全部评价完)
public const IS_EVALUATE_NO = 0; // is_evaluate 未评价(订单中的已评价指全部评价完)
/** 订单状态文本描述 **/
public static function orderStatusTextList(): array
{
return [
self::STATUS_ORDER_PLACED => '已下单',
self::STATUS_MAKEING => '制作中',
self::STATUS_SHIPPED => '已发货',
self::STATUS_ARRIVED => '已送达',
self::STATUS_CANCEL => '已取消',
];
}
/**
* 获取订单列表
*
* @param int $accountId
* @param array $fields
* @return array
* @throws RepositoryException
*/
public function listByUid(int $accountId, array $fields = []): array
{
return $this->findList(['account_id' => $accountId], $fields);
}
/**
* 获取指定sku库存
*
* @param array $codingIds
* @return array
*/
public function stock(array $codingIds): array
{
return Sku::where('coding', 'in', $codingIds)->column('stock', 'coding');
}
/**
* 订单提交数据 校验+处理
*
* @param array $data
* @return array
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function validateOrderData(array $data): array
{
/** 订单提交的数据 必需按下列格式
* $data['sku_list'] = [
* [
* 'sku_coding' => '16261684919319972261',
* 'group_id' => 1,
* 'num' => 1,
* ],
* [
* 'sku_coding' => '16261684169515092561',
* 'group_id' => 0,
* 'num' => 1,
* ],
* [
* 'sku_coding' => '16261684169515092561',
* 'group_id' => 1,
* 'num' => 1,
* ],
* ];
* $data['total'] = 950; //订单应付总金额(前端计算的结果)单位:元
* $data['address_id'] = 26; //地址ID 0=自提
* $data['express_code'] = ''; //快递公司代号
* $data['pick_self'] = 0; //是否自提 0=否 1=是
* $data['pick_self_phone'] = '13541194066'; //自提时必填 自提人联系方式
* $data['original_total'] = 1000; //商品总价 单位元
* $data['freight'] = 0; //运费 单位元
* $data['remarks'] = '';//买家备注
*/
if (!isset($data['sku_list']) || empty($data['sku_list'])) {
throw new RepositoryException('商品不能为空');
}
if (!isset($data['address']) || empty($data['address'])) {
throw new RepositoryException('收货地址不能为空');
}
if (!isset($data['phone']) || empty($data['phone'])||!preg_match("/^1[3456789]{1}\d{9}$/",$data['phone'])) {
throw new RepositoryException('收货联系电话不能为空或格式不正确');
}
if (!isset($data['contacts']) || empty($data['contacts'])) {
throw new RepositoryException('收货联系人不能为空');
}
// if (!isset($data['pick_self']) ||
// !isset($data['freight'])) {
// throw new RepositoryException('订单参数错误');
// }
// $data['coupon_id'] = $data['coupon_id'] ?? 0;
// $data['coupon_price'] = $data['coupon_price'] ?? 0;
// $data['is_score'] = $data['is_score'] ?? 0;
// if ($data['pick_self'] == Order::COMMON_OFF && !isset($data['address_id'])) {
// throw new RepositoryException('请选择收货地址');
// }
// if ($data['pick_self'] == Order::COMMON_ON && !isset($data['pick_self_phone'])) {
// throw new RepositoryException('自提时请填写联系人方式');
// }
// if (isset($data['express_code']) && !empty($data['express_code'])) {
// if (!$express = Express::where('code', $data['express_code'])->find()) {
// throw new RepositoryException('快递公司不存在');
// }
// $data['express_name'] = $express['name'] ?? '';
// }
$skuList = $this->handleSku($data['sku_list']);
//$groupList = $this->handleGroup($data['sku_list']);
$data['sku_list'] = $skuList;
//$data['group_list'] = $groupList;
return $data;
}
/**
* sku数据处理
*
* @param array $data
* @return array
* @throws RepositoryException
*/
protected function handleSku(array $data): array
{
$skuList = [];
foreach ($data as $sku) {
if (!isset($sku['sku_coding']) || empty($sku['sku_coding'])) {
throw new RepositoryException('SKU编码不能为空');
}
//多个相同sku_coding合并
if (!empty($skuList[$sku['sku_coding']])) {
$skuList[$sku['sku_coding']] += $sku['num'] ?? 1;
} else {
$skuList[$sku['sku_coding']] = $sku['num'] ?? 1;
}
}
return $skuList;
}
/**
* 拼团数据处理
*
* @param array $data
* @return array
* @throws RepositoryException
*/
protected function handleGroup(array $data): array
{
$list = [];
foreach ($data as $sku) {
if (!isset($sku['sku_coding']) || empty($sku['sku_coding'])) {
throw new RepositoryException('SKU编码不能为空');
}
$list[$sku['sku_coding']] = $sku['group_id'] ?? 0;
}
return $list;
}
/**
* 订单前置数据 校验+处理
*
* @param array $data
* @return array
* @throws RepositoryException
*/
public function validatePrepareData(array $data): array
{
/** 订单前置数据 必需按下列格式
* $data['sku_list'] = [
* [
* 'sku_coding' => '16261684919319972261',
* 'num' => 1,
* ],
* [
* 'sku_coding' => '16261684169515092561',
* 'num' => 1,
* ],
* [
* 'sku_coding' => '16261684169515092561',
* 'num' => 1,
* ],
* ];
*/
if (!isset($data['sku_list']) || empty($data['sku_list'])) {
throw new RepositoryException('商品不能为空1');
}
$skuList = $this->handleSku($data['sku_list']);
$data['sku_list'] = $skuList;
return $data;
}
/**
* 创建订单 流程1、参数完整性校验 2、参数正确性校验[收货地址、商品列表、商品价格等] 3、对比前端计算结果 4、创建订单
*
* @param int $accountId 用户ID
* @param array $data 订单数据
* @return Model
* @throws RepositoryException|GuzzleException
*/
public function createOrder(int $accountId, array $data): Model
{
// 启动事务
Db::startTrans();
try {
$account = Account::where('id', $accountId)->lock(true)->find();
if (!$account) {
throw new RepositoryException('用户不存在');
}
$data = $this->validateOrderData($data);
$dataSku = $data['sku_list'];//商品sku coding=>num 商品数量
$skuList = SpuRepository::getInstance()->listBySkuCoding(array_keys($dataSku), true);
$skuPriceList = $skuList->column('original_price', 'coding');
// if (isset($data['is_only']) && $data['is_only'] > Spu::COMMON_OFF) {
// $skuPriceList = $skuList->column('original_price', 'coding');
// } else {
// $skuPriceList = $skuList->column('price', 'coding');
// }
$skuScoreList = $skuList->column('score', 'coding');//商品规格=》积分
$skuStockList = $skuList->column('stock', 'coding');
$totalPrice = 0;//商品原总价 单位(元) 未做任何优惠
$totalScore = 0;//商品总积分
foreach ($dataSku as $coding => $num) {
if (!isset($skuPriceList[$coding])) {
throw new RepositoryException('部分商品不存在或已下架 规格编码:'.$coding);
}
$totalPrice += $skuPriceList[$coding] * $num;
$totalScore += $skuScoreList[$coding] * $num;
if ($skuStockList[$coding] < $num) {
throw new RepositoryException('有商品库存不足');
}
}
$skuArr = $skuList->each(function ($item) use ($dataSku, $accountId) {
$item->num = $dataSku[$item->coding] ?? 1;
$item->account_id = $accountId;
});
// 限购检查 包含活动商品和普通商品
$this->skuCheck($skuArr->toArray());
//商品减库存
$updateSkuStock = [];//规格库存
$updateSpuStock = [];
foreach ($skuList as $sku) {
$arr = [];
$arr['id'] = $sku['id'];
$arr['stock'] = Db::raw('`stock` - '.($dataSku[$sku['coding']] ?? 1));;
$updateSkuStock[] = $arr;
$updateSpuStock[$sku['spu_id']] = $sku['spu_id'];
}
(new Sku())->saveAll($updateSkuStock);
foreach ($updateSpuStock as $spuItem) {
$this->updateStockById($spuItem);
}
// $freight = 0;//运费(元)
// // 邮寄方式
// if ($data['pick_self'] == Order::COMMON_OFF) {
// // 快递公司默认运费
// if (isset($data['express_code']) && !empty($data['express_code'])) {
// $freight = Express::getDefaultFreight($data['express_code']);
// }
//
// if (!$address = AccountAddress::findById($data['address_id'])) {
// throw new RepositoryException('收货地址不存在');
// }
//
// $addressInfo = sprintf("%s,%s,%s %s %s %s", $address['name'], $address['phone'],
// $address['province_str'], $address['city_str'], $address['county_str'], $address['address']);
// } else {
// $addressInfo = '自提';
// }
//$realTotalPrice = $totalPrice - $data['coupon_price'];//实际应支付=商品总价-优惠券金额
$realTotalPrice = $totalPrice;//实际应支付=商品总价-优惠券金额
$realTotalPrice = $realTotalPrice <= 0 ? 0 : $realTotalPrice;
// $realTotalPrice += $freight;//加运费
$insert = [];
$time = time();
$now = date('Y-m-d H:i:s', $time);
// 付款有效期
$expired = date('Y-m-d H:i:s', $time + 15 * 60);
$payType = Order::PAY_TYPE_WECHAT;
// 积分支付
// if ($data['is_score'] > 0) {
// if ($account['score'] < $totalScore) {
// throw new RepositoryException('积分不足');
// }
// $payType = Order::PAY_TYPE_SCORE;
// }
$create = [
'coding' => generateCode(),
'account_id' => $accountId,
'original_price' => $totalPrice,
'price' => $realTotalPrice,
'pay_type' => $payType,//支付方式
'status' => self::STATUS_ORDER_PLACED,
'created_at' => $now,
'score' => $totalScore,//使用积分
'expired_at' => $expired,
'address_id' => $data['address_id'] ?? 0,
//'address' => $addressInfo,
'remarks' => $data['remarks'] ?? '',
'express_code' => $data['express_code']??'',
'express_name' => $data['express_name'] ?? '',
'pick_self' => $data['pick_self'] ??0,
'coupon_id' => $data['coupon_id'] ?? 0,
'freight' => 0,
'coupon_price' => $data['coupon_price'] ?? 0,
//'phone' => $address['phone'] ?? $data['pick_self_phone'],
'is_only' => isset($data['is_only']) && $data['is_only'] > 0 ? 1 : 0,
'is_score' => $data['is_score'] ?? 0,
'wedding_date' => !empty($data['wedding_date']) ?$data['wedding_date']: null,
'expected_delivery_date' => !empty($data['expected_delivery_date']) ?$data['expected_delivery_date']: null,
'address' => $data['address'] ?? '',
'phone' => $data['phone'] ?? '',
'contacts' => $data['contacts'] ?? '',
];
if ($realTotalPrice == 0) {
$create['status'] = self::STATUS_ORDER_PLACED;
$create['paid_at'] = $now;
}
//创建订单
if (!$order = $this->create($create)) {
throw new RepositoryException('订单创建失败,请稍后重试');
}
// // 优惠券状态变更
// if ($data['coupon_id'] > 0 && $data['coupon_price'] > 0) {
// AccountCoupon::use($accountId, $data['coupon_id'], $order['coding']);
// }
// 创建活动商品订单
// OrderRepository::getInstance()->createActivityOrder($accountId, $order->coding, $skuArr->toArray());
// if ($realTotalPrice > 0) {
// //创建支付记录
// PaymentLog::create([
// 'account_id' => $accountId,
// 'order_coding' => $order['coding'],
// 'created_at' => $now,
// 'type' => 'order',
// 'amount' => $order['price'],
// ]);
// }
// $order->needPay = $realTotalPrice > 0;//是否需要付款
$orderHasVirtual = 0;//是否拥有虚拟商品
$skuIds = [];//当前订单涉及到的skuID
foreach ($skuList as $sku) {
$skuIds[] = $sku->id;
$arr = [];
$arr['order_coding'] = $order->coding;
$arr['coding'] = $sku->coding;
$arr['spu_id'] = $sku->spu_id;
$arr['spu_activity_id'] = $sku->spu_activity_id;
$arr['sku_id'] = $sku->id;
$arr['account_id'] = $accountId;
$arr['price'] = (isset($data['is_only']) && $data['is_only'] > Spu::COMMON_OFF) ? $sku->original_price : $sku->price;
$arr['score'] = $sku->score;
$arr['num'] = $dataSku[$sku->coding];
$arr['status'] = 'normal';
$arr['created_at'] = $now;
$arr['spu_cover'] = $sku->spu_cover;
$arr['spu_name'] = $sku->spu_name;
$arr['sku_cover'] = $sku->sku_cover;
$arr['sku_name'] = $sku->sku_name;
$arr['is_virtual'] = $sku->is_virtual;
$arr['check_type'] = $sku->check_type;
$arr['spec_text'] = $sku->spec_text ? json_encode($sku->spec_text, JSON_UNESCAPED_UNICODE) : '';
$arr['not_check_num'] = $dataSku[$sku->coding];
$arr['has_comment'] = OrderSku::HAS_COMMENT_NO;
$arr['is_activity'] = $sku->is_activity;
$arr['activity_type'] = $sku->activity_type;
$arr['sku_unit'] = $sku->unit;
$arr['subtotal'] = $sku->original_price * $dataSku[$sku->coding];
$insert[] = $arr;
if ($sku->is_virtual > 0) {
$orderHasVirtual = 1;
}
}
if ($orderHasVirtual > 0) {
$order->save(['has_virtual' => Order::COMMON_ON]);
}
// 删除购物车中相关SKU
ShoppingCart::whereIn('sku_id', $skuIds)->where('account_id', $accountId)->delete();
//保存订单商品
if (!(new OrderSku())->saveAll($insert)) {
throw new RepositoryException('订单商品保存失败');
}
// if ($realTotalPrice > 0) {
// //统一下单
// $res = $this->wechatMiniPay($order->coding, $account->openid, Math::yuan2Fen($realTotalPrice));
// $order->save(['prepay_id' => $res['prepay_id']]);
//
// //生成小程序支付请求的参数
// $order->payment_params = $this->miniPayReqParams($res['prepay_id']);
// }
// 积分订单 扣减积分并记录
// if ($totalScore > 0) {
// AccountDataLog::log($accountId,
// '积分商品下单', -$totalScore,
// AccountDataLog::TYPE_SCORE,
// AccountDataLog::ACTION_ORDER,
// $account['score'] - $totalScore);
// $account->score = Db::raw('`score` - '.$totalScore);
// }
$account->mobile = $data["phone"];
$account->address = $data["address"];
$account->contacts = $data["contacts"];
$account->save();
// if ($realTotalPrice == 0) {
// // 设为已付款
// $this->afterPaid($accountId, $order['coding']);
// }
// 设为已付款
$this->afterPaid($accountId, $order['coding']);
Db::commit();
return $order;
} catch (RepositoryException $e) {
Db::rollback();
throw new RepositoryException($e->getMessage(), $e->getCode());
} catch (Exception $e) {
//回滚
Db::rollback();
self::log('订单创建失败', $e, 'error', 'order');
throw new RepositoryException('订单创建失败'.$e->getMessage());
}
}
/**
* 根据统一下单返回的prepay_id 生成小程序支付请求参数
*
* @param string $prepayId
* @return array
*/
protected function miniPayReqParams(string $prepayId): array
{
CConfig::load('extra/wechat', 'wechat');
$config = config('wechat');
$params = [
'appId' => $config['applets_appId'] ?? '',
'timeStamp' => (string) time(),
'nonceStr' => uniqid(),
'package' => 'prepay_id='.$prepayId,
'signType' => 'MD5'
];
$params['sign'] = generate_sign($params, $config['key']);
return $params;
}
/**
* 微信支付
*
* @param string $orderId
* @param string $openid
* @param int $price
* @return array|\EasyWeChat\Kernel\Support\Collection|object|ResponseInterface|string
* @throws GuzzleException
* @throws InvalidArgumentException
* @throws InvalidConfigException
* @throws RepositoryException
*/
public function wechatMiniPay(string $orderId, string $openid, int $price)
{
$res = WechatPay::getInstance()->order->unify([
'body' => '商品购买',
'out_trade_no' => $orderId,
'total_fee' => $price,
'trade_type' => 'JSAPI',
'openid' => $openid,
]);
$log = sprintf("[微信统一下单返回][订单号=%s]%s", $orderId, json_encode($res, JSON_UNESCAPED_UNICODE));
Log::info($log);
//return_code仅是通信状态 非支付状态
if (isset($res['return_code']) && $res['return_code'] == 'SUCCESS') {
if (isset($res['result_code']) && $res['result_code'] == 'SUCCESS') {
return $res;
} else {
throw new RepositoryException('支付请求失败');
}
} else {
throw new RepositoryException('请求微信接口失败');
}
}
/**
* 订单准备信息获取
* 即根据商品sku列表 收货地址 返回商品列表信息、运费、总价、可用分红额度等信息
*
* @param int $accountId 用户ID
* @param array $data 订单数据
* @return array
* @throws RepositoryException
*/
public function prepareInfo(int $accountId, array $data): array
{
$data = $this->validateprepareData($data);
$dataSku = $data['sku_list'];//商品sku coding=>num 商品数量
try {
$res = [];//待返回数据
if (!$account = Account::findById($accountId, ['id', 'score'])) {
throw new RepositoryException('读取用户信息失败');
}
$skuList = SpuRepository::getInstance()->listBySkuCoding(array_keys($dataSku));
$skuPriceList = $skuList->column('price', 'coding');
$totalPrice = 0;//商品原总价 单位(元) 未做任何优惠
foreach ($dataSku as $coding => $num) {
$totalPrice += $skuPriceList[$coding] * $num;
}
$skuList->each(function ($item) use ($dataSku, $data) {
$item->num = $dataSku[$item->coding] ?? 1;
if (!empty($item->spu_cover) && !isHttpUrl($item->spu_cover)) {
$item->spu_cover = $data['domain'].$item->spu_cover;
}
if (!empty($item->sku_cover) && !isHttpUrl($item->sku_cover)) {
$item->sku_cover = $data['domain'].$item->sku_cover;
}
});
$res['original_total'] = $totalPrice;
$res['list'] = $skuList;
$res['account'] = $account;
$res['discount'] = 0;
return $res;
} catch (RepositoryException $e) {
throw new RepositoryException($e->getMessage(), $e->getCode());
} catch (Exception $e) {
self::log('订单准备信息获取失败', $e, 'error', 'order');
throw new RepositoryException('订单数据错误');
}
}
/**
* 获取可以自动收货的订单列表
*
* @param int $day
* @return Collection
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function autoReceiptList(int $day = 20): Collection
{
//发货后day天 自动确认收货
$shipped_at = date('Y-m-d H:i:s', time() - $day * 86400);
return $this->model->where('status', self::STATUS_SHIPPED)
->field('status,id,coding')
->where('shipped_at', '<', $shipped_at)
->select();
}
/**
* 支付过期的订单列表
*
* @return Collection
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function expiredList(): Collection
{
//过期时间大于当前时间
return $this->model->where('status', self::STATUS_WAITING)
->field('status,id')
->where('expired_at', '<', date('Y-m-d H:i:s'))
->select();
}
/**
* 订单状态改为已过期
*
* @param string $orderCoding
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function setExpired(string $orderCoding): bool
{
return $this->setClosed($orderCoding, Order::STATUS_EXPIRED);
}
/**
* 检测用户所有已过期的订单
*
* @param int $accountId
* @return bool
*/
public function checkAccountExpiredOrders(int $accountId = 0): bool
{
if ($accountId <= 0) {
return false;
}
Db::startTrans();
try {
if ($accountId > 0 && !Account::findById($accountId)) {
throw new RepositoryException('用户不存在');
}
$where = [];
$where[] = ['status', '=', self::STATUS_WAITING];
if ($accountId > 0) {
$where[] = ['account_id', '=', $accountId];
}
$waitingOrders = $this->model->where($where)->select();
$toExpiredIds = [];
$toExpiredOrderIds = [];
$nowTime = time();
foreach ($waitingOrders as $order) {
if (!empty($order['expired_at'])) {
$expireTime = strtotime($order['expired_at']);
if ($nowTime >= $expireTime) {
$toExpiredIds[] = $order['id'];
$toExpiredOrderIds[] = $order['coding'];
}
}
}
// 订单状态变更
if (count($toExpiredIds) > 0) {
$this->model->whereIn('id', $toExpiredIds)
->update(['status' => OrderRepository::STATUS_EXPIRED]);
}
// 商品库存量恢复
if (count($toExpiredOrderIds) > 0) {
$this->backStock($toExpiredOrderIds);
}
Db::commit();
} catch (RepositoryException | Exception $e) {
Db::rollback();
self::log('订单已过期检测失败', $e, 'error', 'order');
return false;
}
return true;
}
/**
* 退还订单库存
*
* @param array $orderCoding 订单编号数组
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function backStock(array $orderCoding)
{
$orderSkus = OrderSku::whereIn('coding', $orderCoding)->field('num, sku_id')->select();
if (!$orderSkus->isEmpty()) {
$sku2Num = [];
foreach ($orderSkus as $item) {
if (isset($sku2Num[$item['sku_id']])) {
$sku2Num[$item['sku_id']] += $item['num'];
} else {
$sku2Num[$item['sku_id']] = $item['num'];
}
}
$skuList = Sku::where('id', 'in', array_keys($sku2Num))->field('id,stock')->lock(true)->select();
$skuList->each(function ($item) use ($sku2Num) {
if (isset($sku2Num[$item->id])) {
$item->stock += $sku2Num[$item->id];
}
});
$backSkuStocks = $skuList->toArray();
(new Sku())->saveAll($backSkuStocks);
}
}
/**
* 订单状态改为已支付
*
* @param string $orderCoding
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function setPaid(string $orderCoding): bool
{
if (!$order = $this->findOneByWhere(['coding' => $orderCoding])) {
throw new RepositoryException('订单不存在');
}
if ($order['status'] == OrderRepository::STATUS_PAID) {
$this->afterPaid($order['account_id'], $orderCoding);
return true;
}
if ($order['status'] != OrderRepository::STATUS_PAID && $order['status'] != OrderRepository::STATUS_WAITING) {
throw new RepositoryException('订单状态异常,若您已支付,请联系管理员');
}
$now = date('Y-m-d H:i:s');
if ($order['status'] === OrderRepository::STATUS_WAITING) {
Db::startTrans();
try {
$payment = PaymentLog::findOne(['account_id' => $order['account_id'], 'coding' => $orderCoding]);
if (!$payment) {
PaymentLog::create([
'account_id' => $order['account_id'],
'coding' => $orderCoding,
'created_at' => $order['created_at'],
'paid_at' => $now,
'type' => 'order',
'amount' => $order['price'],
]);
} else {
$payment->paid_at = $now;
$payment->status = PaymentLog::COMMON_ON;
$payment->save();
}
$order->status = Order::STATUS_PAID;
if ($order['pick_self'] == Order::COMMON_ON) {
//自提 改为已发货
$order->status = Order::STATUS_SHIPPED;
$order->shipped_at = $now;
}
$order->paid_at = $now;
$res = $order->save();
$this->afterPaid($order['account_id'], $orderCoding);
if ($res) {
Db::commit();
}
return $res;
} catch (RepositoryException $e) {
Db::rollback();
throw new RepositoryException($e->getMessage(), $e->getCode());
} catch (Exception $e) {
//回滚
Db::rollback();
self::log('支付成功状态变更失败', $e, 'error', 'order');
throw new RepositoryException('操作失败');
}
}
return false;
}
/**
* 支付后调用
*
* @param int $accountId
* @param string $orderCoding
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function afterPaid(int $accountId, string $orderCoding)
{
OrderSku::setPaid($orderCoding);//订单商品
//OrderActivity::setPaid($orderCoding);//活动订单
// OrderGroupList::setPaid($orderCoding);//订单拼团列表
// 付款后 所有商品销量添加
$this->updateAmount($orderCoding);
//清理除购物车
OrderRepository::getInstance()->delShoppingCartByOrder($accountId, $orderCoding);
}
/**
* 取消支付后调用
*
* @param int $accountId
* @param string $orderCoding
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function afterCancelPaid(int $accountId, string $orderCoding)
{
// OrderSku::setPaid($orderCoding);//订单商品
// OrderActivity::setPaid($orderCoding);//活动订单
// OrderGroupList::setPaid($orderCoding);//订单拼团列表
// 销量减少
$this->updateAmount($orderCoding, 'reduce');
}
/**
* 订单验收 - 确认收货
*
* @param int $orderId
* @param int $accountId
* @throws RepositoryException
*/
public function orderAccepted(int $orderId, int $accountId = 0)
{
Db::startTrans();
try {
$order = OrderRepository::getInstance()->findById($orderId, []);
if (empty($order)) {
throw new RepositoryException('没有相关的订单记录');
}
$accountId = $accountId == 0 ? $order['account_id'] : $accountId;
if (!in_array($order['status'] ,[self::STATUS_SHIPPED,self::STATUS_ARRIVED])) {
throw new RepositoryException('当前订单状态不支持收货操作');
}
$accountRepo = AccountRepository::getInstance();
$nowDateTime = date('Y-m-d H:i:s');
$account = $accountRepo->findById($accountId, []);
if ($order['account_id'] !== $accountId || empty($account)) {
throw new RepositoryException('用户不存在');
}
$this->update([
'status' => self::STATUS_ARRIVED,
'commission_give' => Order::COMMON_ON,
'accepted_at' => $nowDateTime
], ['id' => $orderId]);
/*
CConfig::load('extra/commission_withdrawal', 'commission');
$config = config('commission');
*/
// 一级佣金发放
/*
if ($account['inviter_account_id'] > 0 && isset($config['commission_first']) && $config['commission_first'] > 0) {
$firstRate = Math::div($config['commission_first'], 100);
$firstCommission = Math::mul($order['price'], $firstRate);
$accountFirst = Account::findById($account['inviter_account_id']);
if ($accountFirst && $firstCommission > 0) {
AccountDataLog::log($account['inviter_account_id'],
'订单确认收货-一级分销佣金发放',
$firstCommission,
AccountDataLog::TYPE_COMMISSION,
AccountDataLog::ACTION_ORDER,
Math::add($accountFirst['commission'], $firstCommission)
);
$accountFirst->save([
'commission' => Db::raw('`commission` + '.$firstCommission)
]);
}
}
*/
// 二级分销发放佣金
/*
if ($account['inviter_parent_id'] > 0 && isset($config['commission_second']) && $config['commission_second'] > 0) {
$secondRate = Math::div($config['commission_second'], 100);
$secondCommission = Math::mul($order['price'], $secondRate);
$accountSecond = Account::findById($account['inviter_parent_id']);
if ($accountSecond && $secondCommission > 0) {
AccountDataLog::log($account['inviter_parent_id'],
'订单确认收货-二级分销佣金发放',
$secondCommission,
AccountDataLog::TYPE_COMMISSION,
AccountDataLog::ACTION_ORDER,
Math::add($accountSecond['commission'], $secondCommission)
);
$accountSecond->save([
'commission' => Db::raw('`commission` + '.$secondCommission)
]);
}
}
*/
Db::commit();
} catch (RepositoryException $e) {
Db::rollback();
throw new RepositoryException($e->getMessage());
} catch (Exception $e) {
Db::rollback();
self::log('订单确认收货失败', $e, 'error', 'order');
throw new RepositoryException('订单数据错误');
}
}
/**
* 我的订单
*
* @param int $accountId
* @param string $tag all=全部[待付款 已付款 待发货 已发货 已完成 已取消] after_sale=售后[已发货 已完成等]
* @param int $page
* @param int $size
* @return array|null
* @throws RepositoryException
*/
public function mine(int $accountId, string $tag, int $page = 1, int $size = 20): ?array
{
$where = [];
$where[] = ['account_id', '=', $accountId];
event('OrderCheck');
switch ($tag) {
//待付款 已付款 已过期 待发货 待收货
case self::STATUS_ORDER_PLACED:
case self::STATUS_MAKEING:
case self::STATUS_SHIPPED:
case self::STATUS_ARRIVED:
case self::STATUS_CANCEL:
$status = [$tag];
break;
// 待评价 已确认收货 未评价
/*
case 'waiting_comment':
$where[] = ['is_evaluate', '=', self::IS_EVALUATE_YES];
$status = [self::STATUS_ARRIVED];
break;
*/
// 待核验
/*
case 'check':
$where[] = ['has_virtual', '=', Order::COMMON_ON];
$where[] = ['virtual_check', '=', Order::COMMON_OFF];
$where[] = ['frontend_check', '=', Order::COMMON_OFF];
$status = [self::STATUS_PAID, self::STATUS_ARRIVED, self::STATUS_SHIPPED];
break;
*/
// 售后记录
/*
case 'after_sale':
$status = [self::STATUS_PAID, self::STATUS_SHIPPED, self::STATUS_ARRIVED];
$where[] = ['is_after_sale', '=', self::BOOL_TRUE];
break;
*/
default:
$status = [
self::STATUS_ORDER_PLACED ,
self::STATUS_MAKEING ,
self::STATUS_SHIPPED ,
self::STATUS_ARRIVED ,
self::STATUS_CANCEL ,
];
}
if (!empty($status)) {
$where[] = ['status', 'in', $status];
}
$fields = [];
return $this->findList($where, $fields, $page, $size, function ($q) use ($tag) {
return $q->with(['skus'])->order('created_at', 'desc');
});
}
/**
* 立即免拼 单人开团
* 免拼逻辑 1. 直接将订单状态修改 2. 订单相关拼团列表的剩余数量 全部设为0
* is_only=0 is_group_make=1 open_one=1 open_one_success=0group_make_end_at < 当前时间 订单列表才显示【立即免拼】,其他都不显示
*
* @param int $accountId
* @param string $coding
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function openOne(int $accountId, string $coding)
{
if (!$order = Order::findOne(['coding' => $coding])) {
throw new RepositoryException('订单不存在');
}
if ($order['is_group_make'] != Order::COMMON_ON) {
throw new RepositoryException('不是拼团订单');
}
if (in_array($order['status'], [Order::STATUS_WAITING, Order::STATUS_CLOSED, Order::STATUS_EXPIRED])) {
throw new RepositoryException('订单状态错误');
}
if ($order['account_id'] != $accountId) {
throw new RepositoryException('不是您的订单');
}
$order->save(['open_one_success' => Order::COMMON_ON]);
OrderGroupList::where('coding', $coding)->save(['surplus' => 0]);
}
/**
* 订单数量
*
* @param int $accountId
* @return array
*/
public function orderCount(int $accountId): array
{
$statusList = [self::STATUS_ORDER_PLACED, self::STATUS_MAKEING, self::STATUS_SHIPPED, self::STATUS_ARRIVED];
$data = Order::where('account_id', $accountId)
->whereIn('status', $statusList)
->group('status')
->column('count(`id`) as count', 'status');
$list = [];
foreach ($statusList as $status) {
$list[$status] = $data[$status] ?? 0;
}
return $list;
}
/**
* 订单详情
*
* @param int $orderId
* @return mixed|null
* @throws RepositoryException
*/
public function detail(int $orderId)
{
$fields = [];
return $this->findById($orderId, $fields, function ($q) {
return $q->with(['skus'])->order('created_at', 'desc');
});
}
/**
* 获取订单物流信息
*
* @param string $orderCoding
* @return array|Model
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function logistics(string $orderCoding)
{
$fields = ['id', 'status', 'coding', 'is_after_sale', 'prepay_id', 'original_price', 'price', 'express_number', 'express_code', 'express_name'];
if (!$order = $this->model->with(['skus'])->field($fields)->where('coding', $orderCoding)->find()) {
throw new RepositoryException('订单不存在');
}
$expressInfo = [];
if (!empty($order['express_code']) && !empty($order['express_number'])) {
try {
if ($order['status'] == self::STATUS_ARRIVED) {
$exprLog = $this->findExpressLogByNu($order['coding'], $order['express_code'], $order['express_number']);
if (!empty($exprLog)) {
$expressInfo['status'] = 200;
$expressInfo['state'] = $exprLog['state'];
$expressInfo['state_desc'] = Kd100::state()[$exprLog['state']] ?? '';
$expressInfo['com'] = $exprLog['com'];
$expressInfo['nu'] = $exprLog['nu'];
$expressInfo['data'] = $exprLog['content'];
} else {
$expressInfo = $this->handleOrderExpressLog($order['coding'], $order['express_code'], $order['express_number']);
}
} else {
$expressInfo = $this->handleOrderExpressLog($order['coding'], $order['express_code'], $order['express_number']);
}
} catch (Exception $e) {
$expressInfo = [];
}
}
$order->express_info = $expressInfo;
return $order;
}
/**
* 订单发货
*
* @param int $orderId 订单ID
* @param int $expressId 快递公司ID
* @param string $expressNumber 快递单号
* @param string $businessRemarks 卖家备注
* @throws RepositoryException
*/
public function orderShipping(int $orderId, int $expressId, string $expressNumber = '', string $businessRemarks = '')
{
Db::startTrans();
try {
$order = $this->findById($orderId, []);
if (empty($order)) {
throw new RepositoryException('没有相关的订单记录');
}
if ($order['status'] !== self::STATUS_PAID) {
throw new RepositoryException('当前订单状态不支持发货操作');
}
$express = $this->expressInfo($expressId);
if (empty($express)) {
throw new RepositoryException('相关的快递公司不存在');
}
if (empty($expressNumber) || strlen($expressNumber) > 100) {
throw new RepositoryException('请填写正确的快递单号');
}
$this->update([
'shipped_at' => date('Y-m-d H:i:s'),
'business_remarks' => $businessRemarks,
'express_code' => $express['code'],
'express_name' => $express['name'],
'express_number' => $expressNumber,
'status' => self::STATUS_SHIPPED,
], ['id' => $orderId]);
Db::commit();
} catch (RepositoryException $e) {
Db::rollback();
throw new RepositoryException($e->getMessage());
} catch (Exception $e) {
Db::rollback();
self::log('订单发货失败', $e, 'error', 'order');
throw new RepositoryException('订单发货失败');
}
}
/**
* 更新商品库存 TODO
*
* @param array $spuIds
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function updateSpuStock(array $spuIds)
{
$skuList = Sku::whereIn('id', $spuIds)->field('id,spu_id,stock')->select()->toArray();
$updateSpu = [];
foreach ($skuList as $sku) {
$arr = [];
// $arr['spu']
}
}
/**
* 商品规格检查 1.限购检查 2.活动时间检查 3.团购、拼团数量检查
* 包含活动与普通商品
* 商品数量不多的情况下,循环内部执行检测 TODO 待优化 部分查询可拿到循环外部
* @param array $data
* @return bool
* @throws RepositoryException|Exception
*/
public function skuCheck(array $data): bool
{
$now = date('Y-m-d H:i:s');
foreach ($data as $item) {
//限购检测
if ($item['limit_num'] > 0) {
//存在限购数量
$where = [];
$where[] = ['is_paid', '=', OrderSku::COMMON_ON];
if ($item['spu_id'] > 0) {
$where[] = ['spu_id', '=', $item['spu_id']];
}
if ($item['spu_activity_id'] > 0) {
$where[] = ['spu_activity_id', '=', $item['spu_activity_id']];
}
if ($item['limit_time'] > 0) {
$endAt = date('Y-m-d 23:59:59');//结束时间
$day = ($item['limit_time'] - 1) ?: 0;
$str = '-'.$day.' days';
$beginAt = date('Y-m-d 00:00:00', strtotime($str));
$where[] = ['paid_at', '>', $beginAt];
$where[] = ['paid_at', '<', $endAt];
$where[] = ['account_id', '=', $item['account_id']];
//存在限购时间 数字单位为天
//结合限购数量 生效规则N天内 限购数量-已购买数量 >= 本次购买数量 否则无法购买
}
$history = OrderSku::history($where, ['id', 'spu_name']);
if ($item['limit_num'] - $history->count() < $item['num']) {
throw new RepositoryException(sprintf("商品[%s]%s仅限购买%d件", $item['goods_name'],
$item['limit_time'] > 0 ? $item['limit_time'].'天内' : '', $item['limit_num']).$item['coding']);
}
}
// 活动时间检测
if (!empty($item['begin_at'])) {
if ($now < $item['begin_at']) {
throw new RepositoryException('商品['.$item['goods_name'].']商品尚未开始');
}
}
// 活动时间检测
if (!empty($item['end_at'])) {
if ($now > $item['end_at']) {
throw new RepositoryException('商品['.$item['goods_name'].']商品已结束');
}
}
}
return true;
}
/**
* 更新订单销量
*
* @param string $orderCoding
* @param string $type add=添加 reduce=减少
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function updateAmount(string $orderCoding, string $type = 'add'): bool
{
$op = '+';
if ($type != 'add') {
$op = '-';
}
$list = OrderSku::where('coding', $orderCoding)->select();
if ($list->isEmpty()) {
return true;
}
$spuList = $list->where('spu_id', '>', 0)->toArray();
$activityList = $list->where('spu_activity_id', '>', 0)->toArray();
$spuUpdate = [];
foreach ($spuList as $spu) {
if (!isset($spuUpdate[$spu['spu_id']])) {
$spuUpdate[$spu['spu_id']] = [];
$spuUpdate[$spu['spu_id']]['id'] = $spu['spu_id'];
$spuUpdate[$spu['spu_id']]['amount'] = $spu['num'];
} else {
$spuUpdate[$spu['spu_id']]['amount'] += $spu['num'];
}
}
foreach ($spuUpdate as &$spu) {
$spu['amount'] = Db::raw('`amount` '.$op.' '.$spu['amount']);
}
$activityUpdate = [];
foreach ($activityList as $activity) {
if (!isset($spuUpdate[$activity['spu_activity_id']])) {
$activityUpdate[$activity['spu_activity_id']] = [];
$activityUpdate[$activity['spu_activity_id']]['id'] = $activity['spu_activity_id'];
$activityUpdate[$activity['spu_activity_id']]['amount'] = $activity['num'];
} else {
$activityUpdate[$activity['spu_activity_id']]['amount'] += $activity['num'];
}
}
foreach ($activityUpdate as &$activity) {
$activity['amount'] = Db::raw('`amount` '.$op.' '.$activity['amount']);
}
if (!empty($spuUpdate)) {
(new Spu())->saveAll($spuUpdate);
}
if (!empty($activityUpdate)) {
(new SpuActivity())->saveAll($activityUpdate);
}
return true;
}
/**
* 取消订单或设置过期
*
* @param string $orderCoding
* @param string $status 操作类型 默认为取消
* @param string $reason 取消原因
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function setClosed(string $orderCoding, string $status = Order::STATUS_CANCEL, string $reason = ''): bool
{
if (!$order = Order::findOne(['coding' => $orderCoding])) {
throw new RepositoryException('订单不存在');
}
if (!in_array($order['status'], [Order::STATUS_ORDER_PLACED])) {
throw new RepositoryException('订单状态不允许此操作');
}
// 启动事务
Db::startTrans();
try {
//库存还原
$this->backStock([$orderCoding]);
// 取消拼团 拼团数量未恢复,因拼团是绝对成功。即便此拼团名额满 也可以自行发起
// OrderGroupList::cancel($orderCoding);
// 活动订单取消
OrderActivity::cancel($orderCoding);
//$account = Account::findById($order['account_id']);
$order->close_reason = $reason;
//待付款订单
// if ($order['status'] == Order::STATUS_ORDER_PLACED) {
$order->status = $status;
$order->save();
//积分日志
/*
if ($order['score'] > 0) {
AccountDataLog::log($order['account_id'], '取消订单退回', $order['score'], AccountDataLog::TYPE_SCORE,
AccountDataLog::ACTION_ORDER, $account['score'] + $order['score']);
}
*/
//待付款时 积分才退回
/*
$account->save([
'score' => Db::raw('`score` + '.$order['score'])
]);
*/
// 优惠券退回
/*
AccountCoupon::where('order_coding', $orderCoding)->where('account_id', $order['account_id'])
->where('coupon_id', $order['coupon_id'])
->where('status', AccountCoupon::STATUS_USED)
->update(['status' => AccountCoupon::STATUS_NORMAL]);
*/
Db::commit();
return true;
//}
/*
if ($order['status'] == Order::STATUS_PAID && $status == Order::STATUS_CANCEL) {
//已付款订单 取消
//若已有虚拟商品被核销 则订单不允许退回 积分不退回
// $hasCheckSku = OrderSku::where('coding', $orderCoding)
// ->where('is_virtual', OrderSku::COMMON_ON)
// ->whereRaw('`num` > `not_check_num`')
// ->count();
// if ($hasCheckSku > 0) {
// throw new RepositoryException('订单部分商品已核销,无法进行此操作');
// }
$refundCoding = generateCode();
$now = date('Y-m-d H:i:s');
//售后记录
OrderAfterSale::create([
'coding' => $orderCoding,
'account_id' => $order['account_id'],
'description' => '取消订单并退款',
'created_at' => $now,
'status' => OrderAfterSale::STATUS_DONE,
]);
//积分日志
if ($order['score'] > 0) {
AccountDataLog::log($order['account_id'], '取消订单退回', $order['score'], AccountDataLog::TYPE_SCORE,
AccountDataLog::ACTION_ORDER, $account['score'] + $order['score']);
}
//待付款时 积分才退回
$account->save([
'score' => Db::raw('`score` + '.$order['score'])
]);
// 订单金额大于0 退回
if ($order['price'] > 0) {
$order->is_after_sale = Order::COMMON_ON;
$order->after_sale_at = $now;
$order->after_sale_end_at = $now;
$result = WechatPay::getInstance()->refund->byOutTradeNumber($orderCoding, $refundCoding, $order['price'] * 100, $order['price'] * 100, [
'refund_desc' => '【'.$orderCoding.'】订单退款',
]);
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'FAIL') {
//退款不成功
self::log('订单退款不成功['.$orderCoding.']'.$result['err_code_des'].'all_msg:'.json_encode($result), null);
$order->need_refund = Order::COMMON_ON;//需要退款 但未成功
}
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
self::log('订单退款成功['.$orderCoding.']'.'all_msg:'.json_encode($result), null);
}
}
$order->refund_coding = $refundCoding;
$order->status = self::STATUS_CLOSED;
$order->save();
Db::commit();
return true;
}
*/
} catch (RepositoryException $e) {
Db::rollback();
throw $e;
} catch (Exception $e) {
Db::rollback();
throw new RepositoryException($e->getMessage());
}
}
/**
* 购物车数量
*
* @param string $type 类型 spu=商品购物车 score=积分商品购物车
* @param int $accountId 用户ID
* @throws Exception
*/
public function shoppingCartCount(int $accountId, string $type = 'spu'): int
{
$isScore = (int) ($type == 'score');
event('CheckShoppingCart', $accountId);
return ShoppingCart::where('is_score', $isScore)->where('account_id', $accountId)->count();
}
/**
* 订单付款
*
* @param string $orderCoding 订单编号
* @param int $accountId 用户ID
* @throws RepositoryException|Exception
*/
public function pay(string $orderCoding, int $accountId): array
{
$order = $this->findOneByWhere(['coding' => $orderCoding]);
if (empty($order)) {
throw new RepositoryException('没有相关的订单记录');
}
if ($order['status'] !== self::STATUS_WAITING) {
throw new RepositoryException('订单状态已非待付款');
}
if ($order['account_id'] != $accountId) {
throw new RepositoryException('非本人订单');
}
//统一下单
// $res = $this->wechatMiniPay($order->coding, $account->openid, $order->price);
$prepayId = $order['prepay_id'];
//生成小程序支付请求的参数
$paymentParams = $this->miniPayReqParams($prepayId);
return [
'prepay_id' => $prepayId,
'payment_params' => $paymentParams,
];
}
/**
* 商品sku核验 线下核验
*
* @param string $orderCoding 订单编号
* @param int $id 订单商品ID =0则核验该订单下所有虚拟商品 >0核验具体记录
* @param int $num 核验数量 仅当$id>0时生效
* @param int $checkBy 核验人account_id =0则后台无account用户
* @param string $checkType
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
* @throws Exception
*/
public function check(string $orderCoding, int $id = 0, int $num = 1, int $checkBy = 0, string $checkType = Order::CHECK_TYPE_FRONTEND): bool
{
if (!$checkUser = Account::findById($checkBy)) {
throw new RepositoryException('核验人信息不存在');
}
if ($checkUser['is_staff'] != Account::COMMON_ON) {
throw new RepositoryException('此操作仅限员工');
}
$checkUserInfo = sprintf("ID:%d 昵称:%s", $checkUser['id'], $checkUser['nickname']);
return $this->checkBase($orderCoding, $id, $num, $checkUserInfo, $checkType);
}
/**
* 商品sku核验
*
* @param string $orderCoding 订单编号
* @param int $id 订单商品ID =0则核验该订单下所有虚拟商品 >0核验具体记录
* @param int $num 核验数量 仅当$id>0时生效
* @param string $checkBy 核验人信息
* @param string $checkType
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
* @throws Exception
*/
public function checkBase(string $orderCoding, int $id = 0, int $num = 1, string $checkBy = '', string $checkType = Order::CHECK_TYPE_FRONTEND): bool
{
if (!$order = Order::findOne(['coding' => $orderCoding])) {
throw new RepositoryException('订单不存在');
}
if (in_array($order['status'], [Order::STATUS_CLOSED, Order::STATUS_WAITING])) {
// 订单关闭或未付款
throw new RepositoryException('当前订单状态无法核验');
}
$where = [];
$where[] = ['coding', '=', $orderCoding];
$where[] = ['check_type', '=', $checkType];
$where[] = ['is_virtual', '=', Order::COMMON_ON];//虚拟商品 才能核验
$where[] = ['is_check', '=', Order::COMMON_OFF];// 非核验完成商品 才能核验
$where[] = ['not_check_num', '>', Order::COMMON_OFF];// 未核验数量大于0 才能核验
if ($id > 0) {
$where[] = ['id', '=', $id];
}
$skuList = OrderSku::where($where)->select();
if ($skuList->isEmpty()) {
throw new RepositoryException('当前订单无可核验商品');
}
$update = [];
foreach ($skuList as $sku) {
$arr = [];
$arr['id'] = $sku['id'];
$arr['check_by'] = $checkBy;
if ($id > 0) {
// 指定订单商品核验
if ($id == $sku['id']) {
if ($sku['not_check_num'] < $num) {
throw new RepositoryException('核验商品数量不足');
}
if ($sku['not_check_num'] == $num) {
$arr['is_check'] = OrderSku::COMMON_ON;
}
$arr['not_check_num'] = Db::raw('`not_check_num` - '.$num);
} else {
unset($arr);
}
} else {
//订单下所有商品全部核验完
$arr['is_check'] = OrderSku::COMMON_ON;
$arr['not_check_num'] = OrderSku::COMMON_OFF;
}
if (isset($arr)) {
$update[] = $arr;
}
}
// 更新订单商品状态
(new OrderSku())->saveAll($update);
// 检测订单是否已完成所有核验
$notCheckNumList = $this->checkAllVirtual($orderCoding);
$orderUpdate = [];
if ($notCheckNumList['total'] == 0) {
$orderUpdate['virtual_check'] = Order::COMMON_ON;
}
if ($notCheckNumList['frontend'] == 0) {
$orderUpdate['frontend_check'] = Order::COMMON_ON;
}
if ($notCheckNumList['backend'] == 0) {
$orderUpdate['backend_check'] = Order::COMMON_ON;
}
if (!empty($orderUpdate)) {
$order->save($orderUpdate);
}
return true;
}
/**
* 商品sku核验检测
* 检测逻辑打开商品二维码时待核验的数量发生变更或为0时 标识本次核验成功
*
* @param string $orderCoding 订单编号
* @param int $id 订单商品ID =0则核验该订单下所有虚拟商品 >0核验具体记录
* @param int $notCheckNum 未核验的数量
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function checkResult(string $orderCoding, int $id, int $notCheckNum): bool
{
if (!$order = Order::findOne(['coding' => $orderCoding])) {
throw new RepositoryException('订单不存在');
}
if (in_array($order['status'], [Order::STATUS_CLOSED, Order::STATUS_WAITING])) {
// 订单关闭或未付款
throw new RepositoryException('当前订单状态无法核验');
}
if (!$item = OrderSku::findById($id)) {
throw new RepositoryException('该商品不存在');
}
if ($item['coding'] != $orderCoding) {
throw new RepositoryException('该商品不属于此订单');
}
if ($item['not_check_num'] == 0 || $item['not_check_num'] != $notCheckNum) {
return true;
}
return false;
}
/**
* 获取订单虚拟商品未核验数量
*
* @param string $orderCoding
* @return array ['total'=所有类型未核验的数量]
*/
public function checkAllVirtual(string $orderCoding): array
{
$where = [];
$where[] = ['coding', '=', $orderCoding];
$where[] = ['is_virtual', '=', Order::COMMON_ON];// 虚拟商品
$where[] = ['is_check', '=', Order::COMMON_OFF];// 未核验完成
$where[] = ['not_check_num', '>', Order::COMMON_OFF];// 未核验数量大于0
$res = [];
$res['total'] = 0;
$countList = OrderSku::where($where)->group('check_type')->column('count(`id`) as count', 'check_type');
foreach (Order::checkTypeList() as $type) {
$res[$type] = $countList[$type] ?? 0;
$res['total'] += $res[$type];
}
return $res;
}
public function userOrderList(int $accountId, array $where = [], int $page = 1, int $size = 0, array $statusArray = [])
{
return Order::when(!empty($statusArray), function ($q) use ($statusArray) {
$q->whereIn('status', $statusArray);
})->where($where)
->when($size > 0, function ($q) use ($page, $size) {
$q->page($page, $size);
})
->order('id', 'desc')
->where('account_id', $accountId)
->select();
}
/**
* 获取订单的拼团ID
*
* @param string $orderCoding
* @return mixed
*/
public function groupId(string $orderCoding)
{
return OrderGroupList::where('coding', $orderCoding)
->where('is_paid', OrderGroupList::COMMON_ON)
->value('group_id', 0);
}
public function getOrderSku($id)
{
return OrderSku::findById($id);
}
public function getOrderOriginalPrice($orderCodeing,$skuCoding)
{
return OrderSku::where("order_coding",$orderCodeing)->where("coding","<>",$skuCoding)->sum("subtotal");
}
}