Sfoglia il codice sorgente

缓存载荷去对象化,适配 v13 的 serializable_classes

Laravel 13 的 config/cache.php 新增 serializable_classes 且默认为 false,
读缓存时走 unserialize(..., ['allowed_classes' => false]),任何 PHP 对象
都会变成 __PHP_Incomplete_Class。写入端不受影响,且 Cache::remember 首次
调用返回闭包原值,所以只有第二次及以后的请求才会出问题。

- RelationResource: json_decode 加 true,日期改 toISOString()。
  实测缓存往返后有 870 个字段损坏,但 json_encode 不报错,接口返回
  200 + 结构损坏的 JSON,日志里一条都查不到。
- RelationController: buildVocabularyPayload 改用 resolve()。
- TemplateRender: render_quote 的 paliPath 同样是 json_decode 出来的
  stdClass 数组。
- AiTranslateService: makeByTask 的返回值里嵌了 Task 与 AiModel 模型,
  会被 ProcessAITranslateJob::publish() 写入缓存,改为先摊平成数组。

三处的 JSON 输出与升级前逐字节一致,前端无需配合改动:
Relation 的 from/to/match 共 430 个非空对象、0 个空对象,PaliText.path
抽样 300 行全为非空数组,故 json_decode(..., true) 不会把 {} 变成 [];
Carbon 的 jsonSerialize() 本就等于 toISOString()。

(string) 转型顺带修掉 PHP 8.5 的 json_decode(null) deprecation。

未跑 pint:这 4 个文件在 HEAD 上就不合规,格式化会产生 900 行无关改动,
留到单独的提交处理。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
visuddhinanda 3 giorni fa
parent
commit
8ebbe02d4b

+ 1 - 1
api-v13/app/Http/Api/TemplateRender.php

@@ -757,7 +757,7 @@ class TemplateRender
 
                 if ($PaliText) {
                     $output["pali"] = $PaliText->toc;
-                    $output["paliPath"] = \json_decode($PaliText->path);
+                    $output["paliPath"] = \json_decode((string) $PaliText->path, true);
                     $output["innerString"] = $PaliText->toc;
                 }
                 return $output;

+ 4 - 3
api-v13/app/Http/Controllers/RelationController.php

@@ -238,9 +238,10 @@ class RelationController extends Controller
     /**
      * Build the full vocabulary payload for caching.
      *
-     * ResourceCollection is resolved to a plain PHP array via toArray() before
+     * ResourceCollection is resolved to a plain PHP array via resolve() before
      * being stored, preventing __PHP_Incomplete_Class_Name deserialization
-     * errors when using Redis or file-based cache drivers.
+     * errors when using Redis or file-based cache drivers. RelationResource is
+     * responsible for keeping the nested values object-free.
      *
      * @return array{rows: array<int, array<string, mixed>>, count: int}
      */
@@ -260,7 +261,7 @@ class RelationController extends Controller
         ])->orderBy('updated_at', 'desc')->get();
 
         return [
-            'rows'  => RelationResource::collection($rows)->toArray(request()),
+            'rows'  => RelationResource::collection($rows)->resolve(),
             'count' => $rows->count(),
         ];
     }

+ 10 - 5
api-v13/app/Http/Resources/RelationResource.php

@@ -12,6 +12,11 @@ class RelationResource extends JsonResource
     /**
      * Transform the resource into an array.
      *
+     * The payload must contain only scalars and arrays: it is cached by
+     * RelationController, and Laravel 13 refuses to unserialize objects from
+     * cache unless they are listed in cache.serializable_classes. Hence the
+     * associative json_decode() and the ISO-8601 date strings.
+     *
      * @param  \Illuminate\Http\Request  $request
      * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
      */
@@ -21,12 +26,12 @@ class RelationResource extends JsonResource
             "id" => $this->id,
             "name" => $this->name,
             "case" => $this->case,
-            "from" => json_decode($this->from),
-            "to" => json_decode($this->to),
-            "match" => json_decode($this->match),
+            "from" => json_decode((string) $this->from, true),
+            "to" => json_decode((string) $this->to, true),
+            "match" => json_decode((string) $this->match, true),
             "category" => $this->category,
-            "created_at" => $this->created_at,
-            "updated_at" => $this->updated_at,
+            "created_at" => $this->created_at?->toISOString(),
+            "updated_at" => $this->updated_at?->toISOString(),
         ];
 
 

+ 12 - 4
api-v13/app/Services/AiTranslateService.php

@@ -595,6 +595,14 @@ class AiTranslateService
         $aiModel = AiModel::findOrFail($aiAssistantId);
         $modelToken = AuthService::getUserToken($aiModel->uid);
         $aiModel['token'] = $modelToken;
+
+        /**
+         * 返回值会被 ProcessAITranslateJob::publish() 写入缓存。Laravel 13 默认
+         * 拒绝从缓存反序列化对象(cache.serializable_classes = false),所以这里
+         * 先把模型摊平成数组。JSON 输出与直接编码模型完全一致。
+         */
+        $aiModelData = $aiModel->toArray();
+        $taskData = $task->toArray();
         $sumLen = 0;
         $mqData = [];
         foreach ($sentences as $key => $sentence) {
@@ -633,9 +641,9 @@ class AiTranslateService
             $prompt = $mdRender->convert($content, []);
             //gen mq
             $aiMqData = [
-                'model' => $aiModel,
+                'model' => $aiModelData,
                 'task' => [
-                    'info' => $task,
+                    'info' => $taskData,
                     'progress' => [
                         'current' => $sumLen,
                         'total' => $totalLen
@@ -657,8 +665,8 @@ class AiTranslateService
         }
 
         $output = [
-            'model' => $aiModel->toArray(),
-            'task' => $task,
+            'model' => $aiModelData,
+            'task' => $taskData,
         ];
         $us = ['openai.com', 'googleapis.com', 'x.ai', 'anthropic.com'];
         $found = array_filter($us, function ($value) use ($output) {