服务订单

pull/1/head
wanghousheng 10 months ago
parent 6856f5c2cc
commit 88f1002f9c
  1. 198
      app/api/controller/Server.php
  2. 115
      app/api/model/Server/ServerOrder.php
  3. 37
      app/api/model/UserCoupon.php
  4. 16
      app/api/service/Notify.php
  5. 326
      app/api/service/Server/PaySuccess.php
  6. 327
      app/api/service/Server/ServerPayment.php
  7. 10
      app/api/service/User.php
  8. 6
      app/common/model/UserCoupon.php
  9. 274
      app/common/service/server/Order.php

@ -7,7 +7,9 @@ namespace app\api\controller;
use app\api\model\Server\ServerCategory;
use app\api\model\Server\ServerOrder;
use app\api\service\Server\ServerPayment;
use app\common\enum\ServerEnum;
use app\common\service\server\Order as ServerServiceOrder;
use cores\exception\BaseException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
@ -94,50 +96,198 @@ class Server extends Controller
$where['order_status'] = $order_status;
}
$model = new ServerOrder($where);
$list = $model->userOrders($where);
$list = $model->orderList($where);
$data['list'] = $list->items();
$data['total'] = $list->total();
if (!$list->isEmpty()) {
if (!$list->isEmpty()) {
foreach ($data['list'] as $key => $value) {
unset($data['list'][$key]['image']);
foreach ($data['list'] as $key => $value) {
$data['list'][$key]['is_cancel'] = ServerServiceOrder::checkCancel($value);
$data['list'][$key]['is_dispatch'] = ServerServiceOrder::checkDispatch($value);
$data['list'][$key]['is_pay'] = ServerServiceOrder::checkPay($value);
unset($data['list'][$key]['image']);
if (!empty($data['list'][$key]['dealer'])) {
unset($data['list'][$key]['dealer']);
}
if (!empty($data['list'][$key]['user'])) {
unset($data['list'][$key]['user']);
}
}
}
return $this->renderSuccess($data);
}
/**
* @notes:
* @notes:确认订单
* @return Json
* @throws BaseException
* @throws DbException
* @author: wanghousheng
*/
public function dealerOrderList(): Json
public function checkOrder(): Json
{
$order_no = $this->request->post('order_no');
$order_status = intval($this->request->post('order_status'));
$where = [];
if (!empty($order_no)) {
$where['order_no'] = $order_no;
$serverId = intval($this->request->post('server_id'));
$couponId = intval($this->request->post('coupon_id'));
if (!$serverId) {
return $this->renderError('非法请求');
}
if ($order_status) {
$where['order_status'] = $order_status;
$orderService = new ServerServiceOrder();
$data = $orderService->onCheck($serverId, $couponId);
return $this->renderSuccess($data);
}
/**
* @notes:生成订单
* @return Json
* @throws BaseException
* @throws DbException
* @author: wanghousheng
*/
public function createOrder(): Json
{
$serverId = intval($this->request->post('server_id'));
$couponId = intval($this->request->post('coupon_id'));
$remake = $this->request->post('remake');
if (!$serverId) {
return $this->renderError('非法请求');
}
$model = new ServerOrder($where);
$list = $model->dealerOrders($where);
$data['list'] = $list->items();
$data['total'] = $list->total();
if (!$list->isEmpty()) {
if (!$list->isEmpty()) {
foreach ($data['list'] as $key => $value) {
unset($data['list'][$key]['image']);
unset($data['list'][$key]['dealer']);
}
}
$orderService = new ServerServiceOrder();
$result = $orderService->createOrder($serverId, $couponId, $remake);
if ($result) {
return $this->renderSuccess($result);
}
return $this->renderError('创建订单失败');
}
/**
* @notes:取消订单
* @return Json
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function cancelOrder(): Json
{
$orderId = intval($this->request->post('order_id'));
if (!$orderId) {
return $this->renderError('非法请求');
}
if (ServerServiceOrder::userCancelOrder($orderId)) {
return $this->renderSuccess('取消成功');
}
return $this->renderError('操作失败');
}
/**
* @notes:派单人员列表
* @return Json
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function getEngineer(): Json
{
$list = ServerServiceOrder::getEngineer();
return $this->renderSuccess(compact('list'));
}
/**
* @notes:派单
* @return Json
* @throws BaseException
* @author: wanghousheng
*/
public function dispatchOrders(): Json
{
$orderId = intval($this->request->post('order_id'));
if (!$orderId) {
return $this->renderError('非法请求');
}
$dealerId = intval($this->request->post('dealer_id'));
if (!$dealerId) {
return $this->renderError('分配人员不能为空');
}
if (ServerServiceOrder::userDispatchOrders($orderId, $dealerId)) {
return $this->renderSuccess('操作成功');
}
return $this->renderError('操作失败');
}
/**
* @notes:获取支付订单的信息
* @return Json
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function orderInfo(): Json
{
$orderId = intval($this->request->post('order_id'));
if (!$orderId) {
return $this->renderError('非法请求');
}
$client = $this->request->post('client');
if (!$client) {
return $this->renderError('客户端不能为空');
}
$paymentService = new ServerPayment();
$data = $paymentService->setOrderId($orderId)->setClient($client)->orderInfo();
return $this->renderSuccess($data);
}
/**
* @notes:确认订单支付事件
* @return array
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function orderPay(): array
{
$orderId = intval($this->request->post('order_id'));
if (!$orderId) {
return $this->renderError('非法请求');
}
$method = $this->request->post('method');
if (!$method) {
return $this->renderError('支付方式不能为空');
}
$client = $this->request->post('client');
if (!$client) {
return $this->renderError('客户端不能为空');
}
$paymentService = new ServerPayment();
$data = $paymentService->setOrderId($orderId)
->setMethod($method)
->setClient($client)
->orderPay();
return $this->renderSuccess($data, $paymentService->getMessage() ?: '下单成功');
}
/**
* @notes:交易查询
* @param string $outTradeNo
* @param string $method
* @param string $client
* @return Json
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function tradeQuery(string $outTradeNo, string $method, string $client): Json
{
$paymentService = new ServerPayment;
$result = $paymentService->setMethod($method)->setClient($client)->tradeQuery($outTradeNo);
$message = $result ? '恭喜您,订单已付款成功' : ($paymentService->getError() ?: '很抱歉,订单未支付,请重新发起');
return $this->renderSuccess(['isPay' => $result], $message);
}
}

@ -2,11 +2,15 @@
namespace app\api\model\Server;
use app\api\model\Setting as SettingModel;
use app\api\service\User as UserService;
use app\common\enum\order\PayStatus;
use app\common\enum\ServerEnum;
use app\common\model\server\Order;
use cores\exception\BaseException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Paginator;
class ServerOrder extends Order
@ -33,38 +37,103 @@ class ServerOrder extends Order
* @throws DbException
* @author: wanghousheng
*/
public function userOrders($where, int $listRows = 15): Paginator
public function orderList($where, int $listRows = 15): Paginator
{
// 当前用户ID
$userId = UserService::getCurrentLoginUserId();
// 查询列表数据
return $this->with(['image', 'dealer'])
->where($where)
->where('user_id', '=', $userId)
->where('is_delete', '=', 0)
->order(['create_time' => 'desc'])
->paginate($listRows);
//判断当前用户身份
if (UserService::isDealerEngineer()) {
//分销商工程师
return $this->with(['image', 'user'])
->where($where)
->where('dealer_id', '=', $userId)
->where('is_delete', '=', 0)
->order(['create_time' => 'desc'])
->paginate($listRows);
} elseif (UserService::isStore()) {
// 店主
return $this->with(['image', 'user', 'dealer'])
->where($where)
->where('is_delete', '=', 0)
->order(['create_time' => 'desc'])
->paginate($listRows);
} else {
return $this->with(['image', 'dealer'])
->where($where)
->where('user_id', '=', $userId)
->where('is_delete', '=', 0)
->order(['create_time' => 'desc'])
->paginate($listRows);
}
}
/**
* @notes:分销员订单列表
* @param $where
* @param int $listRows
* @return Paginator
* 获取用户订单详情(仅订单记录)
* @param int $orderId
* @param array $with
* @param bool $onlyCurrentUser 只查询当前登录用户的记录
* @return array|Order|null
* @throws BaseException
*/
public static function getDetail(int $orderId, array $with = [], bool $onlyCurrentUser = true)
{
// 查询条件
$where = ['order_id' => $orderId];
$onlyCurrentUser && $where['user_id'] = UserService::getCurrentLoginUserId();
// 查询订单记录
$order = static::detail($where, $with);
empty($order) && throwError('订单不存在');
return $order;
}
/**
* 待支付订单详情
* @param string $orderNo 订单号
* @return null|static
*/
public static function getPayDetail(string $orderNo): ?Order
{
$where = ['order_no' => $orderNo, 'is_delete' => 0];
return self::detail($where, ['user']);
}
/**
* @notes:获取未支付的订单详情(用于订单支付)
* @param int $orderId 订单ID
* @return array
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function dealerOrders($where, int $listRows = 15): Paginator
public static function getUnpaidOrderDetail(int $orderId): array
{
// 当前用户ID
$userId = UserService::getCurrentLoginUserId();
// 查询列表数据
return $this->with(['image', 'user'])
->where($where)
->where('dealer_id', '=', $userId)
->where('is_delete', '=', 0)
->order(['create_time' => 'desc'])
->paginate($listRows);
// 获取订单详情
$orderInfo = static::getDetail($orderId);
// 验证订单状态
if ($orderInfo['order_status'] != ServerEnum::APPLYPAY || $orderInfo['pay_status'] = PayStatus::SUCCESS) {
throwError('当前订单状态不允许支付');
}
// 未支付订单的过期时间
$orderCloseTime = SettingModel::getOrderCloseTime() * 60 * 60;
// 订单超时截止时间
$expirationTime = $orderInfo->getData('create_time') + $orderCloseTime;
if ($orderCloseTime > 0 && $expirationTime <= time()) {
throwError('当前订单支付已超时,请重新下单');
}
// 仅返回需要的数据
return [
'orderId' => $orderInfo['order_id'],
'order_no' => $orderInfo['order_no'],
'pay_price' => $orderInfo['pay_price'],
'pay_status' => $orderInfo['pay_status'],
'order_status' => $orderInfo['order_status'],
'create_time' => $orderInfo['create_time'],
'showExpiration' => $orderCloseTime > 0,
'expirationTime' => format_time($expirationTime),
];
}
}

@ -12,12 +12,13 @@ declare (strict_types=1);
namespace app\api\model;
use app\api\service\User as UserService;
use app\api\model\Coupon as CouponModel;
use app\common\model\UserCoupon as UserCouponModel;
use app\common\enum\coupon\CouponType as CouponTypeEnum;
use app\api\service\User as UserService;
use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
use app\common\enum\coupon\CouponType as CouponTypeEnum;
use app\common\enum\coupon\TypeCase;
use app\common\library\helper;
use app\common\model\UserCoupon as UserCouponModel;
use cores\exception\BaseException;
/**
@ -169,14 +170,14 @@ class UserCoupon extends UserCouponModel
* @return array
* @throws \think\db\exception\DbException
*/
public static function getUserCouponList(int $userId, float $orderPayPrice): array
public static function getUserCouponList(int $userId, float $orderPayPrice, $couponType = TypeCase::SHOP): array
{
// 判断订单商品总金额不能为1分
if ($orderPayPrice <= 0.01) {
return [];
}
// 获取用户可用的优惠券列表
$list = (new static)->getList($userId, ['dataType' => 'isUsable', 'amount' => $orderPayPrice]);
$list = (new static)->getList($userId, ['dataType' => 'isUsable', 'amount' => $orderPayPrice, 'coupon_case' => $couponType]);
$data = $list->isEmpty() ? [] : $list->toArray()['data'];
foreach ($data as &$item) {
// 计算最大能折扣的金额
@ -217,4 +218,30 @@ class UserCoupon extends UserCouponModel
}
return $couponList;
}
/**
* @notes:判断当前优惠券是否满足服务订单使用条件
* @param array $couponList
* @param array $serverIds
* @return array
* @author: wanghousheng
*/
public static function couponListApplyRangeServer(array $couponList, array $serverIds): array
{
// 名词解释(is_apply):允许用于抵扣当前订单
foreach ($couponList as &$item) {
if ($item['apply_range'] == ApplyRangeEnum::ALL) {
// 1. 全部服务
$item['is_apply'] = true;
} elseif ($item['apply_range'] == ApplyRangeEnum::SOME) {
// 2. 指定服务, 判断订单是否存在可用
if (!empty($item['apply_range_config']['applyServerIds'])) {
$applyServerIds = array_intersect($item['apply_range_config']['applyServerIds'], $serverIds);
$item['is_apply'] = !empty($applyServerIds);
}
}
!$item['is_apply'] && $item['not_apply_info'] = '该优惠券不支持当前服务';
}
return $couponList;
}
}

@ -13,16 +13,17 @@ declare (strict_types=1);
namespace app\api\service;
use app\api\model\Payment as PaymentModel;
use app\api\model\PaymentTrade as PaymentTradeModel;
use app\api\model\PaymentTemplate as PaymentTemplateModel;
use app\api\model\PaymentTrade as PaymentTradeModel;
use app\api\service\order\PaySuccess as OrderPaySuccesService;
use app\api\service\recharge\PaySuccess as RechargePaySuccesService;
use app\api\service\Server\PaySuccess;
use app\common\enum\OrderType as OrderTypeEnum;
use app\common\enum\payment\Method as PaymentMethodEnum;
use app\common\library\Log;
use app\common\library\helper;
use app\common\library\payment\gateway\Driver;
use app\common\library\Log;
use app\common\library\payment\Facade as PaymentFacade;
use app\common\library\payment\gateway\Driver;
use app\common\library\payment\gateway\driver\wechat\V3 as WechatPaymentV3;
use cores\exception\BaseException;
@ -154,6 +155,15 @@ class Notify
->setPaymentData($paymentData)
->handle();
}
// 订单支付成功业务处理 (服务订单)
if ($tradeInfo['order_type'] == OrderTypeEnum::SERVER) {
$service = new PaySuccess();
$service->setOrderNo($tradeInfo['order_no'])
->setMethod($tradeInfo['pay_method'])
->setTradeId($tradeInfo['trade_id'])
->setPaymentData($paymentData)
->handle();
}
Log::append('Notify-orderPaySucces', ['message' => '订单支付成功']);
} catch (\Throwable $e) {
// 记录错误日志

@ -0,0 +1,326 @@
<?php
namespace app\api\service\Server;
use app\api\model\Order as OrderModel;
use app\api\model\PaymentTrade as PaymentTradeModel;
use app\api\model\Server\ServerOrder;
use app\api\model\User as UserModel;
use app\api\model\user\BalanceLog as BalanceLogModel;
use app\common\enum\order\PayStatus as PayStatusEnum;
use app\common\enum\OrderType as OrderTypeEnum;
use app\common\enum\payment\Method as PaymentMethodEnum;
use app\common\enum\user\balanceLog\Scene as SceneEnum;
use app\common\library\Lock;
use app\common\library\Log;
use app\common\service\BaseService;
use cores\exception\BaseException;
use think\facade\Event;
class PaySuccess extends BaseService
{
// 当前订单信息
public ServerOrder $orderInfo;
// 当前用户信息
private UserModel $userInfo;
// 当前订单号
private string $orderNo;
// 当前订单ID
private string $orderId;
// 订单支付方式
private string $method;
// 第三方交易记录ID
private ?int $tradeId = null;
// 第三方支付成功返回的数据
private array $paymentData = [];
/**
* 设置支付的订单ID
* @param int $orderId 订单ID
*/
public function setOrderId(int $orderId): PaySuccess
{
$this->orderId = $orderId;
return $this;
}
/**
* 设置当前的订单号
* @param string $orderNo
* @return $this
*/
public function setOrderNo(string $orderNo): PaySuccess
{
$this->orderNo = $orderNo;
return $this;
}
/**
* 设置订单支付方式
* @param string $method
* @return $this
*/
public function setMethod(string $method): PaySuccess
{
$this->method = $method;
return $this;
}
/**
* 第三方支付交易记录ID
* @param int|null $tradeId
* @return $this
*/
public function setTradeId(?int $tradeId = null): PaySuccess
{
$this->tradeId = $tradeId;
return $this;
}
/**
* 第三方支付成功返回的数据
* @param array $paymentData
* @return $this
*/
public function setPaymentData(array $paymentData): PaySuccess
{
$this->paymentData = $paymentData;
return $this;
}
/**
* 订单支付成功业务处理
* @return bool
* @throws BaseException
*/
public function handle(): bool
{
// 验证当前参数是否合法
$this->verifyParameters();
// 当前订单开启并发锁
$this->lockUp();
// 验证当前订单是否允许支付
if ($this->checkOrderStatusOnPay()) {
// 更新订单状态为已付款
$this->updatePayStatus();
// 订单支付成功事件 (处理订单来源相关业务)
Event::trigger('OrderPaySuccess', [
'order' => $this->getOrderInfo(),
'orderType' => OrderTypeEnum::SERVER
]);
}
// 当前订单解除并发锁
$this->unLock();
return true;
}
/**
* 验证当前参数是否合法
* @throws BaseException
*/
private function verifyParameters()
{
if (empty($this->orderNo)) {
throwError('orderNo not found');
}
if (empty($this->method)) {
throwError('method not found');
}
if ($this->tradeId) {
empty($this->paymentData) && throwError('PaymentData not found');
!isset($this->paymentData['tradeNo']) && throwError('PaymentData not found');
}
// 记录日志
Log::append('PaySuccess', [
'orderNo' => $this->orderNo, 'method' => $this->method,
'tradeId' => $this->tradeId, 'paymentData' => $this->paymentData
]);
}
/**
* 订单模型
* @return OrderModel|null
* @throws BaseException
*/
private function orderModel(): ?OrderModel
{
return $this->getOrderInfo();
}
/**
* 订单已付款事件
* @return void
* @throws BaseException
*/
private function updatePayStatus(): void
{
// 记录日志
Log::append('PaySuccess --updatePayStatus', ['title' => '订单已付款事件']);
// 当前订单信息
$orderInfo = $this->getOrderInfo();
// 事务处理
$this->orderModel()->transaction(function () use ($orderInfo) {
// 更新订单状态
$this->updateOrderStatus();
// 累积用户总消费金额
UserModel::setIncPayMoney($orderInfo['user_id'], (float)$orderInfo['pay_price']);
// 记录订单支付信息
$this->updatePayInfo();
});
}
/**
* 记录订单支付的信息
* @throws BaseException
*/
private function updatePayInfo()
{
// 当前订单信息
$orderInfo = $this->getOrderInfo();
// 余额支付
if ($this->method == PaymentMethodEnum::BALANCE) {
// 更新用户余额
UserModel::setDecBalance((int)$orderInfo['user_id'], (float)$orderInfo['pay_price']);
// 新增余额变动记录
BalanceLogModel::add(SceneEnum::CONSUME, [
'user_id' => (int)$orderInfo['user_id'],
'money' => -$orderInfo['pay_price'],
], ['order_no' => $orderInfo['order_no']]);
}
// 将第三方交易记录更新为已支付状态
if (in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY])) {
$this->updateTradeRecord();
}
}
/**
* 将第三方交易记录更新为已支付状态
*/
private function updateTradeRecord()
{
if ($this->tradeId && !empty($this->paymentData)) {
PaymentTradeModel::updateToPaySuccess($this->tradeId, $this->paymentData['tradeNo']);
}
}
/**
* 更新订单状态
* @throws BaseException
*/
private function updateOrderStatus(): void
{
// 更新订单状态
$this->orderModel()->save([
'pay_method' => $this->method,
'pay_status' => PayStatusEnum::SUCCESS,
'pay_time' => time(),
'trade_id' => $this->tradeId ?: 0,
]);
}
/**
* 获取买家用户信息
* @return UserModel|array|null
* @throws BaseException
*/
private function getUserInfo()
{
if (empty($this->userInfo)) {
$this->userInfo = UserModel::detail($this->getOrderInfo()['user_id']);
}
if (empty($this->userInfo)) {
throwError('未找到买家用户信息');
}
return $this->userInfo;
}
/**
* 验证当前订单是否允许支付
* @return bool
* @throws BaseException
*/
private function checkOrderStatusOnPay(): bool
{
// 当前订单信息
$orderInfo = $this->getOrderInfo();
// 验证余额支付时用户余额是否满足
if ($this->method == PaymentMethodEnum::BALANCE) {
if ($this->getUserInfo()['balance'] < $orderInfo['pay_price']) {
throwError('账户余额不足,无法使用余额支付');
}
}
// 检查订单状态是否为已支付
if ($orderInfo['pay_status'] == PayStatusEnum::SUCCESS) {
$this->onOrderPaid();
return false;
}
return true;
}
/**
* 处理订单已支付的情况
* @throws BaseException
*/
private function onOrderPaid()
{
// 记录日志
Log::append('PaySuccess --onOrderPaid', ['title' => '处理订单已支付的情况']);
// 当前订单信息
$orderInfo = $this->getOrderInfo();
// 余额支付直接返回错误信息
if ($this->method == PaymentMethodEnum::BALANCE) {
throwError('当前订单已支付,无需重复支付');
}
// 第三方支付判断是否为重复下单 (因异步回调可能存在网络延迟的原因,在并发的情况下会出现同时付款两次,这里需要容错)
// 如果订单记录中已存在tradeId并且和当前支付的tradeId不一致, 那么判断为重复的订单, 需进行退款处理
if ($this->tradeId > 0 && $orderInfo['trade_id'] != $this->tradeId) {
// 执行原路退款
throwError('当前订单异常');
}
}
/**
* 获取当前订单的详情信息
* @return OrderModel|null
* @throws BaseException
*/
private function getOrderInfo(): ?OrderModel
{
// 获取订单详情 (待支付状态)
if (empty($this->orderInfo)) {
$this->orderInfo = ServerOrder::getPayDetail($this->orderNo);
}
// 判断订单是否存在
if (empty($this->orderInfo)) {
throwError('未找到该订单信息');
}
return $this->orderInfo;
}
/**
* 订单锁:防止并发导致重复支付
* @throws BaseException
*/
private function lockUp()
{
$orderInfo = $this->getOrderInfo();
Lock::lockUp("OrderPaySuccess_{$orderInfo['order_id']}");
}
/**
* 订单锁:防止并发导致重复支付
* @throws BaseException
*/
private function unLock()
{
$orderInfo = $this->getOrderInfo();
Lock::unLock("OrderPaySuccess_{$orderInfo['order_id']}");
}
}

@ -0,0 +1,327 @@
<?php
namespace app\api\service\Server;
use app\api\model\Payment as PaymentModel;
use app\api\model\PaymentTrade as PaymentTradeModel;
use app\api\model\Server\ServerOrder;
use app\api\service\Order as OrderService;
use app\api\service\User as UserService;
use app\common\enum\Client as ClientEnum;
use app\common\enum\OrderType as OrderTypeEnum;
use app\common\enum\payment\Method as PaymentMethodEnum;
use app\common\library\payment\Facade as PaymentFacade;
use app\common\service\BaseService;
use cores\exception\BaseException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
class ServerPayment extends BaseService
{
// 提示信息
private string $message = '';
// 订单信息
private ServerOrder $orderInfo;
// 支付方式 (余额、微信支付、支付宝)
private string $method = '';
// 下单的客户端
private string $client = '';
//未支付的订单ID
private int $orderId;
/**
* 设置支付的订单ID
* @param int $orderId 订单ID
*/
public function setOrderId(int $orderId): ServerPayment
{
$this->orderId = $orderId;
return $this;
}
/**
* 设置当前支付方式
* @param string $method 支付方式
* @return $this
*/
public function setMethod(string $method): ServerPayment
{
$this->method = $method;
return $this;
}
/**
* 设置下单的客户端
* @param string $client 客户端
* @return $this
*/
public function setClient(string $client): ServerPayment
{
$this->client = $client;
return $this;
}
/**
* 返回消息提示
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
/**
* @notes:获取支付订单的信息
* @return array
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function orderInfo(): array
{
// 当期用户信息
$userInfo = UserService::getCurrentLoginUser(true);
// 根据指定客户端获取可用的支付方式
$PaymentModel = new PaymentModel;
$methods = $PaymentModel->getMethodsByClient($this->client);
// 获取结算订单信息
$OrderModel = new ServerOrder();
$orderInfo = $OrderModel->getUnpaidOrderDetail($this->orderId);
return [
'order' => $orderInfo,
'personal' => $userInfo,
'paymentMethods' => $methods
];
}
/**
* @notes:确认订单支付事件
* @param array $extra 附加数据
* @return array
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function orderPay(array $extra = []): array
{
// 获取订单信息
$this->orderInfo = ServerOrder::getDetail($this->orderId);
// 订单支付事件
$this->orderPayEvent();
// 构建第三方支付请求的参数
$payment = $this->unifiedorder($extra);
// 记录第三方交易信息
$this->recordPaymentTrade($payment);
// 返回结果
return compact('payment');
}
/**
* 订单支付事件
* @return void
* @throws BaseException
* @author: wanghousheng
*/
private function orderPayEvent(): void
{
// 余额支付
if ($this->method == PaymentMethodEnum::BALANCE) {
$this->orderPaySuccess($this->orderInfo['order_no']);
}
}
/**
* 获取支付方式的配置信息
* @return mixed
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
private function getPaymentConfig()
{
$PaymentModel = new PaymentModel;
$templateInfo = $PaymentModel->getPaymentInfo($this->method, $this->client, $this->getStoreId());
return $templateInfo['template']['config'][$this->method];
}
/**
* 获取支付宝端的用户buyerId(仅支付宝小程序端)
* @return null
* @throws BaseException
* @author: wanghousheng
*/
private function getAlipayBuyerId()
{
if ($this->client == ClientEnum::MP_ALIPAY) {
// 当前登录用户信息
$useInfo = UserService::getCurrentLoginUser(true);
if (!$useInfo['currentOauth'] || empty($useInfo['currentOauth']['oauth_id'])) {
throwError('很抱歉,您当前不存在buyerId 无法发起支付宝支付');
}
// 当前第三方用户标识
return $useInfo['currentOauth']['oauth_id'];
}
return null;
}
/**
* 整理下单接口所需的附加数据
* @param array $extra
* @return array
* @throws BaseException
* @author: wanghousheng
*/
private function extraAsUnify(array $extra = []): array
{
// 微信支付时需要的附加数据
if ($this->method === PaymentMethodEnum::WECHAT) {
// 微信小程序端和微信公众号端需要openid
if (in_array($this->client, [ClientEnum::WXOFFICIAL, ClientEnum::MP_WEIXIN])) {
$extra['openid'] = $this->getWechatOpenid();
}
}
// 支付宝支付时需要的附加数据
if ($this->method === PaymentMethodEnum::ALIPAY) {
// 支付宝小程序端需要buyerId
if ($this->client == ClientEnum::MP_ALIPAY) {
$extra['buyerId'] = $this->getAlipayBuyerId();
}
}
return $extra;
}
/**
* 获取微信端的用户openid(仅微信小程序和微信公众号)
* @return null
* @throws BaseException
* @author: wanghousheng
*/
private function getWechatOpenid()
{
if (in_array($this->client, [ClientEnum::MP_WEIXIN, ClientEnum::WXOFFICIAL])) {
// 当前登录用户信息
$useInfo = UserService::getCurrentLoginUser(true);
if (!$useInfo['currentOauth'] || empty($useInfo['currentOauth']['oauth_id'])) {
throwError('很抱歉,您当前不存在openid 无法发起微信支付');
}
// 当前第三方用户标识
return $useInfo['currentOauth']['oauth_id'];
}
return null;
}
/**
* 查询订单是否支付成功 (仅限第三方支付订单)
* @param string $outTradeNo 商户订单号
* @return bool
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public function tradeQuery(string $outTradeNo): bool
{
// 判断支付方式是否合法
if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY])) {
return false;
}
// 获取支付方式的配置信息
$options = $this->getPaymentConfig();
// 构建支付模块
$Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
// 执行第三方支付查询API
$result = $Payment->tradeQuery($outTradeNo);
// 订单支付成功事件
if (!empty($result) && $result['paySuccess']) {
// 获取第三方交易记录信息
$tradeInfo = PaymentTradeModel::detailByOutTradeNo($outTradeNo);
// 订单支付成功事件
$this->orderPaySuccess($tradeInfo['order_no'], $tradeInfo['trade_id'], $result);
}
// 返回订单状态
return $result ? $result['paySuccess'] : false;
}
/**
* 构建第三方支付请求的参数
* @param array $extra 附加数据
* @return array
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
private function unifiedorder(array $extra = []): array
{
// 生成第三方交易订单号 (并非主订单号)
$outTradeNo = OrderService::createOrderNo();
// 获取支付方式的配置信息
$options = $this->getPaymentConfig();
// 整理下单接口所需的附加数据
$extra = $this->extraAsUnify($extra);
// 构建支付模块
$Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
// 执行第三方支付下单API
if (!$Payment->unify($outTradeNo, (string)$this->orderInfo['pay_price'], $extra)) {
throwError('第三方支付下单API调用失败');
}
// 返回客户端需要的支付参数
return $Payment->getUnifyResult();
}
/**
* 记录第三方交易信息
* @param array $payment 第三方支付数据
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
private function recordPaymentTrade(array $payment): void
{
if ($this->method != PaymentMethodEnum::BALANCE) {
PaymentTradeModel::record(
$this->orderInfo,
$this->method,
$this->client,
OrderTypeEnum::SERVER,
$payment
);
}
}
/**
* 订单支付成功事件
* @param string $orderNo 当前订单号
* @param int|null $tradeId 第三方交易记录ID
* @param array $paymentData 第三方支付成功返回的数据
* @return void
* @throws BaseException
* @author: wanghousheng
*/
private function orderPaySuccess(string $orderNo, ?int $tradeId = null, array $paymentData = []): void
{
// 获取订单详情
$service = new PaySuccess();
// 订单支付成功业务处理
$service->setOrderNo($orderNo)->setMethod($this->method)->setTradeId($tradeId)->setPaymentData($paymentData);
if (!$service->handle()) {
throwError($service->getError() ?: '订单支付失败');
}
$this->message = '恭喜您,订单支付成功';
}
}

@ -86,7 +86,7 @@ class User extends UserService
* @throws BaseException
* @author: wanghousheng
*/
public function isDealerMember(): bool
public static function isDealerMember(): bool
{
$userType = static::getCurrentLoginUserType();
if ($userType && $userType == UserTypeEnum::DEALER) {
@ -106,7 +106,7 @@ class User extends UserService
* @throws BaseException
* @author: wanghousheng
*/
public function isDealerEngineer(): bool
public static function isDealerEngineer(): bool
{
$userType = static::getCurrentLoginUserType();
if ($userType && $userType == UserTypeEnum::DEALER) {
@ -126,7 +126,7 @@ class User extends UserService
* @throws BaseException
* @author: wanghousheng
*/
public function isStore(): bool
public static function isStore(): bool
{
$userType = static::getCurrentLoginUserType();
if ($userType && $userType == UserTypeEnum::STORE) {
@ -134,7 +134,7 @@ class User extends UserService
//商家表里有没有
$model = new \app\api\model\Store();
$store_id = $model->where(['user_id' => $userId])->value('store_id');
if ($store_id && $store_id == $this->getStoreId()) {
if ($store_id && $store_id == (new self())->getStoreId()) {
return $userId;
}
}
@ -147,7 +147,7 @@ class User extends UserService
* @throws BaseException
* @author: wanghousheng
*/
public function isNormalMember(): bool
public static function isNormalMember(): bool
{
$userType = static::getCurrentLoginUserType();
if ($userType && $userType == UserTypeEnum::NORMAL) {

@ -12,10 +12,11 @@ declare (strict_types=1);
namespace app\common\model;
use cores\BaseModel;
use app\common\enum\coupon\ExpireType as ExpireTypeEnum;
use app\common\enum\coupon\TypeCase;
use app\common\library\helper;
use app\common\model\Coupon as CouponModel;
use app\common\enum\coupon\ExpireType as ExpireTypeEnum;
use cores\BaseModel;
use think\model\relation\BelongsTo;
/**
@ -176,6 +177,7 @@ class UserCoupon extends BaseModel
// 整理领取记录
$data = [
'coupon_id' => $couponInfo['coupon_id'],
'coupon_case' => !empty($couponInfo['coupon_case']) ?: TypeCase::SHOP,
'name' => $couponInfo['name'],
'coupon_type' => $couponInfo['coupon_type'],
'reduce_price' => $couponInfo['reduce_price'],

@ -4,12 +4,21 @@ declare (strict_types=1);
namespace app\common\service\server;
use app\api\model\dealer\User as DealerUserModel;
use app\api\model\Server\ServerOrder;
use app\api\model\UserCoupon;
use app\api\service\User as UserService;
use app\common\enum\coupon\CouponType as CouponTypeEnum;
use app\common\enum\coupon\TypeCase;
use app\common\enum\dealer\DealerUserEnum;
use app\common\enum\order\PayStatus;
use app\common\enum\ServerEnum;
use app\common\library\helper;
use app\common\model\server\Server;
use app\common\model\UserCoupon as UserCouponModel;
use app\common\service\BaseService;
use app\common\service\Order as OrderService;
use app\common\service\order\Refund as RefundService;
use app\store\model\dealer\User as UserModel;
use cores\exception\BaseException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
@ -17,13 +26,29 @@ use think\db\exception\ModelNotFoundException;
class Order extends BaseService
{
/**
* 订单结算的规则
* @var array
*/
private array $checkoutRule = [
'isUserGrade' => true, // 会员等级折扣
'isCoupon' => true, // 优惠券抵扣
'isUsePoints' => true, // 是否使用积分抵扣
'isDealer' => true, // 是否开启分销
];
private array $serverInfo;
private int $serverId;
private int $couponId;
/**
* 生成订单号
* @return string
*/
public static function createOrderNo(): string
private function orderNo(): string
{
return 'SE' . date('Ymd') . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13)))), 0, 8);
return OrderService::createOrderNo();
}
/**
@ -62,6 +87,51 @@ class Order extends BaseService
return false;
}
/**
* @notes:是否可以取消
* @param array $order
* @return bool
* @author: wanghousheng
*/
public static function checkCancel(array $order): bool
{
if (!empty($order['order_status']) && $order['order_status'] != ServerEnum::COMPLETED || $order['order_status'] != ServerEnum::CANCELLED) {
return true;
}
return false;
}
/**
* @notes:是否可以派单
* @param array $order
* @return bool
* @throws BaseException
* @author: wanghousheng
*/
public static function checkDispatch(array $order): bool
{
if (!empty($order) && $order['order_status'] == ServerEnum::APPLYDISPATCH && UserService::isStore()) {
return true;
}
return false;
}
/**
* @notes:是否可以支付
* @param array $order
* @return bool
* @throws BaseException
* @author: wanghousheng
*/
public static function checkPay(array $order): bool
{
$userId = UserService::getCurrentLoginUserId();
if (!empty($order) && $order['pay_status'] == PayStatus::PENDING && $order['user_id'] == $userId) {
return true;
}
return false;
}
/**
* @notes:派单
* @param array $order
@ -88,4 +158,204 @@ class Order extends BaseService
}
return false;
}
/**
* @notes:生成订单
* @param int $serverId
* @param int $couponId
* @param string $buyer_remark
* @return array
* @throws BaseException
* @throws DbException
* @author: wanghousheng
*/
public function createOrder(int $serverId, int $couponId, string $buyer_remark = ''): array
{
$userId = UserService::getCurrentLoginUserId();
$data = $this->onCheck($serverId, $couponId);
// 设置优惠券使用状态
$couponId > 0 && UserCoupon::setIsUse($couponId);
//封装数据
$order_data = [
'server_id' => $this->serverInfo['server_id'],
'order_no' => $this->orderNo(),
'server_image_id' => $this->serverInfo['image_id'],
'total_price' => $this->serverInfo['server_price'],
'store_id' => $this->storeId,
'user_id' => $userId,
'coupon_id' => $data['coupon_id'],
'coupon_money' => $data['coupon_money'],
'pay_price' => $data['pay_price'],
'buyer_remark' => $buyer_remark,
];
$model = new ServerOrder();
$order_id = $model->insertGetId($order_data);
if ($order_id) {
return ['order_no' => $order_data['order_no'], 'order_id' => $order_id];
}
return [];
}
/**
* @notes:订单确认-结算台
* @param int $serverId
* @param int $couponId
* @return array
* @throws BaseException
* @throws DbException
* @author: wanghousheng
*/
public function onCheck(int $serverId, int $couponId): array
{
$this->serverId = $serverId;
$this->couponId = $couponId;
return $this->checkOut();
}
/**
* @notes:订单结算台
* @return array
* @throws BaseException
* @throws DbException
* @author: wanghousheng
*/
private function checkOut(): array
{
$info = Server::detail(['server_id' => $this->serverId, 'status' => 1], ['image', 'category']);
if ($info->isEmpty()) {
throwError('未找到服务信息');
}
$this->serverInfo = $info->toArray();
$couponList = $this->getUserCouponList();
$couponMoney = 0;
if ($couponList && $this->couponId) {
$couponMoney = $this->getCouponMoney($this->couponId, $couponList);
}
return [
'info' => $this->serverInfo,
'server_id' => $this->serverId,
'coupon_list' => $couponList,
'coupon_money' => $couponMoney,
'coupon_id' => $this->couponId,
'pay_price' => helper::number2($this->serverInfo['server_price'] - $couponMoney),
];
}
/**
* 当前用户可用的优惠券列表
* @return array
* @throws DbException
*/
private function getUserCouponList(): array
{
// 是否开启优惠券折扣
if (!$this->checkoutRule['isCoupon']) {
return [];
}
// 当前用户可用的优惠券列表
$couponList = UserCoupon::getUserCouponList($this->user['user_id'], $this->serverInfo['server_price'], TypeCase::SERVER);
// 判断当前优惠券是否满足订单使用条件 (优惠券适用范围)
return UserCoupon::couponListApplyRangeServer($couponList, [$this->serverId]);
}
/**
* 查找指定的优惠券信息
* @param array $couponList 优惠券列表
* @return false|mixed
*/
private function getCouponInfo(int $userCouponId, array $couponList)
{
return helper::getArrayItemByColumn($couponList, 'user_coupon_id', $userCouponId);
}
/**
* @notes:获取优惠金额
* @param int $userCouponId
* @param array $couponList
* @return float|int|mixed
* @author: wanghousheng
*/
private function getCouponMoney(int $userCouponId, array $couponList)
{
// 获取优惠券信息
$couponInfo = $this->getCouponInfo($userCouponId, $couponList);
$reducedMoney = $this->serverInfo['server_price'];
// 计算打折金额
if ($couponInfo['coupon_type'] == CouponTypeEnum::DISCOUNT) {
$reducePrice = $reducedMoney - ($reducedMoney * ($couponInfo['discount'] / 10));
} else {
$reducePrice = $couponInfo['reduce_price'];
}
$reducePrice = helper::number2($reducePrice);
// 优惠券最大允许抵扣到一分钱,所以此处判断抵扣金额大于等于订单金额时,减去一分钱
return ($reducePrice >= $reducedMoney) ? $reducedMoney - 1 : $reducePrice;
}
/**
* @notes:用户取消订单
* @param int $orderId
* @return bool
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public static function userCancelOrder(int $orderId): bool
{
$userId = UserService::getCurrentLoginUserId();
$where['order_id'] = $orderId;
if (UserService::isDealerMember()) {
return false;
}
if (UserService::isPlusMember() || UserService::isNormalMember()) {
$where['user_id'] = $userId;
}
$orderInfo = ServerOrder::detail($where);
if ($orderInfo->isEmpty()) {
throwError('订单信息不存在');
}
return self::cancelOrder($orderInfo->toArray());
}
/**
* @notes:派单
* @param int $orderId
* @param int $dealerId
* @return bool
* @throws BaseException
* @author: wanghousheng
*/
public static function userDispatchOrders(int $orderId, int $dealerId): bool
{
if (!UserService::isStore()) {
return false;
}
$orderInfo = ServerOrder::detail(['order_id' => $orderId]);
if ($orderInfo->isEmpty()) {
throwError('订单信息不存在');
}
return self::dispatchOrders($orderInfo->toArray(), $dealerId);
}
/**
* @notes:分销商工程师-派单人员列表
* @return array
* @throws BaseException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @author: wanghousheng
*/
public static function getEngineer(): array
{
if (UserService::isStore()) {
$list = UserModel::getEngineer();
if (!$list->isEmpty()) {
return $list->toArray();
}
}
return [];
}
}
Loading…
Cancel
Save