diff --git a/app/admin/controller/BrandController.php b/app/admin/controller/BrandController.php new file mode 100644 index 0000000..f416d9d --- /dev/null +++ b/app/admin/controller/BrandController.php @@ -0,0 +1,53 @@ +service = new BrandService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (!empty($data['logo'])) + $data['logo_full'] = (new OssService)->generatePrivateUrl($data['logo']); + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (empty($data['logo'])) + $data['logo_full'] = (new OssService)->generatePrivateUrl($data['logo']); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/controller/BrandSeriesController.php b/app/admin/controller/BrandSeriesController.php new file mode 100644 index 0000000..c2d95f7 --- /dev/null +++ b/app/admin/controller/BrandSeriesController.php @@ -0,0 +1,48 @@ +service = new BrandSeriesService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/controller/SysAccountController.php b/app/admin/controller/SysAccountController.php new file mode 100644 index 0000000..64398bb --- /dev/null +++ b/app/admin/controller/SysAccountController.php @@ -0,0 +1,52 @@ +service = new SysAccountService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + $data['currency_name'] = SysCurrency::where('id', $data['currency_id'])->value('currency_name'); + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + $data['currency_name'] = SysCurrency::where('id', $data['currency_id'])->value('currency_name'); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/controller/SysCurrencyController.php b/app/admin/controller/SysCurrencyController.php new file mode 100644 index 0000000..0e96169 --- /dev/null +++ b/app/admin/controller/SysCurrencyController.php @@ -0,0 +1,52 @@ +service = new SysCurrencyService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (isset($data['last_updated'])) + $data['last_updated'] = date('Y-m-d H:i:s', $data['last_updated']); + return $data; + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (isset($data['last_updated'])) + $data['last_updated'] = date('Y-m-d H:i:s', $data['last_updated']); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/controller/SysDictController.php b/app/admin/controller/SysDictController.php new file mode 100644 index 0000000..34be3e1 --- /dev/null +++ b/app/admin/controller/SysDictController.php @@ -0,0 +1,78 @@ +service = new SysDictService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['sysDictItem'])) + $data['sysDictItem'] = SysDictItem::where('dict_id',$data['id'])->get(); + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + $data['sysDictItem'] = SysDictItem::where('dict_id',$data['id'])->get(); + return $data; + } + + + + public function listScopeFormat($data) + { + $rdata = []; + foreach ($data as $item) { + $rdata[$item['dict_code']] = $item['sysDictItem']; + } + return $rdata; + } + + public function listScope(Request $request) + { + try { + $raw = $this->service->serviceListAllScope($request); + $data = $this->transformData($raw, method_exists($this, 'listScopeFormat') + ? [$this, 'listScopeFormat'] + : null); + return $this->success($data); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/admin/controller/SysDictItemController.php b/app/admin/controller/SysDictItemController.php new file mode 100644 index 0000000..9262933 --- /dev/null +++ b/app/admin/controller/SysDictItemController.php @@ -0,0 +1,48 @@ +service = new SysDictItemService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/controller/SysPortController.php b/app/admin/controller/SysPortController.php new file mode 100644 index 0000000..d98577e --- /dev/null +++ b/app/admin/controller/SysPortController.php @@ -0,0 +1,53 @@ +service = new SysPortService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (isset($data['country_id'])) + if (isset($data['country_id'])) + $data['country_name'] = Country::where('id', $data['country_id'])->value('cn_addr'); + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + $data['country_name'] = Country::where('id', $data['country_id'])->value('cn_addr'); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/controller/VehColorController.php b/app/admin/controller/VehColorController.php new file mode 100644 index 0000000..0d208ae --- /dev/null +++ b/app/admin/controller/VehColorController.php @@ -0,0 +1,33 @@ +service = new VehColorService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + // protected function indexFormat($data) + // { + // if (isset($data['created_at'])) + // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + // if (isset($data['updated_at'])) + // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + // return $data; + // } +} \ No newline at end of file diff --git a/app/admin/controller/VehModificationController.php b/app/admin/controller/VehModificationController.php new file mode 100644 index 0000000..fc057e1 --- /dev/null +++ b/app/admin/controller/VehModificationController.php @@ -0,0 +1,48 @@ +service = new VehModificationService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } + + /** + * 数据格式化 + * + * @param array $data + * @return array + */ + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/logic/BrandLogic.php b/app/admin/logic/BrandLogic.php new file mode 100644 index 0000000..6b12364 --- /dev/null +++ b/app/admin/logic/BrandLogic.php @@ -0,0 +1,17 @@ +validator; + } + + /** + * 构建查询条件 + */ + + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['sort'] = 'desc'; + $config['order']['id'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + $config['where']['brand_id'] = $p['brand_id']; + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['series_code','series_name'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + //$config['where']['status'] = 1; + break; + case 'show': + $config['where']['id'] = $p['id']; + //$config['where']['status'] = 1; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/service/BrandService.php b/app/admin/service/BrandService.php new file mode 100644 index 0000000..c35df96 --- /dev/null +++ b/app/admin/service/BrandService.php @@ -0,0 +1,79 @@ +validator; + } + + /** + * 构建查询条件 + */ + + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['sort'] = 'desc'; + $config['order']['id'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['brand_code','brand_name'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + //$config['where']['status'] = 1; + break; + case 'show': + $config['where']['id'] = $p['id']; + //$config['where']['status'] = 1; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/service/SysAccountService.php b/app/admin/service/SysAccountService.php new file mode 100644 index 0000000..d413778 --- /dev/null +++ b/app/admin/service/SysAccountService.php @@ -0,0 +1,82 @@ +validator; + } + + /** + * 构建查询条件 + */ + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['sort'] = 'desc'; + $config['order']['id'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 状态 + if (isset($p['type']) && $p['type']) { + $config['where']['type'] = $p['type']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['account_name','account_number','bank_name','bank_branch','description'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + //$config['where']['status'] = 1; + break; + case 'show': + $config['where']['id'] = $p['id']; + //$config['where']['status'] = 1; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/service/SysCurrencyService.php b/app/admin/service/SysCurrencyService.php new file mode 100644 index 0000000..19f0529 --- /dev/null +++ b/app/admin/service/SysCurrencyService.php @@ -0,0 +1,61 @@ +validator; + } + + + /** + * 构造 CrudUtil 查询配置 + */ + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'default': + $config['order']['id'] = 'desc'; + case 'index': + break; + case 'show': + $config['where']['id'] = $p['id']; + break; + case 'store': + break; + case 'update': + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/service/SysDictItemService.php b/app/admin/service/SysDictItemService.php new file mode 100644 index 0000000..577b9f0 --- /dev/null +++ b/app/admin/service/SysDictItemService.php @@ -0,0 +1,75 @@ +validator; + } + + /** + * 构建查询条件 + */ + protected function buildQueryConfig(Request $request, string $scene = 'index'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['sort'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + + $config['where']['dict_id'] = $p['id']; + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['item_label','item_value','remark'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + break; + case 'show': + $config['where']['id'] = $p['id']; + break; + case 'update': + case 'destroy': + $config['where']['id'] = $p['id']; + break; + case 'store': + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/service/SysDictService.php b/app/admin/service/SysDictService.php new file mode 100644 index 0000000..1261c30 --- /dev/null +++ b/app/admin/service/SysDictService.php @@ -0,0 +1,109 @@ +validator; + } + + + /** + * 构建查询条件 + */ + protected function buildQueryConfig(Request $request, string $scene = 'index'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['id'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['dict_name','dict_code','description'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + + // ====== ====== 关联 ====== ====== + $config['with']['sysDictItem'][] = 'item_label'; + $config['with']['sysDictItem'][] = 'item_value'; + break; + case 'show': + $config['where']['id'] = $p['id']; + // ====== ====== 关联 ====== ====== + $config['with']['sysDictItem'][] = 'item_label'; + $config['with']['sysDictItem'][] = 'item_value'; + break; + case 'update': + case 'destroy': + $config['where']['id'] = $p['id']; + break; + case 'store': + break; + case 'scope': + $config['order']['id'] = 'desc'; + if (isset($p['scope']) && !empty($p['scope'])) { + $config['where']['id'] = ['in', explode(',', $p['scope'])]; + } + $config['with']['sysDictItem'][] = 'dict_id'; + $config['with']['sysDictItem'][] = 'item_label'; + $config['with']['sysDictItem'][] = 'item_value'; + break; + } + + return $config; + } + + + public function serviceListAllScope(Request $request) + { + $config = $this->buildQueryConfig($request, 'scope'); + $result = $this->logicListAll($config); + return $this->postProcess('ListAllScope', $result); + } + + + public function afterServiceListAllScope($data) + { + + // $data->each(function (&$item) { + + // }); + + return $data; + } +} \ No newline at end of file diff --git a/app/admin/service/SysPortService.php b/app/admin/service/SysPortService.php new file mode 100644 index 0000000..7a2dfd0 --- /dev/null +++ b/app/admin/service/SysPortService.php @@ -0,0 +1,103 @@ +validator; + } + + /** + * 构建查询条件 + */ + + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['sort'] = 'desc'; + $config['order']['id'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['port_code','port_name'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + //$config['where']['status'] = 1; + break; + case 'show': + $config['where']['id'] = $p['id']; + //$config['where']['status'] = 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) + { + if(empty($data['lat'])) + unset($data['lat']); + if(empty($data['lng'])) + unset($data['lng']); + return $data; + } + + /** + * 添加前置处理 + */ + public function beforeServiceUpdate($data, Request $request) + { + if(empty($data['lat'])) + unset($data['lat']); + if(empty($data['lng'])) + unset($data['lng']); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/service/VehColorService.php b/app/admin/service/VehColorService.php new file mode 100644 index 0000000..a701ef4 --- /dev/null +++ b/app/admin/service/VehColorService.php @@ -0,0 +1,31 @@ +validator; + } +} \ No newline at end of file diff --git a/app/admin/service/VehModificationService.php b/app/admin/service/VehModificationService.php new file mode 100644 index 0000000..6c56d9b --- /dev/null +++ b/app/admin/service/VehModificationService.php @@ -0,0 +1,83 @@ +validator; + } + + /** + * 构建查询条件 + */ + + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + // ====== ====== 排序 ====== ====== + $config['order']['sort'] = 'desc'; + $config['order']['id'] = 'desc'; + + // ====== ====== 筛选 ====== ====== + // 状态 + if (isset($p['status']) && in_array($p['status'], [0,1])) { + $config['where']['status'] = $p['status']; + } + // 状态 + if (isset($p['type']) && $p['type']) { + $config['where']['type'] = $p['type']; + } + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['code','name'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + //$config['where']['status'] = 1; + break; + case 'show': + $config['where']['id'] = $p['id']; + //$config['where']['status'] = 1; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/validate/BrandSeriesValidate.php b/app/admin/validate/BrandSeriesValidate.php new file mode 100644 index 0000000..31e30e2 --- /dev/null +++ b/app/admin/validate/BrandSeriesValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/BrandValidate.php b/app/admin/validate/BrandValidate.php new file mode 100644 index 0000000..370728d --- /dev/null +++ b/app/admin/validate/BrandValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/SysAccountValidate.php b/app/admin/validate/SysAccountValidate.php new file mode 100644 index 0000000..65cc1d7 --- /dev/null +++ b/app/admin/validate/SysAccountValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/SysCurrencyValidate.php b/app/admin/validate/SysCurrencyValidate.php new file mode 100644 index 0000000..4aebb21 --- /dev/null +++ b/app/admin/validate/SysCurrencyValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/SysDictItemValidate.php b/app/admin/validate/SysDictItemValidate.php new file mode 100644 index 0000000..2d4ad58 --- /dev/null +++ b/app/admin/validate/SysDictItemValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/SysDictValidate.php b/app/admin/validate/SysDictValidate.php new file mode 100644 index 0000000..bdb6d1e --- /dev/null +++ b/app/admin/validate/SysDictValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/SysPortValidate.php b/app/admin/validate/SysPortValidate.php new file mode 100644 index 0000000..26cb9cd --- /dev/null +++ b/app/admin/validate/SysPortValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/VehColorValidate.php b/app/admin/validate/VehColorValidate.php new file mode 100644 index 0000000..1c619ec --- /dev/null +++ b/app/admin/validate/VehColorValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/admin/validate/VehModificationValidate.php b/app/admin/validate/VehModificationValidate.php new file mode 100644 index 0000000..805b049 --- /dev/null +++ b/app/admin/validate/VehModificationValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/common/controller/PresetController.php b/app/common/controller/PresetController.php index 37073c4..0d3ebf1 100644 --- a/app/common/controller/PresetController.php +++ b/app/common/controller/PresetController.php @@ -3,6 +3,15 @@ namespace app\common\controller; use app\admin\service\RoleService; +use app\common\service\BrandSeriesService; +use app\common\service\BrandService; +use app\common\service\SysAccountService; +use app\common\service\SysCurrencyService; +use app\common\service\SysDictItemService; +use app\common\service\SysDictService; +use app\common\service\SysPortService; +use app\common\service\VehColorService; +use app\common\service\VehModificationService; use support\Request; use app\common\service\CorpService; use app\common\service\CorpFeishuAppService; @@ -76,6 +85,99 @@ class PresetController extends BaseController } } + public function currency() + { + try { + $result = (new SysCurrencyService)->getList(); + return $this->success($result, '获取货币列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function brand() + { + try { + $result = (new BrandService)->getList(); + return $this->success($result, '获取品牌列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function series(Request $request) + { + try { + $params = $request->all(); + $result = (new BrandSeriesService)->getList($params); + return $this->success($result, '获取车系列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function account() + { + try { + $result = (new SysAccountService)->getList(); + return $this->success($result, '获取账号列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function dict() + { + try { + $result = (new SysDictService)->getList(); + return $this->success($result, '获取字典列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function dictItem(Request $request) + { + try { + $params = $request->all(); + $result = (new SysDictItemService)->getList($params); + return $this->success($result, '获取字典项列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function port() + { + try { + $result = (new SysPortService)->getList(); + return $this->success($result, '获取港口列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function color(Request $request) + { + try { + $params = $request->all(); + $result = (new VehColorService)->getList($params); + return $this->success($result, '获取颜色列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function modification() + { + try { + $result = (new VehModificationService)->getList(); + return $this->success($result, '获取货币列表'); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + public function cacheClear() { diff --git a/app/common/controller/StockOrdersController.php b/app/common/controller/StockOrdersController.php new file mode 100644 index 0000000..4bb5a50 --- /dev/null +++ b/app/common/controller/StockOrdersController.php @@ -0,0 +1,33 @@ +service = new StockOrdersService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + // protected function indexFormat($data) + // { + // if (isset($data['created_at'])) + // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + // if (isset($data['updated_at'])) + // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + // return $data; + // } +} \ No newline at end of file diff --git a/app/common/controller/VehDictI18nController.php b/app/common/controller/VehDictI18nController.php new file mode 100644 index 0000000..6a87c9f --- /dev/null +++ b/app/common/controller/VehDictI18nController.php @@ -0,0 +1,33 @@ +service = new VehDictI18nService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + // protected function indexFormat($data) + // { + // if (isset($data['created_at'])) + // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + // if (isset($data['updated_at'])) + // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + // return $data; + // } +} \ No newline at end of file diff --git a/app/common/controller/VehDictMappingController.php b/app/common/controller/VehDictMappingController.php new file mode 100644 index 0000000..d1a1dfe --- /dev/null +++ b/app/common/controller/VehDictMappingController.php @@ -0,0 +1,33 @@ +service = new VehDictMappingService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + // protected function indexFormat($data) + // { + // if (isset($data['created_at'])) + // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + // if (isset($data['updated_at'])) + // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + // return $data; + // } +} \ No newline at end of file diff --git a/app/common/controller/VehVehicleListingController.php b/app/common/controller/VehVehicleListingController.php new file mode 100644 index 0000000..59d49aa --- /dev/null +++ b/app/common/controller/VehVehicleListingController.php @@ -0,0 +1,33 @@ +service = new VehVehicleListingService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + // protected function indexFormat($data) + // { + // if (isset($data['created_at'])) + // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + // if (isset($data['updated_at'])) + // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + // return $data; + // } +} \ No newline at end of file diff --git a/app/common/controller/VehVehicleSkuController.php b/app/common/controller/VehVehicleSkuController.php new file mode 100644 index 0000000..3272f28 --- /dev/null +++ b/app/common/controller/VehVehicleSkuController.php @@ -0,0 +1,204 @@ +service = new VehVehicleSkuService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } + + protected function showFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } + + + public function store(Request $request) + { + try { + $data = $this->validate($this->service->getValidator(), $request->post(), 'store'); + $res = $this->service->serviceStore($request, $data); + return $this->success($res, '创建成功'); + } catch (\PDOException $e) { + $errorCode = $e->getCode(); + $errorMessage = $e->getMessage(); + if ($errorCode == '23000') { + if (strpos($errorMessage, "Duplicate entry") !== false && strpos($errorMessage, "uk_fingerprint") !== false) { + return $this->error("该车辆指纹已存在,请勿重复提交"); + } + // 其他类型的完整性约束错误 + throw new BusinessException("数据完整性错误", 0, $e); + } + // 重新抛出其他类型的异常 + throw $e; + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + public function update(Request $request) + { + try { + $data = $this->validate($this->service->getValidator(), $request->post(), 'update'); + $res = $this->service->serviceUpdate($request, $data); + return $this->success([], '更新成功'); + } catch (\PDOException $e) { + $errorCode = $e->getCode(); + $errorMessage = $e->getMessage(); + if ($errorCode == '23000') { + if (strpos($errorMessage, "Duplicate entry") !== false && strpos($errorMessage, "uk_fingerprint") !== false) { + return $this->error("该车辆指纹已存在,请勿重复提交"); + } + // 其他类型的完整性约束错误 + throw new BusinessException("数据完整性错误", 0, $e); + } + // 重新抛出其他类型的异常 + throw $e; + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + + public function fields_desc(Request $request) + { + $data = [ + 'sku_code' , // 'SKU编码,系统生成', + 'fingerprint' , // '规格指纹(sha256)去重', + 'brand_code' , // '品牌代码,如 BYD、HYUNDAI、KIA', + 'brand_name' , // '品牌名称展示,如 比亚迪、现代、起亚', + 'series_code' , // '车系代码,如 ELANTRA、SONGPLUS', + 'series_name' , // '车系名称展示', + 'model_code' , // '车型/配置代码(工厂/内部编码)', + 'model_name' , // '车型/配置名称(可读,如 伊兰特 1.5L、海狮07)', + 'model_year' , // '年款(YYYY)', + 'trim_code' , // '版型代码(TRIM)', + 'trim_level_code' , // '配置等级代码(TRIM_LEVEL)', + 'edition_code' , // '特别版代码(EDITION)', + 'trim' , // '版型/细分配置(如 TOP旗舰版、荣耀版)', + 'trim_level' , // '配置等级(豪华型/旗舰型等,便于筛选)', + 'edition_name' , // '特别版/纪念版(如 荣耀版、致敬版等)', + 'body_type_code' , // '车身类型:SEDAN/SUV/HB/MPV/Pickup', + 'drive_type_code' , // '驱动:FWD/RWD/AWD/4WD', + 'motor_layout_code' , // '电驱/电机布置(MOTOR_LAYOUT):FWD/RWD/AWD/DM-i/DM-p 等', + 'fuel_type_code' , // '能源:GAS/HEV/PHEV/EV/EREV', + 'engine_disp_l' , // '排量(L),如 1.5;纯电为空', + 'engine_turbo' , // '0' COMMENT '是否涡轮(0/1)', + 'engine_power_kw' , // '最大功率(kW)', + 'battery_kwh' , // '电池容量(kWh)', + 'transmission_code' , // '变速箱:MT/AT/CVT/DCT/1SPEED', + 'gears' , // '挡位数(EV多为1)', + 'range_km' , // '续航里程(km)', + 'range_std_code' , // '续航标准:CLTC/WLTP/EPA/NEDC', + 'doors' , // '门数', + 'seats' , // '座位数', + 'weight_kg' , // '整备质量(kg)', + 'dimensions_mm' , // '尺寸(mm):{length,width,height,wheelbase}', + 'color_exterior_code' , // '外观色代码', + 'color_exterior_text' , // '外观色名称', + 'color_interior_code' , // '内饰色代码', + 'color_interior_text' , // '内饰色名称', + 'options_official' , // '官方选装(结构化 JSON)', + 'features' , // '功能/安全/智能配置(结构化 JSON)', + 'emission_std_code' , // '排放标准:CHN6/EURO6/EPA', + 'origin_country' , // '原产国 ISO-3166-1 alpha-2', + 'currency' , // 'CNY' COMMENT '币种 ISO-4217', + 'msrp' , // '建议零售价/参考价', + 'is_active' , // '1' COMMENT '启用(1)/禁用(0)', + 'corp_id' , // '0' COMMENT '租户/公司(全局库可写0)', + 'remark' , // '备注', + 'created_by' , // '0', + 'updated_by' , // '0', + ]; + + + $data = [ + 'fingerprint' , // '规格指纹(sha256)去重', + 'brand_name' , // '品牌名称展示,如 比亚迪、现代、起亚', + 'series_name' , // '车系名称展示', + 'model_name' , // '车型/配置名称(可读,如 伊兰特 1.5L、海狮07)', + 'model_year' , // '年款(YYYY)', + 'trim' , // '版型/细分配置(如 TOP旗舰版、荣耀版)', + 'trim_level' , // '配置等级(豪华型/旗舰型等,便于筛选)', + 'edition_name' , // '特别版/纪念版(如 荣耀版、致敬版等)', + 'engine_disp_l' , // '排量(L),如 1.5;纯电为空', + 'engine_turbo' , // '0' COMMENT '是否涡轮(0/1)', + 'engine_power_kw' , // '最大功率(kW)', + 'battery_kwh' , // '电池容量(kWh)', + 'gears' , // '挡位数(EV多为1)', + 'range_km' , // '续航里程(km)', + 'doors' , // '门数', + 'seats' , // '座位数', + 'weight_kg' , // '整备质量(kg)', + 'dimensions_mm' , // '尺寸(mm):{length,width,height,wheelbase}', + 'color_exterior_text' , // '外观色名称', + 'color_interior_text' , // '内饰色名称', + 'options_official' , // '官方选装(结构化 JSON)', + 'features' , // '功能/安全/智能配置(结构化 JSON)', + 'origin_country' , // '原产国 ISO-3166-1 alpha-2', + 'currency' , // 'CNY' COMMENT '币种 ISO-4217', + 'msrp' , // '建议零售价/参考价', + 'is_active' , // '1' COMMENT '启用(1)/禁用(0)', + 'corp_id' , // '0' COMMENT '租户/公司(全局库可写0)', + 'remark' , // '备注', + 'created_by' , // '0', + 'updated_by' , // '0', + + 'body_type', // '车身类型' + 'drive_type', // '驱动:FWD/RWD/AWD/4WD', + 'motor_layout', // '电机布置(MOTOR_LAYOUT):FWD/RWD/AWD/DM-i/DM-p 等' + 'fuel_type', // '能源:GAS/HEV/PHEV/EV/EREV' + 'transmission', // '变速箱:MT/AT/CVT/DCT/1SPEED' + 'range_std', // '续航标准:CLTC/WLTP/EPA/NEDC' + 'emission_std', // '排放标准:CHN6/EURO6/EPA' + + 'sku_code' , // 'SKU编码,系统生成', + 'brand_code' , // '品牌代码,如 BYD、HYUNDAI、KIA', + 'series_code' , // '车系代码,如 ELANTRA、SONGPLUS', + 'model_code' , // '车型/配置代码(工厂/内部编码)', + 'trim_code' , // '版型代码(TRIM)', + 'trim_level_code' , // '配置等级代码(TRIM_LEVEL)', + 'edition_code' , // '特别版代码(EDITION)', + 'color_exterior_code' , // '外观色代码', + 'color_interior_code' , // '内饰色代码', + + 'body_type_code' , // '车身类型:SEDAN/SUV/HB/MPV/Pickup', + 'drive_type_code' , // '驱动:FWD/RWD/AWD/4WD', + 'motor_layout_code' , // '电驱/电机布置(MOTOR_LAYOUT):FWD/RWD/AWD/DM-i/DM-p 等', + 'fuel_type_code' , // '能源:GAS/HEV/PHEV/EV/EREV', + 'transmission_code' , // '变速箱:MT/AT/CVT/DCT/1SPEED', + 'range_std_code' , // '续航标准:CLTC/WLTP/EPA/NEDC', + 'emission_std_code' , // '排放标准:CHN6/EURO6/EPA', + ]; + } +} \ No newline at end of file diff --git a/app/common/controller/VehVehicleUnitController.php b/app/common/controller/VehVehicleUnitController.php new file mode 100644 index 0000000..3d2e7e2 --- /dev/null +++ b/app/common/controller/VehVehicleUnitController.php @@ -0,0 +1,33 @@ +service = new VehVehicleUnitService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + // protected function indexFormat($data) + // { + // if (isset($data['created_at'])) + // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + // if (isset($data['updated_at'])) + // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + // return $data; + // } +} \ No newline at end of file diff --git a/app/common/logic/BrandLogic.php b/app/common/logic/BrandLogic.php new file mode 100644 index 0000000..3040572 --- /dev/null +++ b/app/common/logic/BrandLogic.php @@ -0,0 +1,22 @@ +orderBy('sort','desc')->get(['id', 'brand_code', 'brand_name', 'logo', 'description']); + } + +} \ No newline at end of file diff --git a/app/common/logic/BrandSeriesLogic.php b/app/common/logic/BrandSeriesLogic.php new file mode 100644 index 0000000..956ca55 --- /dev/null +++ b/app/common/logic/BrandSeriesLogic.php @@ -0,0 +1,22 @@ +where($where)->orderBy('sort','desc')->get(['id', 'series_code', 'series_name', 'description']); + } + +} \ No newline at end of file diff --git a/app/common/logic/Fingerprint.php b/app/common/logic/Fingerprint.php new file mode 100644 index 0000000..a066ada --- /dev/null +++ b/app/common/logic/Fingerprint.php @@ -0,0 +1,262 @@ +['msrp']]); // 可选排除 + */ +class Fingerprint +{ + /** 你的 veh_vehicle_sku 所有列(保持与表结构一致,顺序无关但建议完整) */ + public const COLS = [ + 'id','sku_code','fingerprint', + 'brand_code','brand_name','series_code','series_name', + 'model_code','model_name','model_year', + 'trim_code','trim_level_code','edition_code', + 'trim','trim_level','edition_name', + 'body_type_code','drive_type_code','motor_layout_code','fuel_type_code', + 'engine_disp_l','engine_turbo','engine_power_kw','battery_kwh', + 'transmission_code','gears','range_km','range_std_code', + 'doors','seats','weight_kg','dimensions_mm', + 'color_exterior_code','color_exterior_text', + 'color_interior_code','color_interior_text', + 'options_official','features','emission_std_code', + 'origin_country','currency','msrp', + 'is_active','corp_id','remark', + 'created_by','updated_by','created_at','updated_at', + ]; + + /** 默认剔除:技术/循环依赖字段 */ + public const EXCLUDE_DEFAULT = [ + 'id','sku_code','fingerprint', 'dimensions_mm', 'options_official', 'features', + 'origin_country','currency','msrp', + 'is_active','corp_id','remark', + 'created_by','updated_by','created_at','updated_at' + ]; + + /** 识别为“code类”的字段模式(统一大写) */ + private const CODE_FIELD_REGEX = '/(_code|_country|^currency)$/i'; + + /** 识别为 JSON 字段 */ + private const JSON_FIELDS = ['dimensions_mm','options_official','features']; + + /** + * 从 Eloquent 模型生成 + */ + public static function makeFromModel($model, array $opts = []): string + { + return self::makeFromArray($model->toArray(), $opts); + } + + /** + * 从数组生成指纹 + * @param array $row 一般为 $_POST 合并后的业务数据或 $model->toArray() + * @param array $opts ['exclude'=>[], 'extra'=>[], 'salt'=>string] + * - exclude: 额外排除的字段(默认仅排除 id,fingerprint) + * - extra : 额外拼入的键值(例如上下文、租户策略),会参与计算 + * - salt : 额外加盐字符串 + */ + public static function makeFromArray(array $row, array $opts = []): string + { + $exclude = array_values(array_unique(array_merge(self::EXCLUDE_DEFAULT, $opts['exclude'] ?? []))); + $extra = $opts['extra'] ?? []; + $salt = $opts['salt'] ?? ''; + + // 1) 统一抽取“表中定义的字段” + extra,缺失字段也要纳入(值按 NULL 处理) + $data = []; + foreach (self::COLS as $col) { + if (in_array($col, $exclude, true)) continue; + $data[$col] = array_key_exists($col, $row) ? $row[$col] : null; + } + // extra 也参与 + foreach ($extra as $k => $v) { + if (!is_string($k) || $k === '') continue; + $data['__extra__'.$k] = $v; + } + + // 2) 规范化(按字段特征) + $canon = []; + foreach ($data as $k => $v) { + $canon[$k] = self::normalize($k, $v); + } + + // 3) 键名排序,确保顺序无关 + ksort($canon, SORT_STRING); + + // 4) 拼成稳定串: key=value | key=value... + $parts = []; + foreach ($canon as $k => $v) { + // 为空也必须占位,避免 key 消失影响其他键的意义 + $parts[] = $k.'='.$v; + } + if ($salt !== '') { + $parts[] = '__salt__='.$salt; + } + $plain = implode('|', $parts); + + // 5) sha256 + return hash('sha256', $plain); + } + + /** + * 规范化单个字段 + * - NULL -> 特殊占位 "∅"(避免与空串混淆) + * - 布尔/整数/浮点 -> 统一数字字符串 + * - code类字段 -> 去首尾空白 & 转大写 + * - 普通字符串 -> trim & 压缩连续空白为单空格 + * - JSON字段 -> 解析后做“键排序 + 值规范化 + 索引数组可排序”,再无空白编码 + */ + private static function normalize(string $field, $value): string + { + if ($value === null) return '∅'; + + // 数值类型 + if (is_int($value)) return (string)$value; + if (is_bool($value)) return $value ? '1' : '0'; + + if (is_float($value) || (is_string($value) && is_numeric($value) && self::looksLikeNumberField($field))) { + // 统一浮点格式,最多 6 位小数并去尾零 + $num = (float)$value; + $s = number_format($num, 6, '.', ''); + $s = rtrim(rtrim($s, '0'), '.'); + return $s === '' ? '0' : $s; + } + + // JSON 字段 + if (in_array($field, self::JSON_FIELDS, true)) { + return self::normalizeJson($value); + } + + // 字符串 + if (is_string($value)) { + $s = trim($value); + // code类:统一大写 + if (preg_match(self::CODE_FIELD_REGEX, $field)) { + $s = mb_strtoupper($s, 'UTF-8'); + } else { + // 普通文本:压缩连续空白 + $s = preg_replace('/\s+/u', ' ', $s); + } + return $s === '' ? '' : $s; + } + + // 数组(极少出现,按 JSON 处理) + if (is_array($value)) { + return self::normalizeJson($value); + } + + // 其他类型(对象等):转 JSON 后处理 + return self::normalizeJson($value); + } + + /** 判断这个字段是否“看起来像数值字段” */ + private static function looksLikeNumberField(string $field): bool + { + static $numKeys = [ + 'engine_disp_l','engine_power_kw','battery_kwh','gears', + 'range_km','doors','seats','weight_kg','msrp','model_year', + 'created_at','updated_at','corp_id','is_active' + ]; + return in_array($field, $numKeys, true); + } + + /** + * JSON 规范化: + * - 字符串:若能 json_decode 则按 JSON;否则当普通字符串再规范化 + * - 对象:键名排序;值递归规范化 + * - 索引数组:元素递归规范化后,按字符串值排序(视为集合,避免顺序抖动) + * - 最终 json_encode(JSON_UNESCAPED_UNICODE|UNESCAPED_SLASHES) + */ + private static function normalizeJson($val): string + { + // 已是数组/对象 + if (is_array($val)) { + $normalized = self::canonArray($val); + return json_encode($normalized, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + } + + // 字符串尝试解析 + if (is_string($val)) { + $val = trim($val); + if ($val === '') return '""'; + $decoded = json_decode($val, true); + if (json_last_error() === JSON_ERROR_NONE) { + $normalized = self::canonArray($decoded); + return json_encode($normalized, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + } + // 非 JSON 字符串,按普通字符串规范化 + $s = preg_replace('/\s+/u', ' ', $val); + return json_encode($s, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + } + + // 其他类型:直接编码 + return json_encode($val, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + } + + /** 递归规范化数组/对象 */ + private static function canonArray($arr) + { + if (!is_array($arr)) return $arr; + + // 关联数组(对象) + if (self::isAssoc($arr)) { + $out = []; + ksort($arr, SORT_STRING); + foreach ($arr as $k => $v) { + $out[(string)$k] = self::canonValue($v); + } + return $out; + } + + // 索引数组(当集合处理:元素字符串化后排序) + $tmp = []; + foreach ($arr as $v) { + $tmp[] = self::canonValue($v); + } + // 将元素转为字符串再排序,确保稳定 + usort($tmp, function($a, $b) { + $sa = is_scalar($a) ? (string)$a : json_encode($a, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + $sb = is_scalar($b) ? (string)$b : json_encode($b, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + return strcmp($sa, $sb); + }); + return $tmp; + } + + private static function canonValue($v) + { + if (is_array($v)) return self::canonArray($v); + if (is_bool($v)) return $v ? 1 : 0; + if (is_int($v)) return $v; + if (is_float($v)) { + $s = number_format($v, 6, '.', ''); + $s = rtrim(rtrim($s, '0'), '.'); + return $s === '' ? '0' : $s; + } + if (is_string($v)) { + $s = trim($v); + $s = preg_replace('/\s+/u', ' ', $s); + return $s; + } + return $v; + } + + private static function isAssoc(array $arr): bool + { + // 有任意一个键不是 0..n 的递增整数,就视为关联数组 + $i = 0; + foreach ($arr as $k => $_) { + if ($k !== $i++) return true; + } + return false; + } +} \ No newline at end of file diff --git a/app/common/logic/ModelNaming.php b/app/common/logic/ModelNaming.php new file mode 100644 index 0000000..b50a4cf --- /dev/null +++ b/app/common/logic/ModelNaming.php @@ -0,0 +1,101 @@ + 15L + if (!empty($s['engine_turbo']) && (int)$s['engine_turbo'] === 1) $key[] = 'T'; + if (!empty($s['transmission_code'])) $key[] = self::tok($s['transmission_code']); + break; + + case 'HEV': + case 'PHEV': + case 'EREV': + $key[] = $fuel; + if (!empty($s['engine_disp_l'])) $key[] = self::dispToken($s['engine_disp_l']); + if (!empty($s['motor_layout_code'])) $key[] = self::tok($s['motor_layout_code']); // 如 DM-I/DM-P/AWD + break; + + default: + $key[] = $fuel; + } + + if (!empty($s['drive_type_code'])) $key[] = self::tok($s['drive_type_code']); + + $parts[] = implode('_', array_filter($key)); + return strtoupper(implode('_', array_filter($parts))); + } + + /** 生成中文可读的 model_name(走快照中文,不依赖 i18n) */ + public static function buildModelName(array $s): string + { + $seriesName = trim((string)($s['series_name'] ?? $s['series_code'] ?? '')); + $yearPart = !empty($s['model_year']) ? ($s['model_year'] . '款 ') : ''; + + $fuel = strtoupper($s['fuel_type_code'] ?? ''); + $tail = ''; + + if ($fuel === 'EV') { + $range = !empty($s['range_km']) ? (int)$s['range_km'] . 'KM ' : ''; + $drive = trim((string)($s['drive_type'] ?? $s['drive_type_code'] ?? '')); + $tail = "EV {$range}{$drive}"; + } elseif ($fuel === 'GAS') { + $disp = (!empty($s['engine_disp_l'])) + ? rtrim(rtrim(number_format((float)$s['engine_disp_l'], 1, '.', ''), '0'), '.') . 'L ' + : ''; + $trans = trim((string)($s['transmission'] ?? $s['transmission_code'] ?? '')); + $tail = "{$disp}{$trans}"; + } else { + $tail = trim((string)($s['fuel_type'] ?? $fuel)); + } + + $extras = []; + foreach (['trim','trim_level','edition_name'] as $k) { + if (!empty($s[$k])) $extras[] = trim((string)$s[$k]); + } + $extraStr = $extras ? ' ' . implode(' ', $extras) : ''; + + return trim($seriesName . ' ' . $yearPart . $tail . $extraStr); + } + + /* ===== Helpers ===== */ + + private static function tok($v): string + { + $v = strtoupper((string)$v); + return preg_replace('/[^A-Z0-9_]/', '', $v ?? ''); + } + + private static function numToken($v): string + { + return preg_replace('/[^0-9]/', '', (string)$v); + } + + /** 1.5L -> 15L;2.0L -> 20L(便于 code 稳定检索) */ + private static function dispToken($l): string + { + $x = (float)$l; + return (string)round($x * 10) . 'L'; + } +} \ No newline at end of file diff --git a/app/common/logic/SkuCodeBuilder.php b/app/common/logic/SkuCodeBuilder.php new file mode 100644 index 0000000..823b8dc --- /dev/null +++ b/app/common/logic/SkuCodeBuilder.php @@ -0,0 +1,233 @@ + 'FLG', + 'PREMIUM' => 'PRM', + 'STANDARD' => 'STD', + 'DELUXE' => 'DLX', + // edition + 'HONOR' => 'HNR', + 'CHAMPION' => 'CHP', + 'ANNIV' => 'ANV', + // trans + '1SPEED' => '1SP', + // drive/fuel/body 等一般已短,不必压缩 + ]; + + /** 入口:生成 sku_code */ + public static function build(array $s, array $opts = []): string + { + $maxLen = $opts['max_len'] ?? 40; + $ensureUnique = $opts['ensure_unique'] ?? true; + + $tokens = [ + 'brand' => self::tok($s['brand_code'] ?? ''), + 'series' => self::tok($s['series_code'] ?? ''), + 'year' => isset($s['model_year']) ? (string)intval($s['model_year']) : '', + 'fuel' => self::tok($s['fuel_type_code'] ?? ''), + 'drive' => self::tok($s['drive_type_code'] ?? ''), + 'spec' => self::buildSpec($s, self::tok($s['fuel_type_code'] ?? '')), + 'level' => self::abbr(self::tok($s['trim_level_code'] ?? '')), + 'edition' => self::abbr(self::tok($s['edition_code'] ?? '')), + ]; + + // 过滤空段并按既定顺序拼接 + $order = ['brand','series','year','fuel','drive','spec','level','edition']; + $parts = []; + foreach ($order as $k) { + if (!empty($tokens[$k])) $parts[$k] = $tokens[$k]; + } + + $sku = self::normalizeSku(implode('-', $parts)); + + if (strlen($sku) > $maxLen) { + $sku = self::shrink($parts, $maxLen); // 传入“带键名的 parts” + } + + if ($ensureUnique) { + $sku = self::ensureUnique($sku, $maxLen); + } + return $sku; + } + + /** 构造规格位(SPEC),按能源类型差异化 */ + private static function buildSpec(array $s, string $fuel): string + { + $fuel = strtoupper($fuel); + if ($fuel === 'EV') { + if (!empty($s['range_km'])) { + $r = (int)$s['range_km']; + $r10 = (int)round($r / 10) * 10; // 到 10 位 + return (string)$r10; + } + if (!empty($s['battery_kwh'])) { + return self::numToken($s['battery_kwh']) . 'KWH'; + } + return ''; + } + + if ($fuel === 'GAS') { + $bits = []; + if (!empty($s['engine_disp_l'])) { + $bits[] = self::dispToken($s['engine_disp_l']); // 1.5L -> 15L + } + if (!empty($s['engine_turbo']) && intval($s['engine_turbo']) === 1) { + $bits[] = 'T'; + } + if (!empty($s['transmission_code'])) { + $bits[] = self::abbr(self::tok($s['transmission_code'])); + } + return implode('', $bits); // 如 15LTCVT + } + + // HEV/PHEV/EREV 等 + $bits = []; + if (!empty($s['engine_disp_l'])) { + $bits[] = self::dispToken($s['engine_disp_l']); // 可空 + } + if (!empty($s['motor_layout_code'])) { + $bits[] = self::tok($s['motor_layout_code']); // 如 AWD / DM-I / DM-P + } + return implode('', $bits); + } + + /** 冲突检测并追加短校验(全局唯一) */ + private static function ensureUnique(string $sku, int $maxLen): string + { + if (!VehVehicleSku::where('sku_code', $sku)->exists()) { + return $sku; + } + // 用 crc32 生成 5~6 位 base36 校验(稳定且短) + $crc = self::base36Crc($sku); + $suffix = '-X' . $crc; + + if (strlen($sku . $suffix) > $maxLen) { + $sku = substr($sku, 0, max(0, $maxLen - strlen($suffix))); + $sku = rtrim($sku, '-'); // 避免尾部 '-' + } + $sku .= $suffix; + + // 若极端情况下仍冲突,追加递增计数 + $i = 2; + $candidate = $sku; + while (VehVehicleSku::where('sku_code', $candidate)->exists()) { + $extra = '-'.$i; + $cut = max(0, $maxLen - strlen($extra)); + $candidate = substr($sku, 0, $cut) . $extra; + $i++; + } + return $candidate; + } + + /** 把 token 数组尽量压到 maxLen(逐步压缩策略) */ + private static function shrink(array $partsByKey, int $maxLen): string + { + // 1) 先对 series/level/edition 做激进压缩 + $p = $partsByKey; + foreach (['series','level','edition'] as $k) { + if (!empty($p[$k])) $p[$k] = self::heavyAbbr($p[$k]); + } + $sku = self::normalizeSku(implode('-', $p)); + if (strlen($sku) <= $maxLen) return $sku; + + // 2) 依次移除不太关键的信息:edition → level(若存在) + if (!empty($p['edition'])) { unset($p['edition']); } + $sku = self::normalizeSku(implode('-', $p)); + if (strlen($sku) <= $maxLen) return $sku; + + if (!empty($p['level'])) { unset($p['level']); } + $sku = self::normalizeSku(implode('-', $p)); + if (strlen($sku) <= $maxLen) return $sku; + + // 3) 压缩 spec(如去 KM/KWH,仅保留核心) + if (!empty($p['spec'])) { $p['spec'] = self::compactSpec($p['spec']); } + $sku = self::normalizeSku(implode('-', $p)); + if (strlen($sku) <= $maxLen) return $sku; + + // 4) 仍超长:从右向左截断次要段(spec/drive/year),保留前三段(brand/series/fuel)优先 + $dropOrder = ['spec','drive','year']; + $q = $p; + foreach ($dropOrder as $k) { + if (!empty($q[$k])) { unset($q[$k]); } + $sku = self::normalizeSku(implode('-', $q)); + if (strlen($sku) <= $maxLen) return $sku; + } + + // 5) 最后兜底:硬截到 maxLen,后续 ensureUnique 会追加 -X 校验并避免冲突 + return substr(self::normalizeSku(implode('-', $p)), 0, $maxLen); + } + + /* ===== Helpers ===== */ + + private static function tok($v): string + { + $v = strtoupper((string)$v); + return preg_replace('/[^A-Z0-9_]/', '', $v ?? ''); + } + + private static function abbr(string $t): string + { + return self::ABBR[$t] ?? $t; + } + + private static function heavyAbbr(string $t): string + { + // 更激进:去除元音,连字符化 + $t = str_replace(['STANDARD','FLAGSHIP','PREMIUM','DELUXE'], ['STD','FLG','PRM','DLX'], $t); + $t = preg_replace('/[AEIOU]/', '', $t); + return $t; + } + + private static function compactSpec(string $s): string + { + // 规则:去 KM/KWH 等单位,只保留数字与核心字母 + $s = strtoupper($s); + $s = str_replace(['KM','KWH'], '', $s); + return $s; + } + + private static function numToken($v): string + { + return preg_replace('/[^0-9]/', '', (string)$v); + } + + /** 1.5L -> 15L;2.0L -> 20L */ + private static function dispToken($l): string + { + $x = (float)$l; + return (string)round($x * 10) . 'L'; + } + + private static function base36Crc(string $s): string + { + $crc = sprintf('%u', crc32($s)); // 无符号 + $n = (int)$crc; + $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $res = ''; + do { + $res = $chars[$n % 36] . $res; + $n = intdiv($n, 36); + } while ($n > 0); + return substr($res, -6); // 取后6位足够 + } + + private static function normalizeSku(string $s): string + { + // 连续的 '-' 合并,首尾修剪 + $s = preg_replace('/-+/', '-', $s); + $s = trim($s, '-'); + return $s; + } +} \ No newline at end of file diff --git a/app/common/logic/StockOrdersLogic.php b/app/common/logic/StockOrdersLogic.php new file mode 100644 index 0000000..29d09ad --- /dev/null +++ b/app/common/logic/StockOrdersLogic.php @@ -0,0 +1,17 @@ +orderBy('sort','desc')->get(['id', 'account_name', 'currency_id', 'account_number', 'bank_name', 'bank_branch', 'description']); + } + +} \ No newline at end of file diff --git a/app/common/logic/SysCurrencyLogic.php b/app/common/logic/SysCurrencyLogic.php new file mode 100644 index 0000000..206efa2 --- /dev/null +++ b/app/common/logic/SysCurrencyLogic.php @@ -0,0 +1,21 @@ +get(['id', 'currency_code', 'currency_name', 'currency_symbol', 'exchange_rate', 'float_percent']); + } +} \ No newline at end of file diff --git a/app/common/logic/SysDictItemLogic.php b/app/common/logic/SysDictItemLogic.php new file mode 100644 index 0000000..d6efc72 --- /dev/null +++ b/app/common/logic/SysDictItemLogic.php @@ -0,0 +1,22 @@ +where($where)->orderBy('sort','desc')->get(['id', 'item_label', 'item_value', 'remark', 'dict_id']); + } + +} \ No newline at end of file diff --git a/app/common/logic/SysDictLogic.php b/app/common/logic/SysDictLogic.php new file mode 100644 index 0000000..6447ed3 --- /dev/null +++ b/app/common/logic/SysDictLogic.php @@ -0,0 +1,27 @@ +where('code', $code)->orderBy('sort','desc')->first(['id']); + } + + public function getList() + { + return SysDict::where('status', 1)->get(['id', 'dict_name', 'dict_code', 'description']); + } + +} \ No newline at end of file diff --git a/app/common/logic/SysPortLogic.php b/app/common/logic/SysPortLogic.php new file mode 100644 index 0000000..a2e78a8 --- /dev/null +++ b/app/common/logic/SysPortLogic.php @@ -0,0 +1,22 @@ +orderBy('sort','desc')->get(['id', 'port_code', 'port_name', 'country_id', 'description']); + } + +} \ No newline at end of file diff --git a/app/common/logic/VehColorLogic.php b/app/common/logic/VehColorLogic.php new file mode 100644 index 0000000..7a36811 --- /dev/null +++ b/app/common/logic/VehColorLogic.php @@ -0,0 +1,22 @@ +where($where)->orderBy('sort','desc')->get(['id', 'color_code', 'color_text']); + } + +} \ No newline at end of file diff --git a/app/common/logic/VehDictI18nLogic.php b/app/common/logic/VehDictI18nLogic.php new file mode 100644 index 0000000..f25b50b --- /dev/null +++ b/app/common/logic/VehDictI18nLogic.php @@ -0,0 +1,17 @@ +orderBy('sort','desc')->get(['id', 'code', 'name', 'type', 'price']); + } + +} \ No newline at end of file diff --git a/app/common/logic/VehVehicleListingLogic.php b/app/common/logic/VehVehicleListingLogic.php new file mode 100644 index 0000000..6c0ad44 --- /dev/null +++ b/app/common/logic/VehVehicleListingLogic.php @@ -0,0 +1,17 @@ +hasMany(SysDictItem::class, 'dict_id', 'id'); + } +} \ No newline at end of file diff --git a/app/common/model/SysDictItem.php b/app/common/model/SysDictItem.php new file mode 100644 index 0000000..f53a7a9 --- /dev/null +++ b/app/common/model/SysDictItem.php @@ -0,0 +1,59 @@ +belongsTo(SysDict::class, 'dict_id', 'id'); + } +} \ No newline at end of file diff --git a/app/common/model/SysPort.php b/app/common/model/SysPort.php new file mode 100644 index 0000000..2249b98 --- /dev/null +++ b/app/common/model/SysPort.php @@ -0,0 +1,49 @@ + 'integer', + 'enabled' => 'integer', + 'sort_order' => 'integer', + 'created_at' => 'integer', + 'updated_at' => 'integer', + ]; + + protected $fillable = [ + 'field','dict','required','enabled','sort_order', + 'remark','created_at','updated_at' + ]; +} \ No newline at end of file diff --git a/app/common/model/VehModification.php b/app/common/model/VehModification.php new file mode 100644 index 0000000..d3bd16e --- /dev/null +++ b/app/common/model/VehModification.php @@ -0,0 +1,49 @@ + 'array', + 'features' => 'array', + 'dimensions_mm' => 'array', + 'model_year' => 'integer', + 'engine_turbo' => 'integer', + 'gears' => 'integer', + 'range_km' => 'integer', + 'doors' => 'integer', + 'seats' => 'integer', + 'weight_kg' => 'integer', + 'msrp' => 'float', + 'battery_kwh' => 'float', + 'engine_disp_l' => 'float', + 'is_active' => 'integer', + 'corp_id' => 'integer', + 'created_at' => 'integer', + 'updated_at' => 'integer', + ]; + + protected $fillable = [ + 'sku_code','fingerprint', + 'brand_code','brand_name', + 'series_code','series_name', + 'model_code','model_name','model_year', + 'trim_code','trim_level_code','edition_code', + 'trim','trim_level','edition_name', + 'body_type_code','drive_type_code','motor_layout_code', + 'fuel_type_code','engine_disp_l','engine_turbo','engine_power_kw', + 'battery_kwh','transmission_code','gears', + 'range_km','range_std_code', + 'doors','seats','weight_kg','dimensions_mm', + 'color_exterior_code','color_exterior_text', + 'color_interior_code','color_interior_text', + 'options_official','features','emission_std_code', + 'origin_country','currency','msrp', + 'is_active','corp_id','remark', + 'created_by','updated_by','created_at','updated_at', + 'body_type', 'drive_type', 'motor_layout', 'fuel_type', 'transmission', + 'range_std', 'emission_std' + ]; +} \ No newline at end of file diff --git a/app/common/model/VehVehicleUnit.php b/app/common/model/VehVehicleUnit.php new file mode 100644 index 0000000..28e38f1 --- /dev/null +++ b/app/common/model/VehVehicleUnit.php @@ -0,0 +1,49 @@ +logic = (new BrandSeriesLogic()); + } + + /** + * 获取系统list + */ + public function getList($params) + { + if (empty($params['brand_id'])) + throw new BusinessException('品牌id不能为空'); + $where = []; + if(isset($params['brand_id']) && $params['brand_id']) + $where[] = ['brand_id', '=', $params['brand_id']]; + return $this->logic->getList($where); + } +} \ No newline at end of file diff --git a/app/common/service/BrandService.php b/app/common/service/BrandService.php new file mode 100644 index 0000000..c3430fe --- /dev/null +++ b/app/common/service/BrandService.php @@ -0,0 +1,24 @@ +logic = (new BrandLogic()); + } + + /** + * 获取系统list + */ + public function getList() + { + return $this->logic->getList(); + } +} \ No newline at end of file diff --git a/app/common/service/StockOrdersService.php b/app/common/service/StockOrdersService.php new file mode 100644 index 0000000..a5c73ac --- /dev/null +++ b/app/common/service/StockOrdersService.php @@ -0,0 +1,31 @@ +validator; + } +} \ No newline at end of file diff --git a/app/common/service/SysAccountService.php b/app/common/service/SysAccountService.php new file mode 100644 index 0000000..b9c59ad --- /dev/null +++ b/app/common/service/SysAccountService.php @@ -0,0 +1,28 @@ +logic = (new SysAccountLogic()); + } + + /** + * 获取系统list + */ + public function getList() + { + return $this->logic->getList(); + } +} \ No newline at end of file diff --git a/app/common/service/SysCurrencyService.php b/app/common/service/SysCurrencyService.php new file mode 100644 index 0000000..f0108d0 --- /dev/null +++ b/app/common/service/SysCurrencyService.php @@ -0,0 +1,25 @@ +logic = (new SysCurrencyLogic()); + } + + /** + * 获取系统list + */ + public function getList() + { + return $this->logic->getList(); + } +} \ No newline at end of file diff --git a/app/common/service/SysDictItemService.php b/app/common/service/SysDictItemService.php new file mode 100644 index 0000000..c1e51ed --- /dev/null +++ b/app/common/service/SysDictItemService.php @@ -0,0 +1,42 @@ +logic = (new SysDictItemLogic()); + } + + /** + * 获取系统list + */ + public function getList($params) + { + if (empty($params['dict_id']) && empty($params['dict_code'])) + throw new BusinessException('未明确要查询的字典组'); + $where = []; + if(isset($params['dict_id']) && $params['dict_id']) + $where[] = ['dict_id', '=', $params['dict_id']]; + if(isset($params['dict_code']) && $params['dict_code']){ + $dict = (new SysDictLogic)->getDictByCode($params['dict_code']); + if (empty($dict['id'])) + throw new BusinessException('字典组code错误'); + $where[] = ['dict_id', '=', $dict['id']]; + } + + return $this->logic->getList($where); + } +} \ No newline at end of file diff --git a/app/common/service/SysDictService.php b/app/common/service/SysDictService.php new file mode 100644 index 0000000..57ab33c --- /dev/null +++ b/app/common/service/SysDictService.php @@ -0,0 +1,28 @@ +logic = (new SysDictLogic()); + } + + /** + * 获取系统list + */ + public function getList() + { + return $this->logic->getList(); + } +} \ No newline at end of file diff --git a/app/common/service/SysPortService.php b/app/common/service/SysPortService.php new file mode 100644 index 0000000..2918a96 --- /dev/null +++ b/app/common/service/SysPortService.php @@ -0,0 +1,28 @@ +logic = (new SysPortLogic()); + } + + /** + * 获取系统list + */ + public function getList() + { + return $this->logic->getList(); + } +} \ No newline at end of file diff --git a/app/common/service/VehColorService.php b/app/common/service/VehColorService.php new file mode 100644 index 0000000..2060efe --- /dev/null +++ b/app/common/service/VehColorService.php @@ -0,0 +1,33 @@ +logic = (new VehColorLogic()); + } + + /** + * 获取系统list + */ + public function getList($params) + { + if (empty($params['type'])) + throw new BusinessException('类型不能为空'); + $where = []; + if(isset($params['type']) && $params['type']) + $where[] = ['type', '=', $params['type']]; + return $this->logic->getList($where); + } +} \ No newline at end of file diff --git a/app/common/service/VehDictI18nService.php b/app/common/service/VehDictI18nService.php new file mode 100644 index 0000000..1c738d2 --- /dev/null +++ b/app/common/service/VehDictI18nService.php @@ -0,0 +1,75 @@ +validator; + } + + + /** 本进程缓存:[dict][code] => labels array */ + protected static array $labelsCache = []; + + public static function label(string $dict=null, ?string $code=null, string $locale='zh-CN', ?string $fallbackZh=null): ?string + { + if (!$dict || !$code) return $fallbackZh; + + $labels = self::labels($dict, $code); + if (!$labels) return $fallbackZh ?? $code; + + return $labels[$locale] ?? $labels['zh-CN'] ?? $code; + } + + /** 返回 labels 数组,命中缓存 */ + public static function labels(string $dict, string $code): ?array + { + $dict = strtoupper($dict); + $code = (string)$code; + + if (isset(self::$labelsCache[$dict][$code])) { + return self::$labelsCache[$dict][$code]; + } + + $term = DictTerm::where('dict',$dict)->where('code',$code)->first(['labels']); + if (!$term) return null; + + $labels = $term->labels ?? null; + if (!is_array($labels)) return null; + + self::$labelsCache[$dict][$code] = $labels; + return $labels; + } + + /** 校验 code 是否存在于字典(用于必填维度) */ + public static function ensureExists(string $dict, ?string $code, string $fieldNameForError): void + { + if ($code === null || $code === '') { + throw new \InvalidArgumentException("缺少必填字段:{$fieldNameForError}"); + } + $ok = VehDictI18n::where('dict',$dict)->where('code',$code)->where('status', 1)->exists(); + if (!$ok) throw new \InvalidArgumentException("字典不存在或未启用:{$dict}.{$code}"); + } +} \ No newline at end of file diff --git a/app/common/service/VehDictMappingService.php b/app/common/service/VehDictMappingService.php new file mode 100644 index 0000000..67c5288 --- /dev/null +++ b/app/common/service/VehDictMappingService.php @@ -0,0 +1,57 @@ +validator; + } + + + public static function all(): array + { + $rows = VehDictMapping::where('status', 1)->orderBy('sort')->orderBy('id')->get()->toArray(); + $out = []; + foreach ($rows as $r) { + $out[$r['dict']] = [ + 'field' => $r['field'], + 'dict' => $r['dict'], + 'required' => (int)$r['required'] === 1, + ]; + } + return $out; + } + + public static function requiredDicts(): array + { + return array_keys(array_filter(self::all(), fn($x)=>$x['required']===true)); + } + + public static function enabled(string $dict): bool + { + $all = self::all(); + return isset($all[$dict]); + } +} \ No newline at end of file diff --git a/app/common/service/VehModificationService.php b/app/common/service/VehModificationService.php new file mode 100644 index 0000000..31c7119 --- /dev/null +++ b/app/common/service/VehModificationService.php @@ -0,0 +1,28 @@ +logic = (new VehModificationLogic()); + } + + /** + * 获取系统list + */ + public function getList() + { + return $this->logic->getList(); + } +} \ No newline at end of file diff --git a/app/common/service/VehVehicleListingService.php b/app/common/service/VehVehicleListingService.php new file mode 100644 index 0000000..3aee146 --- /dev/null +++ b/app/common/service/VehVehicleListingService.php @@ -0,0 +1,31 @@ +validator; + } +} \ No newline at end of file diff --git a/app/common/service/VehVehicleSkuService.php b/app/common/service/VehVehicleSkuService.php new file mode 100644 index 0000000..ca25040 --- /dev/null +++ b/app/common/service/VehVehicleSkuService.php @@ -0,0 +1,162 @@ +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']['id'] = 'desc'; + break; + case 'index': + $config['where']['is_active'] = 1; + $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); + 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); + 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; + } +} \ No newline at end of file diff --git a/app/common/service/VehVehicleUnitService.php b/app/common/service/VehVehicleUnitService.php new file mode 100644 index 0000000..4af3ea4 --- /dev/null +++ b/app/common/service/VehVehicleUnitService.php @@ -0,0 +1,31 @@ +validator; + } +} \ No newline at end of file diff --git a/app/common/utils/scaffold/Generate.php b/app/common/utils/scaffold/Generate.php index eb82025..2ed10a0 100644 --- a/app/common/utils/scaffold/Generate.php +++ b/app/common/utils/scaffold/Generate.php @@ -156,11 +156,11 @@ class Generate protected function deleteFileIfExists($path) { if (file_exists($path)) { - "文件: {$path} 已存在,核对后手动删除。\n"; - return; + // "文件: {$path} 已存在,核对后手动删除。\n"; + // return; - // unlink($path); - // echo "删除已存在文件: {$path} \n"; + unlink($path); + echo "删除已存在文件: {$path} \n"; } } diff --git a/app/common/validate/StockOrdersValidate.php b/app/common/validate/StockOrdersValidate.php new file mode 100644 index 0000000..78fccf3 --- /dev/null +++ b/app/common/validate/StockOrdersValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/common/validate/VehDictI18nValidate.php b/app/common/validate/VehDictI18nValidate.php new file mode 100644 index 0000000..cb65500 --- /dev/null +++ b/app/common/validate/VehDictI18nValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/common/validate/VehDictMappingValidate.php b/app/common/validate/VehDictMappingValidate.php new file mode 100644 index 0000000..1b4bc7a --- /dev/null +++ b/app/common/validate/VehDictMappingValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/common/validate/VehVehicleListingValidate.php b/app/common/validate/VehVehicleListingValidate.php new file mode 100644 index 0000000..ffa9c83 --- /dev/null +++ b/app/common/validate/VehVehicleListingValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/common/validate/VehVehicleSkuValidate.php b/app/common/validate/VehVehicleSkuValidate.php new file mode 100644 index 0000000..7305e71 --- /dev/null +++ b/app/common/validate/VehVehicleSkuValidate.php @@ -0,0 +1,128 @@ + 'max:40|alphaNum', + 'fingerprint' => 'length:64|alphaNum', + 'brand_code' => 'require|max:32', + 'brand_name' => 'require|max:64', + 'series_code' => 'max:64', + 'series_name' => 'max:128', + 'model_code' => 'max:64', + 'model_name' => 'require|max:128', + 'model_year' => 'require|number|between:1900,2100', + 'trim_code' => 'max:64', + 'trim_level_code' => 'max:64', + 'edition_code' => 'max:64', + 'trim' => 'max:128', + 'trim_level' => 'max:64', + 'edition_name' => 'max:64', + 'body_type_code' => 'max:32', + 'drive_type_code' => 'max:16', + 'motor_layout_code' => 'max:32', + 'fuel_type_code' => 'max:16', + 'engine_disp_l' => 'float|between:0,99.9', + 'engine_turbo' => 'in:0,1', + 'engine_power_kw' => 'number|between:0,2000', + 'battery_kwh' => 'float|between:0,999.99', + 'transmission_code' => 'max:16', + 'gears' => 'number|between:1,20', + 'range_km' => 'number|between:0,2000', + 'range_std_code' => 'max:16', + 'doors' => 'number|between:2,10', + 'seats' => 'number|between:1,20', + 'weight_kg' => 'number|between:0,10000', + 'dimensions_mm' => 'array', + 'color_exterior_code'=> 'max:32', + 'color_exterior_text'=> 'max:64', + 'color_interior_code'=> 'max:32', + 'color_interior_text'=> 'max:64', + 'options_official' => 'array', + 'features' => 'array', + 'emission_std_code' => 'max:16', + 'origin_country' => 'alpha|length:2', + 'currency' => 'require|alpha|length:3', + 'msrp' => 'float|between:0,9999999999.99', + 'is_active' => 'in:0,1', + 'corp_id' => 'require|number', + 'remark' => 'max:255', + 'created_by' => 'number', + 'updated_by' => 'number', + 'created_at' => 'require|number', + 'updated_at' => 'require|number', + ]; + + /** + * 字段别名 + */ + protected $field = [ + 'sku_code' => 'SKU编码', + 'fingerprint' => '规格指纹', + 'brand_code' => '品牌代码', + 'brand_name' => '品牌名称', + 'series_code' => '车系代码', + 'series_name' => '车系名称', + 'model_code' => '车型代码', + 'model_name' => '车型名称', + 'model_year' => '年款', + 'trim_code' => '版型代码', + 'trim_level_code' => '配置等级代码', + 'edition_code' => '特别版代码', + 'trim' => '版型/细分配置', + 'trim_level' => '配置等级', + 'edition_name' => '特别版名称', + 'body_type_code' => '车身类型', + 'drive_type_code' => '驱动方式', + 'motor_layout_code' => '电机布置', + 'fuel_type_code' => '能源类型', + 'engine_disp_l' => '排量(L)', + 'engine_turbo' => '是否涡轮', + 'engine_power_kw' => '最大功率(kW)', + 'battery_kwh' => '电池容量(kWh)', + 'transmission_code' => '变速箱代码', + 'gears' => '挡位数', + 'range_km' => '续航里程', + 'range_std_code' => '续航标准', + 'doors' => '门数', + 'seats' => '座位数', + 'weight_kg' => '整备质量', + 'dimensions_mm' => '车身尺寸', + 'color_exterior_code'=> '外观色代码', + 'color_exterior_text'=> '外观色名称', + 'color_interior_code'=> '内饰色代码', + 'color_interior_text'=> '内饰色名称', + 'options_official' => '官方选装', + 'features' => '功能配置', + 'emission_std_code' => '排放标准', + 'origin_country' => '原产国', + 'currency' => '币种', + 'msrp' => '建议零售价', + 'is_active' => '是否启用', + 'corp_id' => '公司ID', + 'remark' => '备注', + 'created_by' => '创建人', + 'updated_by' => '更新人', + 'created_at' => '创建时间', + 'updated_at' => '更新时间', + ]; + + /** + * 场景定义 + */ + protected $scene = [ + 'store' => [ + 'brand_code', 'brand_name', 'model_year','currency' + ], + 'update' => [ + 'id', 'brand_code','brand_name', 'model_year','currency' + ], + ]; +} \ No newline at end of file diff --git a/app/common/validate/VehVehicleUnitValidate.php b/app/common/validate/VehVehicleUnitValidate.php new file mode 100644 index 0000000..11f91a9 --- /dev/null +++ b/app/common/validate/VehVehicleUnitValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/controller/IndexController.php b/app/controller/IndexController.php index ba87d98..a0f6dba 100644 --- a/app/controller/IndexController.php +++ b/app/controller/IndexController.php @@ -78,25 +78,25 @@ class IndexController $options = [ 'controller' => [ 'module' => 'common', - 'name' => 'message_notifications' + 'name' => 'stock_orders' ], 'model' => [ 'module' => 'common', - 'name' => 'message_notifications' + 'name' => 'stock_orders' ], 'service' => [ 'module' => 'common', - 'name' => 'message_notifications' + 'name' => 'stock_orders' ], 'logic' => [ 'module' => 'common', - 'name' => 'message_notifications' + 'name' => 'stock_orders' ], 'validate' => [ 'module' => 'common', - 'name' => 'message_notifications' + 'name' => 'stock_orders' ], - 'table' => 'message_notifications' + 'table' => 'stock_orders' ]; $result = (new Generate($options))->doGenerate(); @@ -106,43 +106,24 @@ class IndexController public function test() { + $params = [ + 'email' => '', + 'phone' => '11', + 'whatsApp' => '' + ]; + $corpId = 1; + $customer = \app\crm\model\Customer::where('corp_id', $corpId) + ->where((function (\Illuminate\Database\Eloquent\Builder $query) use ($params) { + if(!empty($params['email'])) + $query->Orwhere('email', '=', $params['email']); + if(!empty($params['phone'])) + $query->Orwhere('phone', '=', $params['phone']); + if(!empty($params['whatsApp'])) + $query->Orwhere('whatsApp', '=', $params['whatsApp']); + return $query; + })) + ->first(); - // $data = [ - // 'sender_id' => 0, - // 'sender_type' => 'system', - // 'title' => '测试消息 - 标题', - // 'content' => '测试消息 - 内容', - // 'scene' => 1, - // 'recipient' => [1], - // ]; - - // $data['recipient'] = [1, 74]; - // // // 同步推送 - // // (new MessageNotificationsService())->pushNotice($data); - - - - // // 异步推送 - // \Webman\RedisQueue\Redis::send('notice', $data); - - - $info = InquiryLog::with(['channelInfo', 'countryInfo', 'enter'])->where('id', 1)->where('sync_crm', 0)->first(); - - var_dump(empty($info->recipient)); - - if ($info) { - var_dump(11); - } else { - var_dump(22); - } - - - if ($info !== NULL) { - var_dump(33); - } else { - var_dump(44); - } - - return json(['code' => 0, 'msg' => 'success']); + return json($customer); } } \ No newline at end of file diff --git a/app/functions.php b/app/functions.php index d9bb411..db8cfe7 100644 --- a/app/functions.php +++ b/app/functions.php @@ -304,6 +304,29 @@ if (!function_exists('generateMsgId')) { // 随机部分(3位) $rand = mt_rand(100, 999); + // 拼接:日期时间 + 微秒 + 随机数 + return $date . $micro . $rand; + } +} + +/** + * 生成sku_code + * 格式:年月日时分秒 + 微秒 + 随机数 + * 例如:20250820175530987654321 + * + * @return string + */ +if (!function_exists('skuCode')) { + function skuCode(): string + { + // 时间部分(精确到微秒) + $time = microtime(true); + $micro = sprintf("%06d", ($time - floor($time)) * 1e6); + $date = date('YmdHis', (int)$time); + + // 随机部分(3位) + $rand = mt_rand(100, 999); + // 拼接:日期时间 + 微秒 + 随机数 return $date . $micro . $rand; } diff --git a/config/route.php b/config/route.php index 86aa1a0..38c7907 100644 --- a/config/route.php +++ b/config/route.php @@ -61,8 +61,17 @@ Route::group('/common', function () { Route::any('/feishu/app', [app\common\controller\PresetController::class, 'feishuApp']); Route::any('/systems', [app\common\controller\PresetController::class, 'systems']); Route::any('/roles', [app\common\controller\PresetController::class, 'roles']); + Route::any('/currency', [app\common\controller\PresetController::class, 'currency']); Route::any('/cache/clear', [app\common\controller\PresetController::class, 'cacheClear']); Route::any('/menus', [app\common\controller\PresetController::class, 'menus']); + Route::any('/brand', [app\common\controller\PresetController::class, 'brand']); + Route::any('/brand/series', [app\common\controller\PresetController::class, 'series']); + Route::any('/account', [app\common\controller\PresetController::class, 'account']); + Route::any('/dict', [app\common\controller\PresetController::class, 'dict']); + Route::any('/dictItem', [app\common\controller\PresetController::class, 'dictItem']); + Route::any('/port', [app\common\controller\PresetController::class, 'port']); + Route::any('/color', [app\common\controller\PresetController::class, 'color']); + Route::any('/modification', [app\common\controller\PresetController::class, 'modification']); }); @@ -182,6 +191,99 @@ Route::group('/admin', function () { Route::any('/allocation', [app\crmAdmin\controller\CustomerController::class, 'allocation']); }); }); + + // 车辆 + Route::group('/vehicle', function () { + // 基础信息 + Route::group('/sku', function () { + Route::any('/list', [app\common\controller\VehVehicleSkuController::class, 'index']); + Route::any('/show', [app\common\controller\VehVehicleSkuController::class, 'show']); + Route::any('/store', [app\common\controller\VehVehicleSkuController::class, 'store']); + Route::any('/update', [app\common\controller\VehVehicleSkuController::class, 'update']); + Route::any('/destroy', [app\common\controller\VehVehicleSkuController::class, 'destroy']); + }); + }); + + // 字典 + Route::group('/dict', function () { + Route::any('/list', [app\admin\controller\SysDictController::class, 'index']); + Route::any('/show', [app\admin\controller\SysDictController::class, 'show']); + Route::any('/store', [app\admin\controller\SysDictController::class, 'store']); + Route::any('/update', [app\admin\controller\SysDictController::class, 'update']); + Route::any('/destroy', [app\admin\controller\SysDictController::class, 'destroy']); + Route::any('/scope', [app\admin\controller\SysDictController::class, 'listScope']); + + Route::group('/item', function () { + Route::any('/list', [app\admin\controller\SysDictItemController::class, 'index']); + Route::any('/show', [app\admin\controller\SysDictItemController::class, 'show']); + Route::any('/store', [app\admin\controller\SysDictItemController::class, 'store']); + Route::any('/update', [app\admin\controller\SysDictItemController::class, 'update']); + Route::any('/destroy', [app\admin\controller\SysDictItemController::class, 'destroy']); + }); + }); + + // 货币 + Route::group('/currency', function () { + Route::any('/list', [app\admin\controller\SysCurrencyController::class, 'index']); + Route::any('/show', [app\admin\controller\SysCurrencyController::class, 'show']); + Route::any('/store', [app\admin\controller\SysCurrencyController::class, 'store']); + Route::any('/update', [app\admin\controller\SysCurrencyController::class, 'update']); + Route::any('/destroy', [app\admin\controller\SysCurrencyController::class, 'destroy']); + }); + + // 品牌 + Route::group('/brand', function () { + Route::any('/list', [app\admin\controller\BrandController::class, 'index']); + Route::any('/show', [app\admin\controller\BrandController::class, 'show']); + Route::any('/store', [app\admin\controller\BrandController::class, 'store']); + Route::any('/update', [app\admin\controller\BrandController::class, 'update']); + Route::any('/destroy', [app\admin\controller\BrandController::class, 'destroy']); + // 车系 + Route::group('/series', function () { + Route::any('/list', [app\admin\controller\BrandSeriesController::class, 'index']); + Route::any('/show', [app\admin\controller\BrandSeriesController::class, 'show']); + Route::any('/store', [app\admin\controller\BrandSeriesController::class, 'store']); + Route::any('/update', [app\admin\controller\BrandSeriesController::class, 'update']); + Route::any('/destroy', [app\admin\controller\BrandSeriesController::class, 'destroy']); + }); + }); + + // 汽车颜色 + Route::group('/color', function () { + Route::any('/list', [app\admin\controller\VehColorController::class, 'index']); + Route::any('/show', [app\admin\controller\VehColorController::class, 'show']); + Route::any('/store', [app\admin\controller\VehColorController::class, 'store']); + Route::any('/update', [app\admin\controller\VehColorController::class, 'update']); + Route::any('/destroy', [app\admin\controller\VehColorController::class, 'destroy']); + }); + + // 汽车改装项目 + Route::group('/modification', function () { + Route::any('/list', [app\admin\controller\VehModificationController::class, 'index']); + Route::any('/show', [app\admin\controller\VehModificationController::class, 'show']); + Route::any('/store', [app\admin\controller\VehModificationController::class, 'store']); + Route::any('/update', [app\admin\controller\VehModificationController::class, 'update']); + Route::any('/destroy', [app\admin\controller\VehModificationController::class, 'destroy']); + }); + + // 账户 + Route::group('/account', function () { + Route::any('/list', [app\admin\controller\SysAccountController::class, 'index']); + Route::any('/show', [app\admin\controller\SysAccountController::class, 'show']); + Route::any('/store', [app\admin\controller\SysAccountController::class, 'store']); + Route::any('/update', [app\admin\controller\SysAccountController::class, 'update']); + Route::any('/destroy', [app\admin\controller\SysAccountController::class, 'destroy']); + }); + + // 账户 + Route::group('/port', function () { + Route::any('/list', [app\admin\controller\SysPortController::class, 'index']); + Route::any('/show', [app\admin\controller\SysPortController::class, 'show']); + Route::any('/store', [app\admin\controller\SysPortController::class, 'store']); + Route::any('/update', [app\admin\controller\SysPortController::class, 'update']); + Route::any('/destroy', [app\admin\controller\SysPortController::class, 'destroy']); + }); + });