добавляю все изменения проекта на текущий момент

This commit is contained in:
vasya
2026-02-27 18:49:27 +03:00
parent e927910fda
commit 9c35f4e35e
303 changed files with 79434 additions and 2558 deletions
@@ -0,0 +1,90 @@
<?php
namespace Modules\Taxi\App\Console;
use Error;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use App\Console\Commands\BaseScheduleCommand;
use App\Mail\BaseMailerObj;
use Illuminate\Support\Facades\Mail;
use App\Mail\Mailer;
use Modules\Taxi\App\Models\TaxiMain;
use DateTime;
class SendOrdersToTaxiCommand extends BaseScheduleCommand
{
/**
* The name and signature of the console command.
*/
public $signature = 'taxi:send';
/**
* The console command description.
*/
public $description = 'Отправка заказов такси на сегодня агрегаторам';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->executeCommand(function () {
$todayOrders = TaxiMain::where('cancel_rqst', 0)
->where('taxi_date', (new DateTime())->format('Y-m-d'))
->get()
->toArray();
if (!$todayOrders) {
Mail::send(new Mailer(
new BaseMailerObj(
)
));
echo '<pre>'; var_dump('da'); echo'</pre>';
} else {
echo '<pre>'; var_dump('net'); echo'</pre>';
}
throw new Error('Ошипка!');
}, 'еще инва');
// try {
// throw new Error('Ошипка!');
// } catch (\Throwable $th) {
// \Log::channel('schedule_err')->error($th->getFile() . ": " . $th->getMessage() . " Line: " . $th->getLine());
// }
//$this->info('Daily report generated at ' . now());
return 0;
}
/**
* Get the console command arguments.
*/
protected function getArguments(): array
{
return [
['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
/**
* Get the console command options.
*/
protected function getOptions(): array
{
return [
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace Modules\Taxi\App\DTO;
use Modules\Taxi\App\Http\Requests\TaxiOrderRequest;
/**
* Класс для формирования DTO с данными по заказу на такси
*/
class TaxiOrderDTO
{
public function __construct(
public readonly int | null $id,
public readonly string | null $emp_login,
public readonly string $emp_phone,
public readonly string $taxi_date,
public readonly string $taxi_time,
public readonly string $taxi_address_from,
public readonly string $taxi_address_to,
public readonly int $cancel_rqst = 0,
) {}
/**
* Метод формирования DTO с данными по заказу такси
*
* По сути, простой сеттер, позволяющий создать для обрабатываемого объекта данных экземпляр класса для более удобного взаимодействия с ним, более стандартизированного и более надежного (так как все параметры прописаны в одном месте)
* Использование self позволяет нам не зависеть от названия текущего класса, если мы захотим его переименовать
*
* @param TaxiOrderRequest $rqst запрос с валидными данными по заказу, пришедшими с фронта
* @param string $rqstMethod метод обрабатываемого запроса. Обновление или создание, например
* @return self
*/
public static function setTaxiOrderDTO(TaxiOrderRequest $rqst, string $rqstMethod): self
{
$validated = $rqst->validated();
switch ($rqstMethod) {
case 'PATCH':
return new self(
id: $validated['id'],
//При обновлении запроса логин пользователя должен оставаться неизменным
emp_login: $rqst->emp_login,
emp_phone: $validated['emp_phone'],
taxi_date: $validated['taxi_date'],
taxi_time: $validated['taxi_time'],
taxi_address_from: $validated['taxi_address_from'],
taxi_address_to: $validated['taxi_address_to'],
cancel_rqst: $validated['cancel_rqst'] ?? null,
);
break;
default:
return new self(
id: $validated['id'] ?? null,
emp_login: $validated['emp_login'],
emp_phone: $validated['emp_phone'],
taxi_date: $validated['taxi_date'],
taxi_time: $validated['taxi_time'],
taxi_address_from: $validated['taxi_address_from'],
taxi_address_to: $validated['taxi_address_to'],
cancel_rqst: $validated['cancel_rqst'] ?? null,
);
break;
}
}
}
@@ -0,0 +1,350 @@
<?php
namespace Modules\Taxi\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\Taxi\App\Models as TaxiModels;
use App\Models\OldMagicModels;
#Гаврилов
//Rule и Validatir НЕ НУЖЕН? ЕСЛИ МЫ ВАЛИДИРУЕМ В ОТДЕЛЬОМ FORM_REQUEST
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Validator;
use Modules\Taxi\App\Http\Requests\TaxiOrderRequest;
#Гаврилов
//КЛАСС НИЖЕ НЕ НУЖЕН, ТАК КАК ИСПОЛЬЗУЕМ СЕРВИС?
use Illuminate\Support\Facades\Mail;
use Modules\Taxi\App\Services\TaxiOrderService;
use Modules\Taxi\App\Services\TaxiMailerService;
use Modules\Taxi\App\DTO\TaxiOrderDTO;
use Modules\Taxi\App\Jobs\SendMailNewOrder;
//use Illuminate\Support\Facades\Config;
use App\Mail\Mailer;
use DateTime;
use Error;
use Exception;
use Illuminate\Support\Facades\DB;
use Modules\Taxi\App\Mail\NewOrderMail;
use Illuminate\Support\Facades\Redis;
use App\Facades\RedisNotifications;
class TaxiController extends Controller
{
/**
* Конструктор
*
* @param TaxiMailerService $sendMailService сервис отправки писем
* @param TaxiOrderService $orderService сервис работы с заказами
*/
public function __construct(protected TaxiMailerService $sendMailService, protected TaxiOrderService $orderService) {
}
/**
* Получаем информацию по существующему заказу
*
* @param integer $orderId идентификатор заказа
*/
public function getOrderById(int $orderId)
{
$taxiOrder = TaxiModels\TaxiMain::where('id', $orderId)->first();
return $taxiOrder;
}
/**
* Получение адресов офисов для подстановки в запросы такси
*
* @return array
*/
public function getOfficeAddress()
{
$taxiAddress = TaxiModels\OfficeAddress::all();
return $taxiAddress;
}
/**
* Получение всех активных заказов
*
* @return array
*/
public function getActiveOrders()
{
$todaydate = new DateTime();
$todaydate->setTime(00, 00, 00);
$todaydate = $todaydate->format('Y-m-d');
$orders = TaxiModels\TaxiMain::where('taxi_date', '>=', $todaydate)
->get();
return $orders;
}
/**
* Метод получения заказов по условиям фильтра
*
* @param Request $rqst запрос с параметрами поиска
* @return string
*/
public function getFilterOrders(Request $rqst)
{
$rqstCond = [];
foreach ($rqst->all() as $fieldName => $fieldVal) {
if (!$fieldVal) continue;
$rqstCond[] = [$fieldName, '=', $fieldVal];
}
$filterRqst = TaxiModels\TaxiMain::where($rqstCond)->get();
return json_encode($filterRqst->toArray());
}
//ГАВРИЛОВ используй транзакцию, а то создается запрос, не отправляется письмо и сообщение об ошибке
/**
* Show the form for creating a new resource.
*/
public function createTaxiOrder(TaxiOrderRequest $rqst)
{
$validateData = TaxiOrderDTO::setTaxiOrderDTO($rqst, $rqst->method());
//Пытаемся найти уже существующий запрос на этот же логин и эту же дату. Если запрос найден, возвращаем ошибку.
$searchExistsOrder = TaxiModels\TaxiMain::where([
['emp_login', '=', $validateData->emp_login],
['taxi_date', '=', $validateData->taxi_date]
])->take(1)->get();
if (count($searchExistsOrder)) {
return response()->json(['message' => "Ошибка! Заявка для сотрудника $validateData->emp_login на дату $validateData->taxi_date уже существует"], 400);
}
// $createRqstMsg;
try {
$newOrderId = (new TaxiModels\TaxiMain())->createOrder($validateData);
//$newOrderId = $this->orderService->createOrder($validateData);
$createRqstMsg = "Заявка успешно создана. Номер $newOrderId";
#Гаврилов
//ПРОВЕРЬ, ЧТОБЫ ПИСЬМО НЕ ОТПРАВЛЯЛОСЬ, ЕСЛИ ВОЗНИКАЕТ ОШИБКА НА ЭТАПЕ СОЗДАНИЯ ЗАЯВКИ
//Один метод отправки письма для всех методов работы с заказом: создание, редактирование, отмена
$this->sendMailService->sendMail(new NewOrderMail($validateData, $newOrderId));
// SendMailNewOrder::dispatch(json_decode(json_encode($validateData), true), $newOrderId)->onQueue('emails')->delay(10);
// Mail::send(new Mailer(
// [$validateData->emp_login],
// 'Создание заказа на такси',
// "Для вас создана заявка на заказ такси на дату <b>{$validateData->taxi_date}</b> и время <b>{$validateData->taxi_time}</b>. Номер заказа <b>{$newOrderId}</b>",
// #Гаврилов
// //ЗДЕСЬ НЕПОНЯТНО ОТКУДА ВЗЯТЬ ЗНАЧЕНИЕ. ОБРАТИТЬСЯ К ЛОКАЛЬНО МОДУЛЬНОМУ ENV?
// config('taxi.name_ru'),
// ));
return response()->json( ['message' => "Заявка успешно создана. Номер заявки $newOrderId"] );
} catch (\Throwable $t) {
#Гаврилов
//ОТПРАВКА СООБЩЕНИЯ С ОШИБКОЙ. ЗАТЕСТИ и отправляй response
//ОШИБКИ $T->GETmESSAGE НУЖНО ЛОГИРОВАТЬ, НО ПОЛЬЗОВАТЕЛЯМ ВЫВОДИТЬ ПРОСТО СООБЩЕНИЕ оШИБКА, ИНАЧЕ ОНИ ВИДЯТ ТЕХН ЧЕСКИЙ ТЕКСТ
//СНАЧАЛА ПЕРЕХВАТЫЙ EXCEPTION, ПОТОМ УЖЕ THOWBLE. EXCEPTION ВЫВОДИ ПОЛЬЗОВАТЕЛЮ, THORWBLE ЛОГИРУЙ ОТДЕЛЬНО
echo '<pre>'; var_dump($t->getMessage()); echo'</pre>';
echo '<pre>'; var_dump($t->getTrace()); echo'</pre>';
$createRqstMsg = "Ошибка!" . $t->getMessage() . ". Заявка не создана";
return response()->json(['message' => $createRqstMsg], 400);
}
//ГАВРИЛОВ
//ДОБАВЬ ФОРМИРОВАНИЕ ОТВЕТА ЧЕРЕЗ ГЕНЕРАЦИЮ РЕСУРСА, а не через response()
}
#Гаврилов
//ИСПОЛЬЗУЙ TaxiOrderRequest ПРИ ОБРАБОТКЕ ЗАЯВОК НА ТАКСИ
/**
* Метод отмены запроса
*
* @param integer $rqstId идентификатор запроса
* @return string
*/
public function cancelRqst(int $rqstId): string
{
#Гаврилов
//ПОПРОБУЙ ОТПРАВИТЬ ЗАПРОС К API ЕНДПОИНТУ (ОТМЕНА ЗАПРОСА НА ТАКСИ) ЧЕРЕЗ POSTMAN С ПЕРЕДАЧЕЙ ЗАГОЛОВКА АУТЕНТИФИКАЦИИ С SANCTUM ТОКЕНОМ. ДОЛЖЕН ПРОПУСТИТЬ, А БЕЗ ЗАГОЛОВКА НЕ ДОЛЖЕН
$rqstData = $this->getOrderById($rqstId);
try {
#Гаврилов
//ПЕРЕНЕСИ ЭТУ ПРОВЕРКУ В РОУТИНГ? FINDORFAIL С РЕДИРЕКТОМ И ОШИБКОЙ
if (!$rqstData) {
throw new \Exception("Запрос $rqstId не существует");
}
if (!$this->checkRqstDate($rqstData->taxi_date)) {
throw new \Exception("Дата запроса $rqstId уже прошла");
}
} catch (\Throwable $t) {
return response()->json([
'errorMsg' => "Ошибка: $t->getMessage(). Заявка не отменена",
], 400);
}
$rqstData['cancel_rqst'] = 1;
$rqstData->save();
$this->sendMailService->sendCancelOrderMail($validateData, $newOrderId);
// Mail::send(new Mailer(
// [$rqstData->emp_login],
// 'Отмена заказа на такси',
// "Заказ номер $rqstId на дату $rqstData->taxi_date и время $rqstData->taxi_time отменен",
// $this->appName));
return redirect('/taxi/home')->with([
'notification' => "Запрос $rqstId отменен",
'notification_err' => false
]);
}
/**
* Проверяем находится ли дата запроса на такси в диапазоне сегодняшнего или завтрашнего дня.
*
* @param string $rqstDate дата запроса на такси
* @return boolean
*/
private function checkRqstDate($rqstDate): bool
{
$todaydate = new DateTime();
$todaydate->setTime(00, 00, 00);
return new DateTime($rqstDate) >= $todaydate;
}
/**
* Получаем временные промежутки
*
* @return array
*/
public function getTimePeriods()
{
$empAllData = TaxiModels\TaxiTimePeriod::select('time_period', 'is_morning_time')
->where('archive', 0)
->get();
return $empAllData;
}
/**
* Метод получает информацию по всем активным пользователям
*
* @return array
*/
public function getActiveUsersInfo()
{
$empAllData = OldMagicModels\CcEmp::whereIn('emp_area', ['Курск', 'Пенза'])
->whereNotIn('emp_state', ['Уволен', 'Декрет', 'Перевод на позицию', 'Перевод в ГРАН'])
->get();
foreach ($empAllData as &$empData) {
$empData['full_name'] = $empData['emp_surname'] . " " . $empData['emp_name'] . " " . $empData['emp_surname'];
$empData['extension_number'] = $empData['emp_phone'];
$empData['emp_phone'] = $empData['emp_mob_phone'];
}
return $empAllData;
}
/**
* Метод получает информацию по конкретному пользователю
*
* @return array
*/
public function getUserInfoByLogin(string $userLogin)
{
$empLoginData = OldMagicModels\CcEmp::where('emp_login', $userLogin)
->get();
// dd($empLoginData);
$empLoginData[0]['full_name'] = $empLoginData[0]['emp_surname'] . " " . $empLoginData[0]['emp_name'] . " " . $empLoginData[0]['emp_surname'];
$empLoginData[0]['extension_number'] = $empLoginData[0]['emp_phone'];
$empLoginData[0]['emp_phone'] = $empLoginData[0]['emp_mob_phone'];
return $empLoginData;
}
#Гаврилов
//НЕ ЗАБУДЬ УДАЛИТЬ
public function testRedisMethod()
{
RedisNotifications::module('taxi')
->notifications(['текст1', 'tеуые2'])
->types(['error', 'success'])
->put();
}
/**
* Обновление заявки на такси
*
* @param TaxiOrderRequest $rqst информация по измененному запросу
* @return response
*/
public function updateTaxiOrder(TaxiOrderRequest $rqst)
{
try {
DB::beginTransaction();
#Гаврилов
//ЕЩЕ РАЗ, ЧТО ЗА LOCKFORUPDATE
$orderData = TaxiModels\TaxiMain::lockForUpdate()->find($rqst->id);
$oldOrderData = TaxiModels\TaxiMain::find($rqst->id);
if (!$orderData) {
throw new Exception("Заказ номер " . $rqst->id . " не найден");
//return response()->json(['message' => 'Заказ номер ' . $rqst->id . ' не найден'], 400);
}
$editOrderData = TaxiOrderDTO::setTaxiOrderDTO($rqst, $rqst->method());
#Гаврилов
//НА ДОМАШНЕЙ СТРАНИЦЕ ПОКАЗЫВАТЬ ТОЛЬКО ТЕ ЗАПРОСЫ, ДАТА КОТОРЫХ СЕГОДНЯ ИЛИ ЗАВТРА
$dateInterval = (new DateTime())->diff(new DateTime($orderData->taxi_date));
if ($dateInterval->format('%R') == '-' && $dateInterval->format('%a') >= 1) {
throw new Exception("Нельзя менять заявки, дата которых уже прошла");
// return response()->json(['message' => 'Нельзя менять заявки, дата которых уже прошла'], 400);
}
foreach($editOrderData as $fieldName => $fieldVal) {
if (isset($orderData->$fieldName)) {
$orderData[$fieldName] = $fieldVal;
}
}
$orderDataDiff = $orderData->getDirty();
if (empty($orderDataDiff)) {
throw new Exception("Вы не внесли никаких изменений");
//return response()->json(['message' => 'Вы не внесли никаких изменений'], 400);
}
$orderData->save();
$this->sendMailService->sendEditOrderMail($oldOrderData->toArray(), $orderDataDiff, $orderData['id']);
DB::commit();
return response()->json(['message' => 'Заявка успешно изменена'], 200);
} catch (\Exception $e) {
DB::rollBack();
return response()->json(['message' => "Ошибка." . $e->getMessage() . ". Не удалось обновить заявку"], 400);
}
//ГАВРИЛОВ. ДОБАВЬ БЛОКИРОВКУ В МОМЕНТ ПОЛУЧЕНИЯ ДАННЫХ ПО РЕДАКТИРУЕМОМУ ЗАПРОСУ И ДО МОМЕНТА ЕГО ОБНОВЛЕНИЯ. ЛИБО ЧЕРЕЗ УСТАНОВКУ УРОВНЯ ИЗОЛЯЦИИ
//ГАВРИЛОВ. ДОБАВЬ В DTO НЕ ОБНУЛЕНИЕ НЕНУЖНЫХД ДАННЫХ, А ПРИНУДИТЕЬНАЯ ИХ УСТАНОВКА. НАПРИМЕР ПРИ ОБНОВЛЕНИИ ЗАПРОСА НА ТАКСИ, УСТАНАВЛИВАТЬ EMP_LOGIN = ПЕРЕДАННОМУ ЗНАЧЕНИЮ, ВЗЯТОМУ ИЗ СУЩЕСТВУЮЩЕГО ЗАПРОСА.
//Обновляем текущую модель, добавляя туда измененные значения, затем проверяем есть ли измененные значения через getDirty()
//ГАВРИЛОВ. убрать поле Запрос актуален? и при отмене заявки убирать ее из таблицы Активные заявки
//гаврилов. СДЕЛАТЬ ВОЗМОЖНОСТЬ ВЫГРУЗИТЬ В ОТЧЕТНОСТЬ ВСЕ ИМЕЮЩИЕСЯ ЗАЯВКИ В CSV ФАЙЛ.
}
}
@@ -0,0 +1,52 @@
<?php
namespace Modules\Taxi\App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
/**
* Блокировщик работы с заявками на такси в случае слишком ранееЙ, слишком поздней работы с ресурсом
*
* Решил блочить рабоут приложения в части работы с заявками в очень ранее или очень позднее время. Это связано с нежеланием валидировать при создании или редактировании заявки передаваемый временной промежуток. Например, если текущее время 22:15, а заявка созается на 22:00.
*/
class CheckTimeRqstAvailability
{
public function handle(Request $request, Closure $next)
{
#Гаврилов
//ПОМЕНЯЙ НА 21 НА РЕЛИЗЕ
$maxRqstTime = 23;
$minRqstTime = 7;
$timezone = new \DateTimeZone('Europe/Moscow');
$timeNow = (new \DateTime())->setTimezone($timezone);
$timeMax = (new \DateTime())->setTimezone($timezone);
$timeMin = (new \DateTime())->setTimezone($timezone);
$timeMax->setTime($maxRqstTime, 00, 00);
$timeMin->setTime($minRqstTime, 00, 00);
#Гаврилов
//ПОМЕНЯЙ
if (!($timeNow > $timeMin && $timeNow < $timeMax)) {
//if (!($timeNow > $timeMin && $timeNow > $timeMax)) {
$errMsg = "Работа с заявками на такси заблокирована с $maxRqstTime:00 по 0$minRqstTime:00";
#Гаврилов
//ПОДУМАТЬ КАКОЙ КОД ЛУЧШЕ ВЕРНУТЬ
//Метод expectsJson позволяет определить вызывается ли api роут через ajax/fetch, либо web route.
if ($request->expectsJson()) {
return response()->json([
'errorMsg' => $errMsg
], 400);
} else {
#Гаврилов
//ПРОВЕРЬ КОГДА РЕАЛИЗУЕШЬ ОПОВЕЩЕНИЯ
return redirect('/taxi/home')->with([
'notification' => $errMsg,
'notification_err' => true
]);
}
} else {
return $next($request);
}
}
}
@@ -0,0 +1,109 @@
<?php
namespace Modules\Taxi\App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use App\Models\OldMagicModels;
use Illuminate\Validation\Rule;
use DateTime;
class TaxiOrderRequest extends FormRequest
{
/**
* Действия перед валидацией.
*
* @return void
*/
protected function prepareForValidation()
{
//echo '<pre>'; var_dump($this->all()); echo'</pre>';
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
//Получаем список активных пользователей
$empActiveLogins = OldMagicModels\CcEmp::select('emp_login')
->whereNotIn('emp_state', ['Уволен', 'Декрет'])
->get()
->toArray();
//Правила валидации
$validationRules = [
'id' => [
'int'
],
'taxi_date' => [
'date_format:Y-m-d',
//Заказ можно создать только на сегодняшний или завтрашний день
Rule::in([
(new DateTime())->format('Y-m-d'),
(new DateTime())->modify('+1 day')->format('Y-m-d')
]),
],
'taxi_time' => [
'required',
'string',
'regex:/^[0-9]{2}:[0-9]{2}$/', //время в формате 00:00
],
'cancel_rqst' => 'boolean',
'emp_phone' => [
'required',
'int',
'regex:/^(7|8)?[0-9]{10}$/'
],
'emp_login' => [
$validationRules['emp_login'] = [
'required',
'string',
'regex:/^[a-z\-]+[0-9]*$/',
//Логины ищем среди активных логинов на текущий момент
Rule::in(
array_map(fn($el) => $el['emp_login'], $empActiveLogins)
)
],
],
'taxi_address_from' => [
'required',
'string',
'max: 500'
],
'taxi_address_to' => [
'required',
'string',
'max: 500'
]
];
//Во время создания запроса поле ID не валидируется
if ($this->isMethod('POST')){
$validationRules['id'] = [
'prohibited'
];
}
return $validationRules;
}
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Переопределение сообщений об ошибках валидации
*
* @return array
*/
public function messages(): array
{
return [
'taxi_date.in' => 'Заявку на такси можно завести только на сегодняшний или завтрашний день'
];
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Modules\Taxi\App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Mail;
use App\Mail\Mailer;
use App\Mail\BaseMailerObj;
use App\Job\BaseJob;
use Error;
/**
* Джоба размещения задачи на отправку писем по такси
*/
class TaxiMailJob extends BaseJob
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*/
public function __construct(public BaseMailerObj $jobData)
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
Mail::send(new Mailer(
$this->jobData
));
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Modules\Taxi\App\Mail;
use App\Mail\BaseMailerObj;
use Modules\Taxi\App\DTO\TaxiOrderDTO;
/**
* Базовый класс отправки письма по заказу такси. Объявляет обязательный к реализации метод формирования данных для отправки.
*/
abstract class BaseTaxiMail
{
/**
* Undocumented function
*
* @param TaxiOrderDTO $orderData
* @param integer|null $orderId идентификатор заказа такси
*/
public function __construct(
public TaxiOrderDTO $orderData,
public ?int $orderId
){
}
/**
* Метод формирует объект с данными письма для передачи в джобу
*
* @return BaseMailerObj базовый класс объекта параметров письма для мейлера
*/
abstract public function prepareDataForMail(): BaseMailerObj;
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Modules\Taxi\App\Mail;
use Modules\Taxi\App\DTO\TaxiOrderDTO;
use App\Mail\BaseMailerObj;
/**
* Класс формирования объекта с данными для передачи в джобу отправки письма. Наследует базовый класс отправки письма по такси, где объвляется обязательный метод
*/
class NewOrderMail extends BaseTaxiMail
{
public function __construct(
public TaxiOrderDTO $orderData,
public ?int $orderId)
{
}
/**
* Метод подготавливает данные письма для передачи в джобу
*
* @return BaseMailerObj
*/
public function prepareDataForMail(): BaseMailerObj
{
return new BaseMailerObj(
[$this->orderData->emp_login],
"Создана заявка на такси",
"Для вас создана заявка на такси № {$this->orderId}:
<ul>
<li><b>Дата:</b> {$this->orderData->taxi_date}</li>
<li><b>Время:</b> {$this->orderData->taxi_time}</li>
<li><b>Откуда:</b> {$this->orderData->taxi_address_from}</li>
<li><b>Куда:</b> {$this->orderData->taxi_address_to}</li>
</ul>",
config('taxi.name_ru'),
);
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Modules\Taxi\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Modules\Taxi\Database\factories\OfficeAddressFactory;
class OfficeAddress extends Model
{
protected $table = 'office_address';
protected $guarded = [];
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Modules\Taxi\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Modules\Taxi\Database\factories\TaxiMainFactory;
//use Spatie\Activitylog\Traits\LogsActivity;
// use Spatie\Activitylog\LogOptions;
use App\Traits\LogsActivity_custom;
use Modules\Taxi\App\DTO\TaxiOrderDTO;
use App\Enums\LogBusinessEvents;
class TaxiMain extends Model
{
//use LogsActivity;
use LogsActivity_custom;
protected $table = 'taxi_main';
protected $guarded = [];
// protected $activityCustomProperties = [
// 'emp_login' => 'dgavrilov',
// ];
// public function getActivitylogOptions(): LogOptions
// {
// return LogOptions::defaults()
// ->logAll()
// ->logOnlyDirty()
// ->useLogName('Taxi')
// ->logExcept(['created_at', 'updated_at']);
// }
public function __construct()
{
parent::__construct();
//В трейте LogsActivity_custom это свойство отвечает за название приложения, события в котором логируются. Оно записывается в поле activity_log.log_name
$this->logActivity_custom__name = 'Taxi';
$this->addCustomLogProperties(['custom__test' => 'test']);
}
//ГАВРИЛОВ. сделай метод не статическим
public static function getFieldsTitle()
{
return [
'id' => 'Номер запроса',
'emp_login' => 'Логин сотрудника',
'emp_phone' => 'Телефон сотрудника',
'taxi_date' => 'Дата заказа',
'taxi_time' => 'Время заказа',
'taxi_address_from' => 'Адрес откуда',
'taxi_address_to' => 'Адрес куда',
'cancel_rqst' => 'Запрос отменен',
];
}
public function createOrder(TaxiOrderDTO $orderData): string
{
//ГАВРИЛОВ. проверь будет ли возвращаться ошибка без try catch
// try {
$this->logBusinessEvent(LogBusinessEvents::Create);
$newOrder = $this->create(
[
'emp_login' => $orderData->emp_login,
'taxi_date' => $orderData->taxi_date,
'emp_phone' => $orderData->emp_phone,
'taxi_time' => $orderData->taxi_time,
'taxi_address_from' => $orderData->taxi_address_from,
'taxi_address_to' => $orderData->taxi_address_to
]
);
return $newOrder->id;
// } catch (\Exception $e) {
// return $e->getMessage();
// }
}
// /**
// * @return string Метод нужен для переопределения параметра трейта. Простое переопределение вызывает конфликт
// */
//protected $logActivity_custom__name = 'Taxi';
// public function getLogName(): string
// {
// return 'Taxi';
// }
//protected $logActivity_custom__name;
// public function getActivitylogOptions(): LogOptions
// {
// //Через метод default() получаем параметры класса логирования по умолчанию. Ниже можем их переопределять в зависимости от специфики работы с моделью
// $logOptions = LogOptions::defaults()
// ->logOnly(['*']); //Устанавиваем список логируемых данных (полей модели), либо, как в этом случае, логируем все поля
// //->logExcept(['created_at', 'updated_at']) //Не логируем поля изменения и создания записи в ЛЮБОМ случае
// //->logOnlyDirty(); //Логируем только те поля, которые были изменены
// $logOptions->logName = 'Taxi'; //Устанавливаем значение для имени приложения
// return $logOptions;
// }
}
@@ -0,0 +1,14 @@
<?php
namespace Modules\Taxi\App\Models;
use Illuminate\Database\Eloquent\Model;
class TaxiTimePeriod extends Model
{
/**
* The attributes that are mass assignable.
*/
protected $guarded = [];
}
View File
@@ -0,0 +1,68 @@
<?php
namespace Modules\Taxi\App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The module namespace to assume when generating URLs to actions.
*/
protected string $moduleNamespace = 'Modules\Taxi\App\Http\Controllers';
protected $middleware_checkTimeRqstAvailable = [Modules\Taxi\Http\Middleware\CheckTimeRqstAvailability::class];
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*/
public function boot(): void
{
parent::boot();
}
/**
* Define the routes for the application.
*/
public function map(): void
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*/
protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->moduleNamespace)
->group(module_path('Taxi', '/routes/web.php'));
Route::prefix('taxi')
->middleware('web')
->group(base_path('Modules/Taxi/routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')
->namespace($this->moduleNamespace)
->group(module_path('Taxi', '/routes/api.php'));
Route::prefix('taxi')
->middleware('api')
->group(base_path('Modules/Taxi/routes/api.php'));
}
}
@@ -0,0 +1,29 @@
<?php
namespace Modules\Taxi\App\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Taxi\App\Services\TaxiMailerService;
class TaxiMailerProvider extends ServiceProvider
{
/**
* Register the service provider.
*/
public function register(): void
{
#Гаврилов
//ЗАЧЕМ В КАЧЕСТВЕ АРГУМЕНТА ПЕРЕЛАЕТСЯ $app?
$this->app->bind(TaxiMailerService::class, function($app) {
return new TaxiMailerService();
});
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
}
@@ -0,0 +1,27 @@
<?php
namespace Modules\Taxi\App\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Taxi\App\Services\TaxiOrderService;
class TaxiOrderProvider extends ServiceProvider
{
/**
* Register the service provider.
*/
public function register(): void
{
$this->app->bind(TaxiOrderService::class, function($app) {
return new TaxiOrderService();
});
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
}
@@ -0,0 +1,30 @@
<?php
namespace Modules\Taxi\App\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Taxi\App\Console\SendOrdersToTaxiCommand;
class TaxiScheduleProvider extends ServiceProvider
{
/**
* Register the service provider.
*/
public function register(): void
{
// Регистрируем команду в системе
if ($this->app->runningInConsole()) {
$this->commands([
SendOrdersToTaxiCommand::class, // ✅ Теперь Laravel знает о команде
]);
}
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
}
@@ -0,0 +1,114 @@
<?php
namespace Modules\Taxi\App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class TaxiServiceProvider extends ServiceProvider
{
protected string $moduleName = 'Taxi';
protected string $moduleNameLower = 'taxi';
/**
* Boot the application events.
*/
public function boot(): void
{
$this->registerCommands();
$this->registerCommandSchedules();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
}
/**
* Register the service provider.
*/
public function register(): void
{
$this->app->register(RouteServiceProvider::class);
}
/**
* Register commands in the format of Command::class
*/
protected function registerCommands(): void
{
// $this->commands([]);
}
/**
* Register command Schedules.
*/
protected function registerCommandSchedules(): void
{
// $this->app->booted(function () {
// $schedule = $this->app->make(Schedule::class);
// $schedule->command('inspire')->hourly();
// });
}
/**
* Register translations.
*/
public function registerTranslations(): void
{
$langPath = resource_path('lang/modules/'.$this->moduleNameLower);
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
$this->loadJsonTranslationsFrom($langPath);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
}
}
/**
* Register config.
*/
protected function registerConfig(): void
{
$this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
$this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
}
/**
* Register views.
*/
public function registerViews(): void
{
$viewPath = resource_path('views/modules/'.$this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'resources/views');
$this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
$componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
private function getPublishableViewPaths(): array
{
$paths = [];
foreach (config('view.paths') as $path) {
if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
$paths[] = $path.'/modules/'.$this->moduleNameLower;
}
}
return $paths;
}
}
@@ -0,0 +1,27 @@
<?php
namespace Modules\Taxi\App\Services;
use Modules\Taxi\App\Mail\BaseTaxiMail;
use Modules\Taxi\App\Jobs\TaxiMailJob;
/**
* Сервис отправки письма. Отвечает за формирование всех писем приложения Такси
*/
class TaxiMailerService
{
/**
* Единый метод отправки всех писем по такси
*
* @param BaseTaxiMail $taxiMailClass
* @return void
*/
public function sendMail(BaseTaxiMail $taxiMailClass)
{
#Гаврилов
//delay наверное не нужен?
//Вызов джобы для отправки письма
TaxiMailJob::dispatch($taxiMailClass->prepareDataForMail())->onQueue('emails')->delay(5);
}
}
@@ -0,0 +1,42 @@
<?php
namespace Modules\Taxi\App\Services;
use Modules\Taxi\App\Models as TaxiModels;
use Modules\Taxi\App\DTO\TaxiOrderDTO;
/**
* Сервис работы с заказами такси
*/
class TaxiOrderService
{
public function __construct()
{
}
/**
* Метод создания заказа на такси
*
* @param TaxiOrderDTO $orderData объект с данными по заказу
* @return int идентификатор созданного заказа
*/
public function createOrder(TaxiOrderDTO $orderData): string
{
//ГАВРИЛОВ. проверь будет ли возвращаться ошибка без try catch
// try {
$newOrder = TaxiModels\TaxiMain::create(
[
'emp_login' => $orderData->emp_login,
'taxi_date' => $orderData->taxi_date,
'emp_phone' => $orderData->emp_phone,
'taxi_time' => $orderData->taxi_time,
'taxi_address_from' => $orderData->taxi_address_from,
'taxi_address_to' => $orderData->taxi_address_to
]
);
return $newOrder->id;
// } catch (\Exception $e) {
// return $e->getMessage();
// }
}
}