KnowledgeGraphManagement.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <?php
  2. namespace App\Filament\Pages;
  3. use App\Services\KnowledgeGraphService;
  4. use Filament\Actions\Action;
  5. use Filament\Forms\Components\FileUpload;
  6. use Filament\Forms\Components\Select;
  7. use Filament\Forms\Components\Textarea;
  8. use Filament\Forms\Components\TextInput;
  9. use Filament\Notifications\Notification;
  10. use Filament\Pages\Page;
  11. use Filament\Support\Enums\ActionSize;
  12. use Illuminate\Support\Facades\Storage;
  13. class KnowledgeGraphManagement extends Page
  14. {
  15. protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-rectangle-stack';
  16. protected static string|\UnitEnum|null $navigationGroup = '资源';
  17. protected static ?string $navigationLabel = '知识图谱管理';
  18. protected static ?int $navigationSort = 6;
  19. protected static ?string $title = '知识图谱管理';
  20. protected string $view = 'filament.pages.knowledge-graph-management';
  21. public array $knowledgePoints = [];
  22. public function mount(KnowledgeGraphService $service): void
  23. {
  24. $result = $service->listKnowledgePoints(1, 1000);
  25. $this->knowledgePoints = $result['data'] ?? [];
  26. }
  27. public function edit(string $code): void
  28. {
  29. $this->mountAction('edit', ['code' => $code]);
  30. }
  31. public function delete(string $code): void
  32. {
  33. $this->mountAction('delete', ['code' => $code]);
  34. }
  35. protected function getHeaderActions(): array
  36. {
  37. return [
  38. Action::make('create')
  39. ->label('新增知识点')
  40. ->icon('heroicon-o-plus')
  41. ->color('primary')
  42. ->form([
  43. TextInput::make('cn_name')->label('中文名称')->required(),
  44. TextInput::make('en_name')->label('英文名称'),
  45. TextInput::make('kp_code')->label('知识点编码')->placeholder('自动生成或手动输入'),
  46. Select::make('phase')->label('学段')->options([
  47. '小学' => '小学', '初中' => '初中', '高中' => '高中'
  48. ])->required(),
  49. Select::make('grade')->label('年级')->options([
  50. 7 => '七年级', 8 => '八年级', 9 => '九年级'
  51. ]),
  52. TextInput::make('category')->label('分类')->default('数学'),
  53. TextInput::make('importance')->label('重要性')->numeric()->default(5),
  54. Textarea::make('description')->label('描述'),
  55. ])
  56. ->action(function (array $data, KnowledgeGraphService $service) {
  57. if ($service->createKnowledgePoint($data)) {
  58. Notification::make()->title('创建成功')->success()->send();
  59. $this->mount($service);
  60. } else {
  61. Notification::make()->title('创建失败')->danger()->send();
  62. }
  63. }),
  64. Action::make('import')
  65. ->label('导入图谱数据')
  66. ->icon('heroicon-o-arrow-up-tray')
  67. ->color('success')
  68. ->form([
  69. FileUpload::make('tree_file')
  70. ->label('Tree JSON (知识点结构)')
  71. ->helperText('上传包含知识点层次结构的 JSON 文件')
  72. ->required()
  73. ->storeFiles(false),
  74. FileUpload::make('edges_file')
  75. ->label('Edges JSON (依赖关系)')
  76. ->helperText('上传包含知识点间关系的 JSON 文件')
  77. ->required()
  78. ->storeFiles(false),
  79. ])
  80. ->action(function (array $data, KnowledgeGraphService $service) {
  81. try {
  82. $treeFile = $data['tree_file'];
  83. $edgesFile = $data['edges_file'];
  84. if (is_array($treeFile)) $treeFile = reset($treeFile);
  85. if (is_array($edgesFile)) $edgesFile = reset($edgesFile);
  86. // 详细日志
  87. \Log::info('开始导入知识图谱', [
  88. 'tree_file' => is_object($treeFile) ? get_class($treeFile) : $treeFile,
  89. 'edges_file' => is_object($edgesFile) ? get_class($edgesFile) : $edgesFile
  90. ]);
  91. // 处理 TemporaryUploadedFile 对象
  92. $treeContent = null;
  93. $edgesContent = null;
  94. // 读取 Tree 文件
  95. if ($treeFile instanceof \Illuminate\Http\UploadedFile) {
  96. // 从临时上传文件读取
  97. $treeContent = json_decode(file_get_contents($treeFile->getRealPath()), true);
  98. } elseif (is_string($treeFile)) {
  99. // 从字符串路径读取
  100. if (Storage::disk('public')->exists($treeFile)) {
  101. $treeContent = json_decode(Storage::disk('public')->get($treeFile), true);
  102. } elseif (file_exists($treeFile)) {
  103. $treeContent = json_decode(file_get_contents($treeFile), true);
  104. } else {
  105. throw new \Exception("Tree文件不存在: {$treeFile}");
  106. }
  107. } else {
  108. throw new \Exception("无效的Tree文件类型");
  109. }
  110. // 读取 Edges 文件
  111. if ($edgesFile instanceof \Illuminate\Http\UploadedFile) {
  112. $edgesContent = json_decode(file_get_contents($edgesFile->getRealPath()), true);
  113. } elseif (is_string($edgesFile)) {
  114. if (Storage::disk('public')->exists($edgesFile)) {
  115. $edgesContent = json_decode(Storage::disk('public')->get($edgesFile), true);
  116. } elseif (file_exists($edgesFile)) {
  117. $edgesContent = json_decode(file_get_contents($edgesFile), true);
  118. } else {
  119. throw new \Exception("Edges文件不存在: {$edgesFile}");
  120. }
  121. } else {
  122. throw new \Exception("无效的Edges文件类型");
  123. }
  124. if (json_last_error() !== JSON_ERROR_NONE) {
  125. throw new \Exception('JSON解析错误: ' . json_last_error_msg());
  126. }
  127. if (!$treeContent) {
  128. throw new \Exception('Tree JSON解析失败或为空');
  129. }
  130. if (!$edgesContent) {
  131. throw new \Exception('Edges JSON解析失败或为空');
  132. }
  133. \Log::info('文件解析成功', [
  134. 'tree_keys' => array_keys($treeContent),
  135. 'edges_count' => is_array($edgesContent) ? count($edgesContent) : 0
  136. ]);
  137. if ($service->importGraph($treeContent, $edgesContent)) {
  138. Notification::make()->title('导入成功')->success()->send();
  139. $this->mount($service);
  140. } else {
  141. Notification::make()->title('导入失败')->body('请查看日志获取详细信息')->danger()->send();
  142. }
  143. } catch (\Exception $e) {
  144. \Log::error('导入知识图谱失败', [
  145. 'error' => $e->getMessage(),
  146. 'trace' => $e->getTraceAsString()
  147. ]);
  148. Notification::make()
  149. ->title('导入失败')
  150. ->body($e->getMessage())
  151. ->danger()
  152. ->persistent()
  153. ->send();
  154. }
  155. })
  156. ];
  157. }
  158. public function editAction(): Action
  159. {
  160. return Action::make('edit')
  161. ->label('编辑')
  162. ->icon('heroicon-o-pencil-square')
  163. ->color('warning')
  164. ->modalHeading('编辑知识点')
  165. ->modalDescription('修改知识点的基本信息和属性')
  166. ->form([
  167. TextInput::make('cn_name')->label('中文名称')->required(),
  168. TextInput::make('en_name')->label('英文名称'),
  169. Select::make('phase')->label('学段')->options([
  170. '小学' => '小学', '初中' => '初中', '高中' => '高中'
  171. ])->required(),
  172. Select::make('grade')->label('年级')->options([
  173. 7 => '七年级', 8 => '八年级', 9 => '九年级'
  174. ]),
  175. TextInput::make('category')->label('分类'),
  176. TextInput::make('importance')->label('重要性')->numeric(),
  177. Textarea::make('description')->label('描述'),
  178. ])
  179. ->fillForm(function (array $arguments, KnowledgeGraphService $service) {
  180. $code = $arguments['code'];
  181. $data = $service->getKnowledgePoint($code);
  182. return $data ?? [];
  183. })
  184. ->action(function (array $data, array $arguments, KnowledgeGraphService $service) {
  185. if ($service->updateKnowledgePoint($arguments['code'], $data)) {
  186. Notification::make()->title('更新成功')->success()->send();
  187. $this->mount($service);
  188. } else {
  189. Notification::make()->title('更新失败')->danger()->send();
  190. }
  191. });
  192. }
  193. public function deleteAction(): Action
  194. {
  195. return Action::make('delete')
  196. ->label('删除')
  197. ->icon('heroicon-o-trash')
  198. ->color('error')
  199. ->requiresConfirmation()
  200. ->modalHeading('删除知识点')
  201. ->modalDescription('此操作将永久删除该知识点及其所有关联数据,此操作不可恢复!')
  202. ->modalConfirmButtonLabel('确认删除')
  203. ->action(function (array $arguments, KnowledgeGraphService $service) {
  204. if ($service->deleteKnowledgePoint($arguments['code'])) {
  205. Notification::make()->title('删除成功')->success()->send();
  206. $this->mount($service);
  207. } else {
  208. Notification::make()->title('删除失败')->danger()->send();
  209. }
  210. });
  211. }
  212. }