MistakeBookController.php 28 KB

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