Files
xk-api-old-laravel/app/Http/Controllers/Member/OrderController.php
2025-01-11 14:06:19 +08:00

805 lines
28 KiB
PHP

<?php
namespace App\Http\Controllers\Member;
use App\Http\Resources\ProductOrderDetailResource;
use App\Http\Traits\ProductOrderTrait;
use App\Jobs\ProductOrderCancelJob;
use App\Models\Cart;
use App\Models\ExpressFee;
use App\Models\Pay\Bill;
use App\Models\YiiModels\Address;
use App\Models\YiiModels\Drug;
use App\Models\YiiModels\Prescription;
use App\Models\YiiModels\ProductOrder;
use App\Models\YiiModels\ProductOrderRefund;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Yansongda\LaravelPay\Facades\Pay;
/**
* 订单
*/
class OrderController extends MemberBaseController
{
use ProductOrderTrait;
/**
* 订单预览页面
*
* @return void
*/
public function preview(Request $request)
{
$this->toValidator([
'cart_ids' => 'nullable|string',
'drug_ids' => 'required|string',
], [], [
'cart_ids' => '购物车商品',
'drug_ids' => '商品',
]);
$cart_ids = string_to_array($request->input('cart_ids'));
$drug_ids = string_to_array($request->input('drug_ids'));
$numbers = string_to_array($request->input('numbers'));
$drugs = Drug::with([
'drugstoreDrug' => function (Relation $relation) {
$relation->select(['drug_id', 'price'])
->where('drugstore_id', $this->getDrugstoreId());
},
])
->select(['id', 'drug_name', 'usage', 'specification', 'image'])
->whereHasIn('drugStoreRelation', function (Builder $builder) {
$builder->where('store_id', $this->getStoreId());
})
->whereIn('id', $drug_ids)
->get()
->keyBy('id');
$carts = [];
if (count($cart_ids)) {
$carts = Cart::with([
'drug' => function (Relation $relation) {
$relation->select(['id']);
}
])
->whereHasIn('drug')
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->whereIn('id', $cart_ids)
->get()
->keyBy('drug.id');
}
$data = [];
$fee = 0;
$new_drug_ids = $new_cart_ids = [];
foreach ($drug_ids as $k => $drug_id) {
if (!isset($drugs[$drug_id])) {
json_error('商品信息已更新,请重新下单');
}
$drug = $drugs[$drug_id];
if (!$drug['drugstoreDrug']) {
json_error('商品信息已更新,请重新下单');
}
$new_drug_ids[] = $drug_id;
$drug['qty'] = $numbers[$k] ?? 1;
if (isset($carts[$drug_id])) {
$new_cart_ids[] = $carts[$drug_id]['id'];
$drug['qty'] = $carts[$drug_id]['qty'];
}
$fee += $drug['drugstoreDrug']['price'] * $drug['qty'];
$data[] = $drug;
}
$store = $this->getStore();
$expressFeeInfo = ExpressFee::getDefaultExpressFee();
if (!$expressFeeInfo) {
json_error('运费信息错误');
}
$isFree = $fee >= $expressFeeInfo->free_express_fee;
$express_fee = $isFree ? 0 : $expressFeeInfo->base_express_fee;
$fee_total = $isFree ? $fee : ($expressFeeInfo->base_express_fee + $fee);
json_success([
'store_name' => $store->name,
'fee' => $fee,
'is_free' => $isFree,
'express_fee' => $express_fee,
'fee_total' => $fee_total,
'data' => $data,
'drug_ids' => $new_drug_ids,
'cart_ids' => $new_cart_ids,
]);
}
/**
* 提交订单
*
* @param Request $request
* @return void
* @throws \Throwable
*/
public function submit(Request $request)
{
$this->toValidator([
'address_id' => 'required|integer',
'cart_ids' => 'nullable|string',
'drug_ids' => 'required|string',
'numbers' => 'required|string',
], [
'address_id' => '请选择地址',
], [
'cart_ids' => '购物车商品',
'drug_ids' => '商品',
'numbers' => '商品数量',
]);
$cart_ids = string_to_array($request->input('cart_ids'));
$drug_ids = string_to_array($request->input('drug_ids'));
$numbers = string_to_array($request->input('numbers'));
$address_id = $request->input('address_id');
$remark = $request->input('remark');
if (count($drug_ids) != count($numbers)) {
json_error('商品数量不正确');
}
$address = Address::where('user_id', $this->userId())
->where('id', $address_id)
->first();
if (!$address) {
json_error('用户地址获取失败');
}
$expressFeeInfo = ExpressFee::getDefaultExpressFee();
if (!$expressFeeInfo) {
json_error('运费信息错误');
}
$drugs = Drug::with([
'drugStoreRelation' => function (Relation $relation) {
$relation->where('store_id', $this->getStoreId());
},
'drugstoreDrug' => function (Relation $relation) {
$relation->where('drugstore_id', $this->getDrugstoreId());
},
])
->whereHasIn('drugStoreRelation', function (Builder $builder) {
$builder->where('store_id', $this->getStoreId());
})
->whereIn('id', $drug_ids)
->get();
/* @var Drug $drug */
foreach ($drugs as $k => $drug) {
if (!$drug->drugstoreDrug) {
if (!$drug['drugstoreDrug']) {
json_error('商品信息已更新,请重新下单');
}
if ($drug->drugstoreDrug->stock < $numbers[$k]) {
json_error("{$drug->drug_name}库存不足");
}
}
}
$order_no = Carbon::now()->format('YmdHis') . rand(10000, 99999) . rand(10000, 99999);
DB::beginTransaction();
try {
$productOrder = ProductOrder::create([
'store_id' => $this->getStoreId(),
'drugstore_id' => $this->getDrugstoreId(),
'user_id' => $this->userId(),
'order_no' => $order_no,
'order_type' => ProductOrder::ORDER_TYPE_SHOP,
'type' => ProductOrder::TYPE_WECHAT,
'is_pay' => ProductOrder::IS_PAY_FALSE,
'status' => ProductOrder::STATUS_UNPAID,
'pay_method' => ProductOrder::PAY_METHOD_ONLINE,
'remark' => $remark,
'address_id' => $address->id,
'express_name' => $address->name,
'express_mobile' => $address->mobile,
'express_region' => $address->region,
'express_address' => $address->detail_address,
]);
$productOrder->logs()->create([
'content' => '下单成功'
]);
$fee = 0;
/* @var Drug $drug */
foreach ($drugs as $k => $drug) {
$fee += $drug->drugstoreDrug->price * $numbers[$k];
$productOrder->items()->create([
'drug_id' => $drug->id,
'drug_image' => $drug->image,
'number' => $numbers[$k],
'price' => $drug->drugstoreDrug->price,
'drug_name' => $drug->drug_name,
'small_info' => $drug->small_info,
]);
$drug->drugstoreDrug->stock -= $numbers[$k];
$drug->drugstoreDrug->frozen_number += $numbers[$k];
$drug->drugstoreDrug->save();
}
$isFree = $fee >= $expressFeeInfo->free_express_fee;
$express_fee = $isFree ? 0 : $expressFeeInfo->base_express_fee;
$fee_total = $isFree ? $fee : ($expressFeeInfo->base_express_fee + $fee);
$productOrder->fill([
'items_price' => $fee,
'total_pay_price' => $fee_total,
'trans_expenses' => $express_fee,
'free_ship' => $isFree ? ProductOrder::FREE_SHIP_TRUE : ProductOrder::FREE_SHIP_FALSE,
])->update();
if (count($cart_ids)) {
Cart::where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->whereIn('id', $cart_ids)
->delete();
}
// // 同步数据
// $productOrder->refresh();
// $productOrder->load(['items']);
// Artisan::call('data:sync', [
// '--type' => 'product_order_sync',
// '--data' => $productOrder->toArray(),
// ]);
ProductOrderCancelJob::dispatch($productOrder->id)->delay(now()->addMinutes(15));
DB::commit();
} catch (Exception $exception) {
DB::rollBack();
log_s('下单错误:' . var_export($request->all(), true) . $exception->getMessage() . $exception->getTraceAsString(), 'order', 'error');
json_error('下单错误,请重试');
}
json_success('下单成功,请支付', [
'order_id' => $productOrder->id,
]);
}
/**
* 订单列表
*
* @return void
*/
public function lists(Request $request)
{
$this->toValidator([
'type' => 'nullable|string',
], [], [
'type' => '类型',
]);
$type = $request->input('type');
$keyword = $request->input('keyword');
$lists = ProductOrder::with([
'store:id,name',
'items',
'prescription:id,prescription_type',
])
->select([
'id', 'store_id', 'order_no', 'p_id', 'order_type', 'is_pay', 'total_pay_price', 'items_price', 'status',
'trans_expenses', 'cancel_status',
])
->when($type, function (Builder $builder) use ($type) {
if ($type == 'unpaid') {
$builder->where('status', ProductOrder::STATUS_UNPAID);
} elseif ($type == 'wait_send') {
$builder->where('status', ProductOrder::STATUS_WAIT_SEND);
} elseif ($type == 'wait_accept') {
$builder->where('status', ProductOrder::STATUS_WAIT_ACCEPT);
} elseif ($type == 'finished') {
$builder->whereIn('status', [
ProductOrder::STATUS_WAIT_COMMENT,
ProductOrder::STATUS_REFUND,
ProductOrder::STATUS_REFUNDING,
ProductOrder::STATUS_ACCEPTED,
ProductOrder::STATUS_CONFIRM,
ProductOrder::STATUS_CANCEL,
]);
}
})
->when($keyword, function (Builder $builder) use ($keyword) {
$builder->whereHasIn('items', fn(Builder $builder) => $builder->where('drug_name', 'like', "%$keyword%"));
})
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->latest()
->paginate(request('per_page'));
$lists->getCollection()->each(function (ProductOrder $productOrder) {
$productOrder->append(['status_string', 'cancel_status_string']);
if ($productOrder->prescription) {
$productOrder->prescription->append(['prescription_type_string']);
}
});
json_page($lists);
}
/**
* 订单详情
*
* @param Request $request
* @return void.
*/
public function detail(Request $request)
{
$this->toValidator([
'order_id' => 'required|integer',
], [], [
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
/* @var ProductOrder $productOrder */
$productOrder = ProductOrder::with([
'items',
'prescription:id,content,prescription_type',
])
->where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if ($productOrder->express_no_id && $productOrder->status == ProductOrder::STATUS_WAIT_ACCEPT) {
Artisan::call('express', [
'type' => 'update',
'--id' => $productOrder->express_no_id,
]);
$productOrder->load(['expressNo.detail']);
}
if (
$productOrder->prescription
&& in_array($productOrder->prescription->prescription_type, [Prescription::CHINESE_MEDICINES_DECOCTION_PIECE, Prescription::DISPENSING_GRANULES]
)) {
json_success(new ProductOrderDetailResource($productOrder));
} else {
json_success(new ProductOrderDetailResource($productOrder));
}
}
/**
* 物流详情
*
* @param Request $request
* @return void.
*/
public function expressDetail(Request $request)
{
$this->toValidator([
'order_id' => 'required|integer',
], [], [
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
$productOrder = ProductOrder::where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if (!$productOrder->express_no_id) {
json_error('无物流信息');
}
Artisan::call('express', [
'type' => 'update',
'--id' => $productOrder->express_no_id,
]);
$productOrder->load(['expressNo.details']);
if (!$productOrder->expressNo) {
json_error('无物流信息');
}
json_success([
'address' => $productOrder->only([
'express_name',
'express_mobile',
'express_region',
'express_address',
'express_no_id',
]),
'express' => $productOrder->expressNo,
]);
}
/**
* 获取支付
*
* @return void
*/
public function getPay(Request $request)
{
$this->toValidator([
'order_id' => 'required|integer',
], [], [
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
$productOrder = ProductOrder::where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if (!$productOrder->express_name) {
json_error('请选择收货地址');
}
if ($productOrder->is_pay == ProductOrder::IS_PAY_TRUE) {
json_error('订单已支付');
}
if ($productOrder->status != ProductOrder::STATUS_UNPAID) {
json_error('支付失败,订单状态为' . $productOrder->status_string);
}
/* @var Bill $bill */
$bill = $productOrder->bills()->create([
'user_id' => 1,
'openid' => $this->user()->openid,
'title' => $this->getStore()->name . "订单支付",
'pay_way' => Bill::PAY_WAY_WECHAT,
'pay_amount' => $productOrder->total_pay_price,
]);
// 开发测试用
if (app()->isLocal() && file_exists(base_path('./../dev.ini'))) {
$order = [
'out_trade_no' => $bill->pay_no,
'description' => $bill->title,
'amount' => [
'total' => $bill->pay_amount * 100,
],
];
$result = Pay::wechat()->scan($order);
if (isset($result['code'])) {
json_error($result['message'] ?? '');
}
json_success($result->toArray());
}
$order = [
'out_trade_no' => $bill->pay_no,
'description' => $bill->title,
'amount' => [
'total' => $bill->pay_amount * 100,
],
'payer' => [
'openid' => $bill->openid,
]
];
$result = Pay::wechat()->mini($order);
if (isset($result['code'])) {
json_error($result['message'] ?? '');
}
json_success($result->toArray());
}
/**
* 取消订单
*
* @param Request $request
* @return void
* @throws \Throwable
*/
public function cancel(Request $request)
{
$this->toValidator([
'order_id' => 'required|integer',
], [], [
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
$productOrder = ProductOrder::where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if ($productOrder->is_pay == ProductOrder::IS_PAY_TRUE) {
json_error('订单已支付');
}
if ($productOrder->status != ProductOrder::STATUS_UNPAID) {
json_error('取消失败,订单状态为' . $productOrder->status_string);
}
DB::beginTransaction();
try {
$productOrder->status = ProductOrder::STATUS_CANCEL;
$productOrder->cancel_status = ProductOrder::CANCEL_STATUS_FALSE;
$productOrder->cancel_time = time();
$productOrder->cancel_remark = '用户主动取消';
$productOrder->save();
$productOrder->logs()->create([
'content' => '未支付用户取消订单'
]);
$this->createNotice($productOrder, '您的订单已取消');
$productOrder->loadMissing(['items.drugstoreDrug' => fn(Relation $relation) => $relation->where('drugstore_id', $productOrder->drugstore_id)]);
foreach ($productOrder->items as $item) {
if ($item->drugstoreDrug) {
$item->drugstoreDrug->stock += $item->number;
$item->drugstoreDrug->frozen_number -= $item->number;
$item->drugstoreDrug->save();
}
}
// // 同步数据
// $productOrder->refresh();
// $productOrder->load(['items']);
// Artisan::call('data:sync', [
// '--type' => 'product_order_sync',
// '--data' => $productOrder->toArray(),
// ]);
if ($productOrder->is_online == ProductOrder::IS_ONLINE_TRUE && $productOrder->sync_order_no) {
// 同步数据(老)
Artisan::call('data:sync-old', [
'--type' => 'syncOrder',
'--data' => json_encode([
'order_no' => $productOrder->sync_order_no,
'status' => 2, // 1已支付 2已取消 3已退款
'time' => Carbon::createFromTimestamp($productOrder->cancel_time)->toDateTimeString(),
]),
]);
}
DB::commit();
} catch (Exception $exception) {
DB::rollBack();
log_s('取消错误:' . var_export($request->all(), true) . $exception->getMessage() . $exception->getTraceAsString(), 'order', 'error');
json_error('取消错误,请重试');
}
json_success('取消成功');
}
/**
* 确认收货
*
* @param Request $request
* @return void
* @throws \Throwable
*/
public function confirm(Request $request)
{
$this->toValidator([
'order_id' => 'required|integer',
], [], [
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
/* @var ProductOrder $productOrder */
$productOrder = ProductOrder::where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if ($productOrder->status != ProductOrder::STATUS_WAIT_ACCEPT) {
json_error('确认收货失败,订单状态为' . $productOrder->status_string);
}
DB::beginTransaction();
try {
$productOrder->status = ProductOrder::STATUS_CONFIRM;
$productOrder->save();
$productOrder->logs()->create([
'content' => '用户确认收货'
]);
$this->createNotice($productOrder, '您的订单已确认收货');
$productOrder->loadMissing(['items.drugstoreDrug' => fn(Relation $relation) => $relation->where('drugstore_id', $productOrder->drugstore_id)]);
foreach ($productOrder->items as $item) {
if ($item->drugstoreDrug) {
$item->drugstoreDrug->frozen_number -= $item->number;
$item->drugstoreDrug->save();
}
}
// // 同步数据
// $productOrder->refresh();
// $productOrder->load(['items']);
// Artisan::call('data:sync', [
// '--type' => 'product_order_sync',
// '--data' => $productOrder->toArray(),
// ]);
if ($productOrder->order_type === 5) {
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.xiaokang88.com/open-api/client-confirm', [
'json' => [
'order_id' => $order_id,
]
]);
$body = json_decode($response->getBody()->getContents(), true);
if ($body['code'] != 0) {
exit($body['message']);
}
}
DB::commit();
} catch (Exception $exception) {
DB::rollBack();
log_s('确认收货错误:' . var_export($request->all(), true) . $exception->getMessage() . $exception->getTraceAsString(), 'order', 'error');
json_error('确认收货错误,请重试');
}
json_success('确认收货成功');
}
/**
* 退款基础信息
*
* @return void
*/
public function refundBase(Request $request)
{
$this->toValidator([
'order_id' => 'required|integer',
], [], [
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
$productOrder = ProductOrder::where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if (!in_array($productOrder->status, [
ProductOrder::STATUS_WAIT_SEND,
ProductOrder::STATUS_WAIT_ACCEPT,
ProductOrder::STATUS_WAIT_COMMENT,
ProductOrder::STATUS_ACCEPTED,
ProductOrder::STATUS_CONFIRM,
])) {
json_error('申请失败,订单状态为' . $productOrder->status_string);
}
$reasons = pluck_to_array(ProductOrderRefund::REASON);
$refundTypes = pluck_to_array(ProductOrderRefund::REFUND_TYPE);
json_success([
'total_pay_price' => $productOrder->total_pay_price,
'trans_expenses' => $productOrder->trans_expenses,
'reasons' => $reasons,
'refund_types' => $refundTypes,
]);
}
/**
* 申请退款
*
* @param Request $request
* @return void
* @throws \Throwable
*/
public function refundSubmit(Request $request)
{
$this->toValidator([
'refund_type' => 'required|integer',
'reason_id' => 'required|integer',
'order_id' => 'required|integer',
], [], [
'refund_type' => '退款类型',
'reason_id' => '退款原因',
'order_id' => '订单号',
]);
$order_id = $request->input('order_id');
$refund_type = $request->input('refund_type');
$reason_id = $request->input('reason_id');
$image = $request->input('image');
/* @var ProductOrder $productOrder */
$productOrder = ProductOrder::where('id', $order_id)
->where('store_id', $this->getStoreId())
->where('user_id', $this->userId())
->first();
if (!$productOrder) {
json_error('订单信息不存在');
}
if (!in_array($productOrder->status, [
ProductOrder::STATUS_WAIT_SEND,
ProductOrder::STATUS_WAIT_ACCEPT,
ProductOrder::STATUS_WAIT_COMMENT,
ProductOrder::STATUS_ACCEPTED,
ProductOrder::STATUS_CONFIRM,
])) {
json_error('申请失败,订单状态为' . $productOrder->status_string);
}
if ($productOrder->is_pay != ProductOrder::IS_PAY_TRUE) {
json_error('申请失败,订单尚未支付');
}
if ($productOrder->cancel_status == ProductOrder::CANCEL_STATUS_TRUE) {
json_error('申请失败,订单已取消');
}
if (!in_array($productOrder->refund_status, [
ProductOrder::REFUND_STATUS_NORMAL,
ProductOrder::REFUND_STATUS_CANCEL,
])) {
json_error('申请失败,订单退款状态为' . $productOrder->refund_status_string);
}
if (in_array($productOrder->status, [
ProductOrder::STATUS_WAIT_ACCEPT,
ProductOrder::STATUS_WAIT_COMMENT,
ProductOrder::STATUS_ACCEPTED,
ProductOrder::STATUS_CONFIRM,
])) {
if (now()->diffInDays($productOrder->created_at) > 30) {
json_error('申请失败,已超过最大申请时效');
}
}
DB::beginTransaction();
$refundResult = [];
try {
$beforeStatus = $productOrder->status;
$productOrder->status = ProductOrder::STATUS_REFUNDING;
$productOrder->refund_status = $beforeStatus == ProductOrder::STATUS_WAIT_SEND ? ProductOrder::REFUND_STATUS_AGREE : ProductOrder::REFUND_STATUS_APPLYING;
$productOrder->save();
$productOrder->logs()->create([
'content' => '用户申请退款'
]);
/* @var ProductOrderRefund $orderRefund */
$orderRefund = $productOrder->refunds()->create([
'user_id' => $productOrder->user_id,
'order_id' => $productOrder->id,
'reason' => ProductOrderRefund::REASON[$reason_id],
'refund_images' => $image,
'refund_type' => ProductOrder::STATUS_WAIT_SEND ? ProductOrderRefund::REFUND_TYPE_REFUND_ONLY : ProductOrderRefund::REFUND_TYPE[$refund_type],
'refund_no' => ProductOrderRefund::getNewNumber('refund_no'),
'refund_price' => $productOrder->total_pay_price,
'is_refund' => ProductOrderRefund::IS_REFUND_FALSE,
'status' => $beforeStatus == ProductOrder::STATUS_WAIT_SEND ? ProductOrderRefund::STATUS_PASS : ProductOrderRefund::STATUS_APPLY,
]);
if ($beforeStatus == ProductOrder::STATUS_WAIT_SEND) {
$refundResult = $productOrder->toRefund($productOrder->total_pay_price, $orderRefund->refund_no);
}
DB::commit();
} catch (Exception $exception) {
DB::rollBack();
log_s('申请错误:' . var_export($request->all(), true) . $exception->getMessage() . $exception->getTraceAsString(), 'order', 'error');
json_error('申请错误,请重试');
}
if (count($refundResult)) {
response()->json($refundResult)->throwResponse();
}
json_success('申请成功');
}
}