TaskService.php 18 KB

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