文件
yjyf/server/application/admin/controller/Service.php

302 行
11 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\admin\controller;
use app\common\controller\Backend;
use think\Db;
use think\exception\PDOException;
use think\exception\ValidateException;
use Exception;
/**
* 商品管理(商品仅定义施工标的物与细分规格,服务价格统一配置在关联的主服务与增项服务上)
*
* @icon fa fa-cubes
*/
class Service extends Backend
{
/**
* Service模型对象
* @var \app\admin\model\Service
*/
protected $model = null;
protected $relationSearch = true;
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\Service;
$this->view->assign("priceTypeList", $this->model->getPriceTypeList());
$this->view->assign("statusList", $this->model->getStatusList());
// 获取统一服务字典表(fa_service_type,不绑定单一类目,主服务与增项服务同源)
$typesPool = Db::name('service_type')
->where('status', 'normal')
->order('weigh desc, id asc')
->select();
$this->view->assign("typesPool", $typesPool);
}
public function index()
{
$this->request->filter(['strip_tags', 'trim']);
if (false === $this->request->isAjax()) {
$cats = Db::name('service_cat')
->where('status', 'normal')
->order('weigh desc, id asc')
->select();
$this->view->assign('catList', $cats);
return $this->view->fetch();
}
if ($this->request->request('keyField')) {
return $this->selectpage();
}
[$where, $sort, $order, $offset, $limit] = $this->buildparams();
$list = $this->model
->with(['cat'])
->where($where)
->order($sort, $order)
->paginate($limit);
$rows = $list->items();
if (!empty($rows)) {
$serviceIds = [];
foreach ($rows as $r) {
$serviceIds[] = (int)$r['id'];
}
// 一次性批量查询当前页商品的细分规格,避免 N+1 查询
$allSpecs = Db::name('service_spec')
->field('id,service_id,name,description')
->where('service_id', 'in', $serviceIds)
->where('status', 'normal')
->order('weigh desc, id asc')
->select();
$specMap = [];
foreach ($allSpecs as $sp) {
$sid = (int)$sp['service_id'];
if (!isset($specMap[$sid])) {
$specMap[$sid] = [];
}
$specMap[$sid][] = $sp['name'];
}
foreach ($rows as &$r) {
$sid = (int)$r['id'];
$r['specs_list'] = isset($specMap[$sid]) ? $specMap[$sid] : [];
// 直接在服务端解析为数组返回,防止前端 bootstrap-table escape:true 将 JSON 双引号转义为 &quot; 导致解析失败
$stArr = [];
if (!empty($r['service_types'])) {
$stArr = is_array($r['service_types']) ? $r['service_types'] : (json_decode($r['service_types'], true) ?: []);
}
$adArr = [];
if (!empty($r['addons'])) {
$adArr = is_array($r['addons']) ? $r['addons'] : (json_decode($r['addons'], true) ?: []);
}
$r['service_types_list'] = $stArr;
$r['addons_list'] = $adArr;
}
unset($r);
}
$result = ['total' => $list->total(), 'rows' => $rows];
return json($result);
}
/**
* 添加商品并配置关联服务定价
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$params = $this->preExcludeFields($params);
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
$params[$this->dataLimitField] = $this->auth->id;
}
$params = $this->formatServiceConfig($params);
$result = false;
Db::startTrans();
try {
$result = $this->model->allowField(true)->save($params);
$serviceId = $this->model->id;
// 处理商品规格列表
$this->syncSpecs($serviceId, $this->request->post('specs/a', []), isset($params['base_price']) ? (float)$params['base_price'] : 0);
Db::commit();
} catch (ValidateException|PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success();
} else {
$this->error(__('No rows were inserted'));
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
return $this->view->fetch();
}
/**
* 编辑商品与关联服务定价
*/
public function edit($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds) && !in_array($row[$this->dataLimitField], $adminIds)) {
$this->error(__('You have no permission'));
}
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$params = $this->preExcludeFields($params);
$params = $this->formatServiceConfig($params);
$result = false;
Db::startTrans();
try {
$result = $row->allowField(true)->save($params);
// 同步处理商品规格列表
$this->syncSpecs($row->id, $this->request->post('specs/a', []), isset($params['base_price']) ? (float)$params['base_price'] : (float)$row->base_price);
Db::commit();
} catch (ValidateException|PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success();
} else {
$this->error(__('No rows were updated'));
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
// 解析当前商品已关联的主服务与增项服务配置
$serviceTypes = [];
if (!empty($row->service_types)) {
$serviceTypes = is_array($row->service_types) ? $row->service_types : json_decode($row->service_types, true);
}
$addons = [];
if (!empty($row->addons)) {
$addons = is_array($row->addons) ? $row->addons : json_decode($row->addons, true);
}
// 获取已有商品规格
$specs = Db::name('service_spec')
->where('service_id', $row->id)
->where('status', 'normal')
->order('weigh desc, id asc')
->select();
$catName = Db::name('service_cat')->where('id', $row->cat_id)->value('name') ?: '未归类';
$this->view->assign("row", $row);
$this->view->assign("catName", $catName);
$this->view->assign("serviceTypes", $serviceTypes ?: []);
$this->view->assign("assignedAddons", $addons ?: []);
$this->view->assign("specs", $specs ?: []);
return $this->view->fetch();
}
/**
* 格式化商品关联的主服务与增项服务 JSON,并自动提取默认主服务价格用于底层索引兼容
*/
protected function formatServiceConfig($params)
{
$parsedTypes = [];
if (isset($params['service_types_raw'])) {
if (is_string($params['service_types_raw'])) {
$parsedTypes = json_decode($params['service_types_raw'], true) ?: [];
$params['service_types'] = $params['service_types_raw'];
} else {
$parsedTypes = is_array($params['service_types_raw']) ? $params['service_types_raw'] : [];
$params['service_types'] = json_encode($parsedTypes, JSON_UNESCAPED_UNICODE);
}
unset($params['service_types_raw']);
}
if (isset($params['addons_raw'])) {
$params['addons'] = is_string($params['addons_raw']) ? $params['addons_raw'] : json_encode($params['addons_raw'], JSON_UNESCAPED_UNICODE);
unset($params['addons_raw']);
}
// 自动从关联的主服务(优先默认主服务或“安装”)同步底层 base_price 与 price_type,商品无需单独填价格
if (!empty($parsedTypes)) {
$defItem = $parsedTypes[0];
foreach ($parsedTypes as $pt) {
if (!empty($pt['is_default'])) {
$defItem = $pt;
break;
}
}
$supportFixed = isset($defItem['support_fixed']) ? (int)$defItem['support_fixed'] : 1;
$params['price_type'] = $supportFixed === 1 ? 'fixed' : 'quote';
$params['base_price'] = $supportFixed === 1 ? (float)$defItem['price'] : 0.00;
}
return $params;
}
/**
* 同步商品规格型号表(规格不设独立商品价格,仅标识具体施工标的物型号)
*/
protected function syncSpecs($serviceId, $specsInput, $defaultBasePrice = 0.00)
{
if (empty($serviceId)) return;
$now = time();
$keepIds = [];
if (!empty($specsInput) && is_array($specsInput)) {
$idx = 0;
foreach ($specsInput as $item) {
$name = trim(isset($item['name']) ? $item['name'] : '');
if ($name === '') continue;
$specDesc = isset($item['description']) ? trim($item['description']) : '';
$specId = isset($item['id']) ? (int)$item['id'] : 0;
$weigh = max(0, 100 - $idx * 5);
$idx++;
if ($specId > 0) {
Db::name('service_spec')->where('id', $specId)->where('service_id', $serviceId)->update([
'name' => $name,
'base_price' => $defaultBasePrice,
'description' => $specDesc,
'weigh' => $weigh,
'updatetime' => $now,
]);
$keepIds[] = $specId;
} else {
$newId = Db::name('service_spec')->insertGetId([
'service_id' => $serviceId,
'name' => $name,
'base_price' => $defaultBasePrice,
'description' => $specDesc,
'weigh' => $weigh,
'status' => 'normal',
'createtime' => $now,
'updatetime' => $now,
]);
$keepIds[] = $newId;
}
}
}
$delQuery = Db::name('service_spec')->where('service_id', $serviceId);
if (!empty($keepIds)) {
$delQuery->whereNotIn('id', $keepIds);
}
$delQuery->delete();
}
}