TaskConditionService.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. <?php
  2. namespace App\Module\Task\Services;
  3. use App\Module\Task\Enums\TASK_STATUS;
  4. use App\Module\Task\Models\Task;
  5. use App\Module\Task\Models\TaskUserTask;
  6. use App\Module\Task\Repositorys\TaskAchievementConditionRepository;
  7. use App\Module\Task\Repositorys\TaskConditionRepository;
  8. use App\Module\Task\Repositorys\TaskUserProgressRepository;
  9. use App\Module\Task\Repositorys\TaskUserTaskRepository;
  10. use App\Module\Task\Services\TaskRewardGroupService;
  11. use App\Module\Game\Services\TaskTempService;
  12. use Illuminate\Support\Carbon;
  13. use Illuminate\Support\Facades\DB;
  14. use Illuminate\Support\Facades\Log;
  15. /**
  16. * 任务条件服务类
  17. *
  18. * 提供任务条件相关的服务,包括条件验证、进度更新等功能。
  19. * 该类主要处理任务条件的逻辑,与TaskProgressService配合使用。
  20. */
  21. class TaskConditionService
  22. {
  23. /**
  24. * 任务条件数据仓库
  25. *
  26. * @var TaskConditionRepository
  27. */
  28. protected $conditionRepository;
  29. /**
  30. * 任务达成条件数据仓库
  31. *
  32. * @var TaskAchievementConditionRepository
  33. */
  34. protected $achievementConditionRepository;
  35. /**
  36. * 用户任务数据仓库
  37. *
  38. * @var TaskUserTaskRepository
  39. */
  40. protected $userTaskRepository;
  41. /**
  42. * 用户任务进度数据仓库
  43. *
  44. * @var TaskUserProgressRepository
  45. */
  46. protected $userProgressRepository;
  47. /**
  48. * 构造函数
  49. *
  50. * @param TaskConditionRepository $conditionRepository
  51. * @param TaskAchievementConditionRepository $achievementConditionRepository
  52. * @param TaskUserTaskRepository $userTaskRepository
  53. * @param TaskUserProgressRepository $userProgressRepository
  54. */
  55. public function __construct(
  56. TaskConditionRepository $conditionRepository,
  57. TaskAchievementConditionRepository $achievementConditionRepository,
  58. TaskUserTaskRepository $userTaskRepository,
  59. TaskUserProgressRepository $userProgressRepository
  60. ) {
  61. $this->conditionRepository = $conditionRepository;
  62. $this->achievementConditionRepository = $achievementConditionRepository;
  63. $this->userTaskRepository = $userTaskRepository;
  64. $this->userProgressRepository = $userProgressRepository;
  65. }
  66. /**
  67. * 获取所有可用的任务条件
  68. *
  69. * @return array 条件列表
  70. */
  71. public function getAllConditions(): array
  72. {
  73. return $this->conditionRepository->getActiveConditions();
  74. }
  75. /**
  76. * 获取任务的所有达成条件
  77. *
  78. * @param int $taskId 任务ID
  79. * @param string|null $conditionType 条件类型(prerequisite=前置条件,progress=进度条件)
  80. * @return array 达成条件列表
  81. */
  82. public function getTaskConditions(int $taskId, ?string $conditionType = null): array
  83. {
  84. return $this->achievementConditionRepository->getConditionsByTaskId($taskId, $conditionType);
  85. }
  86. /**
  87. * 验证任务前置条件
  88. *
  89. * @param int $userId 用户ID
  90. * @param int $taskId 任务ID
  91. * @return array 验证结果
  92. */
  93. public function validatePrerequisiteConditions(int $userId, int $taskId): array
  94. {
  95. try {
  96. // 获取任务的前置条件
  97. $prerequisites = $this->getTaskConditions($taskId, 'prerequisite');
  98. if (empty($prerequisites)) {
  99. return [
  100. 'success' => true,
  101. 'message' => '无前置条件',
  102. ];
  103. }
  104. // 验证每个前置条件
  105. $failedConditions = [];
  106. foreach ($prerequisites as $prerequisite) {
  107. $condition = $this->conditionRepository->find($prerequisite['condition_id']);
  108. if (!$condition) {
  109. continue;
  110. }
  111. // 获取条件处理器类
  112. $handlerClass = $condition->handler_class;
  113. if (!class_exists($handlerClass)) {
  114. Log::error('条件处理器类不存在', [
  115. 'condition_id' => $condition->id,
  116. 'handler_class' => $handlerClass,
  117. ]);
  118. continue;
  119. }
  120. // 实例化条件处理器
  121. $handler = new $handlerClass();
  122. // 验证条件
  123. $isValid = $handler->validate($userId, $prerequisite['condition_params']);
  124. if (!$isValid) {
  125. $failedConditions[] = [
  126. 'condition_id' => $condition->id,
  127. 'name' => $condition->name,
  128. 'description' => $condition->description,
  129. ];
  130. }
  131. }
  132. if (!empty($failedConditions)) {
  133. return [
  134. 'success' => false,
  135. 'message' => '前置条件未满足',
  136. 'failed_conditions' => $failedConditions,
  137. ];
  138. }
  139. return [
  140. 'success' => true,
  141. 'message' => '前置条件已满足',
  142. ];
  143. } catch (\Exception $e) {
  144. Log::error('验证前置条件失败', [
  145. 'user_id' => $userId,
  146. 'task_id' => $taskId,
  147. 'error' => $e->getMessage(),
  148. ]);
  149. return [
  150. 'success' => false,
  151. 'message' => '验证前置条件失败: ' . $e->getMessage(),
  152. ];
  153. }
  154. }
  155. /**
  156. * 更新任务进度条件
  157. *
  158. * @param int $userId 用户ID
  159. * @param string $conditionCode 条件代码
  160. * @param array $params 条件参数
  161. * @param int $increment 增量值
  162. * @return array 更新结果
  163. */
  164. public function updateProgressCondition(int $userId, string $conditionCode, array $params, int $increment = 1): array
  165. {
  166. try {
  167. // 获取条件
  168. $condition = $this->conditionRepository->getByCode($conditionCode);
  169. if (!$condition) {
  170. throw new \Exception('条件不存在');
  171. }
  172. // 获取使用该条件的任务达成条件
  173. $achievementConditions = $condition->achievementConditions()
  174. ->where('condition_type', 'progress')
  175. ->get();
  176. if ($achievementConditions->isEmpty()) {
  177. return [
  178. 'success' => true,
  179. 'message' => '无相关任务进度需要更新',
  180. 'updated_tasks' => [],
  181. ];
  182. }
  183. // 获取条件处理器类
  184. $handlerClass = $condition->handler_class;
  185. if (empty($handlerClass)) {
  186. throw new \Exception('条件处理器类未配置: ' . $condition->code);
  187. }
  188. if (!class_exists($handlerClass)) {
  189. throw new \Exception('条件处理器类不存在: ' . $handlerClass);
  190. }
  191. // 实例化条件处理器
  192. $handler = new $handlerClass();
  193. // 更新任务进度
  194. $updatedTasks = [];
  195. foreach ($achievementConditions as $achievementCondition) {
  196. // 获取用户任务
  197. $userTask = $this->userTaskRepository->getUserTask($userId, $achievementCondition->task_id);
  198. if (!$userTask || $userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  199. continue;
  200. }
  201. // 检查条件参数是否匹配
  202. $conditionParams = $achievementCondition->condition_params ?? [];
  203. if (!$handler->isMatch($params, $conditionParams)) {
  204. Log::info('跳过任务:条件参数不匹配', [
  205. 'event_params' => $params,
  206. 'condition_params' => $conditionParams
  207. ]);
  208. continue;
  209. }
  210. // 计算实际的进度增量
  211. $actualIncrement = $handler->calculateProgress($params, $conditionParams);
  212. Log::info('计算进度增量', [
  213. 'actual_increment' => $actualIncrement,
  214. 'params' => $params,
  215. 'condition_params' => $conditionParams
  216. ]);
  217. if ($actualIncrement <= 0) {
  218. Log::info('跳过任务:进度增量为0或负数');
  219. continue;
  220. }
  221. // 获取用户任务进度
  222. $userProgress = $this->userProgressRepository->getUserProgress($userId, $achievementCondition->id);
  223. Log::info('获取用户任务进度', [
  224. 'user_progress_exists' => $userProgress ? 'yes' : 'no',
  225. 'achievement_condition_id' => $achievementCondition->id
  226. ]);
  227. // 如果进度记录不存在,创建新记录
  228. if (!$userProgress) {
  229. Log::info('创建新的进度记录', [
  230. 'user_id' => $userId,
  231. 'task_id' => $achievementCondition->task_id,
  232. 'achievement_condition_id' => $achievementCondition->id,
  233. 'target_value' => $achievementCondition->target_value
  234. ]);
  235. $userProgress = $this->userProgressRepository->createUserProgress([
  236. 'user_id' => $userId,
  237. 'task_id' => $achievementCondition->task_id,
  238. 'achievement_condition_id' => $achievementCondition->id,
  239. 'current_value' => 0,
  240. 'target_value' => $achievementCondition->target_value,
  241. ]);
  242. Log::info('进度记录创建结果', [
  243. 'created_progress_id' => $userProgress ? $userProgress->id : 'null'
  244. ]);
  245. }
  246. // 更新进度值
  247. $oldValue = $userProgress->current_value;
  248. $newValue = $oldValue + $actualIncrement;
  249. if ($newValue > $userProgress->target_value) {
  250. $newValue = $userProgress->target_value;
  251. }
  252. Log::info('更新进度值', [
  253. 'old_value' => $oldValue,
  254. 'increment' => $actualIncrement,
  255. 'new_value' => $newValue,
  256. 'target_value' => $userProgress->target_value
  257. ]);
  258. $userProgress->current_value = $newValue;
  259. $saveResult = $userProgress->save();
  260. Log::info('保存进度结果', [
  261. 'save_result' => $saveResult,
  262. 'final_current_value' => $userProgress->current_value
  263. ]);
  264. // 计算任务总进度
  265. $progressResult = $this->calculateTaskTotalProgress($userId, $achievementCondition->task_id);
  266. // 检查任务是否完成,如果是自动任务则自动处理
  267. if ($progressResult['success'] && $progressResult['progress'] >= 100) {
  268. $this->handleAutoTaskCompletion($userId, $achievementCondition->task_id);
  269. }
  270. $taskProgress = min(100, round($newValue / $userProgress->target_value * 100));
  271. // 记录任务进度更新到暂存系统
  272. $task = $userTask->task;
  273. TaskTempService::recordTaskProgressUpdate(
  274. $userId,
  275. $achievementCondition->task_id,
  276. $task->name ?? '未知任务',
  277. $progressResult['progress'] ?? $taskProgress,
  278. [
  279. 'condition_id' => $achievementCondition->id,
  280. 'condition_name' => $condition->name ?? '未知条件',
  281. 'current_value' => $newValue,
  282. 'target_value' => $userProgress->target_value,
  283. 'increment' => $actualIncrement,
  284. ]
  285. );
  286. $updatedTasks[] = [
  287. 'task_id' => $achievementCondition->task_id,
  288. 'condition_id' => $achievementCondition->id,
  289. 'current_value' => $newValue,
  290. 'target_value' => $userProgress->target_value,
  291. 'progress' => $taskProgress,
  292. ];
  293. }
  294. return [
  295. 'success' => true,
  296. 'message' => '任务进度更新成功',
  297. 'updated_tasks' => $updatedTasks,
  298. ];
  299. } catch (\Exception $e) {
  300. Log::error('更新任务进度失败', [
  301. 'user_id' => $userId,
  302. 'condition_code' => $conditionCode,
  303. 'params' => $params,
  304. 'increment' => $increment,
  305. 'error' => $e->getMessage(),
  306. ]);
  307. return [
  308. 'success' => false,
  309. 'message' => '更新任务进度失败: ' . $e->getMessage(),
  310. ];
  311. }
  312. }
  313. /**
  314. * 计算任务总进度
  315. *
  316. * @param int $userId 用户ID
  317. * @param int $taskId 任务ID
  318. * @return array 计算结果
  319. */
  320. public function calculateTaskTotalProgress(int $userId, int $taskId): array
  321. {
  322. try {
  323. // 获取用户任务
  324. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  325. if (!$userTask || $userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  326. throw new \Exception('任务不存在或状态不正确');
  327. }
  328. // 获取任务的进度条件
  329. $progressConditions = $this->getTaskConditions($taskId, 'progress');
  330. if (empty($progressConditions)) {
  331. throw new \Exception('任务没有进度条件');
  332. }
  333. // 计算总进度
  334. $totalProgress = 0;
  335. $completedConditions = 0;
  336. foreach ($progressConditions as $condition) {
  337. // 获取用户任务进度
  338. $userProgress = $this->userProgressRepository->getUserProgress($userId, $condition['id']);
  339. if (!$userProgress) {
  340. continue;
  341. }
  342. // 计算条件进度百分比
  343. $conditionProgress = min(100, round($userProgress->current_value / $userProgress->target_value * 100));
  344. // 如果条件是必要条件,累加进度
  345. if ($condition['is_required']) {
  346. $totalProgress += $conditionProgress;
  347. }
  348. // 如果条件已完成,增加完成条件计数
  349. if ($conditionProgress >= 100) {
  350. $completedConditions++;
  351. }
  352. }
  353. // 计算平均进度
  354. $requiredConditions = array_filter($progressConditions, function ($condition) {
  355. return $condition['is_required'];
  356. });
  357. $requiredCount = count($requiredConditions);
  358. if ($requiredCount > 0) {
  359. $totalProgress = round($totalProgress / $requiredCount);
  360. } else {
  361. $totalProgress = 0;
  362. }
  363. // 更新用户任务进度
  364. $userTask->progress = $totalProgress;
  365. $userTask->save();
  366. return [
  367. 'success' => true,
  368. 'message' => '任务总进度计算成功',
  369. 'progress' => $totalProgress,
  370. 'completed_conditions' => $completedConditions,
  371. 'total_conditions' => count($progressConditions),
  372. ];
  373. } catch (\Exception $e) {
  374. Log::error('计算任务总进度失败', [
  375. 'user_id' => $userId,
  376. 'task_id' => $taskId,
  377. 'error' => $e->getMessage(),
  378. ]);
  379. return [
  380. 'success' => false,
  381. 'message' => '计算任务总进度失败: ' . $e->getMessage(),
  382. ];
  383. }
  384. }
  385. /**
  386. * 重置任务进度
  387. *
  388. * @param int $userId 用户ID
  389. * @param int $taskId 任务ID
  390. * @return array 重置结果
  391. */
  392. public function resetTaskProgress(int $userId, int $taskId): array
  393. {
  394. try {
  395. // 获取用户任务
  396. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  397. if (!$userTask) {
  398. throw new \Exception('任务不存在');
  399. }
  400. // 获取任务的进度条件
  401. $progressConditions = $this->getTaskConditions($taskId, 'progress');
  402. // 重置每个条件的进度
  403. foreach ($progressConditions as $condition) {
  404. $userProgress = $this->userProgressRepository->getUserProgress($userId, $condition['id']);
  405. if ($userProgress) {
  406. $userProgress->current_value = 0;
  407. $userProgress->save();
  408. }
  409. }
  410. // 重置用户任务进度
  411. $userTask->progress = 0;
  412. $userTask->status = TASK_STATUS::IN_PROGRESS->value;
  413. $userTask->completed_at = null;
  414. $userTask->rewarded_at = null;
  415. $userTask->save();
  416. return [
  417. 'success' => true,
  418. 'message' => '任务进度重置成功',
  419. ];
  420. } catch (\Exception $e) {
  421. Log::error('重置任务进度失败', [
  422. 'user_id' => $userId,
  423. 'task_id' => $taskId,
  424. 'error' => $e->getMessage(),
  425. ]);
  426. return [
  427. 'success' => false,
  428. 'message' => '重置任务进度失败: ' . $e->getMessage(),
  429. ];
  430. }
  431. }
  432. /**
  433. * 处理自动任务完成
  434. *
  435. * @param int $userId 用户ID
  436. * @param int $taskId 任务ID
  437. * @return void
  438. */
  439. protected function handleAutoTaskCompletion(int $userId, int $taskId): void
  440. {
  441. try {
  442. // 获取任务信息
  443. $task = Task::find($taskId);
  444. if (!$task) {
  445. return;
  446. }
  447. // 检查是否是自动任务
  448. $displayParams = $task->display_params ?? [];
  449. if (!isset($displayParams['auto_complete']) || !$displayParams['auto_complete']) {
  450. return;
  451. }
  452. // 获取用户任务
  453. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  454. if (!$userTask || $userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  455. return;
  456. }
  457. // 自动完成任务
  458. $this->autoCompleteTask($userId, $taskId, $task, $userTask);
  459. } catch (\Exception $e) {
  460. Log::error('处理自动任务完成失败', [
  461. 'user_id' => $userId,
  462. 'task_id' => $taskId,
  463. 'error' => $e->getMessage()
  464. ]);
  465. }
  466. }
  467. /**
  468. * 自动完成任务
  469. *
  470. * @param int $userId 用户ID
  471. * @param int $taskId 任务ID
  472. * @param mixed $task 任务对象
  473. * @param mixed $userTask 用户任务对象
  474. * @return void
  475. */
  476. protected function autoCompleteTask(int $userId, int $taskId, $task, $userTask): void
  477. {
  478. try {
  479. DB::beginTransaction();
  480. // 标记任务为已完成
  481. $userTask->status = TASK_STATUS::COMPLETED->value;
  482. $userTask->completed_at = Carbon::now();
  483. $userTask->save();
  484. Log::info('自动完成任务', [
  485. 'user_id' => $userId,
  486. 'task_id' => $taskId,
  487. 'task_name' => $task->name
  488. ]);
  489. // 检查是否自动发放奖励
  490. $displayParams = $task->display_params ?? [];
  491. if (isset($displayParams['auto_reward']) && $displayParams['auto_reward']) {
  492. $this->autoClaimReward($userId, $taskId, $task, $userTask);
  493. }
  494. // 检查是否需要自动重置(循环任务)
  495. if ($task->reset_type === 'immediate' && $task->max_completions === -1) {
  496. $this->autoResetTask($userId, $taskId, $task);
  497. }
  498. DB::commit();
  499. } catch (\Exception $e) {
  500. DB::rollBack();
  501. Log::error('自动完成任务失败', [
  502. 'user_id' => $userId,
  503. 'task_id' => $taskId,
  504. 'error' => $e->getMessage()
  505. ]);
  506. }
  507. }
  508. /**
  509. * 自动领取奖励
  510. *
  511. * @param int $userId 用户ID
  512. * @param int $taskId 任务ID
  513. * @param mixed $task 任务对象
  514. * @param mixed $userTask 用户任务对象
  515. * @return void
  516. */
  517. protected function autoClaimReward(int $userId, int $taskId, $task, $userTask): void
  518. {
  519. try {
  520. // 检查任务是否有奖励组
  521. if (!$task->reward_group_id) {
  522. return;
  523. }
  524. // 使用奖励组服务发放奖励
  525. $result = TaskRewardGroupService::distributeRewards(
  526. $userId,
  527. $taskId,
  528. $userTask->id
  529. );
  530. if ($result['success']) {
  531. // 更新任务状态为已领取奖励
  532. $userTask->status = TASK_STATUS::REWARDED->value;
  533. $userTask->rewarded_at = Carbon::now();
  534. $userTask->save();
  535. Log::info('自动领取任务奖励成功', [
  536. 'user_id' => $userId,
  537. 'task_id' => $taskId,
  538. 'task_name' => $task->name,
  539. 'rewards' => $result['rewards'] ?? []
  540. ]);
  541. } else {
  542. Log::error('自动领取任务奖励失败', [
  543. 'user_id' => $userId,
  544. 'task_id' => $taskId,
  545. 'error' => $result['message'] ?? '未知错误'
  546. ]);
  547. }
  548. } catch (\Exception $e) {
  549. Log::error('自动领取奖励异常', [
  550. 'user_id' => $userId,
  551. 'task_id' => $taskId,
  552. 'error' => $e->getMessage()
  553. ]);
  554. }
  555. }
  556. /**
  557. * 自动重置任务(循环任务)
  558. *
  559. * @param int $userId 用户ID
  560. * @param int $taskId 任务ID
  561. * @param mixed $task 任务对象
  562. * @return void
  563. */
  564. protected function autoResetTask(int $userId, int $taskId, $task): void
  565. {
  566. try {
  567. // 重置任务进度
  568. $resetResult = $this->resetTaskProgress($userId, $taskId);
  569. if ($resetResult['success']) {
  570. Log::info('自动重置循环任务成功', [
  571. 'user_id' => $userId,
  572. 'task_id' => $taskId,
  573. 'task_name' => $task->name
  574. ]);
  575. } else {
  576. Log::error('自动重置循环任务失败', [
  577. 'user_id' => $userId,
  578. 'task_id' => $taskId,
  579. 'error' => $resetResult['message']
  580. ]);
  581. }
  582. } catch (\Exception $e) {
  583. Log::error('自动重置任务异常', [
  584. 'user_id' => $userId,
  585. 'task_id' => $taskId,
  586. 'error' => $e->getMessage()
  587. ]);
  588. }
  589. }
  590. /**
  591. * 自动接取任务
  592. *
  593. * @param int $userId 用户ID
  594. * @param int $taskId 任务ID
  595. * @return bool 是否成功接取
  596. */
  597. public function autoAcceptTask(int $userId, int $taskId): bool
  598. {
  599. try {
  600. // 获取任务信息
  601. $task = Task::find($taskId);
  602. if (!$task || !$task->is_active) {
  603. return false;
  604. }
  605. // 检查是否是自动接取任务
  606. $displayParams = $task->display_params ?? [];
  607. if (!isset($displayParams['auto_accept']) || !$displayParams['auto_accept']) {
  608. return false;
  609. }
  610. // 检查用户是否已接取该任务
  611. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  612. if ($userTask) {
  613. return true; // 已接取
  614. }
  615. // 创建用户任务记录
  616. $userTask = TaskUserTask::create([
  617. 'user_id' => $userId,
  618. 'task_id' => $taskId,
  619. 'status' => TASK_STATUS::IN_PROGRESS->value,
  620. 'progress' => 0,
  621. 'accepted_at' => Carbon::now(),
  622. ]);
  623. Log::info('自动接取任务成功', [
  624. 'user_id' => $userId,
  625. 'task_id' => $taskId,
  626. 'task_name' => $task->name
  627. ]);
  628. return true;
  629. } catch (\Exception $e) {
  630. Log::error('自动接取任务失败', [
  631. 'user_id' => $userId,
  632. 'task_id' => $taskId,
  633. 'error' => $e->getMessage()
  634. ]);
  635. return false;
  636. }
  637. }
  638. }