Explorar o código

Merge pull request #2367 from visuddhinanda/development

Development
visuddhinanda hai 1 semana
pai
achega
ca826f4f70
Modificáronse 100 ficheiros con 1264 adicións e 601 borrados
  1. 57 0
      api-v13/app/Console/Commands/AiArticleTranslate.php
  2. 1 0
      api-v13/app/Console/Commands/CreateOpenSearchIndex.php
  3. 33 28
      api-v13/app/Console/Commands/ExportPaliSynonyms.php
  4. 3 2
      api-v13/app/Console/Commands/IndexTipitaka.php
  5. 3 1
      api-v13/app/Console/Commands/InitSystemChannel.php
  6. 31 29
      api-v13/app/Console/Commands/InitSystemDict.php
  7. 5 0
      api-v13/app/Console/Commands/PostInstall.php
  8. 245 0
      api-v13/app/Console/Commands/UpdateCorpus.php
  9. 2 2
      api-v13/app/Console/Commands/UpgradeAITerm.php
  10. 2 2
      api-v13/app/Console/Commands/UpgradeAITranslation.php
  11. 9 9
      api-v13/app/Console/Commands/UpgradeDict.php
  12. 16 5
      api-v13/app/Console/Commands/UpgradeProgress.php
  13. 2 2
      api-v13/app/DTO/LLMTranslation/TranslationResponseDTO.php
  14. 3 3
      api-v13/app/DTO/LLMTranslation/TranslationUsageDTO.php
  15. 2 2
      api-v13/app/Http/Api/AiTaskPrepare.php
  16. 1 38
      api-v13/app/Http/Api/AuthApi.php
  17. 10 2
      api-v13/app/Http/Api/TemplateRender.php
  18. 4 3
      api-v13/app/Http/Controllers/AccessTokenController.php
  19. 2 2
      api-v13/app/Http/Controllers/AiAssistantController.php
  20. 5 5
      api-v13/app/Http/Controllers/AiModelController.php
  21. 8 8
      api-v13/app/Http/Controllers/ArticleController.php
  22. 3 3
      api-v13/app/Http/Controllers/ArticleMapController.php
  23. 5 5
      api-v13/app/Http/Controllers/AttachmentController.php
  24. 5 25
      api-v13/app/Http/Controllers/AuthController.php
  25. 11 11
      api-v13/app/Http/Controllers/ChannelController.php
  26. 2 2
      api-v13/app/Http/Controllers/ChapterContentController.php
  27. 6 6
      api-v13/app/Http/Controllers/CollectionController.php
  28. 2 2
      api-v13/app/Http/Controllers/CommandController.php
  29. 5 5
      api-v13/app/Http/Controllers/CorpusController.php
  30. 8 8
      api-v13/app/Http/Controllers/CourseController.php
  31. 8 8
      api-v13/app/Http/Controllers/CourseMemberController.php
  32. 7 7
      api-v13/app/Http/Controllers/DhammaTermController.php
  33. 2 2
      api-v13/app/Http/Controllers/DictController.php
  34. 2 2
      api-v13/app/Http/Controllers/DictPreferenceController.php
  35. 6 6
      api-v13/app/Http/Controllers/DiscussionController.php
  36. 2 2
      api-v13/app/Http/Controllers/DiscussionCountController.php
  37. 2 2
      api-v13/app/Http/Controllers/ExportController.php
  38. 7 7
      api-v13/app/Http/Controllers/GroupController.php
  39. 3 3
      api-v13/app/Http/Controllers/GroupMemberController.php
  40. 2 2
      api-v13/app/Http/Controllers/InteractiveController.php
  41. 3 3
      api-v13/app/Http/Controllers/InviteController.php
  42. 22 77
      api-v13/app/Http/Controllers/Library/HomeController.php
  43. 4 4
      api-v13/app/Http/Controllers/LikeController.php
  44. 2 2
      api-v13/app/Http/Controllers/ModelLogController.php
  45. 5 5
      api-v13/app/Http/Controllers/NissayaEndingController.php
  46. 4 4
      api-v13/app/Http/Controllers/NotificationController.php
  47. 4 4
      api-v13/app/Http/Controllers/ProjectController.php
  48. 2 2
      api-v13/app/Http/Controllers/ProjectTreeController.php
  49. 2 2
      api-v13/app/Http/Controllers/RecentController.php
  50. 5 5
      api-v13/app/Http/Controllers/RelationController.php
  51. 7 7
      api-v13/app/Http/Controllers/SentPrController.php
  52. 7 7
      api-v13/app/Http/Controllers/SentenceController.php
  53. 4 4
      api-v13/app/Http/Controllers/ShareController.php
  54. 1 1
      api-v13/app/Http/Controllers/StudioController.php
  55. 3 3
      api-v13/app/Http/Controllers/SysModelController.php
  56. 3 3
      api-v13/app/Http/Controllers/TagController.php
  57. 3 3
      api-v13/app/Http/Controllers/TagMapController.php
  58. 5 5
      api-v13/app/Http/Controllers/TaskController.php
  59. 2 2
      api-v13/app/Http/Controllers/TaskGroupController.php
  60. 2 2
      api-v13/app/Http/Controllers/TaskStatusController.php
  61. 3 3
      api-v13/app/Http/Controllers/TermExportController.php
  62. 4 4
      api-v13/app/Http/Controllers/TransferController.php
  63. 5 5
      api-v13/app/Http/Controllers/UserDictController.php
  64. 2 2
      api-v13/app/Http/Controllers/UserOperationDailyController.php
  65. 5 5
      api-v13/app/Http/Controllers/ViewController.php
  66. 2 2
      api-v13/app/Http/Controllers/WbwController.php
  67. 2 2
      api-v13/app/Http/Controllers/WbwLookupController.php
  68. 3 3
      api-v13/app/Http/Controllers/WbwSentenceController.php
  69. 4 4
      api-v13/app/Http/Controllers/WebHookController.php
  70. 1 1
      api-v13/app/Http/Middleware/SetLocale.php
  71. 2 2
      api-v13/app/Http/Middleware/UserOperation.php
  72. 2 2
      api-v13/app/Http/Requests/StoreDiscussionRequest.php
  73. 2 2
      api-v13/app/Http/Resources/ArticleResource.php
  74. 3 3
      api-v13/app/Http/Resources/CourseResource.php
  75. 9 9
      api-v13/app/Http/Resources/GroupResource.php
  76. 2 2
      api-v13/app/Http/Resources/SentPrResource.php
  77. 1 1
      api-v13/app/Http/Resources/SentSimResource.php
  78. 2 2
      api-v13/app/Http/Resources/TermResource.php
  79. 3 2
      api-v13/app/Services/AIAssistant/AITermService.php
  80. 48 7
      api-v13/app/Services/AIAssistant/ArticleTranslateService.php
  81. 3 3
      api-v13/app/Services/AIAssistant/TranslateService.php
  82. 8 6
      api-v13/app/Services/AiTranslateService.php
  83. 1 1
      api-v13/app/Services/ArticleService.php
  84. 69 0
      api-v13/app/Services/AuthService.php
  85. 2 2
      api-v13/app/Services/CollectionService.php
  86. 39 1
      api-v13/app/Services/SentenceService.php
  87. 62 0
      api-v13/app/Services/TermService.php
  88. 5 1
      api-v13/config/mint.php
  89. 28 0
      api-v13/database/migrations/2026_05_06_021102_add_index_to_user_dicts_parent_column.php
  90. 34 0
      api-v13/database/migrations/2026_05_09_091039_add_source_type_to_channels.php
  91. 33 0
      api-v13/database/migrations/2026_05_09_091529_add_source_id_to_channels.php
  92. 127 127
      api-v13/public/data/category/default.json
  93. 11 1
      api-v13/resources/lang/en/site.php
  94. 20 0
      api-v13/resources/lang/my/auth.php
  95. 5 0
      api-v13/resources/lang/my/buttons.php
  96. 7 0
      api-v13/resources/lang/my/labels.php
  97. 10 0
      api-v13/resources/lang/my/language.php
  98. 19 0
      api-v13/resources/lang/my/pagination.php
  99. 22 0
      api-v13/resources/lang/my/passwords.php
  100. 32 0
      api-v13/resources/lang/my/site.php

+ 57 - 0
api-v13/app/Console/Commands/AiArticleTranslate.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Attributes\Description;
+use Illuminate\Console\Attributes\Signature;
+use Illuminate\Console\Command;
+use App\Services\AIAssistant\ArticleTranslateService;
+use App\Services\ArticleService;
+
+
+#[Signature('app:ai-article-translate  {--article=} {--anthology=} {--model=}  {--channel=}  {--token=} {--endpoint=}')]
+#[Description('translate article by ai ')]
+class AiArticleTranslate extends Command
+{
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        if (
+            !$this->option('model') ||
+            !$this->option('channel')
+        ) {
+            $this->error('model,article,channel is requested');
+            return;
+        }
+        //
+        // ===== 创建 Service =====
+        $service = app(ArticleTranslateService::class);
+        $articleService = app(ArticleService::class);
+        // ===== 执行 =====
+        if ($this->option('article')) {
+            $this->info('article translate start');
+            $total = $service->setModel($this->option('model'))
+                ->setChannel($this->option('channel'))
+                ->translateArticle($this->option('article'))
+                ->saveRpc($this->option('endpoint'), $this->option('token'));
+            $this->info("{$total} sentences saved");
+        }
+        if ($this->option('anthology')) {
+            $this->info('anthology translate start');
+            $articleIds = $articleService->articlesInAnthology($this->option('anthology'));
+
+            foreach ($articleIds as $article) {
+                $this->info('article translate start');
+                $total = $service->setModel($this->option('model'))
+                    ->setChannel($this->option('channel'))
+                    ->translateArticle($article)
+                    ->saveRpc($this->option('endpoint'), $this->option('token'));
+                $this->info("{$total} sentences saved");
+            }
+
+            $this->info(count($articleIds) . " article saved");
+        }
+    }
+}

+ 1 - 0
api-v13/app/Console/Commands/CreateOpenSearchIndex.php

@@ -51,6 +51,7 @@ class CreateOpenSearchIndex extends Command
 
         // Attempt to create or update index
         try {
+            $this->info('create openSearch index. index name='.config('mint.opensearch.index'));
             $crate = $openSearch->createIndex();
             if ($crate['acknowledged']) {
                 $this->info('Index created successfully: ' . $crate['index']);

+ 33 - 28
api-v13/app/Console/Commands/ExportPaliSynonyms.php

@@ -44,45 +44,50 @@ class ExportPaliSynonyms extends Command
             $this->error('please set output file option --output=file');
             return 1;
         }
+        /*
         //irregular
         $dictId = ['4d3a0d92-0adc-4052-80f5-512a2603d0e8'];
         //regular
         $dictId[] = DictApi::getSysDict('system_regular');
-
+        $dictId[] = DictApi::getSysDict('robot_compound');
+*/
         $filename = $this->option('output');
         $fp = fopen($filename, 'w') or die("Unable to open file!");
-        foreach ($dictId as  $dict) {
-            $parents = UserDict::where('dict_id', $dict)
-                ->select('parent')
-                ->groupBy('parent')->cursor();
 
-            foreach ($parents as  $parent) {
-                $words = UserDict::where('dict_id', $dict)
-                    ->where('parent', $parent->parent)
-                    ->select('word')
-                    ->groupBy('word')->get();
-                $wordsList = [];
-                foreach ($words as $word) {
-                    $wordsList[$word->word] = 1;
-                }
-                $teams = DhammaTerm::where('word', $parent->parent)
-                    ->select(['meaning'])->get();
-                foreach ($teams as $term) {
-                    $wordsList[$term->meaning] = 1;
-                }
-                $this->info("[{$parent->parent}] " . count($words) . " team=" . count($teams));
-                // 合并 $parent->parent, $words->word, $team->meaning 为一个字符串数组
-                $combinedArray = [];
-                $combinedArray[] = $parent->parent;
-                foreach ($wordsList as $word => $value) {
-                    $combinedArray[] = $word;
-                }
+        $parents = UserDict::select('parent')
+            ->whereNotNull('parent')
+            ->where('parent', '<>', '')
+            ->groupBy('parent')->cursor();
 
-                // 将 $combinedArray 写入 CSV 文件
-                fputcsv($fp, $combinedArray);
+        foreach ($parents as  $parent) {
+            if (str_contains($parent->parent, ' ')) {
+                continue;
+            }
+            $words = UserDict::where('parent', $parent->parent)
+                ->select('word')
+                ->groupBy('word')->get();
+            $wordsList = [];
+            foreach ($words as $word) {
+                $wordsList[$word->word] = 1;
+            }
+            $teams = DhammaTerm::where('word', $parent->parent)
+                ->select(['meaning'])->get();
+            foreach ($teams as $term) {
+                $wordsList[$term->meaning] = 1;
             }
+            $this->info("[{$parent->parent}] " . count($words) . " team=" . count($teams));
+            // 合并 $parent->parent, $words->word, $team->meaning 为一个字符串数组
+            $combinedArray = [];
+            $combinedArray[] = $parent->parent;
+            foreach ($wordsList as $word => $value) {
+                $combinedArray[] = $word;
+            }
+
+            // 将 $combinedArray 写入 CSV 文件
+            fputcsv($fp, $combinedArray);
         }
 
+
         // 关闭文件
         fclose($fp);
         $this->info('done');

+ 3 - 2
api-v13/app/Console/Commands/IndexTipitaka.php

@@ -233,13 +233,14 @@ class IndexTipitaka extends Command
             'resource_id' => $paraInfo['uid'], // Use uid from getPaliData for resource_id
             'resource_type' => 'original_text',
             'title' => [
-                'pali' => "{$currChapter} paragraph {$paraInfo['paragraph']}"
+                ['text'=>['pali' => "{$currChapter} paragraph {$paraInfo['paragraph']}"]]
+                
             ],
             'summary' => [
                 'text' => $this->summary ? $this->summaryService->summarize($content['markdown']) : ''
             ],
             'content' => [
-                'pali' => implode("\n\n", $markdown),
+                ['text'=>['pali' => implode("\n\n", $markdown)]]
             ],
             'bold_single' => implode(" ", $bold_single),
             'bold_multi' => implode(" ", $bold_multi),

+ 3 - 1
api-v13/app/Console/Commands/InitSystemChannel.php

@@ -120,7 +120,7 @@ class InitSystemChannel extends Command
         if (\App\Tools\Tools::isStop()) {
             return 0;
         }
-        $this->info("start");
+        $this->info("init:system.channel start");
         foreach ($this->channels as $key => $value) {
             # code...
             $channel = Channel::firstOrNew([
@@ -143,6 +143,8 @@ class InitSystemChannel extends Command
             $channel->save();
             $this->info("created" . $value['name']);
         }
+        $this->info("init:system.channel successful");
+
         return 0;
     }
 }

+ 31 - 29
api-v13/app/Console/Commands/InitSystemDict.php

@@ -25,41 +25,41 @@ class InitSystemDict extends Command
     /**
      * name 不要修改。因为在程序其他地方,用name 查询词典id
      */
-    protected $dictionary =[
+    protected $dictionary = [
         [
-            "name"=>'robot_compound',
-            'shortname'=>'compound',
-            'description'=>'split compound by AI',
-            'src_lang'=>'pa',
-            'dest_lang'=>'cm',
+            "name" => 'robot_compound',
+            'shortname' => 'compound',
+            'description' => 'split compound by AI',
+            'src_lang' => 'pa',
+            'dest_lang' => 'cm',
         ],
         [
-            "name"=>'system_regular',
-            'shortname'=>'regular',
-            'description'=>'system regular',
-            'src_lang'=>'pa',
-            'dest_lang'=>'cm',
+            "name" => 'system_regular',
+            'shortname' => 'regular',
+            'description' => 'system regular',
+            'src_lang' => 'pa',
+            'dest_lang' => 'cm',
         ],
         [
-            "name"=>'community',
-            'shortname'=>'社区',
-            'description'=>'由用户贡献词条的社区字典',
-            'src_lang'=>'pa',
-            'dest_lang'=>'cm',
+            "name" => 'community',
+            'shortname' => '社区',
+            'description' => '由用户贡献词条的社区字典',
+            'src_lang' => 'pa',
+            'dest_lang' => 'cm',
         ],
         [
-            "name"=>'community_extract',
-            'shortname'=>'社区汇总',
-            'description'=>'由用户贡献词条的社区字典汇总统计',
-            'src_lang'=>'pa',
-            'dest_lang'=>'cm',
+            "name" => 'community_extract',
+            'shortname' => '社区汇总',
+            'description' => '由用户贡献词条的社区字典汇总统计',
+            'src_lang' => 'pa',
+            'dest_lang' => 'cm',
         ],
         [
-            "name"=>'system_preference',
-            'shortname'=>'系统单词首选项',
-            'description'=>'通过系统筛选出的首选项,只包含语法信息',
-            'src_lang'=>'pa',
-            'dest_lang'=>'cm',
+            "name" => 'system_preference',
+            'shortname' => '系统单词首选项',
+            'description' => '通过系统筛选出的首选项,只包含语法信息',
+            'src_lang' => 'pa',
+            'dest_lang' => 'cm',
         ],
     ];
 
@@ -80,10 +80,10 @@ class InitSystemDict extends Command
      */
     public function handle()
     {
-        if(\App\Tools\Tools::isStop()){
+        if (\App\Tools\Tools::isStop()) {
             return 0;
         }
-        $this->info("start");
+        $this->info("init:system.dict start");
         foreach ($this->dictionary as $key => $value) {
             # code...
             $channel = DictInfo::firstOrNew([
@@ -94,10 +94,12 @@ class InitSystemDict extends Command
             $channel->description = $value['description'];
             $channel->src_lang = $value['src_lang'];
             $channel->dest_lang = $value['dest_lang'];
-            $channel->meta = json_encode($value,JSON_UNESCAPED_UNICODE);
+            $channel->meta = json_encode($value, JSON_UNESCAPED_UNICODE);
             $channel->save();
             $this->info("updated {$value['name']}");
         }
+        $this->info("init:system.dict successful");
+
         return 0;
     }
 }

+ 5 - 0
api-v13/app/Console/Commands/PostInstall.php

@@ -27,7 +27,12 @@ class DeplyInit extends Command
     {
         //
         $this->info('deploy init start');
+
         $this->call('create:opensearch.index');
+        $this->call('init:system.channel');
+        $this->call('init:system.dict');
+        $this->call('upgrade:dict');
+
         $this->info('deploy init done');
     }
 }

+ 245 - 0
api-v13/app/Console/Commands/UpdateCorpus.php

@@ -0,0 +1,245 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Services\SentenceService;
+use Illuminate\Console\Attributes\Description;
+use Illuminate\Console\Attributes\Signature;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+use App\Models\Channel;
+
+#[Signature('app:update-corpus')]
+#[Description('Update corpus from JSONL files in corpus directory')]
+class UpdateCorpus extends Command
+{
+    /**
+     * The SentenceService instance.
+     *
+     * @var SentenceService
+     */
+    protected SentenceService $sentenceService;
+
+    /**
+     * Create a new command instance.
+     *
+     * @param SentenceService $sentenceService
+     */
+    public function __construct(SentenceService $sentenceService)
+    {
+        parent::__construct();
+        $this->sentenceService = $sentenceService;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle(): int
+    {
+        $this->info('Starting corpus update process...');
+
+        // Get the corpus base path from config
+        $corpusBasePath = config('mint.path.corpus');
+
+        if (!is_dir($corpusBasePath)) {
+            $this->error("Corpus directory not found: {$corpusBasePath}");
+            return self::FAILURE;
+        }
+
+        // Scan subdirectories of the corpus path
+        $subdirectories = $this->getSubdirectories($corpusBasePath);
+
+        if (empty($subdirectories)) {
+            $this->warn('No subdirectories found in corpus path.');
+            return self::SUCCESS;
+        }
+
+        $this->info("Found " . count($subdirectories) . " subdirectories to process.");
+
+        $totalProcessed = 0;
+        $totalErrors = 0;
+
+        foreach ($subdirectories as $subdir) {
+            $this->info("Processing directory: {$subdir}");
+
+            try {
+                $stats = $this->processCorpusDirectory($subdir);
+                $totalProcessed += $stats['processed'];
+                $totalErrors += $stats['errors'];
+                $this->info("Directory processed: {$stats['processed']} records saved, {$stats['errors']} errors");
+            } catch (\Exception $e) {
+                $this->error("Failed to process directory {$subdir}: {$e->getMessage()}");
+                $totalErrors++;
+            }
+        }
+
+        $this->info("Corpus update completed. Total processed: {$totalProcessed}, Total errors: {$totalErrors}");
+
+        return $totalErrors > 0 ? self::FAILURE : self::SUCCESS;
+    }
+
+    /**
+     * Get all subdirectories of a given directory.
+     *
+     * @param string $path
+     * @return array
+     */
+    protected function getSubdirectories(string $path): array
+    {
+        $directories = [];
+
+        $items = scandir($path);
+        foreach ($items as $item) {
+            if ($item === '.' || $item === '..') {
+                continue;
+            }
+
+            $fullPath = $path . DIRECTORY_SEPARATOR . $item;
+            if (is_dir($fullPath)) {
+                $directories[] = $fullPath;
+            }
+        }
+
+        return $directories;
+    }
+
+    /**
+     * Process a single corpus directory.
+     *
+     * @param string $directoryPath
+     * @return array
+     * @throws \Exception
+     */
+    protected function processCorpusDirectory(string $directoryPath): array
+    {
+        $stats = [
+            'processed' => 0,
+            'errors' => 0,
+        ];
+
+        // Read meta.json file
+        $metaFile = $directoryPath . DIRECTORY_SEPARATOR . 'meta.json';
+
+        if (!file_exists($metaFile)) {
+            $this->warn("meta.json not found in directory: {$directoryPath}");
+            return $stats;
+        }
+
+        $metaData = json_decode(file_get_contents($metaFile), true);
+
+        if (!isset($metaData['id'])) {
+            $this->error("Invalid meta.json: missing 'id' field in {$directoryPath}");
+            return $stats;
+        }
+
+        $sourceId = $metaData['id'];
+        $this->info("Processing {$directoryPath} source ID: {$sourceId}");
+
+        // Find all channel records with matching source_id
+        $channels = Channel::where('source_id', $sourceId)->get();
+
+        if ($channels->isEmpty()) {
+            $this->warn("No channels found with source_id: {$sourceId}");
+            return $stats;
+        }
+
+        $this->info("Found {$channels->count()} channel(s) for source ID: {$sourceId}");
+
+        // Scan subdirectories of the current directory for JSONL files
+        $childDirectories = $this->getSubdirectories($directoryPath);
+
+        foreach ($childDirectories as $childDir) {
+            $this->info("Scanning directory for JSONL files: {$childDir}");
+            $jsonlFiles = glob($childDir . DIRECTORY_SEPARATOR . '*.jsonl');
+
+            foreach ($jsonlFiles as $jsonlFile) {
+                $this->line("Processing file: {$jsonlFile}");
+                $fileStats = $this->processJsonlFile($jsonlFile, $channels);
+                $stats['processed'] += $fileStats['processed'];
+                $stats['errors'] += $fileStats['errors'];
+            }
+        }
+
+        return $stats;
+    }
+
+    /**
+     * Process a single JSONL file and save records for each channel.
+     *
+     * @param string $filePath
+     * @param \Illuminate\Database\Eloquent\Collection $channels
+     * @return array
+     */
+    protected function processJsonlFile(string $filePath, $channels): array
+    {
+        $stats = [
+            'processed' => 0,
+            'errors' => 0,
+        ];
+
+        $handle = fopen($filePath, 'r');
+
+        if (!$handle) {
+            $this->error("Failed to open file: {$filePath}");
+            return $stats;
+        }
+
+        $lineNumber = 0;
+        $robotUid = config('mint.admin.robot_uuid');
+
+        if (!$robotUid) {
+            $this->error('robot_uuid not configured in mint.admin.robot_uuid');
+            fclose($handle);
+            return $stats;
+        }
+
+        while (($line = fgets($handle)) !== false) {
+            $lineNumber++;
+            $line = trim($line);
+
+            if (empty($line)) {
+                continue;
+            }
+
+            // Parse JSON line
+            $data = json_decode($line, true);
+
+            if ($data === null) {
+                $this->error("Failed to parse JSON at line {$lineNumber} in file: {$filePath}");
+                $stats['errors']++;
+                continue;
+            }
+
+            // Save for each channel
+            foreach ($channels as $channel) {
+                try {
+                    $saveData = [
+                        'book_id' => $data['book'],
+                        'paragraph' => $data['paragraph'],
+                        'word_start' => $data['start'],
+                        'word_end' => $data['end'],
+                        'content' => $data['content'],
+                        'channel_uid' => $channel->uid,
+                        'editor_uid' => $robotUid,
+                    ];
+
+                    DB::transaction(function () use ($saveData) {
+                        $this->sentenceService->save($saveData);
+                    });
+
+                    $stats['processed']++;
+                    //$this->line("Saved record for channel: {$channel->uid}");
+                } catch (\Exception $e) {
+                    $this->error("Failed to save record for channel {$channel->uid} at line {$lineNumber}: {$e->getMessage()}");
+                    $stats['errors']++;
+                }
+            }
+        }
+
+        fclose($handle);
+        $this->line("$lineNumber lines write");
+        return $stats;
+    }
+}

+ 2 - 2
api-v13/app/Console/Commands/UpgradeAITerm.php

@@ -7,9 +7,9 @@ use Illuminate\Console\Command;
 use App\Services\AIModelService;
 use App\Services\TermService;
 use App\Services\AIAssistant\AITermService;
+use App\Services\AuthService;
 
 use App\Http\Resources\AiModelResource;
-use App\Http\Controllers\AuthController;
 
 
 class UpgradeAITerm extends Command
@@ -62,7 +62,7 @@ class UpgradeAITerm extends Command
         $this->aiTermService->setModel($modelId);
         $this->model = $this->modelService->getModelById($modelId);
         $this->info("model:{$this->model['model']}");
-        $this->modelToken = AuthController::getUserToken($modelId);
+        $this->modelToken = AuthService::getUserToken($modelId);
 
         if ($this->option('id')) {
             $terms = [['guid' => $this->option('id'), 'word' => 'word']];

+ 2 - 2
api-v13/app/Console/Commands/UpgradeAITranslation.php

@@ -10,9 +10,9 @@ use App\Services\AIModelService;
 use App\Services\SentenceService;
 use App\Services\SearchPaliDataService;
 use App\Services\AIAssistant\NissayaTranslateService;
+use App\Services\AuthService;
 
 use App\Http\Resources\AiModelResource;
-use App\Http\Controllers\AuthController;
 
 use App\Models\PaliText;
 use App\Models\PaliSentence;
@@ -75,7 +75,7 @@ class UpgradeAITranslation extends Command
         if ($this->option('model')) {
             $this->model = $this->modelService->getModelById($this->option('model'));
             $this->info("model:{$this->model['model']}");
-            $this->modelToken = AuthController::getUserToken($this->model['uid']);
+            $this->modelToken = AuthService::getUserToken($this->model['uid']);
         }
         $this->workChannel = ChannelApi::getById($this->ask('请输入结果channel'));
 

+ 9 - 9
api-v13/app/Console/Commands/UpgradeDict.php

@@ -39,7 +39,7 @@ class UpgradeDict extends Command
         parent::__construct();
     }
 
-    private function scandict($dir)
+    private function scanDict(string $dir)
     {
         if (is_dir($dir)) {
             $this->info("scan:" . $dir);
@@ -50,7 +50,7 @@ class UpgradeDict extends Command
                     $fullPath = $dir . "/" . $file;
                     if (is_dir($fullPath) && $file !== '.' && $file !== '..') {
                         //是目录继续搜索
-                        $this->scandict($fullPath);
+                        $this->scanDict($fullPath);
                     } else {
                         //是文件,查看是否是字典信息文件
                         $infoFile = $fullPath;
@@ -198,14 +198,14 @@ class UpgradeDict extends Command
     /**
      * 获取列的值
      */
-    protected function get($data, $colname, $defualt = "")
+    protected function get(array $data, string $colName, $default = "")
     {
-        if (isset($this->cols[$colname])) {
-            return $data[$this->cols[$colname]];
-        } else if (isset($this->dictInfo['cols'][$colname])) {
-            return $this->dictInfo['cols'][$colname];
+        if (isset($this->cols[$colName])) {
+            return $data[$this->cols[$colName]];
+        } else if (isset($this->dictInfo['cols'][$colName])) {
+            return $this->dictInfo['cols'][$colName];
         } else {
-            return $defualt;
+            return $default;
         }
     }
     /**
@@ -219,7 +219,7 @@ class UpgradeDict extends Command
             return 0;
         }
         $this->info("upgrade dict start");
-        $this->scandict(config("mint.path.dict_text"));
+        $this->scanDict(config("mint.path.dict_text"));
         $this->info("upgrade dict done");
 
         return 0;

+ 16 - 5
api-v13/app/Console/Commands/UpgradeProgress.php

@@ -17,7 +17,7 @@ class UpgradeProgress extends Command
      * php artisan upgrade:progress --book=168 --para=916 --channel=19f53a65-81db-4b7d-8144-ac33f1217d34
      * @var string
      */
-    protected $signature = 'upgrade:progress {--book=} {--para=} {--channel=}';
+    protected $signature = 'upgrade:progress {--book=} {--para=} {--channel=} {--resume}';
 
     /**
      * The console command description.
@@ -52,18 +52,28 @@ class UpgradeProgress extends Command
         $para = $this->option('para');
         $channelId = $this->option('channel');
         if ($book && $para && $channelId) {
-            $sentences = Sentence::where('strlen', '>', 0)
+                $sentences = Sentence::where('strlen', '>', 0)
                 ->where('book_id', $book)
                 ->where('paragraph', $para)
                 ->where('channel_uid', $channelId)
                 ->groupby('book_id', 'paragraph', 'channel_uid')
                 ->select('book_id', 'paragraph', 'channel_uid');
         } else {
-            $sentences = Sentence::where('strlen', '>', 0)
+            if($this->option('resume')){
+                $sentences = Sentence::where('strlen', '>', 0)
+                ->whereBetween('book_id', [$book,1000])
+                ->where('paragraph','>=', $para)
+                ->whereNotNull('channel_uid')
+                ->groupby('book_id', 'paragraph', 'channel_uid')
+                ->select('book_id', 'paragraph', 'channel_uid');
+            }else{
+                $sentences = Sentence::where('strlen', '>', 0)
                 ->where('book_id', '<', 1000)
-                ->where('channel_uid', '<>', '')
+                ->whereNotNull('channel_uid')
                 ->groupby('book_id', 'paragraph', 'channel_uid')
                 ->select('book_id', 'paragraph', 'channel_uid');
+            }
+
         }
         $count = $sentences->count();
         $sentences = $sentences->cursor();
@@ -111,7 +121,8 @@ class UpgradeProgress extends Command
                     'created_at' => $finalAt,
                     'updated_at' => $updateAt,
                 ];
-                Log::debug('Progress updateOrInsert', ['para' => $paraInfo, 'data' => $paraData]);
+                //Log::debug('Progress updateOrInsert', ['para' => $paraInfo, 'data' => $paraData]);
+                $this->info('Progress updateOrInsert'.json_encode($paraInfo));
                 Progress::updateOrInsert($paraInfo, $paraData);
             }
         }

+ 2 - 2
api-v13/app/DTO/LLMTranslation/TranslationResponseDTO.php

@@ -22,7 +22,7 @@ readonly class TranslationResponseDTO extends BaseDTO
         public bool $success,
         public string $error,
         public array $data,
-        public TranslationMetaDTO $meta,
+        public ?TranslationMetaDTO $meta = null,
     ) {}
 
     public static function fromArray(array $payload): self
@@ -35,7 +35,7 @@ readonly class TranslationResponseDTO extends BaseDTO
                 $payload['data']
             ),
 
-            meta: TranslationMetaDTO::fromArray($payload['meta']),
+            meta: isset($payload['meta']) ? TranslationMetaDTO::fromArray($payload['meta']) : null,
         );
     }
 }

+ 3 - 3
api-v13/app/DTO/LLMTranslation/TranslationUsageDTO.php

@@ -10,7 +10,7 @@ readonly class TranslationUsageDTO extends BaseDTO
         public int $promptTokens,
         public int $completionTokens,
         public int $totalTokens,
-        public TranslationPromptTokenDetailsDTO $promptTokensDetails,
+        public ?TranslationPromptTokenDetailsDTO $promptTokensDetails = null,
     ) {}
 
     public static function fromArray(array $data): self
@@ -19,9 +19,9 @@ readonly class TranslationUsageDTO extends BaseDTO
             promptTokens: $data['prompt_tokens'],
             completionTokens: $data['completion_tokens'],
             totalTokens: $data['total_tokens'],
-            promptTokensDetails: TranslationPromptTokenDetailsDTO::fromArray(
+            promptTokensDetails: isset($data['prompt_tokens_details']) ? TranslationPromptTokenDetailsDTO::fromArray(
                 $data['prompt_tokens_details']
-            ),
+            ) : null,
         );
     }
 }

+ 2 - 2
api-v13/app/Http/Api/AiTaskPrepare.php

@@ -12,7 +12,7 @@ use App\Http\Api\Mq;
 use App\Http\Api\ChannelApi;
 
 use Illuminate\Support\Facades\Log;
-use App\Http\Controllers\AuthController;
+use App\Services\AuthService;
 
 class AiTaskPrepare
 {
@@ -114,7 +114,7 @@ class AiTaskPrepare
 
         # ai model
         $aiModel = AiModel::findOrFail($task->executor_id);
-        $modelToken = AuthController::getUserToken($aiModel->uid);
+        $modelToken = AuthService::getUserToken($aiModel->uid);
         $aiModel['token'] = $modelToken;
         $sumLen = 0;
         $mqData = [];

+ 1 - 38
api-v13/app/Http/Api/AuthApi.php

@@ -7,41 +7,4 @@ use Illuminate\Http\Request;
 use Firebase\JWT\JWT;
 use Firebase\JWT\Key;
 
-class AuthApi
-{
-    public static function getJwtKey()
-    {
-        return config('mint.app.jwt_secrets_key');
-    }
-    public static function getToken(Request $request)
-    {
-        $token = $request->bearerToken();
-        return $token;
-    }
-    public static function current(Request $request)
-    {
-        $token = $request->bearerToken();
-        if ($token) {
-            try {
-
-                $jwt = JWT::decode($token, new Key(self::getJwtKey(), 'HS512'));
-            } catch (\Exception $e) {
-                return false;
-            }
-            if ($jwt->exp < time()) {
-                //过期
-                return false;
-            } else {
-                //有效的token
-                return ['user_uid' => $jwt->uid, 'user_id' => $jwt->id];
-            }
-        } else if (isset($_COOKIE['user_uid'])) {
-            return [
-                'user_uid' => $_COOKIE['user_uid'],
-                'user_id' => $_COOKIE['user_id']
-            ];
-        } else {
-            return false;
-        }
-    }
-}
+class AuthApi {}

+ 10 - 2
api-v13/app/Http/Api/TemplateRender.php

@@ -405,8 +405,16 @@ class TemplateRender
     private function render_term()
     {
         $word = $this->get_param($this->param, "word", 1);
-
-        $props = $this->getTermProps($word, '');
+        if (str_contains($word, '@')) {
+            [$wordHead, $tag] = explode('@', $word);
+        }
+        if (str_contains($word, '#')) {
+            [$wordHead, $display] = explode('#', $word);
+        }
+        $props = $this->getTermProps($wordHead ?? $word, $tag ?? '');
+        if (isset($display)) {
+            $props['meaning'] = $display;
+        }
 
         $output = $props['word'];
         switch ($this->format) {

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

@@ -8,7 +8,7 @@ use Illuminate\Support\Str;
 use Firebase\JWT\JWT;
 use Firebase\JWT\Key;
 use Illuminate\Support\Facades\Log;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ChannelApi;
 
 class AccessTokenController extends Controller
@@ -33,13 +33,14 @@ class AccessTokenController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('未登录');
             return $this->error(__('auth.failed'), [], 401);
         }
         $payload = $request->input('payload');
         $result = array();
+        Log::debug('token', ['payload' => $payload]);
         foreach ($payload as $key => $value) {
             //鉴权
             switch ($value['res_type']) {
@@ -76,7 +77,7 @@ class AccessTokenController extends Controller
             }
 
             try {
-                $jwt = JWT::encode($value, $token->token, 'HS512');
+                $jwt = JWT::encode($value, $token->token . $token->token, 'HS512');
             } catch (\Exception $e) {
                 Log::error('jwt', ['error' => $e]);
                 continue;

+ 2 - 2
api-v13/app/Http/Controllers/AiAssistantController.php

@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
 use App\Models\AiModel;
 use Illuminate\Http\Request;
 use App\Http\Resources\AiAssistantResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ShareApi;
 
 use Illuminate\Support\Facades\Log;
@@ -20,7 +20,7 @@ class AiAssistantController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);

+ 5 - 5
api-v13/app/Http/Controllers/AiModelController.php

@@ -6,7 +6,7 @@ use App\Http\Requests\StoreAiModelRequest;
 use App\Http\Requests\UpdateAiModelRequest;
 use App\Models\AiModel;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Str;
 use Illuminate\Support\Facades\Log;
 use App\Http\Api\StudioApi;
@@ -23,7 +23,7 @@ class AiModelController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);
@@ -76,7 +76,7 @@ class AiModelController extends Controller
     public function store(StoreAiModelRequest $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -117,7 +117,7 @@ class AiModelController extends Controller
     public function update(UpdateAiModelRequest $request, AiModel $aiModel)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -145,7 +145,7 @@ class AiModelController extends Controller
     public function destroy(Request $request, AiModel $aiModel)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 8 - 8
api-v13/app/Http/Controllers/ArticleController.php

@@ -15,7 +15,7 @@ use App\Models\CustomBookId;
 use App\Models\Sentence;
 
 use App\Http\Resources\ArticleResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ShareApi;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ChannelApi;
@@ -165,7 +165,7 @@ class ArticleController extends Controller
                 break;
             case 'studio':
                 # 获取studio内所有 article
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'), [], 401);
                 }
@@ -245,7 +245,7 @@ class ArticleController extends Controller
      */
     public function showMyNumber(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -276,7 +276,7 @@ class ArticleController extends Controller
     public function store(Request $request)
     {
         //判断权限
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('未登录');
             return $this->error(__('auth.failed'), [], 401);
@@ -433,7 +433,7 @@ class ArticleController extends Controller
             return $this->error("no recorder");
         }
         //判断权限
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             $user_uid = "";
         } else {
@@ -460,7 +460,7 @@ class ArticleController extends Controller
             return $this->error("no recorder");
         }
         //判断权限
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             $user_uid = "";
         } else {
@@ -493,7 +493,7 @@ class ArticleController extends Controller
             return $this->error("no recorder");
         }
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         } else {
@@ -550,7 +550,7 @@ class ArticleController extends Controller
     public function destroy(Request $request, Article $article)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 3 - 3
api-v13/app/Http/Controllers/ArticleMapController.php

@@ -6,7 +6,7 @@ use App\Models\ArticleCollection;
 use App\Models\Article;
 use App\Models\Collection;
 use App\Http\Api\ShareApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Http\Request;
 use App\Http\Resources\ArticleMapResource;
 use Illuminate\Support\Facades\Log;
@@ -91,7 +91,7 @@ class ArticleMapController extends Controller
             return $this->error("no recorder");
         }
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -167,7 +167,7 @@ class ArticleMapController extends Controller
             return $this->error("no recorder");
         }
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 5 - 5
api-v13/app/Http/Controllers/AttachmentController.php

@@ -8,7 +8,7 @@ use Illuminate\Http\Request;
 use Illuminate\Support\Str;
 use Illuminate\Support\Facades\App;
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Resources\AttachmentResource;
 use App\Models\Attachment;
@@ -30,7 +30,7 @@ class AttachmentController extends Controller
         //
         switch ($request->input('view')) {
             case 'studio':
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -72,7 +72,7 @@ class AttachmentController extends Controller
      */
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -209,7 +209,7 @@ class AttachmentController extends Controller
     public function update(Request $request, Attachment $attachment)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -228,7 +228,7 @@ class AttachmentController extends Controller
     public function destroy(Request $request, string $id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 5 - 25
api-v13/app/Http/Controllers/AuthController.php

@@ -5,11 +5,10 @@ namespace App\Http\Controllers;
 use Illuminate\Http\Request;
 use App\Models\UserInfo;
 use Firebase\JWT\JWT;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Facades\Storage;
 use Illuminate\Support\Facades\App;
-use App\Http\Api\UserApi;
-use App\Http\Api\AiAssistantApi;
+
 use Illuminate\Support\Facades\Log;
 
 class AuthController extends Controller
@@ -84,7 +83,7 @@ class AuthController extends Controller
         $user = $query->first();
         if ($user) {
             $ExpTime = time() + 60 * 60 * 24 * 365;
-            $key = AuthApi::getJwtKey();
+            $key = AuthService::getJwtKey();
             $payload = [
                 'nbf' => time(),
                 'exp' => $ExpTime,
@@ -101,30 +100,11 @@ class AuthController extends Controller
         }
     }
 
-    public static function getUserToken($userUid)
-    {
-        $user = UserApi::getByUuid($userUid);
-        if (!$user) {
-            $user = AiAssistantApi::getByUuid($userUid);
-        }
-        if ($user) {
-            $ExpTime = time() + 60 * 60 * 24 * 365;
-            $key = AuthApi::getJwtKey();
-            $payload = [
-                'nbf' => time(),
-                'exp' => $ExpTime,
-                'uid' => $user['id'],
-                'id' => $user['sn'],
-            ];
-            $jwt = JWT::encode($payload, $key, 'HS512');
-            return $jwt;
-        }
-        return null;
-    }
+
 
     public function getUserInfoByToken(Request $request)
     {
-        $curr = AuthApi::current($request);
+        $curr = AuthService::current($request);
         if (!$curr) {
             Log::warning('invalid token');
             return $this->error('invalid token', 401, 401);

+ 11 - 11
api-v13/app/Http/Controllers/ChannelController.php

@@ -16,7 +16,7 @@ use App\Models\CustomBook;
 
 use App\Http\Resources\ChannelResource;
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ShareApi;
 use App\Http\Api\PaliTextApi;
@@ -62,7 +62,7 @@ class ChannelController extends Controller
                 break;
             case 'studio':
                 # 获取studio内所有channel
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -95,7 +95,7 @@ class ChannelController extends Controller
                  * studio 的和协作的
                  */
                 #获取user所有有权限的channel列表
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -121,7 +121,7 @@ class ChannelController extends Controller
                  * 某用户有编辑权限的
                  */
                 #获取user所有有权限的channel列表
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -142,7 +142,7 @@ class ChannelController extends Controller
                 break;
             case 'user-in-chapter':
                 #获取user 在某章节 所有有权限的channel列表
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -306,7 +306,7 @@ class ChannelController extends Controller
      */
     public function showMyNumber(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -417,7 +417,7 @@ class ChannelController extends Controller
         }
 
         #获取 user 在某章节 所有有权限的 channel 列表
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user !== false) {
             //我自己的
             if ($request->input('owner') === 'all' || $request->input('owner') === 'my') {
@@ -543,7 +543,7 @@ class ChannelController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -627,7 +627,7 @@ class ChannelController extends Controller
     public function update(Request $request, Channel $channel)
     {
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -659,7 +659,7 @@ class ChannelController extends Controller
     public function patch(Request $request, Channel $channel)
     {
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }
@@ -703,7 +703,7 @@ class ChannelController extends Controller
     public function destroy(Request $request, Channel $channel)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 2 - 2
api-v13/app/Http/Controllers/ChapterContentController.php

@@ -15,7 +15,7 @@ use App\Http\Api\ChannelApi;
 use App\Http\Api\StudioApi;
 
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Resources\TocResource;
 use App\Services\PaliContentService;
 
@@ -87,7 +87,7 @@ class ChapterContentController extends Controller
     public function show(Request $request, string $id)
     {
         /**
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $this->userUuid = $user['user_uid'];
         }

+ 6 - 6
api-v13/app/Http/Controllers/CollectionController.php

@@ -6,7 +6,7 @@ use App\Models\Collection;
 use Illuminate\Http\Request;
 use Illuminate\Support\Str;
 use Illuminate\Support\Facades\Log;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Resources\CollectionResource;
 use App\Services\CollectionService;
@@ -63,7 +63,7 @@ class CollectionController extends Controller
     // studio 分支的鉴权逻辑留在 controller
     private function buildStudioIndex(Request $request): Builder
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             throw new \Illuminate\Auth\AuthenticationException(__('auth.failed'));
         }
@@ -93,7 +93,7 @@ class CollectionController extends Controller
 
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -132,7 +132,7 @@ class CollectionController extends Controller
 
         if ($result->status < 30) {
             Log::info('私有文章,判断权限' . $id);
-            $user = AuthApi::current($request);
+            $user = AuthService::current($request);
             if (!$user) {
                 Log::warning('未登录');
                 return $this->error(__('auth.failed'), 403, 403);
@@ -158,7 +158,7 @@ class CollectionController extends Controller
             return $this->error('no recorder');
         }
 
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -185,7 +185,7 @@ class CollectionController extends Controller
 
     public function destroy(Request $request, string $id)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 2 - 2
api-v13/app/Http/Controllers/CommandController.php

@@ -3,7 +3,7 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\Mq;
 
 class CommandController extends Controller
@@ -28,7 +28,7 @@ class CommandController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user || $user['user_uid'] !== 'ba5463f3-72d1-4410-858e-eadd10884713') {
             return $this->error(__('auth.failed'), 403, 403);
         }

+ 5 - 5
api-v13/app/Http/Controllers/CorpusController.php

@@ -22,7 +22,7 @@ use App\Http\Api\SuggestionApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Api\UserApi;
 use App\Http\Api\StudioApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Arr;
 use App\Http\Resources\TocResource;
@@ -183,7 +183,7 @@ class CorpusController extends Controller
      */
     public function showSent(Request  $request, string $id)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $this->userUuid = $user['user_uid'];
         }
@@ -208,7 +208,7 @@ class CorpusController extends Controller
      */
     public function showSentences(Request $request, string $type, string $id)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $this->userUuid = $user['user_uid'];
         }
@@ -275,7 +275,7 @@ class CorpusController extends Controller
         if ($request->has('debug')) {
             $this->debug = explode(',', $request->input('debug'));
         }
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $this->userUuid = $user['user_uid'];
         }
@@ -382,7 +382,7 @@ class CorpusController extends Controller
         if ($request->has('debug')) {
             $this->debug = explode(',', $request->input('debug'));
         }
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $this->userUuid = $user['user_uid'];
         }

+ 8 - 8
api-v13/app/Http/Controllers/CourseController.php

@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
 use App\Models\Course;
 use App\Models\CourseMember;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Resources\CourseResource;
 use Illuminate\Support\Facades\DB;
@@ -67,7 +67,7 @@ class CourseController extends Controller
                 break;
             case 'create':
                 # 获取 studio 建立的所有 course
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -79,7 +79,7 @@ class CourseController extends Controller
                 $table = Course::where('studio_id', $user["user_uid"]);
                 break;
             case 'study':
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -98,7 +98,7 @@ class CourseController extends Controller
                 break;
             case 'teach':
                 //我任教的课程
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -139,7 +139,7 @@ class CourseController extends Controller
      */
     public function showMyCourseNumber(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -166,7 +166,7 @@ class CourseController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -244,7 +244,7 @@ class CourseController extends Controller
     public function update(Request $request, Course $course)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -300,7 +300,7 @@ class CourseController extends Controller
     public function destroy(Request $request, Course $course)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 8 - 8
api-v13/app/Http/Controllers/CourseMemberController.php

@@ -8,7 +8,7 @@ use App\Models\UserInfo;
 
 use Illuminate\Http\Request;
 use App\Http\Resources\CourseMemberResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Facades\Log;
 use App\Http\Api\UserApi;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -24,7 +24,7 @@ class CourseMemberController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed', [403], 403));
         }
@@ -110,7 +110,7 @@ class CourseMemberController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed', [403], 403));
         }
@@ -189,7 +189,7 @@ class CourseMemberController extends Controller
     public function show(Request $request, string $courseId)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -222,7 +222,7 @@ class CourseMemberController extends Controller
          * 增加一条新纪录
          * 原有记录变为历史记录
          */
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -253,7 +253,7 @@ class CourseMemberController extends Controller
     public function set_channel(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -286,7 +286,7 @@ class CourseMemberController extends Controller
     {
         //查看删除者有没有删除权限
         //查询删除者的权限
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -319,7 +319,7 @@ class CourseMemberController extends Controller
      */
     public function curr(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 7 - 7
api-v13/app/Http/Controllers/DhammaTermController.php

@@ -11,7 +11,7 @@ use App\Models\DhammaTerm;
 use App\Models\Channel;
 use App\Http\Resources\TermResource;
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Api\ShareApi;
@@ -96,7 +96,7 @@ class DhammaTermController extends Controller
                 break;
             case 'studio':
                 # 获取 studio 内所有 term
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'), [], 401);
                 }
@@ -109,7 +109,7 @@ class DhammaTermController extends Controller
                 break;
             case 'channel':
                 # 获取 studio 内所有 term
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -130,7 +130,7 @@ class DhammaTermController extends Controller
                 break;
             case 'user':
                 # code...
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -209,7 +209,7 @@ class DhammaTermController extends Controller
      */
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -324,7 +324,7 @@ class DhammaTermController extends Controller
     public function update(Request $request, string $id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }
@@ -373,7 +373,7 @@ class DhammaTermController extends Controller
         /**
          * 一次删除多个单词
          */
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 2 - 2
api-v13/app/Http/Controllers/DictController.php

@@ -9,7 +9,7 @@ use App\Models\GroupMember;
 use Illuminate\Http\Request;
 use App\Tools\CaseMan;
 use App\Http\Api\DictApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 require_once __DIR__ . "/../../Tools/grm_abbr.php";
 
@@ -41,7 +41,7 @@ class DictController extends Controller
         /**
          * 临时代码判断是否在缅汉字典群里面。在群里的用户可以产看缅汉字典pdf
          */
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $inMyHanGroup = GroupMember::where('group_id', '905af467-1bde-4d2c-8dc7-49cfb74e0b09')
                 ->where('user_id', $user['user_uid'])->exists();

+ 2 - 2
api-v13/app/Http/Controllers/DictPreferenceController.php

@@ -9,7 +9,7 @@ use App\Models\UserDict;
 use App\Models\WordIndex;
 use App\Http\Resources\DictPreferenceResource;
 use App\Http\Api\DictApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 class DictPreferenceController extends Controller
 {
@@ -91,7 +91,7 @@ class DictPreferenceController extends Controller
      */
     public function update(Request $request,  $id)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }

+ 6 - 6
api-v13/app/Http/Controllers/DiscussionController.php

@@ -15,7 +15,7 @@ use App\Http\Controllers\ArticleController;
 use App\Http\Controllers\WbwSentenceController;
 use App\Http\Resources\DiscussionResource;
 use App\Http\Api\MdRender;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\Mq;
 use App\Http\Api\UserApi;
 use App\Http\Api\ChannelApi;
@@ -31,7 +31,7 @@ class DiscussionController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $userInfo = UserApi::getByUuid($user['user_uid']);
         }
@@ -216,7 +216,7 @@ class DiscussionController extends Controller
 
         $can_create = false;
         $can_reply = false;
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
 
         switch ($request->input('type', 'discussion')) {
             case 'qa':
@@ -302,7 +302,7 @@ class DiscussionController extends Controller
      */
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('discussion store auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), [401], 401);
@@ -418,7 +418,7 @@ class DiscussionController extends Controller
     public function update(Request $request, Discussion $discussion)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [403], 403);
         }
@@ -474,7 +474,7 @@ class DiscussionController extends Controller
     public function destroy(Request $request, Discussion $discussion)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [401], 401);
         }

+ 2 - 2
api-v13/app/Http/Controllers/DiscussionCountController.php

@@ -3,7 +3,7 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ChannelApi;
 use App\Http\Resources\DiscussionCountResource;
 use App\Http\Resources\TagMapResource;
@@ -63,7 +63,7 @@ class DiscussionCountController extends Controller
          * 3. 计算答案channel的结果
          * 4. 计算作业channel的结果
          */
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error('auth.failed', 401, 401);
         }

+ 2 - 2
api-v13/app/Http/Controllers/ExportController.php

@@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\App;
 use Illuminate\Support\Facades\Storage;
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\Mq;
 use Illuminate\Support\Facades\Cache;
 use App\Tools\ExportDownload;
@@ -23,7 +23,7 @@ class ExportController extends Controller
     public function index(Request $request)
     {
         $queryId = Str::uuid();
-        $token = AuthApi::getToken($request);
+        $token = AuthService::getToken($request);
         switch ($request->input('type', 'chapter')) {
             case 'chapter':
                 $data = [

+ 7 - 7
api-v13/app/Http/Controllers/GroupController.php

@@ -8,7 +8,7 @@ use Illuminate\Http\Request;
 use Illuminate\Support\Str;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\DB;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Resources\GroupResource;
 
@@ -28,7 +28,7 @@ class GroupController extends Controller
         switch ($request->input('view')) {
             case 'studio':
                 # 获取studio内所有group
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -85,7 +85,7 @@ class GroupController extends Controller
      */
     public function showMyNumber(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -110,7 +110,7 @@ class GroupController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -162,7 +162,7 @@ class GroupController extends Controller
         }
         if ($result->status < 30) {
             //私有,判断权限
-            $user = AuthApi::current($request);
+            $user = AuthService::current($request);
             if (!$user) {
                 return $this->error(__('auth.failed'));
             }
@@ -191,7 +191,7 @@ class GroupController extends Controller
     public function update(Request $request, GroupInfo $group)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -219,7 +219,7 @@ class GroupController extends Controller
     public function destroy(Request $request, GroupInfo $group)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 3 - 3
api-v13/app/Http/Controllers/GroupMemberController.php

@@ -6,7 +6,7 @@ use App\Models\GroupMember;
 use App\Models\GroupInfo;
 use Illuminate\Http\Request;
 use App\Http\Resources\GroupMemberResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 class GroupMemberController extends Controller
 {
@@ -18,7 +18,7 @@ class GroupMemberController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -135,7 +135,7 @@ class GroupMemberController extends Controller
         //
         //查看删除者有没有删除权限
         //查询删除者的权限
-        $currUser = AuthApi::current($request);
+        $currUser = AuthService::current($request);
         if (!$currUser) {
             return $this->error(__('auth.failed'));
         }

+ 2 - 2
api-v13/app/Http/Controllers/InteractiveController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\Discussion;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 class InteractiveController extends Controller
 {
@@ -38,7 +38,7 @@ class InteractiveController extends Controller
     public function show(Request $request, string $res_id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         $data = [];
         switch ($request->input('res_type')) {
             case 'article':

+ 3 - 3
api-v13/app/Http/Controllers/InviteController.php

@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
 use App\Models\Invite;
 use App\Models\UserInfo;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\UserApi;
 use App\Http\Api\StudioApi;
 use App\Http\Resources\InviteResource;
@@ -23,7 +23,7 @@ class InviteController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -80,7 +80,7 @@ class InviteController extends Controller
         //
         $sender = '';
         if (!empty($request->input('studio'))) {
-            $user = AuthApi::current($request);
+            $user = AuthService::current($request);
             if (!$user) {
                 return $this->error(__('auth.failed'), 401, 401);
             }

+ 22 - 77
api-v13/app/Http/Controllers/Library/HomeController.php

@@ -3,15 +3,14 @@
 namespace App\Http\Controllers\Library;
 
 use App\Http\Controllers\Controller;
+use Illuminate\Support\Facades\Cookie;
 
-use Illuminate\Http\Request;
 use Illuminate\Support\Facades\File;
 
 
 use App\Models\PaliText;
 use App\Models\ProgressChapter;
-use App\Models\Tag;
-use App\Models\TagMap;
+use App\Services\TermService;
 
 
 class HomeController extends Controller
@@ -25,23 +24,26 @@ class HomeController extends Controller
         'linear-gradient(160deg, #1a1a2d,rgb(76, 68, 146))',
         'linear-gradient(160deg, #1a2820,rgb(55, 124, 99))',
     ];
-    // -------------------------------------------------------------------------
-    // 从 uid / id 字符串中提取一个稳定的整数,用于色池取余
-    // -------------------------------------------------------------------------
-    private function colorIndex(string $uid): int
-    {
-        return hexdec(substr(str_replace('-', '', $uid), 0, 4)) % 255;
-    }
+    /**
+     * 构造函数,注入 OpenSearchService
+     *
+     * @param  \App\Services\OpenSearchService  $searchService
+     */
+    public function __construct(
+        protected TermService $termService,
+    ) {}
+
     protected static int $nextId = 1;
 
     public function index()
     {
         $categories = $this->loadCategories();
-
+        $locale = Cookie::get('language') ?? 'en';
         // 获取一级分类和对应的书籍
         $categoryData = [];
         foreach ($categories as $category) {
             if ($category['level'] == 1) {
+
                 $children = $this->subCategories($categories, $category['id']);
                 $categoryData[] = [
                     'category' => $category,
@@ -49,6 +51,7 @@ class HomeController extends Controller
                 ];
             }
         }
+        $categoryData = $this->termService->attachLocalName($categoryData, $locale);
         $recentBooks = $this->getUpdateBooks();
 
         return view('library.index', compact(
@@ -58,7 +61,13 @@ class HomeController extends Controller
         ));
     }
 
-
+    // -------------------------------------------------------------------------
+    // 从 uid / id 字符串中提取一个稳定的整数,用于色池取余
+    // -------------------------------------------------------------------------
+    private function colorIndex(string $uid): int
+    {
+        return hexdec(substr(str_replace('-', '', $uid), 0, 4)) % 255;
+    }
 
     private function subCategories($categories, int $id)
     {
@@ -66,71 +75,7 @@ class HomeController extends Controller
             return $cat['parent_id'] == $id;
         });
     }
-    private function getRecent()
-    {
-        return [
-            [
-                'id'         => 'book-001',
-                'title'      => '相应部·因缘篇',
-                'author'     => 'Bhikkhu Bodhi',
-                'cover'      => null,                          // 无封面时显示渐变
-                'cover_gradient' => 'linear-gradient(135deg, #2d5a8e 0%, #1a3a5c 100%)',
-                'updated_at' => '2小时前',
-                'is_new'     => true,                          // true=新增, false=更新
-                'category'   => '经藏',
-            ],
-            [
-                'id'         => 'book-002',
-                'title'      => '长部·梵网经',
-                'author'     => 'Bhikkhu Sujato',
-                'cover'      => null,
-                'cover_gradient' => 'linear-gradient(135deg, #5a2d8e 0%, #3a1a5c 100%)',
-                'updated_at' => '昨天',
-                'is_new'     => false,
-                'category'   => '经藏',
-            ],
-            [
-                'id'         => 'book-003',
-                'title'      => '法句经注',
-                'author'     => 'Buddhaghosa',
-                'cover'      => null,
-                'cover_gradient' => 'linear-gradient(135deg, #8e5a2d 0%, #5c3a1a 100%)',
-                'updated_at' => '3天前',
-                'is_new'     => false,
-                'category'   => '注释',
-            ],
-            [
-                'id'         => 'book-004',
-                'title'      => '律藏·波罗夷',
-                'author'     => 'Bhikkhu Brahmali',
-                'cover'      => null,
-                'cover_gradient' => 'linear-gradient(135deg, #2d8e5a 0%, #1a5c3a 100%)',
-                'updated_at' => '5天前',
-                'is_new'     => true,
-                'category'   => '律藏',
-            ],
-            [
-                'id'         => 'book-005',
-                'title'      => '清净道论',
-                'author'     => 'Buddhaghosa',
-                'cover'      => null,
-                'cover_gradient' => 'linear-gradient(135deg, #8e2d2d 0%, #5c1a1a 100%)',
-                'updated_at' => '1周前',
-                'is_new'     => false,
-                'category'   => '注释',
-            ],
-            [
-                'id'         => 'book-006',
-                'title'      => '增支部·一集',
-                'author'     => 'Bhikkhu Bodhi',
-                'cover'      => null,
-                'cover_gradient' => 'linear-gradient(135deg, #2d7a8e 0%, #1a4a5c 100%)',
-                'updated_at' => '1周前',
-                'is_new'     => false,
-                'category'   => '经藏',
-            ],
-        ];
-    }
+
 
     private function getUpdateBooks()
     {

+ 4 - 4
api-v13/app/Http/Controllers/LikeController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\Like;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\UserApi;
 use App\Http\Resources\LikeResource;
 use Illuminate\Support\Str;
@@ -27,7 +27,7 @@ class LikeController extends Controller
                     ->select("type")
                     ->selectRaw("count(*)")
                     ->get();
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if ($user) {
                     foreach ($result as $key => $value) {
                         $curr = Like::where([
@@ -70,7 +70,7 @@ class LikeController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -135,7 +135,7 @@ class LikeController extends Controller
     public function destroy(Request $request, Like $like)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 2 - 2
api-v13/app/Http/Controllers/ModelLogController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\ModelLog;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Resources\ModelLogResource;
 use Illuminate\Support\Str;
 
@@ -18,7 +18,7 @@ class ModelLogController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 5 - 5
api-v13/app/Http/Controllers/NissayaEndingController.php

@@ -7,7 +7,7 @@ use App\Models\Relation;
 use App\Models\DhammaTerm;
 use Illuminate\Http\Request;
 use App\Http\Resources\NissayaEndingResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ChannelApi;
 use Illuminate\Support\Facades\App;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -87,7 +87,7 @@ class NissayaEndingController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -134,7 +134,7 @@ class NissayaEndingController extends Controller
     public function update(Request $request, NissayaEnding $nissayaEnding)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -178,7 +178,7 @@ class NissayaEndingController extends Controller
     public function destroy(Request $request, NissayaEnding $nissayaEnding)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -217,7 +217,7 @@ class NissayaEndingController extends Controller
 
     public function import(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 4 - 4
api-v13/app/Http/Controllers/NotificationController.php

@@ -6,7 +6,7 @@ use Illuminate\Http\Request;
 use Illuminate\Support\Str;
 use Illuminate\Support\Facades\Log;
 use App\Models\Notification;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Resources\NotificationResource;
 
 class NotificationController extends Controller
@@ -19,7 +19,7 @@ class NotificationController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);
@@ -62,7 +62,7 @@ class NotificationController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);
@@ -120,7 +120,7 @@ class NotificationController extends Controller
     public function update(Request $request, Notification $notification)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 4 - 4
api-v13/app/Http/Controllers/ProjectController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\Project;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ShareApi;
 
@@ -21,7 +21,7 @@ class ProjectController extends Controller
      */
     public function index(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);
@@ -102,7 +102,7 @@ class ProjectController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -161,7 +161,7 @@ class ProjectController extends Controller
     public function update(Request $request, Project $project)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 2 - 2
api-v13/app/Http/Controllers/ProjectTreeController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\Project;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Str;
 use App\Http\Api\StudioApi;
 use Illuminate\Support\Facades\Log;
@@ -30,7 +30,7 @@ class ProjectTreeController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);

+ 2 - 2
api-v13/app/Http/Controllers/RecentController.php

@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
 use App\Models\Recent;
 use Illuminate\Http\Request;
 use App\Http\Resources\RecentResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Str;
 
 class RecentController extends Controller
@@ -46,7 +46,7 @@ class RecentController extends Controller
      */
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }

+ 5 - 5
api-v13/app/Http/Controllers/RelationController.php

@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
 use App\Models\Relation;
 use Illuminate\Http\Request;
 use App\Http\Resources\RelationResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Facades\App;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
@@ -87,7 +87,7 @@ class RelationController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }
@@ -145,7 +145,7 @@ class RelationController extends Controller
     public function update(Request $request, Relation $relation)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -184,7 +184,7 @@ class RelationController extends Controller
     public function destroy(Request $request, Relation $relation)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -226,7 +226,7 @@ class RelationController extends Controller
 
     public function import(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 7 - 7
api-v13/app/Http/Controllers/SentPrController.php

@@ -15,7 +15,7 @@ use App\Models\Sentence;
 use App\Models\Notification;
 use App\Http\Resources\SentPrResource;
 use App\Http\Api\Mq;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 class SentPrController extends Controller
 {
@@ -41,7 +41,7 @@ class SentPrController extends Controller
         }
         if ($result) {
             //修改notification 已读状态
-            $user = AuthApi::current($request);
+            $user = AuthService::current($request);
             if ($user) {
                 $id = array();
                 foreach ($result as $key => $row) {
@@ -110,7 +110,7 @@ class SentPrController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -152,7 +152,7 @@ class SentPrController extends Controller
 
         $suggestionData =  [
             'data' => new SentPrResource($new),
-            'token' => AuthApi::getToken($request),
+            'token' => AuthService::getToken($request),
             'notification' => $request->input('notification', true),
             'webhook' => $request->input('webhook', true),
         ];
@@ -195,7 +195,7 @@ class SentPrController extends Controller
             return $this->error('no data', 404, 404);
         }
         //修改notification 已读状态
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             Notification::where('res_id', $uid)
                 ->where('to', $user['user_uid'])
@@ -214,7 +214,7 @@ class SentPrController extends Controller
      */
     public function update(Request $request, string $id)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -241,7 +241,7 @@ class SentPrController extends Controller
     public function destroy(Request $request, string $id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 7 - 7
api-v13/app/Http/Controllers/SentenceController.php

@@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Redis;
 
 use App\Http\Resources\SentResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ShareApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Api\PaliTextApi;
@@ -34,7 +34,7 @@ class SentenceController extends Controller
      */
     public function index(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         $result = false;
         $indexCol = [
             'id',
@@ -113,7 +113,7 @@ class SentenceController extends Controller
                 $channelTable = Channel::where("type", $type)->select(['uid', 'name']);
                 $channelPub = $channelTable->where('status', 30)->get();
 
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 $channelShare = array();
                 $channelMy = array();
                 if ($user) {
@@ -276,9 +276,9 @@ class SentenceController extends Controller
                     return false;
                 }
                 $key = AccessToken::where('res_id', $channelId)->value('token');
-                $jwt = JWT::decode($access_token, new Key($key, 'HS512'));
+                $jwt = JWT::decode($access_token, new Key($key . $key, 'HS512'));
                 Log::debug('access token', ['jwt' => $jwt]);
-                if ($jwt->book !== $book) {
+                if ($jwt->book && $jwt->book !== $book) {
                     Log::error('access token error');
                     return false;
                 }
@@ -295,7 +295,7 @@ class SentenceController extends Controller
     public function store(Request $request)
     {
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             //未登录用户
             return $this->error(__('auth.failed'), 401, 401);
@@ -477,7 +477,7 @@ class SentenceController extends Controller
         $param = \explode('_', $id);
 
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             //未登录鉴权失败
             return $this->error(__('auth.failed'), 403, 403);

+ 4 - 4
api-v13/app/Http/Controllers/ShareController.php

@@ -6,7 +6,7 @@ use App\Models\Share;
 use App\Models\GroupInfo;
 use Illuminate\Http\Request;
 use App\Http\Resources\ShareResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ShareApi;
 use Illuminate\Support\Str;
 
@@ -20,7 +20,7 @@ class ShareController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         $result = false;
         $role = "member";
         $indexCol = ['id', 'res_id', 'res_type', 'power', 'updated_at', 'created_at'];
@@ -140,7 +140,7 @@ class ShareController extends Controller
     public function update(Request $request, Share $share)
     {
         //查询权限
-        $currUser = AuthApi::current($request);
+        $currUser = AuthService::current($request);
         if (!$currUser) {
             return $this->error(__('auth.failed'));
         }
@@ -174,7 +174,7 @@ class ShareController extends Controller
     public function destroy(Request $request, Share $share)
     {
         //查询权限
-        $currUser = AuthApi::current($request);
+        $currUser = AuthService::current($request);
         if (!$currUser) {
             return $this->error(__('auth.failed'));
         }

+ 1 - 1
api-v13/app/Http/Controllers/StudioController.php

@@ -3,7 +3,7 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ShareApi;
 use App\Models\Channel;

+ 3 - 3
api-v13/app/Http/Controllers/SysModelController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\AiModel;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Cache;
 use App\Http\Resources\AiModelResource;
@@ -20,7 +20,7 @@ class SysModelController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);
@@ -48,7 +48,7 @@ class SysModelController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);

+ 3 - 3
api-v13/app/Http/Controllers/TagController.php

@@ -9,7 +9,7 @@ use App\Models\TagMap;
 use App\Models\ProgressChapter;
 use Illuminate\Http\Request;
 use App\Http\Resources\TagResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 
 class TagController extends Controller
@@ -60,7 +60,7 @@ class TagController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -107,7 +107,7 @@ class TagController extends Controller
     public function update(Request $request, Tag $tag)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 3 - 3
api-v13/app/Http/Controllers/TagMapController.php

@@ -7,7 +7,7 @@ use App\Models\Tag;
 use Illuminate\Http\Request;
 use App\Http\Resources\TagMapResource;
 use App\Http\Resources\TagResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\CourseApi;
 use Illuminate\Support\Str;
@@ -86,7 +86,7 @@ class TagMapController extends Controller
      */
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -161,7 +161,7 @@ class TagMapController extends Controller
      */
     public function destroy(Request $request, TagMap $tagMap)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 5 - 5
api-v13/app/Http/Controllers/TaskController.php

@@ -11,7 +11,7 @@ use App\Models\TaskAssignee;
 use App\Models\Project;
 use App\Http\Resources\TaskResource;
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\TaskApi;
 
@@ -26,7 +26,7 @@ class TaskController extends Controller
     public function index(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             Log::error('notification auth failed {request}', ['request' => $request]);
             return $this->error(__('auth.failed'), 401, 401);
@@ -145,7 +145,7 @@ class TaskController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -214,7 +214,7 @@ class TaskController extends Controller
     public function update(Request $request, Task $task)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }
@@ -296,7 +296,7 @@ class TaskController extends Controller
     public function destroy(Request $request, Task  $task)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 2 - 2
api-v13/app/Http/Controllers/TaskGroupController.php

@@ -10,7 +10,7 @@ use App\Models\TaskAssignee;
 use Illuminate\Support\Facades\Log;
 
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 use Illuminate\Support\Str;
 
@@ -37,7 +37,7 @@ class TaskGroupController extends Controller
 
         //
 
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 2 - 2
api-v13/app/Http/Controllers/TaskStatusController.php

@@ -12,7 +12,7 @@ use App\Models\TaskRelation;
 use App\Models\TaskAssignee;
 use App\Models\AiModel;
 use App\Http\Resources\TaskResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\WatchApi;
 use App\Http\Api\UserApi;
 
@@ -85,7 +85,7 @@ class TaskStatusController extends Controller
     {
         //
         $task = Task::findOrFail($id);
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 3 - 3
api-v13/app/Http/Controllers/TermExportController.php

@@ -10,7 +10,7 @@ use Illuminate\Support\Str;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Api\ShareApi;
@@ -25,7 +25,7 @@ class TermExportController extends Controller
      */
     public function index(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -107,7 +107,7 @@ class TermExportController extends Controller
 
     public function import(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), 401, 401);
         }

+ 4 - 4
api-v13/app/Http/Controllers/TransferController.php

@@ -6,7 +6,7 @@ use App\Models\Transfer;
 use App\Models\Channel;
 use App\Models\Article;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\UserApi;
 use App\Http\Resources\TransferResource;
@@ -25,7 +25,7 @@ class TransferController extends Controller
         switch ($request->input('view')) {
             case 'studio':
                 # 获取studio内所有channel
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -83,7 +83,7 @@ class TransferController extends Controller
      */
     public function store(Request $request)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }
@@ -164,7 +164,7 @@ class TransferController extends Controller
      */
     public function update(Request $request, Transfer $transfer)
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [403], 403);
         }

+ 5 - 5
api-v13/app/Http/Controllers/UserDictController.php

@@ -8,7 +8,7 @@ use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Redis;
 use Illuminate\Support\Facades\Log;
 use App\Http\Api;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\DictApi;
 use App\Http\Resources\UserDictResource;
 use Illuminate\Support\Str;
@@ -43,7 +43,7 @@ class UserDictController extends Controller
         switch ($request->input('view')) {
             case 'all':
                 # 获取studio内所有channel
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -51,7 +51,7 @@ class UserDictController extends Controller
                 break;
             case 'studio':
                 # 获取studio内所有channel
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -144,7 +144,7 @@ class UserDictController extends Controller
     public function store(Request $request)
     {
         //
-        $user  = AuthApi::current($request);
+        $user  = AuthService::current($request);
         if (!$user) {
             $this->error("not login");
         }
@@ -263,7 +263,7 @@ class UserDictController extends Controller
     public function destroy(Request $request, $id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 403);
         }

+ 2 - 2
api-v13/app/Http/Controllers/UserOperationDailyController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\UserOperationDaily;
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\UserApi;
 
 class UserOperationDailyController extends Controller
@@ -20,7 +20,7 @@ class UserOperationDailyController extends Controller
         switch ($request->input('view')) {
             case "user-all":
                 $queryUserUuid = UserApi::getIdByName($request->input('studio_name'));
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }

+ 5 - 5
api-v13/app/Http/Controllers/ViewController.php

@@ -9,7 +9,7 @@ use Illuminate\Http\Request;
 use Illuminate\Support\Str;
 use Illuminate\Support\Facades\Log;
 use App\Http\Resources\ViewResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 
 class ViewController extends Controller
@@ -74,7 +74,7 @@ class ViewController extends Controller
                 return $this->ok($count);
                 break;
             case 'user-recent':
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -85,7 +85,7 @@ class ViewController extends Controller
                 return $this->ok($items);
                 break;
             case 'user':
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -94,7 +94,7 @@ class ViewController extends Controller
                 break;
             case 'studio':
                 # 获取studio内所有 数据
-                $user = AuthApi::current($request);
+                $user = AuthService::current($request);
                 if (!$user) {
                     return $this->error(__('auth.failed'));
                 }
@@ -154,7 +154,7 @@ class ViewController extends Controller
             'target_id' => $target_id,
             'target_type' => $request->input("target_type"),
         ];
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             //已经登陆
             $user_id = $user['user_uid'];

+ 2 - 2
api-v13/app/Http/Controllers/WbwController.php

@@ -11,7 +11,7 @@ use App\Models\Sentence;
 use Illuminate\Http\Request;
 use Illuminate\Support\Str;
 use App\Tools\Tools;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ShareApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Api\Mq;
@@ -39,7 +39,7 @@ class WbwController extends Controller
     {
         //
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             //未登录用户
             return $this->error(__('auth.failed'), [], 401);

+ 2 - 2
api-v13/app/Http/Controllers/WbwLookupController.php

@@ -11,7 +11,7 @@ use App\Tools\CaseMan;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Cache;
 use App\Http\Api\DictApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 
 
@@ -193,7 +193,7 @@ class WbwLookupController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             //未登录用户
             return $this->error(__('auth.failed'), 401, 401);

+ 3 - 3
api-v13/app/Http/Controllers/WbwSentenceController.php

@@ -9,7 +9,7 @@ use App\Models\CourseMember;
 use App\Models\Course;
 
 use Illuminate\Http\Request;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ShareApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Api\CourseApi;
@@ -27,7 +27,7 @@ class WbwSentenceController extends Controller
         //
         $channelsId = [];
         $result = [];
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         $user_uid = null;
         if ($user) {
             $user_uid = $user['user_uid'];
@@ -177,7 +177,7 @@ class WbwSentenceController extends Controller
     {
         //
         //鉴权
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             //未登录用户
             return $this->error(__('auth.failed'), 401, 401);

+ 4 - 4
api-v13/app/Http/Controllers/WebHookController.php

@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
 use App\Models\WebHook;
 use Illuminate\Http\Request;
 use App\Http\Resources\WebHookResource;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Str;
 
 class WebHookController extends Controller
@@ -46,7 +46,7 @@ class WebHookController extends Controller
     public function store(Request $request)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }
@@ -101,7 +101,7 @@ class WebHookController extends Controller
     public function update(Request $request, string $id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'), [], 401);
         }
@@ -141,7 +141,7 @@ class WebHookController extends Controller
     public function destroy(Request $request, string $id)
     {
         //
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return $this->error(__('auth.failed'));
         }

+ 1 - 1
api-v13/app/Http/Middleware/SetLocale.php

@@ -17,7 +17,7 @@ class SetLocale
     public function handle(Request $request, Closure $next): Response
     {
         // 支持的语言(Laravel 推荐小写 + 标准化)
-        $supportedLocales = ['en', 'zh-Hans', 'zh-Hant'];
+        $supportedLocales = array_keys(config('mint.languages'));
         $defaultLocale = config('app.locale', 'en');
 
         // 1️⃣ URL 参数 ?lang=

+ 2 - 2
api-v13/app/Http/Middleware/UserOperation.php

@@ -11,7 +11,7 @@ use Illuminate\Support\Facades\Log;
 use App\Models\UserOperationLog;
 use App\Models\UserOperationFrame;
 use App\Models\UserOperationDaily;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 class UserOperation
 {
@@ -26,7 +26,7 @@ class UserOperation
     {
         $response = $next($request);
 
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (! $user) {
             return $response;
         }

+ 2 - 2
api-v13/app/Http/Requests/StoreDiscussionRequest.php

@@ -3,7 +3,7 @@
 namespace App\Http\Requests;
 
 use Illuminate\Foundation\Http\FormRequest;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use Illuminate\Support\Facades\Log;
 
 class StoreDiscussionRequest extends FormRequest
@@ -16,7 +16,7 @@ class StoreDiscussionRequest extends FormRequest
      */
     public function authorize()
     {
-        $user = AuthApi::current($this);
+        $user = AuthService::current($this);
         if (!$user) {
             Log::warning('discussion store auth failed', ['request' => $this]);
             return false;

+ 2 - 2
api-v13/app/Http/Resources/ArticleResource.php

@@ -17,7 +17,7 @@ use App\Http\Controllers\ArticleController;
 use App\Http\Api\MdRender;
 use App\Http\Api\UserApi;
 use App\Http\Api\StudioApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ChannelApi;
 
 
@@ -45,7 +45,7 @@ class ArticleResource extends JsonResource
             "updated_at" => $this->updated_at,
         ];
 
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $canEdit = ArticleController::userCanEdit($user['user_uid'], $this);
             if ($canEdit) {

+ 3 - 3
api-v13/app/Http/Resources/CourseResource.php

@@ -8,7 +8,7 @@ use App\Http\Api\StudioApi;
 use App\Models\Collection;
 use App\Models\Channel;
 use App\Models\CourseMember;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Models\Attachment;
 use Illuminate\Support\Facades\Storage;
 use Illuminate\Support\Str;
@@ -55,7 +55,7 @@ class CourseResource extends JsonResource
             ->where('is_current', true)
             ->select(['role', 'status'])
             ->get();
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if ($user) {
             $data['my_role'] = CourseMember::where('course_id', $this->id)
                 ->where('is_current', true)
@@ -101,7 +101,7 @@ class CourseResource extends JsonResource
         }
 
         if ($request->input('view') === "study" || $request->input('view') === "teach") {
-            $user = AuthApi::current($request);
+            $user = AuthService::current($request);
             if ($user) {
                 $course_member = CourseMember::where('user_id', $user["user_uid"])
                     ->where('course_id', $this->id)

+ 9 - 9
api-v13/app/Http/Resources/GroupResource.php

@@ -4,7 +4,7 @@ namespace App\Http\Resources;
 
 use Illuminate\Http\Resources\Json\JsonResource;
 use App\Models\GroupMember;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 
 class GroupResource extends JsonResource
@@ -17,7 +17,7 @@ class GroupResource extends JsonResource
      */
     public function toArray($request)
     {
-		$data = [
+        $data = [
             'uid' => $this->uid,
             'name' => $this->name,
             'description' => $this->description,
@@ -26,14 +26,14 @@ class GroupResource extends JsonResource
             'updated_at' => $this->updated_at,
             'created_at' => $this->created_at
         ];
-        $user = AuthApi::current($request);
-        if($user){
-            if($this->owner === $user['user_uid']){
+        $user = AuthService::current($request);
+        if ($user) {
+            if ($this->owner === $user['user_uid']) {
                 $data['role'] = 'owner';
-            }else{
-                $power = GroupMember::where('user_id',$user['user_uid'])
-                                      ->where('group_id',$this->uid)
-                                      ->value('power');
+            } else {
+                $power = GroupMember::where('user_id', $user['user_uid'])
+                    ->where('group_id', $this->uid)
+                    ->value('power');
                 switch ($power) {
                     case 0:
                         $data['role'] = 'owner';

+ 2 - 2
api-v13/app/Http/Resources/SentPrResource.php

@@ -5,7 +5,7 @@ namespace App\Http\Resources;
 use Illuminate\Http\Resources\Json\JsonResource;
 use App\Http\Api\MdRender;
 use App\Http\Api\UserApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\ChannelApi;
 
 class SentPrResource extends JsonResource
@@ -19,7 +19,7 @@ class SentPrResource extends JsonResource
     public function toArray($request)
     {
         //获取用户信息
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         $role = 'reader';
         if ($user && $user["user_uid"] === $this->editor_uid) {
             $role = 'owner';

+ 1 - 1
api-v13/app/Http/Resources/SentSimResource.php

@@ -8,7 +8,7 @@ use App\Models\PaliText;
 use App\Http\Api\UserApi;
 use App\Http\Api\ChannelApi;
 use App\Http\Controllers\CorpusController;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 
 class SentSimResource extends JsonResource
 {

+ 2 - 2
api-v13/app/Http/Resources/TermResource.php

@@ -10,7 +10,7 @@ use App\Http\Api\StudioApi;
 use App\Http\Api\UserApi;
 use App\Http\Api\MdRender;
 use App\Http\Api\ShareApi;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Models\UserOperationDaily;
 use App\Models\DhammaTerm;
 use App\Helpers\MarkdownHelper;
@@ -108,7 +108,7 @@ class TermResource extends JsonResource
             $data["summary"]  = $mdRender->convert($summaryContent, $channels, null);
         }
 
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             $data["role"] = 'reader';
         } else {

+ 3 - 2
api-v13/app/Services/AIAssistant/AITermService.php

@@ -8,8 +8,9 @@ use App\Services\OpenSearchService;
 use App\Services\TermService;
 use App\Services\OpenAIService;
 use App\Services\AIModelService;
+use App\Services\AuthService;
+
 use App\Http\Resources\AiModelResource;
-use App\Http\Controllers\AuthController;
 use App\DTO\Search\SearchDataDTO;
 
 class AITermService
@@ -205,7 +206,7 @@ class AITermService
     public function setModel($id)
     {
         $this->model = $this->modelService->getModelById($id);
-        $this->modelToken = AuthController::getUserToken($id);
+        $this->modelToken = AuthService::getUserToken($id);
         return $this;
     }
 

+ 48 - 7
api-v13/app/Services/AIAssistant/ArticleTranslateService.php

@@ -5,13 +5,14 @@ namespace App\Services\AIAssistant;
 use App\Services\ArticleService;
 use App\Services\PaliContentService;
 use App\Services\SentenceService;
+use App\Services\AuthService;
 
 use App\Models\CustomBook;
 
 
 use Illuminate\Support\Facades\Log;
 use App\Http\Api\ChannelApi;
-use Carbon\Callback;
+
 
 class ArticleTranslateService
 {
@@ -20,9 +21,12 @@ class ArticleTranslateService
     protected TranslateService $translateService;
     protected SentenceService $sentenceService;
 
+
     protected string $modelId;
+    protected string $modelToken;
     protected array $translation = [];
     protected string $outputChannelId;
+    protected string $currArticleId;
 
     protected string $systemPrompt = <<<PROMPT
     请根据提供的原文,翻译为简体中文。
@@ -75,6 +79,7 @@ class ArticleTranslateService
     public function setModel(string $model): self
     {
         $this->modelId = $model;
+        $this->modelToken = app(AuthService::class)->getUserToken($model);
         return $this;
     }
     /**
@@ -88,21 +93,27 @@ class ArticleTranslateService
         $this->outputChannelId = $id;
         return $this;
     }
-    public function translateAnthology($anthologyId, ?callable $onEach = null): int
+
+    public function getCurrArticleId()
+    {
+        return $this->currArticleId;
+    }
+    public function translateAnthology(string $anthologyId, ?callable $onEach = null): int
     {
-        $articles = $this->articleService->articlesInAnthology($anthologyId);
+        $articleIds = $this->articleService->articlesInAnthology($anthologyId);
 
-        foreach ($articles as $article) {
-            $sentences = $this->translateArticle($article)->save();
+        foreach ($articleIds as $article) {
+            $this->translateArticle($article);
             if ($onEach) {
-                $onEach($article, $sentences);
+                $onEach($this);
             }
         }
 
-        return count($articles);
+        return count($articleIds);
     }
     public function translateArticle(string $articleId)
     {
+        $this->currArticleId = $articleId;
         //获取文章中的句子id
         $sentenceIds = $this->articleService->sentenceIds($articleId);
         if (!$sentenceIds || count($sentenceIds) === 0) {
@@ -167,6 +178,36 @@ class ArticleTranslateService
         }
         return count($sentData);
     }
+
+    public function saveRpc(string $endpoint, string $accessToken)
+    {
+        if (
+            !is_array($this->translation) ||
+            count($this->translation) === 0
+        ) {
+            return 0;
+        }
+        $channelInfo = ChannelApi::getById($this->outputChannelId);
+        $sentData = [];
+        $sentData = array_map(function ($n) use ($channelInfo, $accessToken) {
+            $sId = explode('-', $n['id']);
+            return [
+                'book_id' => $sId[0],
+                'paragraph' => $sId[1],
+                'word_start' => $sId[2],
+                'word_end' => $sId[3],
+                'channel_uid' => $channelInfo['id'],
+                'content' => $n['content'],
+                'content_type' => $n['content_type'] ?? 'markdown',
+                'access_token' => $accessToken,
+            ];
+        }, $this->translation);
+        foreach ($sentData as  $value) {
+            $this->sentenceService->saveRpc($endpoint, $value, $this->modelToken);
+        }
+        return count($sentData);
+    }
+
     public function get()
     {
         return $this->translation;

+ 3 - 3
api-v13/app/Services/AIAssistant/TranslateService.php

@@ -6,7 +6,7 @@ use App\Services\NissayaParser;
 use App\Services\OpenAIService;
 use App\Services\RomanizeService;
 use App\Services\AIModelService;
-use App\Http\Controllers\AuthController;
+use App\Services\AuthService;
 
 use Illuminate\Support\Facades\Log;
 use App\Http\Resources\AiModelResource;
@@ -48,7 +48,7 @@ class TranslateService
     public function setModel(string $model): self
     {
         $this->model = $this->aiModelService->getModelById($model);
-        $this->modelToken = AuthController::getUserToken($model);
+        $this->modelToken = AuthService::getUserToken($model);
         return $this;
     }
     /**
@@ -112,7 +112,7 @@ class TranslateService
                 throw new \Exception('LLM返回内容为空');
             }
             Log::info('翻译完成', [
-                'content'=>$content,
+                'content' => $content,
                 'duration' => $complete,
                 'input_tokens' => $response['usage']['prompt_tokens'] ?? 0,
                 'output_tokens' => $response['usage']['completion_tokens'] ?? 0,

+ 8 - 6
api-v13/app/Services/AiTranslateService.php

@@ -15,7 +15,7 @@ use App\Models\Sentence;
 
 use App\Http\Api\ChannelApi;
 
-use App\Http\Controllers\AuthController;
+use App\Services\AuthService;
 
 use App\Http\Api\MdRender;
 use App\Exceptions\SectionTimeoutException;
@@ -379,14 +379,16 @@ class AiTranslateService
     /**
      * 写入句子库
      */
-    private function saveSentence($sentence)
+    private function saveSentence(array $sentence, ?string $token = null)
     {
         $url = config('app.url') . '/api/v2/sentence';
 
         Log::info($this->queue . " sentence update {$url}");
-        $response = Http::timeout($this->apiTimeout)->withToken($this->modelToken)->post($url, [
-            'sentences' => [$sentence],
-        ]);
+        $response = Http::timeout($this->apiTimeout)
+            ->withToken($token ?? $this->modelToken)
+            ->post($url, [
+                'sentences' => [$sentence],
+            ]);
         if ($response->failed()) {
             Log::error($this->queue . ' sentence update failed', [
                 'url' => $url,
@@ -592,7 +594,7 @@ class AiTranslateService
 
         # ai model
         $aiModel = AiModel::findOrFail($aiAssistantId);
-        $modelToken = AuthController::getUserToken($aiModel->uid);
+        $modelToken = AuthService::getUserToken($aiModel->uid);
         $aiModel['token'] = $modelToken;
         $sumLen = 0;
         $mqData = [];

+ 1 - 1
api-v13/app/Services/ArticleService.php

@@ -44,7 +44,7 @@ class ArticleService
         return $matches[1] ?? [];
     }
 
-    public function articlesInAnthology($anthologyId)
+    public function articlesInAnthology(string $anthologyId)
     {
         $inCollection = ArticleCollection::where('collect_id', $anthologyId)
             ->select('article_id')

+ 69 - 0
api-v13/app/Services/AuthService.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Services;
+
+use App\Http\Api\UserApi;
+use App\Http\Api\AiAssistantApi;
+
+use Illuminate\Http\Request;
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+class AuthService
+{
+    public static function getUserToken(string $userUid)
+    {
+        $user = UserApi::getByUuid($userUid);
+        if (!$user) {
+            $user = AiAssistantApi::getByUuid($userUid);
+        }
+        if ($user) {
+            $ExpTime = time() + 60 * 60 * 24 * 365;
+            $key = self::getJwtKey();
+            $payload = [
+                'nbf' => time(),
+                'exp' => $ExpTime,
+                'uid' => $user['id'],
+                'id' => $user['sn'],
+            ];
+            $jwt = JWT::encode($payload, $key, 'HS512');
+            return $jwt;
+        }
+        return null;
+    }
+    public static function getJwtKey()
+    {
+        return config('mint.app.jwt_secrets_key');
+    }
+    public static function getToken(Request $request)
+    {
+        $token = $request->bearerToken();
+        return $token;
+    }
+    public static function current(Request $request)
+    {
+        $token = $request->bearerToken();
+        if ($token) {
+            try {
+
+                $jwt = JWT::decode($token, new Key(self::getJwtKey(), 'HS512'));
+            } catch (\Exception $e) {
+                return false;
+            }
+            if ($jwt->exp < time()) {
+                //过期
+                return false;
+            } else {
+                //有效的token
+                return ['user_uid' => $jwt->uid, 'user_id' => $jwt->id];
+            }
+        } else if (isset($_COOKIE['user_uid'])) {
+            return [
+                'user_uid' => $_COOKIE['user_uid'],
+                'user_id' => $_COOKIE['user_id']
+            ];
+        } else {
+            return false;
+        }
+    }
+}

+ 2 - 2
api-v13/app/Services/CollectionService.php

@@ -3,7 +3,7 @@
 namespace App\Services;
 
 use App\Models\Collection;
-use App\Http\Api\AuthApi;
+use App\Services\AuthService;
 use App\Http\Api\StudioApi;
 use App\Http\Api\ShareApi;
 use Illuminate\Http\Request;
@@ -55,7 +55,7 @@ class CollectionService
      */
     public function getMyNumber(Request $request): array
     {
-        $user = AuthApi::current($request);
+        $user = AuthService::current($request);
         if (!$user) {
             return ['error' => __('auth.failed'), 'code' => 403];
         }

+ 39 - 1
api-v13/app/Services/SentenceService.php

@@ -5,12 +5,17 @@ namespace App\Services;
 use App\Models\Sentence;
 use App\Models\SentHistory;
 use App\Models\Channel;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Http;
+use Illuminate\Http\Client\RequestException;
 
 use Illuminate\Support\Str;
 
 class SentenceService
 {
-    public function save($data)
+    protected $timeOut = 30;
+
+    public function save(array $data)
     {
         $row = Sentence::firstOrNew([
             "book_id" => $data['book_id'],
@@ -47,4 +52,37 @@ class SentenceService
         $row->modify_time = time() * 1000;
         $row->save();
     }
+
+    /**
+     *                'sentence' => [
+                    'book_id' => $sentence['id'][0],
+                    'paragraph' => $sentence['id'][1],
+                    'word_start' => $sentence['id'][2],
+                    'word_end' => $sentence['id'][3],
+                    'channel_uid' => $channelId,
+                    'content' => $prompt,
+                    'content_type' => 'markdown',
+                    'access_token' => $sentChannelInfo[1] ?? $params['token'],
+                ],
+     */
+    public function saveRpc(string $endpoint, array $sentence, string $editorToken)
+    {
+        $url = $endpoint;
+
+        Log::info(" sentence update {$url}");
+        $response = Http::timeout($this->timeOut)
+            ->withToken($editorToken)
+            ->post($url, [
+                'sentences' => [$sentence],
+            ]);
+        if ($response->failed()) {
+            Log::error(' sentence update failed', [
+                'url' => $url,
+                'data' => $response->json(),
+            ]);
+            throw new DatabaseException("sentence 数据库写入错误");
+        }
+        $count = $response->json()['data']['count'];
+        return $count;
+    }
 }

+ 62 - 0
api-v13/app/Services/TermService.php

@@ -10,6 +10,68 @@ use Illuminate\Http\Request;
 
 class TermService
 {
+    public function attachLocalName(array $categoryData, string $lang): array
+    {
+        $allNames = [];
+
+        // 收集所有 name
+        foreach ($categoryData as $item) {
+
+            if (!empty($item['category']['name'])) {
+                $allNames[] = $item['category']['name'];
+            }
+
+            foreach ($item['children'] as $child) {
+                if (!empty($child['name'])) {
+                    $allNames[] = $child['name'];
+                }
+            }
+        }
+
+        // 去重
+        $allNames = array_values(array_unique($allNames));
+
+        // 查词典
+        $terms = $this->glossaryByLemma($allNames, $lang);
+
+        // 构建映射
+        $termMap = [];
+        if ($terms) {
+            foreach ($terms as $term) {
+                $termMap[$term->word] = $term->meaning;
+            }
+        }
+
+        // 回填
+        foreach ($categoryData as &$item) {
+
+            $name = $item['category']['name'] ?? null;
+            $item['category']['local_name'] = $termMap[$name] ?? $name;
+
+            foreach ($item['children'] as &$child) {
+                $childName = $child['name'] ?? null;
+                $child['local_name'] = $termMap[$childName] ?? $childName;
+            }
+        }
+
+        unset($item, $child);
+
+        return $categoryData;
+    }
+    public function glossaryByLemma(array $words, string $lang)
+    {
+        $localTermChannel = ChannelApi::getSysChannel(
+            "_community_term_" . strtolower($lang) . "_"
+        );
+        if (!$localTermChannel) {
+            return null;
+        }
+        $result = DhammaTerm::select(['guid', 'word', 'tag', 'meaning', 'other_meaning'])
+            ->whereIn('word', $words)
+            ->where('channal', $localTermChannel)
+            ->get();
+        return $result;
+    }
     public function getCommunityGlossary($lang)
     {
         $localTermChannel = ChannelApi::getSysChannel(

+ 5 - 1
api-v13/config/mint.php

@@ -10,7 +10,10 @@ return [
     'languages' => [
         'en' => 'English',
         'zh-Hans' => '简体中文',
-        'zh-Hant' => '繁体中文',
+        'zh-Hant' => '繁體中文',
+        'my' => 'မြန်မာဘာသာ',
+        'th' => 'ภาษาไทย',
+        'si' => 'සිංහල',
     ],
     'default_language' => 'en',
     'library' => [
@@ -89,6 +92,7 @@ return [
         'paliword_index' => storage_path('resources/pali_word/index'),
         'word_statistics' => storage_path('resources/word_statistics/data'),
         'dict_text' => storage_path('resources/dict_text'),
+        'corpus' => storage_path('resources/corpus'),
     ],
 
     'admin' => [

+ 28 - 0
api-v13/database/migrations/2026_05_06_021102_add_index_to_user_dicts_parent_column.php

@@ -0,0 +1,28 @@
+<?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::table('user_dicts', function (Blueprint $table) {
+            $table->index('parent');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('user_dicts', function (Blueprint $table) {
+            $table->dropIndex(['parent']);
+        });
+    }
+};

+ 34 - 0
api-v13/database/migrations/2026_05_09_091039_add_source_type_to_channels.php

@@ -0,0 +1,34 @@
+<?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::table('channels', function (Blueprint $table) {
+            $table->string('source_type', 16)
+                ->default('original')->comment('来源类型: original-原创, reprint-转载, ai-AI生成');
+
+            // 添加普通索引
+            $table->index('source_type');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('channels', function (Blueprint $table) {
+            // 先删除索引再删除字段
+            $table->dropIndex(['source_type']);
+            $table->dropColumn('source_type');
+        });
+    }
+};

+ 33 - 0
api-v13/database/migrations/2026_05_09_091529_add_source_id_to_channels.php

@@ -0,0 +1,33 @@
+<?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::table('channels', function (Blueprint $table) {
+            $table->string('source_id', 36)
+                ->nullable()->comment('来源id 程序自动更新用 reprint-转载, ai-AI生成 使用');
+
+            // 添加普通索引
+            $table->index('source_id');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('channels', function (Blueprint $table) {
+            $table->dropIndex(['source_id']);
+            $table->dropColumn('source_id');
+        });
+    }
+};

+ 127 - 127
api-v13/public/data/category/default.json

@@ -82,35 +82,35 @@
                         "tag": ["sutta", "aṅguttaranikāya", "tikanipāta"]
                     },
                     {
-                        "name": "Catukkanipāta",
+                        "name": "catukkanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "catukkanipāta"]
                     },
                     {
-                        "name": "Pañcakanipāta",
+                        "name": "pañcakanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "pañcakanipāta"]
                     },
                     {
-                        "name": "Chakkanipāta",
+                        "name": "chakkanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "chakkanipāta"]
                     },
                     {
-                        "name": "Sattakanipāta",
+                        "name": "sattakanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "sattakanipāta"]
                     },
                     {
-                        "name": "Aṭṭhakanipāta",
+                        "name": "aṭṭhakanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "aṭṭhakanipāta"]
                     },
                     {
-                        "name": "Navakanipāta",
+                        "name": "navakanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "navakanipāta"]
                     },
                     {
-                        "name": "Dasakanipāta",
+                        "name": "dasakanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "dasakanipāta"]
                     },
                     {
-                        "name": "Ekādasakanipāta",
+                        "name": "ekādasakanipāta",
                         "tag": ["sutta", "aṅguttaranikāya", "ekādasakanipāta"]
                     }
                 ]
@@ -120,79 +120,79 @@
                 "tag": ["sutta", "khuddakanikāya"],
                 "children": [
                     {
-                        "name": "Khuddakapāṭha",
+                        "name": "khuddakapāṭha",
                         "tag": ["sutta", "khuddakanikāya", "khuddakapāṭha"]
                     },
                     {
-                        "name": "Dhammapada",
+                        "name": "dhammapada",
                         "tag": ["sutta", "khuddakanikāya", "dhammapada"]
                     },
                     {
-                        "name": "Udāna",
+                        "name": "udāna",
                         "tag": ["sutta", "khuddakanikāya", "udāna"]
                     },
                     {
-                        "name": "Itivuttaka",
+                        "name": "itivuttaka",
                         "tag": ["sutta", "khuddakanikāya", "itivuttaka"]
                     },
                     {
-                        "name": "Suttanipāta",
+                        "name": "suttanipāta",
                         "tag": ["sutta", "khuddakanikāya", "suttanipāta"]
                     },
                     {
-                        "name": "Vimānavatthu",
+                        "name": "vimānavatthu",
                         "tag": ["sutta", "khuddakanikāya", "vimānavatthu"]
                     },
                     {
-                        "name": "Petavatthu",
+                        "name": "petavatthu",
                         "tag": ["sutta", "khuddakanikāya", "petavatthu"]
                     },
                     {
-                        "name": "Theragāthā",
+                        "name": "theragāthā",
                         "tag": ["sutta", "khuddakanikāya", "theragāthā"]
                     },
                     {
-                        "name": "Therīgāthā",
+                        "name": "therīgāthā",
                         "tag": ["sutta", "khuddakanikāya", "therīgāthā"]
                     },
                     {
-                        "name": "Therāpadāna",
+                        "name": "therāpadāna",
                         "tag": ["sutta", "khuddakanikāya", "therāpadāna"]
                     },
                     {
-                        "name": "Buddhavaṃsa",
+                        "name": "buddhavaṃsa",
                         "tag": ["sutta", "khuddakanikāya", "buddhavaṃsa"]
                     },
                     {
-                        "name": "Cariyāpiṭaka",
+                        "name": "cariyāpiṭaka",
                         "tag": ["sutta", "khuddakanikāya", "cariyāpiṭaka"]
                     },
                     {
-                        "name": "Jātaka",
+                        "name": "jātaka",
                         "tag": ["sutta", "khuddakanikāya", "jātaka"]
                     },
                     {
-                        "name": "Mahāniddesa",
+                        "name": "mahāniddesa",
                         "tag": ["sutta", "khuddakanikāya", "mahāniddesa"]
                     },
                     {
-                        "name": "Cūḷaniddesa",
+                        "name": "cūḷaniddesa",
                         "tag": ["sutta", "khuddakanikāya", "cūḷaniddesa"]
                     },
                     {
-                        "name": "Paṭisambhidāmagga",
+                        "name": "paṭisambhidāmagga",
                         "tag": ["sutta", "khuddakanikāya", "paṭisambhidāmagga"]
                     },
                     {
-                        "name": "Nettippakaraṇa",
+                        "name": "nettippakaraṇa",
                         "tag": ["sutta", "khuddakanikāya", "nettippakaraṇa"]
                     },
                     {
-                        "name": "Milindapañha",
+                        "name": "milindapañha",
                         "tag": ["sutta", "khuddakanikāya", "milindapañha"]
                     },
                     {
-                        "name": "Peṭakopadesa",
+                        "name": "peṭakopadesa",
                         "tag": ["sutta", "khuddakanikāya", "peṭakopadesa"]
                     }
                 ]
@@ -224,51 +224,51 @@
                 "tag": ["vinaya", "parivāra"]
             },
             {
-                "name": "īkā",
+                "name": "īkā",
                 "tag": ["vinaya", "ṭīkā"],
                 "children": [
                     {
-                        "name": "Sāratthadīpanī",
+                        "name": "sāratthadīpanī",
                         "tag": ["vinaya", "ṭīkā", "sāratthadīpanī"]
                     },
                     {
-                        "name": "Pātimokkha",
+                        "name": "pātimokkha",
                         "tag": ["vinaya", "ṭīkā", "pātimokkha"]
                     },
                     {
-                        "name": "Vajirabuddhi",
+                        "name": "vajirabuddhi",
                         "tag": ["vinaya", "ṭīkā", "vajirabuddhi"]
                     },
                     {
-                        "name": "Vimativinodanī",
+                        "name": "vimativinodanī",
                         "tag": ["vinaya", "ṭīkā", "vimativinodanī"]
                     },
                     {
-                        "name": "Vinayavinicchayo",
+                        "name": "vinayavinicchayo",
                         "tag": ["vinaya", "ṭīkā", "vinayavinicchaya"]
                     },
                     {
-                        "name": "Vinayasaṅgaha",
+                        "name": "vinayasaṅgaha",
                         "tag": ["vinaya", "ṭīkā", "vinayasaṅgaha"]
                     },
                     {
-                        "name": "Vinayālaṅkāra",
+                        "name": "vinayālaṅkāra",
                         "tag": ["vinaya", "ṭīkā", "vinayālaṅkāra"]
                     },
                     {
-                        "name": "Uttaravinicchaya",
+                        "name": "uttaravinicchaya",
                         "tag": ["vinaya", "ṭīkā", "uttaravinicchaya"]
                     },
                     {
-                        "name": "Pācityādiyojanā",
+                        "name": "pācityādiyojanā",
                         "tag": ["vinaya", "ṭīkā", "pācityādiyojanā"]
                     },
                     {
-                        "name": "Khuddasikkhā",
+                        "name": "khuddasikkhā",
                         "tag": ["vinaya", "ṭīkā", "khuddasikkhā"]
                     },
                     {
-                        "name": "Mūlasikkhā",
+                        "name": "mūlasikkhā",
                         "tag": ["vinaya", "ṭīkā", "mūlasikkhā"]
                     }
                 ]
@@ -276,67 +276,67 @@
         ]
     },
     {
-        "name": "Abhidhammapiṭaka",
+        "name": "abhidhammapiṭaka",
         "tag": ["abhidhamma"],
         "children": [
             {
-                "name": "Dhammasaṅgaṇī",
+                "name": "dhammasaṅgaṇī",
                 "tag": ["abhidhamma", "dhammasaṅgaṇī"]
             },
             {
-                "name": "Vibhaṅga",
+                "name": "vibhaṅga",
                 "tag": ["abhidhamma", "vibhaṅga"]
             },
             {
-                "name": "Dhātukathā",
+                "name": "dhātukathā",
                 "tag": ["abhidhamma", "dhātukathā"]
             },
             {
-                "name": "Puggalapaññatti",
+                "name": "puggalapaññatti",
                 "tag": ["abhidhamma", "puggalapaññatti"]
             },
             {
-                "name": "Kathāvatthu",
+                "name": "kathāvatthu",
                 "tag": ["abhidhamma", "kathāvatthu"]
             },
             {
-                "name": "Yamaka",
+                "name": "yamaka",
                 "tag": ["abhidhamma", "yamaka"]
             },
             {
-                "name": "Paṭṭhāna",
+                "name": "paṭṭhāna",
                 "tag": ["abhidhamma", "paṭṭhāna"]
             },
             {
-                "name": "Añña",
+                "name": "añña",
                 "tag": [],
                 "children": [
                     {
-                        "name": "Abhidhammatthasaṅgaha",
+                        "name": "abhidhammatthasaṅgaha",
                         "tag": ["abhidhamma", "abhidhammatthasaṅgaha"]
                     },
                     {
-                        "name": "Abhidhammāvatāra",
+                        "name": "abhidhammāvatāra",
                         "tag": ["abhidhamma", "abhidhammāvatāra"]
                     },
                     {
-                        "name": "Nāmarūpaparicchedo",
+                        "name": "nāmarūpaparicchedo",
                         "tag": ["abhidhamma", "nāmarūpapariccheda"]
                     },
                     {
-                        "name": "Paramatthavinicchayo",
+                        "name": "paramatthavinicchayo",
                         "tag": ["abhidhamma", "paramatthavinicchaya"]
                     },
                     {
-                        "name": "Saccasaṅkhepo",
+                        "name": "saccasaṅkhepo",
                         "tag": ["abhidhamma", "saccasaṅkhepa"]
                     },
                     {
-                        "name": "Abhidhammamātikāpāḷi",
+                        "name": "abhidhammamātikāpāḷi",
                         "tag": ["abhidhamma", "abhidhammamātikāpāḷi"]
                     },
                     {
-                        "name": "Mohavicchedanī",
+                        "name": "mohavicchedanī",
                         "tag": ["abhidhamma", "mohavicchedanī"]
                     }
                 ]
@@ -344,19 +344,19 @@
         ]
     },
     {
-        "name": "Añña",
+        "name": "añña",
         "tag": ["añña"],
         "children": [
             {
-                "name": "Visuddhimagga",
+                "name": "visuddhimagga",
                 "tag": ["añña", "visuddhimagga"],
                 "children": [
                     {
-                        "name": "Visuddhimagga",
+                        "name": "visuddhimagga",
                         "tag": ["añña", "visuddhimagga", "aṭṭhakathā"]
                     },
                     {
-                        "name": "Visuddhimagga-mahāṭīkā",
+                        "name": "visuddhimagga-mahāṭīkā",
                         "tag": [
                             "añña",
                             "visuddhimagga",
@@ -365,17 +365,17 @@
                         ]
                     },
                     {
-                        "name": "Visuddhimagga-nidānakathā",
+                        "name": "visuddhimagga-nidānakathā",
                         "tag": ["añña", "visuddhimagga", "nidānakathā"]
                     }
                 ]
             },
             {
-                "name": "Saṃgāyanassa-Pucchā Vissajjanā",
+                "name": "saṃgāyanassa-pucchā vissajjanā",
                 "tag": ["añña", "saṅgayana-puccha vissajjanā"],
                 "children": [
                     {
-                        "name": "Dīghanikāya(Pu-Vi)",
+                        "name": "dīghanikāya(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -383,7 +383,7 @@
                         ]
                     },
                     {
-                        "name": "Majjhimanikāya(Pu-Vi)",
+                        "name": "majjhimanikāya(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -391,7 +391,7 @@
                         ]
                     },
                     {
-                        "name": "Saṃyuttanikāya(Pu-Vi)",
+                        "name": "saṃyuttanikāya(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -399,7 +399,7 @@
                         ]
                     },
                     {
-                        "name": "Aṅguttaranikāya(Pu-Vi)",
+                        "name": "aṅguttaranikāya(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -407,7 +407,7 @@
                         ]
                     },
                     {
-                        "name": "Vinayapiṭaka(Pu-Vi)",
+                        "name": "vinayapiṭaka(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -415,7 +415,7 @@
                         ]
                     },
                     {
-                        "name": "Abhidhammapiṭaka(Pu-Vi)",
+                        "name": "abhidhammapiṭaka(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -423,7 +423,7 @@
                         ]
                     },
                     {
-                        "name": "Aṭṭhakathā(Pu-Vi)",
+                        "name": "aṭṭhakathā(pu-vi)",
                         "tag": [
                             "añña",
                             "saṅgayana-puccha vissajjanā",
@@ -433,23 +433,23 @@
                 ]
             },
             {
-                "name": "Leḍī Sayādo Gantha-Saṅgaho",
+                "name": "leḍī sayādo gantha-saṅgaho",
                 "tag": ["añña", "leḍī sayādo"],
                 "children": [
                     {
-                        "name": "Niruttidīpanīpāṭha",
+                        "name": "niruttidīpanīpāṭha",
                         "tag": ["añña", "leḍī sayādo", "niruttidīpanīpāṭha"]
                     },
                     {
-                        "name": "Paramatthadīpanī",
+                        "name": "paramatthadīpanī",
                         "tag": ["añña", "leḍī sayādo", "paramatthadīpanī"]
                     },
                     {
-                        "name": "Anudīpanīpāṭha",
+                        "name": "anudīpanīpāṭha",
                         "tag": ["añña", "leḍī sayādo", "anudīpanīpāṭha"]
                     },
                     {
-                        "name": "Paṭṭhānuddesa dīpanīpāṭha",
+                        "name": "paṭṭhānuddesa dīpanīpāṭha",
                         "tag": [
                             "añña",
                             "leḍī sayādo",
@@ -459,11 +459,11 @@
                 ]
             },
             {
-                "name": "Buddha-Vandanā Gantha-Saṅgaho",
+                "name": "buddha-vandanā gantha-saṅgaho",
                 "tag": ["añña", "buddha-vandanā ganthasaṅgaha"],
                 "children": [
                     {
-                        "name": "Namakkāratīkā",
+                        "name": "namakkāratīkā",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -471,7 +471,7 @@
                         ]
                     },
                     {
-                        "name": "Mahāpaṇāmapāṭha",
+                        "name": "mahāpaṇāmapāṭha",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -479,7 +479,7 @@
                         ]
                     },
                     {
-                        "name": "Lakkhaṇāto Buddhathomanāgāthā",
+                        "name": "lakkhaṇāto buddhathomanāgāthā",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -487,7 +487,7 @@
                         ]
                     },
                     {
-                        "name": "Suttavandanā",
+                        "name": "suttavandanā",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -495,7 +495,7 @@
                         ]
                     },
                     {
-                        "name": "Jinālaṅkāra",
+                        "name": "jinālaṅkāra",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -503,7 +503,7 @@
                         ]
                     },
                     {
-                        "name": "Kamalāñjali",
+                        "name": "kamalāñjali",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -511,7 +511,7 @@
                         ]
                     },
                     {
-                        "name": "Pajjamadhu",
+                        "name": "pajjamadhu",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -519,7 +519,7 @@
                         ]
                     },
                     {
-                        "name": "Buddhaguṇagāthāvalī",
+                        "name": "buddhaguṇagāthāvalī",
                         "tag": [
                             "añña",
                             "buddha-vandanā ganthasaṅgaha",
@@ -529,11 +529,11 @@
                 ]
             },
             {
-                "name": "Vaṃsa Gantha-Saṅgaho",
+                "name": "vaṃsa gantha-saṅgaho",
                 "tag": ["añña", "vaṃsa ganthasaṅgaha"],
                 "children": [
                     {
-                        "name": "Cūḷaganthavaṃsa",
+                        "name": "cūḷaganthavaṃsa",
                         "tag": [
                             "añña",
                             "vaṃsa ganthasaṅgaha",
@@ -549,17 +549,17 @@
                         ]
                     },
                     {
-                        "name": "Mahāvaṃsa",
+                        "name": "mahāvaṃsa",
                         "tag": ["añña", "vaṃsa ganthasaṅgaha", "mahāvaṃsapāḷi"]
                     }
                 ]
             },
             {
-                "name": "Byākaraṇa Gantha-Saṅgaho",
+                "name": "byākaraṇa gantha-saṅgaho",
                 "tag": ["añña", "byākaraṇa ganthasaṅgaha"],
                 "children": [
                     {
-                        "name": "Moggallānasuttapāṭha",
+                        "name": "moggallānasuttapāṭha",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -567,7 +567,7 @@
                         ]
                     },
                     {
-                        "name": "Moggallānabyākaraṇa",
+                        "name": "moggallānabyākaraṇa",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -575,7 +575,7 @@
                         ]
                     },
                     {
-                        "name": "Kaccāyanabyākaraṇaṃ",
+                        "name": "kaccāyanabyākaraṇaṃ",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -583,7 +583,7 @@
                         ]
                     },
                     {
-                        "name": "Saddanītippakaraṇaṃ",
+                        "name": "saddanītippakaraṇaṃ",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -591,7 +591,7 @@
                         ]
                     },
                     {
-                        "name": "Padarūpasiddhi",
+                        "name": "padarūpasiddhi",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -599,7 +599,7 @@
                         ]
                     },
                     {
-                        "name": "Moggallāna pañcikā ṭīkā",
+                        "name": "moggallāna pañcikā ṭīkā",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -607,7 +607,7 @@
                         ]
                     },
                     {
-                        "name": "Payogasiddhipāṭha",
+                        "name": "payogasiddhipāṭha",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -615,11 +615,11 @@
                         ]
                     },
                     {
-                        "name": "Vuttodayaṃ",
+                        "name": "vuttodayaṃ",
                         "tag": ["añña", "byākaraṇa ganthasaṅgaha", "vuttodaya"]
                     },
                     {
-                        "name": "Abhidhānappadīpikāpāṭha",
+                        "name": "abhidhānappadīpikāpāṭha",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -627,7 +627,7 @@
                         ]
                     },
                     {
-                        "name": "Subodhālaṅkārapāṭha",
+                        "name": "subodhālaṅkārapāṭha",
                         "tag": [
                             "añña",
                             "byākaraṇa ganthasaṅgaha",
@@ -635,49 +635,49 @@
                         ]
                     },
                     {
-                        "name": "Bālāvatāra",
+                        "name": "bālāvatāra",
                         "tag": ["añña", "byākaraṇa ganthasaṅgaha", "bālāvatāra"]
                     }
                 ]
             },
             {
-                "name": "Nīti-Gantha-Saṅgaho",
+                "name": "nīti-gantha-saṅgaho",
                 "tag": ["añña", "nīti-ganthasaṅgaha"],
                 "children": [
                     {
-                        "name": "Kavidappaṇanīti",
+                        "name": "kavidappaṇanīti",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "kavidappaṇanīti"]
                     },
                     {
-                        "name": "Nītimañjarī",
+                        "name": "nītimañjarī",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "nītimañjarī"]
                     },
                     {
-                        "name": "Dhammanīti",
+                        "name": "dhammanīti",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "dhammanīti"]
                     },
                     {
-                        "name": "Mahārahanīti",
+                        "name": "mahārahanīti",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "mahārahanīti"]
                     },
                     {
-                        "name": "Lokanīti",
+                        "name": "lokanīti",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "lokanīti"]
                     },
                     {
-                        "name": "Suttantanīti",
+                        "name": "suttantanīti",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "suttantanīti"]
                     },
                     {
-                        "name": "Sūrassatīnīti",
+                        "name": "sūrassatīnīti",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "sūrassatīnīti"]
                     },
                     {
-                        "name": "Cāṇakyanītipāḷi",
+                        "name": "cāṇakyanītipāḷi",
                         "tag": ["añña", "nīti-ganthasaṅgaha", "cāṇakyanītipāḷi"]
                     },
                     {
-                        "name": "Naradakkhadīpanī",
+                        "name": "naradakkhadīpanī",
                         "tag": [
                             "añña",
                             "nīti-ganthasaṅgaha",
@@ -685,7 +685,7 @@
                         ]
                     },
                     {
-                        "name": "Caturārakkhadīpanī",
+                        "name": "caturārakkhadīpanī",
                         "tag": [
                             "añña",
                             "nīti-ganthasaṅgaha",
@@ -695,15 +695,15 @@
                 ]
             },
             {
-                "name": "Pakiṇṇaka-Gantha-Saṅgaho",
+                "name": "pakiṇṇaka-gantha-saṅgaho",
                 "tag": ["añña", "pakiṇṇaka-ganthasaṅgaha"],
                 "children": [
                     {
-                        "name": "Rasavāhinī",
+                        "name": "rasavāhinī",
                         "tag": ["añña", "pakiṇṇaka-ganthasaṅgaha", "rasavāhinī"]
                     },
                     {
-                        "name": "Sīmavisodhanī",
+                        "name": "sīmavisodhanī",
                         "tag": [
                             "añña",
                             "pakiṇṇaka-ganthasaṅgaha",
@@ -711,7 +711,7 @@
                         ]
                     },
                     {
-                        "name": "Vessantarāgīti",
+                        "name": "vessantarāgīti",
                         "tag": [
                             "añña",
                             "pakiṇṇaka-ganthasaṅgaha",
@@ -721,11 +721,11 @@
                 ]
             },
             {
-                "name": "Sihaḷa-Gantha-Saṅgaho",
+                "name": "sihaḷa-gantha-saṅgaho",
                 "tag": ["añña", "sihaḷa-ganthasaṅgaha"],
                 "children": [
                     {
-                        "name": "Moggallāna vuttivivaraṇapañcikā",
+                        "name": "moggallāna vuttivivaraṇapañcikā",
                         "tag": [
                             "añña",
                             "sihaḷa-ganthasaṅgaha",
@@ -733,15 +733,15 @@
                         ]
                     },
                     {
-                        "name": "Thupavaṃsa",
+                        "name": "thupavaṃsa",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "thupavaṃsa"]
                     },
                     {
-                        "name": "Dāṭhāvaṃsa",
+                        "name": "dāṭhāvaṃsa",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "dāṭhāvaṃsa"]
                     },
                     {
-                        "name": "Dhātupāṭha vilāsiniyā",
+                        "name": "dhātupāṭha vilāsiniyā",
                         "tag": [
                             "añña",
                             "sihaḷa-ganthasaṅgaha",
@@ -749,11 +749,11 @@
                         ]
                     },
                     {
-                        "name": "Dhātuvaṃsa",
+                        "name": "dhātuvaṃsa",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "dhātuvaṃsa"]
                     },
                     {
-                        "name": "Hatthavanagallavihāravaṃsa",
+                        "name": "hatthavanagallavihāravaṃsa",
                         "tag": [
                             "añña",
                             "sihaḷa-ganthasaṅgaha",
@@ -761,15 +761,15 @@
                         ]
                     },
                     {
-                        "name": "Jinacaritaya",
+                        "name": "jinacaritaya",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "jinacaritaya"]
                     },
                     {
-                        "name": "Jinavaṃsadīpaṃ",
+                        "name": "jinavaṃsadīpaṃ",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "jinavaṃsadīpa"]
                     },
                     {
-                        "name": "Telakaṭāhagāthā",
+                        "name": "telakaṭāhagāthā",
                         "tag": [
                             "añña",
                             "sihaḷa-ganthasaṅgaha",
@@ -777,15 +777,15 @@
                         ]
                     },
                     {
-                        "name": "Milidaṭīkā",
+                        "name": "milidaṭīkā",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "milidaṭīkā"]
                     },
                     {
-                        "name": "Padamañjarī",
+                        "name": "padamañjarī",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "padamañjarī"]
                     },
                     {
-                        "name": "Padasādhana",
+                        "name": "padasādhana",
                         "tag": ["añña", "sihaḷa-ganthasaṅgaha", "padasādhana"]
                     },
                     {
@@ -797,7 +797,7 @@
                         ]
                     },
                     {
-                        "name": "Kaccāyanadhātumañjūsā",
+                        "name": "kaccāyanadhātumañjūsā",
                         "tag": [
                             "añña",
                             "sihaḷa-ganthasaṅgaha",
@@ -805,7 +805,7 @@
                         ]
                     },
                     {
-                        "name": "Samantakūṭavaṇṇanā",
+                        "name": "samantakūṭavaṇṇanā",
                         "tag": [
                             "añña",
                             "sihaḷa-ganthasaṅgaha",

+ 11 - 1
api-v13/resources/lang/en/site.php

@@ -13,10 +13,20 @@ return [
     'logo' => '/logo.png',
     'title' => 'wikipali',
     'subhead' => 'wikipali',
-    'keywords' => ['pali','buddhistm'],
+    'keywords' => ['pali', 'buddhistm'],
     'description' => 'wikipali',
     'copyright' => 'iapt 2022',
     'author.name' => 'iapt',
     'author.email' => 'visuddhindand@gmail.com',
+    'nav' => [
+        'home' => 'Home',
+        'tipitaka' => 'Tipitaka',
+        'wiki' => 'Wiki',
+        'anthology' => 'Anthology',
+        'download' => 'Download',
 
+        'menu' => 'Menu',
+        'open_menu' => 'Open menu',
+        'close_menu' => 'Close menu',
+    ],
 ];

+ 20 - 0
api-v13/resources/lang/my/auth.php

@@ -0,0 +1,20 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used during authentication for various
+    | messages that we need to display to the user. You are free to modify
+    | these language lines according to your application's requirements.
+    |
+    */
+
+    'failed' => 'These credentials do not match our records.',
+    'password' => 'The provided password is incorrect.',
+    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
+
+];

+ 5 - 0
api-v13/resources/lang/my/buttons.php

@@ -0,0 +1,5 @@
+<?php
+return [
+    'more' => 'More',
+    'online-read' =>  'Online Read',
+];

+ 7 - 0
api-v13/resources/lang/my/labels.php

@@ -0,0 +1,7 @@
+<?php
+return [
+    'home' => 'Home',
+    'translation' => 'Translation',
+    'original' => 'Original',
+    'nissaya' => 'Nissaya'
+];

+ 10 - 0
api-v13/resources/lang/my/language.php

@@ -0,0 +1,10 @@
+<?php
+// api-v12/resources/lang/en/language.php
+return [
+    'en' => 'English',
+    'pali' => 'pali',
+    'zh' => '中文',
+    'zh-Hans' => '简体中文',
+    'zh-Hant' => '繁体中文',
+    'my' => 'မြန်မာဘာသာ',
+];

+ 19 - 0
api-v13/resources/lang/my/pagination.php

@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Pagination Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used by the paginator library to build
+    | the simple pagination links. You are free to change them to anything
+    | you want to customize your views to better match your application.
+    |
+    */
+
+    'previous' => '&laquo; Previous',
+    'next' => 'Next &raquo;',
+
+];

+ 22 - 0
api-v13/resources/lang/my/passwords.php

@@ -0,0 +1,22 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reset Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are the default lines which match reasons
+    | that are given by the password broker for a password update attempt
+    | has failed, such as for an invalid token or invalid new password.
+    |
+    */
+
+    'reset' => 'Your password has been reset!',
+    'sent' => 'We have emailed your password reset link!',
+    'throttled' => 'Please wait before retrying.',
+    'token' => 'This password reset token is invalid.',
+    'user' => "We can't find a user with that email address.",
+
+];

+ 32 - 0
api-v13/resources/lang/my/site.php

@@ -0,0 +1,32 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | site Language Lines
+    |--------------------------------------------------------------------------
+    |
+    |
+    */
+
+    'logo' => '/logo.png',
+    'title' => 'wikipali',
+    'subhead' => 'wikipali',
+    'keywords' => ['pali', 'buddhistm'],
+    'description' => 'wikipali',
+    'copyright' => 'iapt 2022',
+    'author.name' => 'iapt',
+    'author.email' => 'visuddhindand@gmail.com',
+    'nav' => [
+        'home' => 'ပင်မစာမျက်နှာ',
+        'tipitaka' => 'တိပိဋက',
+        'wiki' => 'ဗဟုသုတ',
+        'anthology' => 'စာစု',
+        'download' => 'ဒေါင်းလုဒ်',
+
+        'menu' => 'မီနူး',
+        'open_menu' => 'မီနူးဖွင့်ရန်',
+        'close_menu' => 'မီနူးပိတ်ရန်',
+    ],
+];

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio