243 lines
9.1 KiB
PHP
243 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Order;
|
|
|
|
use App\Exceptions\RepositoryException;
|
|
use App\Models\NoticeModel;
|
|
use App\Models\ProductModel;
|
|
use App\Models\StoreOrderItemModel;
|
|
use App\Models\StoreOrderModel;
|
|
use App\Models\UserModel;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Modules\AnnoRoute\Attribute\GetRoute;
|
|
use Modules\AnnoRoute\Attribute\PutRoute;
|
|
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
|
use Modules\Common\Http\Controllers\BaseController;
|
|
|
|
/**
|
|
* 门店订单管理(订单只读 + 状态管理;创建/取消在小程序端)
|
|
*/
|
|
#[RequestAttribute('/order/store', 'order.store')]
|
|
class StoreOrderController extends BaseController
|
|
{
|
|
/** 状态中文名(通知文案用) */
|
|
private const array STATUS_NAMES = [
|
|
StoreOrderModel::STATUS_PENDING => '待接单',
|
|
StoreOrderModel::STATUS_SUMMARIZED => '已接单',
|
|
StoreOrderModel::STATUS_DELIVERING => '采购中',
|
|
StoreOrderModel::STATUS_DISTRIBUTION => '配送中',
|
|
StoreOrderModel::STATUS_COMPLETED => '已完成',
|
|
StoreOrderModel::STATUS_CANCELLED => '已取消',
|
|
];
|
|
|
|
protected array $searchField = [
|
|
'store_id' => '=',
|
|
'status' => '=',
|
|
'order_no' => 'like',
|
|
'order_date' => 'betweenDate',
|
|
];
|
|
|
|
/** 订单列表 */
|
|
#[GetRoute(authorize: 'query')]
|
|
public function query(Request $request): JsonResponse
|
|
{
|
|
$params = $request->all();
|
|
$pageSize = $params['pageSize'] ?? 10;
|
|
$query = StoreOrderModel::query()->with([
|
|
'store:id,name,address,contact,phone',
|
|
'items:id,order_id,product_id,product_name,product_spec,price,quantity,amount',
|
|
'items.product'
|
|
]);
|
|
$data = $this->buildSearch($params, $query)
|
|
->orderBy('order_date', 'desc')
|
|
->orderBy('id', 'desc')
|
|
->paginate($pageSize)
|
|
->toArray();
|
|
$box_amount = site_config('services.box_amount');
|
|
$tray_amount = site_config('services.tray_amount');
|
|
foreach ($data['data'] as &$item) {
|
|
$item['box_price'] = number_format($box_amount, 2);
|
|
$item['tray_price'] = number_format($tray_amount, 2);
|
|
$item['box_amount'] = number_format($box_amount * $item['box_num'], 2);
|
|
$item['tray_amount'] = number_format($tray_amount * $item['tray_num'], 2);
|
|
if(count($item['items']) > 0) {
|
|
foreach ($item['items'] as &$product) {
|
|
if($product['product'] && $product['product']['images_arr'] > 0) {
|
|
$product['image'] = $product['product']['images_arr'][0]['preview_url'] ?? '';
|
|
unset($product['product']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $this->success($data);
|
|
}
|
|
|
|
/**
|
|
* 待汇总预览:聚合所有待汇总订单明细(按商品分组),
|
|
* 供生成采购单前确认(C1 前置)
|
|
*/
|
|
#[GetRoute('/summary', 'query')]
|
|
public function summary(): JsonResponse
|
|
{
|
|
$rows = StoreOrderItemModel::query()
|
|
->select('product_id')
|
|
->selectRaw('MAX(product_name) as product_name')
|
|
->selectRaw('MAX(product_spec) as product_spec')
|
|
->selectRaw('SUM(quantity) as total_quantity')
|
|
->selectRaw('COUNT(DISTINCT store_id) as store_count')
|
|
->whereHas('order', function ($query) {
|
|
$query->where('status', StoreOrderModel::STATUS_PENDING);
|
|
})
|
|
->groupBy('product_id')
|
|
->orderBy('product_id')
|
|
->get()
|
|
->toArray();
|
|
|
|
// 补充计价单位(商品档案,含已下架/软删除)
|
|
$units = ProductModel::withTrashed()
|
|
->whereIn('id', array_column($rows, 'product_id'))
|
|
->pluck('unit', 'id');
|
|
foreach ($rows as &$row) {
|
|
$row['unit'] = $units[$row['product_id']] ?? '';
|
|
}
|
|
|
|
return $this->success($rows);
|
|
}
|
|
|
|
/** 订单详情:订单头 + 明细(含商品快照) */
|
|
#[GetRoute(route: '/{id}', authorize: 'query', where: ['id' => '[0-9]+'])]
|
|
public function detail(int $id): JsonResponse
|
|
{
|
|
$order = StoreOrderModel::with(['store:id,name', 'items'])->find($id);
|
|
if (empty($order)) {
|
|
throw new RepositoryException('订单不存在');
|
|
}
|
|
return $this->success($order->toArray());
|
|
}
|
|
|
|
/**
|
|
* 状态流转(待汇总→配送中→完成;待汇总可取消;已汇总可转配送中)
|
|
*/
|
|
#[PutRoute(route: '/{id}/status', authorize: 'update', where: ['id' => '[0-9]+'])]
|
|
public function status(int $id, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'status' => 'required|integer|in:2,3,9',
|
|
], [
|
|
'status.required' => '目标状态不能为空',
|
|
'status.in' => '目标状态值不正确',
|
|
]);
|
|
|
|
$order = StoreOrderModel::find($id);
|
|
if (empty($order)) {
|
|
throw new RepositoryException('订单不存在');
|
|
}
|
|
|
|
$target = (int) $data['status'];
|
|
$allowed = match ($order->status) {
|
|
StoreOrderModel::STATUS_PENDING => [
|
|
StoreOrderModel::STATUS_DELIVERING,
|
|
StoreOrderModel::STATUS_CANCELLED,
|
|
],
|
|
StoreOrderModel::STATUS_SUMMARIZED => [
|
|
StoreOrderModel::STATUS_DELIVERING,
|
|
],
|
|
StoreOrderModel::STATUS_DELIVERING => [
|
|
StoreOrderModel::STATUS_COMPLETED,
|
|
],
|
|
default => [],
|
|
};
|
|
if (! in_array($target, $allowed, true)) {
|
|
throw new RepositoryException(
|
|
'订单当前状态为「' . (self::STATUS_NAMES[$order->status] ?? $order->status) . '」,不允许该操作'
|
|
);
|
|
}
|
|
|
|
$order->status = $target;
|
|
$order->save();
|
|
|
|
$this->notifyStore($order);
|
|
|
|
return $this->success();
|
|
}
|
|
|
|
/**
|
|
* 修改周转框/周转托盘数量(仅已接单、采购中、配送中可改),
|
|
* 自动重算附加金额与订单总金额
|
|
*/
|
|
#[PutRoute(route: '/{id}/container', authorize: 'update', where: ['id' => '[0-9]+'])]
|
|
public function container(int $id, Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'box_num' => 'required|integer|min:0',
|
|
'tray_num' => 'required|integer|min:0',
|
|
], [
|
|
'box_num.required' => '周转框数量不能为空',
|
|
'box_num.integer' => '周转框数量必须为整数',
|
|
'box_num.min' => '周转框数量不能小于 0',
|
|
'tray_num.required' => '周转托盘数量不能为空',
|
|
'tray_num.integer' => '周转托盘数量必须为整数',
|
|
'tray_num.min' => '周转托盘数量不能小于 0',
|
|
]);
|
|
|
|
$order = StoreOrderModel::find($id);
|
|
if (empty($order)) {
|
|
throw new RepositoryException('订单不存在');
|
|
}
|
|
|
|
$editable = [
|
|
StoreOrderModel::STATUS_SUMMARIZED,
|
|
StoreOrderModel::STATUS_DELIVERING,
|
|
StoreOrderModel::STATUS_DISTRIBUTION,
|
|
];
|
|
if (! in_array($order->status, $editable, true)) {
|
|
throw new RepositoryException(
|
|
'订单当前状态为「' . (self::STATUS_NAMES[$order->status] ?? $order->status) . '」,不允许修改周转框/托盘数量'
|
|
);
|
|
}
|
|
|
|
$boxPrice = (float) site_config('services.box_amount', 0);
|
|
$trayPrice = (float) site_config('services.tray_amount', 0);
|
|
$addedAmount = round($data['box_num'] * $boxPrice + $data['tray_num'] * $trayPrice, 2);
|
|
|
|
// 历史订单未写商品金额:按「总额 - 附加」反推并回写,保证 总额 = 商品 + 附加 恒成立
|
|
$productAmount = (float) $order->product_amount;
|
|
if ($productAmount <= 0 && (float) $order->total_amount > 0) {
|
|
$productAmount = round((float) $order->total_amount - (float) $order->added_amount, 2);
|
|
}
|
|
|
|
$order->box_num = (int) $data['box_num'];
|
|
$order->tray_num = (int) $data['tray_num'];
|
|
$order->product_amount = $productAmount;
|
|
$order->added_amount = $addedAmount;
|
|
$order->total_amount = round($productAmount + $addedAmount, 2);
|
|
$order->save();
|
|
|
|
return $this->success();
|
|
}
|
|
|
|
/**
|
|
* 状态流转后通知门店用户
|
|
*/
|
|
private function notifyStore(StoreOrderModel $order): void
|
|
{
|
|
$userIds = UserModel::query()
|
|
->where('store_id', $order->store_id)
|
|
->where('status', UserModel::STATUS_NORMAL)
|
|
->pluck('id');
|
|
|
|
$statusName = self::STATUS_NAMES[$order->status] ?? (string) $order->status;
|
|
foreach ($userIds as $userId) {
|
|
NoticeModel::create([
|
|
'user_id' => $userId,
|
|
'type' => NoticeModel::TYPE_ORDER,
|
|
'title' => '订单状态更新',
|
|
'content' => mb_substr("您的订单 {$order->order_no} 状态已更新为「{$statusName}」", 0, 500),
|
|
'data' => ['order_id' => $order->id],
|
|
'is_read' => NoticeModel::UNREAD,
|
|
]);
|
|
}
|
|
}
|
|
}
|