From 92c9a92e611c809ec2fa8b178dfa15b8b3d60596 Mon Sep 17 00:00:00 2001 From: fomichev Date: Wed, 6 May 2026 11:16:35 +0300 Subject: [PATCH] review fixes 2 --- erp24/controllers/AutoMarkController.php | 34 +++++++++++-------- erp24/jobs/AutoMarkPredictionJob.php | 5 ++- ...6_120000_add_automark_rbac_permissions.php | 18 ++++++---- erp24/services/AutoMarkService.php | 5 --- .../automark/CoordinatorLlmTransport.php | 4 +-- erp24/services/automark/LlmVerifier.php | 13 ++++--- .../automark/OpenAiCompatibleLlmTransport.php | 2 +- erp24/services/automark/RuleBasedParser.php | 23 ++++++++++--- erp24/views/auto-mark/index.php | 2 +- erp24/views/auto-mark/review.php | 2 ++ 10 files changed, 67 insertions(+), 41 deletions(-) diff --git a/erp24/controllers/AutoMarkController.php b/erp24/controllers/AutoMarkController.php index 4fbc6e08..41a4cee6 100644 --- a/erp24/controllers/AutoMarkController.php +++ b/erp24/controllers/AutoMarkController.php @@ -74,26 +74,30 @@ class AutoMarkController extends Controller $prediction = $this->findPrediction($id); if (Yii::$app->request->isPost) { + if (!$prediction->isPending()) { + Yii::$app->session->setFlash('error', 'Предсказание уже обработано.'); + return $this->redirect(['index']); + } + $action = Yii::$app->request->post('action'); $service = new AutoMarkService(); if ($action === 'approve') { - $transaction = \Yii::$app->db->beginTransaction(); - try { - $prediction->status = Products1cAutomarkPrediction::STATUS_APPROVED; - $prediction->approved_by = Yii::$app->user->id; - $prediction->updated_at = date('Y-m-d H:i:s'); - if (!$prediction->save()) { - $transaction->rollBack(); - Yii::$app->session->setFlash('error', 'Не удалось сохранить предсказание.'); - } else { - $service->applyApprovedPrediction($prediction->id); - $transaction->commit(); - Yii::$app->session->setFlash('success', 'Разметка применена.'); + $prediction->status = Products1cAutomarkPrediction::STATUS_APPROVED; + $prediction->approved_by = Yii::$app->user->id; + $prediction->updated_at = date('Y-m-d H:i:s'); + if (!$prediction->save()) { + Yii::$app->session->setFlash('error', 'Не удалось сохранить предсказание.'); + } else { + try { + if ($service->applyApprovedPrediction($prediction->id)) { + Yii::$app->session->setFlash('success', 'Разметка применена.'); + } else { + Yii::$app->session->setFlash('error', 'Не удалось применить разметку в номенклатуру.'); + } + } catch (\Exception) { + Yii::$app->session->setFlash('error', 'Ошибка при применении разметки.'); } - } catch (\Exception) { - $transaction->rollBack(); - Yii::$app->session->setFlash('error', 'Ошибка при применении разметки.'); } } elseif ($action === 'reject') { $prediction->status = Products1cAutomarkPrediction::STATUS_REJECTED; diff --git a/erp24/jobs/AutoMarkPredictionJob.php b/erp24/jobs/AutoMarkPredictionJob.php index 4bbd1741..b9bcdb2c 100644 --- a/erp24/jobs/AutoMarkPredictionJob.php +++ b/erp24/jobs/AutoMarkPredictionJob.php @@ -13,6 +13,9 @@ class AutoMarkPredictionJob extends \yii\base\BaseObject implements JobInterface public function execute($queue): void { - (new AutoMarkService())->predictForProduct($this->productId); + $result = (new AutoMarkService())->predictForProduct($this->productId); + if ($result === null) { + \Yii::error("AutoMarkPredictionJob: не удалось создать предсказание для productId={$this->productId}", 'automark'); + } } } diff --git a/erp24/migrations/m260506_120000_add_automark_rbac_permissions.php b/erp24/migrations/m260506_120000_add_automark_rbac_permissions.php index 554b30d0..add9e22b 100644 --- a/erp24/migrations/m260506_120000_add_automark_rbac_permissions.php +++ b/erp24/migrations/m260506_120000_add_automark_rbac_permissions.php @@ -69,7 +69,7 @@ class m260506_120000_add_automark_rbac_permissions extends Migration ->one(); if ($row !== false && $row !== null) { - $existing = array_filter(explode(',', (string) $row['config'])); + $existing = array_filter(array_map('trim', explode(',', (string) $row['config']))); if (in_array(self::PERMISSION, $existing, true)) { echo " Группа {$groupId}: разрешение уже есть, пропускаем\n"; continue; @@ -104,15 +104,19 @@ class m260506_120000_add_automark_rbac_permissions extends Migration } $filtered = array_values(array_diff( - array_filter(explode(',', (string) $row['config'])), + array_filter(array_map('trim', explode(',', (string) $row['config']))), [self::PERMISSION] )); - $this->update( - 'admin_group_rbac_config', - ['config' => implode(',', $filtered)], - ['admin_group_id' => $groupId] - ); + if ($filtered === []) { + $this->delete('admin_group_rbac_config', ['admin_group_id' => $groupId]); + } else { + $this->update( + 'admin_group_rbac_config', + ['config' => implode(',', $filtered)], + ['admin_group_id' => $groupId] + ); + } } } diff --git a/erp24/services/AutoMarkService.php b/erp24/services/AutoMarkService.php index 2c1ee417..1f72cf3b 100644 --- a/erp24/services/AutoMarkService.php +++ b/erp24/services/AutoMarkService.php @@ -106,11 +106,6 @@ class AutoMarkService $nomenclature->size = $prediction->size ?? $nomenclature->size; $nomenclature->color = $prediction->color ?? $nomenclature->color; - $nomenclature->classification_status = 'classified'; - $nomenclature->confidence = (int) round($prediction->confidence * 100); - $nomenclature->classified_by = $prediction->approved_by; - $nomenclature->classified_at = date('Y-m-d H:i:s'); - // save(false): location/type_num могут быть '', required-валидатор для форм здесь неприменим if (!$nomenclature->save(false)) { $transaction->rollBack(); diff --git a/erp24/services/automark/CoordinatorLlmTransport.php b/erp24/services/automark/CoordinatorLlmTransport.php index 98b9df5c..0f59ffc8 100644 --- a/erp24/services/automark/CoordinatorLlmTransport.php +++ b/erp24/services/automark/CoordinatorLlmTransport.php @@ -42,7 +42,7 @@ final class CoordinatorLlmTransport implements LlmTransportInterface 'json' => $payload, ]); } catch (GuzzleException $e) { - throw new \RuntimeException('LLM HTTP ошибка: ' . $e->getMessage(), 0, $e); + throw new \RuntimeException('LLM HTTP ошибка: ' . $e->getCode()); } return self::parseAnalyzeResponse((string) $response->getBody()); @@ -68,7 +68,7 @@ final class CoordinatorLlmTransport implements LlmTransportInterface throw new \RuntimeException('Coordinator вернул невалидный JSON'); } - if (($decoded['ok'] ?? true) === false) { + if (array_key_exists('ok', $decoded) && !$decoded['ok']) { $message = $decoded['error'] ?? $decoded['detail']['title'] ?? 'Remote coordinator error'; throw new \RuntimeException((string) $message); } diff --git a/erp24/services/automark/LlmVerifier.php b/erp24/services/automark/LlmVerifier.php index b84bf5aa..71cd69b7 100644 --- a/erp24/services/automark/LlmVerifier.php +++ b/erp24/services/automark/LlmVerifier.php @@ -154,13 +154,13 @@ TAXONOMY; foreach ($predictions as $prediction) { $items[] = [ 'id' => $prediction->id, - 'name' => $prediction->product->name ?? '', + 'name' => $prediction->product?->name ?? '', 'category' => $prediction->category, 'subcategory' => $prediction->subcategory, 'species' => $prediction->species, 'size' => $prediction->size, 'color' => $prediction->color, - 'examples' => $this->getSimilarExamples($prediction->product->name ?? ''), + 'examples' => $this->getSimilarExamples($prediction->product?->name ?? ''), ]; } return $items; @@ -312,9 +312,14 @@ TAXONOMY; $corrections['species'] = $result->species; $corrections['size'] = $result->size; $corrections['color'] = $result->color; + } elseif (!$result->isApproved() && !$result->hasCorrections()) { + \Yii::warning( + "LLM вернул verdict=rejected без corrected_prediction для id={$result->predictionId}", + 'automark' + ); } - $name = (string) ($prediction->product->name ?? ''); + $name = (string) ($prediction->product?->name ?? ''); $explicitSize = $this->extractExplicitSize($name); if ($explicitSize !== null && (float) ($prediction->size ?? 0) !== $explicitSize && !array_key_exists('size', $corrections)) { $corrections['size'] = $explicitSize; @@ -385,7 +390,7 @@ PROMPT; 'similar_examples' => $item['examples'], ]; } - return json_encode($payload, JSON_UNESCAPED_UNICODE); + return json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE); } public function buildEffectiveSystemPrompt(): string diff --git a/erp24/services/automark/OpenAiCompatibleLlmTransport.php b/erp24/services/automark/OpenAiCompatibleLlmTransport.php index 12b84166..6e105749 100644 --- a/erp24/services/automark/OpenAiCompatibleLlmTransport.php +++ b/erp24/services/automark/OpenAiCompatibleLlmTransport.php @@ -40,7 +40,7 @@ final class OpenAiCompatibleLlmTransport implements LlmTransportInterface ], ]); } catch (GuzzleException $e) { - throw new \RuntimeException('LLM HTTP ошибка: ' . $e->getMessage(), 0, $e); + throw new \RuntimeException('LLM HTTP ошибка: ' . $e->getCode()); } return self::parseChatCompletionResponse((string) $response->getBody()); diff --git a/erp24/services/automark/RuleBasedParser.php b/erp24/services/automark/RuleBasedParser.php index c73d89cd..e71b82ed 100644 --- a/erp24/services/automark/RuleBasedParser.php +++ b/erp24/services/automark/RuleBasedParser.php @@ -81,6 +81,9 @@ class RuleBasedParser if ($species !== null && $category === null) { $category = 'Срезка'; + } elseif ($species !== null && $category === 'Горшечные_растения') { + // Вид из SPECIES_SREZY + горшечный маркер — противоречие; срезка приоритетнее + $category = 'Срезка'; } $subcategory = $species !== null ? (self::SPECIES_SUBCATEGORY[$species] ?? null) : null; @@ -185,11 +188,21 @@ class RuleBasedParser ?float $size ): float { $score = 0.5; - if ($category !== null) $score += 0.2; - if ($species !== null) $score += 0.15; - if ($color !== null) $score += 0.1; - if ($size !== null) $score += 0.05; - if ($sort !== null) $score += 0.05; + if ($category !== null) { + $score += 0.2; + } + if ($species !== null) { + $score += 0.15; + } + if ($color !== null) { + $score += 0.1; + } + if ($size !== null) { + $score += 0.05; + } + if ($sort !== null) { + $score += 0.05; + } return min(1.0, $score); } } diff --git a/erp24/views/auto-mark/index.php b/erp24/views/auto-mark/index.php index f52d2d46..39ab2437 100644 --- a/erp24/views/auto-mark/index.php +++ b/erp24/views/auto-mark/index.php @@ -87,7 +87,7 @@ $formatSize = static function ($value): string { ]); } - [$color, $label] = $badgeMap[$m->llm_verdict] ?? ['#374151', $m->llm_verdict]; + [$color, $label] = $badgeMap[$m->llm_verdict] ?? ['#374151', Html::encode($m->llm_verdict)]; return Html::tag('span', $label, [ 'style' => sprintf('color:%s;font-weight:700;', $color), ]); diff --git a/erp24/views/auto-mark/review.php b/erp24/views/auto-mark/review.php index c4486de7..1b22454f 100644 --- a/erp24/views/auto-mark/review.php +++ b/erp24/views/auto-mark/review.php @@ -43,11 +43,13 @@ if ($prediction->size !== null && (float) $prediction->size > 0.0) { isPending()): ?>
$prediction->id], 'post') ?> + request->csrfParam, Yii::$app->request->csrfToken) ?> 'btn btn-success']) ?> $prediction->id], 'post') ?> + request->csrfParam, Yii::$app->request->csrfToken) ?> 'btn btn-danger']) ?> -- 2.39.5