Files
xin-procurement/app/Http/Controllers/Product/ProductController.php
T
2026-07-23 20:41:25 +08:00

241 lines
8.8 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\Http\Controllers\Product;
use App\Exceptions\RepositoryException;
use App\Http\Requests\Product\BatchPriceRequest;
use App\Http\Requests\Product\ProductFormRequest;
use App\Models\CustomerLevelModel;
use App\Models\NoticeModel;
use App\Models\ProductModel;
use App\Models\ProductPriceModel;
use App\Models\StoreModel;
use App\Models\UserModel;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
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;
/**
* 商品档案管理(A1 商品列表 / A2 价格矩阵与批量调价 / 多等级价格体系)
*/
#[RequestAttribute('/product/goods', 'product.goods')]
class ProductController extends BaseController
{
protected array $searchField = [
'name' => 'like',
'category_id' => '=',
'supplier_id' => '=',
'status' => '=',
];
protected array $quickSearchField = ['name', 'spec'];
/** A1 商品列表(含分类/供应商/各等级价格) */
#[GetRoute(authorize: 'query')]
public function query(Request $request): JsonResponse
{
$params = $request->all();
$pageSize = $params['pageSize'] ?? 10;
$data = $this->buildSearch(
$params,
ProductModel::query()->with(['category:id,name', 'supplier:id,name', 'prices.level:id,name'])
)
->orderBy('sort')
->orderBy('id', 'desc')
->paginate($pageSize)
->toArray();
return $this->success($data);
}
/** 创建商品(事务内建商品 + 同步等级价格) */
#[PostRoute(authorize: 'create')]
public function create(ProductFormRequest $request): JsonResponse
{
$validated = $request->validated();
$prices = $validated['prices'] ?? [];
unset($validated['prices']);
$product = DB::transaction(function () use ($validated, $prices) {
$product = ProductModel::create($validated);
foreach ($prices as $row) {
ProductPriceModel::create([
'product_id' => $product->id,
'level_id' => (int) $row['level_id'],
'price' => $row['price'],
]);
}
return $product;
});
return $this->success(['id' => $product->id]);
}
/** 编辑商品(prices 按 level_id upsert,删除已移除的等级行;未提交 prices 键时保持原价) */
#[PutRoute(route: '/{id}', authorize: 'update', where: ['id' => '[0-9]+'])]
public function update(int $id, ProductFormRequest $request): JsonResponse
{
$product = ProductModel::find($id);
if (empty($product)) {
throw new RepositoryException('商品不存在');
}
$validated = $request->validated();
$prices = $validated['prices'] ?? [];
unset($validated['prices']);
DB::transaction(function () use ($product, $validated, $prices, $request) {
$product->update($validated);
if ($request->has('prices')) {
$levelIds = [];
foreach ($prices as $row) {
$levelId = (int) $row['level_id'];
$levelIds[] = $levelId;
ProductPriceModel::updateOrCreate(
['product_id' => $product->id, 'level_id' => $levelId],
['price' => $row['price']],
);
}
$product->prices()->whereNotIn('level_id', $levelIds)->delete();
}
});
return $this->success();
}
/** 删除商品(软删除,连带价格行一并删除) */
#[DeleteRoute(route: '/{id}', authorize: 'delete', where: ['id' => '[0-9]+'])]
public function delete(int $id): JsonResponse
{
$product = ProductModel::find($id);
if (empty($product)) {
throw new RepositoryException('商品不存在');
}
DB::transaction(function () use ($product) {
$product->prices()->delete();
$product->delete();
});
return $this->success();
}
/**
* A2 价格矩阵:行=商品(支持 category_id / keyword 过滤),列=全部启用等级,值=price(缺失为 null
*/
#[GetRoute('/priceMatrix', 'query')]
public function priceMatrix(Request $request): JsonResponse
{
$query = ProductModel::query()->with('prices:id,product_id,level_id,price');
if (($categoryId = (int) $request->input('category_id', 0)) > 0) {
$query->where('category_id', $categoryId);
}
$keyword = trim((string) $request->input('keyword', ''));
if ($keyword !== '') {
$query->where(function ($q) use ($keyword) {
$q->where('name', 'like', '%' . $keyword . '%')
->orWhere('spec', 'like', '%' . $keyword . '%');
});
}
$products = $query->orderBy('sort')->orderBy('id')->get();
$levels = CustomerLevelModel::query()
->where('status', CustomerLevelModel::STATUS_NORMAL)
->orderBy('sort')
->get(['id', 'name']);
$rows = $products->map(function (ProductModel $product) use ($levels) {
$priceMap = $product->prices->keyBy('level_id');
$row = [
'id' => $product->id,
'name' => $product->name,
'spec' => $product->spec,
'unit' => $product->unit,
];
foreach ($levels as $level) {
$row['price_' . $level->id] = isset($priceMap[$level->id])
? (float) $priceMap[$level->id]->price
: null;
}
return $row;
});
return $this->success([
'levels' => $levels->toArray(),
'rows' => $rows->toArray(),
]);
}
/**
* A2 批量调价:事务写入,写完后给受影响门店生成 Noticetype=price
*/
#[PutRoute('/batchPrice', 'batchPrice')]
public function batchPrice(BatchPriceRequest $request): JsonResponse
{
$updates = $request->validated('updates');
DB::transaction(function () use ($updates) {
$productIds = [];
$levelIds = [];
foreach ($updates as $row) {
ProductPriceModel::updateOrCreate(
['product_id' => (int) $row['product_id'], 'level_id' => (int) $row['level_id']],
['price' => $row['price']],
);
$productIds[(int) $row['product_id']] = true;
$levelIds[(int) $row['level_id']] = true;
}
$productNames = ProductModel::whereIn('id', array_keys($productIds))
->pluck('name')
->implode('、');
$content = mb_substr('以下商品价格已调整:' . $productNames . ',下次登录小程序后按新价格显示', 0, 500);
// 受影响门店:客户等级在本次调价等级范围内的正常门店,通知其绑定的正常用户
$userIds = UserModel::query()
->where('type', UserModel::TYPE_STORE)
->where('status', UserModel::STATUS_NORMAL)
->whereIn('store_id', function ($q) use ($levelIds) {
$q->select('id')
->from('store')
->where('status', StoreModel::STATUS_NORMAL)
->whereIn('level_id', array_keys($levelIds));
})
->pluck('id');
foreach ($userIds as $userId) {
NoticeModel::create([
'user_id' => $userId,
'type' => NoticeModel::TYPE_PRICE,
'title' => '商品价格变更',
'content' => $content,
'data' => [
'product_ids' => array_keys($productIds),
'level_ids' => array_keys($levelIds),
],
'is_read' => NoticeModel::UNREAD,
]);
}
});
return $this->success();
}
/** 商品下拉选项(仅上架,下单等场景用) */
#[GetRoute('/options', 'query')]
public function options(Request $request): JsonResponse
{
$query = ProductModel::query()->where('status', ProductModel::STATUS_ON);
$keyword = trim((string) $request->input('keyword', ''));
if ($keyword !== '') {
$query->where('name', 'like', '%' . $keyword . '%');
}
$data = $query->orderBy('sort')
->get(['id', 'name', 'spec', 'unit'])
->toArray();
return $this->success($data);
}
}