MistakeBookController.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Http\Controllers\Controller;
  4. use App\Services\MistakeBookService;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Http\JsonResponse;
  7. use Illuminate\Support\Facades\Log;
  8. class MistakeBookController extends Controller
  9. {
  10. protected MistakeBookService $mistakeBookService;
  11. public function __construct(MistakeBookService $mistakeBookService)
  12. {
  13. $this->mistakeBookService = $mistakeBookService;
  14. }
  15. /**
  16. * 获取错题列表
  17. *
  18. * @param Request $request
  19. * @return JsonResponse
  20. */
  21. public function listMistakes(Request $request): JsonResponse
  22. {
  23. try {
  24. $params = $request->only([
  25. 'student_id',
  26. 'start_time',
  27. 'end_time',
  28. 'kp_ids',
  29. 'skill_ids',
  30. 'error_types',
  31. 'time_range',
  32. 'page',
  33. 'per_page',
  34. 'sort_by'
  35. ]);
  36. // 设置默认值
  37. $params['page'] = (int) ($params['page'] ?? 1);
  38. $params['per_page'] = (int) ($params['per_page'] ?? 20);
  39. $params['sort_by'] = $params['sort_by'] ?? 'created_at_desc';
  40. // 调用服务层获取错题列表
  41. $result = $this->mistakeBookService->listMistakes($params);
  42. Log::info('获取错题列表成功', [
  43. 'student_id' => $params['student_id'] ?? 'N/A',
  44. 'page' => $params['page'],
  45. 'per_page' => $params['per_page'],
  46. 'total' => $result['meta']['total'] ?? 0
  47. ]);
  48. return response()->json([
  49. 'success' => true,
  50. 'data' => $result['data'] ?? [],
  51. 'meta' => $result['meta'] ?? [],
  52. 'statistics' => $result['statistics'] ?? null
  53. ]);
  54. } catch (\Exception $e) {
  55. Log::error('获取错题列表失败', [
  56. 'error' => $e->getMessage(),
  57. 'trace' => $e->getTraceAsString(),
  58. 'params' => $request->all()
  59. ]);
  60. return response()->json([
  61. 'success' => false,
  62. 'message' => '获取错题列表失败: ' . $e->getMessage(),
  63. 'data' => [],
  64. 'meta' => [
  65. 'total' => 0,
  66. 'page' => (int) ($request->get('page', 1)),
  67. 'per_page' => (int) ($request->get('per_page', 20))
  68. ]
  69. ], 500);
  70. }
  71. }
  72. /**
  73. * 新增错题
  74. */
  75. public function addMistake(Request $request): JsonResponse
  76. {
  77. // 优先从body获取数据,不使用query params
  78. $payload = $request->json()->all();
  79. if (empty($payload)) {
  80. $payload = $request->all();
  81. }
  82. $validator = validator($payload, [
  83. 'student_id' => 'required|string|min:1',
  84. 'question_id' => 'required|string|min:1',
  85. 'my_answer' => 'nullable|string',
  86. 'correct_answer' => 'nullable|string',
  87. 'source' => 'nullable|string|in:exam,homework,test,quiz,other',
  88. 'happened_at' => 'nullable|date',
  89. 'idempotency_key' => 'nullable|string|max:255',
  90. ]);
  91. if ($validator->fails()) {
  92. return response()->json([
  93. 'success' => false,
  94. 'message' => '参数错误',
  95. 'errors' => $validator->errors()->toArray(),
  96. ], 422);
  97. }
  98. try {
  99. $validated = $validator->validated();
  100. $result = $this->mistakeBookService->createMistake($validated);
  101. return response()->json([
  102. 'success' => true,
  103. 'data' => $result,
  104. 'duplicate' => $result['duplicate'] ?? false,
  105. ]);
  106. } catch (\Exception $e) {
  107. Log::error('新增错题失败', [
  108. 'error' => $e->getMessage(),
  109. 'payload' => $payload,
  110. ]);
  111. return response()->json([
  112. 'success' => false,
  113. 'message' => '新增错题失败: ' . $e->getMessage(),
  114. ], 500);
  115. }
  116. }
  117. /**
  118. * 获取单条错题详情
  119. *
  120. * @param string $mistakeId
  121. * @param Request $request
  122. * @return JsonResponse
  123. */
  124. public function getMistakeDetail(string $mistakeId, Request $request): JsonResponse
  125. {
  126. try {
  127. $studentId = $request->get('student_id');
  128. $mistake = $this->mistakeBookService->getMistakeDetail($mistakeId, $studentId);
  129. if (empty($mistake)) {
  130. return response()->json([
  131. 'success' => false,
  132. 'message' => '错题记录不存在'
  133. ], 404);
  134. }
  135. Log::info('获取错题详情成功', [
  136. 'mistake_id' => $mistakeId,
  137. 'student_id' => $studentId ?? 'N/A'
  138. ]);
  139. return response()->json([
  140. 'success' => true,
  141. 'data' => $mistake
  142. ]);
  143. } catch (\Exception $e) {
  144. Log::error('获取错题详情失败', [
  145. 'mistake_id' => $mistakeId,
  146. 'error' => $e->getMessage(),
  147. 'trace' => $e->getTraceAsString()
  148. ]);
  149. return response()->json([
  150. 'success' => false,
  151. 'message' => '获取错题详情失败: ' . $e->getMessage()
  152. ], 500);
  153. }
  154. }
  155. /**
  156. * 获取错题统计概要
  157. *
  158. * @param Request $request
  159. * @return JsonResponse
  160. */
  161. public function getSummary(Request $request): JsonResponse
  162. {
  163. try {
  164. $studentId = $request->get('student_id');
  165. if (!$studentId) {
  166. return response()->json([
  167. 'success' => false,
  168. 'message' => '缺少必要参数:student_id'
  169. ], 400);
  170. }
  171. $startTime = $request->get('start_time');
  172. $endTime = $request->get('end_time');
  173. // 构建查询参数
  174. $params = ['student_id' => $studentId];
  175. if ($startTime) {
  176. $params['start_time'] = $startTime;
  177. }
  178. if ($endTime) {
  179. $params['end_time'] = $endTime;
  180. }
  181. // 获取错题列表(用于计算统计信息)
  182. $mistakesResult = $this->mistakeBookService->listMistakes($params);
  183. // 获取概要统计
  184. $summary = $this->mistakeBookService->summarize($studentId);
  185. Log::info('获取错题概要统计成功', [
  186. 'student_id' => $studentId,
  187. 'total_mistakes' => $summary['total'] ?? 0
  188. ]);
  189. return response()->json([
  190. 'success' => true,
  191. 'data' => $summary // ✅ 直接返回统计信息,不嵌套
  192. ]);
  193. } catch (\Exception $e) {
  194. Log::error('获取错题概要统计失败', [
  195. 'student_id' => $request->get('student_id'),
  196. 'error' => $e->getMessage(),
  197. 'trace' => $e->getTraceAsString()
  198. ]);
  199. return response()->json([
  200. 'success' => false,
  201. 'message' => '获取错题概要统计失败: ' . $e->getMessage()
  202. ], 500);
  203. }
  204. }
  205. /**
  206. * 获取错误模式分析
  207. *
  208. * @param Request $request
  209. * @return JsonResponse
  210. */
  211. public function getMistakePatterns(Request $request): JsonResponse
  212. {
  213. try {
  214. $studentId = $request->get('student_id');
  215. if (!$studentId) {
  216. return response()->json([
  217. 'success' => false,
  218. 'message' => '缺少必要参数:student_id'
  219. ], 400);
  220. }
  221. $patterns = $this->mistakeBookService->getMistakePatterns($studentId);
  222. Log::info('获取错误模式分析成功', [
  223. 'student_id' => $studentId,
  224. 'top_kps_count' => count($patterns['top_kps'] ?? [])
  225. ]);
  226. return response()->json([
  227. 'success' => true,
  228. 'data' => $patterns
  229. ]);
  230. } catch (\Exception $e) {
  231. Log::error('获取错误模式分析失败', [
  232. 'student_id' => $request->get('student_id'),
  233. 'error' => $e->getMessage(),
  234. 'trace' => $e->getTraceAsString()
  235. ]);
  236. return response()->json([
  237. 'success' => false,
  238. 'message' => '获取错误模式分析失败: ' . $e->getMessage()
  239. ], 500);
  240. }
  241. }
  242. /**
  243. * 收藏/取消收藏错题
  244. *
  245. * @param string $mistakeId
  246. * @param Request $request
  247. * @return JsonResponse
  248. */
  249. public function toggleFavorite(string $mistakeId, Request $request): JsonResponse
  250. {
  251. try {
  252. $data = $request->only(['favorite']);
  253. $favorite = $data['favorite'] ?? true;
  254. $result = $this->mistakeBookService->toggleFavorite($mistakeId, $favorite);
  255. Log::info(($favorite ? '收藏' : '取消收藏') . '错题', [
  256. 'mistake_id' => $mistakeId,
  257. 'favorite' => $favorite,
  258. 'result' => $result
  259. ]);
  260. return response()->json([
  261. 'success' => $result,
  262. 'message' => $result ? '操作成功' : '操作失败',
  263. 'data' => [
  264. 'mistake_id' => $mistakeId,
  265. 'favorite' => $favorite
  266. ]
  267. ]);
  268. } catch (\Exception $e) {
  269. Log::error('收藏错题失败', [
  270. 'mistake_id' => $mistakeId,
  271. 'error' => $e->getMessage(),
  272. 'trace' => $e->getTraceAsString()
  273. ]);
  274. return response()->json([
  275. 'success' => false,
  276. 'message' => '收藏错题失败: ' . $e->getMessage()
  277. ], 500);
  278. }
  279. }
  280. /**
  281. * 标记错题已复习
  282. *
  283. * @param string $mistakeId
  284. * @return JsonResponse
  285. */
  286. public function markReviewed(string $mistakeId): JsonResponse
  287. {
  288. try {
  289. $result = $this->mistakeBookService->markReviewed($mistakeId);
  290. Log::info('标记错题已复习', [
  291. 'mistake_id' => $mistakeId,
  292. 'result' => $result
  293. ]);
  294. return response()->json([
  295. 'success' => $result,
  296. 'message' => $result ? '操作成功' : '操作失败',
  297. 'data' => [
  298. 'mistake_id' => $mistakeId,
  299. 'reviewed' => $result,
  300. 'reviewed_at' => $result ? now()->toISOString() : null
  301. ]
  302. ]);
  303. } catch (\Exception $e) {
  304. Log::error('标记错题已复习失败', [
  305. 'mistake_id' => $mistakeId,
  306. 'error' => $e->getMessage(),
  307. 'trace' => $e->getTraceAsString()
  308. ]);
  309. return response()->json([
  310. 'success' => false,
  311. 'message' => '标记错题已复习失败: ' . $e->getMessage()
  312. ], 500);
  313. }
  314. }
  315. /**
  316. * 加入重练清单
  317. *
  318. * @param string $mistakeId
  319. * @return JsonResponse
  320. */
  321. public function addToRetryList(string $mistakeId): JsonResponse
  322. {
  323. try {
  324. $result = $this->mistakeBookService->addToRetryList($mistakeId);
  325. Log::info('加入重练清单', [
  326. 'mistake_id' => $mistakeId,
  327. 'result' => $result
  328. ]);
  329. return response()->json([
  330. 'success' => $result,
  331. 'message' => $result ? '操作成功' : '操作失败',
  332. 'data' => [
  333. 'mistake_id' => $mistakeId,
  334. 'added' => $result
  335. ]
  336. ]);
  337. } catch (\Exception $e) {
  338. Log::error('加入重练清单失败', [
  339. 'mistake_id' => $mistakeId,
  340. 'error' => $e->getMessage(),
  341. 'trace' => $e->getTraceAsString()
  342. ]);
  343. return response()->json([
  344. 'success' => false,
  345. 'message' => '加入重练清单失败: ' . $e->getMessage()
  346. ], 500);
  347. }
  348. }
  349. /**
  350. * 获取题目正确率
  351. *
  352. * @param string $questionId
  353. * @return JsonResponse
  354. */
  355. public function getQuestionAccuracy(string $questionId): JsonResponse
  356. {
  357. try {
  358. $accuracy = $this->mistakeBookService->getQuestionAccuracy($questionId);
  359. Log::info('获取题目正确率成功', [
  360. 'question_id' => $questionId,
  361. 'accuracy' => $accuracy
  362. ]);
  363. return response()->json([
  364. 'success' => true,
  365. 'data' => [
  366. 'question_id' => $questionId,
  367. 'accuracy' => $accuracy,
  368. 'correct_attempts' => null // LearningAnalytics不返回具体次数
  369. ]
  370. ]);
  371. } catch (\Exception $e) {
  372. Log::error('获取题目正确率失败', [
  373. 'question_id' => $questionId,
  374. 'error' => $e->getMessage(),
  375. 'trace' => $e->getTraceAsString()
  376. ]);
  377. return response()->json([
  378. 'success' => false,
  379. 'message' => '获取题目正确率失败: ' . $e->getMessage()
  380. ], 500);
  381. }
  382. }
  383. /**
  384. * 推荐练习题
  385. *
  386. * @param Request $request
  387. * @return JsonResponse
  388. */
  389. public function recommendPractice(Request $request): JsonResponse
  390. {
  391. try {
  392. $studentId = $request->get('student_id');
  393. $kpIds = $request->get('kp_ids', []);
  394. $skillIds = $request->get('skill_ids', []);
  395. if (!$studentId) {
  396. return response()->json([
  397. 'success' => false,
  398. 'message' => '缺少必要参数:student_id'
  399. ], 400);
  400. }
  401. $recommendations = $this->mistakeBookService->recommendPractice(
  402. $studentId,
  403. $kpIds,
  404. $skillIds
  405. );
  406. Log::info('获取推荐练习题成功', [
  407. 'student_id' => $studentId,
  408. 'kp_ids' => $kpIds,
  409. 'recommendations_count' => count($recommendations['data'] ?? [])
  410. ]);
  411. return response()->json([
  412. 'success' => true,
  413. 'data' => $recommendations['data'] ?? []
  414. ]);
  415. } catch (\Exception $e) {
  416. Log::error('获取推荐练习题失败', [
  417. 'student_id' => $request->get('student_id'),
  418. 'error' => $e->getMessage(),
  419. 'trace' => $e->getTraceAsString()
  420. ]);
  421. return response()->json([
  422. 'success' => false,
  423. 'message' => '获取推荐练习题失败: ' . $e->getMessage()
  424. ], 500);
  425. }
  426. }
  427. /**
  428. * 获取错题本快照数据(用于仪表板)
  429. *
  430. * @param Request $request
  431. * @return JsonResponse
  432. */
  433. public function getSnapshot(Request $request): JsonResponse
  434. {
  435. try {
  436. $studentId = $request->get('student_id');
  437. $limit = (int) ($request->get('limit', 5));
  438. if (!$studentId) {
  439. return response()->json([
  440. 'success' => false,
  441. 'message' => '缺少必要参数:student_id'
  442. ], 400);
  443. }
  444. $snapshot = $this->mistakeBookService->getPanelSnapshot($studentId, $limit);
  445. Log::info('获取错题本快照数据成功', [
  446. 'student_id' => $studentId,
  447. 'limit' => $limit,
  448. 'recent_count' => count($snapshot['recent'] ?? [])
  449. ]);
  450. return response()->json([
  451. 'success' => true,
  452. 'data' => $snapshot
  453. ]);
  454. } catch (\Exception $e) {
  455. Log::error('获取错题本快照数据失败', [
  456. 'student_id' => $request->get('student_id'),
  457. 'error' => $e->getMessage(),
  458. 'trace' => $e->getTraceAsString()
  459. ]);
  460. return response()->json([
  461. 'success' => false,
  462. 'message' => '获取错题本快照数据失败: ' . $e->getMessage()
  463. ], 500);
  464. }
  465. }
  466. /**
  467. * 修改错题复习状态
  468. */
  469. public function updateReviewStatus(Request $request, string $mistakeId): JsonResponse
  470. {
  471. try {
  472. // 优先从body获取数据,不使用query params
  473. $payload = $request->json()->all();
  474. if (empty($payload)) {
  475. $payload = $request->all();
  476. }
  477. $validator = validator($payload, [
  478. 'action' => 'required|in:increment,reset,mastered',
  479. 'force_review' => 'boolean',
  480. ]);
  481. if ($validator->fails()) {
  482. return response()->json([
  483. 'success' => false,
  484. 'message' => '参数错误',
  485. 'errors' => $validator->errors()->toArray(),
  486. ], 422);
  487. }
  488. $validated = $validator->validated();
  489. $action = $validated['action'];
  490. $forceReview = $validated['force_review'] ?? false;
  491. $result = $this->mistakeBookService->updateReviewStatus($mistakeId, $action, $forceReview);
  492. if ($result['success'] ?? false) {
  493. return response()->json([
  494. 'success' => true,
  495. 'data' => $result,
  496. 'message' => $result['message'] ?? '复习状态更新成功',
  497. ]);
  498. }
  499. return response()->json([
  500. 'success' => false,
  501. 'message' => $result['error'] ?? '更新复习状态失败',
  502. ], 400);
  503. } catch (\Throwable $e) {
  504. Log::error('Update review status error', [
  505. 'mistake_id' => $mistakeId,
  506. 'error' => $e->getMessage(),
  507. ]);
  508. return response()->json([
  509. 'success' => false,
  510. 'message' => '更新复习状态失败',
  511. 'error' => $e->getMessage(),
  512. ], 500);
  513. }
  514. }
  515. /**
  516. * 获取错题复习状态
  517. */
  518. public function getReviewStatus(string $mistakeId): JsonResponse
  519. {
  520. try {
  521. $result = $this->mistakeBookService->getReviewStatus($mistakeId);
  522. if ($result['success'] ?? false) {
  523. return response()->json([
  524. 'success' => true,
  525. 'data' => $result,
  526. ]);
  527. }
  528. return response()->json([
  529. 'success' => false,
  530. 'message' => $result['error'] ?? '获取复习状态失败',
  531. ], 400);
  532. } catch (\Throwable $e) {
  533. Log::error('Get review status error', [
  534. 'mistake_id' => $mistakeId,
  535. 'error' => $e->getMessage(),
  536. ]);
  537. return response()->json([
  538. 'success' => false,
  539. 'message' => '获取复习状态失败',
  540. 'error' => $e->getMessage(),
  541. ], 500);
  542. }
  543. }
  544. /**
  545. * 增加复习次数
  546. */
  547. public function incrementReview(Request $request, string $mistakeId): JsonResponse
  548. {
  549. try {
  550. $validated = $request->validate([
  551. 'force_review' => 'boolean',
  552. ]);
  553. $forceReview = $validated['force_review'] ?? false;
  554. $result = $this->mistakeBookService->incrementReviewCount($mistakeId, $forceReview);
  555. if ($result['success'] ?? false) {
  556. return response()->json([
  557. 'success' => true,
  558. 'data' => $result,
  559. 'message' => $result['message'] ?? '复习次数增加成功',
  560. ]);
  561. }
  562. return response()->json([
  563. 'success' => false,
  564. 'message' => $result['error'] ?? '增加复习次数失败',
  565. ], 400);
  566. } catch (\Throwable $e) {
  567. Log::error('Increment review count error', [
  568. 'mistake_id' => $mistakeId,
  569. 'error' => $e->getMessage(),
  570. ]);
  571. return response()->json([
  572. 'success' => false,
  573. 'message' => '增加复习次数失败',
  574. 'error' => $e->getMessage(),
  575. ], 500);
  576. }
  577. }
  578. /**
  579. * 重置为强制复习状态
  580. */
  581. public function resetReview(string $mistakeId): JsonResponse
  582. {
  583. try {
  584. $result = $this->mistakeBookService->resetReviewStatus($mistakeId);
  585. if ($result['success'] ?? false) {
  586. return response()->json([
  587. 'success' => true,
  588. 'data' => $result,
  589. 'message' => $result['message'] ?? '复习状态重置成功',
  590. ]);
  591. }
  592. return response()->json([
  593. 'success' => false,
  594. 'message' => $result['error'] ?? '重置复习状态失败',
  595. ], 400);
  596. } catch (\Throwable $e) {
  597. Log::error('Reset review status error', [
  598. 'mistake_id' => $mistakeId,
  599. 'error' => $e->getMessage(),
  600. ]);
  601. return response()->json([
  602. 'success' => false,
  603. 'message' => '重置复习状态失败',
  604. 'error' => $e->getMessage(),
  605. ], 500);
  606. }
  607. }
  608. /**
  609. * 批量操作错题
  610. */
  611. public function batchOperation(Request $request): JsonResponse
  612. {
  613. try {
  614. $validated = $request->validate([
  615. 'mistake_ids' => 'required|array|min:1',
  616. 'mistake_ids.*' => 'string',
  617. 'operation' => 'required|string|in:favorite,reviewed,mastered,retry_list,remove_retry_list,set_error_type,set_importance',
  618. 'params' => 'array',
  619. ]);
  620. $mistakeIds = $validated['mistake_ids'];
  621. $operation = $validated['operation'];
  622. $params = $validated['params'] ?? [];
  623. $result = $this->mistakeBookService->batchOperation($mistakeIds, $operation, $params);
  624. if ($result['success'] ?? false) {
  625. return response()->json([
  626. 'success' => true,
  627. 'data' => $result,
  628. 'message' => "批量操作成功,成功处理 {$result['success_count']} 条记录",
  629. ]);
  630. }
  631. return response()->json([
  632. 'success' => false,
  633. 'message' => $result['error'] ?? '批量操作失败',
  634. ], 400);
  635. } catch (\Throwable $e) {
  636. Log::error('Batch operation error', [
  637. 'error' => $e->getMessage(),
  638. 'request' => $request->all(),
  639. ]);
  640. return response()->json([
  641. 'success' => false,
  642. 'message' => '批量操作失败',
  643. 'error' => $e->getMessage(),
  644. ], 500);
  645. }
  646. }
  647. /**
  648. * 批量标记为已复习
  649. */
  650. public function batchMarkReviewed(Request $request): JsonResponse
  651. {
  652. $validated = $request->validate([
  653. 'mistake_ids' => 'required|array|min:1',
  654. 'mistake_ids.*' => 'string',
  655. ]);
  656. $result = $this->mistakeBookService->batchOperation(
  657. $validated['mistake_ids'],
  658. 'reviewed'
  659. );
  660. return response()->json([
  661. 'success' => $result['success'] ?? false,
  662. 'data' => $result,
  663. 'message' => $result['success'] ?? false
  664. ? "已标记 {$result['success_count']} 道题为已复习"
  665. : ($result['error'] ?? '操作失败'),
  666. ]);
  667. }
  668. /**
  669. * 批量标记为已掌握
  670. */
  671. public function batchMarkMastered(Request $request): JsonResponse
  672. {
  673. $validated = $request->validate([
  674. 'mistake_ids' => 'required|array|min:1',
  675. 'mistake_ids.*' => 'string',
  676. ]);
  677. $result = $this->mistakeBookService->batchOperation(
  678. $validated['mistake_ids'],
  679. 'mastered'
  680. );
  681. return response()->json([
  682. 'success' => $result['success'] ?? false,
  683. 'data' => $result,
  684. 'message' => $result['success'] ?? false
  685. ? "已标记 {$result['success_count']} 道题为已掌握"
  686. : ($result['error'] ?? '操作失败'),
  687. ]);
  688. }
  689. /**
  690. * 批量加入重练清单
  691. */
  692. public function batchAddToRetryList(Request $request): JsonResponse
  693. {
  694. $validated = $request->validate([
  695. 'mistake_ids' => 'required|array|min:1',
  696. 'mistake_ids.*' => 'string',
  697. ]);
  698. $result = $this->mistakeBookService->batchOperation(
  699. $validated['mistake_ids'],
  700. 'retry_list'
  701. );
  702. return response()->json([
  703. 'success' => $result['success'] ?? false,
  704. 'data' => $result,
  705. 'message' => $result['success'] ?? false
  706. ? "已加入 {$result['success_count']} 道题到重练清单"
  707. : ($result['error'] ?? '操作失败'),
  708. ]);
  709. }
  710. /**
  711. * 批量从重练清单移除
  712. */
  713. public function batchRemoveFromRetryList(Request $request): JsonResponse
  714. {
  715. $validated = $request->validate([
  716. 'mistake_ids' => 'required|array|min:1',
  717. 'mistake_ids.*' => 'string',
  718. ]);
  719. $result = $this->mistakeBookService->batchOperation(
  720. $validated['mistake_ids'],
  721. 'remove_retry_list'
  722. );
  723. return response()->json([
  724. 'success' => $result['success'] ?? false,
  725. 'data' => $result,
  726. 'message' => $result['success'] ?? false
  727. ? "已从重练清单移除 {$result['success_count']} 道题"
  728. : ($result['error'] ?? '操作失败'),
  729. ]);
  730. }
  731. /**
  732. * 批量设置错误类型
  733. */
  734. public function batchSetErrorType(Request $request): JsonResponse
  735. {
  736. $validated = $request->validate([
  737. 'mistake_ids' => 'required|array|min:1',
  738. 'mistake_ids.*' => 'string',
  739. 'error_type' => 'required|string|in:concept,calculation,careless,logic,other',
  740. ]);
  741. $result = $this->mistakeBookService->batchOperation(
  742. $validated['mistake_ids'],
  743. 'set_error_type',
  744. ['error_type' => $validated['error_type']]
  745. );
  746. return response()->json([
  747. 'success' => $result['success'] ?? false,
  748. 'data' => $result,
  749. 'message' => $result['success'] ?? false
  750. ? "已为 {$result['success_count']} 道题设置错误类型"
  751. : ($result['error'] ?? '操作失败'),
  752. ]);
  753. }
  754. /**
  755. * 批量设置重要程度
  756. */
  757. public function batchSetImportance(Request $request): JsonResponse
  758. {
  759. $validated = $request->validate([
  760. 'mistake_ids' => 'required|array|min:1',
  761. 'mistake_ids.*' => 'string',
  762. 'importance' => 'required|integer|min:1|max:10',
  763. ]);
  764. $result = $this->mistakeBookService->batchOperation(
  765. $validated['mistake_ids'],
  766. 'set_importance',
  767. ['importance' => $validated['importance']]
  768. );
  769. return response()->json([
  770. 'success' => $result['success'] ?? false,
  771. 'data' => $result,
  772. 'message' => $result['success'] ?? false
  773. ? "已为 {$result['success_count']} 道题设置重要程度"
  774. : ($result['error'] ?? '操作失败'),
  775. ]);
  776. }
  777. /**
  778. * 批量切换收藏状态
  779. */
  780. public function batchToggleFavorite(Request $request): JsonResponse
  781. {
  782. $validated = $request->validate([
  783. 'mistake_ids' => 'required|array|min:1',
  784. 'mistake_ids.*' => 'string',
  785. ]);
  786. $result = $this->mistakeBookService->batchOperation(
  787. $validated['mistake_ids'],
  788. 'favorite'
  789. );
  790. return response()->json([
  791. 'success' => $result['success'] ?? false,
  792. 'data' => $result,
  793. 'message' => $result['success'] ?? false
  794. ? "已为 {$result['success_count']} 道题切换收藏状态"
  795. : ($result['error'] ?? '操作失败'),
  796. ]);
  797. }
  798. }