TaskService.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. <?php
  2. namespace App\Module\Task\Services;
  3. use App\Module\Task\Enums\TASK_STATUS;
  4. use App\Module\Task\Enums\TASK_TYPE;
  5. use App\Module\Task\Events\TaskCompletedEvent;
  6. use App\Module\Task\Events\TaskRewardClaimedEvent;
  7. use App\Module\Task\Models\Task;
  8. use App\Module\Task\Models\TaskReward;
  9. use App\Module\Task\Models\TaskUserTask;
  10. use App\Module\Task\Repositorys\TaskRepository;
  11. use App\Module\Task\Repositorys\TaskRewardRepository;
  12. use App\Module\Task\Repositorys\TaskUserTaskRepository;
  13. use Carbon\Carbon;
  14. use Illuminate\Support\Facades\DB;
  15. use Illuminate\Support\Facades\Log;
  16. /**
  17. * 任务服务类
  18. *
  19. * 提供任务相关的核心服务,包括任务查询、接取、完成、奖励领取等功能。
  20. * 该类是任务模块对外提供服务的主要入口。
  21. */
  22. class TaskService
  23. {
  24. /**
  25. * 任务数据仓库
  26. *
  27. * @var TaskRepository
  28. */
  29. protected $taskRepository;
  30. /**
  31. * 用户任务数据仓库
  32. *
  33. * @var TaskUserTaskRepository
  34. */
  35. protected $userTaskRepository;
  36. /**
  37. * 任务奖励数据仓库
  38. *
  39. * @var TaskRewardRepository
  40. */
  41. protected $rewardRepository;
  42. /**
  43. * 构造函数
  44. *
  45. * @param TaskRepository $taskRepository
  46. * @param TaskUserTaskRepository $userTaskRepository
  47. * @param TaskRewardRepository $rewardRepository
  48. */
  49. public function __construct(
  50. TaskRepository $taskRepository,
  51. TaskUserTaskRepository $userTaskRepository,
  52. TaskRewardRepository $rewardRepository
  53. ) {
  54. $this->taskRepository = $taskRepository;
  55. $this->userTaskRepository = $userTaskRepository;
  56. $this->rewardRepository = $rewardRepository;
  57. }
  58. /**
  59. * 获取用户可用的任务列表
  60. *
  61. * @param int $userId 用户ID
  62. * @param string|null $type 任务类型(可选)
  63. * @param int|null $categoryId 任务分类ID(可选)
  64. * @param bool $includeCompleted 是否包含已完成的任务
  65. * @return array 任务列表
  66. */
  67. public function getAvailableTasks(int $userId, ?string $type = null, ?int $categoryId = null, bool $includeCompleted = false): array
  68. {
  69. // 获取用户信息(可以调用用户模块的服务)
  70. // $userInfo = UserService::getUserInfo($userId);
  71. // 获取用户等级
  72. $userLevel = 1; // 默认等级,实际应从用户信息中获取
  73. // 获取符合条件的任务
  74. $tasks = $this->taskRepository->getAvailableTasks($type, $categoryId, $userLevel);
  75. // 获取用户任务状态
  76. $userTasks = $this->userTaskRepository->getUserTasks($userId);
  77. // 将用户任务状态合并到任务数据中
  78. $result = [];
  79. foreach ($tasks as $task) {
  80. $userTask = $userTasks->where('task_id', $task['id'])->first();
  81. // 如果任务已完成且不包含已完成任务,则跳过
  82. if ($userTask && $userTask['status'] >= TASK_STATUS::COMPLETED->value && !$includeCompleted) {
  83. continue;
  84. }
  85. // 合并任务数据和用户任务状态
  86. $taskData = $task;
  87. $taskData['user_status'] = $userTask ? $userTask['status'] : TASK_STATUS::NOT_ACCEPTED->value;
  88. $taskData['progress'] = $userTask ? $userTask['progress'] : 0;
  89. $taskData['completed_at'] = $userTask && $userTask['completed_at'] ? $userTask['completed_at'] : null;
  90. $taskData['rewarded_at'] = $userTask && $userTask['rewarded_at'] ? $userTask['rewarded_at'] : null;
  91. // 获取任务奖励
  92. $taskData['rewards'] = $this->rewardRepository->getRewardsByTaskId($task['id']);
  93. $result[] = $taskData;
  94. }
  95. return $result;
  96. }
  97. /**
  98. * 接取任务
  99. *
  100. * @param int $userId 用户ID
  101. * @param int $taskId 任务ID
  102. * @return array 接取结果
  103. */
  104. public function acceptTask(int $userId, int $taskId): array
  105. {
  106. try {
  107. // 开始事务
  108. DB::beginTransaction();
  109. // 获取任务信息
  110. $task = $this->taskRepository->find($taskId);
  111. if (!$task || !$task->is_active) {
  112. throw new \Exception('任务不存在或未激活');
  113. }
  114. // 检查任务是否已过期
  115. if ($task->end_time && Carbon::now()->greaterThan($task->end_time)) {
  116. throw new \Exception('任务已过期');
  117. }
  118. // 检查用户是否已接取该任务
  119. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  120. if ($userTask) {
  121. throw new \Exception('已接取该任务');
  122. }
  123. // 检查任务前置条件
  124. // $this->checkTaskPrerequisites($userId, $task);
  125. // 检查任务接取消耗
  126. // $this->checkAndConsumeTaskCosts($userId, $task);
  127. // 创建用户任务记录
  128. $userTask = $this->userTaskRepository->createUserTask([
  129. 'user_id' => $userId,
  130. 'task_id' => $taskId,
  131. 'status' => TASK_STATUS::IN_PROGRESS->value,
  132. 'progress' => 0,
  133. 'accepted_at' => Carbon::now(),
  134. ]);
  135. // 提交事务
  136. DB::commit();
  137. return [
  138. 'success' => true,
  139. 'message' => '任务接取成功',
  140. 'user_task' => $userTask,
  141. ];
  142. } catch (\Exception $e) {
  143. // 回滚事务
  144. DB::rollBack();
  145. Log::error('任务接取失败', [
  146. 'user_id' => $userId,
  147. 'task_id' => $taskId,
  148. 'error' => $e->getMessage(),
  149. ]);
  150. return [
  151. 'success' => false,
  152. 'message' => $e->getMessage(),
  153. ];
  154. }
  155. }
  156. /**
  157. * 完成任务
  158. *
  159. * @param int $userId 用户ID
  160. * @param int $taskId 任务ID
  161. * @return array 完成结果
  162. */
  163. public function completeTask(int $userId, int $taskId): array
  164. {
  165. try {
  166. // 开始事务
  167. DB::beginTransaction();
  168. // 获取用户任务
  169. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  170. if (!$userTask) {
  171. throw new \Exception('未接取该任务');
  172. }
  173. // 检查任务状态
  174. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  175. throw new \Exception('任务状态不正确');
  176. }
  177. // 检查任务进度
  178. if ($userTask->progress < 100) {
  179. throw new \Exception('任务进度未达到100%');
  180. }
  181. // 更新任务状态
  182. $userTask->status = TASK_STATUS::COMPLETED->value;
  183. $userTask->completed_at = Carbon::now();
  184. $userTask->save();
  185. // 获取任务信息
  186. $task = $this->taskRepository->find($taskId);
  187. // 触发任务完成事件
  188. event(new TaskCompletedEvent(
  189. $userId,
  190. $taskId,
  191. $task->name,
  192. $userTask->completed_at
  193. ));
  194. // 提交事务
  195. DB::commit();
  196. return [
  197. 'success' => true,
  198. 'message' => '任务完成成功',
  199. 'user_task' => $userTask,
  200. ];
  201. } catch (\Exception $e) {
  202. // 回滚事务
  203. DB::rollBack();
  204. Log::error('任务完成失败', [
  205. 'user_id' => $userId,
  206. 'task_id' => $taskId,
  207. 'error' => $e->getMessage(),
  208. ]);
  209. return [
  210. 'success' => false,
  211. 'message' => $e->getMessage(),
  212. ];
  213. }
  214. }
  215. /**
  216. * 领取任务奖励
  217. *
  218. * @param int $userId 用户ID
  219. * @param int $taskId 任务ID
  220. * @return array 领取结果
  221. */
  222. public function claimTaskReward(int $userId, int $taskId): array
  223. {
  224. try {
  225. // 开始事务
  226. DB::beginTransaction();
  227. // 获取用户任务
  228. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  229. if (!$userTask) {
  230. throw new \Exception('未接取该任务');
  231. }
  232. // 检查任务状态
  233. if ($userTask->status !== TASK_STATUS::COMPLETED->value) {
  234. throw new \Exception('任务未完成,无法领取奖励');
  235. }
  236. // 获取任务信息
  237. $task = $this->taskRepository->find($taskId);
  238. // 获取任务奖励
  239. $rewards = $this->rewardRepository->getRewardsByTaskId($taskId);
  240. // 发放奖励
  241. $rewardResults = $this->distributeRewards($userId, $rewards);
  242. // 更新任务状态
  243. $userTask->status = TASK_STATUS::REWARDED->value;
  244. $userTask->rewarded_at = Carbon::now();
  245. $userTask->save();
  246. // 记录奖励领取日志
  247. // $this->recordRewardLog($userId, $taskId, $userTask->id, $rewards);
  248. // 触发奖励领取事件
  249. event(new TaskRewardClaimedEvent(
  250. $userId,
  251. $taskId,
  252. $task->name,
  253. $rewards,
  254. $userTask->rewarded_at,
  255. true
  256. ));
  257. // 提交事务
  258. DB::commit();
  259. return [
  260. 'success' => true,
  261. 'message' => '奖励领取成功',
  262. 'rewards' => $rewardResults,
  263. ];
  264. } catch (\Exception $e) {
  265. // 回滚事务
  266. DB::rollBack();
  267. Log::error('奖励领取失败', [
  268. 'user_id' => $userId,
  269. 'task_id' => $taskId,
  270. 'error' => $e->getMessage(),
  271. ]);
  272. // 触发奖励领取失败事件
  273. event(new TaskRewardClaimedEvent(
  274. $userId,
  275. $taskId,
  276. $task->name ?? '未知任务',
  277. [],
  278. Carbon::now(),
  279. false
  280. ));
  281. return [
  282. 'success' => false,
  283. 'message' => $e->getMessage(),
  284. ];
  285. }
  286. }
  287. /**
  288. * 发放奖励
  289. *
  290. * @param int $userId 用户ID
  291. * @param array $rewards 奖励列表
  292. * @return array 发放结果
  293. */
  294. protected function distributeRewards(int $userId, array $rewards): array
  295. {
  296. $results = [];
  297. foreach ($rewards as $reward) {
  298. try {
  299. switch ($reward['reward_type']) {
  300. case 'item':
  301. // 调用物品模块服务添加物品
  302. // $itemResult = ItemService::addItem(
  303. // $userId,
  304. // $reward['reward_param2'],
  305. // $reward['quantity'],
  306. // [
  307. // 'source_type' => 'task_reward',
  308. // 'source_id' => $reward['task_id'],
  309. // ]
  310. // );
  311. // 模拟物品发放结果
  312. $itemResult = [
  313. 'success' => true,
  314. 'message' => '物品发放成功',
  315. ];
  316. $results[] = [
  317. 'type' => 'item',
  318. 'param1' => $reward['reward_param1'],
  319. 'param2' => $reward['reward_param2'],
  320. 'quantity' => $reward['quantity'],
  321. 'success' => $itemResult['success'],
  322. 'message' => $itemResult['message'] ?? '',
  323. ];
  324. break;
  325. case 'currency':
  326. // 调用货币模块服务添加货币
  327. // $currencyResult = CurrencyService::addCurrency(
  328. // $userId,
  329. // $reward['reward_param1'],
  330. // $reward['reward_param2'],
  331. // $reward['quantity'],
  332. // [
  333. // 'source_type' => 'task_reward',
  334. // 'source_id' => $reward['task_id'],
  335. // ]
  336. // );
  337. // 模拟货币发放结果
  338. $currencyResult = [
  339. 'success' => true,
  340. 'message' => '货币发放成功',
  341. ];
  342. $results[] = [
  343. 'type' => 'currency',
  344. 'param1' => $reward['reward_param1'],
  345. 'param2' => $reward['reward_param2'],
  346. 'quantity' => $reward['quantity'],
  347. 'success' => $currencyResult['success'],
  348. 'message' => $currencyResult['message'] ?? '',
  349. ];
  350. break;
  351. case 'experience':
  352. // 调用经验模块服务添加经验
  353. // $expResult = ExperienceService::addExperience(
  354. // $userId,
  355. // $reward['reward_param1'],
  356. // $reward['quantity'],
  357. // [
  358. // 'source_type' => 'task_reward',
  359. // 'source_id' => $reward['task_id'],
  360. // ]
  361. // );
  362. // 模拟经验发放结果
  363. $expResult = [
  364. 'success' => true,
  365. 'message' => '经验发放成功',
  366. ];
  367. $results[] = [
  368. 'type' => 'experience',
  369. 'param1' => $reward['reward_param1'],
  370. 'param2' => $reward['reward_param2'],
  371. 'quantity' => $reward['quantity'],
  372. 'success' => $expResult['success'],
  373. 'message' => $expResult['message'] ?? '',
  374. ];
  375. break;
  376. // 其他奖励类型...
  377. default:
  378. $results[] = [
  379. 'type' => $reward['reward_type'],
  380. 'param1' => $reward['reward_param1'],
  381. 'param2' => $reward['reward_param2'],
  382. 'quantity' => $reward['quantity'],
  383. 'success' => false,
  384. 'message' => '未知的奖励类型',
  385. ];
  386. break;
  387. }
  388. } catch (\Exception $e) {
  389. Log::error('奖励发放失败', [
  390. 'user_id' => $userId,
  391. 'reward' => $reward,
  392. 'error' => $e->getMessage(),
  393. ]);
  394. $results[] = [
  395. 'type' => $reward['reward_type'],
  396. 'param1' => $reward['reward_param1'],
  397. 'param2' => $reward['reward_param2'],
  398. 'quantity' => $reward['quantity'],
  399. 'success' => false,
  400. 'message' => $e->getMessage(),
  401. ];
  402. }
  403. }
  404. return $results;
  405. }
  406. /**
  407. * 获取任务详情
  408. *
  409. * @param int $userId 用户ID
  410. * @param int $taskId 任务ID
  411. * @return array 任务详情
  412. */
  413. public function getTaskDetail(int $userId, int $taskId): array
  414. {
  415. // 获取任务信息
  416. $task = $this->taskRepository->find($taskId);
  417. if (!$task) {
  418. return [
  419. 'success' => false,
  420. 'message' => '任务不存在',
  421. ];
  422. }
  423. // 获取用户任务状态
  424. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  425. // 获取任务奖励
  426. $rewards = $this->rewardRepository->getRewardsByTaskId($taskId);
  427. // 构建任务详情
  428. $taskDetail = $task->toArray();
  429. $taskDetail['user_status'] = $userTask ? $userTask->status : TASK_STATUS::NOT_ACCEPTED->value;
  430. $taskDetail['progress'] = $userTask ? $userTask->progress : 0;
  431. $taskDetail['completed_at'] = $userTask && $userTask->completed_at ? $userTask->completed_at : null;
  432. $taskDetail['rewarded_at'] = $userTask && $userTask->rewarded_at ? $userTask->rewarded_at : null;
  433. $taskDetail['rewards'] = $rewards;
  434. return [
  435. 'success' => true,
  436. 'task' => $taskDetail,
  437. ];
  438. }
  439. /**
  440. * 放弃任务
  441. *
  442. * @param int $userId 用户ID
  443. * @param int $taskId 任务ID
  444. * @return array 放弃结果
  445. */
  446. public function abandonTask(int $userId, int $taskId): array
  447. {
  448. try {
  449. // 获取用户任务
  450. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  451. if (!$userTask) {
  452. throw new \Exception('未接取该任务');
  453. }
  454. // 检查任务状态
  455. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  456. throw new \Exception('只能放弃进行中的任务');
  457. }
  458. // 删除用户任务记录
  459. $userTask->delete();
  460. return [
  461. 'success' => true,
  462. 'message' => '任务放弃成功',
  463. ];
  464. } catch (\Exception $e) {
  465. Log::error('任务放弃失败', [
  466. 'user_id' => $userId,
  467. 'task_id' => $taskId,
  468. 'error' => $e->getMessage(),
  469. ]);
  470. return [
  471. 'success' => false,
  472. 'message' => $e->getMessage(),
  473. ];
  474. }
  475. }
  476. /**
  477. * 更新任务进度
  478. *
  479. * @param int $userId 用户ID
  480. * @param int $taskId 任务ID
  481. * @param int $progress 进度值(0-100)
  482. * @return array 更新结果
  483. */
  484. public function updateTaskProgress(int $userId, int $taskId, int $progress): array
  485. {
  486. try {
  487. // 获取用户任务
  488. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  489. if (!$userTask) {
  490. throw new \Exception('未接取该任务');
  491. }
  492. // 检查任务状态
  493. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  494. throw new \Exception('只能更新进行中的任务进度');
  495. }
  496. // 验证进度值
  497. if ($progress < 0 || $progress > 100) {
  498. throw new \Exception('进度值必须在0-100之间');
  499. }
  500. // 更新任务进度
  501. $userTask->progress = $progress;
  502. $userTask->save();
  503. // 如果进度达到100%,自动完成任务
  504. if ($progress === 100) {
  505. return $this->completeTask($userId, $taskId);
  506. }
  507. return [
  508. 'success' => true,
  509. 'message' => '任务进度更新成功',
  510. 'progress' => $progress,
  511. ];
  512. } catch (\Exception $e) {
  513. Log::error('任务进度更新失败', [
  514. 'user_id' => $userId,
  515. 'task_id' => $taskId,
  516. 'progress' => $progress,
  517. 'error' => $e->getMessage(),
  518. ]);
  519. return [
  520. 'success' => false,
  521. 'message' => $e->getMessage(),
  522. ];
  523. }
  524. }
  525. /**
  526. * 批量领取任务奖励
  527. *
  528. * @param int $userId 用户ID
  529. * @param array $taskIds 任务ID列表
  530. * @return array 领取结果
  531. */
  532. public function batchClaimTaskRewards(int $userId, array $taskIds): array
  533. {
  534. $results = [];
  535. $successCount = 0;
  536. $failCount = 0;
  537. foreach ($taskIds as $taskId) {
  538. $result = $this->claimTaskReward($userId, $taskId);
  539. $results[$taskId] = $result;
  540. if ($result['success']) {
  541. $successCount++;
  542. } else {
  543. $failCount++;
  544. }
  545. }
  546. return [
  547. 'success' => $failCount === 0,
  548. 'message' => "成功领取{$successCount}个任务奖励,失败{$failCount}个",
  549. 'results' => $results,
  550. ];
  551. }
  552. }