TaskService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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 \UCore\Dto\Res 接取结果
  98. */
  99. public function acceptTask(int $userId, int $taskId): \UCore\Dto\Res
  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 \UCore\Dto\Res::success('任务接取成功', [
  133. 'user_task' => $userTask,
  134. ]);
  135. } catch (\Exception $e) {
  136. // 回滚事务
  137. DB::rollBack();
  138. Log::error('任务接取失败', [
  139. 'user_id' => $userId,
  140. 'task_id' => $taskId,
  141. 'error' => $e->getMessage(),
  142. ]);
  143. return \UCore\Dto\Res::error($e->getMessage());
  144. }
  145. }
  146. /**
  147. * 完成任务
  148. *
  149. * @param int $userId 用户ID
  150. * @param int $taskId 任务ID
  151. * @return array 完成结果
  152. */
  153. public function completeTask(int $userId, int $taskId): array
  154. {
  155. try {
  156. // 开始事务
  157. DB::beginTransaction();
  158. // 获取用户任务
  159. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  160. if (!$userTask) {
  161. throw new \Exception('未接取该任务');
  162. }
  163. // 检查任务状态
  164. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  165. throw new \Exception('任务状态不正确');
  166. }
  167. // 检查任务进度
  168. if ($userTask->progress < 100) {
  169. throw new \Exception('任务进度未达到100%');
  170. }
  171. // 更新任务状态
  172. $userTask->status = TASK_STATUS::COMPLETED->value;
  173. $userTask->completed_at = Carbon::now();
  174. $userTask->save();
  175. // 获取任务信息
  176. $task = $this->taskRepository->find($taskId);
  177. // 触发任务完成事件
  178. event(new TaskCompletedEvent(
  179. $userId,
  180. $taskId,
  181. $task->name,
  182. $userTask->completed_at,
  183. true,
  184. []
  185. ));
  186. // 提交事务
  187. DB::commit();
  188. return [
  189. 'success' => true,
  190. 'message' => '任务完成成功',
  191. 'user_task' => $userTask,
  192. ];
  193. } catch (\Exception $e) {
  194. // 回滚事务
  195. DB::rollBack();
  196. Log::error('任务完成失败', [
  197. 'user_id' => $userId,
  198. 'task_id' => $taskId,
  199. 'error' => $e->getMessage(),
  200. ]);
  201. return [
  202. 'success' => false,
  203. 'message' => $e->getMessage(),
  204. ];
  205. }
  206. }
  207. /**
  208. * 领取任务奖励
  209. *
  210. * @param int $userId 用户ID
  211. * @param int $taskId 任务ID
  212. * @return \UCore\Dto\Res 领取结果
  213. */
  214. public function claimTaskReward(int $userId, int $taskId): \UCore\Dto\Res
  215. {
  216. return $this->taskLogic->claimTaskReward($userId, $taskId);
  217. }
  218. /**
  219. * 获取任务奖励信息
  220. *
  221. * @param int $taskId 任务ID
  222. * @return \UCore\Dto\Res 奖励信息
  223. */
  224. public function getTaskRewardInfo(int $taskId): \UCore\Dto\Res
  225. {
  226. return $this->taskLogic->getTaskRewardInfo($taskId);
  227. }
  228. /**
  229. * 迁移任务奖励到奖励组
  230. *
  231. * @param int $taskId 任务ID
  232. * @return array 迁移结果
  233. */
  234. public function migrateTaskReward(int $taskId): array
  235. {
  236. return $this->taskLogic->migrateTaskReward($taskId);
  237. }
  238. /**
  239. * 获取任务详情
  240. *
  241. * @param int $userId 用户ID
  242. * @param int $taskId 任务ID
  243. * @return array 任务详情
  244. */
  245. public function getTaskDetail(int $userId, int $taskId): array
  246. {
  247. // 获取任务信息
  248. $task = $this->taskRepository->find($taskId);
  249. if (!$task) {
  250. return [
  251. 'success' => false,
  252. 'message' => '任务不存在',
  253. ];
  254. }
  255. // 获取用户任务状态
  256. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  257. // 获取任务奖励
  258. $rewards = $this->taskLogic->getTaskRewardInfo($taskId)['rewards'];
  259. // 构建任务详情
  260. $taskDetail = $task->toArray();
  261. $taskDetail['user_status'] = $userTask ? $userTask->status : TASK_STATUS::NOT_ACCEPTED->value;
  262. $taskDetail['progress'] = $userTask ? $userTask->progress : 0;
  263. $taskDetail['completed_at'] = $userTask && $userTask->completed_at ? $userTask->completed_at : null;
  264. $taskDetail['rewarded_at'] = $userTask && $userTask->rewarded_at ? $userTask->rewarded_at : null;
  265. $taskDetail['rewards'] = $rewards;
  266. return [
  267. 'success' => true,
  268. 'task' => $taskDetail,
  269. ];
  270. }
  271. /**
  272. * 放弃任务
  273. *
  274. * @param int $userId 用户ID
  275. * @param int $taskId 任务ID
  276. * @return array 放弃结果
  277. */
  278. public function abandonTask(int $userId, int $taskId): array
  279. {
  280. try {
  281. // 获取用户任务
  282. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  283. if (!$userTask) {
  284. throw new \Exception('未接取该任务');
  285. }
  286. // 检查任务状态
  287. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  288. throw new \Exception('只能放弃进行中的任务');
  289. }
  290. // 删除用户任务记录
  291. $userTask->delete();
  292. return [
  293. 'success' => true,
  294. 'message' => '任务放弃成功',
  295. ];
  296. } catch (\Exception $e) {
  297. Log::error('任务放弃失败', [
  298. 'user_id' => $userId,
  299. 'task_id' => $taskId,
  300. 'error' => $e->getMessage(),
  301. ]);
  302. return [
  303. 'success' => false,
  304. 'message' => $e->getMessage(),
  305. ];
  306. }
  307. }
  308. /**
  309. * 更新任务进度
  310. *
  311. * @param int $userId 用户ID
  312. * @param int $taskId 任务ID
  313. * @param int $progress 进度值(0-100)
  314. * @return array 更新结果
  315. */
  316. public function updateTaskProgress(int $userId, int $taskId, int $progress): array
  317. {
  318. try {
  319. // 获取用户任务
  320. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  321. if (!$userTask) {
  322. throw new \Exception('未接取该任务');
  323. }
  324. // 检查任务状态
  325. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  326. throw new \Exception('只能更新进行中的任务进度');
  327. }
  328. // 验证进度值
  329. if ($progress < 0 || $progress > 100) {
  330. throw new \Exception('进度值必须在0-100之间');
  331. }
  332. // 更新任务进度
  333. $userTask->progress = $progress;
  334. $userTask->save();
  335. // 如果进度达到100%,自动完成任务
  336. if ($progress === 100) {
  337. return $this->completeTask($userId, $taskId);
  338. }
  339. return [
  340. 'success' => true,
  341. 'message' => '任务进度更新成功',
  342. 'progress' => $progress,
  343. ];
  344. } catch (\Exception $e) {
  345. Log::error('任务进度更新失败', [
  346. 'user_id' => $userId,
  347. 'task_id' => $taskId,
  348. 'progress' => $progress,
  349. 'error' => $e->getMessage(),
  350. ]);
  351. return [
  352. 'success' => false,
  353. 'message' => $e->getMessage(),
  354. ];
  355. }
  356. }
  357. /**
  358. * 批量领取任务奖励
  359. *
  360. * @param int $userId 用户ID
  361. * @param array $taskIds 任务ID列表
  362. * @return array 领取结果
  363. */
  364. public function batchClaimTaskRewards(int $userId, array $taskIds): array
  365. {
  366. $results = [];
  367. $successCount = 0;
  368. $failCount = 0;
  369. foreach ($taskIds as $taskId) {
  370. $result = $this->claimTaskReward($userId, $taskId);
  371. $results[$taskId] = $result;
  372. if ($result['success']) {
  373. $successCount++;
  374. } else {
  375. $failCount++;
  376. }
  377. }
  378. return [
  379. 'success' => $failCount === 0,
  380. 'message' => "成功领取{$successCount}个任务奖励,失败{$failCount}个",
  381. 'results' => $results,
  382. ];
  383. }
  384. }