349 lines
13 KiB
PHP
349 lines
13 KiB
PHP
<?php
|
||
|
||
namespace app\common\service;
|
||
|
||
use app\common\logic\Fingerprint;
|
||
use app\common\logic\ModelNaming;
|
||
use app\common\logic\SkuCodeBuilder;
|
||
use app\common\validate\VehVehicleSkuValidate;
|
||
use app\common\traits\crud\CrudServiceTrait;
|
||
use app\common\model\VehVehicleSku;
|
||
use support\Request;
|
||
|
||
class VehVehicleSkuService
|
||
{
|
||
|
||
use CrudServiceTrait;
|
||
|
||
/**
|
||
* 指定当前要操作的模型
|
||
*/
|
||
protected string $modelClass = VehVehicleSku::class;
|
||
|
||
/**
|
||
* 验证器
|
||
*/
|
||
protected $validator = VehVehicleSkuValidate::class;
|
||
|
||
/**
|
||
* 获取验证器
|
||
*/
|
||
public function getValidator()
|
||
{
|
||
return new $this->validator;
|
||
}
|
||
|
||
|
||
// 枚举
|
||
private const MAPS = [
|
||
'BODY' => [
|
||
'SEDAN' => '三厢轿车', 'SUV'=>'SUV', 'HB'=>'两厢/掀背', 'MPV'=>'MPV', 'PICKUP'=>'皮卡'
|
||
],
|
||
'DRIVE' => [
|
||
'FWD'=>'前驱', 'RWD'=>'后驱', 'AWD'=>'四驱(AWD)', '4WD'=>'四驱(4WD)'
|
||
],
|
||
'MOTOR_LAYOUT' => [
|
||
'FWD'=>'前置单电机', 'RWD'=>'后置单电机', 'AWD'=>'双电机四驱', 'DM-I'=>'DM-i', 'DM-P'=>'DM-p'
|
||
],
|
||
'FUEL' => [
|
||
'GAS'=>'汽油', 'HEV'=>'油电混动', 'PHEV'=>'插电混动', 'EV'=>'纯电', 'EREV'=>'增程'
|
||
],
|
||
'TRANS' => [
|
||
'MT'=>'手动', 'AT'=>'自动', 'CVT'=>'无级', 'DCT'=>'双离合', '1SPEED'=>'单速'
|
||
],
|
||
'RANGE_STD' => [
|
||
'CLTC'=>'CLTC', 'WLTP'=>'WLTP', 'EPA'=>'EPA', 'NEDC'=>'NEDC'
|
||
],
|
||
'EMISSION' => [
|
||
'CHN6'=>'国六', 'EURO6'=>'欧6', 'EPA'=>'美国EPA'
|
||
],
|
||
];
|
||
|
||
// *_code取中文
|
||
public function label(string $dict, ?string $code): ?string
|
||
{
|
||
if ($code === null || $code === '') return null;
|
||
$dict = strtoupper($dict);
|
||
$code = strtoupper($code);
|
||
return self::MAPS[$dict][$code] ?? $code;
|
||
}
|
||
|
||
// 静态映射把 *_code → 中文
|
||
private function backfill(array $d): array
|
||
{
|
||
$d['body_type'] = $this->label('BODY', $d['body_type_code']);
|
||
$d['drive_type'] = $this->label('DRIVE', $d['drive_type_code']);
|
||
$d['motor_layout'] = $this->label('MOTOR_LAYOUT', $d['motor_layout_code']);
|
||
$d['fuel_type'] = $this->label('FUEL', $d['fuel_type_code']);
|
||
$d['transmission'] = $this->label('TRANS', $d['transmission_code']);
|
||
$d['range_std'] = $this->label('RANGE_STD', $d['range_std_code']);
|
||
$d['emission_std'] = $this->label('EMISSION', $d['emission_std_code']);
|
||
return $d;
|
||
}
|
||
|
||
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
|
||
{
|
||
$p = $request->all();
|
||
|
||
$config = [];
|
||
$config['model'] = $this->modelClass;
|
||
switch ($scene) {
|
||
case 'default':
|
||
$config['where']['is_active'] = 1;
|
||
$config['order']['sort'] = 'desc';
|
||
$config['order']['id'] = 'desc';
|
||
break;
|
||
case 'index':
|
||
$config['where']['is_active'] = 1;
|
||
$config['order']['sort'] = 'desc';
|
||
$config['order']['id'] = 'desc';
|
||
break;
|
||
case 'show':
|
||
$config['where']['id'] = $p['id'];
|
||
$config['where']['is_active'] = 1;
|
||
break;
|
||
case 'store':
|
||
break;
|
||
case 'update':
|
||
$config['where']['id'] = $p['id'];
|
||
break;
|
||
case 'destroy':
|
||
$config['where']['id'] = $p['id'];
|
||
break;
|
||
}
|
||
|
||
return $config;
|
||
}
|
||
|
||
public function beforeServiceStore($data, Request $request)
|
||
{
|
||
$data = $this->backfill($data);
|
||
// 未提供时自动生成;若你想“强制覆盖人工输入”,可去掉 isset 判断
|
||
if (empty($data['model_code'])) {
|
||
$data['model_code'] = ModelNaming::buildModelCode($data);
|
||
}
|
||
if (empty($data['model_name'])) {
|
||
$data['model_name'] = ModelNaming::buildModelName($data);
|
||
}
|
||
// 填充系统字段
|
||
$data['sku_code'] = SkuCodeBuilder::build($data);
|
||
$data['corp_id'] = 0;
|
||
$data['created_by'] = $request->user->uid;
|
||
$data['updated_by'] = $request->user->uid;
|
||
// 指纹
|
||
$data['fingerprint'] = Fingerprint::makeFromArray($data);
|
||
return $data;
|
||
}
|
||
|
||
protected function afterServiceStore($res)
|
||
{
|
||
|
||
return $res;
|
||
}
|
||
|
||
|
||
public function beforeServiceUpdate($data, Request $request)
|
||
{
|
||
$data = $this->backfill($data);
|
||
// 未提供时自动生成;若你想“强制覆盖人工输入”,可去掉 isset 判断
|
||
if (empty($data['model_code'])) {
|
||
$data['model_code'] = ModelNaming::buildModelCode($data);
|
||
}
|
||
if (empty($data['model_name'])) {
|
||
$data['model_name'] = ModelNaming::buildModelName($data);
|
||
}
|
||
// 填充系统字段
|
||
$data['updated_by'] = $request->user->uid;
|
||
// 指纹
|
||
$data['fingerprint'] = Fingerprint::makeFromArray($data);
|
||
return $data;
|
||
}
|
||
|
||
protected function afterServiceUpdate($res)
|
||
{
|
||
|
||
return $res;
|
||
}
|
||
|
||
|
||
/** SKU 字段名 → dict 名(用于 i18n 取词 & 必填校验) */
|
||
protected static array $SKU_FIELD_TO_DICT = [
|
||
'brand_code' => 'BRAND',
|
||
'series_code' => 'SERIES',
|
||
'model_code' => 'MODEL',
|
||
'trim_code' => 'TRIM',
|
||
'trim_level_code' => 'TRIM_LEVEL',
|
||
'edition_code' => 'EDITION',
|
||
'fuel_type_code' => 'FUEL',
|
||
'drive_type_code' => 'DRIVE',
|
||
'transmission_code' => 'TRANS',
|
||
'motor_layout_code' => 'MOTOR_LAYOUT',
|
||
'body_type_code' => 'BODY',
|
||
'range_std_code' => 'RANGE_STD',
|
||
'emission_std_code' => 'EMISSION',
|
||
'color_exterior_code'=> 'COLOR',
|
||
'color_interior_code'=> 'COLOR',
|
||
];
|
||
|
||
/** 创建 SKU(含指纹与字典校验) */
|
||
public static function create(array $p, int $userId=0, int $corpId=0): VehicleSku
|
||
{
|
||
// SkuValidator::validateForCreate($p);
|
||
|
||
// 必填字典:由映射表控制(一般 BRAND/SERIES/FUEL)
|
||
$requiredDicts = MappingService::requiredDicts();
|
||
foreach (self::$SKU_FIELD_TO_DICT as $field => $dict) {
|
||
if (!MappingService::enabled($dict)) continue;
|
||
$isRequired = in_array($dict, $requiredDicts, true);
|
||
if ($isRequired) {
|
||
DictService::ensureExists($dict, $p[$field] ?? null, $field);
|
||
}
|
||
}
|
||
|
||
// 填充系统字段
|
||
$now = time();
|
||
$p['sku_code'] = $p['sku_code'] ?? ('SKU'.date('Ymd').'-'.substr(bin2hex(random_bytes(3)),0,6));
|
||
$p['corp_id'] = $p['corp_id'] ?? $corpId;
|
||
$p['created_by'] = $p['created_by'] ?? $userId;
|
||
$p['updated_by'] = $p['updated_by'] ?? $userId;
|
||
$p['created_at'] = $p['created_at'] ?? $now;
|
||
$p['updated_at'] = $p['updated_at'] ?? $now;
|
||
|
||
// 指纹
|
||
$p['fingerprint'] = Fingerprint::make($p /*, $scopeCorp=true/false*/);
|
||
|
||
return DB::connection()->transaction(function () use ($p) {
|
||
$sku = new VehicleSku($p);
|
||
$sku->save();
|
||
return $sku;
|
||
});
|
||
}
|
||
|
||
/** 更新 SKU(部分字段;指纹随事实变动而刷新) */
|
||
public static function update(int $id, array $p, int $userId=0): VehicleSku
|
||
{
|
||
SkuValidator::validateForUpdate($p);
|
||
|
||
$sku = VehicleSku::findOrFail($id);
|
||
|
||
// 若传入了必填 dict 的 code,需要确认字典存在
|
||
$requiredDicts = MappingService::requiredDicts();
|
||
foreach (self::$SKU_FIELD_TO_DICT as $field => $dict) {
|
||
if (!array_key_exists($field, $p)) continue;
|
||
if (!MappingService::enabled($dict)) continue;
|
||
$isRequired = in_array($dict, $requiredDicts, true);
|
||
if ($isRequired) {
|
||
DictService::ensureExists($dict, $p[$field], $field);
|
||
} else {
|
||
// 可选的:若传了 code 则也校验存在
|
||
if (($p[$field] ?? null) !== null && $p[$field] !== '') {
|
||
$exists = \app\model\DictTerm::where('dict',$dict)->where('code',$p[$field])->exists();
|
||
if (!$exists) throw new \InvalidArgumentException("字典不存在:{$dict}.{$p[$field]}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 刷新系统字段
|
||
$p['updated_by'] = $userId;
|
||
$p['updated_at'] = time();
|
||
|
||
// 赋值保存
|
||
$sku->fill($p);
|
||
|
||
// 只要影响到指纹的事实字段变动,就重算指纹
|
||
$fingerFields = [
|
||
'brand_code','series_code','model_year','fuel_type_code',
|
||
'drive_type_code','transmission_code','body_type_code',
|
||
'engine_disp_l','engine_turbo','battery_kwh','range_km',
|
||
'range_std_code','emission_std_code',
|
||
'corp_id'
|
||
];
|
||
foreach ($fingerFields as $f) {
|
||
if (array_key_exists($f, $p)) {
|
||
$sku->fingerprint = Fingerprint::make($sku->toArray());
|
||
break;
|
||
}
|
||
}
|
||
|
||
$sku->save();
|
||
return $sku;
|
||
}
|
||
|
||
/** 详情(带 i18n label 与标准标题) */
|
||
public static function detail(int $id, string $locale='zh-CN'): array
|
||
{
|
||
$sku = VehicleSku::findOrFail($id);
|
||
$data = $sku->toArray();
|
||
|
||
$labelOf = function(string $field) use ($data, $locale) {
|
||
$dict = self::$SKU_FIELD_TO_DICT[$field] ?? null;
|
||
if (!$dict) return null;
|
||
|
||
$code = $data[$field] ?? null;
|
||
// 若是品牌/车系,允许以中文兜底
|
||
$fallback = null;
|
||
if ($field === 'brand_code') $fallback = $data['brand_name'] ?? null;
|
||
if ($field === 'series_code') $fallback = $data['series_name'] ?? null;
|
||
|
||
// 版型/等级/特别版允许中文兜底
|
||
if ($field === 'trim_code') $fallback = $data['trim'] ?? null;
|
||
if ($field === 'trim_level_code') $fallback = $data['trim_level'] ?? null;
|
||
if ($field === 'edition_code') $fallback = $data['edition_name'] ?? null;
|
||
|
||
// 颜色兜底中文
|
||
if ($field === 'color_exterior_code') $fallback = $data['color_exterior_text'] ?? null;
|
||
if ($field === 'color_interior_code') $fallback = $data['color_interior_text'] ?? null;
|
||
|
||
return \app\service\DictService::label($dict, $code, $locale, $fallback);
|
||
};
|
||
|
||
$i18n = [
|
||
'brand_label' => $labelOf('brand_code'),
|
||
'series_label' => $labelOf('series_code'),
|
||
'model_label' => \app\service\DictService::label('MODEL', $data['model_code'] ?? null, $locale, $data['model_name'] ?? null),
|
||
'trim_label' => $labelOf('trim_code'),
|
||
'trim_level_label' => $labelOf('trim_level_code'),
|
||
'edition_label' => $labelOf('edition_code'),
|
||
'fuel_label' => $labelOf('fuel_type_code'),
|
||
'drive_label' => $labelOf('drive_type_code'),
|
||
'trans_label' => $labelOf('transmission_code'),
|
||
'motor_layout_label' => $labelOf('motor_layout_code'),
|
||
'body_label' => $labelOf('body_type_code'),
|
||
'range_std_label' => $labelOf('range_std_code'),
|
||
'emission_label' => $labelOf('emission_std_code'),
|
||
'color_exterior_label' => $labelOf('color_exterior_code'),
|
||
'color_interior_label' => $labelOf('color_interior_code'),
|
||
'listing_title' => TitleBuilder::build($data, $locale),
|
||
];
|
||
|
||
return ['sku'=>$data, 'i18n'=>$i18n];
|
||
}
|
||
|
||
/** 列表查询(支持基础筛选 & i18n 列) */
|
||
public static function list(array $q, string $locale='zh-CN', int $page=1, int $pageSize=20): array
|
||
{
|
||
$builder = VehicleSku::query();
|
||
|
||
// 常用筛选
|
||
if (!empty($q['brand_code'])) $builder->where('brand_code', $q['brand_code']);
|
||
if (!empty($q['series_code'])) $builder->where('series_code', $q['series_code']);
|
||
if (!empty($q['fuel_type_code'])) $builder->where('fuel_type_code', $q['fuel_type_code']);
|
||
if (!empty($q['model_year'])) $builder->where('model_year', (int)$q['model_year']);
|
||
if (isset($q['is_active'])) $builder->where('is_active', (int)$q['is_active']);
|
||
|
||
$builder->orderByDesc('created_at');
|
||
|
||
$total = (clone $builder)->count();
|
||
$rows = $builder->forPage($page, $pageSize)->get()->toArray();
|
||
|
||
// 附加 i18n 字段(轻量)
|
||
foreach ($rows as &$row) {
|
||
$row['brand_label'] = DictService::label('BRAND', $row['brand_code'] ?? null, $locale, $row['brand_name'] ?? null);
|
||
$row['series_label'] = DictService::label('SERIES', $row['series_code'] ?? null, $locale, $row['series_name'] ?? null);
|
||
$row['fuel_label'] = DictService::label('FUEL', $row['fuel_type_code'] ?? null, $locale, null);
|
||
$row['listing_title']= TitleBuilder::build($row, $locale);
|
||
}
|
||
|
||
return ['total'=>$total, 'list'=>$rows, 'page'=>$page, 'page_size'=>$pageSize];
|
||
}
|
||
} |