Browse Source

警报 通知

master
wanghongjun 1 month ago
parent
commit
4b3e87ebf0
  1. 62
      app/Exports/AdminNoticeExport.php
  2. 188
      app/Http/Controllers/Admin/NoticeController.php
  3. 29
      app/Models/AdminNotice.php
  4. 5
      app/Models/ParkingCamera.php
  5. 147
      app/Services/AdminNoticeService.php
  6. 41
      database/migrations/2026_04_23_143449_create_admin_notice_table.php
  7. 9
      database/seeders/AdminConfigSeeder.php
  8. 10
      resources/lang/en/exports.php
  9. 9
      resources/lang/en/service.php
  10. 4
      resources/lang/en/validation.php
  11. 10
      resources/lang/zh-CN/exports.php
  12. 9
      resources/lang/zh-CN/service.php
  13. 4
      resources/lang/zh-CN/validation.php
  14. 10
      resources/lang/zh-TW/exports.php
  15. 9
      resources/lang/zh-TW/service.php
  16. 4
      resources/lang/zh-TW/validation.php
  17. 7
      routes/admin/api.php

62
app/Exports/AdminNoticeExport.php

@ -0,0 +1,62 @@
<?php
namespace App\Exports;
use App\Models\AdminNotice;
use App\Services\AdminNoticeService;
use App\Services\OperationLogService;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
class AdminNoticeExport implements FromArray, WithHeadings
{
public function array(): array
{
$data = [];
$index = 1;
$columns = [
'alarm_time',
'alarm_type',
'camera_ip',
'msg_type',
'space_id',
'msg_content'
];
AdminNotice::all($columns)->each(
function ($item) use (&$data, &$index) {
$oldItem = (new AdminNoticeService(
new OperationLogService()
))->getItem($item);
$data[] = [
'id' => $index,
'alarm_time' => $oldItem['alarm_time'],
'alarm_type' => $oldItem['alarm_type'],
'msg_type' => $oldItem['msg_type'],
'space_number' => $oldItem['parking_space_number'],
'license_plate' => $oldItem['license_plate'],
'camera_ip' => $oldItem['camera_ip'],
'msg_content' => $oldItem['msg_content']
];
$index += 1;
}
);
return $data;
}
/**
* @return array
*/
public function headings(): array
{
return [
__('exports.global.index'),
__('exports.notice.alarm_time'),
__('exports.notice.alarm_type'),
__('exports.notice.msg_type'),
__('exports.notice.space_number'),
__('exports.notice.license_plate'),
__('exports.notice.camera_ip'),
__('exports.notice.msg_content')
];
}
}

188
app/Http/Controllers/Admin/NoticeController.php

@ -0,0 +1,188 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Exports\AdminNoticeExport;
use App\Models\AdminNotice;
use App\Services\AdminNoticeService;
use App\Services\ApiResponseService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class NoticeController extends BaseController
{
protected AdminNoticeService $service;
/**
* 构造函数
* @param ApiResponseService $responseService
* @param AdminNoticeService $service
*/
public function __construct(
ApiResponseService $responseService,
AdminNoticeService $service
) {
parent::__construct($responseService);
$this->service = $service;
}
public function index(Request $request): JsonResponse
{
try {
$query = AdminNotice::query();
if ($request->has('alarm_type')) {
$alarm_type = $request->input('alarm_type');
if ($alarm_type) {
$query->where('alarm_type', $alarm_type);
}
}
if ($request->has('msg_type')) {
$msg_type = $request->input('msg_type');
if ($msg_type) {
$query->where('msg_type', $msg_type);
}
}
if ($request->has('start_time') && $request->has('end_time')) {
$start_time = $request->input('start_time');
$end_time = $request->input('end_time');
if ($start_time && $end_time) {
$query->whereBetween('alarm_time', [$start_time, $end_time]
);
}
}
// 分页
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$columns = [
'id',
'alarm_time',
'alarm_type',
'camera_ip',
'msg_type',
'space_id'
];
$total = $query->count();
$items = $query->latest()->forPage($page, $perPage)->select(
$columns
)->get()->each(function ($item) {
return $this->service->getItem($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()
);
}
}
public function search(): JsonResponse
{
try {
$data = [
'alarm_type_list' => get_select_data(
AdminNoticeService::getAlarmType()
),
'msg_type_list' => get_select_data(
AdminNoticeService::getMsgType()
)
];
return $this->responseService->success($data);
} catch (Exception $e) {
$m_prefix = __('exception.exception_handler.resource');
return $this->responseService->systemError(
$m_prefix . ':' . $e->getMessage()
);
}
}
public function setting(Request $request): JsonResponse
{
try {
$data = $request->all();
$rules = [
'close_alert' => 'required|in:0,1',
'close_all_alert' => 'required|in:0,1',
];
$messages = [
'close_alert.required' => __(
'validation.notice.c_empty'
),
'close_alert.numeric' => __(
'validation.notice.c_in'
),
'close_all_alert.required' => __(
'validation.notice.c_empty'
),
'close_all_alert.numeric' => __(
'validation.notice.c_in'
),
];
$validator = Validator::make($data, $rules, $messages);
if ($validator->fails()) {
throw new ValidationException($validator);
}
$this->service->updateConfig($data);
return $this->responseService->success([],
__('admin.operation_successful'));
} catch (Exception $e) {
$m_prefix = __('exception.exception_handler.resource');
return $this->responseService->systemError(
$m_prefix . ':' . $e->getMessage()
);
}
}
public function show(string $id): JsonResponse
{
try {
$columns = [
'id',
'alarm_time',
'alarm_type',
'camera_ip',
'msg_type',
'space_id',
'floor_id',
'camera_id'
];
$data = AdminNotice::query()->find($id, $columns)->toArray();
$data = $this->service->getItem($data);
return $this->responseService->success($data);
} catch (Exception $e) {
return $this->responseService->systemError(
__('exception.get_data_failed') . ':' . $e->getMessage()
);
}
}
/**
* @return BinaryFileResponse
*/
public function export(): BinaryFileResponse
{
return Excel::download(
new AdminNoticeExport(),
__('exports.notice.list') . time() . '.xlsx'
);
}
}

29
app/Models/AdminNotice.php

@ -0,0 +1,29 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
class AdminNotice extends Model
{
use HasFactory, Notifiable;
protected $table = 'admin_notice';
/**
* The attributes that should be hidden for serialization.
* @var array<int, string>
*/
protected $hidden
= [
'updated_at',
'deleted_at'
];
public function getAlarmTimeAttribute($value): string
{
return get_datetime('datetime', strtotime($value));
}
}

5
app/Models/ParkingCamera.php

@ -36,4 +36,9 @@ class ParkingCamera extends Model
{
return get_datetime('datetime', strtotime($value));
}
public static function getNumber($id)
{
return self::query()->where('id', $id)->value('number') ?? '';
}
}

147
app/Services/AdminNoticeService.php

@ -0,0 +1,147 @@
<?php
namespace App\Services;
use App\Models\AdminConfigs;
use App\Models\AdminFloor;
use App\Models\AdminNotice;
use App\Models\ParkingCamera;
use App\Models\ParkingLicensePlate;
use App\Models\ParkingSpace;
use Exception;
use Illuminate\Support\Facades\DB;
class AdminNoticeService extends BaseService
{
private static array $alarmTypeArr
= [
1 => 'hint',
2 => 'support'
];
private static array $msgTypeArr
= [
1 => 'touch',
2 => 'illegal',
3 => 'offline',
4 => 'vip',
5 => 'task'
];
public function __construct(OperationLogService $logService)
{
parent::__construct($logService);
$this->logService->menuTitle = 'alarm_notice';
}
/**
* @return array|string[]
*/
public static function getAlarmType(): array
{
$alarmTypeArr = self::$alarmTypeArr;
foreach ($alarmTypeArr as $key => $value) {
$alarmTypeArr[$key] = __('service.notice.' . $value);
}
return $alarmTypeArr;
}
/**
* @return array|string[]
*/
public static function getMsgType(): array
{
$msgTypeArr = self::$msgTypeArr;
foreach ($msgTypeArr as $key => $value) {
$msgTypeArr[$key] = __('service.notice.' . $value);
}
return $msgTypeArr;
}
public function getItem($item)
{
$alarmTypeArr = self::getAlarmType();
$msgTypeArr = self::getMsgType();
$item['alarm_type'] = $alarmTypeArr[$item['alarm_type']];
$item['msg_type'] = $msgTypeArr[$item['msg_type']];
$item['parking_space_number'] = '';
$item['license_plate'] = '';
$ParkingSpace = ParkingSpace::query()->findOrFail(
$item['space_id'],
['number', 'license_plate_id']
);
if ($ParkingSpace) {
$item['parking_space_number'] = $ParkingSpace['number'];
$item['license_plate'] = ParkingLicensePlate::getNumber(
$ParkingSpace['license_plate_id']
);
}
if (isset($item['floor_id'])) {
$item['floor'] = AdminFloor::getName($item['floor_id']);
unset($item['floor_id']);
}
if (isset($item['camera_id'])) {
$item['camera_number'] = ParkingCamera::getNumber($item['camera_id']);
unset($item['camera_id']);
}
unset($item['space_id']);
return $item;
}
public function updateConfig($data)
{
try {
DB::beginTransaction();
$content = [
'close_alert' => $data['close_alert'],
'close_all_alert' => $data['close_all_alert']
];
$model = AdminConfigs::query()->where('name', 'information_setting')
->first();
$oldValues = $model->toArray();
$model->update([
'content' => $content,
'updated_at' => get_datetime()
]);
$this->logService->logUpdated($model, $oldValues, 'config.update');
DB::commit();
return $model;
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
public static function createData($data, $user_id)
{
$alarm_type = $data['alarm_type'];
$camera_ip = $data['camera_ip'];
$msg_type = $data['msg_type'];
$create = [
'alarm_time' => get_datetime(),
'alarm_type' => $alarm_type,
'camera_ip' => $camera_ip,
'msg_type' => $msg_type,
'is_read' => 0,
'admin_user_id' => $user_id,
'created_at' => get_datetime()
];
if (isset($data['space_id']) && $data['space_id']) {
$create['space_id'] = $data['space_id'];
}
if (isset($data['floor_id']) && $data['floor_id']) {
$create['floor_id'] = $data['floor_id'];
}
if (isset($data['camera_id']) && $data['camera_id']) {
$create['camera_id'] = $data['camera_id'];
}
if (isset($data['msg_content']) && $data['msg_content']) {
$create['msg_content'] = $data['msg_content'];
}
AdminNotice::query()->create($create);
}
}

41
database/migrations/2026_04_23_143449_create_admin_notice_table.php

@ -0,0 +1,41 @@
<?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('admin_notice', function (Blueprint $table) {
$table->id();
$table->timestamp('alarm_time')->comment('信息时间');
$table->tinyInteger('alarm_type')->default(0)->comment('信息类型');
$table->string('camera_ip', 15)->comment('ip');
$table->tinyInteger('msg_type')->default(0)->comment('信息内容');
$table->string('msg_content', 255)->default('')->comment('信息内容详情');
$table->integer('space_id')->nullable()->comment('车位id');
$table->integer('floor_id')->nullable()->comment('楼层id');
$table->integer('camera_id')->nullable()->comment('相机设备id');
$table->tinyInteger('is_read')->default(0)->comment('是否已读');
$table->integer('admin_user_id')->default(0)->comment('操作员');
$table->timestamps();
$table->innoDb();
$table->index('alarm_type', 'alarm_type');
$table->index('msg_type', 'msg_type');
$table->comment('警报 & 消息');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('admin_notice');
}
};

9
database/seeders/AdminConfigSeeder.php

@ -69,6 +69,15 @@ class AdminConfigSeeder extends Seeder
]),
'status' => 1,
'created_at' => $created_at
],[
'title' => '信息设定',
'name' => 'information_setting',
'content' => json_encode([
'close_alert' => 0,
'close_all_alert' => 0
]),
'status' => 1,
'created_at' => $created_at
]
];
}

10
resources/lang/en/exports.php

@ -56,5 +56,15 @@ return [
],
'event_calendar' => [
'list' => 'Activity Calendar Template'
],
'notice' => [
'list' => 'Alerts_Notifications',
'alarm_time' => 'Information Time',
'alarm_type' => 'Information Type',
'msg_type' => 'information content',
'space_number' => 'Parking Number',
'license_plate' => 'license plate number',
'camera_ip' => 'IP',
'msg_content' => 'Information Content Details'
]
];

9
resources/lang/en/service.php

@ -78,5 +78,14 @@ return [
'pattern_exists' => 'The current activity mode already exists',
'error_status' => 'The current activity status cannot be deleted',
'error_end' => 'The current activity status cannot be ended'
],
'notice' => [
'hint' => 'Prompt message',
'support' => 'Support Information',
'touch' => 'touch the line',
'illegal' => 'Illegal parking',
'offline' => 'Device offline',
'vip' => 'VIP',
'task' => 'Task switching'
]
];

4
resources/lang/en/validation.php

@ -111,5 +111,9 @@ return [
'p_empty' => 'Activity mode cannot be empty',
's_empty' => 'The start time cannot be empty',
'e_empty' => 'The end time cannot be empty'
],
'notice' => [
'c_empty' => 'Update data cannot be empty',
'c_in' => 'Updating data can only be 0 or 1',
]
];

10
resources/lang/zh-CN/exports.php

@ -56,5 +56,15 @@ return [
],
'event_calendar' => [
'list' => '活动行事历模板'
],
'notice' => [
'list' => '警报通知',
'alarm_time' => '信息时间',
'alarm_type' => '信息类型',
'msg_type' => '信息内容',
'space_number' => '车位号码',
'license_plate' => '车牌号码',
'camera_ip' => 'IP',
'msg_content' => '信息内容详情'
]
];

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

@ -78,5 +78,14 @@ return [
'pattern_exists' => '当前活动模式已存在',
'error_status' => '当前活动状态不可删除',
'error_end' => '当前活动状态不可结束'
],
'notice' => [
'hint' => '提示信息',
'support' => '支援信息',
'touch' => '压线',
'illegal' => '违泊',
'offline' => '设备离线',
'vip' => 'VIP',
'task' => '任务切换'
]
];

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

@ -111,5 +111,9 @@ return [
'p_empty' => '活动模式不能为空',
's_empty' => '开始时间不能为空',
'e_empty' => '结束时间不能为空'
],
'notice' => [
'c_empty' => '更新数据不能为空',
'c_in' => '更新数据只能是0或1',
]
];

10
resources/lang/zh-TW/exports.php

@ -56,5 +56,15 @@ return [
],
'event_calendar' => [
'list' => '活動行事曆範本'
],
'notice' => [
'list' => '警報通知',
'alarm_time' => '資訊時間',
'alarm_type' => '資訊類型',
'msg_type' => '資訊內容',
'space_number' => '車位號碼',
'license_plate' => '車牌號碼',
'camera_ip' => 'IP',
'msg_content' => '資訊內容詳情'
]
];

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

@ -78,5 +78,14 @@ return [
'pattern_exists' => '當前活動模式已存在',
'error_status' => '當前活動狀態不可删除',
'error_end' => '當前活動狀態不可結束'
],
'notice' => [
'hint' => '提示資訊',
'support' => '支援資訊',
'touch' => '壓線',
'illegal' => '違泊',
'offline' => '設備離線',
'vip' => 'VIP',
'task' => '任務切換'
]
];

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

@ -111,5 +111,9 @@ return [
'p_empty' => '活動模式不能為空',
's_empty' => '開始時間不能為空',
'e_empty' => '結束時間不能為空'
],
'notice' => [
'c_empty' => '更新數據不能為空',
'c_in' => '更新數據只能是0或1',
]
];

7
routes/admin/api.php

@ -24,6 +24,7 @@ use App\Http\Controllers\Admin\ParkingCameraController;
use App\Http\Controllers\Admin\ParkingPatternController;
use App\Http\Controllers\Admin\ParkingPatternSpaceController;
use App\Http\Controllers\Admin\EventCalendarController;
use App\Http\Controllers\Admin\NoticeController;
Route::group(['prefix' => 'admin'], function () {
@ -139,6 +140,11 @@ Route::group(['prefix' => 'admin'], function () {
// VIP进出记录
Route::get('/vipAccessRecord', [VipAccessRecordController::class, 'index']);
Route::get('/vipAccessRecord/{id}', [VipAccessRecordController::class, 'show']);
// 警报&通知
Route::get('/notice', [NoticeController::class, 'index']);
Route::get('/notice/search', [NoticeController::class, 'search']);
Route::post('/notice/setting', [NoticeController::class, 'setting']);
Route::get('/notice/show/{id}', [NoticeController::class, 'show']);
// 系统日志
Route::get('/operationLog/index', [OperationLogController::class, 'index']);
@ -203,4 +209,5 @@ Route::group(['prefix' => 'admin'], function () {
Route::get('/pattern/spacesExport', [ParkingPatternController::class, 'importTemplate']);
Route::get('/operationLog/export', [OperationLogController::class, 'export']);
Route::get('/eventCalendar/export', [EventCalendarController::class, 'importTemplate']);
Route::get('/notice/export', [NoticeController::class, 'export']);
});

Loading…
Cancel
Save