/
chieforik
/
AgroPro
Обзор
Документация
Войти
/
chieforik
/
AgroPro
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
controllers/GigaChatController.php
649 строк
22 KB
Орловский Александр
Первый
25 сен 2025, 02:26
25 сен 2025, 02:26
1567008
Код
Авторство
О чём код?
<?php namespace app\controllers; use app\models\GigaChatForm; use app\models\GigaChatMessage; use Yii; use yii\base\InvalidConfigException; use yii\web\Controller; use yii\web\Cookie; use yii\web\Response; use app\models\Report; use app\models\Field; use app\models\FieldCalculation; class GigaChatController extends Controller { private $sessionId; public function init() { parent::init(); $this->getOrCreateSessionId(); } private function getOrCreateSessionId() { $cookies = Yii::$app->request->cookies; $this->sessionId = $cookies->getValue('chat_session_id'); if (!$this->sessionId) { $this->sessionId = GigaChatMessage::generateSessionId(); Yii::$app->response->cookies->add(new Cookie([ 'name' => 'chat_session_id', 'value' => $this->sessionId, 'expire' => time() + 86400 * 30, ])); } } public function actionIndex() { $model = new GigaChatForm(); $history = GigaChatMessage::getChatHistory($this->sessionId); $sessions = GigaChatMessage::getUserSessions(); return $this->render('index', [ 'model' => $model, 'history' => $history, 'sessions' => $sessions, 'sessionId' => $this->sessionId, ]); } public function actionSendMessage() { Yii::$app->response->format = Response::FORMAT_JSON; $message = Yii::$app->request->post('message'); Yii::info('Полученное сообщение: ' . $message, 'gigachat'); if (empty($message)) { return [ 'success' => false, 'error' => 'Сообщение не может быть пустым' ]; } try { $response = Yii::$app->gigaChat->sendAndSaveMessage($message, 'GigaChat', $this->sessionId); Yii::info('Ответ GigaChat сохранен для сеанса: ' . $this->sessionId, 'gigachat'); return [ 'success' => true, 'response' => $response, 'sessionId' => $this->sessionId, ]; } catch (\Exception $e) { Yii::error('GigaChat error: ' . $e->getMessage()); return [ 'success' => false, 'error' => 'Произошла ошибка при обработке запроса: ' . $e->getMessage() ]; } } /** * @throws InvalidConfigException */ public function actionGetHistory(): array { Yii::$app->response->format = Response::FORMAT_JSON; $sessionId = Yii::$app->request->post('session_id', $this->sessionId); $history = GigaChatMessage::getChatHistory($sessionId); $formattedHistory = []; foreach ($history as $message) { $formattedHistory[] = [ 'user_message' => $message->user_message, 'ai_response' => $message->ai_response, 'created_at' => Yii::$app->formatter->asDatetime($message->created_at), 'is_error' => str_starts_with($message->ai_response, 'Error:'), ]; } return [ 'success' => true, 'history' => $formattedHistory, 'sessionId' => $sessionId ]; } public function actionNewSession() { $this->sessionId = GigaChatMessage::generateSessionId(); Yii::$app->response->cookies->add(new Cookie([ 'name' => 'chat_session_id', 'value' => $this->sessionId, 'expire' => time() + 86400 * 30, ])); if (Yii::$app->request->isAjax) { Yii::$app->response->format = Response::FORMAT_JSON; return ['success' => true, 'sessionId' => $this->sessionId]; } return $this->redirect(['index']); } public function actionCalc() { $fields = \app\models\Field::find()->all(); $selectedField = null; $fieldId = Yii::$app->request->get('field_id'); if ($fieldId) { $selectedField = \app\models\Field::findOne($fieldId); } return $this->render('calc', [ 'fields' => $fields, 'selectedField' => $selectedField, 'sessionId' => $this->sessionId, ]); } public function actionReports() { $history = GigaChatMessage::getChatHistory($this->sessionId, 50); return $this->render('reports', [ 'sessionId' => $this->sessionId, 'history' => $history, ]); } public function actionDownloadReport($id) { $message = \app\models\GigaChatMessage::findOne($id); if (!$message) { throw new \yii\web\NotFoundHttpException('Отчет не найден'); } $filename = 'report_' . $message->id . '.csv'; $content = "Дата создания,Ответ AI\n"; $content .= Yii::$app->formatter->asDatetime($message->created_at) . ','; $content .= '"' . str_replace('"', '""', $message->ai_response) . '"' . "\n"; Yii::$app->response->format = \yii\web\Response::FORMAT_RAW; Yii::$app->response->headers->add('Content-Type', 'application/vnd.ms-excel; charset=UTF-8'); Yii::$app->response->headers->add('Content-Disposition', "attachment; filename={$filename}"); echo "\xEF\xBB\xBF"; return $content; } public function actionGetCultures($district) { Yii::$app->response->format = Response::FORMAT_JSON; $cultures = \app\models\FertilizerNorms::getCulturesForDistrict($district); return $cultures; } public function actionGetNorms($district, $culture) { Yii::$app->response->format = Response::FORMAT_JSON; $norms = \app\models\FertilizerNorms::getNorms($district, $culture); if ($norms) { $costCalculation = \app\models\FertilizerNorms::calculateFertilizerCost($district, $culture, 1); return [ 'success' => true, 'norms' => $norms->getNormsArray(), 'formatted' => $norms->getFormattedNorms(), 'basis' => $norms->recommendation_basis, 'cost_per_ha' => $costCalculation ? $costCalculation['total_cost_per_ha'] : 0 ]; } return ['success' => false, 'message' => 'Нормы не найдены']; } public function actionField() { $model = new \app\models\Field(); $model->user_id = 1; // НЕОБХОДИМО ЗАМЕНИТЬ ПРИ РЕАЛИЗАЦИИ АВТОРИЗАЦИИ if ($model->load(Yii::$app->request->post())) { if ($model->save()) { Yii::$app->session->setFlash('success', 'Поле "'.$model->name.'" успешно сохранено!'); return $this->redirect(['giga-chat/calc']); } else { // Показываем ошибки валидации Yii::$app->session->setFlash('error', 'Ошибка при сохранении поля. Проверьте данные.'); // Выводим ошибки в лог для отладки Yii::error('Ошибки сохранения поля: ' . print_r($model->errors, true)); } } return $this->render('field', [ 'model' => $model, ]); } public function actionCreateField() { $model = new \app\models\Field(); $model->user_id = 1; // НЕОБХОДИМО ЗАМЕНИТЬ ПРИ РЕАЛИЗАЦИИ АВТОРИЗАЦИИ if ($model->load(Yii::$app->request->post()) && $model->save()) { Yii::$app->session->setFlash('success', 'Поле успешно сохранено!'); return $this->redirect(['giga-chat/calc']); } return $this->render('field', [ 'model' => $model, 'sessionId' => $this->sessionId, ]); } public function actionFields() { $fields = \app\models\Field::find()->all(); return $this->render('fields', [ 'fields' => $fields, 'sessionId' => $this->sessionId, ]); } public function actionFieldView($id) { $field = \app\models\Field::findOne($id); if (!$field) { throw new \yii\web\NotFoundHttpException('Поле не найдено'); } return $this->render('field-view', [ 'field' => $field, 'sessionId' => $this->sessionId, ]); } public function actionFieldsData() { Yii::$app->response->format = Response::FORMAT_JSON; try { $fields = \app\models\Field::find() ->where(['user_id' => 1]) // НЕОБХОДИМО ЗАМЕНИТЬ ПРИ РЕАЛИЗАЦИИ АВТОРИЗАЦИИ ->all(); $fieldsData = []; foreach ($fields as $field) { $fieldsData[] = $field->getDashboardData(); } $totalFields = count($fields); $completedFields = array_filter($fieldsData, function($field) { return $field['completionRate'] >= 80; }); $totalCompletion = $totalFields > 0 ? array_sum(array_column($fieldsData, 'completionRate')) / $totalFields : 0; $lastIncome = $this->getLastIncomeCalculation(); return [ 'success' => true, 'fields' => $fieldsData, 'stats' => [ 'totalFields' => $totalFields, 'completedFields' => count($completedFields), 'totalCompletion' => round($totalCompletion), 'lastIncome' => $lastIncome, ] ]; } catch (\Exception $e) { Yii::error('Ошибка загрузки данных полей: ' . $e->getMessage()); return [ 'success' => false, 'error' => 'Ошибка загрузки данных' ]; } } private function getLastIncomeCalculation() { $lastMessages = \app\models\GigaChatMessage::find() ->where(['session_id' => $this->getSessionId()]) ->orderBy(['created_at' => SORT_DESC]) ->limit(10) ->all(); foreach ($lastMessages as $message) { // Ищем упоминание дохода в ответе ИИ if (preg_match('/(\d+[\s\d]*)\s*р(уб)?/iu', $message->ai_response, $matches)) { return [ 'amount' => preg_replace('/\s+/', '', $matches[1]), 'date' => Yii::$app->formatter->asDatetime($message->created_at), 'message' => substr(strip_tags($message->ai_response), 0, 100) . '...' ]; } } return null; } private function getSessionId() { $cookies = Yii::$app->request->cookies; return $cookies->getValue('chat_session_id'); } public function actionUpdateField($id) { $model = Field::findOne($id); if (!$model) { throw new \yii\web\NotFoundHttpException('Поле не найдено'); } if ($model->load(Yii::$app->request->post()) && $model->save()) { Yii::$app->session->setFlash('success', 'Поле "' . $model->name . '" успешно обновлено!'); return $this->redirect(['giga-chat/fields']); } return $this->render('field-form', [ 'model' => $model, 'sessionId' => $this->sessionId, ]); } public function actionQuickUpdateField($id) { Yii::$app->response->format = Response::FORMAT_JSON; $model = Field::findOne($id); if (!$model) { return ['success' => false, 'error' => 'Поле не найдено']; } if ($model->load(Yii::$app->request->post()) && $model->save()) { return [ 'success' => true, 'message' => 'Данные поля обновлены', 'completionRate' => $model->getCompletionRate(), 'statusText' => $model->getCompletionStatusText(), 'statusClass' => $model->getCompletionStatusClass(), ]; } return ['success' => false, 'error' => 'Ошибка сохранения']; } public function actionFieldDetail($id) { $model = Field::findOne($id); if (!$model) { throw new \yii\web\NotFoundHttpException('Поле не найдено'); } return $this->render('field-detail', [ 'model' => $model, 'sessionId' => $this->sessionId, ]); } public function actionDeleteField() { Yii::$app->response->format = Response::FORMAT_JSON; // Получаем ID из POST или GET запроса $id = Yii::$app->request->post('id') ?? Yii::$app->request->get('id'); if (!$id) { return ['success' => false, 'error' => 'ID поля не указан']; } try { $model = Field::findOne($id); if (!$model) { return ['success' => false, 'error' => 'Поле не найдено']; } if ($model->user_id != 1) { // НЕОБХОДИМО ЗАМЕНИТЬ ПРИ РЕАЛИЗАЦИИ АВТОРИЗАЦИИ return ['success' => false, 'error' => 'Нет прав для удаления этого поля']; } $fieldName = $model->name; if ($model->delete()) { Yii::info("Поле '{$fieldName}' (ID: {$id}) удалено", 'field'); return [ 'success' => true, 'message' => 'Поле "' . $fieldName . '" успешно удалено' ]; } else { Yii::error("Ошибка удаления поля ID: {$id}", 'field'); return ['success' => false, 'error' => 'Ошибка при удалении поля']; } } catch (\Exception $e) { Yii::error("Ошибка удаления поля ID: {$id}: " . $e->getMessage(), 'field'); return ['success' => false, 'error' => 'Системная ошибка при удалении: ' . $e->getMessage()]; } } public function actionArchiveField($id) { Yii::$app->response->format = Response::FORMAT_JSON; try { $model = Field::findOne($id); if (!$model) { return ['success' => false, 'error' => 'Поле не найдено']; } return ['success' => false, 'error' => 'Мягкое удаление не настроено']; } catch (\Exception $e) { Yii::error("Ошибка архивации поля ID: {$id}: " . $e->getMessage(), 'field'); return ['success' => false, 'error' => 'Системная ошибка']; } } public function actionSaveCalculation() { Yii::$app->response->format = Response::FORMAT_JSON; $fieldId = Yii::$app->request->post('field_id'); $bestCrop = Yii::$app->request->post('best_crop'); $profitPerHa = Yii::$app->request->post('profit_per_ha'); $totalProfit = Yii::$app->request->post('total_profit'); $calculationData = Yii::$app->request->post('calculation_data', []); $aiResponse = Yii::$app->request->post('ai_response', ''); try { $result = FieldCalculation::saveCalculation( $fieldId, $bestCrop, $profitPerHa, $totalProfit, $calculationData, $aiResponse ); if ($result) { return ['success' => true, 'message' => 'Расчет сохранен']; } else { return ['success' => false, 'error' => 'Ошибка сохранения']; } } catch (\Exception $e) { Yii::error('Ошибка сохранения расчета: ' . $e->getMessage()); return ['success' => false, 'error' => 'Системная ошибка']; } } public function actionGetCalculations($fieldId) { Yii::$app->response->format = Response::FORMAT_JSON; $calculations = FieldCalculation::find() ->where(['field_id' => $fieldId]) ->orderBy(['created_at' => SORT_DESC]) ->all(); $data = []; foreach ($calculations as $calculation) { $data[] = [ 'id' => $calculation->id, 'best_crop' => $calculation->best_crop, 'profit_per_ha' => Yii::$app->formatter->asDecimal($calculation->profit_per_ha), 'total_profit' => Yii::$app->formatter->asDecimal($calculation->total_profit), 'created_at' => Yii::$app->formatter->asDatetime($calculation->created_at), 'calculation_data' => $calculation->getCalculationDataArray() ]; } return ['success' => true, 'calculations' => $data]; } private function calculateIncomeTrend($calculations) { if (count($calculations) < 2) { return 0; } $recentCalculation = $calculations[0]; $previousCalculation = $calculations[1]; if ($previousCalculation->total_profit > 0) { $change = (($recentCalculation->total_profit - $previousCalculation->total_profit) / $previousCalculation->total_profit) * 100; return round($change, 1); } return 0; } public function actionIncomeData() { Yii::$app->response->format = Response::FORMAT_JSON; try { $tableExists = Yii::$app->db->getTableSchema('{{%field_calculations}}') !== null; if (!$tableExists) { return [ 'success' => true, 'monthlyIncome' => 0, 'calculationsCount' => 0, 'recommendedCrop' => null ]; } $calculations = \app\models\FieldCalculation::find() ->joinWith('field') ->where(['field.user_id' => 1]) // НЕОБХОДИМО ЗАМЕНИТЬ ПРИ РЕАЛИЗАЦИИ АВТОРИЗАЦИИ ->andWhere(['IS NOT', 'total_profit', null]) ->all(); $totalAnnualIncome = 0; $calculationsCount = count($calculations); $recommendedCrop = null; $maxProfitPerHa = 0; foreach ($calculations as $calculation) { $totalAnnualIncome += $calculation->total_profit; if ($calculation->profit_per_ha > $maxProfitPerHa) { $maxProfitPerHa = $calculation->profit_per_ha; $recommendedCrop = $calculation->best_crop; } } $monthlyIncome = $calculationsCount > 0 ? $totalAnnualIncome / 12 : 0; return [ 'success' => true, 'monthlyIncome' => round($monthlyIncome, 2), 'calculationsCount' => $calculationsCount, 'recommendedCrop' => $recommendedCrop ]; } catch (\Exception $e) { Yii::error('Ошибка загрузки данных о доходах: ' . $e->getMessage()); return [ 'success' => false, 'error' => 'Ошибка загрузки данных' ]; } } public function actionCropRating() { Yii::$app->response->format = Response::FORMAT_JSON; try { // Проверяем существование таблицы field_calculations $tableExists = Yii::$app->db->getTableSchema('{{%field_calculations}}') !== null; if (!$tableExists) { return [ 'success' => true, 'rating' => [] ]; } $calculations = \app\models\FieldCalculation::find() ->select([ 'best_crop', 'AVG(profit_per_ha) as avg_profit', 'COUNT(*) as calculation_count', 'MAX(profit_per_ha) as max_profit', 'MIN(profit_per_ha) as min_profit' ]) ->where(['IS NOT', 'profit_per_ha', null]) ->andWhere(['IS NOT', 'best_crop', null]) ->groupBy('best_crop') ->orderBy('avg_profit DESC') ->asArray() ->all(); $cropRating = []; foreach ($calculations as $calculation) { if ($calculation['avg_profit'] > 0) { // Рассчитываем рейтинг от 0 до 100 на основе прибыльности $maxProfitInSystem = 20000; // Максимальная ожидаемая прибыль для масштабирования $rating = min(100, round(($calculation['avg_profit'] / $maxProfitInSystem) * 100)); $cropRating[] = [ 'crop' => $calculation['best_crop'], 'profit_per_ha' => round($calculation['avg_profit'], 2), 'rating' => $rating, 'district' => 'Среднее по всем районам', 'calculations_count' => $calculation['calculation_count'], 'max_profit' => round($calculation['max_profit'], 2), 'min_profit' => round($calculation['min_profit'], 2) ]; } } if (empty($cropRating)) { return [ 'success' => true, 'rating' => [] ]; } return [ 'success' => true, 'rating' => $cropRating ]; } catch (\Exception $e) { Yii::error('Ошибка загрузки рейтинга культур: ' . $e->getMessage()); return [ 'success' => false, 'error' => 'Ошибка загрузки рейтинга' ]; } } public function actionMarket(){ return $this->render('market'); } }