Files
code-bang/backend/application/common/service/OrderService.php
T
2026-09-21 14:57:50 +08:00

563 lines
23 KiB
PHP
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
namespace app\common\service;
use think\Cache;
use think\Db;
use think\Log;
use app\common\model\Orders;
use app\common\traits\ServiceTrait;
use think\Exception;
class OrderService
{
use ServiceTrait;
/** @var Orders */
public static $Model = Orders::class;
const LOCK_ORDER_PREFIX = 'lock_order_cache:'; //重复创建订单锁
//订单状态
const ORDER_STATUS_NO = 0; //关闭
const ORDER_STATUS_WAIT_PAY = 10; //待付款
const ORDER_STATUS_WAIT_SHIP = 20; //已支付(待发货)
const ORDER_STATUS_SHIPPED = 30; //已发货(待收货)
const ORDER_STATUS_FAILED = 31; //交易失败
const ORDER_STATUS_TIMEOUT = 33; //订单超时
const ORDER_STATUS_FINISHED = 40; //已完成
const ORDER_STATUS_CANCEL = 50; //已取消
const ORDER_STATUS_REFUNDED = 60; //已退款
const ORDER_STATUS_MAP = [
self::ORDER_STATUS_NO => '订单关闭',
self::ORDER_STATUS_WAIT_PAY => '待支付',
self::ORDER_STATUS_WAIT_SHIP => '待发货',
self::ORDER_STATUS_SHIPPED => '已发货',
self::ORDER_STATUS_FAILED => '交易失败',
self::ORDER_STATUS_TIMEOUT => '订单超时',
self::ORDER_STATUS_CANCEL => '用户取消订单',
self::ORDER_STATUS_FINISHED => '交易完成',
self::ORDER_STATUS_REFUNDED => '已退款',
];
// 订单操作按钮
const BUTTON_PAY = 1; // 付款
const BUTTON_CANCEL = 2; // 取消订单
const BUTTON_CONFIRM = 3; // 确认收货
const BUTTON_APPLY_REFUND = 4; // 申请售后
const BUTTON_VIEW_REFUND = 5; // 查看退款
const BUTTON_COMMENT = 6; // 评价
const BUTTON_DELETE = 7; // 删除订单
const BUTTON_DELIVERY = 8; // 查看物流
const BUTTON_REBUY = 9; // 再次购买
const BUTTON_INVITE_GROUPON = 11; //邀请好友拼团
const BUTTON_MAP = [
self::BUTTON_PAY => '付款', // 付款
self::BUTTON_CANCEL => '取消订单', // 取消订单
self::BUTTON_CONFIRM => '确认收货', // 确认收货
self::BUTTON_APPLY_REFUND => '申请售后', // 申请售后
self::BUTTON_VIEW_REFUND => '查看退款', // 查看退款
self::BUTTON_COMMENT => '评价', // 评价
self::BUTTON_DELETE => '删除订单', // 删除订单
self::BUTTON_DELIVERY => '查看物流', // 查看物流
self::BUTTON_REBUY => '再次购买', // 再次购买
self::BUTTON_INVITE_GROUPON => '邀请好友拼团', //邀请好友拼团
];
const ORDER_TYPE_SOFTWARE = 0;//软件产品;
const ORDER_TYPE_FIRST_PAYMENT = 1;//首付款款订单;
const ORDER_TYPE_FINAL_PAYMENT = 2; //尾款订单;
const ORDER_TYPE_MAP = [
self::ORDER_TYPE_SOFTWARE => '软件产品',
self::ORDER_TYPE_FIRST_PAYMENT => '需求首付款',
self::ORDER_TYPE_FINAL_PAYMENT => '需求尾款',
];
/**
* 生成订单
* @param $requestData
* @return array
* @throws \Exception
*/
public static function createOrder($requestData)
{
try {
$user = $requestData['userInfo'] ?? [];
$userId = $user['id'] ?? 0;
$openid = $user['openid'] ?? '';
Log::info('创建订单数据-request:' . json_encode($requestData, JSON_UNESCAPED_UNICODE));
if (empty($requestData['goodsRequestList'])) {
throw new \Exception('订单数据错误');
}
//如果30秒内有重复数据,直接返回缓存订单,不再创建
$lockOrderKey = self::getLockOrderKey($userId, $requestData);
if ($lockOrderRes = Cache::get($lockOrderKey)) {
return json_decode($lockOrderRes, true);
}
$goodsRequestList = $requestData['goodsRequestList'];
$count = count($goodsRequestList);
if ($count == 0) {
Log::error('订单创建失败:商品数量不能为0');
throw new \Exception('订单创建失败:商品数量不能为0');
}
$total_amount = 0;
$total_quantity = 0;
$goodsDataList = [];
foreach ($goodsRequestList as $item) {
$quantity = max(1, $item['quantity']);
$total_quantity += $quantity;
$total_amount += $quantity * $item['price'];
$goodsDataList[] = [
'spu_id' => $item['spuId'],
'sku_id' => $item['sku_id'] ?? '',
'goods_name' => $item['goodsName'],
'goods_picture_url' => $item['primaryImage'],
'origin_price' => $item['price'],
'actual_price' => $item['price'],
'buy_quantity' => $quantity,
'item_total_amount' => $item['price'] * $quantity,
'item_discount_amount' => 0,
'item_payment_amount' => $item['price'] * $quantity,
'goods_payment_price' => $item['price'],
];
}
$orderData = [
'user_id' => $userId,
'store_id' => 1,
'order_no' => generate_order_sn(),
'order_status' => OrderService::ORDER_STATUS_WAIT_PAY,
'order_type' => 0,
'total_amount' => $total_amount,
'payment_amount' => $total_amount,
'goods_amount' => $total_quantity,
'discount_amount' => 0,
'remark' => $requestData['storeInfoList'][0]['remark'] ?? '',
];
$existed = OrderService::getOne(['order_no' => $orderData['order_no']]);
if (!$existed) {
//创建订单及订单商品
$orderRes = self::$Model::create($orderData);
Log::info('创建订单数据:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
if (!$orderRes) {
Log::error('创建订单创建失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('订单创建失败');
}
$goodsRes = $orderRes->orderGoods()->saveAll($goodsDataList);
if (!$goodsRes) {
Log::error('创建订单产品失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('订单创建数据失败');
}
}
$orderData += [
'channel' => 'wechat',
'tradeNo' => $orderData['order_no'],
'interactId' => 0,
'transactionId' => 0,
'payInfo' => self::pay($orderData['order_no'], $total_amount, $openid), //统一下单接口
];
//存入缓存
Cache::set($lockOrderKey, json_encode($orderData), 30);
return $orderData;
} catch (Exception $e) {
throw new \Exception('订单数据保存失败:' . $e->getMessage());
}
}
/**
* 生成需求订单
* @param $requestData
* @throws \Exception
*/
public static function createDemandOrder($requestData)
{
try {
$demandId = $requestData['demandId'] ?? 0;
$formData = $requestData['formData'] ?? [];
$totalCost = $formData['totalCost'] ?? 0;
Log::info('创建需求订单数据-request:' . json_encode($requestData, JSON_UNESCAPED_UNICODE));
$demand = DemandService::getOne(['id' => $demandId]);
if (!$demand) {
throw new \Exception('该需求不存在');
}
$winBid = DemandBiddingService::getOne(['demand_id' => $demandId, 'bidding_status' => DemandBiddingService::STATUS_BIDDING_WIN]);
$orderInfo = self::where(['parent_order_no' => $demand->parent_order_no])->find();
$ratio = 0.5; //分期比率50%
if (!$orderInfo) {
$installments = 2;//分期数
for ($i = 1; $i <= $installments; $i++) {
$total_amount = $i == 1 ? $totalCost * $ratio : $totalCost * (1 - $ratio);
$goodsDataList = [[
'spu_id' => 0,
'sku_id' => 0,
'goods_name' => '需求签约-' . ($i == 1 ? '首付款' : '尾款') . $demand->parent_order_no,
'goods_picture_url' => 'https://soft-market.oss-cn-shenzhen.aliyuncs.com/images/contract.png',
'origin_price' => $total_amount,
'actual_price' => $total_amount,
'buy_quantity' => 1,
'item_total_amount' => $total_amount,
'item_discount_amount' => 0,
'item_payment_amount' => $total_amount,
'goods_payment_price' => $total_amount,
]];
$orderData = [
'user_id' => $demand->user_id,
'store_id' => 1,
'parent_order_no' => $demand->parent_order_no,
'order_no' => generate_order_sn(),
'order_status' => OrderService::ORDER_STATUS_WAIT_PAY,
'order_type' => $i,
'total_amount' => $total_amount,
'payment_amount' => $total_amount,
'goods_amount' => $total_amount,
'discount_amount' => 0,
'remark' => '',
];
$existed = OrderService::getOne(['order_no' => $orderData['order_no']]);
if (!$existed) {
//创建订单及订单商品
$orderRes = self::$Model::create($orderData);
Log::info('创建需求订单数据:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
if (!$orderRes) {
Log::error('创建需求订单创建失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('需求订单创建失败');
}
$goodsRes = $orderRes->orderGoods()->saveAll($goodsDataList);
if (!$goodsRes) {
Log::error('创建需求订单产品失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('需求订单创建数据失败');
}
}
}
}
//查找签约记录,没有则创建
$contractInfo = ContractService::getOne(['demand_id' => $demand, 'bidding_id' => $winBid->id ?? 0]);
if (!$contractInfo) {
//创建合同数据
$firstContract = ContractService::getFirstContract($demandId);
$depositAmount = $firstContract ? $firstContract->deposit_amount : $formData['totalCost'] * $ratio;
ContractService::createData($demandId, $winBid->id ?? 0, $depositAmount, $formData);
}
return $contractInfo;
} catch (Exception $e) {
throw new \Exception('需求订单数据保存失败:' . $e->getMessage());
}
}
public static function getDemandOrder($parentOrderNo, $contractType)
{
$res = self::getOne(['parent_order_no' => $parentOrderNo, 'order_type' => $contractType], ['orderGoods', 'orderPayment']);
return $res;
}
public static function orderPay($requestData)
{
try {
$user = $requestData['userInfo'] ?? [];
$userId = $user['id'] ?? 0;
$openid = $user['openid'] ?? '';
$orderNo = $requestData['orderNo'] ?? '';
if (!$orderNo) {
throw new \Exception('订单号不存在');
}
$fields = 'user_id,store_id,order_no,order_status,order_type,total_amount,payment_amount,goods_amount,discount_amount,remark';
$orderInfo = OrderService::where(['order_no' => $orderNo, 'user_id' => $userId])->field($fields)->find();
if (!$orderInfo) {
throw new \Exception('该订单不存在');
}
$orderData = $orderInfo->toArray();
$orderData += [
'channel' => 'wechat',
'tradeNo' => $orderData['order_no'],
'interactId' => 1,
'transactionId' => 0,
'payInfo' => self::pay($orderInfo->order_no, $orderInfo->total_amount, $openid), //统一下单接口
];
return $orderData;
} catch (Exception $e) {
throw new \Exception('订单支付异常:' . $e->getMessage());
}
}
/**
* 订单操作
* @param $request
* @return mixed
* @throws \Exception
*/
public static function orderOperate($requestData, $userId)
{
$goodsInfo = $requestData['goodsInfo'] ?? [];
$type = $requestData['type'] ?? '';
switch ($type) {
case self::BUTTON_CONFIRM:
$res = self::confirmOrder($goodsInfo['id'] ?? 0, $userId);
break;
case self::BUTTON_CANCEL:
$res = self::cancelOrder($goodsInfo['id'] ?? 0, $userId);
break;
default:
throw new \Exception('非法操作');
}
return $res;
}
/**
* 取消订单
* @param $order_id
* @param $userId
* @return mixed
* @throws \Exception
*/
public static function cancelOrder($orderId, $userId = 0)
{
try {
$where = ['id' => $orderId, 'user_id' => $userId];
$order = self::getOne($where);
if (!$order) {
throw new \Exception('该订单不存在');
}
Db::startTrans();
if (!in_array($order->order_status, [OrderService::ORDER_STATUS_WAIT_PAY, OrderService::ORDER_STATUS_WAIT_SHIP])) {
throw new \Exception('该订单为' . OrderService::ORDER_STATUS_MAP[$order->order_status] . ',不能取消');
}
//修改订单状态
$upRes = self::where($where)->update(['order_status' => OrderService::ORDER_STATUS_CANCEL]);
if (!$upRes) {
throw new \Exception('更新订单状态失败');
}
//退款原路返回
// OrderRefund::dispatch(['order_sn' => $order->order_sn, 'actual_amount' => $order->actual_amount]);
//给用户发送出票失败结果通知
// SendMsg::dispatch(['order_id' => $order->id, 'user_id' => $order->user_id, 'template_id' => SubscribeMessageService::MSG_TEMP_ORDER_FAILED, 'desc' => SubscribeMessageService::ORDER_MSG_MAP['failed'], 'desc2' => SubscribeMessageService::ORDER_MSG_MAP['refund']]);
Db::commit();
$info = OrderService::getOne(['id' => $orderId]);
return $info;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
/**
* 确认收货
* @param $orderId
* @param $userId
* @return mixed
* @throws \Exception
*/
public static function confirmOrder($orderId, $userId = 0)
{
try {
$where = ['id' => $orderId, 'user_id' => $userId];
$order = self::getOne($where);
if (!$order) {
throw new \Exception('该订单不存在');
}
Db::startTrans();
if (!in_array($order->order_status, [OrderService::ORDER_STATUS_SHIPPED])) {
throw new \Exception('该订单为' . OrderService::ORDER_STATUS_MAP[$order->order_status] . ',不能确认收货');
}
//修改订单状态
$upRes = self::where($where)->update(['order_status' => OrderService::ORDER_STATUS_FINISHED]);
if (!$upRes) {
throw new \Exception('更新订单状态失败');
}
Db::commit();
$info = OrderService::getOne(['id' => $orderId]);
return $info;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
/**
* 微信支付回调(EasyWeChat 6.x / 支付 v3: 自动验签+解密)
* @return \Psr\Http\Message\ResponseInterface
* @throws \Exception
*/
public static function payNotify()
{
$service = new EasyWeChatService();
return $service->handlePayNotify(function (PayMessage $message) {
Log::info('微信回调信息_request:' . json_encode($message->getOriginalAttributes() ?? [], JSON_UNESCAPED_UNICODE));
$order_no = $message->out_trade_no ?? '';
$orderInfo = OrderService::getOne(['order_no' => $order_no]);
if (!$orderInfo) {
throw new \Exception('获取订单数据失败');
}
//金额校验(v3 通知金额单位为分)
$total = $message->amount['total'] ?? 0;
$checkMoney = sprintf('%.1f', $total / 100) == sprintf('%.1f', $orderInfo->payment_amount);
if (!$checkMoney) {
throw new \Exception('金额错误:' . sprintf('%.2f', $total / 100) . '----' . sprintf('%.2f', $orderInfo->payment_amount));
}
if (($message->trade_state ?? '') != 'SUCCESS') {
throw new \Exception('支付失败:' . ($message->trade_state ?? ''));
}
Log::info('微信回调信息_result:' . json_encode(['order_no' => $order_no, 'trade_state' => $message->trade_state], JSON_UNESCAPED_UNICODE));
//如果为待支付,才可更新为已支付。防止重复更新状态,导致重复发货。
if ($orderInfo->order_status == self::ORDER_STATUS_WAIT_PAY) {
$paymentData = [
'order_no' => $order_no,
'pay_status' => 'SUCCESS',
'amount' => $total / 100,
'currency' => $message->amount['currency'] ?? 'CNY',
'pay_type' => '1', // 1微信 2支付宝
'pay_way' => $message->trade_type ?? 'JSAPI',
'trace_no' => $message->transaction_id ?? '',
'pay_time' => time(),
'pay_success_time' => strtotime((string)($message->success_time ?? '')) ?: time(),
];
Db::transaction(function () use ($order_no, $paymentData, $orderInfo) {
self::where('order_no', $order_no)->update(['order_status' => self::ORDER_STATUS_WAIT_SHIP]);
OrderPaymentService::create($paymentData);
if ($orderInfo->order_type == 1) {
DemandService::customerContractFinishedDeal($orderInfo->parent_order_no);
} elseif ($orderInfo->order_type == 2) {
DemandService::customerWorkAcceptFinishedDeal($orderInfo->parent_order_no);
}
});
}
});
}
/**
* 获取缓存
* @param $userId
* @param $data array
* @return mixed
*/
public static function getLockOrderKey($userId, $data)
{
$key = self::LOCK_ORDER_PREFIX . md5($userId . json_encode($data));
return $key;
}
/**
* @param $order_sn
* @param $total_fee
* @param $openid
* @param string $body
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \Exception
*/
public static function pay($order_sn, $total_fee, $openid, string $body = '软件产品服务')
{
if (!($body && $order_sn && $total_fee && $openid)) {
throw new \Exception('支付参数错误2');
}
$EasyWeChatService = new EasyWeChatService();
$payRes = $EasyWeChatService->unify($body, $order_sn, $total_fee, $openid);
if (!($payRes && !empty($payRes['prepay_id']))) {
throw new \Exception('统一下单接口调用失败:' . ($payRes['message'] ?? ($payRes['errmsg'] ?? '未知错误')));
}
$payData = $EasyWeChatService->sdkConfig($payRes['prepay_id']);
return $payData;
}
/**
* 获取订单操作按钮
* @param $orderStatus
* @return array
*/
public static function getOrderButtons($orderStatus): array
{
$buttons = [];
if ($orderStatus == self::ORDER_STATUS_WAIT_PAY) {
$buttons[] = ['name' => self::BUTTON_MAP[OrderService::BUTTON_CANCEL], 'primary' => false, 'type' => OrderService::BUTTON_CANCEL];
$buttons[] = ['name' => self::BUTTON_MAP[OrderService::BUTTON_PAY], 'primary' => true, 'type' => OrderService::BUTTON_PAY];
}
if ($orderStatus == self::ORDER_STATUS_SHIPPED) {
$buttons[] = ['name' => self::BUTTON_MAP[OrderService::BUTTON_CONFIRM], 'primary' => true, 'type' => OrderService::BUTTON_CONFIRM];
}
return $buttons;
}
/**
* 获取订单统计
* @param $userId
* @return array[]
*/
public static function getOrderCount($userId): array
{
$orderCountList = [
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_WAIT_PAY],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_WAIT_SHIP],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_SHIPPED],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_FINISHED],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_REFUNDED],
];
$countList = self::where(['user_id' => $userId, 'order_type' => self::ORDER_TYPE_SOFTWARE])->field('order_status, COUNT(*) as total')
->group('order_status')
->select();
foreach ($orderCountList as &$value) {
if ($value['tabType'] == OrderService::ORDER_STATUS_FINISHED) {
continue;
}
foreach ($countList as $item) {
if ($item->order_status == $value['tabType']) {
$value['orderNum'] = $item->total;
}
}
}
return $orderCountList;
}
/**
* 订单发货
* @param $id
* @return void
* @throws \Exception
*/
public static function orderShip($id)
{
Db::transaction(function () use ($id) {
$order = OrderService::getOne(['id' => $id], ['orderGoods']);
if ($order->order_status != OrderService::ORDER_STATUS_WAIT_SHIP) {
exception('订单状态异常,不能发货');
}
//查询激活码
$code = SoftCodeService::getAvailableCode();
//生成发货单
OrderShipService::createData($order->order_no, $order->orderGoods[0]->spu_id, $code);
$order->order_status = OrderService::ORDER_STATUS_SHIPPED;
//修改发货状态
$saveRes = $order->save();
if (!$saveRes) {
exception('更新发货状态失败');
}
//发送订单发货通知
$msgData = [
'values' => [
$order->order_no,
mb_substr($order->orderGoods[0]->goods_name, 0, 20),
'已发货',
SubscribeMessageService::MSG_MAP[SubscribeMessageService::MSG_TEMP_ORDER_SHIPPED]['remark'],
]
];
(new EasyWeChatService())->sendMsg($order->user_id, SubscribeMessageService::MSG_TEMP_ORDER_SHIPPED, $msgData);
});
}
}