216 lines
8.0 KiB
PHP
216 lines
8.0 KiB
PHP
<?php
|
||
|
||
namespace App\Service\wx;
|
||
|
||
use App\BaseApp\BaseWxService;
|
||
use App\Models\business\ListItemModel;
|
||
use App\Models\business\ListModel;
|
||
use App\Service\business\PriceService;
|
||
use App\Service\business\SerialNoService;
|
||
|
||
/**
|
||
* 小程序清单
|
||
*
|
||
* 清单是「加购物车」的语义但不含结算,下单由 WxOrderService 从清单快照生成。
|
||
* 所有写操作都必须带 user_id 条件,否则改个 id 就能删别人的清单——老项目的 deleteItem 就是这个漏洞。
|
||
*/
|
||
class WxListService extends BaseWxService
|
||
{
|
||
/**
|
||
* 我的清单,带明细数量
|
||
*
|
||
* 兼容两种返回:
|
||
* - 不传 page / pageSize(老调用方):返回全量扁平数组,保持向后兼容;
|
||
* - 传了 page / pageSize:返回 { items, total, page, pageSize, has_more },供小程序真分页使用。
|
||
* 判断「是否分页模式」只看请求里有没有 page,避免 pageSize 默认值造成歧义。
|
||
*/
|
||
public function list(): array
|
||
{
|
||
// 请求里带了 page 才走分页分支;pageSize 缺省 20,与列表页约定一致
|
||
$page = request()->get('page');
|
||
$paged = $page !== null && $page !== '';
|
||
$page = max(1, (int) $page);
|
||
$pageSize = max(1, (int) (request()->get('pageSize') ?? 20));
|
||
|
||
$base = ListModel::with([
|
||
'items' => fn ($query) => $query->where('deleted_at', 0)->select(['id', 'list_id']),
|
||
])->where('user_id', $this->userId)
|
||
->where('deleted_at', 0)
|
||
->orderBy('id', 'desc');
|
||
|
||
// 不分页:一次拿全,保持老结构
|
||
if (!$paged) {
|
||
$rows = $base->get();
|
||
if ($rows->isEmpty()) {
|
||
return [];
|
||
}
|
||
$rows = $rows->toArray();
|
||
foreach ($rows as &$row) {
|
||
$row['count'] = count($row['items'] ?? []);
|
||
}
|
||
unset($row);
|
||
return $rows;
|
||
}
|
||
|
||
// 分页:拿当前页 + 总数,前端按 has_more 决定是否继续 loadMore
|
||
$total = (clone $base)->count();
|
||
$rows = $base->forPage($page, $pageSize)->get();
|
||
$items = $rows->isEmpty() ? [] : $rows->toArray();
|
||
foreach ($items as &$row) {
|
||
$row['count'] = count($row['items'] ?? []);
|
||
}
|
||
unset($row);
|
||
|
||
return [
|
||
'items' => $items,
|
||
'total' => $total,
|
||
'page' => $page,
|
||
'pageSize' => $pageSize,
|
||
'has_more' => ($page * $pageSize) < $total,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 清单详情:带商品与规格,价格按当前用户的可见性与倍率处理
|
||
*/
|
||
public function detail(int $id): array
|
||
{
|
||
$info = ListModel::with([
|
||
'items' => fn ($query) => $query->where('deleted_at', 0),
|
||
'items.catalogue',
|
||
'items.catalogue.priceSheet' => fn ($query) => $query->where('deleted_at', 0),
|
||
])->where('id', $id)
|
||
->where('user_id', $this->userId)
|
||
->where('deleted_at', 0)
|
||
->first();
|
||
if (empty($info)) {
|
||
$this->utils->errorThrow('清单不存在');
|
||
}
|
||
$info = $info->toArray();
|
||
|
||
$price = PriceService::getInstance();
|
||
$showPrice = $this->canSeePrice();
|
||
$multiplier = $this->priceMultiplier();
|
||
foreach ($info['items'] as &$item) {
|
||
$sheet = $item['catalogue']['price_sheet'] ?? [];
|
||
if (!empty($sheet)) {
|
||
$applied = $price->applyToRows($sheet, $showPrice, $multiplier);
|
||
$item['catalogue']['price_sheet'] = $applied['rows'];
|
||
}
|
||
// 老前端读的是 product 这个键名,保留别名避免小程序改字段
|
||
$item['product'] = $item['catalogue'] ?? null;
|
||
}
|
||
unset($item);
|
||
$info['is_show_price'] = $showPrice;
|
||
return $info;
|
||
}
|
||
|
||
public function create(array $params): mixed
|
||
{
|
||
$name = trim((string) ($params['name'] ?? ''));
|
||
if ($name === '') {
|
||
$this->utils->errorThrow('请填写清单名称');
|
||
}
|
||
return ListModel::insertGetId([
|
||
'list_no' => SerialNoService::getInstance()->generate(SerialNoService::PREFIX_LIST, 'list', 'list_no'),
|
||
'user_id' => $this->userId,
|
||
'name' => $name,
|
||
'remark' => (string) ($params['remark'] ?? ''),
|
||
'created_at' => time(),
|
||
]);
|
||
}
|
||
|
||
public function update(int $id, array $params): mixed
|
||
{
|
||
return ListModel::where('id', $id)->where('user_id', $this->userId)->update([
|
||
'name' => (string) ($params['name'] ?? ''),
|
||
'remark' => (string) ($params['remark'] ?? ''),
|
||
'updated_at' => time(),
|
||
]);
|
||
}
|
||
|
||
public function delete(array|int $ids): mixed
|
||
{
|
||
return ListModel::whereIn('id', (array) $ids)
|
||
->where('user_id', $this->userId)
|
||
->update(['deleted_at' => time(), 'updated_at' => time()]);
|
||
}
|
||
|
||
/**
|
||
* 删清单明细:先确认这些明细属于当前用户的清单
|
||
*/
|
||
public function deleteItem(array|int $ids): mixed
|
||
{
|
||
$ids = (array) $ids;
|
||
$listIds = ListModel::where('user_id', $this->userId)->where('deleted_at', 0)->pluck('id');
|
||
return ListItemModel::whereIn('id', $ids)
|
||
->whereIn('list_id', $listIds)
|
||
->update(['deleted_at' => time(), 'updated_at' => time()]);
|
||
}
|
||
|
||
/**
|
||
* 加入清单;同一清单同一商品同一规格只留一条,重复则累加数量
|
||
*/
|
||
public function toCart(array $params): mixed
|
||
{
|
||
$listId = (int) ($params['list_id'] ?? 0);
|
||
$catalogueId = (int) ($params['product_id'] ?? $params['catalogue_id'] ?? 0);
|
||
if ($listId <= 0 || $catalogueId <= 0) {
|
||
$this->utils->errorThrow('参数错误');
|
||
}
|
||
$list = ListModel::where('id', $listId)->where('user_id', $this->userId)->where('deleted_at', 0)->first();
|
||
if (empty($list)) {
|
||
$this->utils->errorThrow('清单不存在');
|
||
}
|
||
$priceSheetId = (int) ($params['price_sheet_id'] ?? 0);
|
||
$quantity = max(1, (int) ($params['quantity'] ?? 1));
|
||
|
||
$exists = ListItemModel::where('list_id', $listId)
|
||
->where('catalogue_id', $catalogueId)
|
||
->where('price_sheet_id', $priceSheetId)
|
||
->where('deleted_at', 0)
|
||
->first();
|
||
if (!empty($exists)) {
|
||
return ListItemModel::where('id', $exists['id'])->update([
|
||
'quantity' => (int) $exists['quantity'] + $quantity,
|
||
'updated_at' => time(),
|
||
]);
|
||
}
|
||
return ListItemModel::insertGetId([
|
||
'list_id' => $listId,
|
||
'catalogue_id' => $catalogueId,
|
||
'price_sheet_id' => $priceSheetId,
|
||
'material_key' => (string) ($params['material_key'] ?? 'routine'),
|
||
'quantity' => $quantity,
|
||
'remark' => (string) ($params['remark'] ?? ''),
|
||
'created_at' => time(),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 改明细的规格与数量:转订单前用户要能在清单里补齐这些信息
|
||
*/
|
||
public function updateItem(array $params): mixed
|
||
{
|
||
$itemId = (int) ($params['id'] ?? 0);
|
||
if ($itemId <= 0) {
|
||
$this->utils->errorThrow('参数错误');
|
||
}
|
||
$listIds = ListModel::where('user_id', $this->userId)->where('deleted_at', 0)->pluck('id');
|
||
$update = ['updated_at' => time()];
|
||
if (array_key_exists('quantity', $params)) {
|
||
$update['quantity'] = max(1, (int) $params['quantity']);
|
||
}
|
||
if (array_key_exists('price_sheet_id', $params)) {
|
||
$update['price_sheet_id'] = (int) $params['price_sheet_id'];
|
||
}
|
||
if (array_key_exists('material_key', $params)) {
|
||
$update['material_key'] = (string) $params['material_key'];
|
||
}
|
||
if (array_key_exists('remark', $params)) {
|
||
$update['remark'] = (string) $params['remark'];
|
||
}
|
||
return ListItemModel::where('id', $itemId)->whereIn('list_id', $listIds)->update($update);
|
||
}
|
||
}
|