Browse Source

设备管理

master
wanghongjun 2 weeks ago
parent
commit
e7468ee9c1
  1. 350
      app/Http/Controllers/Admin/ParkingEquipmentController.php
  2. 36
      app/Models/ParkingEquipment.php
  3. 193
      app/Services/ParkingEquipmentService.php
  4. 14
      app/common.php
  5. 37
      database/migrations/2026_06_03_143449_create_parking_equipment_table.php
  6. 5
      resources/lang/en/log.php
  7. 8
      resources/lang/en/service.php
  8. 9
      resources/lang/en/validation.php
  9. 5
      resources/lang/zh-CN/log.php
  10. 8
      resources/lang/zh-CN/service.php
  11. 9
      resources/lang/zh-CN/validation.php
  12. 5
      resources/lang/zh-TW/log.php
  13. 8
      resources/lang/zh-TW/service.php
  14. 9
      resources/lang/zh-TW/validation.php
  15. 10
      routes/admin/api.php

350
app/Http/Controllers/Admin/ParkingEquipmentController.php

@ -0,0 +1,350 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Exceptions\CustomException;
use App\Models\Parking;
use App\Models\ParkingChannel;
use App\Models\ParkingEquipment;
use App\Services\AdminTranslationService;
use App\Services\ApiResponseService;
use App\Services\ParkingEquipmentService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class ParkingEquipmentController extends BaseController
{
protected string $menuUri = 'equipmentManagement';
/**
* @var ParkingEquipmentService
*/
protected ParkingEquipmentService $service;
/**
* 构造函数
* @param ApiResponseService $responseService
* @param ParkingEquipmentService $service
*/
public function __construct(
ApiResponseService $responseService,
ParkingEquipmentService $service
) {
parent::__construct($responseService);
$this->service = $service;
}
/**
* @param Request $request
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
try {
$query = ParkingEquipment::query();
if ($request->has('parking_id')) {
$parking_id = $request->input('parking_id');
if ($parking_id) {
$channel_ids = ParkingChannel::getIds($parking_id);
if ($channel_ids) {
$query->whereIn('channel_id', $channel_ids);
} else {
$query->where('id', 0);
}
}
}
if ($request->has('name')) {
$name = $request->input('name');
if ($name) {
$query->where('name', 'like', "%{$name}%");
}
}
if ($request->has('type')) {
$type = $request->input('type');
if ($type) {
$query->where('type', $type);
}
}
if ($request->has('status')) {
$status = $request->input('status');
if ($status) {
$query->where('status', $status);
}
}
if ($request->has('create_start_time')
&& $request->has(
'create_end_time'
)
) {
$create_start_time = $request->input('create_start_time');
$create_end_time = $request->input('create_end_time');
if ($create_start_time && $create_end_time) {
$query->whereBetween('created_at', [
$create_start_time . ' 00:00:00',
$create_end_time . ' 23:59:59'
]);
}
}
// 分页
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$typeArr = ParkingEquipmentService::getType();
$statusArr = ParkingEquipmentService::getStatus();
$purposeArr = ParkingEquipmentService::getPurpose();
$total = $query->count();
$items = $query->latest()->forPage($page, $perPage)->get()->each(
function ($item) use ($typeArr, $statusArr, $purposeArr) {
$parking_id = ParkingChannel::getParkingId(
$item['channel_id']
);
$item['parking_name'] = Parking::getName($parking_id);
$item['name']
= AdminTranslationService::getTranslationTypeName(
$item['id'],
9,
$item['name']
);
$item['type_str'] = $typeArr[$item['type']];
$item['purpose_str'] = $purposeArr[$item['purpose']] ?? '';
$item['channel_name'] = ParkingChannel::getName(
$item['channel_id']
);
$item['status_str'] = $statusArr[$item['status']];
return $item;
}
);
return $this->responseService->success([
'items' => $items,
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'last_page' => ceil($total / $perPage),
]);
} catch (Exception $e) {
$m_prefix = __('exception.exception_handler.resource');
return $this->responseService->systemError(
$m_prefix . ':' . $e->getMessage()
);
}
}
/**
* 列表搜索数据
* @return JsonResponse
*/
public function search(): JsonResponse
{
try {
$data = [
'parking_list' => Parking::getData(),
'type_list' => get_select_data(
ParkingEquipmentService::getType()
),
'status_list' => get_select_data(
ParkingEquipmentService::getStatus()
)
];
return $this->responseService->success($data);
} catch (Exception $e) {
$m_prefix = __('exception.exception_handler.resource');
return $this->responseService->systemError(
$m_prefix . ':' . $e->getMessage()
);
}
}
/**
* @return JsonResponse
*/
public function create(): JsonResponse
{
try {
$data = [
'type_list' => get_select_data(
ParkingEquipmentService::getType()
),
'purpose_list' => get_select_data(
ParkingEquipmentService::getPurpose()
),
'channel_list' => ParkingChannel::getData()
];
return $this->responseService->success($data);
} catch (Exception $e) {
return $this->responseService->systemError(
__('exception.get_data_failed') . ':' . $e->getMessage()
);
}
}
/**
* @param Request $request
* @return JsonResponse
* @throws CustomException
* @throws ValidationException
*/
public function store(Request $request): JsonResponse
{
try {
$this->saveValidator($request->all());
$this->service->createModel($request->all());
return $this->responseService->success(
null,
__('admin.save_succeeded')
);
} catch (ValidationException|CustomException $e) {
throw $e;
} catch (Exception $e) {
return $this->responseService->systemError(
__('admin.operation_failed') . ':'
. $e->getMessage()
);
}
}
/**
* @param array $data
* @param int $id
* @return void
* @throws ValidationException
*/
protected function saveValidator(array $data, int $id = 0): void
{
$rules = [
'name' => 'required',
'type' => 'required',
'channel_id' => 'required',
'purpose' => 'required'
];
$messages = [
'name.required' => __validation(
'equipment_management.n_empty'
),
'type.required' => __validation(
'equipment_management.t_empty'
),
'channel_id.required' => __validation(
'equipment_management.ch_empty'
),
'purpose.required' => __validation(
'equipment_management.pu_empty'
)
];
if ($id) {
$this->validateId($id, ParkingEquipment::class);
}
if (isset($data['type'])) {
$type = $data['type'];
if (in_array($type, [2, 5])) {
$rules['ip'] = 'required|ip';
$messages['ip.required']
= __validation('equipment_management.ip_empty');
$messages['ip.ip'] = __validation('equipment_management.ip');
}
if (in_array($type, [3, 4, 5])) {
$rules['sn'] = 'required';
$messages['sn.required']
= __validation('equipment_management.sn_empty');
}
}
$validator = Validator::make($data, $rules, $messages);
if ($validator->fails()) {
throw new ValidationException($validator);
}
}
/**
* @param string $id
* @return JsonResponse
*/
public function edit(string $id): JsonResponse
{
try {
$this->validateId($id, ParkingEquipment::class);
$data = [
'type_list' => get_select_data(
ParkingEquipmentService::getType()
),
'purpose_list' => get_select_data(
ParkingEquipmentService::getPurpose()
),
'channel_list' => ParkingChannel::getData(),
'item' => ParkingEquipment::query()->find($id)
];
$Translation = AdminTranslationService::getTranslation(
$data['item']['id'],
9
);
$data['item']['en_name'] = $Translation['en'] ?? '';
$data['item']['tw_name'] = $Translation['zh_tw'] ?? '';
return $this->responseService->success($data);
} catch (Exception $e) {
return $this->responseService->systemError(
__('exception.get_data_failed') . ':' . $e->getMessage()
);
}
}
/**
* @param Request $request
* @param string $id
* @return JsonResponse
* @throws CustomException
* @throws ValidationException
*/
public function update(Request $request, string $id): JsonResponse
{
try {
$this->saveValidator($request->all(), $id);
$this->service->updateModel($request->all(), $id);
return $this->responseService->success(
null,
__('admin.update_succeeded')
);
} catch (ValidationException|CustomException $e) {
throw $e;
} catch (Exception $e) {
return $this->responseService->systemError(
__('admin.operation_failed') . ':'
. $e->getMessage()
);
}
}
/**
* @param string $id
* @return JsonResponse
* @throws CustomException
* @throws ValidationException
*/
public function destroy(string $id): JsonResponse
{
try {
$this->validateId($id, ParkingEquipment::class);
$this->service->deleteModel($id);
return $this->responseService->success(
null,
__('admin.delete_succeeded')
);
} catch (ValidationException|CustomException $e) {
throw $e;
} catch (Exception $e) {
return $this->responseService->systemError(
__('admin.operation_failed') . ':'
. $e->getMessage()
);
}
}
}

36
app/Models/ParkingEquipment.php

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class ParkingEquipment extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'parking_equipment';
protected $fillable
= [
'name',
'type',
'purpose',
'channel_id',
'sn',
'ip',
'status'
];
protected $hidden
= [
'updated_at',
'deleted_at'
];
public function getCreatedAtAttribute($value): string
{
return $value ? date("Y-m-d H:i:s", strtotime($value)) : $value;
}
}

193
app/Services/ParkingEquipmentService.php

@ -0,0 +1,193 @@
<?php
namespace App\Services;
use App\Models\ParkingEquipment;
use Exception;
use Illuminate\Support\Facades\DB;
class ParkingEquipmentService extends BaseService
{
public static array $typeArr
= [
1 => 'type1',
2 => 'type2',
3 => 'type3',
4 => 'type4',
5 => 'type5'
];
public static array $statusArr = ['offline', 'online'];
public static array $purposeArr = [1 => 'entrance', 2 => 'export'];
protected string $menuTitle = 'equipment_management';
/**
* @return array|string[]
*/
public static function getType(): array
{
$typeArr = self::$typeArr;
foreach ($typeArr as $key => $value) {
$typeArr[$key] = __('service.equipment_management.' . $value);
}
return $typeArr;
}
/**
* @return array|string[]
*/
public static function getStatus(): array
{
$statusArr = self::$statusArr;
foreach ($statusArr as $key => $value) {
$statusArr[$key] = __('admin.' . $value);
}
return $statusArr;
}
/**
* @return array|string[]
*/
public static function getPurpose(): array
{
$purposeArr = self::$purposeArr;
foreach ($purposeArr as $key => $value) {
$purposeArr[$key] = __('service.channel_management.' . $value);
}
return $purposeArr;
}
/**
* @param array $data
* @throws Exception
*/
public function createModel(array $data)
{
try {
DB::beginTransaction();
$existsWhere = [
['name', '=', $data['name']],
['type', '=', $data['type']]
];
if (ParkingEquipment::query()->where($existsWhere)->exists()) {
throw new Exception(
__service($this->menuTitle . '.name_exists')
);
}
$model = ParkingEquipment::query()->create([
'name' => $data['name'],
'type' => $data['type'],
'purpose' => $data['purpose'] ?? 0,
'channel_id' => $data['channel_id'],
'sn' => $data['sn'] ?? '',
'ip' => $data['ip'] ?? '',
'created_at' => get_datetime()
]);
$this->logService->logCreated($model, $this->menuTitle . '.create');
AdminTranslationService::saveTranslation(
$data['name'],
$data['en_name'] ?? '',
$data['tw_name'] ?? '',
$model->id,
9
);
DB::commit();
return $model;
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
/**
* @param array $data
* @param int $id
* @throws Exception
*/
public function updateModel(array $data, int $id)
{
try {
DB::beginTransaction();
// 验证
$existsWhere = [
['name', '=', $data['name']],
['type', '=', $data['type']],
['id', '<>', $id]
];
if (ParkingEquipment::query()->where($existsWhere)->exists()) {
throw new Exception(
__service($this->menuTitle . '.name_exists')
);
}
// 更新
$model = ParkingEquipment::query()->findOrFail($id);
$oldValues = $model->toArray();
$model->update([
'name' => $data['name'],
'type' => $data['type'],
'purpose' => $data['purpose'] ?? 0,
'channel_id' => $data['channel_id'],
'sn' => $data['sn'] ?? '',
'ip' => $data['ip'] ?? '',
'updated_at' => get_datetime()
]);
$this->logService->logUpdated(
$model,
$oldValues,
$this->menuTitle . '.update'
);
AdminTranslationService::saveTranslation(
$data['name'],
$data['en_name'] ?? '',
$data['tw_name'] ?? '',
$id,
9
);
DB::commit();
return $model;
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
/**
* @param $id
* @return bool
* @throws Exception
*/
public function deleteModel($id): bool
{
try {
DB::beginTransaction();
$model = ParkingEquipment::query()->findOrFail($id);
$this->logService->logDeleted($model, $this->menuTitle . '.delete');
$model->delete();
AdminTranslationService::syncDelete($id, 9);
DB::commit();
return true;
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
}

14
app/common.php

@ -202,3 +202,17 @@ if (!function_exists('option_time')) {
];
}
}
if (!function_exists('__validation')) {
function __validation($str): string
{
return __('validation.' . $str);
}
}
if (!function_exists('__service')) {
function __service($str): string
{
return __('service.' . $str);
}
}

37
database/migrations/2026_06_03_143449_create_parking_equipment_table.php

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('parking_equipment', function (Blueprint $table) {
$table->id();
$table->string('name', 50)->default('')->comment('设备名称');
$table->tinyInteger('type')->default(1)->comment('设备类型');
$table->tinyInteger('purpose')->default(0)->comment('设备用途');
$table->integer('channel_id')->comment('通道id');
$table->string('sn', 78)->default('')->comment('设备编码');
$table->string('ip', 15)->default('')->comment('设备ip');
$table->tinyInteger('status')->default(1)->comment('状态');
$table->timestamps();
$table->softDeletes();
$table->innoDb();
$table->comment('停车场设备');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('parking_equipment');
}
};

5
resources/lang/en/log.php

@ -121,5 +121,10 @@ return [
'create' => 'Create channel permissions',
'update' => 'Update channel permissions',
'delete' => 'Delete channel permissions'
],
'equipment_management' => [
'create' => 'Create device',
'update' => 'Update device',
'delete' => 'Delete device'
]
];

8
resources/lang/en/service.php

@ -113,5 +113,13 @@ return [
],
'channel_permissions' => [
'type_exists' => 'Channel membership type already exists'
],
'equipment_management' => [
'type1' => 'barrier gate',
'type2' => 'Entrance channel camera',
'type3' => 'traffic light',
'type4' => '32 inch display screen',
'type5' => 'Koto recognition camera',
'name_exists' => 'The same type of device name already exists'
]
];

9
resources/lang/en/validation.php

@ -154,5 +154,14 @@ return [
'm_empty' => 'Member type cannot be empty',
'c_empty' => 'Allow channel not to be empty',
'c_array' => 'Allow channel data to be an array'
],
'equipment_management' => [
'n_empty' => 'The device name cannot be empty',
't_empty' => 'The device type cannot be empty',
'ch_empty' => 'Binding channel cannot be empty',
'pu_empty' => 'The purpose of the device cannot be empty',
'ip_empty' => 'The device IP cannot be empty',
'ip' => 'Device IP format error',
'sn_empty' => 'The device code cannot be empty'
]
];

5
resources/lang/zh-CN/log.php

@ -121,5 +121,10 @@ return [
'create' => '创建通道权限',
'update' => '更新通道权限',
'delete' => '删除通道权限'
],
'equipment_management' => [
'create' => '创建设备',
'update' => '更新设备',
'delete' => '删除设备'
]
];

8
resources/lang/zh-CN/service.php

@ -113,5 +113,13 @@ return [
],
'channel_permissions' => [
'type_exists' => '通道会员类型已存在'
],
'equipment_management' => [
'type1' => '道闸',
'type2' => '入口通道相机',
'type3' => '交通灯',
'type4' => '32寸显示屏',
'type5' => '科拓识别相机',
'name_exists' => '同类型设备名称已存在'
]
];

9
resources/lang/zh-CN/validation.php

@ -154,5 +154,14 @@ return [
'm_empty' => '会员类型不能为空',
'c_empty' => '允许通道不能为空',
'c_array' => '允许通道数据必须是数组'
],
'equipment_management' => [
'n_empty' => '设备名称不能为空',
't_empty' => '设备类型不能为空',
'ch_empty' => '绑定通道不能为空',
'pu_empty' => '设备用途不能为空',
'ip_empty' => '设备IP不能为空',
'ip' => '设备IP格式错误',
'sn_empty' => '设备编码不能为空'
]
];

5
resources/lang/zh-TW/log.php

@ -121,5 +121,10 @@ return [
'create' => '創建通道許可權',
'update' => '更新通道許可權',
'delete' => '删除通道許可權'
],
'equipment_management' => [
'create' => '創建設備',
'update' => '更新設備',
'delete' => '删除設備'
]
];

8
resources/lang/zh-TW/service.php

@ -113,5 +113,13 @@ return [
],
'channel_permissions' => [
'type_exists' => '通道會員類型已存在'
],
'equipment_management' => [
'type1' => '道閘',
'type2' => '入口通道相機',
'type3' => '交通燈',
'type4' => '32寸顯示幕',
'type5' => '科拓識別相機',
'name_exists' => '同類型設備名稱已存在'
]
];

9
resources/lang/zh-TW/validation.php

@ -154,5 +154,14 @@ return [
'm_empty' => '會員類型不能為空',
'c_empty' => '允許通道不能為空',
'c_array' => '允許通道數據必須是數組'
],
'equipment_management' => [
'n_empty' => '設備名稱不能為空',
't_empty' => '設備類型不能為空',
'ch_empty' => '綁定通道不能為空',
'pu_empty' => '設備用途不能為空',
'ip_empty' => '設備IP不能為空',
'ip' => '設備IP格式錯誤',
'sn_empty' => '設備編碼不能為空'
]
];

10
routes/admin/api.php

@ -35,6 +35,7 @@ use App\Http\Controllers\Admin\ParkingChannelController;
use App\Http\Controllers\Admin\GuardBoothManagementController;
use App\Http\Controllers\Admin\ParkingDepartureReasonController;
use App\Http\Controllers\Admin\ChannelPermissionsController;
use App\Http\Controllers\Admin\ParkingEquipmentController;
Route::group(['prefix' => 'admin'], function () {
@ -251,6 +252,15 @@ Route::group(['prefix' => 'admin'], function () {
Route::delete('/channelManagement/{id}', [ParkingChannelController::class, 'destroy']);
Route::get('/channelManagement/rule', [ParkingChannelController::class, 'rule']);
Route::get('/channelManagement/search', [ParkingChannelController::class, 'search']);
// 设备管理
Route::get('/equipmentManagement', [ParkingEquipmentController::class, 'index']);
Route::get('/equipmentManagement/create', [ParkingEquipmentController::class, 'create']);
Route::post('/equipmentManagement', [ParkingEquipmentController::class, 'store']);
Route::get('/equipmentManagement/edit/{id}', [ParkingEquipmentController::class, 'edit']);
Route::put('/equipmentManagement/{id}', [ParkingEquipmentController::class, 'update']);
Route::delete('/equipmentManagement/{id}', [ParkingEquipmentController::class, 'destroy']);
Route::get('/equipmentManagement/rule', [ParkingEquipmentController::class, 'rule']);
Route::get('/equipmentManagement/search', [ParkingEquipmentController::class, 'search']);
// 岗亭管理
Route::get('/guardBoothManagement', [GuardBoothManagementController::class, 'index']);
Route::post('/guardBoothManagement', [GuardBoothManagementController::class, 'store']);

Loading…
Cancel
Save