TaskService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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 Carbon\Carbon;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Log;
  11. /**
  12. * 任务服务类
  13. *
  14. * 提供任务相关的核心服务,包括任务查询、接取、完成、奖励领取等功能。
  15. * 该类是任务模块对外提供服务的主要入口。
  16. */
  17. class TaskService
  18. {
  19. /**
  20. * 任务数据仓库
  21. *
  22. * @var TaskRepository
  23. */
  24. protected $taskRepository;
  25. /**
  26. * 用户任务数据仓库
  27. *
  28. * @var TaskUserTaskRepository
  29. */
  30. protected $userTaskRepository;
  31. /**
  32. * 任务逻辑类
  33. *
  34. * @var TaskLogic
  35. */
  36. protected $taskLogic;
  37. /**
  38. * 构造函数
  39. *
  40. * @param TaskRepository $taskRepository
  41. * @param TaskUserTaskRepository $userTaskRepository
  42. * @param TaskLogic $taskLogic
  43. */
  44. public function __construct(
  45. TaskRepository $taskRepository,
  46. TaskUserTaskRepository $userTaskRepository,
  47. TaskLogic $taskLogic
  48. ) {
  49. $this->taskRepository = $taskRepository;
  50. $this->userTaskRepository = $userTaskRepository;
  51. $this->taskLogic = $taskLogic;
  52. }
  53. /**
  54. * 获取用户可用的任务列表
  55. *
  56. * @param int $userId 用户ID
  57. * @param string|null $type 任务类型(可选)
  58. * @param int|null $categoryId 任务分类ID(可选)
  59. * @param bool $includeCompleted 是否包含已完成的任务
  60. * @return array 任务列表
  61. */
  62. public function getAvailableTasks(int $userId, ?string $type = null, ?int $categoryId = null, bool $includeCompleted = false): array
  63. {
  64. // 获取用户信息(可以调用用户模块的服务)
  65. // $userInfo = UserService::getUserInfo($userId);
  66. // 获取用户等级
  67. $userLevel = 1; // 默认等级,实际应从用户信息中获取
  68. // 获取符合条件的任务
  69. $tasks = $this->taskRepository->getAvailableTasks($type, $categoryId, $userLevel);
  70. // 获取用户任务状态
  71. $userTasks = $this->userTaskRepository->getUserTasks($userId);
  72. // 将用户任务状态合并到任务数据中
  73. $result = [];
  74. foreach ($tasks as $task) {
  75. $userTask = $userTasks->where('task_id', $task->id)->first();
  76. // 如果任务已完成且不包含已完成任务,则跳过
  77. if ($userTask && $userTask->status >= TASK_STATUS::COMPLETED->value && !$includeCompleted) {
  78. continue;
  79. }
  80. // 合并任务数据和用户任务状态
  81. $taskData = $task;
  82. $taskData['user_status'] = $userTask ? $userTask['status'] : TASK_STATUS::NOT_ACCEPTED->value;
  83. $taskData['progress'] = $userTask ? $userTask['progress'] : 0;
  84. $taskData['completed_at'] = $userTask && $userTask['completed_at'] ? $userTask['completed_at'] : null;
  85. $taskData['rewarded_at'] = $userTask && $userTask['rewarded_at'] ? $userTask['rewarded_at'] : null;
  86. // 获取任务奖励
  87. $taskData['rewards'] = $this->taskLogic->getTaskRewardInfo($task['id'])['rewards'];
  88. $result[] = $taskData;
  89. }
  90. return $result;
  91. }
  92. /**
  93. * 接取任务
  94. *
  95. * @param int $userId 用户ID
  96. * @param int $taskId 任务ID
  97. * @return array 接取结果
  98. */
  99. public function acceptTask(int $userId, int $taskId): array
  100. {
  101. try {
  102. // 开始事务
  103. DB::beginTransaction();
  104. // 获取任务信息
  105. $task = $this->taskRepository->find($taskId);
  106. if (!$task || !$task->is_active) {
  107. throw new \Exception('任务不存在或未激活');
  108. }
  109. // 检查任务是否已过期
  110. if ($task->end_time && Carbon::now()->greaterThan($task->end_time)) {
  111. throw new \Exception('任务已过期');
  112. }
  113. // 检查用户是否已接取该任务
  114. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  115. if ($userTask) {
  116. throw new \Exception('已接取该任务');
  117. }
  118. // 检查任务前置条件
  119. // $this->checkTaskPrerequisites($userId, $task);
  120. // 检查任务接取消耗
  121. // $this->checkAndConsumeTaskCosts($userId, $task);
  122. // 创建用户任务记录
  123. $userTask = $this->userTaskRepository->createUserTask([
  124. 'user_id' => $userId,
  125. 'task_id' => $taskId,
  126. 'status' => TASK_STATUS::IN_PROGRESS->value,
  127. 'progress' => 0,
  128. 'accepted_at' => Carbon::now(),
  129. ]);
  130. // 提交事务
  131. DB::commit();
  132. return [
  133. 'success' => true,
  134. 'message' => '任务接取成功',
  135. 'user_task' => $userTask,
  136. ];
  137. } catch (\Exception $e) {
  138. // 回滚事务
  139. DB::rollBack();
  140. Log::error('任务接取失败', [
  141. 'user_id' => $userId,
  142. 'task_id' => $taskId,
  143. 'error' => $e->getMessage(),
  144. ]);
  145. return [
  146. 'success' => false,
  147. 'message' => $e->getMessage(),
  148. ];
  149. }
  150. }
  151. /**
  152. * 完成任务
  153. *
  154. * @param int $userId 用户ID
  155. * @param int $taskId 任务ID
  156. * @return array 完成结果
  157. */
  158. public function completeTask(int $userId, int $taskId): array
  159. {
  160. try {
  161. // 开始事务
  162. DB::beginTransaction();
  163. // 获取用户任务
  164. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  165. if (!$userTask) {
  166. throw new \Exception('未接取该任务');
  167. }
  168. // 检查任务状态
  169. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  170. throw new \Exception('任务状态不正确');
  171. }
  172. // 检查任务进度
  173. if ($userTask->progress < 100) {
  174. throw new \Exception('任务进度未达到100%');
  175. }
  176. // 更新任务状态
  177. $userTask->status = TASK_STATUS::COMPLETED->value;
  178. $userTask->completed_at = Carbon::now();
  179. $userTask->save();
  180. // 获取任务信息
  181. $task = $this->taskRepository->find($taskId);
  182. // 触发任务完成事件
  183. event(new TaskCompletedEvent(
  184. $userId,
  185. $taskId,
  186. $task->name,
  187. $userTask->completed_at,
  188. true,
  189. []
  190. ));
  191. // 提交事务
  192. DB::commit();
  193. return [
  194. 'success' => true,
  195. 'message' => '任务完成成功',
  196. 'user_task' => $userTask,
  197. ];
  198. } catch (\Exception $e) {
  199. // 回滚事务
  200. DB::rollBack();
  201. Log::error('任务完成失败', [
  202. 'user_id' => $userId,
  203. 'task_id' => $taskId,
  204. 'error' => $e->getMessage(),
  205. ]);
  206. return [
  207. 'success' => false,
  208. 'message' => $e->getMessage(),
  209. ];
  210. }
  211. }
  212. /**
  213. * 领取任务奖励
  214. *
  215. * @param int $userId 用户ID
  216. * @param int $taskId 任务ID
  217. * @return array 领取结果
  218. */
  219. public function claimTaskReward(int $userId, int $taskId): array
  220. {
  221. return $this->taskLogic->claimTaskReward($userId, $taskId);
  222. }
  223. /**
  224. * 获取任务奖励信息
  225. *
  226. * @param int $taskId 任务ID
  227. * @return array 奖励信息
  228. */
  229. public function getTaskRewardInfo(int $taskId): array
  230. {
  231. return $this->taskLogic->getTaskRewardInfo($taskId);
  232. }
  233. /**
  234. * 迁移任务奖励到奖励组
  235. *
  236. * @param int $taskId 任务ID
  237. * @return array 迁移结果
  238. */
  239. public function migrateTaskReward(int $taskId): array
  240. {
  241. return $this->taskLogic->migrateTaskReward($taskId);
  242. }
  243. /**
  244. * 获取任务详情
  245. *
  246. * @param int $userId 用户ID
  247. * @param int $taskId 任务ID
  248. * @return array 任务详情
  249. */
  250. public function getTaskDetail(int $userId, int $taskId): array
  251. {
  252. // 获取任务信息
  253. $task = $this->taskRepository->find($taskId);
  254. if (!$task) {
  255. return [
  256. 'success' => false,
  257. 'message' => '任务不存在',
  258. ];
  259. }
  260. // 获取用户任务状态
  261. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  262. // 获取任务奖励
  263. $rewards = $this->taskLogic->getTaskRewardInfo($taskId)['rewards'];
  264. // 构建任务详情
  265. $taskDetail = $task->toArray();
  266. $taskDetail['user_status'] = $userTask ? $userTask->status : TASK_STATUS::NOT_ACCEPTED->value;
  267. $taskDetail['progress'] = $userTask ? $userTask->progress : 0;
  268. $taskDetail['completed_at'] = $userTask && $userTask->completed_at ? $userTask->completed_at : null;
  269. $taskDetail['rewarded_at'] = $userTask && $userTask->rewarded_at ? $userTask->rewarded_at : null;
  270. $taskDetail['rewards'] = $rewards;
  271. return [
  272. 'success' => true,
  273. 'task' => $taskDetail,
  274. ];
  275. }
  276. /**
  277. * 放弃任务
  278. *
  279. * @param int $userId 用户ID
  280. * @param int $taskId 任务ID
  281. * @return array 放弃结果
  282. */
  283. public function abandonTask(int $userId, int $taskId): array
  284. {
  285. try {
  286. // 获取用户任务
  287. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  288. if (!$userTask) {
  289. throw new \Exception('未接取该任务');
  290. }
  291. // 检查任务状态
  292. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  293. throw new \Exception('只能放弃进行中的任务');
  294. }
  295. // 删除用户任务记录
  296. $userTask->delete();
  297. return [
  298. 'success' => true,
  299. 'message' => '任务放弃成功',
  300. ];
  301. } catch (\Exception $e) {
  302. Log::error('任务放弃失败', [
  303. 'user_id' => $userId,
  304. 'task_id' => $taskId,
  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. * @param int $progress 进度值(0-100)
  319. * @return array 更新结果
  320. */
  321. public function updateTaskProgress(int $userId, int $taskId, int $progress): array
  322. {
  323. try {
  324. // 获取用户任务
  325. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  326. if (!$userTask) {
  327. throw new \Exception('未接取该任务');
  328. }
  329. // 检查任务状态
  330. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  331. throw new \Exception('只能更新进行中的任务进度');
  332. }
  333. // 验证进度值
  334. if ($progress < 0 || $progress > 100) {
  335. throw new \Exception('进度值必须在0-100之间');
  336. }
  337. // 更新任务进度
  338. $userTask->progress = $progress;
  339. $userTask->save();
  340. // 如果进度达到100%,自动完成任务
  341. if ($progress === 100) {
  342. return $this->completeTask($userId, $taskId);
  343. }
  344. return [
  345. 'success' => true,
  346. 'message' => '任务进度更新成功',
  347. 'progress' => $progress,
  348. ];
  349. } catch (\Exception $e) {
  350. Log::error('任务进度更新失败', [
  351. 'user_id' => $userId,
  352. 'task_id' => $taskId,
  353. 'progress' => $progress,
  354. 'error' => $e->getMessage(),
  355. ]);
  356. return [
  357. 'success' => false,
  358. 'message' => $e->getMessage(),
  359. ];
  360. }
  361. }
  362. /**
  363. * 批量领取任务奖励
  364. *
  365. * @param int $userId 用户ID
  366. * @param array $taskIds 任务ID列表
  367. * @return array 领取结果
  368. */
  369. public function batchClaimTaskRewards(int $userId, array $taskIds): array
  370. {
  371. $results = [];
  372. $successCount = 0;
  373. $failCount = 0;
  374. foreach ($taskIds as $taskId) {
  375. $result = $this->claimTaskReward($userId, $taskId);
  376. $results[$taskId] = $result;
  377. if ($result['success']) {
  378. $successCount++;
  379. } else {
  380. $failCount++;
  381. }
  382. }
  383. return [
  384. 'success' => $failCount === 0,
  385. 'message' => "成功领取{$successCount}个任务奖励,失败{$failCount}个",
  386. 'results' => $results,
  387. ];
  388. }
  389. }