215 lines
6.4 KiB
PHP
215 lines
6.4 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
namespace App\Service\business;
|
|||
|
|
|
|||
|
|
use App\BaseApp\BaseService;
|
|||
|
|
use App\Models\business\CatalogueModel;
|
|||
|
|
use App\Models\business\PriceSheetModel;
|
|||
|
|
use Exception;
|
|||
|
|
use Illuminate\Support\Facades\DB;
|
|||
|
|
use Psr\Container\ContainerExceptionInterface;
|
|||
|
|
use Psr\Container\NotFoundExceptionInterface;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 报价单(商品规格 + 常规价)
|
|||
|
|
*
|
|||
|
|
* 老接口靠 specification-1a / dimension-1b / routine-1b 这种动态键接收多行表单,
|
|||
|
|
* 数量还得靠 count($params) 猜,多一个无关字段就会多循环一轮。
|
|||
|
|
* 新接口收 rows 数组,并提供 saveRows 一次性覆盖某商品的全部规格行——抽屉里就是这个语义。
|
|||
|
|
*/
|
|||
|
|
class PriceSheetService extends BaseService
|
|||
|
|
{
|
|||
|
|
public function __construct()
|
|||
|
|
{
|
|||
|
|
parent::__construct();
|
|||
|
|
$this->model = PriceSheetModel::class;
|
|||
|
|
$this->selectField = array_merge(
|
|||
|
|
['id', 'catalogue_id', 'specification', 'dimension'],
|
|||
|
|
PriceSheetModel::MATERIAL_FIELDS,
|
|||
|
|
['status', 'created_at', 'updated_at']
|
|||
|
|
);
|
|||
|
|
$this->queryField = ['catalogue_id' => '=', 'specification' => 'like', 'status' => '='];
|
|||
|
|
$this->orderBy = ['name' => 'id', 'sort' => 'asc'];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @throws ContainerExceptionInterface
|
|||
|
|
* @throws NotFoundExceptionInterface
|
|||
|
|
*/
|
|||
|
|
public function list(): array
|
|||
|
|
{
|
|||
|
|
return $this->getPageList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function option(): mixed
|
|||
|
|
{
|
|||
|
|
$this->optionField = ['id', 'specification as name'];
|
|||
|
|
return $this->getOption();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
public function detail($id): mixed
|
|||
|
|
{
|
|||
|
|
return $this->getDetail($id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 新增:单行或多行(rows)
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
public function create($params): mixed
|
|||
|
|
{
|
|||
|
|
$catalogueId = (int) ($params['catalogue_id'] ?? 0);
|
|||
|
|
$this->assertCatalogue($catalogueId);
|
|||
|
|
|
|||
|
|
$rows = $this->normalizeRows($catalogueId, $params);
|
|||
|
|
if (empty($rows)) {
|
|||
|
|
$this->utils->errorThrow('请至少填写一行规格');
|
|||
|
|
}
|
|||
|
|
if (count($rows) === 1) {
|
|||
|
|
return $this->insert($rows[0]);
|
|||
|
|
}
|
|||
|
|
return PriceSheetModel::insert($rows);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
public function update($id, $params): mixed
|
|||
|
|
{
|
|||
|
|
unset($params['rows']);
|
|||
|
|
return $this->save($id, $params);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
public function delete($id): mixed
|
|||
|
|
{
|
|||
|
|
return $this->del(is_array($id) ? $id : [$id]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
public function status($id, $status): mixed
|
|||
|
|
{
|
|||
|
|
return $this->save($id, ['status' => (int) $status]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 覆盖式保存某商品的全部规格行:带 id 的更新,不带的新增,界面上被删掉的软删。
|
|||
|
|
* 一次事务完成,避免中途失败留下半套规格。
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
public function saveRows(int $catalogueId, array $rows): bool
|
|||
|
|
{
|
|||
|
|
$this->assertCatalogue($catalogueId);
|
|||
|
|
|
|||
|
|
$now = time();
|
|||
|
|
$keepIds = [];
|
|||
|
|
DB::connection('business')->beginTransaction();
|
|||
|
|
try {
|
|||
|
|
foreach ($rows as $row) {
|
|||
|
|
$payload = $this->pickRowFields($row);
|
|||
|
|
if (trim((string) ($payload['specification'] ?? '')) === '') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$payload['catalogue_id'] = $catalogueId;
|
|||
|
|
$id = (int) ($row['id'] ?? 0);
|
|||
|
|
if ($id > 0) {
|
|||
|
|
$payload['updated_at'] = $now;
|
|||
|
|
PriceSheetModel::where('id', $id)->where('catalogue_id', $catalogueId)->update($payload);
|
|||
|
|
$keepIds[] = $id;
|
|||
|
|
} else {
|
|||
|
|
$payload['created_at'] = $now;
|
|||
|
|
$keepIds[] = (int) PriceSheetModel::insertGetId($payload);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PriceSheetModel::where('catalogue_id', $catalogueId)
|
|||
|
|
->where('deleted_at', 0)
|
|||
|
|
->when(!empty($keepIds), fn ($q) => $q->whereNotIn('id', $keepIds))
|
|||
|
|
->update(['deleted_at' => $now, 'updated_at' => $now]);
|
|||
|
|
|
|||
|
|
DB::connection('business')->commit();
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
DB::connection('business')->rollBack();
|
|||
|
|
$this->utils->errorThrow($e->getMessage());
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 某商品的全部规格行,供报价单抽屉回填
|
|||
|
|
*/
|
|||
|
|
public function rowsOf(int $catalogueId): array
|
|||
|
|
{
|
|||
|
|
return PriceSheetModel::where('catalogue_id', $catalogueId)
|
|||
|
|
->where('deleted_at', 0)
|
|||
|
|
->orderBy('id')
|
|||
|
|
->get($this->selectField)
|
|||
|
|
->toArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 兼容单行字段与 rows 数组两种入参
|
|||
|
|
*/
|
|||
|
|
private function normalizeRows(int $catalogueId, array $params): array
|
|||
|
|
{
|
|||
|
|
$now = time();
|
|||
|
|
$source = [];
|
|||
|
|
if (!empty($params['rows']) && is_array($params['rows'])) {
|
|||
|
|
$source = $params['rows'];
|
|||
|
|
} elseif (trim((string) ($params['specification'] ?? '')) !== '') {
|
|||
|
|
$source = [$params];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$rows = [];
|
|||
|
|
foreach ($source as $row) {
|
|||
|
|
if (!is_array($row)) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$payload = $this->pickRowFields($row);
|
|||
|
|
if (trim((string) ($payload['specification'] ?? '')) === '') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$payload['catalogue_id'] = $catalogueId;
|
|||
|
|
$payload['created_at'] = $now;
|
|||
|
|
$rows[] = $payload;
|
|||
|
|
}
|
|||
|
|
return $rows;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 只取表里真实存在的列,把前端多传的字段挡在外面
|
|||
|
|
*/
|
|||
|
|
private function pickRowFields(array $row): array
|
|||
|
|
{
|
|||
|
|
$allowed = array_merge(['specification', 'dimension'], PriceSheetModel::MATERIAL_FIELDS);
|
|||
|
|
$payload = [];
|
|||
|
|
foreach ($allowed as $field) {
|
|||
|
|
if (array_key_exists($field, $row)) {
|
|||
|
|
$payload[$field] = is_scalar($row[$field]) ? (string) $row[$field] : '';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return $payload;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @throws Exception
|
|||
|
|
*/
|
|||
|
|
private function assertCatalogue(int $catalogueId): void
|
|||
|
|
{
|
|||
|
|
if ($catalogueId <= 0) {
|
|||
|
|
$this->utils->errorThrow('请选择商品');
|
|||
|
|
}
|
|||
|
|
$exists = CatalogueModel::where('id', $catalogueId)->where('deleted_at', 0)->exists();
|
|||
|
|
if (!$exists) {
|
|||
|
|
$this->utils->errorThrow('商品不存在');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|