shopxo/app/service/OrderService.php
2023-01-12 12:11:15 +08:00

2620 lines
93 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2099 http://shopxo.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/mit-license.php )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace app\service;
use think\facade\Db;
use app\service\PaymentService;
use app\service\BuyService;
use app\service\IntegralService;
use app\service\RegionService;
use app\service\ExpressService;
use app\service\ResourcesService;
use app\service\PayLogService;
use app\service\UserService;
use app\service\GoodsService;
use app\service\OrderAftersaleService;
use app\service\OrderCurrencyService;
use app\service\WarehouseService;
use app\service\SystemService;
/**
* 订单服务层
* @author Devil
* @blog http://gong.gg/
* @version 0.0.1
* @datetime 2016-12-01T21:51:08+0800
*/
class OrderService
{
// 业务类型名称
public static $business_type_name = '订单';
/**
* 订单支付
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-26
* @desc description
* @param [array] $params [输入参数]
*/
public static function Pay($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'ids',
'error_msg' => '订单id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 支付订单id
$ids = is_array($params['ids']) ? $params['ids'] : explode(',', urldecode($params['ids']));
if(empty($ids))
{
return DataReturn('订单支付id有误', -1);
}
// 支付基础信息
$order_payment_id = 0;
$client_type = '';
$order_ids = [];
$order_nos = [];
// 循环处理
$order_data = [];
foreach($ids as $k=>$order_id)
{
// 获取订单信息
$where = ['id'=>intval($order_id)];
$order = Db::name('Order')->where($where)->find();
if(empty($order))
{
return DataReturn('订单不存在或已被删除', -1);
}
$operate = self::OrderOperateData($order, 'user');
if($operate['is_pay'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
return DataReturn('状态不可操作['.$status_text.'-'.$order['order_no'].']', -1);
}
// 订单详情
$detail = self::OrderItemList([$order]);
$order['detail'] = (empty($detail) || !array_key_exists($order['id'], $detail)) ? [] : $detail[$order['id']];
// 订单用户
$order['user'] = UserService::UserHandle(UserService::UserInfo('id', $order['user_id']));
if(empty($order['user']))
{
return DataReturn('订单用户无效['.$order_id.']', -1);
}
// 订单数据集合
$order_data[] = $order;
$order_ids[] = $order['id'];
$order_nos[] = $order['order_no'];
// 仅第一个订单获取的数据
if($k == 0)
{
$client_type = $order['client_type'];
$order_payment_id = $order['payment_id'];
}
}
// 订单支付前校验订单商品
$ret = BuyService::MoreOrderPayBeginCheck(['order_data'=>$order_data]);
if($ret['code'] != 0)
{
return $ret;
}
// 金额为0、走直接支付成功
$total_price = 0;
$success_count = 0;
foreach($order_data as $order)
{
if($order['total_price'] <= 0.00)
{
$pay_result = self::OrderDirectSuccess([
'order' => $order,
'user' => $order['user'],
'params' => $params,
]);
if($pay_result['code'] == 0)
{
// 已支付成功订单结束当前循环
$success_count++;
continue;
}
return $pay_result;
}
// 数据集合
$total_price += $order['total_price'];
}
// 是否直接跳转
if($success_count > 0 && $success_count == count($order_data))
{
return DataReturn(MyLang('common.operate_success'), 0, ['is_success'=>1]);
}
// 订单金额大于0则必须存在支付方式
// 支付方式、未指定支付方式则获取第一个订单的支付方式
$payment = [];
$payment_id = empty($params['payment_id']) ? Db::name('Order')->where(['id'=>$ids[0]])->value('payment_id') : intval($params['payment_id']);
if(!empty($payment_id))
{
$payment = PaymentService::PaymentData(['where'=>['id'=>$payment_id]]);
}
if(empty($payment))
{
return DataReturn('支付方式有误', -1);
}
// 更新订单支付方式信息
if($payment['id'] != $order_payment_id)
{
Db::name('Order')->where(['id'=>$ids])->update([
'payment_id' => $payment['id'],
'is_under_line' => in_array($payment['payment'], MyConfig('shopxo.under_line_list')) ? 1 : 0,
'upd_time' => time(),
]);
}
// 支付入口文件检查
$pay_checked = PaymentService::EntranceFileChecked($payment['payment'], 'order');
if($pay_checked['code'] != 0)
{
// 入口文件不存在则创建
$payment_params = [
'payment' => $payment['payment'],
'respond' => '/index/order/respond',
'notify' => '/api/ordernotify/notify',
];
$ret = PaymentService::PaymentEntranceCreated($payment_params);
if($ret['code'] != 0)
{
return $ret;
}
}
// 回调地址
$respond_url = $pay_checked['data']['respond'];
$notify_url = $pay_checked['data']['notify'];
// 是否指定同步回调地址
if(!empty($params['redirect_url']))
{
$redirect_url = base64_decode(urldecode($params['redirect_url']));
if(!empty($redirect_url))
{
// 赋值同步返回地址
$respond_url = $redirect_url;
}
}
if(empty($redirect_url))
{
$redirect_url = MyUrl('index/order/index');
}
// 当前用户
$current_user = empty($params['user']) ? UserService::LoginUserInfo() : $params['user'];
if(!empty($current_user))
{
// 获取用户最新信息
$temp_user = UserService::UserHandle(UserService::UserInfo('id', $current_user['id']));
if(!empty($temp_user))
{
$current_user = $temp_user;
}
}
// 发起支付前处理钩子
$hook_name = 'plugins_service_order_pay_launch_begin';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'user' => $current_user,
'business_ids' => $order_ids,
'business_nos' => $order_nos,
'business_data' => $order_data,
'total_price' => $total_price,
'payment' => $payment['payment'],
'payment_name' => $payment['name'],
'client_type' => $client_type,
'params' => &$params,
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
return $ret;
}
// 新增支付日志
$pay_log = self::OrderPayLogInsert([
'user_id' => $current_user['id'],
'business_ids' => $order_ids,
'business_nos' => $order_nos,
'business_data' => $order_data,
'total_price' => $total_price,
'payment' => $payment['payment'],
'payment_name' => $payment['name'],
]);
if($pay_log['code'] != 0)
{
return $pay_log;
}
// 发起支付数据
$pay_data = [
'params' => $params,
'user' => $current_user,
'out_user' => md5($current_user['id']),
'business_type' => 'system-order',
'business_ids' => $order_ids,
'business_nos' => $order_nos,
'business_data' => $order_data,
'order_id' => $pay_log['data']['id'],
'order_no' => $pay_log['data']['log_no'],
'name' => '订单支付',
'total_price' => $total_price,
'client_type' => $client_type,
'notify_url' => $notify_url,
'call_back_url' => $respond_url,
'redirect_url' => $redirect_url,
'site_name' => MyC('home_site_name', 'ShopXO', true),
'check_url' => MyUrl('index/order/paycheck'),
];
// 发起支付处理钩子
$hook_name = 'plugins_service_order_pay_launch_handle';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'order_ids' => $order_ids,
'params' => &$params,
'pay_data' => &$pay_data,
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
return $ret;
}
// 微信中打开并且webopenid为空
if(APPLICATION_CLIENT_TYPE == 'pc' && IsWeixinEnv() && empty($pay_data['user']['weixin_web_openid']))
{
// 授权成功后回调订单详情页面重新自动发起支付
// 单个订单进入详情,则进入列表
$weixin_params = [
'is_pay_auto' => 1,
'is_pay_submit' => 1,
'payment_id' => $payment['id'],
];
if(count($order_ids) == 1)
{
$weixin_params['id'] = $order_ids[0];
$weixin_params['ids'] = $order_ids[0];
$url = MyUrl('index/order/detail', $weixin_params);
} else {
$weixin_params['ids'] = urldecode(implode(',', $order_ids));
$url = MyUrl('index/order/index', $weixin_params);
}
MySession('plugins_weixinwebauth_pay_callback_view_url', $url);
}
// 发起支付
$pay_name = 'payment\\'.$payment['payment'];
$ret = (new $pay_name($payment['config']))->Pay($pay_data);
if(isset($ret['code']) && $ret['code'] == 0)
{
// 支付信息返回
$ret['data'] = [
// 支付类型(0正常线上支付、1线下支付、2钱包支付)
'is_payment_type' => 0,
// 支付模块处理数据
'data' => $ret['data'],
// 支付日志id
'order_id' => $pay_log['data']['id'],
'order_no' => $pay_log['data']['log_no'],
// 支付方式信息
'payment' => [
'id' => $payment['id'],
'name' => $payment['name'],
'payment' => $payment['payment'],
],
];
// 是否线下支付
if(in_array($payment['payment'], MyConfig('shopxo.under_line_list')))
{
$ret['data']['is_payment_type'] = 1;
// 线下支付处理
// 0 订单状态操作支付成功
// -8888 订单提交成功,等待用户线下支付
// 其他错误
$pay_ret = self::UserOrderPayUnderLine($pay_log['data']['log_no']);
if($pay_ret['code'] == 0)
{
$ret['data']['is_success'] = 1;
} elseif($pay_ret['code'] == -8888)
{
$ret['msg'] = $pay_ret['msg'];
} else {
return $pay_ret;
}
} else {
// 是否钱包支付
if($payment['payment'] == 'WalletPay')
{
$ret['data']['is_payment_type'] = 2;
}
}
return $ret;
}
return DataReturn(
empty($ret['msg']) ? '支付接口异常' : $ret['msg'],
isset($ret['code']) ? $ret['code'] : -1,
isset($ret['data']) ? $ret['data'] : '');
}
/**
* 新增订单支付日志
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-28
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderPayLogInsert($params = [])
{
$business_ids = isset($params['business_ids']) ? $params['business_ids'] : [];
$business_nos = isset($params['business_nos']) ? $params['business_nos'] : [];
return PayLogService::PayLogInsert([
'user_id' => isset($params['user_id']) ? intval($params['user_id']) : 0,
'business_ids' => is_array($business_ids) ? $business_ids : [$business_ids],
'business_nos' => is_array($business_nos) ? $business_nos : [$business_nos],
'total_price' => isset($params['total_price']) ? PriceNumberFormat($params['total_price']) : 0.00,
'subject' => '订单支付',
'payment' => isset($params['payment']) ? $params['payment'] : '',
'payment_name' => isset($params['payment_name']) ? $params['payment_name'] : '',
'business_type' => self::$business_type_name,
]);
}
/**
* 线下订单支付
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-26
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderPaymentUnderLinePay($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '订单id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 获取订单信息
$where = ['id'=>intval($params['id'])];
$order = Db::name('Order')->where($where)->find();
if(empty($order))
{
return DataReturn('资源不存在或已被删除', -1);
}
$operate = self::OrderOperateData($order, 'admin');
if($operate['is_pay'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
return DataReturn('状态不可操作['.$status_text.'-'.$order['order_no'].']', -1);
}
// 订单支付前校验
$ret = BuyService::SingleOrderPayBeginCheck(['order_id'=>$order['id'], 'order_data'=>$order]);
if($ret['code'] != 0)
{
return $ret;
}
// 支付方式
$payment_id = empty($params['payment_id']) ? $order['payment_id'] : intval($params['payment_id']);
$payment = PaymentService::PaymentData(['where'=>['id'=>$payment_id]]);
if(empty($payment))
{
return DataReturn('支付方式有误', -1);
}
// 订单用户信息
$user = UserService::GetUserViewInfo($order['user_id']);
if(empty($user))
{
return DataReturn('订单用户无效', -1);
}
// 线下支付处理
return self::OrderPaymentUnderLineSuccess([
'order' => $order,
'payment' => $payment,
'user' => $user,
'params' => $params,
]);
}
/**
* 订单金额为小于等于0直接成功
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-04-07
* @desc description
* @param [array] $params [输入参数]
*/
private static function OrderDirectSuccess($params = [])
{
if(!empty($params['order']) && !empty($params['user']))
{
if($params['order']['total_price'] <= 0.00)
{
// 支付处理
$pay_params = [
'order' => [$params['order']],
'payment' => [],
'pay_log_data' => [],
'pay' => [
'trade_no' => '',
'subject' => isset($params['params']['subject']) ? $params['params']['subject'] : '订单支付',
'buyer_user' => $params['user']['user_name_view'],
'pay_price' => $params['order']['total_price'],
],
];
return self::OrderPayHandle($pay_params);
}
return DataReturn('订单金额有误、请正常发起支付', -1);
}
return DataReturn('支付传参有误', -1);
}
/**
* 线下支付方式、直接支付成功
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-04-07
* @desc description
* @param [array] $params [输入参数]
*/
private static function OrderPaymentUnderLineSuccess($params = [])
{
if(!empty($params['order']) && !empty($params['payment']) && !empty($params['user']))
{
if(in_array($params['payment']['payment'], MyConfig('shopxo.under_line_list')))
{
// 新增支付日志
$pay_log = self::OrderPayLogInsert([
'user_id' => $params['user']['id'],
'business_ids' => $params['order']['id'],
'business_nos' => $params['order']['order_no'],
'total_price' => $params['order']['total_price'],
'payment' => $params['payment']['payment'],
'payment_name' => $params['payment']['name'],
]);
if($pay_log['code'] != 0)
{
return $pay_log;
}
// 支付处理
$pay_params = [
'order' => [$params['order']],
'payment' => $params['payment'],
'pay_log_data' => $pay_log['data'],
'pay' => [
'trade_no' => '',
'subject' => isset($params['params']['subject']) ? $params['params']['subject'] : '订单支付',
'buyer_user' => $params['user']['user_name_view'],
'pay_price' => $params['order']['total_price'],
],
];
return self::OrderPayHandle($pay_params);
}
return DataReturn('仅线下支付方式处理', -1);
}
return DataReturn('支付传参有误', -1);
}
/**
* 支付同步处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-28
* @desc 一般仅web端回调这个页面
* @param [array] $params [输入参数]
*/
public static function Respond($params = [])
{
// 支付方式
$payment_name = defined('PAYMENT_TYPE') ? PAYMENT_TYPE : (isset($params['paymentname']) ? $params['paymentname'] : '');
if(empty($payment_name))
{
return DataReturn('支付方式标记异常', -1);
}
$payment = PaymentService::PaymentData(['where'=>['payment'=>$payment_name]]);
if(empty($payment))
{
return DataReturn('支付方式有误', -1);
}
// 支付数据校验
$pay_name = 'payment\\'.$payment_name;
$pay_ret = (new $pay_name($payment['config']))->Respond(array_merge(input('get.'), input('post.')));
if(isset($pay_ret['code']) && $pay_ret['code'] == 0)
{
if(empty($pay_ret['data']['out_trade_no']))
{
return DataReturn('单号有误', -1);
}
// 获取订单信息
$where = ['order_no'=>$pay_ret['data']['out_trade_no'], 'is_delete_time'=>0, 'user_is_delete_time'=>0];
$order = Db::name('Order')->where($where)->find();
// 线下支付方式
if(in_array($payment_name, MyConfig('shopxo.under_line_list')))
{
// 线下支付处理
// cpde=-8888 则表示需要用户线下支付,仅表示订单已提交成功
$ret = self::UserOrderPayUnderLine($pay_ret['data']['out_trade_no']);
if($ret['code'] == -8888)
{
$pay_ret['msg'] = $ret['msg'];
}
}
}
return $pay_ret;
}
/**
* 用户线下支付订单
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-08-13
* @desc description
* @param [string] $pay_log_no [订单支付日志单号]
*/
public static function UserOrderPayUnderLine($pay_log_no)
{
// 是否开启线下支付订单状态正常进行
if(MyC('common_is_under_line_order_normal') == 1)
{
// 支付订单数据
$pay_data = self::OrderPayLogValueList($pay_log_no);
if($pay_data['code'] != 0)
{
return $pay_data;
}
// 订单支付日志已支付则直接返回
if($pay_data['data']['pay_log_data']['status'] == 1)
{
return DataReturn(MyLang('common.operate_success'), 0);
}
// 启动事务
Db::startTrans();
// 捕获异常
try {
// 更新订单状态
$order_ids = array_column($pay_data['data']['order_list'], 'id');
$upd_data = [
'status' => 2,
'upd_time' => time(),
];
if(!Db::name('Order')->where(['id'=>$order_ids])->update($upd_data))
{
throw new \Exception('订单更新失败');
}
// 循环处理订单
foreach($pay_data['data']['order_list'] as $order)
{
if(!self::OrderHistoryAdd($order['id'], $upd_data['status'], $order['status'], '用户线下支付', 0, '系统'))
{
throw new \Exception('订单日志添加失败['.$order['id'].']');
}
}
// 更改日志订单状态
if(!Db::name('PayLog')->where(['log_no'=>$pay_log_no])->update(['status'=>1]))
{
throw new \Exception('日志订单更新失败');
}
// 完成
Db::commit();
return DataReturn('支付成功', 0);
} catch(\Exception $e) {
Db::rollback();
return DataReturn($e->getMessage(), -1);
}
}
return DataReturn('提交成功、请尽快联系管理员确认支付信息', -8888);
}
/**
* 支付异步
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-28
* @desc description
* @param [array] $params [输入参数]
*/
public static function Notify($params = [])
{
// 支付方式
$payment = PaymentService::PaymentData(['where'=>['payment'=>PAYMENT_TYPE]]);
if(empty($payment))
{
return DataReturn('支付方式有误', -1);
}
// 支付数据校验
$pay_name = 'payment\\'.PAYMENT_TYPE;
if(!class_exists($pay_name))
{
return DataReturn('支付方式不存在['.PAYMENT_TYPE.']', -1);
}
$payment_obj = new $pay_name($payment['config']);
// 是否存在处理方法
$method = method_exists($payment_obj, 'Notify') ? 'Notify' : 'Respond';
$pay_ret = $payment_obj->$method(array_merge(input('get.'), input('post.')));
if(!isset($pay_ret['code']) || $pay_ret['code'] != 0)
{
return $pay_ret;
}
// 支付结果处理
return self::NotifyHandle($pay_ret['data'], $payment);
}
/**
* 支付异步处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-28
* @desc description
* @param [array] $data [支付数据]
* @param [array] $payment [支付方式]
*/
public static function NotifyHandle($data, $payment)
{
// 支付订单数据
if(empty($data['out_trade_no']))
{
return DataReturn('订单号为空[out_trade_no]', -1);
}
$pay_data = self::OrderPayLogValueList($data['out_trade_no']);
if($pay_data['code'] == 0)
{
// 订单支付日志已支付则直接返回
if($pay_data['data']['pay_log_data']['status'] == 1)
{
return DataReturn('日志订单已支付、无需重复处理', 0);
}
} else {
return $pay_data;
}
// 支付金额是否小于订单金额
if(MyC('common_is_pay_price_must_max_equal', 0) == 1)
{
if($data['pay_price'] < $pay_data['data']['pay_log_data']['total_price'])
{
return DataReturn('支付金额小于日志订单金额['.$data['pay_price'].'<'.$pay_data['data']['pay_log_data']['total_price'].']', -1);
}
}
// 支付处理
$pay_params = [
'order' => $pay_data['data']['order_list'],
'payment' => $payment,
'pay_log_data' => $pay_data['data']['pay_log_data'],
'pay' => [
'trade_no' => $data['trade_no'],
'subject' => $data['subject'],
'buyer_user' => $data['buyer_user'],
'pay_price' => $data['pay_price'],
],
];
// 支付成功异步通知处理钩子
$hook_name = 'plugins_service_order_pay_notify_handle';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'payment' => $payment,
'order' => $pay_data['data']['order_list'],
'pay_params' => &$pay_params,
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
return $ret;
}
// 支付结果处理
return self::OrderPayHandle($pay_params);
}
/**
* 订单支付日志订单列表
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-08-13
* @desc description
* @param [string] $pay_log_no [支付日志订单号]
*/
public static function OrderPayLogValueList($pay_log_no)
{
// 获取支付日志订单
$pay_log_data = Db::name('PayLog')->where(['log_no'=>$pay_log_no])->find();
if(empty($pay_log_data))
{
return DataReturn('日志订单有误', -1);
}
// 获取关联信息
$pay_log_value = Db::name('PayLogValue')->where(['pay_log_id'=>$pay_log_data['id']])->column('business_id');
if(empty($pay_log_value))
{
return DataReturn('日志订单关联信息有误', -1);
}
// 获取订单
$order_list = Db::name('Order')->where(['id'=>$pay_log_value, 'status'=>1])->select()->toArray();
// 订单数据不存在、并且日志订单非支付状态则报错
if(empty($order_list) && $pay_log_data['status'] != 1)
{
return DataReturn('订单信息有误', -1);
}
return DataReturn('获取成功', 0, [
'pay_log_data' => $pay_log_data,
'order_list' => $order_list,
]);
}
/**
* 订单支付处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-28
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderPayHandle($params = [])
{
// 订单信息
if(empty($params['order']) || !is_array($params['order']))
{
return DataReturn('订单数据不存在或类型有误', -1);
}
// 订单金额大于0必须存在支付方式和订单支付日志
$order_total_price = array_sum(array_column($params['order'], 'total_price'));
if($order_total_price > 0)
{
// 支付方式
if(empty($params['payment']))
{
return DataReturn('支付方式有误', -1);
}
// 日志订单
if(empty($params['pay_log_data']))
{
return DataReturn('日志订单有误', -1);
}
}
// 查看支付日志是否已支付处理成功、避免异步通知太快导致重叠处理
if($order_total_price > 0 && !empty($params['pay_log_data']) && !empty($params['payment']))
{
$pay_log_status = Db::name('PayLog')->where(['id'=>$params['pay_log_data']['id']])->field('id,status')->value('status');
if($pay_log_status == 1)
{
return DataReturn('支付成功', 0);
}
}
// 开启事务
Db::startTrans();
// 循环处理
foreach($params['order'] as $order)
{
// 订单非待支付则不处理
if($order['pay_status'] != 0)
{
continue;
}
// 订单支付成功处理前钩子
$hook_name = 'plugins_service_order_pay_handle_begin';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => &$params,
'order_id' => $order['id']
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return $ret;
}
// 消息通知
$detail = '订单支付成功,金额'.PriceBeautify($order['total_price']).'元';
MessageService::MessageAdd($order['user_id'], '订单支付', $detail, self::$business_type_name, $order['id']);
// 订单更新数据
$upd_data = [
'pay_status' => 1,
'pay_price' => $order['total_price'],
'pay_time' => time(),
'upd_time' => time(),
];
// 避免先走订单、后走支付的逻辑
if($order['status'] <= 1)
{
$upd_data['status'] = 2;
}
// 订单金额大于0
if($order['total_price'] > 0 && !empty($params['payment']))
{
// 更新支付方式
$upd_data['payment_id'] = $params['payment']['id'];
// 是否线下支付
$upd_data['is_under_line'] = in_array($params['payment']['payment'], MyConfig('shopxo.under_line_list')) ? 1 : 0;
}
// 更新订单
if(!Db::name('Order')->where(['id'=>$order['id']])->update($upd_data))
{
// 事务回滚
Db::rollback();
return DataReturn('订单更新失败['.$order['id'].']', -10);
}
// 添加状态日志
if(array_key_exists('status', $upd_data))
{
if(!self::OrderHistoryAdd($order['id'], $upd_data['status'], $order['status'], '支付', 0, '系统'))
{
// 事务回滚
Db::rollback();
return DataReturn('订单日志添加失败['.$order['id'].']', -10);
}
}
// 库存扣除
$ret = BuyService::OrderInventoryDeduct(['order_id'=>$order['id'], 'opt_type'=>'pay']);
if($ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return $ret;
}
// 订单商品销量增加
$ret = self::GoodsSalesCountInc(['order_id'=>$order['id'], 'opt_type'=>'pay']);
if($ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return $ret;
}
// 订单支付成功处理完毕钩子
$hook_name = 'plugins_service_order_pay_success_handle_end';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => $params,
'order' => $order,
'order_id' => $order['id']
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return $ret;
}
// 虚拟商品自动触发发货操作
if($order['order_model'] == 3)
{
self::OrderDeliveryHandle([
'id' => $order['id'],
'creator' => 0,
'creator_name' => '系统',
'user_id' => $order['user_id'],
'user_type' => 'admin',
]);
}
}
// 更新支付日志
if($order_total_price > 0 && !empty($params['pay_log_data']) && !empty($params['payment']))
{
$pay_log_data = [
'log_id' => $params['pay_log_data']['id'],
'trade_no' => isset($params['pay']['trade_no']) ? $params['pay']['trade_no'] : '',
'buyer_user' => isset($params['pay']['buyer_user']) ? $params['pay']['buyer_user'] : '',
'pay_price' => isset($params['pay']['pay_price']) ? $params['pay']['pay_price'] : 0,
'subject' => isset($params['pay']['subject']) ? $params['pay']['subject'] : '订单支付',
'payment' => $params['payment']['payment'],
'payment_name' => $params['payment']['name'],
];
$ret = PayLogService::PayLogSuccess($pay_log_data);
if($ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return $ret;
}
}
// 提交事务
Db::commit();
return DataReturn('支付成功', 0);
}
/**
* 订单列表条件
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-29
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderListWhere($params = [])
{
// 用户类型
$user_type = isset($params['user_type']) ? $params['user_type'] : 'user';
// 条件初始化
$where = [
['is_delete_time', '=', 0],
];
// id
if(!empty($params['id']))
{
$where[] = ['id', '=', intval($params['id'])];
}
// 订单号
if(!empty($params['orderno']))
{
$where[] = ['order_no', '=', trim($params['orderno'])];
}
// 用户类型
if(isset($params['user_type']) && $params['user_type'] == 'user')
{
$where[] = ['user_is_delete_time', '=', 0];
// 用户id
if(!empty($params['user']))
{
$where[] = ['user_id', '=', $params['user']['id']];
}
}
// 关键字
if(!empty($params['keywords']))
{
// 查询状态
$keywords_status = false;
// 订单表查询
$oids = Db::name('Order')->where([['order_no|express_number', '=', $params['keywords']]])->column('id');
if(!empty($oids))
{
$where[] = ['id', 'in', $oids];
$keywords_status = true;
}
// 取货码查询
if($keywords_status === false && strlen(intval($params['keywords'])) == 4)
{
$oid = Db::name('OrderExtractionCode')->where(['code'=>$params['keywords']])->value('order_id');
if(!empty($oid))
{
$where[] = ['id', '=', $oid];
$keywords_status = true;
}
}
// 收件姓名电话查询
if($keywords_status === false)
{
$oids = Db::name('OrderAddress')->where([['name|tel', '=', $params['keywords']]])->column('order_id');
if(!empty($oids))
{
$where[] = ['id', 'in', $oids];
$keywords_status = true;
}
}
}
// 是否更多条件
if(isset($params['is_more']) && $params['is_more'] == 1)
{
// 等值
if(isset($params['payment_id']) && $params['payment_id'] > -1)
{
$where[] = ['payment_id', '=', intval($params['payment_id'])];
}
if(isset($params['express_id']) && $params['express_id'] > -1)
{
$where[] = ['express_id', '=', intval($params['express_id'])];
}
if(isset($params['pay_status']) && $params['pay_status'] > -1)
{
$where[] = ['pay_status', '=', intval($params['pay_status'])];
}
if(isset($params['order_model']) && $params['order_model'] > -1)
{
$where[] = ['order_model', '=', intval($params['order_model'])];
}
if(!empty($params['client_type']))
{
$where[] = ['client_type', '=', $params['client_type']];
}
if(isset($params['status']) && $params['status'] != -1)
{
// 多个状态,字符串以半角逗号分割
if(!is_array($params['status']))
{
$params['status'] = explode(',', $params['status']);
}
$where[] = ['status', 'in', $params['status']];
}
// 评价状态
if(isset($params['is_comments']) && $params['is_comments'] > -1)
{
$comments_field = ($user_type == 'user') ? 'user_is_comments' : 'is_comments';
if($params['is_comments'] == 0)
{
$where[] = [$comments_field, '=', 0];
} else {
$where[] = [$comments_field, '>', 0];
}
}
// 时间
if(!empty($params['time_start']))
{
$where[] = ['add_time', '>', strtotime($params['time_start'])];
}
if(!empty($params['time_end']))
{
$where[] = ['add_time', '<', strtotime($params['time_end'])];
}
// 价格
if(!empty($params['price_start']))
{
$where[] = ['price', '>', floatval($params['price_start'])];
}
if(!empty($params['price_end']))
{
$where[] = ['price', '<', floatval($params['price_end'])];
}
}
return $where;
}
/**
* 订单总数
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-29
* @desc description
* @param [array] $where [条件]
*/
public static function OrderTotal($where = [])
{
return (int) Db::name('Order')->where($where)->count();
}
/**
* 订单列表
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-29
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderList($params = [])
{
$where = empty($params['where']) ? [] : $params['where'];
$field = empty($params['field']) ? '*' : $params['field'];
$order_by = empty($params['order_by']) ? 'id desc' : $params['order_by'];
$m = isset($params['m']) ? intval($params['m']) : 0;
$n = isset($params['n']) ? intval($params['n']) : 10;
// 获取订单
$data = Db::name('Order')->where($where)->field($field)->limit($m, $n)->order($order_by)->select()->toArray();
// 数据处理
return self::OrderListHandle($data, $params);
}
/**
* 订单数据处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-05
* @desc description
* @param [array] $data [订单数据]
* @param [array] $params [输入参数]
*/
public static function OrderListHandle($data, $params = [])
{
$result = [];
if(!empty($data))
{
// 订单列表钩子-前面
$hook_name = 'plugins_service_order_list_handle_begin';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => &$params,
'data' => &$data,
]);
// 字段列表
$keys = ArrayKeys($data);
$order_ids = array_column($data, 'id');
// 其它额外处理
$is_operate = isset($params['is_operate']) ? intval($params['is_operate']) : 0;
$is_items = isset($params['is_items']) ? intval($params['is_items']) : 1;
$is_orderaftersale = isset($params['is_orderaftersale']) ? intval($params['is_orderaftersale']) : 0;
$user_type = isset($params['user_type']) ? $params['user_type'] : 'user';
// 静态数据
$order_status_list = MyConst('common_order_status');
$order_pay_status = MyConst('common_order_pay_status');
$common_platform_type = MyConst('common_platform_type');
$common_order_type_list = MyConst('common_order_type_list');
// 仓库信息
if(in_array('warehouse_id', $keys))
{
$we_ids = array_unique(array_column($data, 'warehouse_id'));
$warehouse_list = WarehouseService::WarehouseListHandle(Db::name('Warehouse')->where(['id'=>$we_ids])->field('id,name')->select()->toArray());
if(!empty($warehouse_list))
{
$warehouse_list = array_column($warehouse_list, null, 'id');
}
}
// 默认货币
$currency_default = ResourcesService::CurrencyData();
// 订单货币
$currency_data = OrderCurrencyService::OrderCurrencyGroupList($order_ids);
// 用户列表
if(in_array('user_id', $keys) && isset($params['is_public']) && $params['is_public'] == 0)
{
$user_list = UserService::GetUserViewInfo(array_column($data, 'user_id'));
}
// 快递信息
if(in_array('express_id', $keys))
{
$express_list = ExpressService::ExpressData(array_column($data, 'express_id'));
}
// 支付方式名称
$payment_list = PaymentService::OrderPaymentName($order_ids);
// 取货码
$extraction_data = self::OrderExtractionData($order_ids);
// 订单地址
$address_data = self::OrderAddressData($order_ids);
// 订单详情
$detail = ($is_items == 1) ? self::OrderItemList($data, $is_orderaftersale) : [];
// 循环处理数据
foreach($data as &$v)
{
// 订单处理前钩子
$hook_name = 'plugins_service_order_handle_begin';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => &$params,
'order' => &$v,
'order_id' => $v['id']
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
return $ret;
}
// 订单货币
$v['currency_data'] = (!empty($currency_data) && is_array($currency_data) && array_key_exists($v['id'], $currency_data)) ? $currency_data[$v['id']] : $currency_default;
// 订单所属仓库
if(isset($v['warehouse_id']))
{
if(!empty($warehouse_list) && is_array($warehouse_list) && array_key_exists($v['warehouse_id'], $warehouse_list))
{
$v['warehouse_name'] = $warehouse_list[$v['warehouse_id']]['name'];
$v['warehouse_icon'] = $warehouse_list[$v['warehouse_id']]['icon'];
$v['warehouse_url'] = $warehouse_list[$v['warehouse_id']]['url'];
} else {
$v['warehouse_name'] = '';
$v['warehouse_icon'] = '';
$v['warehouse_url'] = '';
}
}
// 订单模式处理
// 快递模式+自提模式
if(in_array($v['order_model'], [0,2]))
{
// 销售模式+自提模式 地址信息
$v['address_data'] = (!empty($address_data) && array_key_exists($v['id'], $address_data)) ? $address_data[$v['id']] : null;
// 自提模式 添加订单取货码
if($v['order_model'] == 2)
{
$v['extraction_data'] = (isset($v['status']) && !in_array($v['status'], [0,1,5,6]) && !empty($extraction_data) && array_key_exists($v['id'], $extraction_data)) ? $extraction_data[$v['id']] : null;
}
}
// 用户信息
if(isset($v['user_id']))
{
if(isset($params['is_public']) && $params['is_public'] == 0)
{
$v['user'] = (!empty($user_list) && is_array($user_list) && array_key_exists($v['user_id'], $user_list)) ? $user_list[$v['user_id']] : [];
}
}
// 订单模式
$v['order_model_name'] = isset($common_order_type_list[$v['order_model']]) ? $common_order_type_list[$v['order_model']]['name'] : '未知';
// 客户端
$v['client_type_name'] = isset($common_platform_type[$v['client_type']]) ? $common_platform_type[$v['client_type']]['name'] : '';
// 状态
$v['status_name'] = ($v['order_model'] == 2 && $v['status'] == 2) ? '待取货' : (array_key_exists($v['status'], $order_status_list) ? $order_status_list[$v['status']]['name'] : '未知');
// 支付状态
$v['pay_status_name'] = (in_array($v['status'], [2,3,4]) && $v['pay_status'] == 0) ? '待确认' : $order_pay_status[$v['pay_status']]['name'];
// 快递公司
$express = (!empty($express_list) && is_array($express_list) && array_key_exists($v['express_id'], $express_list)) ? $express_list[$v['express_id']] : null;
if(empty($express))
{
$v['express_name'] = '';
$v['express_icon'] = '';
$v['express_website_url'] = '';
} else {
$v['express_name'] = $express['name'];
$v['express_icon'] = $express['icon'];
$v['express_website_url'] = $express['website_url'];
}
// 支付方式
$v['payment_name'] = (!empty($payment_list) && is_array($payment_list) && array_key_exists($v['id'], $payment_list)) ? $payment_list[$v['id']] : null;
// 线下支付 icon 名称
$v['is_under_line_text'] = ($v['is_under_line'] == 1) ? '线下支付' : null;
// 是否可发起售后
$v['is_can_launch_aftersale'] = OrderAftersaleService::OrderIsCanLaunchAftersale($v['collect_time']);
// 创建时间
$v['add_time_time'] = date('Y-m-d H:i:s', $v['add_time']);
$v['add_time_date'] = date('Y-m-d', $v['add_time']);
$v['add_time'] = date('Y-m-d H:i:s', $v['add_time']);
// 更新时间
$v['upd_time'] = empty($v['upd_time']) ? null : date('Y-m-d H:i:s', $v['upd_time']);
// 确认时间
$v['confirm_time'] = empty($v['confirm_time']) ? null : date('Y-m-d H:i:s', $v['confirm_time']);
// 支付时间
$v['pay_time'] = empty($v['pay_time']) ? null : date('Y-m-d H:i:s', $v['pay_time']);
// 发货时间
$v['delivery_time'] = empty($v['delivery_time']) ? null : date('Y-m-d H:i:s', $v['delivery_time']);
// 收货时间
$v['collect_time'] = empty($v['collect_time']) ? null : date('Y-m-d H:i:s', $v['collect_time']);
// 取消时间
$v['cancel_time'] = empty($v['cancel_time']) ? null : date('Y-m-d H:i:s', $v['cancel_time']);
// 关闭时间
$v['close_time'] = empty($v['close_time']) ? null : date('Y-m-d H:i:s', $v['close_time']);
// 评论时间
$v['user_is_comments_time'] = ($v['user_is_comments'] == 0) ? null : date('Y-m-d H:i:s', $v['user_is_comments']);
// 空字段数据处理
if(empty($v['express_number']))
{
$v['express_number'] = null;
}
if(empty($v['user_note']))
{
$v['user_note'] = null;
}
// 扩展数据
$v['extension_data'] = empty($v['extension_data']) ? null : json_decode($v['extension_data'], true);
// 订单详情
if($is_items == 1 && !empty($detail) && array_key_exists($v['id'], $detail))
{
$v['items'] = $detail[$v['id']];
$v['items_count'] = count($v['items']);
$v['describe'] = '共'.$v['buy_number_count'].'件 合计:'.$v['currency_data']['currency_symbol'].$v['total_price'].'元';
}
// 管理员读取
if($user_type == 'admin')
{
// 获取最新一条售后订单
$v['aftersale_first'] = self::OrderAftersaleFirst($v['id']);
}
// 操作状态
if($is_operate == 1 && isset($v['status']) && isset($v['pay_status']))
{
$v['operate_data'] = self::OrderOperateData($v, $user_type);
}
// 订单处理后钩子
$hook_name = 'plugins_service_order_handle_end';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => &$params,
'order' => &$v,
'order_id' => $v['id']
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
return $ret;
}
}
// 订单列表钩子-后面
$hook_name = 'plugins_service_order_list_handle_end';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => &$params,
'data' => &$data,
]);
}
return DataReturn('success', 0, $data);
}
/**
* 订单操作状态处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-08-13
* @desc description
* @param [array] $data [订单数据]
* @param [string] $user_type [用户类型user 用户、admin 管理员)]
*/
public static function OrderOperateData($data, $user_type = 'user')
{
$result = [
// 确认
'is_confirm' => 0,
// 支付
'is_pay' => 0,
// 发货、取货
'is_delivery' => 0,
// 收货
'is_collect' => 0,
// 取消
'is_cancel' => 0,
// 删除
'is_delete' => 0,
// 评论
'is_comments' => 0,
];
if(isset($data['status']) && isset($data['pay_status']))
{
// 管理员
if($user_type == 'admin')
{
$result['is_confirm'] = ($data['status'] == 0) ? 1 : 0;
$result['is_pay'] = ($data['pay_status'] == 0 && !in_array($data['status'], [0,5,6])) ? 1 : 0;
$result['is_delivery'] = ($data['status'] == 2 && (isset($data['order_model']) && in_array($data['order_model'], [0,2,3]))) ? 1 : 0;
$result['is_collect'] = ($data['status'] == 3) ? 1 : 0;
$result['is_cancel'] = (in_array($data['status'], [0,1]) || (in_array($data['status'], [2,3,4]) && $data['pay_status'] == 0)) ? 1 : 0;
$result['is_delete'] = (in_array($data['status'], [5,6]) && isset($data['is_delete_time']) && $data['is_delete_time'] == 0) ? 1 : 0;
// 用户
} else {
$result['is_pay'] = ($data['status'] == 1) ? 1 : 0;
$result['is_collect'] = ($data['status'] == 3) ? 1 : 0;
$result['is_cancel'] = (in_array($data['status'], [0,1]) || $data['status'] == 2 && $data['pay_status'] == 0) ? 1 : 0;
$result['is_comments'] = ($data['status'] == 4 && isset($data['user_is_comments']) && $data['user_is_comments'] == 0) ? 1 : 0;
$result['is_delete'] = (in_array($data['status'], [4,5,6]) && isset($data['user_is_delete_time']) && $data['user_is_delete_time'] == 0) ? 1 : 0;
}
}
return $result;
}
/**
* 订单最新一条售后
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-05-15
* @desc description
* @param [int] $order_id [订单 id]
*/
public static function OrderAftersaleFirst($order_id)
{
$data = Db::name('OrderAftersale')->where(['order_id'=>$order_id])->field('status,type,number,price,reason,msg')->order('id desc')->find();
if(!empty($data))
{
$type_list = MyConst('common_order_aftersale_type_list');
$status_list = MyConst('common_order_aftersale_status_list');
// 类型
$data['type_text'] = array_key_exists($data['type'], $type_list) ? $type_list[$data['type']]['name'] : '';
// 状态
$data['status_text'] = array_key_exists($data['status'], $status_list) ? $status_list[$data['status']]['name'] : '';
}
return $data;
}
/**
* 订单详情列表
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-06-22
* @desc description
* @param [array] $order [订单信息]
* @param [int] $is_orderaftersale [是否读取订单售后0否, 1是]
*/
public static function OrderItemList($order, $is_orderaftersale = 0)
{
$result = [];
$order = array_column($order, null, 'id');
$order_ids = array_keys($order);
$data = Db::name('OrderDetail')->where(['order_id'=>$order_ids])->select()->toArray();
if(!empty($data))
{
// 订单详情自增id
$order_detail_ids = array_column($data, 'id');
// 虚拟商品取货码
$fictitious_value_list = Db::name('OrderFictitiousValue')->where(['order_detail_id'=>$order_detail_ids])->column('value', 'order_detail_id');
// 是否获取最新一条售后信息
$orderaftersale = [];
if($is_orderaftersale == 1)
{
$temp_aftersale = Db::name('OrderAftersale')->where(['order_detail_id'=>$order_detail_ids])->order('id desc')->select()->toArray();
if(!empty($temp_aftersale))
{
foreach($temp_aftersale as $av)
{
if(!array_key_exists($av['order_detail_id'], $orderaftersale))
{
$orderaftersale[$av['order_detail_id']] = $av;
}
}
}
}
// 商品处理
$res = GoodsService::GoodsDataHandle($data, ['data_key_field'=>'goods_id']);
$data = $res['data'];
foreach($data as $vs)
{
// 当前商品订单信息
if(array_key_exists($vs['order_id'], $order))
{
// 当前商品详情主订单信息
$ov = $order[$vs['order_id']];
// 避免订单商品价格被处理,强制使用原始内容
if(!empty($vs['price_container']))
{
$vs['price'] = $vs['price_container']['price'];
$vs['original_price'] = $vs['price_container']['original_price'];
}
// 规格
$vs['spec_text'] = null;
if(!empty($vs['spec']))
{
$vs['spec'] = json_decode($vs['spec'], true);
if(!empty($vs['spec']) && is_array($vs['spec']))
{
$vs['spec_text'] = implode('', array_map(function($spec)
{
return $spec['type'].':'.$spec['value'];
}, $vs['spec']));
}
} else {
$vs['spec'] = null;
}
// 虚拟销售商品 - 虚拟信息处理
if($ov['order_model'] == 3 && $ov['pay_status'] == 1 && in_array($ov['status'], [3,4]))
{
$vs['fictitious_goods_value'] = (!empty($fictitious_value_list) && is_array($fictitious_value_list) && array_key_exists($vs['id'], $fictitious_value_list)) ? $fictitious_value_list[$vs['id']] : '';
}
// 是否获取最新一条售后信息
if($is_orderaftersale == 1)
{
$vs['orderaftersale'] = (!empty($orderaftersale) && array_key_exists($vs['id'], $orderaftersale)) ? $orderaftersale[$vs['id']] : null;
$vs['orderaftersale_btn_text'] = self::OrderAftersaleStatusBtnText($ov['status'], $vs['orderaftersale']);
}
// 加入分组
if(!array_key_exists($vs['order_id'], $result))
{
$result[$vs['order_id']] = [];
}
$result[$vs['order_id']][] = $vs;
}
}
}
return $result;
}
/**
* 订单自提信息
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2019-11-26
* @desc description
* @param [array] $order_ids [订单id]
*/
private static function OrderExtractionData($order_ids)
{
// 必须返回的内容格式
$result = [];
// 获取取货码
$data = Db::name('OrderExtractionCode')->where(['order_id'=>array_unique($order_ids)])->column('code', 'order_id');
if(!empty($data) && is_array($data))
{
foreach($order_ids as $v)
{
$images = null;
if(array_key_exists($v, $data))
{
// 生成二维码参数
$params = [
'content' => $data[$v],
'path' => DS.'download'.DS.'order'.DS.'extraction_code'.DS,
'filename' => $v.'.png',
];
// 图片不存在则去生成二维码图片并保存至目录
$ret = (new \base\Qrcode())->Create($params);
$images = ($ret['code'] == 0) ? $ret['data']['url'] : null;
}
$result[$v] = [
'code' => isset($data[$v]) ? $data[$v] : null,
'images' => $images,
];
}
}
// 订单自提信息钩子
$hook_name = 'plugins_service_order_extraction_data';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'order_ids' => $order_ids,
'data' => &$result,
]);
return $result;
}
/**
* 订单地址
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2019-11-26
* @desc description
* @param [array] $order_ids [订单id]
*/
private static function OrderAddressData($order_ids)
{
// 销售模式+自提模式 地址信息
$data = Db::name('OrderAddress')->where(['order_id'=>$order_ids])->column('*', 'order_id');
if(!empty($data) && is_array($data))
{
foreach($data as &$v)
{
// 附件
$v['idcard_front_old'] = $v['idcard_front'];
$v['idcard_front'] = ResourcesService::AttachmentPathViewHandle($v['idcard_front']);
$v['idcard_back_old'] = $v['idcard_back'];
$v['idcard_back'] = ResourcesService::AttachmentPathViewHandle($v['idcard_back']);
}
}
// 订单地址信息钩子
$hook_name = 'plugins_service_order_address_data';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'order_ids' => $order_ids,
'data' => &$data,
]);
return empty($data) ? [] : $data;
}
/**
* 订单售后操作名称
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @datetime 2019-10-04T13:11:55+0800
* @desc description
* @param [int] $order_status [订单状态]
* @param [array] $orderaftersale [售后数据]
*/
private static function OrderAftersaleStatusBtnText($order_status, $orderaftersale)
{
$text = null;
if(!in_array($order_status, [0,1,5,6]))
{
if(empty($orderaftersale))
{
$text = '退款/退货';
if($order_status == 4)
{
$text = '申请售后';
}
} else {
$text = ($orderaftersale['status'] == 3) ? '查看退款' : '查看进度';
}
}
return $text;
}
/**
* 订单日志添加
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [int] $order_id [订单id]
* @param [int] $new_status [更新后的状态]
* @param [int] $original_status [原始状态]
* @param [string] $msg [描述]
* @param [int] $creator [操作人]
* @param [string] $creator_name [操作人名称]
* @return [boolean] [成功 true, 失败 false]
*/
public static function OrderHistoryAdd($order_id, $new_status, $original_status, $msg = '', $creator = 0, $creator_name = '')
{
// 状态描述
$order_status_list = MyConst('common_order_status');
$original_status_name = $order_status_list[$original_status]['name'];
$new_status_name = $order_status_list[$new_status]['name'];
$msg .= '['.$original_status_name.'-'.$new_status_name.']';
// 添加
$data = [
'order_id' => intval($order_id),
'new_status' => intval($new_status),
'original_status' => intval($original_status),
'msg' => htmlentities($msg),
'creator' => intval($creator),
'creator_name' => htmlentities($creator_name),
'add_time' => time(),
];
// 日志添加
if(Db::name('OrderStatusHistory')->insertGetId($data) > 0)
{
// 订单状态改变添加日志钩子
$hook_name = 'plugins_service_order_status_change_history_success_handle';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'data' => $data,
'order_id' => $data['order_id']
]);
return true;
}
return false;
}
/**
* 订单取消
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderCancel($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '订单id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user_id',
'error_msg' => '用户id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 用户类型
$user_type = empty($params['user_type']) ? 'user' : $params['user_type'];
// 获取订单信息
$where = ['id'=>intval($params['id']), 'user_id'=>$params['user_id'], 'is_delete_time'=>0, 'user_is_delete_time'=>0];
$order = Db::name('Order')->where($where)->field('id,status,pay_status,user_id,order_model')->find();
if(empty($order))
{
return DataReturn('资源不存在或已被删除', -1);
}
// 有效订单情况下、如果未支付可以正常进行取消操作
$operate = self::OrderOperateData($order, $user_type);
if($operate['is_cancel'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
return DataReturn('状态不可操作['.$status_text.']', -1);
}
// 开启事务
Db::startTrans();
$upd_data = [
'status' => 5,
'cancel_time' => time(),
'upd_time' => time(),
];
if(Db::name('Order')->where($where)->update($upd_data))
{
// 库存回滚
$ret = BuyService::OrderInventoryRollback(['order_id'=>$order['id'], 'order_data'=>$upd_data]);
if($ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return DataReturn($ret['msg'], -10);
}
// 用户消息
MessageService::MessageAdd($order['user_id'], '订单取消', '订单取消成功', self::$business_type_name, $order['id']);
// 订单状态日志
$creator = isset($params['creator']) ? intval($params['creator']) : 0;
$creator_name = isset($params['creator_name']) ? htmlentities($params['creator_name']) : '';
self::OrderHistoryAdd($order['id'], $upd_data['status'], $order['status'], '取消', $creator, $creator_name);
// 提交事务
Db::commit();
return DataReturn(MyLang('common.cancel_success'), 0);
}
// 事务回滚
Db::rollback();
return DataReturn(MyLang('common.cancel_fail'), -1);
}
/**
* 订单发货
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderDelivery($params = [])
{
// 订单发货处理
Db::startTrans();
$ret = self::OrderDeliveryHandle($params);
if($ret['code'] == 0)
{
Db::commit();
} else {
Db::rollback();
}
return $ret;
}
/**
* 订单发货处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderDeliveryHandle($params = [])
{
// 捕获异常
try {
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '订单id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user_id',
'error_msg' => '用户id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
throw new \Exception($ret['msg']);
}
// 用户类型
$user_type = empty($params['user_type']) ? 'user' : $params['user_type'];
// 获取订单信息
$where = ['id'=>intval($params['id']), 'user_id'=>$params['user_id'], 'is_delete_time'=>0, 'user_is_delete_time'=>0];
$order = Db::name('Order')->where($where)->field('id,status,pay_status,user_id,order_model')->find();
if(empty($order))
{
throw new \Exception('资源不存在或已被删除');
}
$operate = self::OrderOperateData($order, $user_type);
if($operate['is_delivery'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
throw new \Exception('状态不可操作['.$status_text.']');
}
// 订单模式
switch($order['order_model'])
{
// 销售模式- 订单快递信息校验
case 0 :
$p = [
[
'checked_type' => 'empty',
'key_name' => 'express_id',
'error_msg' => '快递id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'express_number',
'error_msg' => '快递单号有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
throw new \Exception($ret['msg']);
}
break;
// 自提模式 - 验证取货码
case 2 :
$p = [
[
'checked_type' => 'empty',
'key_name' => 'extraction_code',
'error_msg' => '取货码有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
throw new \Exception($ret['msg']);
}
// 校验
$extraction_code = Db::name('OrderExtractionCode')->where(['order_id'=>$order['id']])->value('code');
if(empty($extraction_code))
{
throw new \Exception('订单取货码不存在、请联系管理员');
}
if($extraction_code != $params['extraction_code'])
{
throw new \Exception('取货码不正确');
}
break;
}
// 订单更新
$upd_data = [
'status' => 3,
'express_id' => isset($params['express_id']) ? intval($params['express_id']) : 0,
'express_number' => isset($params['express_number']) ? $params['express_number'] : '',
'delivery_time' => time(),
'upd_time' => time(),
];
if(!Db::name('Order')->where($where)->update($upd_data))
{
throw new \Exception('发货失败');
}
// 库存扣除
$ret = BuyService::OrderInventoryDeduct(['order_id'=>$order['id'], 'opt_type'=>'delivery']);
if($ret['code'] != 0)
{
throw new \Exception($ret['msg']);
}
// 用户消息
MessageService::MessageAdd($order['user_id'], '订单发货', '订单已发货', self::$business_type_name, $order['id']);
// 订单状态日志
$creator = isset($params['creator']) ? intval($params['creator']) : 0;
$creator_name = isset($params['creator_name']) ? htmlentities($params['creator_name']) : '';
self::OrderHistoryAdd($order['id'], $upd_data['status'], $order['status'], '收货', $creator, $creator_name);
// 完成
return DataReturn('发货成功', 0);
} catch(\Exception $e) {
return DataReturn($e->getMessage(), -1);
}
}
/**
* 订单收货
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderCollect($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '订单id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user_id',
'error_msg' => '用户id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 用户类型
$user_type = empty($params['user_type']) ? 'user' : $params['user_type'];
// 获取订单信息
$where = ['id'=>intval($params['id']), 'user_id'=>$params['user_id'], 'is_delete_time'=>0, 'user_is_delete_time'=>0];
$order = Db::name('Order')->where($where)->field('id,status,pay_status,user_id,order_model')->find();
if(empty($order))
{
return DataReturn('资源不存在或已被删除', -1);
}
$operate = self::OrderOperateData($order, $user_type);
if($operate['is_collect'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
return DataReturn('状态不可操作['.$status_text.']', -1);
}
// 开启事务
Db::startTrans();
// 收货处理
$ret = self::OrderCollectHandle($order, $params);
if($ret['code'] == 0)
{
Db::commit();
} else {
Db::rollback();
}
return $ret;
}
/**
* 订单收货完成处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-08-26
* @desc description
* @param [array] $order [订单数据]
* @param [array] $params [输入参数]
*/
public static function OrderCollectHandle($order, $params = [])
{
// 更新订单状态
$upd_data = [
'status' => 4,
'collect_time' => time(),
'upd_time' => time(),
];
if(Db::name('Order')->where(['id'=>$order['id']])->update($upd_data))
{
// 订单商品积分赠送
$ret = IntegralService::OrderGoodsIntegralGiving(['order_id'=>$order['id']]);
if($ret['code'] != 0)
{
return $ret;
}
// 订单商品销量增加
$ret = self::GoodsSalesCountInc(['order_id'=>$order['id'], 'opt_type'=>'collect']);
if($ret['code'] != 0)
{
return $ret;
}
// 用户消息
MessageService::MessageAdd($order['user_id'], '订单收货', '订单收货成功', self::$business_type_name, $order['id']);
// 订单状态日志
$creator = isset($params['creator']) ? intval($params['creator']) : 0;
$creator_name = isset($params['creator_name']) ? htmlentities($params['creator_name']) : '';
self::OrderHistoryAdd($order['id'], $upd_data['status'], $order['status'], '收货', $creator, $creator_name);
return DataReturn('收货成功', 0);
}
return DataReturn('收货失败', -1);
}
/**
* 订单确认
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderConfirm($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '订单id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user_id',
'error_msg' => '用户id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 用户类型
$user_type = empty($params['user_type']) ? 'user' : $params['user_type'];
// 获取订单信息
$where = ['id'=>intval($params['id']), 'user_id'=>$params['user_id'], 'is_delete_time'=>0, 'user_is_delete_time'=>0];
$order = Db::name('Order')->where($where)->field('id,status,pay_status,user_id,order_model')->find();
if(empty($order))
{
return DataReturn('资源不存在或已被删除', -1);
}
$operate = self::OrderOperateData($order, $user_type);
if($operate['is_confirm'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
return DataReturn('状态不可操作['.$status_text.']', -1);
}
// 开启事务
Db::startTrans();
// 更新订单状态
$upd_data = [
'status' => 1,
'confirm_time' => time(),
'upd_time' => time(),
];
if(Db::name('Order')->where($where)->update($upd_data))
{
// 库存扣除
$ret = BuyService::OrderInventoryDeduct(['order_id'=>$params['id'], 'opt_type'=>'confirm']);
if($ret['code'] != 0)
{
// 事务回滚
Db::rollback();
return DataReturn($ret['msg'], -10);
}
// 用户消息
MessageService::MessageAdd($order['user_id'], '订单确认', '订单确认成功', self::$business_type_name, $order['id']);
// 订单状态日志
$creator = isset($params['creator']) ? intval($params['creator']) : 0;
$creator_name = isset($params['creator_name']) ? htmlentities($params['creator_name']) : '';
self::OrderHistoryAdd($order['id'], $upd_data['status'], $order['status'], '确认', $creator, $creator_name);
// 事务提交
Db::commit();
return DataReturn('确认成功', 0);
}
// 事务回滚
Db::rollback();
return DataReturn('确认失败', -1);
}
/**
* 订单删除
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-30
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderDelete($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '订单id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user_id',
'error_msg' => '用户id有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user_type',
'error_msg' => '用户类型有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 用户类型
$user_type = empty($params['user_type']) ? 'user' : $params['user_type'];
switch($user_type)
{
case 'admin' :
$delete_field = 'is_delete_time';
break;
case 'user' :
$delete_field = 'user_is_delete_time';
break;
default :
$delete_field = empty($params['delete_field']) ? '' : $params['delete_field'];
}
if(empty($delete_field))
{
return DataReturn('用户类型有误['.$user_type.']', -2);
}
// 获取订单信息
$where = ['id'=>intval($params['id']), 'user_id'=>$params['user_id'], $delete_field=>0];
$order = Db::name('Order')->where($where)->field('id,status,pay_status,user_id,order_model,is_delete_time,user_is_delete_time')->find();
if(empty($order))
{
return DataReturn('资源不存在或已被删除', -1);
}
$operate = self::OrderOperateData($order, $user_type);
if($operate['is_delete'] != 1)
{
$status_text = MyConst('common_order_status')[$order['status']]['name'];
return DataReturn('状态不可操作['.$status_text.']', -1);
}
// 启动事务
Db::startTrans();
// 捕获异常
try {
// 删除操作
$data = [
$delete_field => time(),
'upd_time' => time(),
];
if(!Db::name('Order')->where($where)->update($data))
{
throw new \Exception(MyLang('common.delete_fail'));
}
// 用户消息
MessageService::MessageAdd($order['user_id'], '订单删除', '订单删除成功', self::$business_type_name, $order['id']);
// 订单删除成功钩子
$hook_name = 'plugins_service_order_delete_success';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'order_id' => $params['id'],
'params' => $params,
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
throw new \Exception($ret['msg']);
}
// 完成
Db::commit();
return DataReturn(MyLang('common.delete_success'), 0);
} catch(\Exception $e) {
Db::rollback();
return DataReturn($e->getMessage(), -1);
}
}
/**
* 订单每个环节状态总数
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-10-10
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderStatusStepTotal($params = [])
{
// 状态数据封装
$result = [];
$order_status_list = MyConst('common_order_status');
foreach($order_status_list as $v)
{
$result[] = [
'name' => $v['name'],
'status' => $v['id'],
'count' => 0,
];
}
// 用户类型
$user_type = isset($params['user_type']) ? $params['user_type'] : '';
// 条件
$where = [];
$where['is_delete_time'] = 0;
// 用户类型
switch($user_type)
{
case 'user' :
$where['user_is_delete_time'] = 0;
break;
}
// 用户条件
if($user_type == 'user')
{
if(!empty($params['user']))
{
$where['user_id'] = $params['user']['id'];
} else {
return DataReturn('用户信息有误', 0, $result);
}
}
$field = 'COUNT(DISTINCT id) AS count, status';
$data = Db::name('Order')->where($where)->field($field)->group('status')->select()->toArray();
// 数据处理
if(!empty($data))
{
foreach($result as &$v)
{
foreach($data as $vs)
{
if($v['status'] == $vs['status'])
{
$v['count'] = $vs['count'];
continue;
}
}
}
}
// 待评价 状态站位100
if(isset($params['is_comments']) && $params['is_comments'] == 1)
{
switch($user_type)
{
case 'user' :
$where['user_is_comments'] = 0;
break;
case 'admin' :
$where['is_comments'] = 0;
break;
default :
$where['user_is_comments'] = 0;
$where['is_comments'] = 0;
}
$where['status'] = 4;
$result[] = [
'name' => '待评价',
'status' => 100,
'count' => (int) Db::name('Order')->where($where)->count(),
];
}
// 退款/售后 状态站位101
if(isset($params['is_aftersale']) && $params['is_aftersale'] == 1)
{
$where = [
['status', '<=', 2],
];
if($user_type == 'user' && !empty($params['user']))
{
$where[] = ['user_id', '=', $params['user']['id']];
}
$result[] = [
'name' => '退款/售后',
'status' => 101,
'count' => (int) Db::name('OrderAftersale')->where($where)->count(),
];
}
return DataReturn('success', 0, $result);
}
/**
* 订单商品销量添加
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-11-14
* @desc description
* @param [array] $params [输入参数]
*/
public static function GoodsSalesCountInc($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'order_id',
'error_msg' => '订单id有误',
],
[
'checked_type' => 'in',
'key_name' => 'opt_type',
'checked_data' => ['pay', 'collect'],
'error_msg' => '订单操作类型有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 增加销量规则、默认订单收货
$status = false;
if(MyC('common_goods_sales_count_inc_rules', 1) == 1)
{
// 订单收货
if($params['opt_type'] == 'collect')
{
$status = true;
}
} else {
// 订单支付
if($params['opt_type'] == 'pay')
{
$status = true;
}
}
if($status)
{
// 获取订单商品
$order_detail = Db::name('OrderDetail')->field('id,goods_id,title,buy_number')->where(['order_id'=>$params['order_id']])->select()->toArray();
if(!empty($order_detail))
{
foreach($order_detail as $v)
{
if(Db::name('Goods')->where(['id'=>$v['goods_id']])->inc('sales_count', $v['buy_number'])->update() === false)
{
return DataReturn('订单商品销量增加失败['.$v['title'].']', -10);
}
}
return DataReturn(MyLang('common.operate_success'), 0);
} else {
return DataReturn('订单有误,没有找到相关商品', -100);
}
}
return DataReturn(MyLang('common.handle_noneed'), 0);
}
/**
* 支付状态校验
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2019-01-08
* @desc description
* @param [array] $params [输入参数]
*/
public static function OrderPayCheck($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'order_no',
'error_msg' => '订单号有误',
],
[
'checked_type' => 'empty',
'key_name' => 'user',
'error_msg' => '用户信息有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 获取订单状态
$where = ['log_no'=>$params['order_no'], 'user_id'=>$params['user']['id']];
$pay_log = Db::name('PayLog')->where($where)->field('id,status')->find();
if(empty($pay_log))
{
return DataReturn('支付订单不存在', -400, ['url'=>SystemService::HomeUrl()]);
}
if($pay_log['status'] == 1)
{
$pay_log_value = Db::name('PayLogValue')->where(['pay_log_id'=>$pay_log['id']])->column('business_id');
if(empty($pay_log_value) || count($pay_log_value) > 1)
{
$url = MyUrl('index/order/index');
} else {
$url = MyUrl('index/order/detail', ['id'=>$pay_log_value[0]]);
}
return DataReturn('支付成功', 0, ['url'=>$url]);
}
return DataReturn('支付中', -300);
}
/**
* 订单支付参数处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-08-07
* @desc description
* @param [array] $params [输入参数]
*/
public static function PayParamsHandle($params = [])
{
// 支付方式
$payment_id = empty($params['payment_id']) ? '' : intval($params['payment_id']);
// 支付订单id、多个订单id以英文逗号分割[ , ]
// 严格处理参数,避免非法数据
$order_ids = '';
if(!empty($params['ids']))
{
$ids = array_filter(array_map(function($v)
{
return intval($v);
}, explode(',', urldecode($params['ids']))));
if(!empty($ids))
{
$order_ids = implode(',', $ids);
}
}
return [
'payment_id' => $payment_id,
'order_ids' => $order_ids,
];
}
}
?>