文件
Henglianshutong/application/admin/controller/shopro/order/Order.php

1254 行
51 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\admin\controller\shopro\order;
use think\Db;
use think\exception\HttpResponseException;
use app\admin\controller\shopro\Common;
use app\admin\model\shopro\order\Order as OrderModel;
use app\admin\model\shopro\order\Address as OrderAddressModel;
use app\admin\model\shopro\order\OrderItem;
use app\admin\model\shopro\Admin;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\order\Action as OrderActionModel;
use app\admin\model\shopro\order\Express as OrderExpressModel;
use addons\shopro\service\pay\PayOper;
use addons\shopro\service\order\OrderOper;
use addons\shopro\service\order\OrderRefund;
use app\admin\model\shopro\Pay as PayModel;
use addons\shopro\service\order\OrderDispatch as OrderDispatchService;
use addons\shopro\library\express\Express as ExpressLib;
use addons\shopro\library\Operator;
use addons\shopro\facade\Wechat;
class Order extends Common
{
protected $noNeedRight = ['getType', 'dispatchList'];
public function _initialize()
{
parent::_initialize();
$this->model = new OrderModel;
}
/**
* 订单列表
*
* @return \think\Response
*/
public function index()
{
if (!$this->request->isAjax()) {
if (sheep_config('shop.platform.WechatMiniProgram.status')) { // 如果开启了小程序平台
// 设置小程序发货信息管理,消息通知跳转地址,有缓存, 不会每次都设置
try {
$uploadshoppingInfo = new \addons\shopro\library\easywechatPlus\WechatMiniProgramShop(Wechat::miniProgram());
$uploadshoppingInfo->checkAndSetMessageJumpPath();
} catch (\Throwable $e) {
\think\Log::error('WechatMiniProgram checkAndSetMessageJumpPath error: ' . $e->getMessage());
}
}
$exportConfig = (new \addons\shopro\library\Export())->getConfig();
$this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download');
return $this->view->fetch();
}
$orders = $this->model->withTrashed()->sheepFilter()->with(['user', 'items', 'address', 'activity_orders'])
->paginate(request()->param('list_rows', 10))->each(function ($order) {
$order->pay_types = $order->pay_types;
$order->pay_types_text = $order->pay_types_text;
$order->items = collection($order->items);
$order->items->each(function ($item) use ($order) {
// 处理每个商品的 activity_order
$item->activity_orders = new \think\Collection;
foreach ($order->activity_orders as $activityOrder) {
if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) {
$item->activity_orders->push($activityOrder);
}
}
return $item;
});
})->toArray();
foreach ($orders['data'] as &$order) {
$order = $this->model->setOrderItemStatusByOrder($order);
}
$result = [
'orders' => $orders,
];
// 查询各个状态下的订单数量
$searchStatus = $this->model->searchStatusList();
// 所有的数量
$result['all'] = $this->model->withTrashed()->sheepFilter(true, function ($filters) {
unset($filters['status']);
return $filters;
})->count();
foreach ($searchStatus as $status => $text) {
$result[$status] = $this->model->withTrashed()->sheepFilter(true, function ($filters) use ($status) {
$filters['status'] = $status;
return $filters;
})->count();
}
$this->success('获取成功', null, $result);
}
// 获取数据类型
public function getType()
{
$type = $this->model->typeList();
$payType = (new PayModel)->payTypeList();
$platform = $this->model->platformList();
$classify = (new \app\admin\model\shopro\activity\Activity)->classifies();
$activityType = $classify['activity'];
$promoType = $classify['promo'];
$applyRefundStatus = $this->model->applyRefundStatusList();
$status = $this->model->searchStatusList();
$result = [
'type' => $type,
'pay_type' => $payType,
'platform' => $platform,
'activity_type' => $activityType,
'promo_types' => $promoType,
'apply_refund_status' => $applyRefundStatus,
'status' => $status
];
$data = [];
foreach ($result as $key => $list) {
$data[$key][] = ['name' => '全部', 'type' => 'all'];
foreach ($list as $k => $v) {
$data[$key][] = [
'name' => $v,
'type' => $k
];
}
}
$this->success('获取成功', null, $data);
}
/**
* 订单详情
*
* @param $id
* @return \think\Response
*/
public function detail($id)
{
if (!$this->request->isAjax()) {
$this->assignconfig('order_id', $id);
return $this->view->fetch();
}
// 更新包裹信息(5分钟缓存)
(new ExpressLib)->updateOrderExpress($id);
$order = $this->model->withTrashed()->with(['user', 'items', 'address', 'activity_orders', 'pays', 'invoice'])->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order->express = $order->express;
if ($order->invoice) {
$order->invoice->order_status = $order->invoice->order_status;
$order->invoice->order_status_text = $order->invoice->order_status_text;
$order->invoice->order_fee = $order->invoice->order_fee;
}
foreach ($order->activity_orders as $activityOrder) {
// 处理每个活动中参与的商品
$activityOrder->items = new \think\Collection();
foreach ($order->items as $item) {
if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) {
$activityOrder->items->push($item);
}
}
}
foreach ($order->items as $item) {
// 处理 order_item 建议退款金额
$item->suggest_refund_fee = $item->suggest_refund_fee;
}
// 处理未支付订单 item status_code(此方法会将 $order 转为数组)
$order = $order->setOrderItemStatusByOrder($order);
$partnerSplit = $this->buildPartnerSplit($order);
if (is_array($order)) {
$order['partner_split'] = $partnerSplit;
} else {
$order->partner_split = $partnerSplit;
}
$this->success('获取成功', null, $order);
}
/**
* 收益中心分成明细:即时奖列人;均分只汇总人数/人均
*/
protected function buildPartnerSplit($order)
{
try {
$orderId = intval(is_array($order) ? ($order['id'] ?? 0) : ($order->id ?? 0));
if ($orderId <= 0) {
return [
'items' => [],
'total_cash' => '0.00',
'total_voucher' => '0.00',
'settle_date' => null,
'has_data' => false,
];
}
$shareTypes = ['venue_share', 'county_share', 'madow_share'];
$items = [];
$totalCash = 0;
$totalVoucher = 0;
// 1. 查询即时收益流水
$rows = \app\admin\model\shopro\partner\Reward::with(['user'])
->where('order_id', $orderId)
->order('id', 'asc')
->select();
foreach ($rows as $row) {
$type = (string) ($row->reward_type ?? '');
if (in_array($type, $shareTypes, true)) {
continue;
}
$amount = round((float) ($row->amount ?? 0), 2);
$baseAmount = round((float) ($row->base_amount ?? 0), 2);
$madow = (float) ($row->madow ?? 0);
$status = intval($row->status ?? 0);
if ($status === 1 && $amount > 0) {
$totalCash = round($totalCash + $amount, 2);
}
if ($type === 'voucher_grant' && $status === 1 && $baseAmount > 0) {
$totalVoucher = round($totalVoucher + $baseAmount, 2);
}
// 友好展示金额
if ($type === 'voucher_grant') {
$amountText = number_format($baseAmount, 2, '.', '') . ' 个';
} elseif ($madow > 0) {
$amountText = \addons\shopro\service\partner\IdentityService::formatMadow($madow) . ' 马豆';
} elseif ($amount > 0) {
$amountText = '¥' . number_format($amount, 2, '.', '');
} else {
$amountText = '¥0.00';
}
$items[] = [
'display' => 'user',
'id' => intval($row->id),
'reward_type' => $type,
'reward_type_text' => (string) ($row->reward_type_text ?: \addons\shopro\service\partner\PartnerConfig::rewardTypeText($type)),
'role' => (string) ($row->role ?? ''),
'role_text' => (string) ($row->role_text ?? ''),
'user_id' => intval($row->user_id),
'user' => $row->user ? (is_object($row->user) ? $row->user->toArray() : $row->user) : null,
'amount' => number_format($amount, 2, '.', ''),
'base_amount' => number_format($baseAmount, 2, '.', ''),
'amount_text' => $amountText,
'madow' => \addons\shopro\service\partner\IdentityService::formatMadow($madow),
'status' => $status,
'status_text' => (string) ($row->status_text ?: (
$status === 1 ? '已入账' : ($status === 0 ? '待入账' : ($status === -1 ? '已取消' : '已退回'))
)),
'formula' => (string) ($row->formula ?? ''),
'skip_reason_text' => (string) ($row->skip_reason_text ?? ''),
'wallet_type' => (string) ($row->wallet_type ?? ($amount > 0 ? \addons\shopro\service\partner\PartnerConfig::rewardWalletType() : 'system')),
'wallet_type_text' => (string) ($row->wallet_type_text ?: ($amount > 0 ? (\addons\shopro\service\partner\PartnerConfig::rewardWalletType() === 'money' ? '商城余额' : '佣金钱包') : '平台留存')),
];
}
// 2. 兼容传统分销佣金记录(如存在)
try {
$commRows = \app\admin\model\shopro\commission\Reward::with(['agent.user'])
->where('order_id', $orderId)
->order('id', 'asc')
->select();
if ($commRows) {
foreach ($commRows as $crow) {
$cCommission = round((float) ($crow->commission ?? 0), 2);
$cStatus = intval($crow->status ?? 0);
if ($cStatus === 1 && $cCommission > 0) {
$totalCash = round($totalCash + $cCommission, 2);
}
$items[] = [
'display' => 'user',
'id' => intval($crow->id),
'reward_type' => 'commission',
'reward_type_text' => '分销佣金',
'role' => 'commission',
'role_text' => '分销商',
'user_id' => intval($crow->agent ? ($crow->agent->user_id ?? 0) : 0),
'user' => ($crow->agent && $crow->agent->user) ? (is_object($crow->agent->user) ? $crow->agent->user->toArray() : $crow->agent->user) : null,
'amount' => number_format($cCommission, 2, '.', ''),
'base_amount' => '0.00',
'amount_text' => '¥' . number_format($cCommission, 2, '.', ''),
'madow' => '0.00000000',
'wallet_type' => 'commission',
'wallet_type_text' => '佣金钱包',
'status' => $cStatus,
'status_text' => (string) ($crow->status_text ?: ($cStatus === 1 ? '已入账' : '待入账')),
'formula' => '分销规则佣金',
'skip_reason_text' => '',
];
}
}
} catch (\Throwable $ce) {
// 忽略非核心分销查询异常
}
// 3. 统计支付日日结均分奖池概况
$settleDate = null;
$paidRaw = is_array($order)
? ($order['paid_time'] ?? null)
: ($order->getData('paid_time') ?? ($order['paid_time'] ?? null));
if ($paidRaw) {
$paidTs = is_numeric($paidRaw) ? intval($paidRaw) : strtotime((string) $paidRaw);
if ($paidTs > 0) {
$settleDate = date('Y-m-d', $paidTs);
}
}
if ($settleDate) {
$dayStart = strtotime($settleDate . ' 00:00:00');
$dayEnd = strtotime($settleDate . ' 23:59:59');
foreach ($shareTypes as $type) {
$shareRows = \app\admin\model\shopro\partner\Reward::where(function ($q) use ($settleDate, $dayStart, $dayEnd) {
$q->where('settle_date', $settleDate)
->whereOr(function ($q2) use ($dayStart, $dayEnd) {
$q2->whereNull('settle_date')->whereBetween('createtime', [$dayStart, $dayEnd]);
});
})
->where('reward_type', $type)
->where('status', 1)
->where('user_id', '>', 0)
->select();
$distinctUsers = [];
$totalAmount = 0;
$totalMadow = 0;
foreach ($shareRows as $sr) {
$totalAmount = round($totalAmount + (float) ($sr->amount ?? 0), 2);
$totalMadow = function_exists('bcadd')
? (float) bcadd((string) $totalMadow, (string) ($sr->madow ?? 0), 8)
: ($totalMadow + (float) ($sr->madow ?? 0));
$distinctUsers[intval($sr->user_id)] = true;
}
$userCount = count($distinctUsers);
if ($userCount <= 0) {
// 分母为 0 进平台的记录
$platRows = \app\admin\model\shopro\partner\Reward::where(function ($q) use ($settleDate, $dayStart, $dayEnd) {
$q->where('settle_date', $settleDate)
->whereOr(function ($q2) use ($dayStart, $dayEnd) {
$q2->whereNull('settle_date')->whereBetween('createtime', [$dayStart, $dayEnd]);
});
})
->where('reward_type', $type)
->where('status', 1)
->where('user_id', 0)
->select();
if (empty($platRows) || count($platRows) <= 0) {
continue;
}
$platAmount = 0;
$platMadow = 0;
foreach ($platRows as $pr) {
$platAmount = round($platAmount + (float) ($pr->amount ?? 0), 2);
$platMadow = function_exists('bcadd')
? (float) bcadd((string) $platMadow, (string) ($pr->madow ?? 0), 8)
: ($platMadow + (float) ($pr->madow ?? 0));
}
$platform = $platRows[0];
$amountText = $type === 'madow_share'
? \addons\shopro\service\partner\IdentityService::formatMadow($platMadow) . ' 马豆'
: '¥' . number_format($platAmount, 2, '.', '');
$items[] = [
'display' => 'share',
'id' => intval($platform->id ?? 0),
'reward_type' => $type,
'reward_type_text' => \addons\shopro\service\partner\PartnerConfig::rewardTypeText($type),
'role' => 'system',
'role_text' => '平台',
'user_id' => 0,
'user' => null,
'user_count' => 0,
'each_amount' => '0.00',
'each_madow' => '0',
'total_amount' => number_format($platAmount, 2, '.', ''),
'total_madow' => \addons\shopro\service\partner\IdentityService::formatMadow($platMadow),
'amount_text' => $amountText,
'wallet_type' => 'system',
'wallet_type_text' => '平台留存',
'settle_date' => $settleDate,
'status' => 1,
'status_text' => '已入账',
'unit' => $type === 'madow_share' ? '马豆' : '元',
'formula' => (string) ($platform->formula ?? ''),
'skip_reason_text' => '',
'tip' => '当日无人参与,奖池进平台',
];
continue;
}
$first = $shareRows[0];
$eachAmount = function_exists('bcdiv') ? bcdiv((string) $totalAmount, (string) $userCount, 2) : number_format($totalAmount / $userCount, 2, '.', '');
$eachMadowVal = function_exists('bcdiv') ? bcdiv((string) $totalMadow, (string) $userCount, 8) : ($totalMadow / $userCount);
$eachMadow = \addons\shopro\service\partner\IdentityService::formatMadow($eachMadowVal);
$items[] = [
'display' => 'share',
'id' => intval($first->id ?? 0),
'reward_type' => $type,
'reward_type_text' => \addons\shopro\service\partner\PartnerConfig::rewardTypeText($type),
'role' => '',
'role_text' => '',
'user_id' => 0,
'user' => null,
'user_count' => $userCount,
'each_amount' => $eachAmount,
'each_madow' => $eachMadow,
'total_amount' => number_format($totalAmount, 2, '.', ''),
'total_madow' => \addons\shopro\service\partner\IdentityService::formatMadow($totalMadow),
'amount_text' => $type === 'madow_share' ? ($eachMadow . ' 马豆/人') : ('¥' . $eachAmount . '/人'),
'wallet_type' => $type === 'madow_share' ? 'madow' : \addons\shopro\service\partner\PartnerConfig::rewardWalletType(),
'wallet_type_text' => $type === 'madow_share' ? '分红马豆' : (\addons\shopro\service\partner\PartnerConfig::rewardWalletType() === 'money' ? '商城余额' : '佣金钱包'),
'settle_date' => $settleDate,
'status' => 1,
'status_text' => '已入账',
'unit' => $type === 'madow_share' ? '马豆' : '元',
'formula' => (string) ($first->formula ?? ''),
'skip_reason_text' => '',
'tip' => '日结均分(不列出参与用户名单)',
];
}
}
return [
'items' => $items,
'total_cash' => number_format($totalCash, 2, '.', ''),
'total_voucher' => number_format($totalVoucher, 2, '.', ''),
'settle_date' => $settleDate,
'has_data' => count($items) > 0,
];
} catch (\Throwable $e) {
\think\Log::error('buildPartnerSplit error: ' . $e->getMessage());
return [
'items' => [],
'total_cash' => '0.00',
'total_voucher' => '0.00',
'settle_date' => null,
'has_data' => false,
];
}
}
/**
* 批量发货渲染模板
*
* @return void
*/
public function batchDispatch()
{
return $this->view->fetch();
}
/**
* 批量发货渲染模板
*
* @return void
*/
public function dispatchList()
{
return $this->view->fetch();
}
/**
* 手动发货
*
* @return void
*/
public function customDispatch()
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$params = $this->request->only(['order_id', 'order_item_ids', 'custom_type', 'custom_content']);
$this->svalidate($params, '.custom_dispatch');
Db::transaction(function () use ($params) {
$service = new OrderDispatchService($params);
$service->customDispatch($params);
});
$this->success('发货成功');
}
/**
* 发货
*
* @description
* 支持分包裹发货
* 支持手动发货
* 支持上传发货单发货
* 支持推送api运单发货 默认使用配置项
* 支持修改发货信息
* 支持取消发货
*
* @remark 此处接口设计如此复杂是因为考虑到权限的问题,订单发货权限可以完成所有发货行为
*
* @param array $action 发货行为(默认:confirm=确认发货, cancel=取消发货, change=修改运单, multiple=解析批量发货单)
* @param int $order_id 订单id
* @param array $order_item_ids 订单商品id
* @param string $method 发货方式(input=手动发货, api=推送运单, upload=上传发货单)
* @param array $sender 发货人信息
* @param array $express 物流信息
*
* @return \think\Response
*/
public function dispatch()
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$params = $this->request->only(['action', 'order_id', 'order_ids', 'order_item_ids', 'method', 'sender', 'express', 'order_express_id']);
$action = $params['action'] ?? 'confirm';
if (!in_array($action, ['confirm', 'cancel', 'change', 'multiple'])) {
$this->error('发货参数错误');
}
$service = new OrderDispatchService($params);
switch ($action) {
case 'confirm':
$express = $service->confirm($params);
$this->success('发货成功', null, $express);
break;
case 'cancel':
$result = $service->cancel($params);
if ($result) {
$this->success('取消发货成功');
}
break;
case 'change':
$express = $service->change($params);
$this->success('修改成功', null, $express);
break;
case 'multiple':
$params['file'] = $this->request->file('file');
$result = $service->multiple($params);
$this->success('待发货列表', null, $result);
break;
}
$this->error('操作失败');
}
/**
* 获取物流快递信息
*/
public function updateExpress($order_express_id = 0)
{
$type = $this->request->param('type');
// 获取包裹
$orderExpress = OrderExpressModel::where('id', $order_express_id)->find();
if (!$orderExpress) {
$this->error('包裹不存在');
}
$orderAddress = OrderAddressModel::where('order_id', $orderExpress->order_id)->find();
if (!$orderAddress) {
$this->error('订单不存在');
}
$expressLib = new ExpressLib();
try {
if ($type == 'subscribe') {
// 重新订阅
$expressLib->subscribe([
'express_code' => $orderExpress['express_code'],
'express_no' => $orderExpress['express_no'],
'mobile' => $orderAddress['mobile']
]);
} else {
// 手动查询
$result = $expressLib->search([
'order_id' => $orderExpress['order_id'],
'express_code' => $orderExpress['express_code'],
'express_no' => $orderExpress['express_no'],
'mobile' => $orderAddress['mobile']
], $orderExpress);
}
} catch (HttpResponseException $e) {
$data = $e->getResponse()->getData();
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
$this->error($message);
} catch (\Exception $e) {
$this->error(($type == 'subscribe' ? '订阅失败:' : '刷新失败:') . $e->getMessage());
}
$this->success(($type == 'subscribe' ? '订阅成功' : '刷新成功'));
}
/**
* 线下付款,确认收货
*/
public function offlineConfirm($id)
{
$admin = $this->auth->getUserInfo();
$order = OrderModel::offline()->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order = Db::transaction(function () use ($order, $admin) {
// 加锁读订单
$order = $order->lock(true)->find($order->id);
$user = User::get($order->user_id);
$payOper = new PayOper($user);
$order = $payOper->offline($order, $order->remain_pay_fee, 'order'); // 订单会变为已支付 paid
// 触发订单支付完成事件
$data = ['order' => $order, 'user' => $user];
\think\Hook::listen('order_offline_paid_after', $data);
$orderOper = new OrderOper();
// 确认收货
$order = $orderOper->confirm($order, [], $admin, 'admin');
return $order;
});
$this->success('收货成功', $order);
}
/**
* 线下付款,拒收
*/
public function offlineRefuse($id)
{
$admin = $this->auth->getUserInfo();
$order = OrderModel::offline()->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order = Db::transaction(function () use ($order, $admin) {
// 加锁读订单
$order = $order->lock(true)->find($order->id);
// 拒收
$orderOper = new OrderOper();
$order = $orderOper->refuse($order, $admin, 'admin');
// 交易关闭
$order = $orderOper->close($order, $admin, 'admin', '用户拒绝收货,管理员关闭订单', ['closed_type' => 'refuse']);
return $order;
});
$this->success('收货成功', $order);
}
/**
* 订单改价,当剩余应支付金额为 0 时,订单将自动支付
*
* @param int $id
* @return void
*/
public function changeFee($id)
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$admin = $this->auth->getUserInfo();
$params = $this->request->param();
$this->svalidate($params, '.change_fee');
$order = $this->model->unpaid()->where('id', $id)->find();
if (!$order) {
$this->error('订单不可改价');
}
Db::transaction(function () use ($order, $admin, $params) {
$pay_fee = $params['pay_fee'];
$change_msg = $params['change_msg'];
$payOper = new PayOper($order->user_id);
$payed_fee = $payOper->getPayedFee($order, 'order');
if ($pay_fee < $payed_fee) {
$this->error('改价金额不能低于订单已支付金额');
}
// 记录原始值
$last_pay_fee = $order->pay_fee; // 上次pay_fee,非原始 pay_fee
$order->pay_fee = $pay_fee;
$order->save();
OrderActionModel::add($order, null, $admin, 'admin', "应支付金额由 ¥" . $last_pay_fee . " 改为 ¥" . $pay_fee . ",改价原因:" . $change_msg);
// 检查订单支付状态, 改价可以直接将订单变为已支付
$order = $payOper->checkAndPaid($order, 'order');
});
$this->success('改价成功');
}
/**
* 修改收货人信息
*
* @param Request $request
* @param integer $id
* @return void
*/
public function editConsignee($id)
{
$admin = $this->auth->getUserInfo();
$params = $this->request->param();
$this->svalidate($params, '.edit_consignee');
$order = $this->model->withTrashed()->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
Db::transaction(function () use ($admin, $order, $params) {
$orderAddress = OrderAddressModel::where('order_id', $order->id)->find();
if (!$orderAddress) {
$this->error(__('No Results were found'));
}
$orderAddress->consignee = $params['consignee'];
$orderAddress->mobile = $params['mobile'];
$orderAddress->province_name = $params['province_name'];
$orderAddress->city_name = $params['city_name'];
$orderAddress->district_name = $params['district_name'];
$orderAddress->address = $params['address'];
$orderAddress->province_id = $params['province_id'];
$orderAddress->city_id = $params['city_id'];
$orderAddress->district_id = $params['district_id'];
$orderAddress->save();
OrderActionModel::add($order, null, $admin, 'admin', "修改订单收货人信息");
});
$this->success('收货人信息修改成功');
}
/**
* 编辑商家备注
*
* @param Request $request
* @param integer $id
* @return void
*/
public function editMemo($id)
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$admin = $this->auth->getUserInfo();
$params = $this->request->param();
$this->svalidate($params, '.edit_memo');
$order = $this->model->withTrashed()->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
Db::transaction(function () use ($admin, $order, $params) {
$order->memo = $params['memo'];
$order->save();
OrderActionModel::add($order, null, $admin, 'admin', "修改卖家备注:" . $params['memo']);
});
$this->success('卖家备注修改成功');
}
/**
* 拒绝用户全额退款申请
*
* @param Request $request
* @param integer $id
* @return void
*/
public function applyRefundRefuse($id)
{
$admin = $this->auth->getUserInfo();
$params = $this->request->param();
// $this->svalidate($params, '.apply_refund_refuse');
$order = $this->model->withTrashed()->paid()->applyRefundIng()->where('id', $id)->find();
if (!$order) {
$this->error('订单未找到或不可拒绝申请');
}
Db::transaction(function () use ($admin, $order, $params) {
$order->apply_refund_status = OrderModel::APPLY_REFUND_STATUS_REFUSE;
$order->save();
OrderActionModel::add($order, null, $admin, 'admin', "拒绝用户申请全额退款");
});
$this->success('拒绝申请成功');
}
/**
* 全额退款 (必须没有进行过任何退款才能使用)
*
* @param Request $request
* @param integer $id
* @return void
*/
public function fullRefund($id)
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$admin = $this->auth->getUserInfo();
$admin = Admin::where('id', $admin['id'])->find();
$params = $this->request->param();
$refund_type = $params['refund_type'] ?? 'back';
Db::transaction(function () use ($admin, $id, $refund_type) {
$order = $this->model->paid()->where('id', $id)->lock(true)->find();
if (!$order) {
$this->error('订单不存在或不可退款');
}
$orderRefund = new OrderRefund($order);
$orderRefund->fullRefund($admin, [
'refund_type' => $refund_type,
'remark' => '平台主动全额退款'
]);
});
$this->success('全额退款成功');
}
/**
* 订单单商品退款
*
* @param Request $request
* @param integer $id
* @param integer $item_id
* @return void
*/
public function refund($id, $item_id)
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$admin = $this->auth->getUserInfo();
$params = $this->request->param();
$this->svalidate($params, '.refund');
$refund_money = round(floatval($params['refund_money']), 2);
$refund_type = $params['refund_type'] ?? 'back';
if ($refund_money < 0) {
$this->error('请输入正确的退款金额');
}
$order = $this->model->paid()->where('id', $id)->find();
if (!$order) {
$this->error('订单不存在或不可退款');
}
$item = OrderItem::where('order_id', $order->id)->where('id', $item_id)->find();
if (!$item) {
$this->error(__('No Results were found'));
}
if (in_array($item['refund_status'], [
OrderItem::REFUND_STATUS_AGREE,
OrderItem::REFUND_STATUS_COMPLETED,
])) {
$this->error('订单商品已退款,不能重复退款');
}
$payOper = new PayOper($order->user_id);
// 获取订单最大可退款金额(不含积分抵扣金额)
$remain_max_refund_money = $payOper->getRemainRefundMoney($order->id);
// 如果退款金额大于订单支付总金额
if (bccomp((string)$refund_money, $remain_max_refund_money, 2) === 1) {
$this->error('退款总金额不能大于实际支付金额');
}
Db::transaction(function () use ($admin, $order, $item, $refund_money, $refund_type) {
// 重新锁定读查询 orderItem
$item = OrderItem::where('order_id', $order->id)->lock(true)->where('id', $item->id)->find();
if (!$item) {
$this->error('订单不存在或不可退款');
}
$orderRefund = new OrderRefund($order);
$orderRefund->refund($item, $refund_money, $admin, [
'refund_type' => $refund_type,
'remark' => '平台主动退款'
]);
});
$this->success('退款成功');
}
/**
* 获取订单操作记录
*
* @param integer $id
* @return void
*/
public function action($id)
{
if (!$this->request->isAjax()) {
return $this->view->fetch();
}
$actions = OrderActionModel::where('order_id', $id)->order('id', 'desc')->select();
$morphs = [
'user' => \app\admin\model\shopro\user\User::class,
'admin' => \app\admin\model\Admin::class,
'system' => \app\admin\model\Admin::class,
];
$actions = morph_to($actions, $morphs, ['oper_type', 'oper_id']);
foreach ($actions as &$action) {
$action['oper'] = Operator::info($action['oper_type'], $action['oper'] ?? null);
}
$this->success('获取成功', null, $actions);
}
public function export()
{
$cellTitles = [
// 订单表字段
'order_id' => 'Id',
'order_sn' => '订单号',
'type_text' => '订单类型',
'user_nickname' => '下单用户',
'user_mobile' => '手机号',
'status_text' => '订单状态',
'pay_text' => '支付状态',
'pay_types_text' => '支付类型',
'remark' => '用户备注',
'memo' => '卖家备注',
'order_amount' => '订单总金额',
'score_amount' => '积分支付数量',
'dispatch_amount' => '运费',
'pay_fee' => '应付总金额',
'real_pay_fee' => '实付总金额',
'remain_pay_fee' => '剩余支付金额',
'total_discount_fee' => '总优惠金额',
'coupon_discount_fee' => '优惠券金额',
'promo_discount_fee' => '营销优惠金额',
'paid_time' => '支付完成时间',
'platform_text' => '交易平台',
'consignee_info' => '收货信息',
'createtime' => '下单时间',
// 订单商品表字段
'activity_type_text' => '活动',
'promo_types' => '促销',
'goods_title' => '商品名称',
'goods_sku_text' => '商品规格',
'goods_num' => '购买数量',
'goods_original_price' => '商品原价',
'goods_price' => '商品价格',
'goods_weight' => '商品重量',
'discount_fee' => '优惠金额',
'goods_pay_fee' => '商品支付金额',
'dispatch_type_text' => '发货方式',
'dispatch_status_text' => '发货状态',
'aftersale_refund' => '售后/退款',
'comment_status_text' => '评价状态',
'refund_fee' => '退款金额',
'refund_msg' => '退款原因',
'express_name' => '快递公司',
'express_no' => '快递单号',
];
// 数据总条数
$total = $this->model->withTrashed()->sheepFilter()->count();
if ($total <= 0) {
$this->error('导出数据为空');
}
$export = new \addons\shopro\library\Export();
$params = [
'file_name' => '订单列表',
'cell_titles' => $cellTitles,
'total' => $total,
'is_sub_cell' => true,
'sub_start_cell' => 'activity_type_text',
'sub_field' => 'items'
];
$total_order_amount = 0;
$total_pay_fee = 0;
$total_real_pay_fee = 0;
$total_discount_fee = 0;
$total_score_amount = 0;
$result = $export->export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, &$total_discount_fee, &$total_score_amount, $total) {
$datas = $this->model->withTrashed()->sheepFilter()->with(['user', 'items' => function ($query) {
$query->with(['express']);
}, 'address'])
->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows'])
->select();
$datas = collection($datas);
$datas = $datas->each(function ($order) {
$order->pay_types_text = $order->pay_types_text;
})->toArray();
$newDatas = [];
foreach ($datas as &$order) {
$order = $this->model->setOrderItemStatusByOrder($order);
// 收货人信息
$consignee_info = '';
if ($order['address']) {
$address = $order['address'];
$consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address'];
}
$data = [
'order_id' => $order['id'],
'order_sn' => $order['order_sn'],
'type_text' => $order['type_text'],
'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-',
'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-',
'status_text' => $order['status_text'],
'pay_text' => in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_COMPLETED]) ? '已支付' : '未支付',
'pay_types_text' => is_array($order['pay_types_text']) ? join(',', $order['pay_types_text']) : ($order['pay_types_text'] ?: ''),
'remark' => $order['remark'],
'memo' => $order['memo'],
'order_amount' => $order['order_amount'],
'score_amount' => $order['score_amount'],
'dispatch_amount' => $order['dispatch_amount'],
'pay_fee' => $order['pay_fee'],
'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2),
'remain_pay_fee' => $order['remain_pay_fee'],
'total_discount_fee' => $order['total_discount_fee'],
'coupon_discount_fee' => $order['coupon_discount_fee'],
'promo_discount_fee' => $order['promo_discount_fee'],
'paid_time' => $order['paid_time'],
'platform_text' => $order['platform_text'],
'consignee_info' => $consignee_info,
'createtime' => $order['createtime'],
];
$items = [];
foreach ($order['items'] as $item) {
$items[] = [
'activity_type_text' => $item['activity_type_text'],
'promo_types_text' => is_array($item['promo_types_text']) ? join(',', $item['promo_types_text']) : ($item['promo_types_text'] ?: '-'),
'goods_title' => $item['goods_title'],
'goods_sku_text' => $item['goods_sku_text'],
'goods_num' => $item['goods_num'],
'goods_original_price' => $item['goods_original_price'],
'goods_price' => $item['goods_price'],
'goods_weight' => $item['goods_weight'],
'discount_fee' => $item['discount_fee'],
'goods_pay_fee' => $item['pay_fee'],
'dispatch_type_text' => $item['dispatch_type_text'],
'dispatch_status_text' => $item['dispatch_status_text'],
'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'],
'comment_status_text' => $item['comment_status_text'],
'refund_fee' => $item['refund_fee'],
'refund_msg' => $item['refund_msg'],
'express_name' => $item['express'] ? $item['express']['express_name'] : '-',
'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '-',
];
}
$data['items'] = $items;
$newDatas[] = $data;
}
$total_order_amount += array_sum(array_column($newDatas, 'order_amount'));
$total_score_amount += array_sum(array_column($newDatas, 'score_amount'));
$total_pay_fee += array_sum(array_column($newDatas, 'pay_fee'));
$total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee'));
$total_discount_fee += array_sum(array_column($newDatas, 'discount_fee'));
if ($pages['is_last_page']) {
$newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";优惠总金额:¥" . $total_discount_fee . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee . ";支付总积分:" . $total_score_amount];
}
return $newDatas;
});
$this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result);
}
public function exportDelivery()
{
$cellTitles = [
// 订单表字段
'order_id' => 'Id',
'order_sn' => '订单号',
'type_text' => '订单类型',
'consignee_info' => '收货信息',
'remark' => '用户备注',
'memo' => '商家备注',
'createtime' => '下单时间',
// 订单商品表字段
'order_item_id' => '子订单Id',
'goods_title' => '商品名称',
'goods_sku_text' => '商品规格',
'goods_num' => '购买数量',
// 'dispatch_fee' => '发货费用',
'dispatch_type_text' => '发货方式',
'dispatch_status_text' => '发货状态',
'aftersale_refund' => '售后/退款',
'express_no' => '快递单号',
];
// 数据总条数
$total = $this->model->sheepFilter()->count(); // nosend 加了 noApplyRefund
if ($total <= 0) {
$this->error('导出数据为空');
}
$export = new \addons\shopro\library\Export();
$params = [
'file_name' => '订单发货单列表',
'cell_titles' => $cellTitles,
'total' => $total,
'is_sub_cell' => true,
'sub_start_cell' => 'order_item_id',
'sub_field' => 'items'
];
$result = $export->export($params, function ($pages) use (&$total) {
// 未申请全额退款的
$datas = $this->model->sheepFilter()->with(['user', 'items' => function ($query) { // nosend 加了 noApplyRefund
$query->with(['express']);
}, 'address'])
->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows'])
->select();
$datas = collection($datas)->toArray();
$newDatas = [];
foreach ($datas as &$order) {
$order = $this->model->setOrderItemStatusByOrder($order);
if (in_array($order['status_code'], ['groupon_ing', 'groupon_invalid'])) {
// 拼团正在进行中,不发货
$total--; // total 减少 1
continue;
}
// 收货人信息
$consignee_info = '';
if ($order['address']) {
$address = $order['address'];
$consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address'];
}
$data = [
'order_id' => $order['id'],
'order_sn' => $order['order_sn'],
'type_text' => $order['type_text'],
'consignee_info' => $consignee_info,
'remark' => $order['remark'],
'memo' => $order['memo'],
'createtime' => $order['createtime']
];
$items = [];
foreach ($order['items'] as $k => $item) {
// 未发货,并且未退款,并且未在申请售后中,并且是快递物流的
if (
$item['dispatch_status'] == OrderItem::DISPATCH_STATUS_NOSEND
&& !in_array($item['refund_status'], [OrderItem::REFUND_STATUS_AGREE, OrderItem::REFUND_STATUS_COMPLETED])
&& $item['aftersale_status'] != OrderItem::AFTERSALE_STATUS_ING
&& $item['dispatch_type'] == 'express'
) {
$items[] = [
'order_item_id' => $item['id'],
'goods_title' => strpos($item['goods_title'], '=') === 0 ? ' ' . $item['goods_title'] : $item['goods_title'],
'goods_sku_text' => $item['goods_sku_text'],
'goods_num' => $item['goods_num'],
// 'dispatch_fee' => $item['dispatch_fee'],
'dispatch_type_text' => $item['dispatch_type_text'],
'dispatch_status_text' => $item['dispatch_status_text'],
'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'],
'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '',
];
}
}
$data['items'] = $items;
$newDatas[] = $data;
};
if ($pages['is_last_page']) {
$newDatas[] = ['order_id' => "订单总数(仅快递物流的待发货订单):" . $total . ";备注:订单中同一包裹请填写相同运单号"];
}
return $newDatas;
});
$this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result);
}
}