推荐商品
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Http\Requests\Client\SpecialBatchAddRequest;
|
||||
use App\Http\Requests\Client\SpecialFormRequest;
|
||||
use App\Models\HomeSpecialModel;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
|
||||
/**
|
||||
* 特价推荐商品配置(小程序首页)
|
||||
*/
|
||||
#[RequestAttribute('/client/special', 'client.special')]
|
||||
class SpecialController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'status' => '=',
|
||||
];
|
||||
|
||||
/** 推荐列表(默认按排序升序;keyword 按商品名模糊搜索) */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$query = $this->buildSearch($params, HomeSpecialModel::query()->with('product'));
|
||||
|
||||
$keyword = trim((string) ($params['keyword'] ?? ''));
|
||||
if ($keyword !== '') {
|
||||
$query->whereHas('product', static function ($q) use ($keyword) {
|
||||
$q->where('name', 'like', '%' . $keyword . '%');
|
||||
});
|
||||
}
|
||||
|
||||
$data = $query->orderBy('sort')
|
||||
->orderBy('id')
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 批量添加推荐商品(已在推荐中的自动跳过) */
|
||||
#[PostRoute('/batch', 'create')]
|
||||
public function batchAdd(SpecialBatchAddRequest $request): JsonResponse
|
||||
{
|
||||
$productIds = $request->validated()['product_ids'];
|
||||
$exists = HomeSpecialModel::whereIn('product_id', $productIds)->pluck('product_id')->all();
|
||||
$newIds = array_values(array_diff($productIds, $exists));
|
||||
|
||||
foreach ($newIds as $productId) {
|
||||
HomeSpecialModel::create([
|
||||
'product_id' => $productId,
|
||||
'sort' => 0,
|
||||
'status' => HomeSpecialModel::STATUS_NORMAL,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'added' => count($newIds),
|
||||
'skipped' => count($productIds) - count($newIds),
|
||||
]);
|
||||
}
|
||||
|
||||
/** 编辑推荐(仅排序与状态) */
|
||||
#[PutRoute(route: '/{id}', authorize: 'update', where: ['id' => '[0-9]+'])]
|
||||
public function update(int $id, SpecialFormRequest $request): JsonResponse
|
||||
{
|
||||
$model = HomeSpecialModel::find($id);
|
||||
if (empty($model)) {
|
||||
throw new RepositoryException('推荐不存在');
|
||||
}
|
||||
$model->update($request->validated());
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除推荐 */
|
||||
#[DeleteRoute(route: '/{id}', authorize: 'delete', where: ['id' => '[0-9]+'])]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = HomeSpecialModel::find($id);
|
||||
if (empty($model)) {
|
||||
throw new RepositoryException('推荐不存在');
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 批量删除推荐 */
|
||||
#[DeleteRoute('/batch', 'delete')]
|
||||
public function batchDelete(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'integer|distinct',
|
||||
], [
|
||||
'ids.required' => '请选择要删除的推荐',
|
||||
'ids.min' => '请选择要删除的推荐',
|
||||
'ids.*.integer' => '推荐 ID 格式错误',
|
||||
'ids.*.distinct' => '存在重复的推荐',
|
||||
]);
|
||||
HomeSpecialModel::whereIn('id', $data['ids'])->delete();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Mini;
|
||||
|
||||
use App\Models\CustomerLevelModel;
|
||||
use App\Models\HomeSpecialModel;
|
||||
use App\Models\ProductModel;
|
||||
use App\Services\CartService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
|
||||
/**
|
||||
* 小程序特价推荐
|
||||
*/
|
||||
#[RequestAttribute('/mini', 'mini', authGuard: 'users')]
|
||||
class SpecialController extends BaseMiniController
|
||||
{
|
||||
/**
|
||||
* 特价推荐商品列表(免登录浏览;行结构与 /mini/product/list 一致:
|
||||
* 登录门店附等级价 price 与 cart_id/cart_quantity 供列表直接加减购物车;
|
||||
* data.cart 为购物车悬浮球汇总,未登录返回零值结构)
|
||||
*/
|
||||
#[GetRoute('/special/list', authorize: false)]
|
||||
public function specials(Request $request): JsonResponse
|
||||
{
|
||||
$pageSize = (int) $request->input('pageSize', 10);
|
||||
$paginator = HomeSpecialModel::query()
|
||||
->where('status', HomeSpecialModel::STATUS_NORMAL)
|
||||
->whereHas('product', static function ($q) {
|
||||
$q->where('status', ProductModel::STATUS_ON);
|
||||
})
|
||||
->with('product')
|
||||
->orderBy('sort')
|
||||
->orderBy('id')
|
||||
->paginate($pageSize);
|
||||
|
||||
// 当前门店的等级(售价 = 成本价 × (100 + 等级上浮比例) / 100)与购物车行
|
||||
$store = $this->optionalStore($request);
|
||||
$level = ($store !== null && $store->level_id > 0) ? $store->level : null;
|
||||
$cartService = app(CartService::class);
|
||||
$cartRows = $store !== null ? $cartService->cartRowMap($store->id) : [];
|
||||
|
||||
$paginator->getCollection()->transform(
|
||||
static function (HomeSpecialModel $special) use ($level, $cartRows): array {
|
||||
$product = $special->product;
|
||||
$row = $product->toArray();
|
||||
// 实际价(按等级上浮比例换算;成本价不随序列化输出)
|
||||
$row['price'] = $level !== null
|
||||
? CustomerLevelModel::calcLevelPrice($product->cost_price, $level->percent)
|
||||
: null;
|
||||
// 购物车数量(列表直接加减用;不在购物车为 0/'0.00')
|
||||
$row['cart_id'] = $cartRows[$product->id]['id'] ?? 0;
|
||||
$row['cart_quantity'] = $cartRows[$product->id]['quantity'] ?? '0.00';
|
||||
return $row;
|
||||
}
|
||||
);
|
||||
|
||||
$data = $paginator->toArray();
|
||||
$data['cart'] = $cartService->summary($store);
|
||||
|
||||
return $this->success($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Client;
|
||||
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 特价推荐 批量添加 验证
|
||||
*/
|
||||
class SpecialBatchAddRequest extends BaseFormRequest
|
||||
{
|
||||
protected $stopOnFirstFailure = true;
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'product_ids' => 'required|array|min:1',
|
||||
'product_ids.*' => 'integer|distinct|exists:product,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'product_ids.required' => '请选择要添加的商品',
|
||||
'product_ids.min' => '请选择要添加的商品',
|
||||
'product_ids.*.integer' => '商品 ID 格式错误',
|
||||
'product_ids.*.distinct' => '存在重复的商品',
|
||||
'product_ids.*.exists' => '所选商品不存在',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Client;
|
||||
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 特价推荐 编辑 验证(仅排序与状态可编辑,商品不可更换)
|
||||
*/
|
||||
class SpecialFormRequest extends BaseFormRequest
|
||||
{
|
||||
protected $stopOnFirstFailure = true;
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'sort' => 'nullable|integer|min:0',
|
||||
'status' => 'required|integer|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'sort.min' => '排序不能小于 0',
|
||||
'status.in' => '状态只能是 0 或 1',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* 小程序首页特价推荐商品
|
||||
*/
|
||||
class HomeSpecialModel extends Model
|
||||
{
|
||||
/** 状态:停用 */
|
||||
public const int STATUS_DISABLED = 0;
|
||||
/** 状态:正常 */
|
||||
public const int STATUS_NORMAL = 1;
|
||||
|
||||
protected $table = 'mini_home_special';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'product_id',
|
||||
'sort',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'product_id' => 'integer',
|
||||
'sort' => 'integer',
|
||||
'status' => 'integer',
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联商品
|
||||
*/
|
||||
public function product(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ProductModel::class, 'product_id');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user