TaskProgressService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <?php
  2. namespace App\Module\Task\Services;
  3. use App\Module\Task\Enums\RESET_TYPE;
  4. use App\Module\Task\Enums\TASK_STATUS;
  5. use App\Module\Task\Events\TaskCompletedEvent;
  6. use App\Module\Task\Models\Task;
  7. use App\Module\Task\Models\TaskUserTask;
  8. use App\Module\Task\Repositorys\TaskRepository;
  9. use App\Module\Task\Repositorys\TaskResetLogRepository;
  10. use App\Module\Task\Repositorys\TaskUserProgressRepository;
  11. use App\Module\Task\Repositorys\TaskUserTaskRepository;
  12. use Carbon\Carbon;
  13. use Illuminate\Support\Facades\DB;
  14. use Illuminate\Support\Facades\Log;
  15. /**
  16. * 任务进度服务类
  17. *
  18. * 提供任务进度相关的服务,包括进度更新、任务完成检查、任务重置等功能。
  19. * 该类主要处理任务进度的逻辑,与TaskConditionService配合使用。
  20. */
  21. class TaskProgressService
  22. {
  23. /**
  24. * 任务数据仓库
  25. *
  26. * @var TaskRepository
  27. */
  28. protected $taskRepository;
  29. /**
  30. * 用户任务数据仓库
  31. *
  32. * @var TaskUserTaskRepository
  33. */
  34. protected $userTaskRepository;
  35. /**
  36. * 用户任务进度数据仓库
  37. *
  38. * @var TaskUserProgressRepository
  39. */
  40. protected $userProgressRepository;
  41. /**
  42. * 任务重置日志数据仓库
  43. *
  44. * @var TaskResetLogRepository
  45. */
  46. protected $resetLogRepository;
  47. /**
  48. * 任务条件服务
  49. *
  50. * @var TaskConditionService
  51. */
  52. protected $conditionService;
  53. /**
  54. * 构造函数
  55. *
  56. * @param TaskRepository $taskRepository
  57. * @param TaskUserTaskRepository $userTaskRepository
  58. * @param TaskUserProgressRepository $userProgressRepository
  59. * @param TaskResetLogRepository $resetLogRepository
  60. * @param TaskConditionService $conditionService
  61. */
  62. public function __construct(
  63. TaskRepository $taskRepository,
  64. TaskUserTaskRepository $userTaskRepository,
  65. TaskUserProgressRepository $userProgressRepository,
  66. TaskResetLogRepository $resetLogRepository,
  67. TaskConditionService $conditionService
  68. ) {
  69. $this->taskRepository = $taskRepository;
  70. $this->userTaskRepository = $userTaskRepository;
  71. $this->userProgressRepository = $userProgressRepository;
  72. $this->resetLogRepository = $resetLogRepository;
  73. $this->conditionService = $conditionService;
  74. }
  75. /**
  76. * 更新任务进度
  77. *
  78. * @param int $userId 用户ID
  79. * @param int $taskId 任务ID
  80. * @param int $progress 进度值(0-100)
  81. * @return array 更新结果
  82. */
  83. public function updateTaskProgress(int $userId, int $taskId, int $progress): array
  84. {
  85. try {
  86. // 获取用户任务
  87. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  88. if (!$userTask) {
  89. throw new \Exception('未接取该任务');
  90. }
  91. // 检查任务状态
  92. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  93. throw new \Exception('只能更新进行中的任务进度');
  94. }
  95. // 验证进度值
  96. if ($progress < 0 || $progress > 100) {
  97. throw new \Exception('进度值必须在0-100之间');
  98. }
  99. // 检查任务是否需要重置
  100. $this->checkAndResetTask($userTask);
  101. // 更新任务进度
  102. $userTask->progress = $progress;
  103. $userTask->save();
  104. // 如果进度达到100%,检查是否可以完成任务
  105. if ($progress === 100) {
  106. return $this->checkTaskCompletion($userId, $taskId);
  107. }
  108. return [
  109. 'success' => true,
  110. 'message' => '任务进度更新成功',
  111. 'progress' => $progress,
  112. ];
  113. } catch (\Exception $e) {
  114. Log::error('任务进度更新失败', [
  115. 'user_id' => $userId,
  116. 'task_id' => $taskId,
  117. 'progress' => $progress,
  118. 'error' => $e->getMessage(),
  119. ]);
  120. return [
  121. 'success' => false,
  122. 'message' => '任务进度更新失败: ' . $e->getMessage(),
  123. ];
  124. }
  125. }
  126. /**
  127. * 检查任务完成状态
  128. *
  129. * @param int $userId 用户ID
  130. * @param int $taskId 任务ID
  131. * @return array 检查结果
  132. */
  133. public function checkTaskCompletion(int $userId, int $taskId): array
  134. {
  135. try {
  136. // 开始事务
  137. DB::beginTransaction();
  138. // 获取用户任务
  139. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  140. if (!$userTask) {
  141. throw new \Exception('未接取该任务');
  142. }
  143. // 检查任务状态
  144. if ($userTask->status !== TASK_STATUS::IN_PROGRESS->value) {
  145. throw new \Exception('只能完成进行中的任务');
  146. }
  147. // 检查任务进度
  148. if ($userTask->progress < 100) {
  149. throw new \Exception('任务进度未达到100%');
  150. }
  151. // 获取任务信息
  152. $task = $this->taskRepository->find($taskId);
  153. // 更新任务状态
  154. $userTask->status = TASK_STATUS::COMPLETED->value;
  155. $userTask->completed_at = Carbon::now();
  156. $userTask->save();
  157. // 触发任务完成事件
  158. event(new TaskCompletedEvent(
  159. $userId,
  160. $taskId,
  161. $task->name,
  162. $userTask->completed_at
  163. ));
  164. // 提交事务
  165. DB::commit();
  166. return [
  167. 'success' => true,
  168. 'message' => '任务完成成功',
  169. 'user_task' => $userTask,
  170. ];
  171. } catch (\Exception $e) {
  172. // 回滚事务
  173. DB::rollBack();
  174. Log::error('任务完成检查失败', [
  175. 'user_id' => $userId,
  176. 'task_id' => $taskId,
  177. 'error' => $e->getMessage(),
  178. ]);
  179. return [
  180. 'success' => false,
  181. 'message' => '任务完成检查失败: ' . $e->getMessage(),
  182. ];
  183. }
  184. }
  185. /**
  186. * 检查并重置任务
  187. *
  188. * @param TaskUserTask $userTask 用户任务
  189. * @return bool 是否重置
  190. */
  191. public function checkAndResetTask(TaskUserTask $userTask): bool
  192. {
  193. try {
  194. // 获取任务信息
  195. $task = $this->taskRepository->find($userTask->task_id);
  196. if (!$task) {
  197. return false;
  198. }
  199. // 如果任务不需要重置,直接返回
  200. if (!$task->reset_type || $task->reset_type === 'none') {
  201. return false;
  202. }
  203. // 如果任务状态不是已完成或已领取奖励,不需要重置
  204. if (!in_array($userTask->status, [TASK_STATUS::COMPLETED->value, TASK_STATUS::REWARDED->value])) {
  205. return false;
  206. }
  207. // 检查是否需要重置
  208. $needReset = $this->needReset($userTask, $task);
  209. if ($needReset) {
  210. // 重置任务
  211. return $this->resetTask($userTask, $task);
  212. }
  213. return false;
  214. } catch (\Exception $e) {
  215. Log::error('检查并重置任务失败', [
  216. 'user_task_id' => $userTask->id,
  217. 'user_id' => $userTask->user_id,
  218. 'task_id' => $userTask->task_id,
  219. 'error' => $e->getMessage(),
  220. ]);
  221. return false;
  222. }
  223. }
  224. /**
  225. * 检查任务是否需要重置
  226. *
  227. * @param TaskUserTask $userTask 用户任务
  228. * @param Task $task 任务
  229. * @return bool 是否需要重置
  230. */
  231. protected function needReset(TaskUserTask $userTask, Task $task): bool
  232. {
  233. // 获取当前时间
  234. $now = Carbon::now();
  235. // 如果下次重置时间未设置,则计算下次重置时间
  236. if (!$userTask->next_reset_time) {
  237. // 如果上次重置时间未设置,则使用任务完成时间或当前时间
  238. $lastResetTime = $userTask->last_reset_time ?? $userTask->completed_at ?? $now;
  239. // 计算下次重置时间
  240. $nextResetTime = $this->calculateNextResetTime($task->reset_type, $lastResetTime);
  241. // 更新下次重置时间
  242. $userTask->next_reset_time = $nextResetTime;
  243. $userTask->save();
  244. // 如果下次重置时间已过,则需要重置
  245. return $now->greaterThanOrEqualTo($nextResetTime);
  246. }
  247. // 如果当前时间已超过下次重置时间,则需要重置
  248. return $now->greaterThanOrEqualTo($userTask->next_reset_time);
  249. }
  250. /**
  251. * 计算下次重置时间
  252. *
  253. * @param string $resetType 重置类型
  254. * @param Carbon $lastResetTime 上次重置时间
  255. * @return Carbon 下次重置时间
  256. */
  257. protected function calculateNextResetTime(string $resetType, Carbon $lastResetTime): Carbon
  258. {
  259. $resetType = RESET_TYPE::tryFrom($resetType) ?? RESET_TYPE::NONE;
  260. // 获取重置间隔(秒)
  261. $interval = RESET_TYPE::getInterval($resetType);
  262. if ($interval === null) {
  263. // 如果没有重置间隔,则返回一个很远的未来时间
  264. return Carbon::now()->addYears(100);
  265. }
  266. // 计算下次重置时间
  267. return $lastResetTime->copy()->addSeconds($interval);
  268. }
  269. /**
  270. * 重置任务
  271. *
  272. * @param TaskUserTask $userTask 用户任务
  273. * @param Task $task 任务
  274. * @return bool 是否成功
  275. */
  276. protected function resetTask(TaskUserTask $userTask, Task $task): bool
  277. {
  278. try {
  279. // 开始事务
  280. DB::beginTransaction();
  281. // 记录重置前的状态
  282. $oldStatus = $userTask->status;
  283. $oldProgress = $userTask->progress;
  284. // 重置任务进度
  285. $this->conditionService->resetTaskProgress($userTask->user_id, $userTask->task_id);
  286. // 更新任务状态
  287. $userTask->status = TASK_STATUS::IN_PROGRESS->value;
  288. $userTask->progress = 0;
  289. $userTask->completed_at = null;
  290. $userTask->rewarded_at = null;
  291. $userTask->last_reset_time = Carbon::now();
  292. // 计算下次重置时间
  293. $userTask->next_reset_time = $this->calculateNextResetTime($task->reset_type, $userTask->last_reset_time);
  294. // 增加重置次数
  295. $userTask->reset_count = ($userTask->reset_count ?? 0) + 1;
  296. // 保存更新
  297. $userTask->save();
  298. // 记录重置日志
  299. $this->resetLogRepository->create([
  300. 'user_id' => $userTask->user_id,
  301. 'task_id' => $userTask->task_id,
  302. 'user_task_id' => $userTask->id,
  303. 'reset_type' => $task->reset_type,
  304. 'old_status' => $oldStatus,
  305. 'old_progress' => $oldProgress,
  306. 'reset_time' => $userTask->last_reset_time,
  307. 'next_reset_time' => $userTask->next_reset_time,
  308. 'reset_count' => $userTask->reset_count,
  309. ]);
  310. // 提交事务
  311. DB::commit();
  312. return true;
  313. } catch (\Exception $e) {
  314. // 回滚事务
  315. DB::rollBack();
  316. Log::error('重置任务失败', [
  317. 'user_task_id' => $userTask->id,
  318. 'user_id' => $userTask->user_id,
  319. 'task_id' => $userTask->task_id,
  320. 'error' => $e->getMessage(),
  321. ]);
  322. return false;
  323. }
  324. }
  325. /**
  326. * 批量检查并重置任务
  327. *
  328. * @param int $userId 用户ID
  329. * @param string|null $resetType 重置类型(可选)
  330. * @return array 重置结果
  331. */
  332. public function batchCheckAndResetTasks(int $userId, ?string $resetType = null): array
  333. {
  334. try {
  335. // 获取用户任务
  336. $query = $this->userTaskRepository->query()
  337. ->where('user_id', $userId)
  338. ->whereIn('status', [TASK_STATUS::COMPLETED->value, TASK_STATUS::REWARDED->value]);
  339. // 如果指定了重置类型,则筛选任务
  340. if ($resetType) {
  341. $query->whereHas('task', function ($q) use ($resetType) {
  342. $q->where('reset_type', $resetType);
  343. });
  344. }
  345. $userTasks = $query->get();
  346. // 重置任务
  347. $resetCount = 0;
  348. foreach ($userTasks as $userTask) {
  349. $task = $this->taskRepository->find($userTask->task_id);
  350. if (!$task) {
  351. continue;
  352. }
  353. if ($this->checkAndResetTask($userTask)) {
  354. $resetCount++;
  355. }
  356. }
  357. return [
  358. 'success' => true,
  359. 'message' => "已重置{$resetCount}个任务",
  360. 'reset_count' => $resetCount,
  361. 'total_count' => $userTasks->count(),
  362. ];
  363. } catch (\Exception $e) {
  364. Log::error('批量检查并重置任务失败', [
  365. 'user_id' => $userId,
  366. 'reset_type' => $resetType,
  367. 'error' => $e->getMessage(),
  368. ]);
  369. return [
  370. 'success' => false,
  371. 'message' => '批量检查并重置任务失败: ' . $e->getMessage(),
  372. ];
  373. }
  374. }
  375. /**
  376. * 获取用户任务进度
  377. *
  378. * @param int $userId 用户ID
  379. * @param int $taskId 任务ID
  380. * @return array 任务进度
  381. */
  382. public function getTaskProgress(int $userId, int $taskId): array
  383. {
  384. try {
  385. // 获取用户任务
  386. $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
  387. if (!$userTask) {
  388. throw new \Exception('未接取该任务');
  389. }
  390. // 获取任务条件进度
  391. $conditionProgress = $this->userProgressRepository->getUserTaskProgress($userId, $taskId);
  392. return [
  393. 'success' => true,
  394. 'task_id' => $taskId,
  395. 'user_id' => $userId,
  396. 'status' => $userTask->status,
  397. 'progress' => $userTask->progress,
  398. 'completed_at' => $userTask->completed_at,
  399. 'rewarded_at' => $userTask->rewarded_at,
  400. 'condition_progress' => $conditionProgress,
  401. ];
  402. } catch (\Exception $e) {
  403. Log::error('获取任务进度失败', [
  404. 'user_id' => $userId,
  405. 'task_id' => $taskId,
  406. 'error' => $e->getMessage(),
  407. ]);
  408. return [
  409. 'success' => false,
  410. 'message' => '获取任务进度失败: ' . $e->getMessage(),
  411. ];
  412. }
  413. }
  414. }