init
- 框架初始化 - 安装插件 - 修复PHP8.4报错
This commit is contained in:
1362
addons/shopro/service/order/OrderCreate.php
Normal file
1362
addons/shopro/service/order/OrderCreate.php
Normal file
File diff suppressed because it is too large
Load Diff
518
addons/shopro/service/order/OrderDispatch.php
Normal file
518
addons/shopro/service/order/OrderDispatch.php
Normal file
@@ -0,0 +1,518 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order;
|
||||
|
||||
use app\admin\model\shopro\order\Order;
|
||||
use app\admin\model\shopro\order\OrderItem;
|
||||
use app\admin\model\shopro\order\Action as OrderAction;
|
||||
use app\admin\model\shopro\order\Express as OrderExpress;
|
||||
use app\admin\model\shopro\activity\Groupon as ActivityGroupon;
|
||||
use app\admin\model\shopro\activity\GrouponLog as ActivityGrouponLog;
|
||||
use app\admin\model\shopro\dispatch\Dispatch as DispatchModel;
|
||||
use app\admin\model\shopro\dispatch\DispatchAutosend;
|
||||
use addons\shopro\library\express\Express as ExpressLib;
|
||||
|
||||
|
||||
|
||||
class OrderDispatch
|
||||
{
|
||||
|
||||
public $order = null;
|
||||
|
||||
public function __construct($params = [])
|
||||
{
|
||||
if (isset($params['order_id']) && !empty($params['order_id'])) {
|
||||
|
||||
$this->order = Order::find($params['order_id']);
|
||||
if (!$this->order) {
|
||||
error_stop('订单不存在');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行发货
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function confirm($params)
|
||||
{
|
||||
$admin = auth_admin();
|
||||
$method = $params['method'] ?? '';
|
||||
if (!in_array($method, ['input', 'api', 'upload'])) {
|
||||
error_stop('请使用正确的发货方式');
|
||||
}
|
||||
if ($this->order->status !== Order::STATUS_PAID && !$this->order->isOffline($this->order)) {
|
||||
error_stop("该订单{$this->order->status_text},不能发货");
|
||||
}
|
||||
|
||||
if ($this->order->apply_refund_status === Order::APPLY_REFUND_STATUS_APPLY) {
|
||||
error_stop("该订单已申请退款,暂不能发货");
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case 'api':
|
||||
list($orderItems, $express) = $this->doByApi($params);
|
||||
break;
|
||||
case 'input':
|
||||
list($orderItems, $express) = $this->doByInput($params);
|
||||
break;
|
||||
case 'upload':
|
||||
list($orderItems, $express) = $this->doByUpload($params);
|
||||
break;
|
||||
}
|
||||
|
||||
// 添加包裹信息
|
||||
$orderExpress = OrderExpress::create([
|
||||
'user_id' => $this->order->user_id,
|
||||
'order_id' => $this->order->id,
|
||||
'express_name' => $express['name'],
|
||||
'express_code' => $express['code'],
|
||||
'express_no' => $express['no'],
|
||||
'method' => $method,
|
||||
'driver' => $express['driver'] ?? null,
|
||||
'ext' => $express['ext'] ?? null
|
||||
]);
|
||||
|
||||
// 修改订单商品发货状态
|
||||
foreach ($orderItems as $orderItem) {
|
||||
$orderItem->order_express_id = $orderExpress->id;
|
||||
$orderItem->dispatch_status = OrderItem::DISPATCH_STATUS_SENDED;
|
||||
$orderItem->ext = array_merge($orderItem->ext, ['send_time' => time()]); // item 发货时间
|
||||
$orderItem->save();
|
||||
OrderAction::add($this->order, $orderItem, $admin, 'admin', "商品{$orderItem->goods_title}已发货");
|
||||
}
|
||||
$this->subscribeExpressInfo($orderExpress);
|
||||
// 订单发货后
|
||||
$data = [
|
||||
'order' => $this->order,
|
||||
'items' => $orderItems,
|
||||
'express' => $orderExpress,
|
||||
'dispatch_type' => 'express',
|
||||
];
|
||||
\think\Hook::listen('order_dispatch_after', $data);
|
||||
return $express;
|
||||
}
|
||||
|
||||
// 手动发货
|
||||
private function doByInput($params)
|
||||
{
|
||||
$orderItems = $this->getDispatchOrderItems($params, 'express');
|
||||
|
||||
$express = $params['express'] ?? null;
|
||||
if (empty($express['name']) || empty($express['code']) || empty($express['no']) || strpos($express['no'], '=') !== false) {
|
||||
error_stop('请输入正确的快递信息');
|
||||
}
|
||||
return [$orderItems, $express];
|
||||
}
|
||||
|
||||
// API发货
|
||||
private function doByApi($params)
|
||||
{
|
||||
$orderItems = $this->getDispatchOrderItems($params, 'express');
|
||||
$sender = $params['sender'] ?? null;
|
||||
$expressLib = new ExpressLib();
|
||||
|
||||
$data = [
|
||||
'order' => $this->order,
|
||||
'sender' => $sender,
|
||||
'consignee' => $this->order->address
|
||||
];
|
||||
$express = $expressLib->eOrder($data, $orderItems);
|
||||
return [$orderItems, $express];
|
||||
}
|
||||
|
||||
// 上传发货模板发货 TODO: 如果发货单比较多,循环更新可能会比较慢,考虑解析完模版信息以后,把数据返回前端,再次执行批量发货流程
|
||||
private function doByUpload($params)
|
||||
{
|
||||
$orderItems = $this->getDispatchOrderItems($params, 'express');
|
||||
|
||||
$express = $params['express'] ?? null;
|
||||
if (empty($express['name']) || empty($express['code']) || empty($express['no'])) {
|
||||
error_stop('请输入正确的快递信息');
|
||||
}
|
||||
return [$orderItems, $express];
|
||||
}
|
||||
|
||||
// 获取可发货的订单商品
|
||||
public function getDispatchOrderItems($params = null, $dispatch_type = 'express')
|
||||
{
|
||||
$orderItemIds = $params['order_item_ids'] ?? [];
|
||||
$whereCanDispatch['order_id'] = $this->order->id;
|
||||
$whereCanDispatch['dispatch_status'] = OrderItem::DISPATCH_STATUS_NOSEND;
|
||||
$whereCanDispatch['aftersale_status'] = ['<>', OrderItem::AFTERSALE_STATUS_ING];
|
||||
$whereCanDispatch['refund_status'] = OrderItem::REFUND_STATUS_NOREFUND;
|
||||
$whereCanDispatch['dispatch_type'] = $dispatch_type;
|
||||
|
||||
if (empty($orderItemIds)) {
|
||||
$orderItems = OrderItem::where($whereCanDispatch)->select();
|
||||
} else {
|
||||
$orderItems = OrderItem::where('id', 'in', $orderItemIds)->where($whereCanDispatch)->select();
|
||||
|
||||
if (count($orderItems) !== count($orderItemIds)) {
|
||||
error_stop('选中商品暂不能发货');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$orderItems) {
|
||||
error_stop('该订单无可发货商品');
|
||||
}
|
||||
return $orderItems;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 取消发货
|
||||
*
|
||||
*/
|
||||
public function cancel($params)
|
||||
{
|
||||
$admin = auth_user();
|
||||
|
||||
$order_express_id = $params['order_express_id'] ?? 0;
|
||||
$orderExpress = OrderExpress::where('id', $order_express_id)->find();
|
||||
if (!$orderExpress) {
|
||||
error_stop('未找到发货单');
|
||||
}
|
||||
// 1.检测是不是用api发的 有些快递不支持取消接口 所以不判断了,统一手动取消
|
||||
// if ($orderExpress->method === 'api') {
|
||||
// // TODO: 走取消运单接口
|
||||
// $expressLib = new ExpressLib();
|
||||
|
||||
// $data = [
|
||||
// 'express_no' => $orderExpress['express_no'],
|
||||
// 'express_code' => $orderExpress['express_code'],
|
||||
// 'order_code' => $orderExpress['ext']['Order']['OrderCode']
|
||||
// ];
|
||||
|
||||
// $express = $expressLib->cancel($data);
|
||||
// }
|
||||
// 2. 变更发货状态
|
||||
$orderItems = OrderItem::where([
|
||||
'order_id' => $this->order->id,
|
||||
'order_express_id' => $orderExpress->id
|
||||
])->where('dispatch_type', 'express')->select();
|
||||
|
||||
|
||||
foreach ($orderItems as $orderItem) {
|
||||
$orderItem->order_express_id = 0;
|
||||
$orderItem->dispatch_status = OrderItem::DISPATCH_STATUS_NOSEND;
|
||||
$orderItem->save();
|
||||
OrderAction::add($this->order, null, $admin, 'admin', "已取消发货");
|
||||
}
|
||||
// 删除发货单
|
||||
$orderExpress->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发货信息
|
||||
*
|
||||
*/
|
||||
public function change($params)
|
||||
{
|
||||
$admin = auth_user();
|
||||
|
||||
$order_express_id = $params['order_express_id'] ?? 0;
|
||||
|
||||
$orderExpress = OrderExpress::where('id', $order_express_id)->find();
|
||||
if (!$orderExpress) {
|
||||
error_stop('未找到发货单');
|
||||
}
|
||||
// 1.1 检测是不是用api发的 如果是则提醒取消运单再重新走发货流程 此时什么都不用做
|
||||
if ($orderExpress->method === 'api') {
|
||||
error_stop('该发货单已被推送第三方平台,请取消后重新发货');
|
||||
}
|
||||
// 1.2 如果不是则手动变更运单信息(快递公司、运单号)
|
||||
$express = $params['express'] ?? null;
|
||||
if (empty($express['name']) || empty($express['code']) || empty($express['no']) || strpos($express['no'], '=') !== false) {
|
||||
error_stop('请输入正确的快递信息');
|
||||
}
|
||||
|
||||
$orderExpress->save([
|
||||
'express_name' => $express['name'],
|
||||
'express_code' => $express['code'],
|
||||
'express_no' => $express['no'],
|
||||
'method' => 'input'
|
||||
]);
|
||||
|
||||
OrderAction::add($this->order, null, $admin, 'admin', "变更发货信息");
|
||||
$this->subscribeExpressInfo($orderExpress);
|
||||
// 修改发货信息
|
||||
$data = [
|
||||
'order' => $this->order,
|
||||
'express' => $orderExpress,
|
||||
'dispatch_type' => 'express',
|
||||
];
|
||||
\think\Hook::listen('order_dispatch_change', $data);
|
||||
return $express;
|
||||
}
|
||||
|
||||
// 解析批量发货信息,筛选出能发货的订单
|
||||
public function multiple($params)
|
||||
{
|
||||
// 上传发货模板
|
||||
if (!empty($params['file'])) {
|
||||
$express = $params['express'];
|
||||
$file = $params['file']->getPathname();
|
||||
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
|
||||
$PHPExcel = $reader->load($file);
|
||||
$currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表
|
||||
$allRow = $currentSheet->getHighestRow(); //取得一共有多少行
|
||||
if ($allRow <= 2) {
|
||||
error_stop('您的发货列表为空');
|
||||
}
|
||||
|
||||
$orderExpressMap = [];
|
||||
$orderId = 0;
|
||||
$orderSn = "";
|
||||
for ($currentRow = 2; $currentRow <= $allRow - 1; $currentRow++) {
|
||||
|
||||
$orderId = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue() ?? $orderId;
|
||||
$orderSn = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue() ?? $orderSn;
|
||||
|
||||
$orderItemId = $currentSheet->getCellByColumnAndRow(8, $currentRow)->getValue();
|
||||
if (empty($orderItemId)) {
|
||||
error_stop("发货单格式不正确");
|
||||
}
|
||||
$orderExpressNo = $currentSheet->getCellByColumnAndRow(15, $currentRow)->getValue();
|
||||
if (empty($orderExpressNo)) {
|
||||
error_stop("请填写 订单ID-{$orderId} 的运单号");
|
||||
}
|
||||
$orderExpressMap["$orderId.$orderSn"][$orderExpressNo][] = $orderItemId;
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($orderExpressMap as $orderFlag => $orderExpress) {
|
||||
foreach ($orderExpress as $expressNo => $orderItemIds) {
|
||||
$order = explode('.', $orderFlag);
|
||||
$list[] = [
|
||||
'order_id' => $order[0],
|
||||
'order_sn' => $order[1],
|
||||
'order_item_ids' => $orderItemIds,
|
||||
'express' => [
|
||||
'name' => $express['name'],
|
||||
'code' => $express['code'],
|
||||
'no' => $expressNo
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
} else {
|
||||
$list = [];
|
||||
$orderIds = $params['order_ids'] ?? [];
|
||||
if (empty($orderIds)) {
|
||||
error_stop('请选择发货订单');
|
||||
}
|
||||
foreach ($orderIds as $orderId) {
|
||||
$list[] = [
|
||||
'order_id' => $orderId,
|
||||
'order_sn' => Order::where('id', $orderId)->value('order_sn'),
|
||||
'order_item_ids' => $this->getDispatchOrderItemIds($orderId)
|
||||
];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取可发货的订单商品
|
||||
private function getDispatchOrderItemIds($orderId)
|
||||
{
|
||||
$whereCanDispatch = [
|
||||
'order_id' => $orderId,
|
||||
'dispatch_status' => OrderItem::DISPATCH_STATUS_NOSEND,
|
||||
'aftersale_status' => ['neq', OrderItem::AFTERSALE_STATUS_ING],
|
||||
'refund_status' => OrderItem::REFUND_STATUS_NOREFUND,
|
||||
'dispatch_type' => 'express'
|
||||
];
|
||||
|
||||
$orderItems = OrderItem::where($whereCanDispatch)->column('id');
|
||||
|
||||
return $orderItems;
|
||||
}
|
||||
|
||||
// 订阅物流追踪
|
||||
private function subscribeExpressInfo($orderExpress)
|
||||
{
|
||||
try {
|
||||
$expressLib = new ExpressLib();
|
||||
|
||||
$a = $expressLib->subscribe([
|
||||
'express_code' => $orderExpress['express_code'],
|
||||
'express_no' => $orderExpress['express_no']
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// Nothing TODO
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 手动发货
|
||||
*
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function customDispatch($params)
|
||||
{
|
||||
$admin = auth_admin();
|
||||
|
||||
$custom_type = $params['custom_type'] ?? 'text';
|
||||
$custom_content = $params['custom_content'] ?? ($custom_type == 'text' ? '' : []);
|
||||
|
||||
if ($this->order->status !== Order::STATUS_PAID && !$this->order->isOffline($this->order)) {
|
||||
error_stop("该订单{$this->order->status_text},不能发货");
|
||||
}
|
||||
|
||||
if ($this->order->apply_refund_status === Order::APPLY_REFUND_STATUS_APPLY) {
|
||||
error_stop("该订单已申请退款,暂不能发货");
|
||||
}
|
||||
|
||||
// 获取手动发货的 items
|
||||
$orderItems = $this->getDispatchOrderItems($params, 'custom');
|
||||
|
||||
$customExt = [ // 手动发货信息
|
||||
'dispatch_content_type' => $custom_type,
|
||||
'dispatch_content' => $custom_content
|
||||
];
|
||||
|
||||
// 修改订单商品发货状态
|
||||
foreach ($orderItems as $orderItem) {
|
||||
$orderItem->dispatch_status = OrderItem::DISPATCH_STATUS_SENDED;
|
||||
$orderItem->ext = array_merge($orderItem->ext, $customExt, ['send_time' => time()]); // item 发货时间
|
||||
$orderItem->save();
|
||||
OrderAction::add($this->order, $orderItem, $admin, 'admin', "商品{$orderItem->goods_title}已发货");
|
||||
}
|
||||
// 订单发货后
|
||||
$data = [
|
||||
'order' => $this->order,
|
||||
'items' => $orderItems,
|
||||
'express' => null,
|
||||
'dispatch_type' => 'custom',
|
||||
];
|
||||
\think\Hook::listen('order_dispatch_after', $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 拼团完成时触发检测自动发货
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function grouponCheckDispatchAndSend()
|
||||
{
|
||||
$this->systemCheckAutoSend();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 普通商品自动发货
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkDispatchAndSend()
|
||||
{
|
||||
// 拼团不自动发货,等成团完成才发货
|
||||
$orderExt = $this->order['ext'];
|
||||
$buy_type = ($orderExt && isset($orderExt['buy_type'])) ? $orderExt['buy_type'] : '';
|
||||
if ($this->order['activity_type'] && strpos($this->order['activity_type'], 'groupon') !== false && $buy_type == 'groupon') {
|
||||
return true; // 这里不对拼团的订单进行自动发货,等拼团成功在检测
|
||||
}
|
||||
|
||||
// 检测需要自动发货的 item
|
||||
$this->systemCheckAutoSend();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 系统检测自动发货
|
||||
*/
|
||||
private function systemCheckAutoSend()
|
||||
{
|
||||
$autosendItems = [];
|
||||
|
||||
// 判断订单是否有需要发货的商品,并进行自动发货(autosend)
|
||||
foreach ($this->order->items as $key => $item) {
|
||||
// 判断不是未发货状态,或者退款完成,continue
|
||||
if (
|
||||
$item['dispatch_status'] == OrderItem::DISPATCH_STATUS_NOSEND
|
||||
&& $item['aftersale_status'] != OrderItem::AFTERSALE_STATUS_ING
|
||||
&& $item['refund_status'] == OrderItem::REFUND_STATUS_NOREFUND
|
||||
) {
|
||||
// 订单可以发货
|
||||
switch ($item['dispatch_type']) {
|
||||
case 'autosend':
|
||||
// 自动发货
|
||||
$autosendItems[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($autosendItems) {
|
||||
$this->autoSendItems($autosendItems, ['oper_type' => 'system']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当前订单需要自动发货的所有商品
|
||||
*
|
||||
* @param object|array $items
|
||||
* @return void
|
||||
*/
|
||||
private function autoSendItems($items)
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
$autosendExt = $this->getAutosendContent($item);
|
||||
|
||||
$item->dispatch_status = OrderItem::DISPATCH_STATUS_SENDED;
|
||||
$item->ext = array_merge($item->ext, $autosendExt, ['send_time' => time()]); // item 发货时间
|
||||
$item->save();
|
||||
OrderAction::add($this->order, $item, null, 'system', "商品{$item->goods_title}已发货");
|
||||
}
|
||||
|
||||
$data = [
|
||||
'order' => $this->order,
|
||||
'items' => $items,
|
||||
'express' => null,
|
||||
'dispatch_type' => 'custom',
|
||||
];
|
||||
|
||||
// 发货后事件,消息通知
|
||||
\think\Hook::listen('order_dispatch_after', $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取商品的自动发货模板数据
|
||||
*
|
||||
* @param object|array $item
|
||||
* @return array
|
||||
*/
|
||||
private function getAutosendContent($item)
|
||||
{
|
||||
// 获取配送模板
|
||||
$result = [];
|
||||
|
||||
$dispatch = DispatchModel::with([$item['dispatch_type']])->show()->where('type', $item['dispatch_type'])->where('id', $item['dispatch_id'])->find();
|
||||
if ($dispatch && $dispatch->autosend) {
|
||||
$autosend = $dispatch->autosend;
|
||||
if (in_array($autosend['type'], ['text', 'params'])) {
|
||||
$result['dispatch_content_type'] = $autosend['type'];
|
||||
$result['dispatch_content'] = $autosend['content'];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
442
addons/shopro/service/order/OrderOper.php
Normal file
442
addons/shopro/service/order/OrderOper.php
Normal file
@@ -0,0 +1,442 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order;
|
||||
|
||||
use app\admin\model\shopro\user\User;
|
||||
use app\admin\model\shopro\order\Order;
|
||||
use app\admin\model\shopro\order\OrderItem;
|
||||
use app\admin\model\shopro\order\Action;
|
||||
use app\admin\model\shopro\goods\Comment;
|
||||
use app\admin\model\shopro\activity\GiftLog;
|
||||
use app\admin\model\shopro\activity\Order as ActivityOrderModel;
|
||||
use app\admin\model\shopro\order\Invoice as OrderInvoice;
|
||||
|
||||
class OrderOper
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单自动关闭`
|
||||
*
|
||||
* @param \think\Model $order
|
||||
* @param \think\Model|null $user
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
public function close($order, $user = null, $type = 'user', $msg = '', $ext = [])
|
||||
{
|
||||
$order->status = Order::STATUS_CLOSED;
|
||||
$order->ext = array_merge($order->ext, $ext, ['closed_time' => time()]); // 关闭时间
|
||||
$order->allowField(true)->save();
|
||||
|
||||
Action::add($order, null, $user, $type, ($msg ? : $this->getOperText($type) . '关闭订单'));
|
||||
|
||||
// 订单自动关闭之后 行为 返还用户优惠券,积分
|
||||
$data = ['order' => $order];
|
||||
\think\Hook::listen('order_close_after', $data);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 订单取消
|
||||
*
|
||||
* @param object $order
|
||||
* @param object $user
|
||||
* @param string $type
|
||||
* @return object
|
||||
*/
|
||||
public function cancel($order, $user = null, $type = 'user', $msg = '')
|
||||
{
|
||||
$order->status = Order::STATUS_CANCEL; // 取消订单
|
||||
$order->ext = array_merge($order->ext, ['cancel_time' => time()]); // 取消时间
|
||||
$order->allowField(true)->save();
|
||||
|
||||
Action::add($order, null, $user, $type, ($msg ?: $this->getOperText($type) . '取消订单'));
|
||||
|
||||
// 订单取消,退回库存,退回优惠券积分,等
|
||||
$data = ['order' => $order];
|
||||
\think\Hook::listen('order_cancel_after', $data);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 申请全额退款
|
||||
*
|
||||
* @param object $order
|
||||
* @param object $user
|
||||
* @param string $type
|
||||
* @return object
|
||||
*/
|
||||
public function applyRefund($order, $user, $type = 'user')
|
||||
{
|
||||
$items = OrderItem::where('order_id', $order->id)->lock(true)->select();
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
if (in_array($item['refund_status'], [
|
||||
OrderItem::REFUND_STATUS_AGREE,
|
||||
OrderItem::REFUND_STATUS_COMPLETED,
|
||||
])) {
|
||||
error_stop('订单有退款,不可申请');
|
||||
}
|
||||
|
||||
if ($item['dispatch_status'] != OrderItem::DISPATCH_STATUS_NOSEND) {
|
||||
error_stop('订单已发货,不可申请');
|
||||
}
|
||||
}
|
||||
|
||||
$order->apply_refund_status = Order::APPLY_REFUND_STATUS_APPLY; // 申请退款
|
||||
$order->ext = array_merge($order->ext, ['apply_refund_time' => time()]); // 申请时间
|
||||
$order->save();
|
||||
|
||||
Action::add($order, null, $user, $type, $this->getOperText($type) . '申请全额退款');
|
||||
|
||||
// 订单申请全额退款
|
||||
$data = ['order' => $order, 'user' => $user];
|
||||
\think\Hook::listen('order_apply_refund_after', $data);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 确认收货
|
||||
*
|
||||
* @param object $order
|
||||
* @param array $itemIds
|
||||
* @param object|null $user
|
||||
* @param string $type
|
||||
* @return object
|
||||
*/
|
||||
public function confirm($order, $itemIds = [], $user = null, $type = 'user')
|
||||
{
|
||||
$items = OrderItem::canConfirm()->where('order_id', $order->id)->where(function ($query) use ($itemIds) {
|
||||
if ($itemIds) {
|
||||
// 只确认收货传入的 ids
|
||||
$query->whereIn('id', $itemIds);
|
||||
}
|
||||
})->lock(true)->select();
|
||||
|
||||
if (!$items) {
|
||||
error_stop('订单已确认收货,请不要重复确认收货');
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
$item->ext = array_merge($item->ext, ['confirm_time' => time()]);
|
||||
$item->dispatch_status = OrderItem::DISPATCH_STATUS_GETED; // 确认收货
|
||||
$item->save();
|
||||
|
||||
Action::add($order, $item, $user, $type, $this->getOperText($type) . '确认收货');
|
||||
|
||||
// 订单确认收货后
|
||||
$data = ['order' => $order, 'item' => $item];
|
||||
\think\Hook::listen('order_confirm_after', $data);
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 拒收收货
|
||||
*
|
||||
* @param object $order
|
||||
* @param array $itemIds
|
||||
* @param object|null $user
|
||||
* @param string $type
|
||||
* @return object
|
||||
*/
|
||||
public function refuse($order, $user = null, $type = 'user')
|
||||
{
|
||||
$items = OrderItem::canConfirm()->where('order_id', $order->id)->lock(true)->select();
|
||||
|
||||
if (!$items) {
|
||||
error_stop('没有可拒收的商品');
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
$item->ext = array_merge($item->ext, ['refuse_time' => time()]);
|
||||
$item->dispatch_status = OrderItem::DISPATCH_STATUS_REFUSE; // 拒收
|
||||
$item->save();
|
||||
|
||||
Action::add($order, $item, $user, $type, $this->getOperText($type) . '操作,用户拒绝收货');
|
||||
}
|
||||
|
||||
// 订单拒收后事件
|
||||
$data = ['order' => $order];
|
||||
\think\Hook::listen('order_refuse_after', $data);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 评价 (根据 comments 中的数据进行评价, 可以之评价一个(系统自动评价时候))
|
||||
*
|
||||
* @param object $order
|
||||
* @param object|null $user
|
||||
* @param string $type
|
||||
* @return object
|
||||
*/
|
||||
public function comment($order, $comments, $user = null, $type = 'user')
|
||||
{
|
||||
// 评价的orderItem id
|
||||
$comments = array_column($comments, null, 'item_id');
|
||||
$itemIds = array_keys($comments);
|
||||
|
||||
$items = OrderItem::canComment()->where('order_id', $order->id)->lock(true)->select();
|
||||
|
||||
if (!$items) {
|
||||
if ($type == 'system') {
|
||||
return $order; // 系统自动评价时检测到用户已经自己评价,这里直接返回,不抛出异常
|
||||
}
|
||||
error_stop('订单已评价,请不要重复评价');
|
||||
}
|
||||
|
||||
$orderConfig = sheep_config('shop.order');
|
||||
$orderUser = User::get($order->user_id);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (!in_array($item['id'], $itemIds)) {
|
||||
// 不在本次评价列表
|
||||
continue;
|
||||
}
|
||||
$comment = $comments[$item['id']] ?? [];
|
||||
|
||||
$status = 'normal';
|
||||
if (isset($orderConfig['comment_check']) && $orderConfig['comment_check'] && $type != 'system') {
|
||||
// 需要检查,并且不是系统自动评价
|
||||
$status = 'hidden';
|
||||
}
|
||||
|
||||
Comment::create([
|
||||
'goods_id' => $item->goods_id,
|
||||
'order_id' => $order->id,
|
||||
'order_item_id' => $item->id,
|
||||
'user_id' => $order->user_id,
|
||||
'user_nickname' => $orderUser ? $orderUser->nickname : null,
|
||||
'user_avatar' => $orderUser ? $orderUser->avatar : null,
|
||||
'level' => $comment['level'] ?? 5,
|
||||
'content' => $comment['content'] ?? ($orderConfig['auto_comment_content'] ?? '用户默认好评'),
|
||||
'images' => $comment['images'] ?? [],
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
$item->ext = array_merge($item->ext, ['comment_time' => time()]);
|
||||
$item->comment_status = OrderItem::COMMENT_STATUS_OK; // 评价
|
||||
$item->save();
|
||||
|
||||
Action::add($order, $item, $user, $type, $this->getOperText($type) . '评价完成');
|
||||
|
||||
// 订单评价后
|
||||
$data = ['order' => $order, 'item' => $item, 'user' => $user];
|
||||
\think\Hook::listen('order_comment_after', $data);
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单申请发票
|
||||
*
|
||||
* @param \think\Model $order
|
||||
* @param \think\Model $userInvoice
|
||||
* @param mixed $user
|
||||
* @param string $type
|
||||
* @return \think\Model
|
||||
*/
|
||||
public function applyInvoice($order, $userInvoice, $user = null, $type = 'user')
|
||||
{
|
||||
$ext = $order->ext;
|
||||
|
||||
if ($order->invoice_status !== 0 || !in_array($order->status_code, ['commented', 'nocomment', 'noget', 'nosend', 'completed'])) {
|
||||
// 可开票但是未开票,并且订单状态已支付,未退款
|
||||
error_stop('当前订单不可开票');
|
||||
}
|
||||
|
||||
// 保存收货地址
|
||||
$orderInvoice = new OrderInvoice();
|
||||
$orderInvoice->type = $userInvoice->type;
|
||||
$orderInvoice->order_id = $order->id;
|
||||
$orderInvoice->user_id = $userInvoice->user_id;
|
||||
$orderInvoice->name = $userInvoice->name;
|
||||
$orderInvoice->tax_no = $userInvoice->tax_no;
|
||||
$orderInvoice->address = $userInvoice->address;
|
||||
$orderInvoice->mobile = $userInvoice->mobile;
|
||||
$orderInvoice->bank_name = $userInvoice->bank_name;
|
||||
$orderInvoice->bank_no = $userInvoice->bank_no;
|
||||
$orderInvoice->amount = $ext['invoice_amount'];
|
||||
$orderInvoice->status = 'waiting';
|
||||
$orderInvoice->save();
|
||||
|
||||
$order->invoice_status = 1;
|
||||
$order->save();
|
||||
|
||||
Action::add($order, null, $user, $type, $this->getOperText($type) . '申请开票');
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 删除订单
|
||||
*
|
||||
* @param object $order
|
||||
* @param object|null $user
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
public function delete($order, $user = null, $type = 'user')
|
||||
{
|
||||
$order->delete(); // 删除订单
|
||||
|
||||
Action::add($order, null, $user, 'user', '用户删除订单');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 存储活动信息
|
||||
*
|
||||
* @param \think\Model $order
|
||||
* @param array $result
|
||||
* @return void
|
||||
*/
|
||||
public function addActivityOrder($order)
|
||||
{
|
||||
$ext = $order->ext;
|
||||
$items = $order->items;
|
||||
$goodsIds = array_column($items, 'goods_id');
|
||||
|
||||
$model = new ActivityOrderModel();
|
||||
$model->order_id = $order->id;
|
||||
$model->user_id = $order->user_id;
|
||||
$model->activity_id = $order->activity_id;
|
||||
$model->activity_title = $ext['activity_title'] ?? null;
|
||||
$model->activity_type = $ext['activity_type'] ?? null;
|
||||
$model->pay_fee = $order->pay_fee;
|
||||
$model->goods_amount = $order->goods_amount;
|
||||
$model->discount_fee = $ext['activity_discount_amount'] ?? 0; // 普通商品总额和活动时商品总额的差
|
||||
$model->goods_ids = join(',', $goodsIds);
|
||||
|
||||
$model->save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 存储促销信息
|
||||
*
|
||||
* @param \think\Model $order
|
||||
* @param array $result
|
||||
* @return void
|
||||
*/
|
||||
public function addPromosOrder($order)
|
||||
{
|
||||
$ext = $order->ext;
|
||||
$promoInfos = $ext['promo_infos'] ?? [];
|
||||
|
||||
foreach ($promoInfos as $key => $info) {
|
||||
$model = new ActivityOrderModel();
|
||||
$model->order_id = $order->id;
|
||||
$model->user_id = $order->user_id;
|
||||
$model->activity_id = $info['activity_id'];
|
||||
$model->activity_title = $info['activity_title'];
|
||||
$model->activity_type = $info['activity_type'];
|
||||
$model->pay_fee = $order->pay_fee;
|
||||
$model->goods_amount = $info['promo_goods_amount'];
|
||||
|
||||
$model->discount_fee = 0;
|
||||
if (in_array($info['activity_type'], ['full_reduce', 'full_discount', 'free_shipping'])) {
|
||||
$model->discount_fee = $info['promo_discount_money'];
|
||||
} else if ($info['activity_type'] == 'full_gift') {
|
||||
// 这里设置为 0,等支付成功之后补充
|
||||
$model->discount_fee = 0;
|
||||
}
|
||||
|
||||
$model->goods_ids = join(',', $info['goods_ids']);
|
||||
$rules = [
|
||||
'rule_type' => $info['rule_type'],
|
||||
'discount_rule' => $info['discount_rule'],
|
||||
];
|
||||
if ($info['activity_type'] == 'full_gift') {
|
||||
$rules['limit_num'] = $info['limit_num'] ?? 0;
|
||||
$rules['event'] = $info['event'] ?? 0;
|
||||
}
|
||||
$currentExt['rules'] = $rules;
|
||||
$model->ext = $currentExt;
|
||||
$model->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将活动订单标记为已支付
|
||||
*
|
||||
* @param [type] $order
|
||||
* @return void
|
||||
*/
|
||||
public function activityOrderPaid($order)
|
||||
{
|
||||
$activityOrders = ActivityOrderModel::where('order_id', $order->id)->select();
|
||||
|
||||
foreach ($activityOrders as $activityOrder) {
|
||||
if ($activityOrder->activity_type == 'full_gift') {
|
||||
$value_money = GiftLog::where('activity_id', $activityOrder->activity_id)
|
||||
->where('order_id', $activityOrder->order_id)
|
||||
->where('user_id', $activityOrder->user_id)
|
||||
->whereIn('type', ['money', 'coupon']) // 这里只算 赠送的余额,和优惠券(不算积分,和赠送商品的价值)
|
||||
->sum('value');
|
||||
|
||||
$activityOrder->discount_fee = $value_money; // 补充赠送的价值
|
||||
} else if (in_array($activityOrder->activity_type, ['groupon', 'groupon_ladder'])) {
|
||||
$ext = $order->ext;
|
||||
$currentExt['buy_type'] = $ext['buy_type'] ?? '';
|
||||
$currentExt['groupon_id'] = $ext['groupon_id'] ?? 0; // 开团时候,支付之后才会有 groupon_id
|
||||
$activityOrder->ext = $currentExt;
|
||||
}
|
||||
|
||||
$activityOrder->status = ActivityOrderModel::STATUS_PAID;
|
||||
$activityOrder->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 oper_type 获取对应的用户
|
||||
*/
|
||||
private function getOperText($oper_type)
|
||||
{
|
||||
switch($oper_type) {
|
||||
case 'user':
|
||||
$oper_text = '用户';
|
||||
break;
|
||||
case 'admin':
|
||||
$oper_text = '管理员';
|
||||
break;
|
||||
case 'system':
|
||||
$oper_text = '系统自动';
|
||||
break;
|
||||
default :
|
||||
$oper_text = '系统自动';
|
||||
break;
|
||||
}
|
||||
|
||||
return $oper_text;
|
||||
}
|
||||
}
|
||||
247
addons/shopro/service/order/OrderRefund.php
Normal file
247
addons/shopro/service/order/OrderRefund.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order;
|
||||
|
||||
use app\admin\model\shopro\order\Order;
|
||||
use app\admin\model\shopro\order\OrderItem;
|
||||
use app\admin\model\shopro\order\Action;
|
||||
use app\admin\model\shopro\Pay as PayModel;
|
||||
use app\admin\model\shopro\user\User;
|
||||
use app\common\model\User as CommonUser;
|
||||
use addons\shopro\service\pay\PayRefund;
|
||||
use addons\shopro\service\pay\PayOper;
|
||||
use addons\shopro\traits\CouponSend;
|
||||
use addons\shopro\service\StockSale;
|
||||
use addons\shopro\facade\Activity as ActivityFacade;
|
||||
|
||||
class OrderRefund
|
||||
{
|
||||
|
||||
use CouponSend;
|
||||
|
||||
protected $order = null;
|
||||
|
||||
protected $default_refund_type = 'back';
|
||||
|
||||
public function __construct($order)
|
||||
{
|
||||
$this->order = $order;
|
||||
|
||||
$this->default_refund_type = $order->ext['refund_type'] ?? 'back';
|
||||
}
|
||||
|
||||
/**
|
||||
* 全额退款(无条件退款, 优惠券积分全退)
|
||||
*
|
||||
* @param \think\Model $user
|
||||
* @param string $remark
|
||||
* @return void
|
||||
*/
|
||||
public function fullRefund($user = null, $data = [])
|
||||
{
|
||||
$items = OrderItem::where('order_id', $this->order->id)->lock(true)->select();
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
if (in_array($item['refund_status'], [
|
||||
OrderItem::REFUND_STATUS_AGREE,
|
||||
OrderItem::REFUND_STATUS_COMPLETED,
|
||||
])) {
|
||||
error_stop('订单有退款,不能全额退款');
|
||||
}
|
||||
}
|
||||
|
||||
// 返还库存,减少销量
|
||||
$stockSale = new StockSale();
|
||||
$stockSale->backStockSale($this->order, $items);
|
||||
|
||||
if ($this->order->apply_refund_status == Order::APPLY_REFUND_STATUS_APPLY) {
|
||||
// 如果订单申请了全额退款,这里将全额退款状态改为 已完成
|
||||
$this->order->apply_refund_status = Order::APPLY_REFUND_STATUS_FINISH;
|
||||
$this->order->save();
|
||||
}
|
||||
|
||||
if ($this->order->coupon_id) {
|
||||
// 订单使用了优惠券,退回用户优惠券
|
||||
$this->backUserCoupon($this->order->coupon_id);
|
||||
}
|
||||
|
||||
if ($this->order->activity_id || $this->order->promo_types) {
|
||||
// 有活动,执行活动失败
|
||||
ActivityFacade::buyFail($this->order, 'refund');
|
||||
}
|
||||
|
||||
$total_refund_fee = '0'; // 已退金额
|
||||
foreach ($items as $key => $item) {
|
||||
$is_last = (count($items) - 1) == $key ? true : false; // 是否最后一个商品
|
||||
|
||||
// 计算 refund_fee
|
||||
$refund_fee = $this->calcRefundFee($item, $total_refund_fee, $is_last);
|
||||
|
||||
$item->refund_status = OrderItem::REFUND_STATUS_AGREE; // 同意退款
|
||||
$item->refund_fee = $refund_fee; // 实时计算,总 退款金额 等于 商品实际支付金额
|
||||
$item->ext = array_merge($item->ext, ['refund_time' => time()]); // 退款时间
|
||||
$item->save();
|
||||
|
||||
// 累加已退金额
|
||||
if ($refund_fee > 0) {
|
||||
$total_refund_fee = bcadd($total_refund_fee, $refund_fee, 2);
|
||||
}
|
||||
|
||||
Action::add($this->order, $item, $user, ($user ? (($user instanceof User || $user instanceof CommonUser) ? 'user' : 'admin') : 'system'), (isset($data['remark']) && $data['remark'] ? $data['remark'] . ',' : '') . '退款金额:¥' . $item->refund_fee);
|
||||
|
||||
// 订单商品退款后
|
||||
$eventData = ['order' => $this->order, 'item' => $item];
|
||||
\think\Hook::listen('order_item_refund_after', $eventData);
|
||||
}
|
||||
|
||||
// 退回已支付的所有金额(积分,余额等)
|
||||
$this->refundAllPays($data);
|
||||
|
||||
// 订单商品退款后
|
||||
$eventData = [
|
||||
'order' => $this->order,
|
||||
'items' => $items,
|
||||
'refund_type' => $data['refund_type'] ?? $this->default_refund_type,
|
||||
'refund_method' => 'full_refund'
|
||||
];
|
||||
\think\Hook::listen('order_refund_after', $eventData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 部分退款 (通过订单商品退款)
|
||||
*
|
||||
* @param \think\Model $item
|
||||
* @param string $refund_money
|
||||
* @param \think\Model $user
|
||||
* @param string $remark
|
||||
* @return void
|
||||
*/
|
||||
public function refund($item, $refund_money, $user = null, $data = [])
|
||||
{
|
||||
$item->refund_status = OrderItem::REFUND_STATUS_AGREE; // 同意退款
|
||||
$item->refund_fee = $refund_money;
|
||||
$item->ext = array_merge($item->ext, ['refund_time' => time()]); // 退款时间
|
||||
$item->save();
|
||||
|
||||
Action::add($this->order, $item, $user, ($user ? (($user instanceof User || $user instanceof CommonUser) ? 'user' : 'admin') : 'system'), (isset($data['remark']) && $data['remark'] ? $data['remark'] . ',' : '') . '退款金额:¥' . $refund_money);
|
||||
|
||||
// 订单商品退款后
|
||||
$eventData = ['order' => $this->order, 'item' => $item];
|
||||
\think\Hook::listen('order_item_refund_after', $eventData);
|
||||
|
||||
// 查找符合条件的 pays 并从中退指定金额
|
||||
$this->refundPaysByMoney((string)$refund_money, $data);
|
||||
|
||||
// 订单商品退款后
|
||||
$eventData = [
|
||||
'order' => $this->order,
|
||||
'items' => [$item],
|
||||
'refund_type' => $data['refund_type'] ?? $this->default_refund_type,
|
||||
'refund_method' => 'item_refund'
|
||||
];
|
||||
\think\Hook::listen('order_refund_after', $eventData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退回已支付的所有 pays 记录
|
||||
*
|
||||
* @param string $remark
|
||||
* @return void
|
||||
*/
|
||||
public function refundAllPays($data = [])
|
||||
{
|
||||
// 商城订单,已支付的 pay 记录
|
||||
$pays = PayModel::typeOrder()->paid()->where('order_id', $this->order->id)->lock(true)->select();
|
||||
|
||||
$refund = new PayRefund($this->order->user_id);
|
||||
foreach ($pays as $key => $pay) {
|
||||
$refund->fullRefund($pay, [
|
||||
'refund_type' => $data['refund_type'] ?? $this->default_refund_type,
|
||||
'platform' => $this->order->platform,
|
||||
'remark' => $data['remark'] ?? ''
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 查找符合条件的 pays 并从中退指定金额 (不退积分,包括积分抵扣的积分)
|
||||
*
|
||||
* @param string $refund_money
|
||||
* @param string $remark
|
||||
* @return void
|
||||
*/
|
||||
protected function refundPaysByMoney(string $refund_money, $data = [])
|
||||
{
|
||||
$payOper = new PayOper($this->order->user_id);
|
||||
$pays = $payOper->getCanRefundPays($this->order->id);
|
||||
$remain_max_refund_money = $payOper->getRemainRefundMoney($pays);
|
||||
|
||||
if (bccomp($refund_money, $remain_max_refund_money, 2) === 1) {
|
||||
// 退款金额超出最大支付金额
|
||||
error_stop('退款金额超出最大可退款金额');
|
||||
}
|
||||
|
||||
$current_refunded_money = '0'; // 本次退款,已退金额累计
|
||||
$refund = new PayRefund($this->order->user_id);
|
||||
foreach ($pays as $key => $pay) {
|
||||
$current_remain_money = bcsub($refund_money, $current_refunded_money, 2); // 剩余应退款金额
|
||||
if ($current_remain_money <= 0) {
|
||||
// 退款完成
|
||||
break;
|
||||
}
|
||||
|
||||
$current_pay_remain_money = bcsub($pay->pay_fee, $pay->refund_fee, 2); // 当前 pay 记录剩余可退金额
|
||||
if ($current_pay_remain_money <= 0) {
|
||||
// 当前 pay 支付的金额已经退完了,循环下一个
|
||||
continue;
|
||||
}
|
||||
|
||||
$current_refund_money = min($current_remain_money, $current_pay_remain_money); // 取最小值
|
||||
|
||||
$refund->refund($pay, $current_refund_money, [
|
||||
'refund_type' => $data['refund_type'] ?? $this->default_refund_type,
|
||||
'platform' => $this->order->platform,
|
||||
'remark' => $data['remark'] ?? ''
|
||||
]);
|
||||
|
||||
$current_refunded_money = bcadd($current_refunded_money, $current_refund_money, 2);
|
||||
}
|
||||
|
||||
if ($refund_money > $current_refunded_money) {
|
||||
// 退款金额超出最大支付金额
|
||||
error_stop('退款金额超出最大可退款金额');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算 item 应退金额
|
||||
*
|
||||
* @param \think\Model $item
|
||||
* @param string $total_refund_fee
|
||||
* @param boolean $is_last
|
||||
* @return string
|
||||
*/
|
||||
private function calcRefundFee($item, $total_refund_fee, $is_last = false)
|
||||
{
|
||||
$pay_fee = $this->order->pay_fee; // 支付总金额
|
||||
|
||||
$current_goods_amount = bcmul($item->goods_price, (string)$item->goods_num, 2);
|
||||
$total_amount = bcadd($current_goods_amount, $item->dispatch_fee, 2);
|
||||
$refund_fee = bcsub($total_amount, $item->discount_fee, 2); // (商品金额 + 运费金额) - 总优惠(活动,优惠券,包邮优惠)
|
||||
if ($total_refund_fee >= $pay_fee) {
|
||||
$refund_fee = 0;
|
||||
} else {
|
||||
$remain_fee = bcsub($pay_fee, $total_refund_fee, 2);
|
||||
$refund_fee = $remain_fee > $refund_fee ? ($is_last ? $remain_fee : $refund_fee) : $remain_fee;
|
||||
}
|
||||
|
||||
return $refund_fee;
|
||||
}
|
||||
}
|
||||
120
addons/shopro/service/order/OrderThrough.php
Normal file
120
addons/shopro/service/order/OrderThrough.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order;
|
||||
|
||||
use app\admin\model\shopro\order\OrderItem;
|
||||
use app\admin\model\shopro\order\Order;
|
||||
use addons\shopro\service\StockSale;
|
||||
use addons\shopro\facade\Activity as ActivityFacade;
|
||||
|
||||
class OrderThrough
|
||||
{
|
||||
|
||||
/**
|
||||
* 商品限购
|
||||
*
|
||||
* @param [type] $buyInfo
|
||||
* @param \Closure $next
|
||||
* @return void
|
||||
*/
|
||||
public function limitBuy ($buyInfo, \Closure $next)
|
||||
{
|
||||
$user = auth_user();
|
||||
$goods = $buyInfo['goods'];
|
||||
$goods_num = $buyInfo['goods_num'] ?? 1;
|
||||
$activity = $goods['activity'];
|
||||
|
||||
if ($activity) {
|
||||
// 活动限购
|
||||
$rules = $activity['rules'] ?? [];
|
||||
$limit_type = 'activity';
|
||||
$limit_num = (isset($rules['limit_num']) && $rules['limit_num'] > 0) ? $rules['limit_num'] : 0;
|
||||
} else {
|
||||
// 普通商品限购
|
||||
$limit_type = $goods->limit_type;
|
||||
$limit_num = ($limit_type != 'none' && $goods->limit_num > 0) ? $goods->limit_num : 0;
|
||||
}
|
||||
|
||||
if ($limit_num) { // limit_num = 0; 不限购
|
||||
// 查询用户老订单,判断本次下单数量,判断是否超过购买限制, 未支付的或者已完成的都算
|
||||
$buy_num = OrderItem::where('user_id', $user['id'])->where('goods_id', $goods->id)
|
||||
->where(function ($query) use ($limit_type, $goods, $activity) {
|
||||
if ($limit_type == 'daily') {
|
||||
// 按天限购
|
||||
$daily_start = strtotime(date('Y-m-d'));
|
||||
$daily_end = strtotime(date('Y-m-d', (time() + 86400))) - 1;
|
||||
$query->where('createtime', 'between', [$daily_start, $daily_end]);
|
||||
} else if ($limit_type == 'activity') {
|
||||
$query->where('activity_id', $activity['id']); // 这个活动下所有的购买记录
|
||||
} else {
|
||||
// all,不加任何条件
|
||||
}
|
||||
|
||||
return $query;
|
||||
})
|
||||
->whereExists(function ($query) use ($goods) {
|
||||
$order_table_name = (new Order())->getQuery()->getTable();
|
||||
$query->table($order_table_name)->where('order_id=' . $order_table_name . '.id')
|
||||
->whereNotIn('status', [Order::STATUS_CLOSED, Order::STATUS_CANCEL]); // 除了交易关闭,和 取消的订单
|
||||
})->sum('goods_num');
|
||||
|
||||
if (($buy_num + $goods_num) > $limit_num) {
|
||||
$msg = '该商品' . ($limit_type == 'daily' ? '每日' : ($limit_type == 'activity' ? '活动期间' : '')) . '限购 ' . $limit_num . ' 件';
|
||||
|
||||
if ($buy_num < $limit_num) {
|
||||
$msg .= ',当前还可购买 ' . ($limit_num - $buy_num) . ' 件';
|
||||
}
|
||||
|
||||
error_stop($msg);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($buyInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function checkStock($buyInfo, \Closure $next)
|
||||
{
|
||||
$goods = $buyInfo['goods'];
|
||||
$activity = $goods['activity'];
|
||||
|
||||
if (!$activity) {
|
||||
$stockSale = new StockSale();
|
||||
$stockSale->stockLock($buyInfo);
|
||||
}
|
||||
|
||||
return $next($buyInfo);
|
||||
}
|
||||
|
||||
|
||||
public function activity($buyInfo, \Closure $next)
|
||||
{
|
||||
$goods = $buyInfo['goods'];
|
||||
$activity = $goods['activity'];
|
||||
|
||||
if ($activity) {
|
||||
$buyInfo = ActivityFacade::buy($buyInfo, $activity);
|
||||
}
|
||||
|
||||
return $next($buyInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function through($throughs = [])
|
||||
{
|
||||
$throughs = is_array($throughs) ? $throughs : [$throughs];
|
||||
|
||||
$pipes = [];
|
||||
foreach ($throughs as $through) {
|
||||
if (method_exists($this, $through)) {
|
||||
$pipes[] = function ($params, \Closure $next) use ($through) {
|
||||
return $this->{$through}($params, $next);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return $pipes;
|
||||
}
|
||||
}
|
||||
102
addons/shopro/service/order/shippingInfo/Base.php
Normal file
102
addons/shopro/service/order/shippingInfo/Base.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order\shippingInfo;
|
||||
|
||||
use addons\shopro\exception\ShoproException;
|
||||
use app\admin\model\shopro\Pay as PayModel;
|
||||
use think\helper\Str;
|
||||
|
||||
class Base
|
||||
{
|
||||
protected $order = null;
|
||||
|
||||
public function __construct($order)
|
||||
{
|
||||
$this->order = $order;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置微信支付相关的参数
|
||||
*
|
||||
* @param array $uploadParams
|
||||
* @param \think\Model $wechatPay
|
||||
* @return array
|
||||
*/
|
||||
protected function setWechatParams($uploadParams, $wechatPay)
|
||||
{
|
||||
$order_key = [
|
||||
'order_number_type' => 2,
|
||||
'transaction_id' => $wechatPay->transaction_id,
|
||||
'out_trade_no' => $wechatPay->pay_sn,
|
||||
];
|
||||
|
||||
$payer = [
|
||||
'openid' => $wechatPay['buyer_info']
|
||||
];
|
||||
|
||||
foreach ($uploadParams as &$params) {
|
||||
$params['order_key'] = $order_key;
|
||||
$params['payer'] = $payer;
|
||||
}
|
||||
|
||||
return $uploadParams;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单中的微信支付 pay 记录
|
||||
*
|
||||
* @return think\Model
|
||||
*/
|
||||
protected function getWechatPay($type = 'order')
|
||||
{
|
||||
$wechatPay = PayModel::{'type' . Str::studly($type)}()->where('order_id', $this->order['id'])
|
||||
->where('status', '<>', PayModel::PAY_STATUS_UNPAID)
|
||||
->where('pay_type', 'wechat')->order('id', 'desc')->find();
|
||||
|
||||
if (!$wechatPay) {
|
||||
throw new ShoproException('未找到订单微信支付记录');
|
||||
}
|
||||
|
||||
return $wechatPay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 配送方式转换
|
||||
*
|
||||
* @param string $dispatch_type
|
||||
* @return integer
|
||||
*/
|
||||
protected function getLogisticsType($dispatch_type)
|
||||
{
|
||||
switch ($dispatch_type) {
|
||||
case 'express':
|
||||
$logistics_type = 1;
|
||||
break;
|
||||
case 'store_delivery':
|
||||
$logistics_type = 2;
|
||||
break;
|
||||
case 'autosend':
|
||||
$logistics_type = 3;
|
||||
break;
|
||||
case 'custom':
|
||||
$logistics_type = 3;
|
||||
break;
|
||||
case 'selfetch':
|
||||
$logistics_type = 4;
|
||||
break;
|
||||
default:
|
||||
$logistics_type = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return $logistics_type;
|
||||
}
|
||||
|
||||
}
|
||||
300
addons/shopro/service/order/shippingInfo/OrderShippingInfo.php
Normal file
300
addons/shopro/service/order/shippingInfo/OrderShippingInfo.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order\shippingInfo;
|
||||
|
||||
use app\admin\model\shopro\order\OrderItem;
|
||||
use app\admin\model\shopro\order\Express as OrderExpress;
|
||||
use app\admin\model\shopro\order\Address as OrderAddress;
|
||||
|
||||
class OrderShippingInfo extends Base
|
||||
{
|
||||
|
||||
protected $orderItems = null;
|
||||
|
||||
protected $dispatchTypes = [];
|
||||
|
||||
|
||||
/**
|
||||
* 获取整个订单的 shippingParams 参数
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getShippingParams()
|
||||
{
|
||||
$wechatPay = $this->getWechatPay();
|
||||
|
||||
$this->setSendOrderItems();
|
||||
|
||||
$uploadParams = [];
|
||||
if (in_array('express', $this->dispatchTypes)) {
|
||||
// 有 快递物流 商品
|
||||
$expressUploadParams = $this->getExpressShippingParams();
|
||||
$uploadParams = array_merge($uploadParams, $expressUploadParams);
|
||||
}
|
||||
|
||||
if (!$uploadParams && array_intersect(['autosend', 'custom'], $this->dispatchTypes)) {
|
||||
// 有 自动发货,或者手动发货 商品
|
||||
$virtualParams = $this->getVirtualShippingParams();
|
||||
$uploadParams[] = $virtualParams;
|
||||
}
|
||||
|
||||
if (!$uploadParams && in_array('selfetch', $this->dispatchTypes)) {
|
||||
// 有 到店自提 商品
|
||||
$selfParams = $this->getSelfetchShippingParams();
|
||||
$uploadParams[] = $selfParams;
|
||||
}
|
||||
|
||||
|
||||
if (!$uploadParams && in_array('store_delivery', $this->dispatchTypes)) {
|
||||
// 有 店铺配送 商品
|
||||
$storeDeliveryParams = $this->getStoreDeliveryShippingParams();
|
||||
$uploadParams[] = $storeDeliveryParams;
|
||||
}
|
||||
|
||||
// 处理微信相关参数
|
||||
return $this->setWechatParams($uploadParams, $wechatPay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 修改物流是获取指定 包裹的 shippingParams
|
||||
*
|
||||
* @param \think\Model $express
|
||||
* @return array
|
||||
*/
|
||||
public function getChangeShippingParams($express)
|
||||
{
|
||||
$wechatPay = $this->getWechatPay();
|
||||
|
||||
$this->setSendOrderItems();
|
||||
|
||||
$orderExpresses = collection([$express]); // 指定包裹
|
||||
|
||||
// 获取包裹的 params
|
||||
$uploadParams = $this->getExpressShippingParamsByExpresses($orderExpresses);
|
||||
|
||||
// 处理微信相关参数
|
||||
return $this->setWechatParams($uploadParams, $wechatPay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单所有包裹的 shippingParams
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getExpressShippingParams()
|
||||
{
|
||||
$orderExpresses = collection(OrderExpress::where('order_id', $this->order['id'])->select());
|
||||
|
||||
return $this->getExpressShippingParamsByExpresses($orderExpresses);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单指定包裹的 shippingParams
|
||||
*
|
||||
* @param \think\Model $order
|
||||
* @param \think\Collection $orderExpresses
|
||||
* @return array
|
||||
*/
|
||||
private function getExpressShippingParamsByExpresses($orderExpresses)
|
||||
{
|
||||
$uploadParams = [];
|
||||
if (!$orderExpresses->isEmpty()) {
|
||||
$orderAddress = OrderAddress::where('order_id', $this->order['id'])->find();
|
||||
|
||||
$receiver_contact = $orderAddress ? mb_substr($orderAddress->mobile, 0, 3) . '****' . mb_substr($orderAddress->mobile, -4) : '130****0000';
|
||||
|
||||
$shippingList = [];
|
||||
foreach ($orderExpresses as $orderExpress) {
|
||||
$currentItems = $this->getItemsByCondition('order_express_id', $orderExpress->id);
|
||||
|
||||
$item_desc = [];
|
||||
foreach ($currentItems as $currentItem) {
|
||||
$item_desc[] = $currentItem['goods_title'] . '*' . $currentItem['goods_num'];
|
||||
}
|
||||
|
||||
$item_desc = join(', ', $item_desc);
|
||||
$item_desc = mb_strlen($item_desc) > 110 ? mb_substr($item_desc, 0, 110) . ' 等商品' : $item_desc; // 处理字符串
|
||||
|
||||
$shippingList[] = [
|
||||
'tracking_no' => $orderExpress['express_no'],
|
||||
'express_company' => $orderExpress['express_code'],
|
||||
'item_desc' => $item_desc,
|
||||
'contact' => [
|
||||
'receiver_contact' => $receiver_contact
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if ($shippingList) {
|
||||
// 发货
|
||||
$uploadParams[] = [
|
||||
'logistics_type' => $this->getLogisticsType('express'),
|
||||
'shipping_list' => $shippingList,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $uploadParams;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单中虚拟商品的 shippingParams
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getVirtualShippingParams()
|
||||
{
|
||||
// 是否存在虚拟发货商品
|
||||
$virtualItems = $this->getItemsByCondition('dispatch_type', ['autosend', 'custom'], 'in_array');
|
||||
|
||||
if (!$virtualItems->isEmpty()) {
|
||||
$shippingList = [];
|
||||
|
||||
$item_desc = [];
|
||||
foreach ($virtualItems as $virtualItem) {
|
||||
$item_desc[] = $virtualItem['goods_title'] . '*' . $virtualItem['goods_num'];
|
||||
}
|
||||
|
||||
$item_desc = join(', ', $item_desc);
|
||||
$item_desc = mb_strlen($item_desc) > 110 ? mb_substr($item_desc, 0, 110) . ' 等商品' : $item_desc; // 处理字符串
|
||||
|
||||
$shippingList[] = [
|
||||
'item_desc' => $item_desc,
|
||||
];
|
||||
|
||||
// 发货
|
||||
$currentParams = [
|
||||
'logistics_type' => $this->getLogisticsType('autosend'),
|
||||
'shipping_list' => $shippingList,
|
||||
];
|
||||
}
|
||||
|
||||
return $currentParams ?? null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单中到店自提商品的 shippingParams
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSelfetchShippingParams()
|
||||
{
|
||||
// 到店自提商品
|
||||
$selfetchItems = $this->getItemsByCondition('dispatch_type', ['selfetch'], 'in_array');
|
||||
if (!$selfetchItems->isEmpty()) {
|
||||
$shippingList = [];
|
||||
|
||||
$item_desc = [];
|
||||
foreach ($selfetchItems as $selfetchItem) {
|
||||
$item_desc[] = $selfetchItem['goods_title'] . '*' . $selfetchItem['goods_num'];
|
||||
}
|
||||
|
||||
$item_desc = join(', ', $item_desc);
|
||||
$item_desc = mb_strlen($item_desc) > 110 ? mb_substr($item_desc, 0, 110) . ' 等商品' : $item_desc; // 处理字符串
|
||||
|
||||
$shippingList[] = [
|
||||
'item_desc' => $item_desc,
|
||||
];
|
||||
|
||||
// 发货
|
||||
$currentParams = [
|
||||
'logistics_type' => $this->getLogisticsType('selfetch'),
|
||||
'shipping_list' => $shippingList,
|
||||
];
|
||||
}
|
||||
|
||||
return $currentParams ?? null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单中店铺配送商品的 shippingParams
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStoreDeliveryShippingParams()
|
||||
{
|
||||
// 到店自提商品
|
||||
$storeDeliveryItems = $this->getItemsByCondition('dispatch_type', ['store_delivery'], 'in_array');
|
||||
if (!$storeDeliveryItems->isEmpty()) {
|
||||
$shippingList = [];
|
||||
|
||||
$item_desc = [];
|
||||
foreach ($storeDeliveryItems as $storeDeliveryItem) {
|
||||
$item_desc[] = $storeDeliveryItem['goods_title'] . '*' . $storeDeliveryItem['goods_num'];
|
||||
}
|
||||
|
||||
$item_desc = join(', ', $item_desc);
|
||||
$item_desc = mb_strlen($item_desc) > 110 ? mb_substr($item_desc, 0, 110) . ' 等商品' : $item_desc; // 处理字符串
|
||||
|
||||
$shippingList[] = [
|
||||
'item_desc' => $item_desc,
|
||||
];
|
||||
|
||||
// 发货
|
||||
$currentParams = [
|
||||
'logistics_type' => $this->getLogisticsType('store_delivery'),
|
||||
'shipping_list' => $shippingList,
|
||||
];
|
||||
}
|
||||
|
||||
return $currentParams ?? null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 设置 orderItems (这里是订单中的所有 items)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setSendOrderItems()
|
||||
{
|
||||
$orderItems = OrderItem::where('order_id', $this->order['id'])->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND)
|
||||
->whereIn('dispatch_status', [OrderItem::DISPATCH_STATUS_SENDED, OrderItem::DISPATCH_STATUS_GETED])->select();
|
||||
|
||||
$this->orderItems = $orderItems instanceof \think\Collection ? $orderItems : collection($orderItems);
|
||||
|
||||
$this->dispatchTypes = array_values(array_unique(array_filter($this->orderItems->column('dispatch_type'))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据条件获取指定 itemd
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @return \think\Collection
|
||||
*/
|
||||
private function getItemsByCondition($field, $value, $exp = '')
|
||||
{
|
||||
$new = [];
|
||||
foreach ($this->orderItems as $item) {
|
||||
if ($exp == 'in_array') {
|
||||
if (in_array($item[$field], $value)) {
|
||||
$new[] = $item;
|
||||
}
|
||||
} else {
|
||||
if ($item[$field] == $value) {
|
||||
$new[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collection($new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\service\order\shippingInfo;
|
||||
|
||||
class TradeOrderShippingInfo extends Base
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 获取整个订单的 shippingParams 参数
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getShippingParams()
|
||||
{
|
||||
$wechatPay = $this->getWechatPay('trade_order');
|
||||
|
||||
$uploadParams = [];
|
||||
|
||||
if ($this->order->type == 'recharge') {
|
||||
// 充值订单
|
||||
$virtualParams = $this->getVirtualShippingParams();
|
||||
$uploadParams[] = $virtualParams;
|
||||
}
|
||||
|
||||
// 处理微信相关参数
|
||||
return $this->setWechatParams($uploadParams, $wechatPay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取订单中虚拟商品的 shippingParams
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getVirtualShippingParams()
|
||||
{
|
||||
$item_desc = '用户充值订单';
|
||||
$shippingList[] = [
|
||||
'item_desc' => $item_desc,
|
||||
];
|
||||
|
||||
// 发货
|
||||
return [
|
||||
'logistics_type' => $this->getLogisticsType('autosend'),
|
||||
'shipping_list' => $shippingList,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user